What is the ROUND function behavior in Orbeon Forms? - orbeon

In Orbeon Forms (dev-post-3.7.1.200911140400) we have code in an XPL that do some calculation, and part of this calculation is we ROUND the result to 2 decimal places. Below is an example of the code we use to do the calculation:
<xsl:when test="$total_c_w != 0">
<gpa><xsl:value-of select="(round(($total_p_c_w div $total_c_w) * 100) div 100)"/></gpa>
</xsl:when>
According to the standard XPATH documentation on the ROUND function; Rounds a numeric value to the nearest whole number, rounding x.5 towards positive infinity.
But we encounter a case where the ROUND function is rounding 237.5 to 237, instead of 238. This is just an example, there are other cases where a similar problem involving x.5 is happening.
For example mention, the values in the calculation are:
$total_p_c_w = 7.6, $total_c_w = 3.2
=====================================================
Alex,
Thanks for the guidance. I did some more debugging and found something weird please refer to the following XSL code that I tested with on the latest Orbeon Forms 3.9.0.201105152046 CE.
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema" version="2.0">
<xsl:template match="/">
<main>
<xsl:variable name="tppcw" select="/root/studentpcw/total_p_c_w" as="xs:double"/>
<xsl:variable name="tccw" select="/root/studentpcw/total_c_w" as="xs:double"/>
<xsl:variable name="tpcw" select="sum(/root/studentpcw/total_p_c_w)"/>
<xsl:variable name="tcw" select="sum(/root/studentpcw/total_c_w)"/>
<xsl:variable name="total_p_c_w" select="7.6"/>
<xsl:variable name="total_c_w" select="3.2"/>
<var1><xsl:value-of select="sum(/root/studentpcw/total_p_c_w)"/></var1>
<var2><xsl:value-of select="sum(/root/studentpcw/total_c_w)"/></var2>
<var3><xsl:value-of select="$total_p_c_w"/></var3>
<var4><xsl:value-of select="$total_c_w"/></var4>
<result1>
<xsl:value-of select="round(($total_p_c_w div $total_c_w) * 100)"/>
</result1>
<result2>
<xsl:value-of select="round(($tppcw div $tccw) * 100)"/>
</result2>
</main>
</xsl:template>
</xsl:transform>
Apply the above code at this sample document:
<root>
<studentpcw>
<total_p_c_w>7.6</total_p_c_w>
<total_c_w>3.2</total_c_w>
</studentpcw>
</root>
The result is quite unexpected;
<main xmlns:xs="http://www.w3.org/2001/XMLSchema">
<var1>7.6</var1>
<var2>3.2</var2>
<var3>7.6</var3>
<var4>3.2</var4>
<result1>238</result1>
<result2>237</result2>
</main>
The problem is that if I assign a literal number to the variable and use that variable in the rounding function, the result is as I expected. If I select the value from a node and assign to the variable and use that variable in the rounding function, the result is wrong or unexpected.

I get a result of 238 running the following stylesheet in a nightly build through the XSLT sandbox (which, if you have Orbeon Forms installed locally, you can access through http://localhost:8080/orbeon/sandbox-transformations/xslt/).
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema" version="2.0">
<xsl:template match="/">
<result>
<xsl:variable name="total_p_c_w" select="7.6"/>
<xsl:variable name="total_c_w" select="3.2"/>
<xsl:value-of select="round(($total_p_c_w div $total_c_w) * 100)"/>
</result>
</xsl:template>
</xsl:transform>
I believe this is the result you expected, but that you might be getting something different with the version you are using. Could you try the above example in the XSLT sandbox of the version you are using? If you're getting 237 instead of 238, then this is a sign that this issue has been fixed, and I would then recommend you to upgrade to a newer version of Orbeon Forms (currently 3.9).
(Note that this is most likely not something in Orbeon Forms per se, but in the XSLT implementation Orbeon Forms uses, which is the excellent Saxon.)

Related

Exponent or power calculation in saxon9HE

Please suggest how to do math functions like power in XSLT2 with Saxon 9HE.
Getting following error:
Cannot find a matching 2-argument function named {http://exslt.org/math}power()
XML:
<root><num>12.3</num></root>
XSLT 2.0:
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:exsl="http://exslt.org/common"
xmlns:math="http://exslt.org/math"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
extension-element-prefixes="exsl math">
<xsl:output method="text" encoding="UTF-8"/>
<xsl:strip-space elements="*"/>
<!--1/(1+e^(-t))--><!-- this is required formula -->
<xsl:template match="num">
<xsl:variable name="varE"><xsl:value-of select="."/></xsl:variable>
<xsl:variable name="varT"><xsl:text>0.718</xsl:text></xsl:variable>
<xsl:variable name="varPower">
<xsl:value-of select="1 div (1 + math:power(number($varE), number(-$varT)))"/>
</xsl:variable>
<xsl:value-of select="$varPower"/>
</xsl:template>
</xsl:stylesheet>
There are XPath standardized math functions like math:pow e.g. math:pow(2, 4) in the namespace https://www.w3.org/2005/xpath-functions/math e.g with the namespace declaration xmlns:math="http://www.w3.org/2005/xpath-functions/math" available in all editions of Saxon (at least with 9.8 but I think it also works with earlier version like 9.7 and 9.6 (documentation http://saxonica.com/html/documentation9.6/functions/math/pow.html says since 9.6 in all editions).

xslt 2.0: read in text files via collection()

I have a bunch of text files that I'd like to process witth XSLT 2.0.
Here's how I try to read them in:
<xsl:variable name="input" select="collection(iri-to-uri('file:///.?select=*.txt'))" />
However, when I do this:
<xsl:message>
<xsl:sequence select="count($input)"/>
</xsl:message>
It outputs 0. No files are selected.
If I do it like this:
<xsl:variable name="input" select="collection(iri-to-uri('.?select=*.txt'))" />
I get the error that collection should return a node but is returning an xs:string.
What I would like do to is read each file and then iterate over each file and process the text, like this
<xsl:for-each select="unparsed-text($input, 'UTF-8')">
<!-- tokenizing, etc. -->
How would I do that?
You need the XPath 3.0 uri-collection function supported in version="3.0" stylesheet in Saxon 9.7 (all versions including HE) and 9.6 (commercial versions I think):
<xsl:template match="/" name="main">
<xsl:for-each select="uri-collection('.?select=*.txt')!unparsed-text(.)">
<xsl:message select="'Parsed:' || . || '
'"/>
</xsl:for-each>
</xsl:template>
collection is supposed to return a sequence of nodes while uri-collection can access other resources not parsable as XML.
With Altova XMLSpy respectively RaptorXML and XSLT 3.0 you can also use uri-collection, it seems the way to access all .txt files is a bit different from Saxon and you use uri-collection('*.txt') to access all .txt files in the directory.

Can I apply a character map to a given node?

If I look at the xslt specs it seems a character map applies to the whole document, bit is it also possible to use it on a given node, or within a template ?
Example : I have a node containing look up values, but they might contain characters that don't play well with regular expressions when using it in another template. For now I use a replace functionwhich works well,, but after a few characters that becomes pretty hard to read or maintain. So if I have something like this :
<xsl:variable name="myLookup" select="
replace(
replace(
replace(
replace(
string-join(/*/lookup/*, '|'),
'\[','\\['),
'\]','\\]'),
'\(','\\('),
'\)','\\)')
"/>
is there a way to achieve something like below fictitious example ?
<xsl:character-map name="escapechar">
<xsl:output-character character="[" string="\[" />
<xsl:output-character character="]" string="\]" />
<xsl:output-character character="(" string="\(" />
<xsl:output-character character=")" string="\)" />
</xsl:character-map>
<xsl:variable name="myLookup" select="string-join(/*/lookup/*, '|')" use-character-map="escapechar"/>
I know this is not working at all, it is just to make my request a bit visual.
Any idea ?
I think character maps in XSLT 2.0 are a serialization feature to be applied when a result tree is serialized to a file or stream so I don't see how you could apply one to a certain string or certain node during a transformation.
As for escaping meta characters of regular expression patterns, maybe http://www.xsltfunctions.com/xsl/functx_escape-for-regex.html helps.
Character maps is only a serialization feature, which means that it is only executed when the final output of a transformation is produced. However, you can significantly simplify your current code.
Just use:
replace($pStr, '(\[|\]|\(|\))','\\$1')
Here is a complete example:
<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" indent="yes"/>
<xsl:template match="/*">
<xsl:value-of select="my:escape(.)"/>
</xsl:template>
<xsl:function name="my:escape" as="xs:string">
<xsl:param name="pStr" as="xs:string"/>
<xsl:value-of select="replace($pStr, '(\[|\]|\(|\))','\\$1')"/>
</xsl:function>
</xsl:stylesheet>
When this transformation is applied on the following XML document:
<t>([a-z]*)</t>
the wanted, correct result is produced:
\(\[a-z\]*\)

XPath syntax error thrown when using evaluate() method

Still i am getting the below error
Error at xsl:param on line 6 of file:/E:/saxon/parastyleText.xsl:
XPST0003: XPath syntax error at char 0 on line 6 in {...le/#w:val[matches(., c
oncat...}:
Invalid character '^' in expression
Failed to compile stylesheet. 1 error detected.
Modified XSL:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:ve="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" xmlns:m="http://schemas.openxmlformats.org/officeDocument/2006/math" xmlns:v="urn:schemas-microsoft-com:vml" xmlns:wp="http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing" xmlns:w10="urn:schemas-microsoft-com:office:word" xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main" xmlns:wne="http://schemas.microsoft.com/office/word/2006/wordml">
<xsl:param name="styleName" select="'articletitle'"/>
<xsl:param name="tagName" select="'//w:p[w:pPr/w:pStyle/#w:val[matches(., concat('^(',$styleName,')$'),'i')]]'"/>
<xsl:output method="text"/>
<xsl:template match="/">
<xsl:value-of select="saxon:evaluate($tagName)" xmlns:saxon="http://saxon.sf.net/"/><xsl:text>
</xsl:text>
</xsl:template>
</xsl:stylesheet>
Please dont reply that, quotes will make 'tagName' as string and remove those quotes. This value will be actually passed from java as a string , tats y for testing purpose i have passed this xpath as string.
According to the online documentation http://www.saxonica.com/documentation9.1/extensions/functions.html Saxon 9.1 supports an evaluate function in the Saxon namespace http://saxon.sf.net/. So with Saxon 9.1 try <xsl:value-of select="saxon:evaluate($tagName)" xmlns:saxon="http://saxon.sf.net/"/>. Of course you can move the namespace declaration up to the xsl:stylesheet element if you want, I just put it on the xsl:value-of in this post for a short but complete sample of code.
Also note that with your variable named tagName it is likely that you simply have a single element name, in that case it might suffice to use <xsl:value-of select="*[local-name() eq $tagName]"/>.

xslt transform that selects element based upon negative value in multiple descendants

I have the following basic xml which I to parse to give the NAME only if none of the DB values = DB1.
<rnas>
<rna ID="1">
<NAME>Segment 6</NAME>
<XREF>
<ID>AF389120</ID>
<DB>DB1</DB>
</XREF>
<XREF>
<ID>ABCDE</ID>
<DB>DB2</DB>
</XREF>
</rna>
<rna ID="10">
<NAME>Segment 3</NAME>
<XREF>
<ID>12345</ID>
<DB>DB2</DB>
</XREF>
<XREF>
<ID>66789</ID>
<DB>DB3</DB>
</XREF>
</rna>
</rnas>
The expected output would be:
<rnas>
<rna ID="10">
<NAME>Segment 3</NAME>
</rna>
<rnas>
I am still a relative newbie and have tried a variety of approaches using XSLT 2.0 but so far have not been able to get anything to work properly. Any help would be much appreciated.
This will do what you want
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="rna[.//DB/text()='DB1']"/>
<xsl:template match="XREF"/>
</xsl:stylesheet>
It's an Identity Transform along with two empty templates. The first matches any rna that contains a DB with the text value DB1, and suppresses it. The second matches all XREF elements, which you do not want to output.

Resources