Forward all param from xsl:template to another - xslt

Is it possible to forward all param received in a template to another without knowing them ?
Example :
<xsl:template match="foo">
<xsl:apply-templates select="bar">
<xsl:with-param name="father-id" select="#id"/>
</xsl:apply-templates>
</xsl:template>
<xsl:template match="*" priority="9">
<!-- do some things -->
<xsl:next-match/>
</xsl:template>
<xsl:template match="bar">
<xsl:param name="father-id"/>
<!-- do some things with my param -->
</xsl:template>
Here my param father-id is lost because of my xsl:template match="*".
So, is there a way to forward it at the <xsl:next-match /> step but not using the following code because there can be more cases than this one and with different params ?
<xsl:template match="*" priority="9">
<xsl:param name="father-id"/>
<!-- do some things -->
<xsl:next-match>
<xsl:with-param name="father-id" select="{father-id}"/>
</xsl:next-match>
</xsl:template>
Thank you in advance.
Gerald.

Try it this way:
<xsl:template match="foo">
<xsl:apply-templates select="bar">
<xsl:with-param name="father-id" select="#id" tunnel="yes"/>
</xsl:apply-templates>
</xsl:template>
<xsl:template match="*" priority="9">
<!-- do some things -->
<xsl:next-match/>
</xsl:template>
<xsl:template match="bar">
<xsl:param name="father-id" tunnel="yes"/>
<!-- do some things with my param -->
</xsl:template>
XSLT 2.0 only - but then so is xsl:next-match.
http://www.w3.org/TR/xslt20/#tunnel-params

Related

Is there a way to use mode in an xsl:if test?

I've been learning to use the mode attribute with XSLT and was wondering if there's a way to test for it within a template, such as in an xsl:if statement? I've only seen it used at the xsl:template level and maybe that's the only way. Say I want to add a "../" in front of a path attribute (#href), but only if mode="print":
<xsl:template name="object" mode="#all">
<img>
<xsl:attribute name="src">
<xsl:if test="mode='print'"><xsl:text>../</xsl:text></xsl:if>
<xsl:value-of select="#href"/>
</xsl:attribute>
</img>
</xsl:template>
I'm calling apply-templates with and without the mode="print" set from various other templates.
Of course I can make a new template with mode="print" but then I'd have to maintain two templates.
Or maybe there's a better way to do this? Thanks for the help. - Scott
There is no direct way to do it yet. One approach can be-
<xsl:template match="/">
<xsl:apply-templates select="something" mode="a">
<xsl:with-param name="mode" select="'a'" tunnel="yes"/>
</xsl:apply-templates>
<xsl:apply-templates select="something" mode="b">
<xsl:with-param name="mode" select="'b'" tunnel="yes"/>
</xsl:apply-templates>
</xsl:template>
and then in the match-
<xsl:template match="blah" mode="a b">
<xsl:param name="mode" tunnel="yes"/>
<xsl:if test="$mode='a'">
<!-- Do Something -->
</xsl:if>
<xsl:if test="$mode='b'">
<!-- Do Something -->
</xsl:if>
</xsl:template>
There's no way to get the current mode, but you can do this:
<xsl:template match="object" mode="#all">
<xsl:param name="print" select="false()"/>
<!-- Your code here -->
</xsl:template>
<xsl:template match="object" mode="print">
<xsl:next-match>
<xsl:with-param name="print" select="true()"/>
</xsl:next-match>
</xsl:template>

XSL: match two template with same XPath-expression but different code inside other template

I know, that I can call xsl:apply-templates inside another template, when I specify the XPath-expression of that subtemplate.
In my xsl-file I got an
<xsl:template match="/">
<xsl:apply-templates select="root/values" />
</xsl:template>
<xsl:template match="root/values>
<xsl:value-of select="value/key" />
</xsl:template>
Now I want to do something with subnodes of root/values again in another context - how do I match this template in my main template?
<xsl:template match="root/values>
<xsl:for-each select="value">
<xsl:value-of select="key" />
</xsl:for-each>
</xsl:template>
I think you want to use a mode:
<xsl:template match="/">
<xsl:apply-templates select="root/values" />
<xsl:apply-templates select="root/values" mode="m1" />
</xsl:template>
<xsl:template match="root/values>
<xsl:value-of select="value/key" />
</xsl:template>
<xsl:template match="root/values" mode="m1">
<xsl:for-each select="value">
<xsl:value-of select="key" />
</xsl:for-each>
</xsl:template>

Use xslt replace function to replace a word with an element

I want to usw the XSLT replace function to replace words in a text with
<strong>word</strong>.
I wrote the following template:
<xsl:template name="make-bold">
<xsl:param name="text"/>
<xsl:param name="word"/>
<xsl:variable name="replacement">
<strong><xsl:value-of select="$word"/></strong>
</xsl:variable>
<xsl:value-of select="replace($text, $word, $replacement )" />
</xsl:template>
Unfortunately, and are not rendered, althoug the rest works.
Could anyone help me?
Best, Suidu
Well the replace function http://www.w3.org/TR/xpath-functions/#func-replace takes a string and returns a string. You seem to want to create an element node, not a simple string. In that case using analyze-string http://www.w3.org/TR/xslt20/#analyze-string instead of replace could help.
Here is a sample XSLT 2.0 stylesheet:
<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 method="html" indent="no"/>
<xsl:template match="#* | node()">
<xsl:copy>
<xsl:apply-templates select="#*, node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="p">
<xsl:copy>
<xsl:apply-templates select="#*"/>
<xsl:apply-templates select="text()" mode="wrap">
<xsl:with-param name="words" as="xs:string+" select="('foo', 'bar')"/>
</xsl:apply-templates>
</xsl:copy>
</xsl:template>
<xsl:template match="text()" mode="wrap">
<xsl:param name="words" as="xs:string+"/>
<xsl:param name="wrapper-name" as="xs:string" select="'strong'"/>
<xsl:analyze-string select="." regex="{string-join($words, '|')}">
<xsl:matching-substring>
<xsl:element name="{$wrapper-name}">
<xsl:value-of select="."/>
</xsl:element>
</xsl:matching-substring>
<xsl:non-matching-substring>
<xsl:value-of select="."/>
</xsl:non-matching-substring>
</xsl:analyze-string>
</xsl:template>
</xsl:stylesheet>
When you run that with an XSLT 2.0 processor like Saxon 9 against the following input sample
<html>
<body>
<p>This is an example with foo and bar words.</p>
</body>
</html>
the output is as follows:
<html>
<body>
<p>This is an example with <strong>foo</strong> and <strong>bar</strong> words.</p>
</body>
</html>
hmm is is because here it its the string value that is replaced, you might try to use the node set?
i cannot test as i dont use xslt 2.0 but you might try a recursive template ie
<xsl:template match="yourtextelement">
<xsl:call-template name="MaketextStrong">
</xsl:template>
<xsl:template name="MaketextStrong">
<xsl:param name="text" select="."/>
<xsl:choose>
<xsl:when test="contains($text, 'texttomakestrong')">
<xsl:value-of select="substring-before($text, 'texttomakestrong')"/>
<strong>texttomakestrong</strong>
<xsl:call-template name="break">
<xsl:with-param name="text" select="substring-after($text,
'texttomakestrong')"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$text"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>

XSLT how to choose a template based on an attribute

In a stylesheet I would like to choose a template based on an attribute in the source xml.
Unfortunately it looks as if one cannot use the mode attribute of apply-templates as this must be a qname literal.
Is there any other similar way to do this?
Example:
source xml:
...
<document type="1">
<item>...</item>
</document>
...
stylesheet:
...
<xsl:template match="document">
<xsl:apply-templates select="item" mode="{#type}" />
</xsl:template>
<xsl:template match="item" mode="1">
...
</xsl:template>
<xsl:template match="item" mode="2">
...
</xsl:template>
Easy answer: pattern matching.
<xsl:template match="item[../#type = 'whatever']"/>
Second easy answer: when you need variable or param references (You can't use them in patterns), use xsl:choose instruction.
<xsl:template match="item">
<xsl:param name="pType"/>
<xsl:choose>
<xsl:when test="$pType = 'whatever'">
</xsl:when>
<xsl:when test="$pType = 'otherthing'">
</xsl:when>
</xsl:choose>
</xsl:template>
Complex answer: use named template reference.
<xsl:variavle name="vTemplate" select="document('')/xsl:template/#name"/>
<xsl:template match="xsl:template/#name[.='typeA']" name="typeA">
<xsl:param name="pContext"/>
</xsl:template>
<xsl:template match="xsl:template/#name[.='typeB']" name="typeB">
<xsl:param name="pContext"/>
</xsl:template>
<xsl:template match="document">
<xsl:apply-templates select="$vTemplate[.='typeA']">
<xsl:with-param name="pContext" select="item"/>
</xsl:apply-templates>
</xsl:template>
Or look at Dimitre's FXSL.

Optimisation <xsl:apply-templates/> for a set of tags

How it is possible to reduce this record?
<xsl:template match="BR">
<br/>
</xsl:template>
<xsl:template match="B">
<strong><xsl:apply-templates /></strong>
</xsl:template>
<xsl:template match="STRONG">
<strong><xsl:apply-templates /></strong>
</xsl:template>
<xsl:template match="I">
<em><xsl:apply-templates /></em>
</xsl:template>
<xsl:template match="EM">
<em><xsl:apply-templates /></em>
</xsl:template>
<xsl:template match="OL">
<ol><xsl:apply-templates /></ol>
</xsl:template>
<xsl:template match="UL">
<ul><xsl:apply-templates /></ul>
</xsl:template>
<xsl:template match="LI">
<li><xsl:apply-templates /></li>
</xsl:template>
<xsl:template match="SUB">
<sub><xsl:apply-templates /></sub>
</xsl:template>
<xsl:template match="SUP">
<sup><xsl:apply-templates /></sup>
</xsl:template>
<xsl:template match="NOBR">
<nobr><xsl:apply-templates /></nobr>
</xsl:template>
Maybe something like:
<xsl:template match="LI|SUB|...">
<xsl:element name="{translate(name(),
'ABCDEFGHIJKLMNOPQRSTUVWXYZ','abcdefghijklmnopqrstuvwxyz')}">
<xsl:apply-templates/>
</xsl:element>
</xsl:template>
I don't think, there's a tolower function in XSLT (at least not in 1.0)
If the elements to be created are not known in advance and only a few known elements needs to be processed in another, more specific way, here's a more dynamic solution:
<xsl:template match="*">
<xsl:element name="{translate(name(), $vUpper, $vLower)}">
<xsl:apply-templates select="node()|#*"/>
</xsl:element>
</xsl:template>
where $vUpper and $vLower are defined as:
<xsl:variable name="vUpper" select=
"'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
"/>
<xsl:variable name="vLower" select=
"'abcdefghijklmnopqrstuvwxyz'
"/>
There must be templates matching the few known elements that should not be processed in the above way. These more specific templates will override the more general template above. For example:
<xsl:template match="specificName">
<!-- Specific processing here -->
</xsl:template>
Also, the generic template above, matching elements should be overriding the "identity rule" (template).