Reputation: 13618
Is there a way to determine if a xsl:matching-substring
is the last()
matching substring?
Example data:
<data>
<value>1 A 1 2 B 2 1 C 3</value>
</data>
Example XSLT:
<?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:output media-type="xhtml" encoding="UTF-8" />
<xsl:template match="/">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>xsl:matching-substring problem</title>
</head>
<body>
<h1>xsl:matching-substring problem</h1>
<xsl:apply-templates/>
</body>
</html>
</xsl:template>
<xsl:template match="//value">
<xsl:element name="p" namespace="http://www.w3.org/1999/xhtml">
<xsl:analyze-string select="." regex="\p{{Nd}}\s\p{{Lu}}\s\p{{Nd}}" flags="i">
<xsl:matching-substring>
<xsl:value-of select="."/>
<xsl:if test="not(last())">
<xsl:element name="br" namespace="http://www.w3.org/1999/xhtml" />
</xsl:if />
</xsl:matching-substring>
</xsl:analyze-string>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
Result:
<?xml version="1.0" encoding="UTF-8"?><html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="xhtml; charset=UTF-8" />
<title>xsl:matching-substring problem</title>
</head>
<body>
<h1>xsl:matching-substring problem</h1>
<p>1 A 12 B 21 C 3</p>
</body>
</html>
So, it seems last()
is true also for the first and the second matching substring (in fact, last()
seems to return the position of the last character of the matching substring).
Without the xsl:if
, i get:
<?xml version="1.0" encoding="UTF-8"?><html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="xhtml; charset=UTF-8" />
<title>xsl:matching-substring problem</title>
</head>
<body>
<h1>xsl:matching-substring problem</h1>
<p>1 A 1<br />2 B 2<br />1 C 3<br /></p>
</body>
</html>
But i would prefer <p>1 A 1<br />2 B 2<br />1 C 3</p>
. Is there a way to achieve this using XSLT 2.0? (Im using Saxon HE as xslt engine).
Upvotes: 1
Views: 508
Reputation: 163360
@Joel_M.Lamsen has identified a bug in your code, but his solution isn't correct.
The spec says this: In processing each substring, the contents of the substring will be the context item (as a value of type xs:string); the position of the substring within the sequence of matching and non-matching substrings will be the context position; and the number of matching and non-matching substrings will be the context size.
So (position()=last()) tests whether you are processing the last substring, which may be either a matching or a non-matching substring. If you are in xsl:matching-substring, processing the last matching substring, then (position()=last()) will return false if there is a non-matching substring still to come.
Since you are not interested in the non-matching substrings, a better solution is to use tokenize():
<xsl:template match="value">
<xhtml:p>
<xsl:for-each select="tokenize(., '\p{{Nd}}\s\p{{Lu}}\s\p{{Nd}}', 'i')">
<xsl:if test="position() ne 1"><xhtml:br/></xsl:if>
<xsl:value-of select="."/>
</xsl:for-each>
</xhtml:p>
</xsl:template>
Note also some other stylistic improvements I have made to your code:
I would also be inclined to avoid the 'i' flag in the regex; instead replace \p{Lu}
by some larger category.
Upvotes: 3