xslt - subtracting days - xslt

Is it possible with xslt to take a date field and subtract N number of days from it? If so, can you please provide me an example?

Here is a demonstration how to do this in XSLT 2.0:
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xsl:output method="text"/>
<xsl:template match="/">
<xsl:variable name="vToday" select="current-date()"/>
Today is: <xsl:sequence select="$vToday"/>
30 days ago it was: <xsl:sequence select=
"$vToday -30*xs:dayTimeDuration('P1D')"/>
365 days ago it was: <xsl:sequence select=
"$vToday -365*xs:dayTimeDuration('P1D')"/>
</xsl:template>
</xsl:stylesheet>
when this transformation is applied on any XML document (not used), the wanted, correct result is produced:
Today is: 2010-10-07-07:00
30 days ago it was: 2010-09-07-07:00
365 days ago it was: 2009-10-07-07:00

Yes, with XSLT 2.0 it is possible, and very easy to do.
There are a lot of Date/Time/Duration functions in XPATH 2.0, which are part of XSLT 2.0.
This example subtracts 1 day from the date 2010-01-01 to produce 2009-12-31:
<?xml version="1.0"?>
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xsl:template match="/">
<xsl:value-of select="xs:date('2010-01-01') - xs:dayTimeDuration('P1D')" />
</xsl:template>
</xsl:stylesheet>

Well, XSLT can split strings and parse numbers, so yes, it would be "possible".
However, it would be much easier and efficient if you could use extension functions and implement that in another language. If and how that works depends on the XSLT engine used, however.
EXSLT may have everything you need: http://www.exslt.org/date/functions/add/index.html

I can see that all the mentioned solutions are for XSLT 2.0. I have similar solution for XSLT 1.0 using EXSLT date:add
Example: Consider that the number of days to be subtracted is 365 and we need it as the default start date. In this case, we have to provide the duration 365 days in xs:dayTimeDuration format i.e. '-P365D'.
Please find below the code.
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:date="http://exslt.org/dates-and-times"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
extension-element-prefixes="date xs"
exclude-result-prefixes="date xs" version="1.0">
<xsl:output method="xml" version="1.0" encoding="UTF-8" />
<xsl:template match="/">
<xsl:variable name="vCurrDate" select="date:date-time()"/>
<xsl:variable name="vDefaultStDate" select="(date:add($vCurrDate, '-P365D'))"/>
</xsl:template>
</xsl:stylesheet>

<xsl:template name="dim" >
<xsl:param name="y" />
<xsl:param name="m"/>
<xsl:choose>
<xsl:when test="($m=1 or $m=3 or $m=5 or $m=7 or $m=8 or $m=10 or $m=12)" >
31
</xsl:when>
<xsl:when test="$m>2">
30
</xsl:when>
<xsl:when test="($m=2) and (not($y mod 4=0)) or ($y mod 100=0) and (not($y mod 400=0))">
28
</xsl:when>
<xsl:otherwise>
29
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template name="minusdays">
<xsl:param name="year" />
<xsl:param name="month" />
<xsl:param name="day"/>
<xsl:param name="days"/>
<xsl:variable name="lm" select="number($month)-1+number($month=1)*12"/>
<xsl:variable name="lmdays">
<xsl:call-template name="dim">
<xsl:with-param name="y" select="$year"/>
<xsl:with-param name="m" select="$lm"/>
</xsl:call-template>
</xsl:variable>
<xsl:choose>
<xsl:when test="$days<$day">
<xsl:value-of select="$year"/>
<xsl:if test="number($month)<10">0</xsl:if>
<xsl:value-of select="$month"/>
<xsl:if test="($day - $days)<10">0</xsl:if>
<xsl:value-of select="$day - $days"/>
</xsl:when>
<xsl:otherwise>
<xsl:call-template name="minusdays">
<xsl:with-param name="year"
select="number($year)-number($month=1)"/>
<xsl:with-param name="month" select="$lm" />
<xsl:with-param name="day" select="$lmdays"/>
<xsl:with-param name="days" select="$days - $day" />
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</xsl:template>

Related

xslt: create a schedule

can someone tell me, how can I create a list of dates using xslt using a start- and end date and a parameter for the calculation period, so sth like the input
<root>
<startdate>2014/01/01</startdate>
<enddate>2015/02/20</enddate>
<period>daily</period>
</root>
gives me a list
<root>
<date>2014/01/01</date>
<date>2014/01/02</date>
...
<date>2014/02/20</date>
</root>
My node period can have values in
daily
weekly
fortnightly
monthly
So the last 3 would give me lists like
<root>
<date>2014/01/01</date>
<date>2014/01/08</date>
...
<date>2015/02/18</date>
</root>
<root>
<date>2014/01/01</date>
<date>2014/01/15</date>
...
<date>2015/02/11</date>
</root>
<root>
<date>2014/01/01</date>
<date>2014/02/01</date>
...
<date>2015/02/01</date>
</root>
with the last date smaller or equal the enddate. The date formats I would use are YYYYMMDD and DD/MM/YYYY, but I would probably be able to adapt any other date format.
Someone knows how to do this?
Thanks very much!
First, you must use YYYY-MM-DD format if you want your dates to be recognized as such.
Now, here's a quick-and-dirty way to achieve the requested result:
XSLT 2.0
<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="xml" version="1.0" encoding="utf-8" indent="yes"/>
<xsl:template match="/root">
<result>
<xsl:call-template name="enumerate-dates">
<xsl:with-param name="startdate" select="startdate"/>
<xsl:with-param name="enddate" select="enddate"/>
<xsl:with-param name="period" select="period"/>
</xsl:call-template>
</result>
</xsl:template>
<xsl:template name="enumerate-dates">
<xsl:param name="startdate" as="xs:date"/>
<xsl:param name="enddate" as="xs:date"/>
<xsl:param name="period"/>
<xsl:if test="$startdate le $enddate">
<date><xsl:value-of select="$startdate" /></date>
<xsl:call-template name="enumerate-dates">
<xsl:with-param name="startdate">
<xsl:choose>
<xsl:when test="$period='daily'">
<xsl:value-of select="$startdate + xs:dayTimeDuration('P1D')" />
</xsl:when>
<xsl:when test="$period='weekly'">
<xsl:value-of select="$startdate + xs:dayTimeDuration('P7D')" />
</xsl:when>
<xsl:when test="$period='fortnightly'">
<xsl:value-of select="$startdate + xs:dayTimeDuration('P14D')" />
</xsl:when>
<xsl:when test="$period='monthly'">
<xsl:value-of select="$startdate + xs:yearMonthDuration('P1M')" />
</xsl:when>
</xsl:choose>
</xsl:with-param>
<xsl:with-param name="enddate" select="$enddate"/>
<xsl:with-param name="period" select="$period"/>
</xsl:call-template>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
Applied to an example input of:
<root>
<startdate>2013-12-15</startdate>
<enddate>2014-03-08</enddate>
<period>fortnightly</period>
</root>
the result is:
<?xml version="1.0" encoding="utf-8"?>
<result>
<date>2013-12-15</date>
<date>2013-12-29</date>
<date>2014-01-12</date>
<date>2014-01-26</date>
<date>2014-02-09</date>
<date>2014-02-23</date>
</result>

How I can parse Pipeline sign from string in xslt

How I can parse pipeline sign from fields in xslt. e.g.
dummy1|dummy2|dummy3|dummy4
Regards,
Sarah
If you use an XSLT 2.0 processor you can use the tokenize function (http://www.w3.org/TR/xpath-functions/#func-tokenize) e.g. with an input of
<foo>dummy1|dummy2|dummy3|dummy4</foo>
you can match
<xsl:template match="foo">
<xsl:value-of select="tokenize(., '\|')"/>
</xsl:template>
to output dummy1 dummy2 dummy3 dummy4. If you use an XSLT 1.0 processor you can check whether it supports an extension function like http://www.exslt.org/str/functions/tokenize/ or you need to write a recursive, named template splitting up the input.
In xlst 1.0 I usually use recursive call of named template, e.g.
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:variable name="dummyVar" select="'dummy1|dummy2|dummy3|dummy4'" />
<xsl:variable name="delimiter" select="'|'" />
<xsl:template match="/">
<dummies>
<xsl:call-template name="parseDummy">
<xsl:with-param name="parsedString" select="$dummyVar" />
</xsl:call-template>
</dummies>
</xsl:template>
<xsl:template name="parseDummy">
<xsl:param name="parsedString" />
<xsl:choose>
<xsl:when test="contains($parsedString, $delimiter)">
<xsl:element name="{substring-before($parsedString, $delimiter)}" />
<xsl:call-template name="parseDummy">
<xsl:with-param name="parsedString" select="substring-after($parsedString, $delimiter)" />
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:element name="{$parsedString}" />
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
It's on you what you need to do with parsed values, in example I construct elements with names of dummies.
Be careful about context which is changing.

Using max in a variable in XSLT 1.0

can I use max function in a variable in XSLT 1?
I need to find a maximum value inside some nodes and I'll need to call this from more places.
So I tried to create a template:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:msxsl="urn:schemas-microsoft-com:xslt"
xmlns:essox="urn:essox-scripts">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:template name="Field001_max_dluznych_splatek">
<xsl:param name="CrRep"/>
<xsl:variable name="PocetDluznychSplatekSplatky">
<xsl:value-of
select="max($CrRep
/Response
/ContractData
/Installments
/InstDetail
/NrOfDueInstalments)" />
</xsl:variable>
<xsl:variable name="PocetDluznychSplatekKarty">
<xsl:value-of
select="max($CrRep
/Response
/ContractData
/Cards
/CardDetail
/NrOfDueInstalments)" />
</xsl:variable>
<xsl:choose>
<xsl:when test="$PocetDluznychSplatekSplatky
>= $PocetDluznychSplatekKarty">
<xsl:value-of select="$PocetDluznychSplatekSplatky"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$PocetDluznychSplatekKarty"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
In XML Spy I get this error:
Error in XPath expression Unknown
function - Name and number of
arguments do not match any function
signature in the static context -
'max'.
What is wrong?
Thanks a lot,
Peter
Use the well known maximum idiom:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:msxsl="urn:schemas-microsoft-com:xslt"
xmlns:essox="urn:essox-scripts">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:template name="Field001_max_dluznych_splatek">
<xsl:param name="CrRep"/>
<xsl:variable name="PocetDluznychSplatekSplatky">
<xsl:call-template name="maximun">
<xsl:with-param name="pSequence"
select="$CrRep
/Response
/ContractData
/Installments
/InstDetail
/NrOfDueInstalments"/>
</xsl:call-template>
</xsl:variable>
<xsl:variable name="PocetDluznychSplatekSplatky">
<xsl:call-template name="maximun">
<xsl:with-param name="pSequence"
select="$CrRep
/Response
/ContractData
/Cards
/CardDetail
/NrOfDueInstalments"/>
</xsl:call-template>
</xsl:variable>
<xsl:choose>
<xsl:when test="$PocetDluznychSplatekSplatky
>= $PocetDluznychSplatekKarty">
<xsl:value-of select="$PocetDluznychSplatekSplatky"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$PocetDluznychSplatekKarty"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template name="maximun">
<xsl:param name="pSequence"/>
<xsl:for-each select="$pSequence">
<xsl:sort select="." data-type="number" order="descending"/>
<xsl:if test="position()=1">
<xsl:value-of select="."/>
</xsl:if>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
Note: In a named template for reuse.
There is no max function in XSLT 1.0. You can work around this by sorting your elements in descending order and then taking the value of the first one.
Here's another (slower) way to do it:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:variable name="PocetDluznychSplatekSplatky"
select="/test/PocetDluznychSplatekSplatky/val[not(../val > .)][1]" />
<xsl:variable name="PocetDluznychSplatekKarty"
select="/test/PocetDluznychSplatekKarty/val[not(../val > .)][1]" />
<xsl:template match="/">
<xsl:choose>
<xsl:when
test="$PocetDluznychSplatekSplatky >=
$PocetDluznychSplatekKarty">
<xsl:value-of select="$PocetDluznychSplatekSplatky" />
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$PocetDluznychSplatekKarty" />
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
Sample source document:
<test>
<PocetDluznychSplatekSplatky>
<val>22</val>
<val>3241</val>
<val>13</val>
</PocetDluznychSplatekSplatky>
<PocetDluznychSplatekKarty>
<val>1</val>
<val>3234341</val>
<val>13</val>
</PocetDluznychSplatekKarty>
</test>
Output:
3234341
The XPath in each variable's select looks like this:
/test/PocetDluznychSplatekSplatky/val[not(../val > .)][1]
Or, select the val element having no val siblings with a greater value (i.e. the max).
(Obviously, you'll need to adjust the XPath to fit your source document.)
Note: The sort solution performs much better (assuming an n*log(n) sort implementation). The second approach needs to compare each val to every one of its siblings and is therefore quadratic.

XSLT to sum product of two attributes

I have the following XML source structure:
<turnovers>
<turnover repid="1" amount="500" rate="0.1"/>
<turnover repid="5" amount="600" rate="0.5"/>
<turnover repid="4" amount="400" rate="0.2"/>
<turnover repid="1" amount="700" rate="0.05"/>
<turnover repid="2" amount="100" rate="0.15"/>
<turnover repid="1" amount="900" rate="0.25"/>
<turnover repid="2" amount="1000" rate="0.18"/>
<turnover repid="5" amount="200" rate="0.55"/>
<turnover repid="9" amount="700" rate="0.40"/>
</turnovers>
I need an XSL:value-of select statement that will return the sum of the product of the rate attribute and the amount attribute for a given rep ID. So for rep 5 I need ((600 x 0.5) + (200 x 0.55)).
<xsl:stylesheet
version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
>
<xsl:template match="/turnovers">
<val>
<!-- call the sum function (with the relevant nodes) -->
<xsl:call-template name="sum">
<xsl:with-param name="nodes" select="turnover[#repid='5']" />
</xsl:call-template>
</val>
</xsl:template>
<xsl:template name="sum">
<xsl:param name="nodes" />
<xsl:param name="sum" select="0" />
<xsl:variable name="curr" select="$nodes[1]" />
<!-- if we have a node, calculate & recurse -->
<xsl:if test="$curr">
<xsl:variable name="runningsum" select="
$sum + $curr/#amount * $curr/#rate
" />
<xsl:call-template name="sum">
<xsl:with-param name="nodes" select="$nodes[position() > 1]" />
<xsl:with-param name="sum" select="$runningsum" />
</xsl:call-template>
</xsl:if>
<!-- if we don't have a node (last recursive step), return sum -->
<xsl:if test="not($curr)">
<xsl:value-of select="$sum" />
</xsl:if>
</xsl:template>
</xsl:stylesheet>
Gives:
<val>410</val>
The two <xsl:if>s can be replaced by a single <xsl:choose>. This would mean one less check during the recursion, but it also means two additional lines of code.
In plain XSLT 1.0 you need a recursive template for this, for example:
<xsl:template match="turnovers">
<xsl:variable name="selectedId" select="5" />
<xsl:call-template name="sum_turnover">
<xsl:with-param name="turnovers" select="turnover[#repid=$selectedId]" />
</xsl:call-template>
</xsl:template>
<xsl:template name="sum_turnover">
<xsl:param name="total" select="0" />
<xsl:param name="turnovers" />
<xsl:variable name="head" select="$turnovers[1]" />
<xsl:variable name="tail" select="$turnovers[position()>1]" />
<xsl:variable name="calc" select="$head/#amount * $head/#rate" />
<xsl:choose>
<xsl:when test="not($tail)">
<xsl:value-of select="$total + $calc" />
</xsl:when>
<xsl:otherwise>
<xsl:call-template name="sum_turnover">
<xsl:with-param name="total" select="$total + $calc" />
<xsl:with-param name="turnovers" select="$tail" />
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
This should do the trick, you'll need to do some further work to select the distinct repid's
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<xsl:variable name="totals">
<product>
<xsl:for-each select="turnovers/turnover">
<repid repid="{#repid}">
<value><xsl:value-of select="#amount * #rate"/></value>
</repid>
</xsl:for-each>
</product>
</xsl:variable>
<totals>
<total repid="5" value="{sum($totals/product/repid[#repid='5']/value)}"/>
</totals>
</xsl:template>
</xsl:stylesheet>
In XSLT 1.0 the use of FXSL makes such problems easy to solve:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:f="http://fxsl.sf.net/"
xmlns:ext="http://exslt.org/common"
exclude-result-prefixes="xsl f ext"
>
<xsl:import href="zipWith.xsl"/>
<xsl:output method="text"/>
<xsl:variable name="vMultFun" select="document('')/*/f:mult-func[1]"/>
<xsl:template match="/">
<xsl:call-template name="profitForId"/>
</xsl:template>
<xsl:template name="profitForId">
<xsl:param name="pId" select="1"/>
<xsl:variable name="vrtfProducts">
<xsl:call-template name="zipWith">
<xsl:with-param name="pFun" select="$vMultFun"/>
<xsl:with-param name="pList1" select="/*/*[#repid = $pId]/#amount"/>
<xsl:with-param name="pList2" select="/*/*[#repid = $pId]/#rate"/>
</xsl:call-template>
</xsl:variable>
<xsl:value-of select="sum(ext:node-set($vrtfProducts)/*)"/>
</xsl:template>
<f:mult-func/>
<xsl:template match="f:mult-func" mode="f:FXSL">
<xsl:param name="pArg1"/>
<xsl:param name="pArg2"/>
<xsl:value-of select="$pArg1 * $pArg2"/>
</xsl:template>
</xsl:stylesheet>
When this transformation is applied on the originally posted source XML document, the correct result is produced:
310
In XSLT 2.0 the same solution using FXSL 2.0 can be expressed by an XPath one-liner:
sum(f:zipWith(f:multiply(),
/*/*[xs:decimal(#repid) eq 1]/#amount/xs:decimal(.),
/*/*[xs:decimal(#repid) eq 1]/#rate/xs:decimal(.)
)
)
The whole transformation:
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:f="http://fxsl.sf.net/"
exclude-result-prefixes="f xs"
>
<xsl:import href="../f/func-zipWithDVC.xsl"/>
<xsl:import href="../f/func-Operators.xsl"/>
<!-- To be applied on testFunc-zipWith4.xml -->
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:template match="/">
<xsl:value-of select=
"sum(f:zipWith(f:multiply(),
/*/*[xs:decimal(#repid) eq 1]/#amount/xs:decimal(.),
/*/*[xs:decimal(#repid) eq 1]/#rate/xs:decimal(.)
)
)
"/>
</xsl:template>
</xsl:stylesheet>
Again, this transformation produces the correct answer:
310
Note the following:
The f:zipWith() function takes as arguments a function fun() (of two arguments) and two lists of items having the same length. It produces a new list of the same length, whose items are the result of the pair-wise application of fun() on the corresponding k-th items of the two lists.
f:zipWith() as in the expression takes the function f:multiply() and two sequences of corresponding "ammount" and "rate" attributes. The sesult is a sequence, each item of which is the product of the corresponding "ammount" and "rate".
Finally, the sum of this sequence is produced.
There is no need to write an explicit recursion and it is also guaranteed that the behind-the scenes recursion used within f:zipWith() is never going to crash (for all practical cases) with "stack overflow"
<?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"
exclude-result-prefixes="xs"
version="2.0">
<xsl:variable name="repid" select="5" />
<xsl:template match="/">
<xsl:value-of select=
"sum(for $x in /turnovers/turnover[#repid=$repid] return $x/#amount * $x/#rate)"/>
</xsl:template>
</xsl:stylesheet>
You can do this if you just need the value and not xml.
The easiest way to do it in XSLT is probably to use programming language bindings, so that you can define your own XPath functions.

XPath 1.0 max function on variables using EXSLT

I am serching an XPath function that works like the XPath 2.0 fn:max function. A function that returns the maximum of several parameters.
After searching a lot I figured out this way:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:math="http://exslt.org/math"
xmlns:exslt="http://exslt.org/common"
xmlns:func="http://exslt.org/functions"
xmlns:my="http://myns.com"
extension-element-prefixes="math exslt func">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:template match="/">
<root>
<xsl:value-of select="my:max(1,2)"/>
</root>
</xsl:template>
<func:function name="my:max">
<xsl:param name="e1"/>
<xsl:param name="e2"/>
<xsl:variable name="x">
<val><xsl:value-of select="$e1"/></val>
<val><xsl:value-of select="$e2"/></val>
</xsl:variable>
<func:result select="math:max(exslt:node-set($x)/val)"/>
</func:function>
</xsl:stylesheet>
Is it possible to do it so that my max function can take more elements?
Cheers
Jan
I don't have my XSLT 1.0 book in front of me, but I think the key here is that you can select 'node sets' and set those equal to your parameter variable, rather than having one-node-per-parameter.
Here's a rough guess:
<xsl:template match="/">
<root>
<xsl:call-template name="max">
<xsl:with-param name="values">
<val>1</val>
<val>2</val>
<val>3</val>
</xsl:with-param>
</xsl:call-template>
</root>
</xsl:template>
<func:function name="my:max">
<xsl:param name="x"/>
<func:result select="math:max($x/val/*)"/>
</func:function>
edit: re-read the question along with some XSLT 1.0 guidance. It should resemble the other answer, simplified only slightly. Keep in mind that if the numbers you want come from the XML data, you can use the select= attribute on xsl:with-param to automatically select the nodes you want to compare.
Presumably you could specify the xml (for the node-set) as the input argument?
I'm not "up" on exslt, but using msxsl (just for the node-set function, which is also in exslt):
<xsl:template name="max">
<xsl:param name="values"/>
<xsl:for-each select="msxsl:node-set($values)/val">
<xsl:sort data-type="number" order="descending"/>
<xsl:if test="position()=1">
<xsl:value-of select="."/>
</xsl:if>
</xsl:for-each>
</xsl:template>
...
<xsl:call-template name="max">
<xsl:with-param name="values">
<val>13</val>
<val>123</val>
<val>18</val>
</xsl:with-param>
</xsl:call-template>
Thank you for your ideas.
You helped me to understand everything a bit better. But my original intention was to get a
handy XPath function that works on xsl variables.
<!-- works with XPath 2.0 -->
<xst:template match="img/#height">
<xsl:variable name="$maximageheight" select="200">
<xsl:value-of select="fn:max( $maximageheight , . )"/>
</xsl:template>
<!-- until now the only way I see to do the same in XSL 1.0 -->
<xst:template match="img/#height">
<xsl:variable name="$maximageheight" select="200">
<xsl:call-template name="max">
<xsl:with-param name="values">
<val>$maximageheight</val>
<val><xsl:value-of select="."/></val>
</xsl:with-param>
</xsl:call-template>
</xsl:template>
For a fixed umber of parameters it would be possible to implement an exslt function:
<func:function name="my:max" xmlns:func="http://exslt.org/functions">
<xsl:param name="e1"/>
<xsl:param name="e2"/>
<xsl:variable name="x">
<val><xsl:value-of select="$e1"/></val>
<val><xsl:value-of select="$e2"/></val>
</xsl:variable>
<func:result select="math:max(exslt:node-set($x)/val)"/>
</func:function>
But i do not see a way to implement it vor a variable number of parameters.