Increment the repeated value in xslt inside for-each - xslt

Here I want to count/Increment the repeated emp_no using for each.
and I just want to concatenate emp_no with duplicate no count..
Here is my XSLT style sheet.
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:map="http://www.w3.org/2005/xpath-functions/map"
xmlns:array="http://www.w3.org/2005/xpath-functions/array" exclude-result-prefixes="#all"
version="3.0">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:param name="input"/>
<xsl:variable name="newline" select="'
'"/>
<xsl:template match="/" name="xsl:initial-template">
<xsl:variable name="input-as-map" select="parse-json($input)" as="map(*)"/>
<root>
<file1>
<xsl:for-each select="$input-as-map?file1?*">
<xsl:value-of select="$newline"/>
<xsl:value-of select="concat(?emp_no,',',count(?emp_no))"/>
<xsl:value-of select="$newline"/>
</xsl:for-each>
</file1>
</root>
</xsl:template>
</xsl:stylesheet>
input json:-
{
"file1": [
{
"emp_no": "0002"
},
{
"emp_no": "0009"
},
{
"emp_no": "0002"
},
{
"emp_no": "0009"
},
{
"emp_no": "0009"
}
]
}
Expected output:-
<root>
<file1>
0002,1
0009,1
0002,2
0009,2
0009,3
</file1>
</root>
Any suggestions also would be helpful.

One solution would be to replace the xsl:for-each with xsl:iterate, maintaining a parameter value containing a map from emp_no to an integer counter.
Another would be to convert the array to XML and use xsl:number.
Or you could use fn:fold-left(), again maintaining a map from emp_no to an integer counter as you step through the array.
None of them particularly easy, I'm afraid: perhaps someone else has a better idea.

Here is Mike's third suggestion spelled out:
<file>
<xsl:value-of
select="fold-left(
parse-json($json)?file1?*,
map{},
function($am, $m) {
let $c := head($am)($m?emp_no),
$new-am := map:put(head($am), $m?emp_no, if (empty($c)) then 1 else $c + 1)
return ($new-am, tail($am), $m?emp_no || ',' || $new-am($m?emp_no))
}
) => tail()"
separator="
"/>
</file>

Related

Date time conversion results in FORG0001 Invalid dateTime value - XSLT 3.0

Resources:
Java 1.8
Saxon-HE-10.3
XSLT3.0
I am merging 2 XML files and want to do date manipulation in the merged output file xml.
I am taking the 2 files as input using java and generate the merged xml - This works fine.
Now, I added the date manipulation in the xsl after the merging operation.
Date Manipulation - convert the given date to epoch/milliseconds
Date format which comes with the input file
<PROP NAME="START_DATE">
<PVAL>16-Aug-2018</PVAL>
</PROP>
The issue is when I try to convert the given date to dateTime or convert to epoch, I get the below error on compilation.
Error at char 10 in expression in xsl:value-of/#select on line 60 column 105 of CASTransform.xsl: FORG0001 Invalid dateTime value "/01--20T00:00:00" (Non-numeric year component) at template reformat-date on line 55 of CASTransform.xsl:
invoked by xsl:call-template at file:/home/Merger/scripts/CASTransform.xsl#50 In template rule with match="element(Q{}RECORDS)/element(Q{}RECORD)/element(Q{}PROP)[(Q{http://www.w3.org/2001/XMLSchema}string(data(attribute::attribute(Q{}NAME)))) eq "START_DATE"]/element(Q{}PVAL)" on line 48 of CASTransform.xsl
invoked by xsl:apply-templates at file:/home/Merger/scripts/CASTransform.xsl#45 In template rule with match="(element()|(text()|(comment()|processing-instruction())))" on line 43 of CASTransform.xsl
invoked by xsl:apply-templates at file:/home/Merger/scripts/CASTransform.xsl#35 at template main on line 14 of CASTransform.xsl: Exception in thread "main" net.sf.saxon.s9api.SaxonApiException: Invalid dateTime value "/01--20T00:00:00" (Non-numeric year component)
at net.sf.saxon.s9api.Xslt30Transformer.callTemplate(Xslt30Transformer.java:488)
XSL Code
<xsl:stylesheet version="3.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:map="http://www.w3.org/2005/xpath-functions/map"
xmlns:array="http://www.w3.org/2005/xpath-functions/array"
exclude-result-prefixes="#all">
<xsl:mode on-no-match="shallow-copy"/>
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:param name="mapData"/>
<xsl:template match="/" name="main">
<RECORDS>
<xsl:variable name="file1" select="map:get($mapData, '1')" />
<xsl:variable name="input-doc1" as="document-node()" select="doc($file1)"/>
<xsl:variable name="file2" select="map:get($mapData, '2')" />
<xsl:variable name="input-doc2" as="document-node()" select="doc($file2)"/>
<xsl:merge>
<xsl:merge-source name="doc1" select="$input-doc1/RECORDS/RECORD">
<xsl:merge-key select="PROP[#NAME = 'Id']/PVAL"></xsl:merge-key>
</xsl:merge-source>
<xsl:merge-source name="doc2" select="$input-doc2/RECORDS/RECORD">
<xsl:merge-key select="PROP[#NAME = 'Id']/PVAL"></xsl:merge-key>
</xsl:merge-source>
<xsl:merge-action>
<xsl:if test="current-merge-group('doc1')">
<xsl:copy>
<xsl:apply-templates select="*, current-merge-group('doc2')/(* except PROP[#NAME = 'Id'])"/>
</xsl:copy>
</xsl:if>
</xsl:merge-action>
</xsl:merge>
</RECORDS>
</xsl:template>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()" />
</xsl:copy>
</xsl:template>
<xsl:template match="RECORDS/RECORD/PROP[#NAME = 'START_DATE']/PVAL | RECORDS/RECORD/PROP[#NAME = 'END_DATE']/PVAL">
<xsl:element name="{name()}">
<xsl:call-template name="reformat-date">
<xsl:with-param name="date" select="." />
</xsl:call-template>
</xsl:element>
</xsl:template>
<xsl:template name="reformat-date">
<xsl:param name="date" />
<xsl:variable name="dd" select="substring($date,1,2)" />
<xsl:variable name="mmm" select="upper-case(substring($date,4,3))" />
<xsl:variable name="yyyy" select="substring($date,8,4)" />
<xsl:variable name="mmm">
<xsl:choose>
<xsl:when test="$mmm = 'JAN'">01</xsl:when>
<xsl:when test="$mmm = 'FEB'">02</xsl:when>
<xsl:when test="$mmm = 'MAR'">03</xsl:when>
<xsl:when test="$mmm = 'APR'">04</xsl:when>
<xsl:when test="$mmm = 'MAY'">05</xsl:when>
<xsl:when test="$mmm = 'JUN'">06</xsl:when>
<xsl:when test="$mmm = 'JUL'">07</xsl:when>
<xsl:when test="$mmm = 'AUG'">08</xsl:when>
<xsl:when test="$mmm = 'SEP'">09</xsl:when>
<xsl:when test="$mmm = 'OCT'">10</xsl:when>
<xsl:when test="$mmm = 'NOV'">11</xsl:when>
<xsl:when test="$mmm = 'DEC'">12</xsl:when>
</xsl:choose>
</xsl:variable>
<xsl:variable name="temp1" as="xs:string" select="string-join(($yyyy,$mmm,$dd),'-')" />
<xsl:value-of select="$temp1" />
</xsl:template>
</xsl:stylesheet>
The above XSL code works perfectly fine and the $temp1 gives proper date like 2018-08-16.
Now, when I try to convert this to dateTime variable I get the mentioned compilation error.
Code which gives error:
<xsl:variable name="temp2" as="xs:dateTime" select="xs:dateTime(concat($temp1,'T00:00:00'))"/>
and tried this as well :
<xsl:value-of select="floor((xs:dateTime(concat($temp1,'T00:00:00')) - xs:dateTime('1970-01-01T00:00:00')) div xs:dayTimeDuration('PT1S')) "/>
Requesting your help as this is my first XSL project.
Full Stacktrace:
Error at char 10 in expression in xsl:value-of/#select on line 60 column 105 of CASTransform.xsl:
FORG0001 Invalid dateTime value "/01--20T00:00:00" (Non-numeric year component)
at template reformat-date on line 55 of CASTransform.xsl:
invoked by xsl:call-template at file:/home/Merger/scripts/CASTransform.xsl#50
In template rule with match="element(Q{}RECORDS)/element(Q{}RECORD)/element(Q{}PROP)[(Q{http://www.w3.org/2001/XMLSchema}string(data(attribute::attribute(Q{}NAME)))) eq "START_DATE"]/element(Q{}PVAL)" on line 48 of CASTransform.xsl
invoked by xsl:apply-templates at file:/home/Merger/scripts/CASTransform.xsl#45
In template rule with match="(element()|(text()|(comment()|processing-instruction())))" on line 43 of CASTransform.xsl
invoked by xsl:apply-templates at file:/home/Merger/scripts/CASTransform.xsl#35
at template main on line 14 of CASTransform.xsl:
Exception in thread "main" net.sf.saxon.s9api.SaxonApiException: Invalid dateTime value "/01--20T00:00:00" (Non-numeric year component)
at net.sf.saxon.s9api.Xslt30Transformer.callTemplate(Xslt30Transformer.java:488)
at com.tracer.Merger.CASExportInput.trans(CASExportInput.java:38)
at com.tracer.Merger.CASExportInput.main(CASExportInput.java:58)
Caused by: ValidationException: Invalid dateTime value "/01--20T00:00:00" (Non-numeric year component)
at net.sf.saxon.type.ValidationFailure.makeException(ValidationFailure.java:406)
at net.sf.saxon.expr.CastExpression.doCast(CastExpression.java:385)
at net.sf.saxon.expr.CastExpression.evaluateItem(CastExpression.java:402)
at net.sf.saxon.expr.CastExpression.evaluateItem(CastExpression.java:30)
at net.sf.saxon.expr.Expression.iterate(Expression.java:872)
at net.sf.saxon.expr.AtomicSequenceConverter.iterate(AtomicSequenceConverter.java:304)
at net.sf.saxon.expr.Expression.process(Expression.java:949)
at net.sf.saxon.expr.instruct.ValueOf.processLeavingTail(ValueOf.java:340)
at net.sf.saxon.expr.LetExpression.processLeavingTail(LetExpression.java:746)
at net.sf.saxon.expr.instruct.Block.processLeavingTail(Block.java:752)
at net.sf.saxon.expr.instruct.NamedTemplate.expand(NamedTemplate.java:264)
at net.sf.saxon.expr.instruct.CallTemplate$CallTemplatePackage.processLeavingTail(CallTemplate.java:549)
at net.sf.saxon.trans.Mode.applyTemplates(Mode.java:478)
at net.sf.saxon.expr.instruct.ApplyTemplates.apply(ApplyTemplates.java:351)
at net.sf.saxon.expr.instruct.ApplyTemplates.process(ApplyTemplates.java:285)
at net.sf.saxon.expr.instruct.ElementCreator.processLeavingTail(ElementCreator.java:352)
at net.sf.saxon.expr.instruct.Copy.processLeavingTail(Copy.java:429)
at net.sf.saxon.expr.instruct.TemplateRule.applyLeavingTail(TemplateRule.java:384)
at net.sf.saxon.trans.Mode.applyTemplates(Mode.java:568)
at net.sf.saxon.expr.instruct.ApplyTemplates.apply(ApplyTemplates.java:351)
at net.sf.saxon.expr.instruct.ApplyTemplates.process(ApplyTemplates.java:285)
at net.sf.saxon.expr.instruct.ElementCreator.processLeavingTail(ElementCreator.java:352)
at net.sf.saxon.expr.instruct.Copy.processLeavingTail(Copy.java:429)
at net.sf.saxon.expr.instruct.Instruction.process(Instruction.java:142)
at net.sf.saxon.expr.parser.ExpressionTool.getIteratorFromProcessMethod(ExpressionTool.java:643)
at net.sf.saxon.expr.instruct.Instruction.iterate(Instruction.java:374)
at net.sf.saxon.expr.instruct.Choose.iterate(Choose.java:1019)
at net.sf.saxon.expr.sort.MergeInstr.lambda$iterate$0(MergeInstr.java:543)
at net.sf.saxon.expr.ContextMappingIterator.next(ContextMappingIterator.java:61)
at net.sf.saxon.om.SequenceIterator.forEachOrFail(SequenceIterator.java:135)
at net.sf.saxon.expr.sort.MergeInstr.processLeavingTail(MergeInstr.java:823)
at net.sf.saxon.expr.instruct.Instruction.process(Instruction.java:142)
at net.sf.saxon.expr.LetExpression.process(LetExpression.java:625)
at net.sf.saxon.expr.instruct.ElementCreator.processLeavingTail(ElementCreator.java:352)
at net.sf.saxon.expr.instruct.ElementCreator.processLeavingTail(ElementCreator.java:298)
at net.sf.saxon.expr.instruct.NamedTemplate.expand(NamedTemplate.java:264)
at net.sf.saxon.trans.XsltController.callTemplate(XsltController.java:850)
at net.sf.saxon.s9api.Xslt30Transformer.callTemplate(Xslt30Transformer.java:480)
... 2 more
Java code which serves the Input files :
package com.tracer.Merger.CASMerger;
import java.io.File;
import java.util.HashMap;
import java.util.Map;
import javax.xml.transform.stream.StreamSource;
import net.sf.saxon.s9api. * ;
public class CASExportInput {
public static void trans(String XSLFile, String File1, String File2, String OutputFileName, String OutputFilePath) throws SaxonApiException {
String MergedFile = OutputFilePath.concat(OutputFileName);
Processor processor = new Processor(false);
XsltCompiler compiler = processor.newXsltCompiler();
XsltExecutable exp = compiler.compile(new StreamSource(new File(XSLFile)));
Serializer out = processor.newSerializer(new File(MergedFile));
out.setOutputProperty(Serializer.Property.METHOD, "xml");
out.setOutputProperty(Serializer.Property.INDENT, "yes");
Xslt30Transformer trans = exp.load30();
Map < String,
String > mapData = new HashMap < String,
String > ();
mapData.put("1", File1);
mapData.put("2", File2);
HashMap < QName,
XdmValue > parameters = new HashMap < >();
parameters.put(new QName("mapData"), XdmMap.makeMap(mapData));
trans.setStylesheetParameters(parameters);
trans.callTemplate(new QName("main"), out);
System.out.println("Output written to : " + MergedFile);
}
public static void main(String args[]) throws SaxonApiException {
if (args.length < 3) System.out.println("\nPlease check if you have entered arguments properly...");
String XSLFile = args[0].toString().trim();
String File1 = args[1].toString().trim();
String File2 = args[2].toString().trim();
String FileName1 = File1.substring(File1.lastIndexOf("/") + 1, File1.length() - 4);
String FileName2 = File2.substring(File2.lastIndexOf("/") + 1, File2.length() - 4);
String OutputFilePath = File1.substring(0, File1.lastIndexOf("/") + 1);
String OutputFileName = FileName1.concat("-" + FileName2 + ".xml");
trans(XSLFile, File1, File2, OutputFileName, OutputFilePath);
}
}
Not sure what the issue was. But, I uninstalled and re-installed Java 1.8 and the issue got fixed. No changes were done in the xsl or java code.

Convert negative decimal to hexadecimal in xslt

Please, can you help me how to convert negative/positive decimal value to hexadecimal in xslt/xslt 2.0 ?
This is what i tried but does not work with negative numbers/decimals,
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template name="ConvertDecToHex">
<xsl:param name="index" />
<xsl:if test="$index > 0">
<xsl:call-template name="ConvertDecToHex">
<xsl:with-param name="index" select="floor($index div 16)" />
</xsl:call-template>
<xsl:choose>
<xsl:when test="$index mod 16 < 10">
<xsl:value-of select="$index mod 16" />
</xsl:when>
<xsl:otherwise>
<xsl:choose>
<xsl:when test="$index mod 16 = 10">A</xsl:when>
<xsl:when test="$index mod 16 = 11">B</xsl:when>
<xsl:when test="$index mod 16 = 12">C</xsl:when>
<xsl:when test="$index mod 16 = 13">D</xsl:when>
<xsl:when test="$index mod 16 = 14">E</xsl:when>
<xsl:when test="$index mod 16 = 15">F</xsl:when>
<xsl:otherwise>A</xsl:otherwise>
</xsl:choose>
</xsl:otherwise>
</xsl:choose>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
This 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 method="text"/>
<xsl:template match="/">
<xsl:value-of select="my:intToHex(53), my:intToHex(-53)" separator="
"/>
</xsl:template>
<xsl:function name="my:intToHex" as="xs:string">
<xsl:param name="pInt" as="xs:integer"/>
<xsl:variable name="vMinusOneHex64" select="18446744073709551615"/>
<xsl:variable name="vCompl" select=
"if($pInt ge 0)
then $pInt
else $vMinusOneHex64 + $pInt +1"/>
<xsl:sequence select=
"if ($vCompl eq 0)
then '0'
else
concat(if ($vCompl gt 16)
then my:intToHex($vCompl idiv 16)
else '',
substring('0123456789ABCDEF',
($vCompl mod 16) +1,
1)
)"/>
</xsl:function>
</xsl:stylesheet>
When applied on any source XML document (ignored), produces the desired result:
35
FFFFFFFFFFFFFFCB

Calling function for filtering

This function "karusell:isCurrentDateTimeBetweenDates" never gets called. Why cant i use it for filtering? If a call i alone and store it in a variable it work. The value of the variable is false.
<xsl:variable name="bannerList" select="verticaldata/contents/content[ karusell:isCurrentDateTimeBetweenDates( 'Thursday', '01' , 'Friday', '03') ][position() <= 5]" />
Edit:
How can the string be returned?
The filtering
The function
<xsl:function name="karusell:isCurrentDateTimeBetweenDates">
<xsl:param name="startDay"/>
<xsl:param name="startHour" />
<xsl:param name="endDay"/>
<xsl:param name="endHour" />
<xsl:variable name="currentDateTime" select="current-dateTime() " />
<xsl:variable name="todayIndex" select="karusell:getDayIndex(format-dateTime($currentDateTime , '[F]'))" />
<xsl:variable name="startDayIndex" select="karusell:getDayIndex($startDay)" />
<xsl:variable name="endDayIndex" select="karusell:getDayIndex($endDay)" />
<xsl:variable name="startDate" select= "$currentDateTime - ( $todayIndex - $startDayIndex )*xs:dayTimeDuration('P1D')"/>
<xsl:variable name="endDate" select= "$currentDateTime + ( $endDayIndex - $todayIndex )*xs:dayTimeDuration('P1D')"/>
<xsl:variable name="startDateTime" select="format-dateTime($startDate, concat('[Y0001]-[M01]-[D01]T', $startHour ,':00:00')) cast as xs:dateTime"/>
<xsl:variable name="endDateTime" select="format-dateTime($endDate, concat('[Y0001]-[M01]-[D01]T', $endHour ,':00:00')) cast as xs:dateTime"/>
<xsl:value-of select="$currentDateTime >= $startDateTime and $currentDateTime < $endDateTime" />
</xsl:function>
Use xsl:sequence, not xsl:value-of to return values of the data type your expression has. So replace
<xsl:value-of select="$currentDateTime >= $startDateTime and $currentDateTime < $endDateTime" />
with
<xsl:sequence select="$currentDateTime >= $startDateTime and $currentDateTime < $endDateTime" />
Additionally you can get better error diagnosis if you define the return type of your function with <xsl:function name="pf:foo" as="xs:boolean">..</xsl:function>.

Math results in scientific float and cannot calculate with it anymore

I am trying to make an xslt that can convert a position (vector3) via a quaternion to a new position. I have made the following set-up, but I am retrieving NaN's for very small values close to 0. How can I calculate further with the values that come from the quaternion to right-vector calculations?
<xsl:template name="object_markingtape_position">
<xsl:param name="sign"/> <!-- left or right (-1 or 1) -->
<xsl:param name="quaternion"/> <!-- quaternion in x,y,z,w -->
<xsl:variable name="rightvec">
<xsl:call-template name="QuatToRight">
<xsl:with-param name="x" select="$quaternion/ns:x"/>
<xsl:with-param name="y" select="$quaternion/ns:y"/>
<xsl:with-param name="z" select="$quaternion/ns:z"/>
<xsl:with-param name="w" select="$quaternion/ns:w"/>
</xsl:call-template>
</xsl:variable>
Right vector y: <xsl:value-of select="number($rightvec/y)"/> <!-- results in a value with 1.5435434E-04 -->
<xsl:element name="position" namespace="{$ns}">
<xsl:element name="x" namespace="{$ns}">
<xsl:value-of select="$sign * 1.5 * $rightvec/x + ns:position/ns:x"/>
</xsl:element>
<xsl:element name="y" namespace="{$ns}"> <!-- results into NaN -->
<xsl:value-of select="$sign * 1.5 * $rightvec/y + ns:position/ns:y"/>
</xsl:element>
<xsl:element name="z" namespace="{$ns}">
<xsl:value-of select="$sign * 1.5 * $rightvec/z + ns:position/ns:z"/>
</xsl:element>
</xsl:element>
</xsl:template>
<!-- Functionality for calculating the vectors from a quaternion -->
<!-- From http://nic-gamedev.blogspot.nl/2011/11/quaternion-math-getting-local-axis.html -->
<xsl:template name="QuatToRight">
<xsl:param name="x"/>
<xsl:param name="y"/>
<xsl:param name="z"/>
<xsl:param name="w"/>
<xsl:element name="vec3">
<xsl:element name="x">
<xsl:value-of select="1 - 2 * ($x * $y - $w * $z)"/>
</xsl:element>
<xsl:element name="y">
<xsl:value-of select="2 * ($x * $y + $w * $z)"/>
</xsl:element>
<xsl:element name="z">
<xsl:value-of select="2 * ($x * $z - $w * $y)"/>
</xsl:element>
</xsl:element>
</xsl:template>
An example of xml values that can come in are the following:
<Item>
<position>
<x>-106.6172</x>
<y>0.780673563</y>
<z>-13.0446815</z>
</position>
<rotation> <!-- this is where the quaternion param is filled with -->
<x>0.0810996</x>
<y>0.354339659</y>
<z>-0.207844481</z>
<w>-0.908111751</w>
</rotation>
</Item>
If you can't find a better solution you might have to resort to using extension functions.
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:quat="urn:myExtension" xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="quat msxsl" version="1.0">
<xsl:param name="x"/>
<xsl:param name="y"/>
<xsl:param name="z"/>
<xsl:param name="w"/>
<xsl:template match="*">
<xsl:value-of select="quat:calcQuat($x, $y, $z, $w)"/>
</xsl:template>
<msxsl:script implements-prefix="quat" language="c#">
<![CDATA[
public string calcQuat(double x, double y, double z, double w)
{
double result = 1 - 2 * (x * y - w * z)
return Double.ToString(result)
}
]]>
</msxsl:script>
There are three problems I can see:
the variable rightvec contains an XML fragment, but is then used as a node-set - this requires a conversion to work usually (but it depends on the XSLT processor)
the named template object_markingtape_position references an element ns:position in the current context, so its result depends on where it is called - better to pass the position as a parameter;
the named tamplate QuatToRight generates a vec3 element containing sub-elements with the values, but its result is then used as if there was not vec3 intermediate element.
Using the .NET XSLT processor, this XSLT:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl"
>
<xsl:output method="xml" indent="yes"/>
<xsl:template name="object_markingtape_position">
<xsl:param name="sign"/> <!-- left or right (-1 or 1) -->
<xsl:param name="quaternion"/> <!-- quaternion in x,y,z,w -->
<xsl:param name="position"/> <!-- position in x,y,z -->
<xsl:variable name="rightvecFragment">
<xsl:call-template name="QuatToRight">
<xsl:with-param name="x" select="$quaternion/x"/>
<xsl:with-param name="y" select="$quaternion/y"/>
<xsl:with-param name="z" select="$quaternion/z"/>
<xsl:with-param name="w" select="$quaternion/w"/>
</xsl:call-template>
</xsl:variable>
<xsl:variable name="rightvec" select="msxsl:node-set($rightvecFragment)"/>
<xsl:element name="position">
<xsl:element name="x">
<xsl:value-of select="$sign * 1.5 * $rightvec/x + $position/x"/>
</xsl:element>
<xsl:element name="y">
<!-- results into NaN -->
<xsl:value-of select="$sign * 1.5 * $rightvec/y + $position/y"/>
</xsl:element>
<xsl:element name="z">
<xsl:value-of select="$sign * 1.5 * $rightvec/z + $position/z"/>
</xsl:element>
</xsl:element>
</xsl:template>
<!-- Functionality for calculating the vectors from a quaternion -->
<!-- From http://nic-gamedev.blogspot.nl/2011/11/quaternion-math-getting-local-axis.html -->
<xsl:template name="QuatToRight">
<xsl:param name="x"/>
<xsl:param name="y"/>
<xsl:param name="z"/>
<xsl:param name="w"/>
<xsl:element name="x">
<xsl:value-of select="1 - 2 * ($x * $y - $w * $z)"/>
</xsl:element>
<xsl:element name="y">
<xsl:value-of select="2 * ($x * $y + $w * $z)"/>
</xsl:element>
<xsl:element name="z">
<xsl:value-of select="2 * ($x * $z - $w * $y)"/>
</xsl:element>
</xsl:template>
<xsl:template match="/*">
<xsl:call-template name="object_markingtape_position">
<xsl:with-param name="sign" select="1"/>
<xsl:with-param name="position" select="position"/>
<xsl:with-param name="quaternion" select="rotation"/>
</xsl:call-template>
</xsl:template>
</xsl:stylesheet>
applied to the example input produces:
<position>
<x>-104.63717236709732</x>
<y>1.4331220235568977</y>
<z>-12.129909788264223</z>
</position>
I resolved this with some data loss, but it is the cleanest solution so far. I used format-number().
<xsl:template name="QuatToRight">
<xsl:param name="x"/>
<xsl:param name="y"/>
<xsl:param name="z"/>
<xsl:param name="w"/>
<xsl:element name="vec3">
<xsl:element name="x">
<xsl:value-of select="format-number(1 - 2 * ($x * $y - $w * $z),'0.000000')"/>
</xsl:element>
<xsl:element name="y">
<xsl:value-of select="format-number(2 * ($x * $y + $w * $z),'0.000000')"/>
</xsl:element>
<xsl:element name="z">
<xsl:value-of select="format-number(2 * ($x * $z - $w * $y),'0.000000')"/>
</xsl:element>
</xsl:element>
</xsl:template>

Is there an exclusive OR 'XOR' in XPath?

Is there an exclusive OR 'XOR' in XPath1.0 ?
Use this XPath 1.0 expression:
x and not(y) or y and not(x)
Always try to avoid the != operator, because it has an unexpected meaning/behavior when one or both of its arguments are node-sets.
In XSLT 2.0 or XQuery 1.0 one can write this as a function and then use just the function in any XPath expression. Below is an XSLT 2.0 function definition for xor and a small example of using this function:
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:f="my:f">
<xsl:output method="text"/>
<xsl:template match="/">
<xsl:sequence select=
"for $x in (true(), false()),
$y in (true(), false())
return
('xor(', $x, ',', $y,') = ', f:xor($x, $y), '
')
"/>
</xsl:template>
<xsl:function name="f:xor">
<xsl:param name="pX" as="xs:boolean"/>
<xsl:param name="pY" as="xs:boolean"/>
<xsl:sequence select=
"$pX and not($pY) or $pY and not($pX)"/>
</xsl:function>
</xsl:stylesheet>
when this transformation is applied on any XML document (not used), the wanted, correct result is produced:
xor( true , true ) = false
xor( true , false ) = true
xor( false , true ) = true
xor( false , false ) = false
No but you can emulate it:
(a or b) and (a != b)
number($boolean_var) converts true() to 1 and false() to 0. (Note that true alone addresses a node!!)
boolean($numeric_var) converts 1 to true() and 0 to false().
Therefore, XOR can be accomplished by:
boolean((number($v1) + number($v2) + number($v3)) mod 2)
i.e. least-significant-bit addition using the mod 2 operator. Yes, XPATH is cumbersome.