XSLT: reverse the string by comma - xslt

I have an XML file:
<schools>
<schcool>
school1, school2, school3, school4, school5
</schcool>
</schools>
I want to write XSLT (version 1.0) to change the result to a reverse order like this:
<schools>
<schcool>
school5, school4, school3, school2, school1
</schcool>
</schools>
Can anyone help me? Many thanks.
DY

<template name="split" xmlns="http://www.w3.org/1999/XSL/Transform">
<param name="s" />
<param name="withcomma" select="false()" />
<choose>
<when test="contains($s, ',')">
<!-- if there is still a comma, call me again
with everything after the first comma... -->
<call-template name="split">
<with-param name="s" select="substring-after($s, ',')" />
<with-param name="withcomma" select="true()" />
</call-template>
<!-- ...and print afterwards the current part -->
<value-of select="substring-before($s, ',')" />
<if test="$withcomma">
<text>, </text>
</if>
</when>
<otherwise>
<!-- No comma left in the remaining part: print the rest -->
<value-of select="$s" />
<if test="$withcomma">
<text>, </text>
</if>
</otherwise>
</choose>
</template>
You might have to fiddle a bit with the whitespace (look up the XPath function 'normalize-space()') to get the exact result, but the reverse sorting in principle is shown in the code above. Call it from your other templates like this:
<call-template name="split">
<with-param name="s" select="." />
</call-template>

A variant without xsl:choose:
<xsl:template name="reverse">
<xsl:param name="text"/>
<xsl:param name="comma" select="false()"/>
<xsl:variable name="item" select="substring-before(concat($text, ','), ',')"/>
<xsl:if test="normalize-space($item) != ''">
<xsl:call-template name="reverse">
<xsl:with-param name="text" select="substring-after(concat($text, ','), concat($item, ','))"/>
<xsl:with-param name="comma" select="true()"/>
</xsl:call-template>
</xsl:if>
<xsl:value-of select="$item"/>
<xsl:if test="$comma and $item != ''">
<xsl:text>,</xsl:text>
</xsl:if>
</xsl:template>

Thank you. You have save my time.
I remove the space by this:
<xsl:call-template name="split">
<xsl:with-param name="s" select="normalize-space(.)" />
</xsl:call-template>
Many thanks.
DY

Related

How to force XSLT to escape chars to numerical format ( ) instead of name format ( )?

I am using Docbook-XSL to process an XML file and produce HTML and XML output files. These files will be used for the online-help in an Eclipse RCP application.
The standard XSL does not produce a contexts.xml file needed to enable F1 context help so I have made use of an XSL I found online here http://blogs.itemis.de/leipzig/archives/691.
This works fine until I add German umlauts into the source text. These characters get converted into named entities such as ä and ß. This causes errors in the outputted XML as these entities cannot be found.
Is there a way to tell the XSL processor to used the numbered entities such as ß?
(I could perform a search and replace on the output file but this seems a bit clumsy...)
The top-level XSL:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:ng="http://docbook.org/docbook-ng" xmlns:db="http://docbook.org/ns/docbook"
xmlns:exsl="http://exslt.org/common" version="1.0"
exclude-result-prefixes="exsl db ng">
<!-- make sure the path to the standard eclipse.xsl is correct -->
<xsl:import href="docbook-xsl-1.78.1/eclipse/eclipse3.xsl" />
<xsl:param name="manifest">
1
</xsl:param>
<xsl:param name="create.plugin.xml">
1
</xsl:param>
<xsl:param name="create.context.xml">
1
</xsl:param>
<xsl:param name="context.constants.package">
org.example.help
</xsl:param>
<xsl:template name="plugin.xml">
<xsl:if test="$create.context.xml = '1'">
<xsl:call-template name="context.xml" />
<xsl:call-template name="contextconstants">
<xsl:with-param name="package">
<xsl:value-of select="$context.constants.package" />
</xsl:with-param>
</xsl:call-template>
</xsl:if>
<xsl:if test="$create.plugin.xml = '1'">
<xsl:call-template name="write.chunk">
<xsl:with-param name="filename">
<xsl:if test="$manifest.in.base.dir != 0">
<xsl:value-of select="$base.dir" />
</xsl:if>
<xsl:value-of select="'plugin.xml'" />
</xsl:with-param>
<xsl:with-param name="method" select="'xml'" />
<xsl:with-param name="encoding" select="'utf-8'" />
<xsl:with-param name="indent" select="'yes'" />
<xsl:with-param name="content">
<xsl:choose>
<xsl:when test="$manifest = '1'">
<plugin>
<extension point="org.eclipse.help.toc">
<toc file="toc.xml" primary="true" />
</extension>
<extension point="org.eclipse.help.contexts">
<contexts file="contexts.xml"></contexts>
</extension>
</plugin>
</xsl:when>
<xsl:otherwise>
<plugin name="{$eclipse.plugin.name}" id="{$eclipse.plugin.id}"
version="1.0" provider-name="{$eclipse.plugin.provider}">
<extension point="org.eclipse.help.toc">
<toc file="toc.xml" primary="true" />
</extension>
</plugin>
</xsl:otherwise>
</xsl:choose>
</xsl:with-param>
</xsl:call-template>
</xsl:if>
</xsl:template>
<xsl:template name="context.xml">
<xsl:call-template name="write.chunk">
<xsl:with-param name="filename" select="'contexts.xml'" />
<xsl:with-param name="method" select="'xml'" />
<xsl:with-param name="encoding" select="'utf-8'" />
<xsl:with-param name="indent" select="'yes'" />
<xsl:with-param name="quiet" select="$chunk.quietly" />
<xsl:with-param name="content">
<contexts>
<xsl:apply-templates select="/*" mode="context.xml" />
</contexts>
</xsl:with-param>
</xsl:call-template>
</xsl:template>
<xsl:template
match="book|part|reference|preface|chapter|bibliography|appendix|article|glossary|section|sect1|sect2|sect3|sect4|sect5|refentry|colophon|bibliodiv|index"
mode="context.xml">
<xsl:variable name="title">
<xsl:if test="$eclipse.autolabel=1">
<xsl:variable name="label.markup">
<xsl:apply-templates select="." mode="label.markup" />
</xsl:variable>
<xsl:if test="normalize-space($label.markup)">
<xsl:value-of select="concat($label.markup,$autotoc.label.separator)" />
</xsl:if>
</xsl:if>
<xsl:apply-templates select="." mode="title.markup" />
</xsl:variable>
<xsl:variable name="href">
<xsl:call-template name="href.target.with.base.dir">
<xsl:with-param name="context" select="/" />
</xsl:call-template>
</xsl:variable>
<!-- <xsl:message>Node=<xsl:value-of select="name(.)"></xsl:value-of>,
ID=<xsl:call-template name="object.id"> <xsl:with-param name="object" select="."/>
</xsl:call-template></xsl:message> -->
<xsl:call-template name="acontext">
<xsl:with-param name="id">
<xsl:call-template name="object.id">
<xsl:with-param name="object" select="." />
</xsl:call-template>
</xsl:with-param>
<xsl:with-param name="label" select="$title" />
<xsl:with-param name="href" select="$href" />
</xsl:call-template>
<xsl:apply-templates
select="part|reference|preface|chapter|bibliography|appendix|article|glossary|section|sect1|sect2|sect3|sect4|sect5|refentry|colophon|bibliodiv|index"
mode="context.xml" />
</xsl:template>
<xsl:template name="acontext">
<xsl:param name="id" />
<xsl:param name="label" />
<xsl:param name="href" />
<xsl:message><xsl:value-of select="$id"/>, <xsl:value-of select="$href"/>
</xsl:message>
<context id="{normalize-space($id)}" title="">
<description />
<topic href="{$href}" label="{normalize-space($label)}" />
</context>
</xsl:template>
<xsl:template name="contextconstants">
<xsl:param name="package" />
<xsl:call-template name="write.chunk">
<xsl:with-param name="filename" select="'ContextConstants.java'" />
<xsl:with-param name="method" select="'java'" />
<xsl:with-param name="encoding" select="'utf-8'" />
<xsl:with-param name="indent" select="'yes'" />
<xsl:with-param name="quiet" select="$chunk.quietly" />
<xsl:with-param name="content">
package
<xsl:value-of select="$package" />
;
public class ContextConstants{
<xsl:apply-templates select="/*" mode="contextconstants" />
}
</xsl:with-param>
</xsl:call-template>
</xsl:template>
<xsl:template
match="book|part|reference|preface|chapter|bibliography|appendix|article|glossary|section|sect1|sect2|sect3|sect4|sect5|refentry|colophon|bibliodiv|index"
mode="contextconstants">
<xsl:variable name="title">
<xsl:if test="$eclipse.autolabel=1">
<xsl:variable name="label.markup">
<xsl:apply-templates select="." mode="label.markup" />
</xsl:variable>
<xsl:if test="normalize-space($label.markup)">
<xsl:value-of select="concat($label.markup,$autotoc.label.separator)" />
</xsl:if>
</xsl:if>
<xsl:apply-templates select="." mode="title.markup" />
</xsl:variable>
<xsl:variable name="href">
<xsl:call-template name="href.target.with.base.dir">
<xsl:with-param name="context" select="/" />
</xsl:call-template>
</xsl:variable>
<xsl:call-template name="acontextconstant">
<xsl:with-param name="id">
<xsl:call-template name="object.id">
<xsl:with-param name="object" select="." />
</xsl:call-template>
</xsl:with-param>
<xsl:with-param name="label" select="$title" />
<xsl:with-param name="href" select="$href" />
</xsl:call-template>
<xsl:apply-templates
select="part|reference|preface|chapter|bibliography|appendix|article|glossary|section|sect1|sect2|sect3|sect4|sect5|refentry|colophon|bibliodiv|index"
mode="contextconstants" />
</xsl:template>
<xsl:template name="acontextconstant">
<xsl:param name="id" />
<xsl:param name="label" />
<xsl:param name="href" />
public static final String C_
<xsl:value-of select="translate($id,'-','_')" />
="
<xsl:value-of select="$id" />
";
</xsl:template>
</xsl:stylesheet>
If you're using Saxon then you could try saxon:character-representation: see
http://www.saxonica.com/documentation/#!extensions/output-extras/character-representation
Needs Saxon-PE or higher.
However, I'm confused about your requirements. If you want XML output, use method="xml". In that case you won't get a tag generated. If you want a tag but don't want HTML character references, you seem to want some kind of hybrid, and I'm not sure why. Perhaps method="xhtml" will fit your needs?

Sum of attributes after truncating a character from the value

I have the following xml.
<xml>
<table>
<cols width="1.00*" />
<cols width="2.00*" />
<cols width="4.00*" />
<row><p>Hello</p></row>
</table>
<p>
Life is good.
</p>
</xml>
Explaination:
I need to read the column width from the above xml and display. But in some cases user specifies the width so less that the table columns overlap on each other.
Hence I thought to do this formula.
col1width=col1width/totalWidth*100;
This will give me the table width in % format so that the columns get distributed properly.
But I am not able to take a total count of all these attributes. My xslt just does not work. Please see the xslt below:
XSLT:
<xsl:template match="node()" mode="table">
<fo:table table-layout="fixed">
<fo:table-header>
<fo:table-row>
<xsl:for-each select="current()/cols">
<xsl:variable name="maxWidth"
select="number(substring-before(current()/table/cols/#width, '*')) + number(substring-before(following-sibling::cols/#width, '*'))" />
</xsl:for-each>
</fo:table-row>
</fo:table>
Solutions tried:
I have tried using sum function. But here, before summing, i have to
truncate the '*' character and convert to number and then add. Does
not work.
Written a recursive template to get the sum. I am getting the sum with this. But I am not able to return the total width from the
template. I guess xslt does not support returning of calculated
values. Below is the recursive xslt.
<xsl:template name="maximumTableWidth">
<xsl:param name="total" select="0" />
<xsl:param name="totalCols" />
<xsl:param name="index" select="1" />
<xsl:if test="$index <= $totalCols">
<xsl:variable name="maxWidth"
select="$total + translate(current()/cols[$index]/#width, '*', '')" />
<xsl:call-template name="maximumTableWidth">
<xsl:with-param name="total" select="$maxWidth" />
<xsl:with-param name="nodes" select="$totalCols" />
<xsl:with-param name="index" select="$index + 1" />
</xsl:call-template>
</xsl:if>
</xsl:template>
XSLT call:
<xsl:template name="main">
<xsl:variable name="maximumWidth">
<xsl:call-template name="maximumTableWidth">
<xsl:with-param name="total" select="0" />
<xsl:with-param name="totalCols"
select="count(current()/cols)" />
</xsl:call-template>
</xsl:variable>
</xsl:template>
Here, the variable is of type string and hence has no value.
Please help me with this problem. Also can suggest any other approach for table column width. I am generating pdf output using xsl fo. And my whole xslt is dynamic. I cannot have a direct path like node1/node2/node3.
Thank you.
You've got a couple of problems with your maximumTableWidth template to start with. Firstly, you should probably wrap the translate function in the number function
<xsl:variable name="maxWidth"
select="$total + number(translate(current()/cols[$index]/#width, '*', ''))" />
Secondly, you need to make sure you call it with the correct parameters. For your recursive call you set a parameter called nodes, when it should be totalCols
<xsl:with-param name="totalCols" select="$totalCols" />
But in terms of returning a value, all you need to do it use xsl:value-of to output the value, and your maximumWidth variable will then be set to that value. All you need to do is change the xsl:if in the template to an xsl:choose and output the value in the xsl:otherwise condition:
<xsl:template name="maximumTableWidth">
<xsl:param name="total" select="0" />
<xsl:param name="totalCols" />
<xsl:param name="index" select="1" />
<xsl:choose>
<xsl:when test="$index <= $totalCols">
<xsl:variable name="maxWidth"
select="$total + number(translate(current()/cols[$index]/#width, '*', ''))" />
<xsl:call-template name="maximumTableWidth">
<xsl:with-param name="total" select="$maxWidth" />
<xsl:with-param name="totalCols" select="$totalCols" />
<xsl:with-param name="index" select="$index + 1" />
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$total" />
</xsl:otherwise>
</xsl:choose>
</xsl:template>
There is another way to write this template recursively. Instead of passing in the index, and incrementing it, pass in the cols element itself, and use following-sibling to iterate over them. Try this template instead
<xsl:template name="maximumTableWidth">
<xsl:param name="col" />
<xsl:param name="total" select="0" />
<xsl:choose>
<xsl:when test="$col">
<xsl:variable name="maxWidth"
select="$total + number(translate($col/#width, '*', ''))" />
<xsl:call-template name="maximumTableWidth">
<xsl:with-param name="total" select="$maxWidth" />
<xsl:with-param name="col" select="$col/following-sibling::cols[1]" />
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$total" />
</xsl:otherwise>
</xsl:choose>
</xsl:template>
You would call this like so:
<xsl:variable name="maximumWidth">
<xsl:call-template name="maximumTableWidth">
<xsl:with-param name="col" select="cols[1]" />
</xsl:call-template>
</xsl:variable>
<xsl:value-of select="$maximumWidth" />
EDIT: If you were able to use XSLT 2.0, then you can do away with the named template altogether, and just set the maximumWidth template to this
<xsl:variable name="maximumWidth" select="sum(cols/(number(translate(#width, '*', ''))))" />

How do I Emit Escaped XML representation of a Node in my XSLT's HTML Output

I'm transforming XML to an HTML document. In this document I want to embed XML markup for a node that was just transformed (the HTML document is a technical spec).
For example, if my XML was this:
<transform-me>
<node id="1">
<stuff type="floatsam">
<details>Various bits</details>
</stuff>
</node>
</transform-me>
I'd want my XSLT output to look something like this:
<h1>Node 1</h1>
<h2>Stuff (floatsam)</h2>
Various bits
<h2>The XML</h2>
<stuff type="floatsam">
<details>Various bits</details>
</stuff>
I'm hoping there is an XSLT function that I can call in my <stuff> template to which I can pass the current node (.) and get back escaped XML markup for <stuff> and all its descendants. I have a feeling unparsed-text() might be the way to go but can't get it to work.
Very simple template
<xsl:template match="node()" mode="print">
<xsl:choose>
<!-- is it element? -->
<xsl:when test="name()">
<!-- start tag -->
<xsl:text><</xsl:text>
<xsl:value-of select="name()" />
<!-- attributes -->
<xsl:apply-templates select="#*" mode="print" />
<xsl:choose>
<!-- has children -->
<xsl:when test="node()">
<!-- closing bracket -->
<xsl:text>></xsl:text>
<!-- children -->
<xsl:apply-templates mode="print" />
<!-- end tag -->
<xsl:text></</xsl:text>
<xsl:value-of select="name()" />
<xsl:text>></xsl:text>
</xsl:when>
<!-- is empty -->
<xsl:otherwise>
<!-- closing bracket -->
<xsl:text>/></xsl:text>
</xsl:otherwise>
</xsl:choose>
</xsl:when>
<!-- text -->
<xsl:otherwise>
<xsl:copy />
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template match="#*" mode="print">
<xsl:text> </xsl:text>
<xsl:value-of select="name()" />
<xsl:text>="</xsl:text>
<xsl:value-of select="." />
<xsl:text>"</xsl:text>
</xsl:template>
Used
<xsl:apply-templates mode="print" />
You can even add pretty printing if you want.
The answer to this is more complex that you would at first think. Proper "double-escaping" of attribute values and text nodes must occur.
This XSLT 1.0 template does a correct (though not complete) printing of an XML node, including (an attempt to do) proper pretty-printing with configurable indentation:
<xsl:stylesheet
version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
>
<xsl:output method="html" encoding="utf-8" />
<!-- defaults and configurable parameters -->
<xsl:param name="NL" select="'
'" /><!-- newline sequence -->
<xsl:param name="INDENTSEQ" select="' '" /><!-- indent sequence -->
<xsl:variable name="LT" select="'<'" />
<xsl:variable name="GT" select="'>'" />
<xsl:template match="transform-me">
<html>
<body>
<!-- this XML-escapes an entire sub-structure -->
<pre><xsl:apply-templates select="*" mode="XmlEscape" /></pre>
</body>
</html>
</xsl:template>
<!-- element nodes will be handled here, incl. proper indenting -->
<xsl:template match="*" mode="XmlEscape">
<xsl:param name="indent" select="''" />
<xsl:value-of select="concat($indent, $LT, name())" />
<xsl:apply-templates select="#*" mode="XmlEscape" />
<xsl:variable name="HasChildNode" select="node()[not(self::text())]" />
<xsl:variable name="HasChildText" select="text()[normalize-space()]" />
<xsl:choose>
<xsl:when test="$HasChildNode or $HasChildText">
<xsl:value-of select="$GT" />
<xsl:if test="not($HasChildText)">
<xsl:value-of select="$NL" />
</xsl:if>
<!-- render child nodes -->
<xsl:apply-templates mode="XmlEscape" select="node()">
<xsl:with-param name="indent" select="concat($INDENTSEQ, $indent)" />
</xsl:apply-templates>
<xsl:if test="not($HasChildText)">
<xsl:value-of select="$indent" />
</xsl:if>
<xsl:value-of select="concat($LT, '/', name(), $GT, $NL)" />
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="concat(' /', $GT, $NL)" />
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<!-- comments will be handled here -->
<xsl:template match="comment()" mode="XmlEscape">
<xsl:param name="indent" select="''" />
<xsl:value-of select="concat($indent, $LT, '!--', ., '--', $GT, $NL)" />
</xsl:template>
<!-- text nodes will be printed XML-escaped -->
<xsl:template match="text()" mode="XmlEscape">
<xsl:if test="not(normalize-space() = '')">
<xsl:call-template name="XmlEscapeString">
<xsl:with-param name="s" select="." />
<xsl:with-param name="IsAttribute" select="false()" />
</xsl:call-template>
</xsl:if>
</xsl:template>
<!-- attributes become a string: '{name()}="{escaped-value()}"' -->
<xsl:template match="#*" mode="XmlEscape">
<xsl:value-of select="concat(' ', name(), '="')" />
<xsl:call-template name="XmlEscapeString">
<xsl:with-param name="s" select="." />
<xsl:with-param name="IsAttribute" select="true()" />
</xsl:call-template>
<xsl:value-of select="'"'" />
</xsl:template>
<!-- template to XML-escape a string -->
<xsl:template name="XmlEscapeString">
<xsl:param name="s" select="''" />
<xsl:param name="IsAttribute" select="false()" />
<!-- chars &, < and > are never allowed -->
<xsl:variable name="step1">
<xsl:call-template name="StringReplace">
<xsl:with-param name="s" select="$s" />
<xsl:with-param name="search" select="'&'" />
<xsl:with-param name="replace" select="'&amp;'" />
</xsl:call-template>
</xsl:variable>
<xsl:variable name="step2">
<xsl:call-template name="StringReplace">
<xsl:with-param name="s" select="$step1" />
<xsl:with-param name="search" select="'<'" />
<xsl:with-param name="replace" select="'&lt;'" />
</xsl:call-template>
</xsl:variable>
<xsl:variable name="step3">
<xsl:call-template name="StringReplace">
<xsl:with-param name="s" select="$step2" />
<xsl:with-param name="search" select="'>'" />
<xsl:with-param name="replace" select="'&gt;'" />
</xsl:call-template>
</xsl:variable>
<!-- chars ", TAB, CR and LF are never allowed in attributes -->
<xsl:choose>
<xsl:when test="$IsAttribute">
<xsl:variable name="step4">
<xsl:call-template name="StringReplace">
<xsl:with-param name="s" select="$step3" />
<xsl:with-param name="search" select="'"'" />
<xsl:with-param name="replace" select="'&quot;'" />
</xsl:call-template>
</xsl:variable>
<xsl:variable name="step5">
<xsl:call-template name="StringReplace">
<xsl:with-param name="s" select="$step4" />
<xsl:with-param name="search" select="' '" />
<xsl:with-param name="replace" select="'&#x9;'" />
</xsl:call-template>
</xsl:variable>
<xsl:variable name="step6">
<xsl:call-template name="StringReplace">
<xsl:with-param name="s" select="$step5" />
<xsl:with-param name="search" select="'
'" />
<xsl:with-param name="replace" select="'&#xD;'" />
</xsl:call-template>
</xsl:variable>
<xsl:variable name="step7">
<xsl:call-template name="StringReplace">
<xsl:with-param name="s" select="$step6" />
<xsl:with-param name="search" select="'
'" />
<xsl:with-param name="replace" select="'&#xD;'" />
</xsl:call-template>
</xsl:variable>
<xsl:value-of select="$step7" />
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$step3" />
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<!-- generic string replace template -->
<xsl:template name="StringReplace">
<xsl:param name="s" select="''" />
<xsl:param name="search" select="''" />
<xsl:param name="replace" select="''" />
<xsl:choose>
<xsl:when test="contains($s, $search)">
<xsl:value-of select="substring-before($s, $search)" />
<xsl:value-of select="$replace" />
<xsl:variable name="rest" select="substring-after($s, $search)" />
<xsl:if test="$rest">
<xsl:call-template name="StringReplace">
<xsl:with-param name="s" select="$rest" />
<xsl:with-param name="search" select="$search" />
<xsl:with-param name="replace" select="$replace" />
</xsl:call-template>
</xsl:if>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$s" />
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
When applied to this test XML:
<transform-me>
<node id="1">
<!-- a comment -->
<stuff type="fl"&apos;
oatsam">
<details>Various bits & pieces</details>
<details>
</details>
<details attr="value">
<childnode>text and <escaped-text /></childnode>
</details>
</stuff>
</node>
</transform-me>
The following output is produced (source code):
<html>
<body>
<pre><node id="1">
<!-- a comment -->
<stuff type="fl&quot;'&#xD;&#x9;oatsam">
<details>Various bits &amp; pieces</details>
<details />
<details attr="value">
<childnode>text and &lt;escaped-text /&lt;</childnode>
</details>
</stuff>
</node>
</pre>
</body>
</html>
and when viewed in the browser you see:
<node id="1">
<!-- a comment -->
<stuff type="fl"'
 oatsam">
<details>Various bits & pieces</details>
<details />
<details attr="value">
<childnode>text and <escaped-text /<</childnode>
</details>
</stuff>
</node>
Note that by "not complete" I mean that things like namespaces and processing instructions for example are currently unhandled. But its easy to make a template for them.

Trimming URL with XSL

I have some XSL on a page with a URL of:
site.domain.com/dir/dir/dir/dir
The same XSL also appears on a page with URL of:
site.domain.com/dir/dir/dir
I need links from these two pages to point to:
site.domain.com/dir/dir/site
So on both pages I need to get: site.domain.com/dir/dir as part of the HREF attribute.
Can anyone think how I might do this in XSL?
Step-by-step string processing by recursion:
<xsl:template name="trim-url">
<xsl:param name="url" select="''" />
<xsl:param name="lvl" select="0" />
<xsl:if test="$lvl > 0">
<xsl:variable name="tmp" select="concat($url, '/')" />
<xsl:value-of select="substring-before($tmp, '/')" />
<xsl:value-of select="'/'" />
<xsl:call-template name="trim-url">
<xsl:with-param name="url" select="substring-after($tmp, '/')" />
<xsl:with-param name="lvl" select="$lvl - 1" />
</xsl:call-template>
</xsl:if>
</xsl:template>
called as follows:
<xsl:variable name="trimmed-url">
<xsl:call-template name="trim-url">
<xsl:with-param name="url" select="$url" />
<xsl:with-param name="lvl" select="3" />
</xsl:call-template>
<xsl:value-of select="'site'" />
</xsl:variable>
When $url is 'site.domain.com/dir/dir/dir/dir', then $trimmed-url will be 'site.domain.com/dir/dir/site'.

Replacing XML value in XSLT

I can't edit XML, I just want to change XML data in an XSLT file.
<xsl:value-of select="Name" disable-output-escaping="yes"/>
The value of XML data is "Northfield Bancorp Inc.(MHC)" and I want to replace it with "Northfield Bancorp Inc." (remove "MHC").
Is there any function available in XSLT which can search and replace the this?
If it is just the "(MHC)" at the end of the string you want to remove, this would do:
<xsl:value-of select="
substring-before(
concat(Name, '(MHC)'),
'(MHC)'
)
" />
If you want to replace dynamically, you could write a function like this:
<xsl:template name="string-replace">
<xsl:param name="subject" select="''" />
<xsl:param name="search" select="''" />
<xsl:param name="replacement" select="''" />
<xsl:param name="global" select="false()" />
<xsl:choose>
<xsl:when test="contains($subject, $search)">
<xsl:value-of select="substring-before($subject, $search)" />
<xsl:value-of select="$replacement" />
<xsl:variable name="rest" select="substring-after($subject, $search)" />
<xsl:choose>
<xsl:when test="$global">
<xsl:call-template name="string-replace">
<xsl:with-param name="subject" select="$rest" />
<xsl:with-param name="search" select="$search" />
<xsl:with-param name="replacement" select="$replacement" />
<xsl:with-param name="global" select="$global" />
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$rest" />
</xsl:otherwise>
</xsl:choose>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$subject" />
</xsl:otherwise>
</xsl:choose>
</xsl:template>
Which would be callable as:
<xsl:call-template name="string-replace">
<xsl:with-param name="subject" select="Name" />
<xsl:with-param name="search" select="'(MHC)'" />
<xsl:with-param name="replacement" select="''" />
<xsl:with-param name="global" select="true()" />
</xsl:call-template>
XSLT 2.0 has a replace() function.
If you're stuck with 1.0, there is a template in the standard library that can stand in for the lack of a native function:
http://prdownloads.sourceforge.net/xsltsl/xsltsl-1.2.1.zip
http://xsltsl.sourceforge.net/string.html#template.str:subst
another xslt 1.0 template from exslt.org
http://www.exslt.org/str/functions/replace/str.replace.template.xsl