I want to split a string at the double quotation. Input string is as follow,
<S>Test Example "{test1}" is "{equal}" "{test2}"</S>
The xslt code that I'm using is,
<xsl:template name="SplitString">
<xsl:param name="text" select="''" />
<xsl:variable name="tag" select="substring-before(substring-after($text, '"'), '"')" />
<xsl:variable name="Remainder" select="substring-after($text, '"')" />
<xsl:choose>
<xsl:when test="$tag != ''">
<xsl:element name = "NP">
<xsl:value-of select = "$tag"/>
</xsl:element>
<!--recursive loop -->
<xsl:call-template name="SplitString">
<xsl:with-param name="text" select="$Remainder" />
</xsl:call-template>
</xsl:when>
</xsl:choose>
</xsl:template>
The output that I'm getting is as follow,
<NP>{test}</NP>
<NP>is</NP>
<NP>{equal}</NP>
<NP> </NP>
<NP>{test2}</NP>
How can avoid the creation of empty element?
the desired output would be,
<NP>{test}</NP>
<NP>is</NP>
<NP>{equal}</NP>
<NP>{test2}</NP>
Just add a condition:
<xsl:if test="normalize-space($tag)">
<xsl:element name = "NP">
<xsl:value-of select = "$tag"/>
</xsl:element>
</xsl:if>
Related
I am doing a upper case to first character content in the xml data using the delimiters (space and hyphen) and am able to get the output correctly however this template is removing the break line tag in the table td area of xml. The output should be an xml.
eg:<text>
<table>
<tbody>
<tr>
<td>test<br />testing<br />tested</td>
</tr>
I see the code transforming as continous line without break tag in the output as below:I need to see the same br tag in xml output as in input xml however by capitalizing first letter of the word.
<text>
<table>
<tbody>
<tr>
<td>Testtestingtested</td>
</tr>
I am expecting to preserve and display the same break line tag to be in output xml even after the xslt tranformation so that the output will look correct instead of continous line
I am using the below xslt transformation:
<?xml version="1.0" encoding="utf-8"?>
<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"
xmlns:n1="urn:hl7-org:v3" xmlns:n2="urn:hl7-org:sdtc">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match='n1:ClinicalDocument/n1:component/n1:structuredBody/n1:component/n1:section/n1:text/n1:table/n1:tbody/n1:tr'>
<xsl:copy>
<xsl:apply-templates select='n1:td'/>
</xsl:copy>
</xsl:template>
<xsl:template match="n1:td">
<xsl:copy>
<xsl:if test="./#ID">
<xsl:attribute name="ID" xml:space="default">
<xsl:value-of select="./#ID"/>
</xsl:attribute>
</xsl:if>
<xsl:call-template name="capitalize">
<xsl:with-param name="text" select="string(.)"/>
</xsl:call-template>
</xsl:copy>
<xsl:text>
</xsl:text>
</xsl:template>
<xsl:template name="capitalize">
<xsl:param name="text" />
<xsl:param name="delimiter" select = "' '"/>
<xsl:variable name="upper-case" select="'ABCDEFGHIJKLMNOPQRSTUVWXYZ'"/>
<xsl:variable name="lower-case" select="'abcdefghijklmnopqrstuvwxyz'"/>
<xsl:variable name="word" select="substring-before(concat($text, $delimiter), $delimiter)" />
<xsl:choose>
<xsl:when test="$delimiter=' '">
<!-- tokenize word by 2nd delimiter -->
<xsl:call-template name="capitalize">
<xsl:with-param name="text" select="$word"/>
<xsl:with-param name="delimiter" select="'-'"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<!-- capitalize word -->
<xsl:value-of select="translate(substring($word, 1, 1), $lower-case, $upper-case) " />
<xsl:value-of select="translate(substring($word, 2), $upper-case, $lower-case)" />
</xsl:otherwise>
</xsl:choose>
<xsl:if test="contains($text, $delimiter)">
<xsl:value-of select="$delimiter"/>
<!-- recursive call -->
<xsl:call-template name="capitalize">
<xsl:with-param name="text" select="substring-after($text, $delimiter)" />
</xsl:call-template>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
Can anyone check and answer?
UPDATE: Both XSLT 1.0 and 2.0 solutions
All solutions preserve your line breaks and also work if you have inline markup of the text content.
First I make the lc and uc parameters global for more minified templates:
<xsl:param name="lc" select="'abcdefghijklmnopqrstuvwxyzàáâãäåæçèéêëìíîïðñòóôõöøùúûüýþÿžšœ'"/>
<xsl:param name="uc" select="'ABCDEFGHIJKLMNOPQRSTUVWXYZÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝÞŸŽŠŒ'"/>
XSLT 1.0 or 2.0: You need to rewrite your first template to this regardless of the following XSLT solutions:
<xsl:template match="n1:td">
<xsl:copy>
<xsl:if test="./#ID">
<xsl:attribute name="ID" xml:space="default">
<xsl:value-of select="./#ID"/>
</xsl:attribute>
</xsl:if>
<xsl:apply-templates select="node()" />
</xsl:copy>
<xsl:text>
</xsl:text>
</xsl:template>
XSLT 1.0
With XSLT 1.0 you need to combine a match template with a name template.
Match template: n1:td//text()
With this match template it becomes precisely what you request, make initial letter uppercase, and make letters following space and - uppercase:
<xsl:template match="n1:td//text()" >
<xsl:param name="text" select="." />
<xsl:param name="currentTextBlock" select="ancestor::n1:td[1]" />
<xsl:param name="isFirstTextNode">
<xsl:choose>
<xsl:when test="preceding::text()[ancestor::n1:td[generate-id(.) = generate-id($currentTextBlock)]]">false</xsl:when>
<xsl:otherwise>true</xsl:otherwise>
</xsl:choose>
</xsl:param>
<xsl:choose>
<xsl:when test="$isFirstTextNode = 'true'">
<xsl:value-of select="translate(substring($text, 1, 1), $lc, $uc)" />
<xsl:call-template name="capitalize">
<xsl:with-param name="text" select="$text" />
<xsl:with-param name="remainingText" select="substring($text,2)" />
<xsl:with-param name="index" select="2" />
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:call-template name="capitalize">
<xsl:with-param name="text" select="$text" />
<xsl:with-param name="remainingText" select="$text" />
<xsl:with-param name="index" select="1" />
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
If you also want initial letter for each text node to be uppercase without adding space or hyphen, you can use this:
<xsl:template match="n1:td//text()" >
<xsl:param name="text" select="." />
<xsl:value-of select="translate(substring($text, 1, 1), $lc, $uc)" />
<xsl:call-template name="capitalize">
<xsl:with-param name="text" select="$text" />
<xsl:with-param name="remainingText" select="substring($text,2)" />
<xsl:with-param name="index" select="2" />
</xsl:call-template>
</xsl:template>
Name template: capitalize
This name template is can be used "as is" with both of the above match templates.
<xsl:template name="capitalize">
<xsl:param name="text" select="''" />
<xsl:param name="remainingText" select="''" />
<xsl:param name="index" select="1" />
<xsl:if test="$remainingText != ''">
<xsl:variable name="currentChar" select="substring($remainingText, 1, 1)" />
<xsl:choose>
<xsl:when test="$index = 1">
<xsl:value-of select="translate($currentChar, $uc, $lc)" />
</xsl:when>
<xsl:otherwise>
<xsl:variable name="previousChar" select="substring($text, $index - 1, 1)" />
<xsl:choose>
<xsl:when test="$previousChar = ' ' or $previousChar = '-'">
<xsl:value-of select="translate($currentChar, $lc, $uc)" />
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="translate($currentChar, $uc, $lc)" />
</xsl:otherwise>
</xsl:choose>
</xsl:otherwise>
</xsl:choose>
<xsl:call-template name="capitalize">
<xsl:with-param name="text" select="$text" />
<xsl:with-param name="remainingText" select="substring($remainingText, 2)" />
<xsl:with-param name="index" select="$index + 1" />
</xsl:call-template>
</xsl:if>
</xsl:template>
XSLT 2.0 (same as before):
This solution does precisely what you request, make initial letter uppercase, and make letters following space and - uppercase:
<xsl:template match="n1:td//text()" >
<xsl:param name="text" select="." />
<xsl:param name="currentTextBlock" select="ancestor::n1:td[1]" />
<xsl:param name="isFirstTextNode">
<xsl:choose>
<xsl:when test="preceding::text()[ancestor::n1:td[generate-id(.) = generate-id($currentTextBlock)]]">false</xsl:when>
<xsl:otherwise>true</xsl:otherwise>
</xsl:choose>
</xsl:param>
<xsl:for-each select="tokenize(replace(replace($text,'(.)','$1\\n'),'\\n$',''),'\\n')">
<xsl:variable name="pos" select="position()" />
<xsl:variable name="char" select="." />
<xsl:choose>
<xsl:when test="$isFirstTextNode = 'true' and $pos = 1">
<xsl:value-of select="translate($char, $lc, $uc) " />
</xsl:when>
<xsl:when test="substring($text, $pos - 1, 1) = ' ' or substring($text, $pos - 1, 1) = '-'">
<xsl:value-of select="translate($char, $lc, $uc) " />
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="translate($char, $uc, $lc)" />
</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
</xsl:template>
If you also want initial letter for each text node to be uppercase without adding space or hyphen, you can use this:
<xsl:template match="n1:td//text()" >
<xsl:param name="text" select="." />
<xsl:param name="currentTextBlock" select="ancestor::n1:td[1]" />
<xsl:for-each select="tokenize(replace(replace($text,'(.)','$1\\n'),'\\n$',''),'\\n')">
<xsl:variable name="pos" select="position()" />
<xsl:variable name="char" select="." />
<xsl:choose>
<xsl:when test="$pos = 1">
<xsl:value-of select="translate($char, $lc, $uc) " />
</xsl:when>
<xsl:when test="substring($text, $pos - 1, 1) = ' ' or substring($text, $pos - 1, 1) = '-'">
<xsl:value-of select="translate($char, $lc, $uc) " />
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="translate($char, $uc, $lc)" />
</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
</xsl:template>
The problem is your call on string(.), which takes the string value of the element: this strips out all markup.
You should be doing a recursive descent using xsl:apply-templates at each level to apply template rules to child nodes, all the way down to the leaf text nodes, and then your template rule for text nodes should be doing the case conversion.
Except that your capitalize logic seems to be trying to do something smart (I'm not sure what) which means it might need to look at something larger than a single text node. You might need a different rule for the first text node and for subsequent text nodes: it's hard to be sure without seeing a spec.
Another approach to this kind of problem is to do multiple passes: the first pass replaces the <br/> elements with some textual marker, e.g. "§br§", in the next pass you process the text as a string, and then finally you convert the markers back to element nodes.
With XSLT 3.0 you could do the first pass using fn:serialize() and the final pass using fn:parse-xml-fragment(); the "textual marker" would then be the actual lexical markup "<br/>" as a string of five characters. (But take care not to capitalise it!)
I hope someone can help me with this...
I am using SharePoint 2013 and trying to render a CQWP to show the latest blog posts. The problem I have is that when displaying the 'content' from the post I get ' ' added and quotes are shown as '"'. I have managed to strip the HTML mark up but can't seem to get rid of these.
My code is as follows - any help would be much appreciated, thanks.
Generate Summary and Remove HTML
<!-- Generate Summary -->
<xsl:template name="GenerateSummary">
<xsl:param name="Content"/>
<xsl:param name="Length"/>
<xsl:param name="Suffix"/>
<xsl:variable name="cleanContent">
<xsl:call-template name="RemoveHtml">
<xsl:with-param name="String" select="$Content"/>
</xsl:call-template>
</xsl:variable>
<xsl:call-template name="SubstringBeforeLast">
<xsl:with-param name="String"
select="substring($cleanContent, 1, $Length)"/>
<xsl:with-param name="Char" select="' '"/>
</xsl:call-template>
<xsl:if test="string-length($cleanContent) > $Length">
<xsl:value-of select="$Suffix"
disable-output-escaping="yes"/>
</xsl:if>
</xsl:template>
<!-- RemoveHTML -->
<xsl:template name="RemoveHtml">
<xsl:param name="String"/>
<xsl:choose>
<xsl:when test="contains($String, '<')">
<xsl:value-of select="substring-before($String, '<')"/>
<xsl:call-template name="RemoveHtml">
<xsl:with-param name="String"
select="substring-after($String, '>')"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$String"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template name="SubstringBeforeLast">
<xsl:param name="String" />
<xsl:param name="Char" />
<xsl:param name="subsequent"/>
<xsl:choose>
<xsl:when test="contains($String, $Char)">
<xsl:if test="$subsequent = 1">
<xsl:value-of select="$Char"/>
</xsl:if>
<xsl:value-of select="substring-before($String, $Char)"/>
<xsl:call-template name="SubstringBeforeLast">
<xsl:with-param name="String"
select="substring-after($String, $Char)" />
<xsl:with-param name="Char" select="$Char" />
<xsl:with-param name="subsequent" select="1"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:if test="$subsequent != 1">
<xsl:value-of select="$String"/>
</xsl:if>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
Calling it here
<div class="fcItemContent">
<xsl:call-template name="GenerateSummary">
<xsl:with-param name="Content" select="#content" />
<xsl:with-param name="Length" select="200" />
<xsl:with-param name="Suffix" select="'...'"/>
</xsl:call-template>
</div>
Use function of XSLT
normalize-space()
**<xsl:value-of select="normalize-space()"/>**
White space is normalized by stripping leading and trailing white space and replacing sequences of white space characters with a single space. If the argument is omitted, the string-value of the context node is normalized and returned.
referred Link :
Remove Space using XSLT
I want to convert some plain text with special marker into HTML formatted text.
For example,
This is the original value
Th<italic>is is a <under>com<bold>bina</bold>tion</under></italic> text.
The original value as actual value (just for reference)
Th<italic>is is a <under>com<bold>bina</bold>tion</under></italic> text.
HTML format I expect as a result
Th<i>is is a <u>com<b>bina</b>tion</u></i> text.
I tried with below template but it can not be parsed by XSLT parser.
<xsl:template name="decorateValue">
<xsl:param name="originalString" />
<xsl:variable name="preString" select="substring-before($originalString, '<')" />
<xsl:variable name="postString" select="substring-after($originalString, '<')" />
<xsl:value-of select="$preString" />
<xsl:variable name="tagName" select="substring-before($postString, '>')" />
<xsl:choose>
<xsl:when test="$tagName='bold'">
<xsl:element name="b">
</xsl:when>
<xsl:when test="$tagName='/bold'">
</xsl:element>
</xsl:when>
<xsl:when test="$tagName='italic'">
<xsl:element name="i">
</xsl:when>
<xsl:when test="$tagName='/italic'">
</xsl:element>
</xsl:when>
<xsl:when test="$tagName='under'">
<xsl:element name="u">
</xsl:when>
<xsl:when test="$tagName='/under'">
</xsl:element>
</xsl:when>
</xsl:choose>
<xsl:call-template name="decorateValue">
<xsl:with-param name="originalString"
select="substring-after($postString, '>')" />
</xsl:call-template>
</xsl:template>
any idea to solve this?
I would appreciate in advance.
If you can actually keep the original text, your life would be easier. Then you can transform
Th<italic>is is a <under>com<bold>bina</bold>tion</under></italic> text. easily into xHTML
<xsl:template match="italic">
<xsl:element name="i">
<xsl:apply-templates />
</xsl:element>
</xsl:template>
You can't mix your tags in your example. You open an xsl:when and open an xsl:element and then close the xsl:when. That is not valid XML!
So if you want to go with the encoded string you need something like:
<xsl:template name="decorateValue">
<xsl:param name="originalString" />
<xsl:if test="$originalString!=''">
<xsl:variable name="preString" select="substring-before($originalString, '<')" />
<xsl:variable name="postString" select="substring-after($originalString, '<')" />
<xsl:variable name="endString" select="'magic happens here!'" />
<xsl:value-of select="$preString" />
<xsl:variable name="tagName" select="substring-before($postString, '>')" />
<xsl:variable name="restString" select="'magic happens here!'" />
<xsl:choose>
<xsl:when test="$tagName='bold'">
<xsl:element name="b">
<xsl:call-template name="decorateValue">
<xsl:with-param name="originalString"
select="$restString" />
</xsl:call-template>
</xsl:element>
</xsl:when>
<xsl:when test="$tagName='italic'">
<xsl:element name="i">
<xsl:call-template name="decorateValue">
<xsl:with-param name="originalString"
select="$restString" />
</xsl:call-template>
</xsl:element>
</xsl:when>
<xsl:when test="$tagName='under'">
<xsl:element name="u">
<xsl:call-template name="decorateValue">
<xsl:with-param name="originalString"
select="$restString" />
</xsl:call-template>
</xsl:element>
</xsl:when>
</xsl:choose>
<xsl:value-of select="$endString" />
</xsl:if>
</xsl:template>
You see 2 places where 'magic happens here'. This is where you need to apply the string-before-last and string-after-last patterns (which are a PITA in XSLT). The best explanation can be found in the XSLT Cookbook (and you want to have XSLT 2.0 at least.
Hope the pointers help. You might consider breaking the pattern into individual, so you don't fish for > alone, but the full tags. You still need to use the before-last / after-last functions.
i am converting an html form to xml sequence, i am using a recursive function to achieve this
so the input to param "list" will be of the form
name=value&name=value&name=value
The template below does this fine and returns an xml sequence as follows
<name>value</name><name>value</name><name>value</name>
Ok so the problem
some of the name value pairs are special and i would like to add a attribute to them so the output would be
<name>value</name><name attr="special">value</name><name>value</name>
so to do this i have an external xml file with a list of special names as follows
<settings><google><option from="color"/><option from="size"/></google></settings>
So if we assume i have a xsl variable $SETTINGS connected to this external document above
<xsl:for-each select="$SETTINGS/google/option"></xsl:for-each>
Should be node with 2 children 1 called color and the other size
wot i want to do is if 1 of these children names = $name add the attribute
something like <xsl:if test="$name = $SETTINGS/google/option/ckild name">
<xsl:template name="tokenize">
<xsl:param name="list"/>
<xsl:variable name="seperator" select="'&'"/>
<xsl:variable name="first" select="substring-before(concat($list, $seperator), $seperator)"/>
<xsl:variable name="butfirst" select="substring-after($list, $seperator)"/>
<xsl:variable name="name" select="normalize-space(substring-before($first, '='))"/>
<xsl:variable name="value" select="normalize-space(substring-after($first, '='))"/>
<xsl:if test="string-length($name)>0 and string-length($value)>0">
<xsl:element name="{$name}">
<xsl:for-each select="$SETTINGS/google/option">
-----> <xsl:if test="$name = $SETTINGS/google/option/ckild name">
<xsl:attribute name="option"/>
</xsl:if>
</xsl:for-each>
<xsl:value-of select="$value"/>
</xsl:element>
</xsl:if>
<xsl:if test="$butfirst">
<xsl:call-template name="tokenize">
<xsl:with-param name="list" select="$butfirst"/>
<xsl:with-param name="seperator" select="$seperator"/>
</xsl:call-template>
</xsl:if>
</xsl:template>
Many Thanks
Tim Dodgson
This stylesheet:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:variable name="SETTINGS" select="/settings"/>
<xsl:template match="/">
<xsl:call-template name="tokenize">
<xsl:with-param name="pString"
select="'color=blue&name=value&size=big'"/>
</xsl:call-template>
</xsl:template>
<xsl:template name="tokenize">
<xsl:param name="pString"/>
<xsl:param name="pSeperator" select="'&'"/>
<xsl:choose>
<xsl:when test="not($pString)"/>
<xsl:when test="contains($pString,$pSeperator)">
<xsl:call-template name="tokenize">
<xsl:with-param name="pString"
select="substring-before($pString, $pSeperator)"/>
<xsl:with-param name="pSeperator" select="$pSeperator"/>
</xsl:call-template>
<xsl:call-template name="tokenize">
<xsl:with-param name="pString"
select="substring-after($pString, $pSeperator)"/>
<xsl:with-param name="pSeperator" select="$pSeperator"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:variable name="vName"
select="normalize-space(substring-before($pString,'='))"/>
<xsl:variable name="vValue"
select="normalize-space(substring-after($pString,'='))"/>
<xsl:if test="$vName and $vValue">
<xsl:element name="{$vName}">
<xsl:if test="$vName = $SETTINGS/google/option/#from">
<xsl:attribute name="attr">special</xsl:attribute>
</xsl:if>
<xsl:value-of select="$vValue"/>
</xsl:element>
</xsl:if>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
With this input:
<settings>
<google>
<option from="color"/>
<option from="size"/>
</google>
</settings>
Output:
<color attr="special">blue</color>
<name>value</name>
<size attr="special">big</size>
Note: Node set comparison
I have a delimited string (delimited by spaces in my example below) that I need to tokenize, sort, and then join back together and I need to do all this using XSLT 1.0. How would I do that? I know I need to use xsl:sort somehow, but everything I’ve tried so far has given me some sort of error.
For example, if I run the code at the bottom of this posting, I get this:
strawberry blueberry orange raspberry
lime lemon
What would I do if I wanted to get this instead?:
blueberry lemon lime orange raspberry
strawberry
Note that I’m using XSLT 1.0.
Here is the code, which is based on code by Jeni Tennison.
<?xml version="1.0"?>
<?xml-stylesheet type="text/xsl" href="tokenize1.xsl"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<xsl:call-template name="tokenize">
<xsl:with-param name="string" select="'strawberry blueberry orange raspberry lime lemon'" />
</xsl:call-template>
</xsl:template>
<xsl:template name="tokenize">
<xsl:param name="string" />
<xsl:param name="delimiter" select="' '" />
<xsl:choose>
<xsl:when test="$delimiter and contains($string, $delimiter)">
<token>
<xsl:value-of select="substring-before($string, $delimiter)" />
</token>
<xsl:text> </xsl:text>
<xsl:call-template name="tokenize">
<xsl:with-param name="string"
select="substring-after($string, $delimiter)" />
<xsl:with-param name="delimiter" select="$delimiter" />
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<token><xsl:value-of select="$string" /></token>
<xsl:text> </xsl:text>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
Here's an inefficient pure version 1 solution:
<!-- Sort the tokens -->
<xsl:template name="sortTokens">
<xsl:param name="tokens" select="''"/> <!-- The list of tokens -->
<xsl:param name="separator" select="' '"/> <!-- What character separates the tokens? -->
<xsl:param name="pivot" select="''"/> <!-- A pivot word used to divide the list -->
<xsl:param name="lessThan" select="''"/> <!-- Accumulator for tokens less than the pivot (with leading separator) -->
<xsl:param name="moreThan" select="''"/> <!-- Accumulator for tokens more than the pivot (with leading separator) -->
<xsl:param name="leadWith" select="''"/> <!-- If set, output this before sorting -->
<xsl:param name="trailWith" select="''"/> <!-- If set, output this after sorting -->
<!-- The first token -->
<xsl:variable name="firstToken" select="substring-before(concat($tokens,$separator),$separator)"/>
<!-- Is the first token more or less than the pivot? -->
<xsl:variable name="pivotVsFirstToken">
<xsl:call-template name="compareStrings">
<xsl:with-param name="a" select="$pivot"/>
<xsl:with-param name="b" select="$firstToken"/>
</xsl:call-template>
</xsl:variable>
<xsl:choose>
<!-- No input, no output -->
<xsl:when test="$tokens = '' and $pivot = ''"></xsl:when>
<!-- At the outset, the first token becomes the pivot -->
<xsl:when test="$pivot = ''">
<xsl:value-of select="$leadWith"/>
<xsl:call-template name="sortTokens">
<xsl:with-param name="separator" select="$separator"/>
<xsl:with-param name="tokens" select="substring-after($tokens,$separator)"/>
<xsl:with-param name="pivot" select="$firstToken"/>
</xsl:call-template>
<xsl:value-of select="$trailWith"/>
</xsl:when>
<!-- When all tokens are in a bucket, output the pivot between sorted buckets -->
<xsl:when test="$tokens = ''">
<xsl:call-template name="sortTokens">
<xsl:with-param name="separator" select="$separator"/>
<xsl:with-param name="tokens" select="substring-after($lessThan,$separator)"/>
<xsl:with-param name="trailWith" select="$separator"/>
</xsl:call-template>
<xsl:value-of select="$pivot"/>
<xsl:call-template name="sortTokens">
<xsl:with-param name="separator" select="$separator"/>
<xsl:with-param name="tokens" select="substring-after($moreThan,$separator)"/>
<xsl:with-param name="leadWith" select="$separator"/>
</xsl:call-template>
</xsl:when>
<!-- If the first token is less than the pivot, put it in the lessThan bucket -->
<xsl:when test="number($pivotVsFirstToken) = 1">
<xsl:call-template name="sortTokens">
<xsl:with-param name="separator" select="$separator"/>
<xsl:with-param name="tokens" select="substring-after($tokens,$separator)"/>
<xsl:with-param name="pivot" select="$pivot"/>
<xsl:with-param name="lessThan" select="concat($separator,$firstToken,$lessThan)"/>
<xsl:with-param name="moreThan" select="$moreThan"/>
</xsl:call-template>
</xsl:when>
<!-- If the first token is more than the pivot, put it in the moreThan bucket -->
<xsl:otherwise>
<xsl:call-template name="sortTokens">
<xsl:with-param name="separator" select="$separator"/>
<xsl:with-param name="tokens" select="substring-after($tokens,$separator)"/>
<xsl:with-param name="pivot" select="$pivot"/>
<xsl:with-param name="lessThan" select="$lessThan"/>
<xsl:with-param name="moreThan" select="concat($separator,$firstToken,$moreThan)"/>
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<!-- Quote an apostrophe -->
<xsl:variable name="apos" select=""'""/>
<!-- The comparison order of the characters -->
<xsl:variable name="characterOrder" select="concat(' !"#$%&',$apos,'()*+,-./0123456789:;<=>?#ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~')"/>
<!-- Return -1 if string a is less, 1 if string b is less, or 0 if they are equal -->
<xsl:template name="compareStrings">
<xsl:param name="a" select="''"/>
<xsl:param name="b" select="''"/>
<xsl:choose>
<xsl:when test="$a = '' and $b = ''">0</xsl:when>
<xsl:when test="$a = ''">-1</xsl:when>
<xsl:when test="$b = ''">1</xsl:when>
<xsl:when test="substring($a,1,1) = substring($b,1,1)">
<xsl:call-template name="compareStrings">
<xsl:with-param name="a" select="substring($a,2)"/>
<xsl:with-param name="b" select="substring($b,2)"/>
</xsl:call-template>
</xsl:when>
<xsl:when test="contains(substring-after($characterOrder,substring($a,1,1)),substring($b,1,1))">-1</xsl:when>
<xsl:otherwise>1</xsl:otherwise>
</xsl:choose>
</xsl:template>
If your processor supports EXSLT, you'd better use str:tokenize
For sorting, why not use xsl:sort?
<xsl:template match="/">
<xsl:variable name="tokens">
<xsl:call-template name="tokenize">
<xsl:with-param name="string" select="'strawberry blueberry orange raspberry lime lemon'" />
</xsl:call-template>
</xsl:variable>
<xsl:for-each select="$tokens">
<xsl:sort select="text()" />
<xsl:value-of select="." />
<xsl:if test="not(last())">
<xsl:text> </xsl:text>
</xsl:if>
</xsl:for-each>
</xsl:template>
Note that you might need exsl:node-set do to the iteration.