Saxon-ce with multiple user input controls - xslt-2.0

I am making controls on a web page (comboboxes) with Saxon-CE and Xslt 2.0. I am having trouble in passing the values of multiple control to a template that uses the values from that control to process the document. Here is what I have:
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:ixsl="http://saxonica.com/ns/interactiveXSLT"
extension-element-prefixes="ixsl">
<xsl:template match="/">
<xsl:result-document href="#comboBox1">
<select id="myBox1">
<option value="1">One</option>
<option value="2">two</option>
</select>
</xsl:result-document>
<xsl:result-document href="#comboBox2">
<select id="myBox2">
<option value="A">Letter-A</option>
<option value="B">Letter-B</option>
</select>
</xsl:result-document>
</xsl:template>
<xsl:template match="select[#id='myBox1'] mode=ixsl:onchange">
<xsl:variable name="control1" select="."/>
<xsl:variable name="numVal" select="ixsl:get($control1,'value')"/>
<xsl:call-template name="displayStuff">
<xsl:with-param name="field1" select="$numVal"/>
</xsl:call-template>
</xsl:template>
<xsl:template match="select[#id='myBox2'] mode=ixsl:onchange">
<xsl:variable name="control2" select="."/>
<xsl:variable name="letVal" select="ixsl:get($control2,'value')"/>
<xsl:call-template name="displayStuff">
<xsl:with-param name="field2" select="$letVal"/>
</xsl:call-template>
</xsl:template>
<xsl:template name="displayStuff">
<xsl:param name="field1" select="0"/>
<xsl:param name="field2" select="Z">
<xsl:result-document href="#display" method="ixsl:replace-content">
<xsl:text>Number: </xsl:text> <xsl:value-of select="$field1"/><br/>
<xsl:text>Letter: </xsl:text> <xsl:value-of select="$field2"/><br/>
</xsl:result-document>
</xsl:template>
</xsl:stylesheet>
The problem is that each control returns the correct value to the display template for an then item just changed, but not the other item.
For example if I select 1 in the first dropbox I get Number: 1 Letter: Z
but now if I change the vale of the second dropbox (for lets say to A) I get Number: 0 Letter:A.
How can I make sure what is passed to the display template is the current selected value of all the dropboxes, not the one just changed?

Instead of passing the current values of the controls as parameters to the displayStuff template, why not have that template access them directly from the HTML page by means of XPath expressions?
I suspect you could combine all this into a single template:
<xsl:template match="select[#id=('myBox1', 'myBox2')] mode=ixsl:onchange">
<xsl:variable name="control1" select="."/>
<xsl:variable name="numVal" select="ixsl:get(id('myBox1'),'value')"/>
<xsl:variable name="letVal" select="ixsl:get(id('myBox2'),'value')"/>
<xsl:result-document href="#display" method="ixsl:replace-content">
<xsl:text>Number: </xsl:text> <xsl:value-of select="$numVal"/><br/>
<xsl:text>Letter: </xsl:text> <xsl:value-of select="$letVal"/><br/>
</xsl:result-document>
</xsl:template>

Related

XSLT 2.0: using a regular expression to extract all matching parts to an array

How can I collect all parts of a string matching a regular expression pattern into an array?
<xsl:variable name="matches" select="function('abc_Xza_Y_Sswq', '_[A-Z]')"/>
returning
('_X', '_Y', '_S')
There are no arrays in XSLT/XPath 2.0, you could however write a function that uses analyze-string to return a sequence of strings:
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:mf="http://example.com/mf"
exclude-result-prefixes="xs mf">
<xsl:function name="mf:extract" as="xs:string*">
<xsl:param name="input" as="xs:string"/>
<xsl:param name="pattern" as="xs:string"/>
<xsl:analyze-string select="$input" regex="{$pattern}">
<xsl:matching-substring>
<xsl:sequence select="."/>
</xsl:matching-substring>
</xsl:analyze-string>
</xsl:function>
<xsl:template match="/">
<xsl:variable name="matches" select="mf:extract('abc_Xza_Y_Sswq', '_[A-Z]')"/>
<xsl:value-of select="$matches" separator=", "/>
</xsl:template>
</xsl:transform>

libxslt: compilation errors

I'm using libxslt on iOS, and I'm getting compilation errors from what should be straightforward xslt, such as:
compilation error: file /Users/yada/Library/Developer/CoreSimulator/Devices/ACE15E20-3230-4966-ACDE-DCADABF48B32/data/Containers/Bundle/Application/A4EC9B34-3A96-42E0-B58F-6EB44C942874/appname.app/upconversion-options.xsl line 19 element function
xsltStylePreCompute: unknown xsl:function
compilation error: file /Users/yada/Library/Developer/CoreSimulator/Devices/ACE15E20-3230-4966-ACDE-DCADABF48B32/data/Containers/Bundle/Application/A4EC9B34-3A96-42E0-B58F-6EB44C942874/appname.app/upconversion-options.xsl line 20 element param
element param only allowed within a template, variable or param
... and many more like that, when I do this:
xmlSubstituteEntitiesDefault(1);
xmlLoadExtDtdDefaultValue = 1;
NSString * pMathMLTocMathMLXSLPath = [[NSBundle mainBundle] pathForResource:#"upconversion-options" ofType:#"xsl"];
xsltStylesheetPtr pMathMLTocMathMLXSLStyleSheet = xsltParseStylesheetFile((const xmlChar *)[pMathMLTocMathMLXSLPath cStringUsingEncoding:NSUTF8StringEncoding]);
The xsl file looks like this:
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:s="http://www.ph.ed.ac.uk/snuggletex"
xmlns="http://www.w3.org/1998/Math/MathML"
exclude-result-prefixes="xs s"
xpath-default-namespace="http://www.w3.org/1998/Math/MathML">
<xsl:function name="s:get-boolean-option" as="xs:boolean">
<xsl:param name="upconversion-options" as="element(s:upconversion-options)"/>
<xsl:param name="name" as="xs:string"/>
<xsl:sequence select="boolean($upconversion-options/s:option[#name=$name]/#value='true')"/>
</xsl:function>
<xsl:function name="s:get-upconversion-option" as="xs:string?">
<xsl:param name="upconversion-options" as="element(s:upconversion-options)"/>
<xsl:param name="name" as="xs:string"/>
<xsl:sequence select="$upconversion-options/s:option[#name=$name]/#value"/>
</xsl:function>
<xsl:function name="s:get-symbol-assumption" as="element(s:symbol)?">
<xsl:param name="element" as="element()"/>
<xsl:param name="upconversion-options" as="element(s:upconversion-options)"/>
<xsl:sequence select="$upconversion-options/s:symbol[deep-equal(*, $element)]"/>
</xsl:function>
<xsl:function name="s:is-assumed-symbol" as="xs:boolean">
<xsl:param name="element" as="element()"/>
<xsl:param name="upconversion-options" as="element(s:upconversion-options)"/>
<xsl:param name="assume" as="xs:string"/>
<xsl:sequence select="exists($upconversion-options/s:symbol[#assume=$assume and deep-equal($element, *)])"/>
</xsl:function>
<xsl:function name="s:is-assumed-function" as="xs:boolean">
<xsl:param name="element" as="element()"/>
<xsl:param name="upconversion-options" as="element(s:upconversion-options)"/>
<xsl:sequence select="s:is-assumed-symbol($element, $upconversion-options, 'function')"/>
</xsl:function>
</xsl:stylesheet>
Any advice?
libxslt does not support XSLT 2.0. You will either need to use Saxon/C which hasn't left beta yet, or revert back to XSLT 1.0. If you revert back to 1.0 and need extra functionality take a look at EXSLT.

Count() inside 'for-each' always returns zero

Currently, I have many "xsl:choose" conditions in my file - one "xsl:choose" for a single letter, and it works well.
I try to simplify this situation by replacing many "xsl:choose" with 'for-each' loop - however with no luck.
It's seems to me, that count() inside 'for-each' always returns 0.
I'm curious, because the same count() without 'for-each' works OK.
Pls help.
<?xml version="1.0"?>
<stocks>
<names date="10/30/2013" time="20:37:12">
<name>WGOS1</name>
<name>WGOS2</name>
<name>WGOS3</name>
<name>WGOS4</name>
<name>WGOS5</name>
</names>
<loc>
<slot>P</slot>
<slot>P</slot>
<slot>P</slot>
<slot>P</slot>
<slot>H</slot>
<slot>S</slot>
</loc>
<loc>
<slot>P</slot>
<slot>P</slot>
<slot>P</slot>
<slot>S</slot>
When I use 'count' function to count values eg. 'B' in 'loc' node, it's works OK
<xsl:variable name="color-table">
<var code="A">pink</var >
<var code="B">silver</var>
<var code="P">red</var>
<var code="D">pink</var>
<var code="H">yellow</var>
<var code="S">lightblue</var>
<var code="T">green</var>
<var code="W">pink</var>
</xsl:variable>
<xsl:template match="/">
<xsl:choose>
<xsl:when test="count(/stocks/loc[$pos]/slot [. eq 'B']) > 0">
<td class="slot-B">
<xsl:value-of select="count(/stocks/loc[$pos]/slot [. eq 'B'])"/>
<xsl:text>B</xsl:text>
</td>
</xsl:when>
</xsl:choose>
But when I try to do the same inside for-each loop - test condition fails, due the count() result is always 0.
<xsl:for-each select="$color-table/var">
<xsl:variable name="p" select="#code"/>
<xsl:choose>
<xsl:when test="count(/stocks/loc[$pos]/slot [. eq $p]) > 0">
<td class="slot-$p">
<xsl:value-of select="count(/stocks/loc[$pos]/slot [. eq $p])"/>
<xsl:value-of select="$p"/>
</td>
</xsl:when>
</xsl:choose>
</xsl:for-each>
The $color-table variable refers to a temporary tree, so when you're inside a
<xsl:for-each select="$color-table/var">
/ is the root of that temporary tree, not the root of the original document, and thus /stocks/loc[$pos]/slot will not find any nodes.
You need to store the outer / in another variable before you go into the for-each.
<xsl:variable name="slash" select="/" />
<xsl:for-each select="$color-table/var">
<xsl:variable name="p" select="#code"/>
<xsl:choose>
<xsl:when test="count($slash/stocks/loc[$pos]/slot [. eq $p]) > 0">
<td class="slot-{$p}">
<xsl:value-of select="count($slash/stocks/loc[$pos]/slot [. eq $p])"/>
<xsl:value-of select="$p"/>
</td>
</xsl:when>
</xsl:choose>
</xsl:for-each>
But rather than iterating over the color-table, it may be more efficient to just for-each-group over the slots themselves
<xsl:for-each-group select="/stocks/loc[$pos]/slot" group-by=".">
<td class="slot-{current-grouping-key()}">
<xsl:value-of select="count(current-group())" />
<xsl:value-of select="current-grouping-key()" />
</td>
</xsl:for-each-group>

tagging words using lookup, what's the best approach?

I have some strings I want to test to see if they contain specific words. The words in question are in a lookup node, and if there is a match the word in the string needs to be tagged. I have a script that works almost ok, but I want to know if I'm using the best format, as I believe it's rather resource consuming, and not very foolproof.
Example xml :
<Main>
<NTUS>
<NTU>match</NTU>
<NTU>test</NTU>
</NTUS>
<Folder id="update">
<about>This content is not in a span so we ignore it completely, even if we would have a match</about>
<Title>
<span class="string simple" lang="en">Some test content containing a single match</span>
</Title>
<Content>
<span class="string complex" lang="en">Also keywords in sub elements should <strong>pass the test</strong>, and match.</span>
</Content>
</Folder>
</Main>
My current xslt :
<xsl:param name="units">
<xsl:copy-of select="//NTU"/>
</xsl:param>
<xsl:template match="/">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="#* | node()">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="NTUS"/>
<xsl:template match="text()[ancestor::span]">
<xsl:analyze-string select="." regex="\s+">
<xsl:matching-substring>
<xsl:value-of select="."/>
</xsl:matching-substring>
<xsl:non-matching-substring>
<xsl:variable name="theWord" select="."/>
<xsl:choose>
<xsl:when test="$units/*[text()=$theWord]">
<ntu>
<xsl:value-of select="."/>
</ntu>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="."/>
</xsl:otherwise>
</xsl:choose>
</xsl:non-matching-substring>
</xsl:analyze-string>
</xsl:template>
results in following :
<Main>
<Folder id="update">
<about>This content is not in a span so we ignore it completely, even if we would have a match</about>
<Title>
<span class="string simple" lang="en">Some <ntu>test</ntu> content containing a single <ntu>match</ntu></span>
</Title>
<Content>
<span class="string complex" lang="en">Also keywords in sub elements should <strong>pass the <ntu>test</ntu></strong>, and match.</span>
</Content>
</Folder>
</Main>
Which is almost ok apart from the last node, as the [match] is at the end of the sentence and therefore not passing the regex. I can adjust it to make it match, but it could become pretty complex then, so i want to know if there are better ways to address this problem.
EDIT : there seems to be a small misbehaviour when you use a comma delimited list (might be on other occasions also, but this one I noticed)...
So for instance following xml
<Main>
<NTUS>
<NTU>OPTION1</NTU>
<NTU>OPTION2</NTU>
<NTU>OPTION3</NTU>
<NTU>OPTION4</NTU>
<NTU>OPTION5</NTU>
</NTUS>
<local xml:lang="en">
<span>Test string containing some comma seperarated lookup values: OPTION1, OPTION2, OPTION3, OPTION4, OPTION5</span>
</local>
Returns following when the script is applied :
<span>Test string containing some comma seperarated lookup values: <ntu>OPTION1</ntu>, OPTION2, <ntu>OPTION3</ntu>, OPTION4, <ntu>OPTION5</ntu></span>
so every second match is skipped. Any idea what is causing this behaviour ?
This transformation:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:variable name="vPatterns" select=
"string-join(/*/NTUS/*, '|')"/>
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="span//text()">
<xsl:analyze-string select="." regex=
"(^|(\P{{L}})+)({$vPatterns})($|(\P{{L}})+)">
<xsl:non-matching-substring><xsl:value-of select="."/></xsl:non-matching-substring>
<xsl:matching-substring>
<xsl:value-of select="regex-group(1)"/>
<ntu><xsl:value-of select="regex-group(3)"/></ntu>
<xsl:value-of select="regex-group(4)"/>
</xsl:matching-substring>
</xsl:analyze-string>
</xsl:template>
</xsl:stylesheet>
When this transformation is applied to the provided XML document:
<Main>
<NTUS>
<NTU>match</NTU>
<NTU>test</NTU>
</NTUS>
<Folder id="update">
<about>This content is not in a span so we ignore it completely, even if we would have a match</about>
<Title>
<span class="string simple" lang="en">Some test content containing a single match</span>
</Title>
<Content>
<span class="string complex" lang="en">Also keywords in sub elements should <strong>pass the test</strong>, and match.</span>
</Content>
</Folder>
</Main>
the wanted, correct result is produced:
<Main>
<NTUS>
<NTU>match</NTU>
<NTU>test</NTU>
</NTUS>
<Folder id="update">
<about>This content is not in a span so we ignore it completely, even if we would have a match</about>
<Title>
<span class="string simple" lang="en">Some <ntu>test</ntu> content containing a testmatch or a single <ntu>match</ntu></span>
</Title>
<Content>
<span class="string complex" lang="en">Also keywords in sub elements should <strong>pass the <ntu>test</ntu></strong>, and <ntu>match</ntu>.</span>
</Content>
</Folder>
</Main>

XSLT - make xsl:analyze-string return string instead of sequence of strings?

Is it possible to make xsl:analyze-string return one string instead of a sequence of strings?
Background: I'd like to use xsl:analyze-string in a xsl:function that should encapsulate the pattern matching. Ideally, the function should return an xs:string to be used as sort criteria in an xsl:sort element.
At the moment, i have to apply string-join() on every result of the function call since xsl:analyze-string returns a sequence of strings, and xsl:sort doesn't accept such a sequence as sort criteria. See line 24 of the stylesheet:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:my="www.my-personal-namespa.ce"
xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xsl:output indent="yes" method="xml" />
<xsl:function name="my:sortierung" >
<xsl:param name="inputstring" as="xs:string"/>
<xsl:analyze-string select="$inputstring" regex="[0-9]+">
<xsl:matching-substring>
<xsl:value-of select="format-number(number(.), '00000')" />
</xsl:matching-substring>
<xsl:non-matching-substring>
<xsl:value-of select="." />
</xsl:non-matching-substring>
</xsl:analyze-string>
</xsl:function>
<xsl:template match="/input">
<result>
<xsl:apply-templates select="value" >
<xsl:sort select="string-join((my:sortierung(.)), ' ')" />
</xsl:apply-templates>
</result>
</xsl:template>
<xsl:template match="value">
<xsl:copy-of select="." />
</xsl:template>
</xsl:stylesheet>
with this input:
<?xml version="1.0" encoding="UTF-8"?>
<input>
<value>A 1 b 120</value>
<value>A 1 b 1</value>
<value>A 1 b 2</value>
<value>A 1 b 1a</value>
</input>
In my example, is there a way to modify the xsl:function to return a xs:string instead of a sequence?
There are several ways I think, you could put the result of the analyze-string into a variable inside of the function and then use xs:sequence select="string-join($var, ' ')" in the function.
However the following with xsl:value-of should also do:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:my="www.my-personal-namespa.ce"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="my xs">
<xsl:output indent="yes" method="xml" />
<xsl:function name="my:sortierung" as="xs:string">
<xsl:param name="inputstring" as="xs:string"/>
<xsl:value-of separator=" ">
<xsl:analyze-string select="$inputstring" regex="[0-9]+">
<xsl:matching-substring>
<xsl:value-of select="format-number(number(.), '00000')" />
</xsl:matching-substring>
<xsl:non-matching-substring>
<xsl:value-of select="." />
</xsl:non-matching-substring>
</xsl:analyze-string>
</xsl:value-of>
</xsl:function>
<xsl:template match="/input">
<result>
<xsl:apply-templates select="value" >
<xsl:sort select="my:sortierung(.)" />
</xsl:apply-templates>
</result>
</xsl:template>
<xsl:template match="value">
<xsl:copy-of select="." />
</xsl:template>
</xsl:stylesheet>

Resources