choosing to have calls to different templates in xsl - xslt

I have the below template that is xsl:apply template
<xsl:apply-templates
select="fpml:dataDocument/fpml:trade/fpml:swap/fpml:swapStream/fpml:payerPartyReference[starts-with(#href, 'PO')]" />
as shown above it is working fine for 'PO' now i want to make it for CPTY too so I have developed it as shown..
<xsl:apply-templates
select="fpml:dataDocument/fpml:trade/fpml:swap/fpml:swapStream/fpml:payerPartyReference[starts-with(#href, 'CPTY')]" />
but the problem is that there can't be two seprate templates with the same name payerPartyReference can you please advise what is the best approach to deal with this ..
what approach i am thinking is ..
<xsl:if test="fpml:dataDocument/fpml:trade/fpml:swap/fpml:swapStream/fpml:payerPartyReference[starts-with(#href, 'PO')]">
</xsl:if>
<xsl:if test="fpml:dataDocument/fpml:trade/fpml:swap/fpml:swapStream/fpml:payerPartyReference[starts-with(#href, 'CPTY')]">
</xsl:if>

You're right that you can't have two templates with exactly the same matching pattern, but you can have
<xsl:template match="fpml:payerPartyReference[starts-with(#href, 'PO')]">
<!-- ... -->
</xsl:template>
<xsl:template match="fpml:payerPartyReference[starts-with(#href, 'CPTY')]">
<!-- ... -->
</xsl:template>
With these separate templates in place you may find you don't need to split out the apply-templates. Depending on the precise details of your problem you might find that you can just do one
<xsl:apply-templates
select="fpml:dataDocument/fpml:trade/fpml:swap/fpml:swapStream/fpml:payerPartyReference" />
and let the template matcher handle the conditional behaviour by picking the appropriate matching template for each target node.

Related

xslt pass parameters throuh all apply-template calls

In a rather complex xslt file some elements are to be processed twice. This is done by a template with a paramater.
<xsl:template macht="x">
<xsl:param name="modus"/>
<!-- comon things to do for both cases -->
<xsl:choose>
<xsl:when test="$modus='case1'"> <!-- things to do in case 1 --> </xsl:when>
<xsl:when test="$modus='case2'"> <!-- things to do in case 2 --> </xsl:when>
</xsl:choose>
</xsl:template>
The problem is: I cannot simply apply or call this template directly. The element x (for which this template with these two cases is for) is often at a quite low level of the xml input. Almost all ancestor elements have to be processed (in both cases) before it actually comes to x.
The call for the two cases is at the allmost top level.
In html it would be like this
<body>
<h1>Case 1</h1>
<xsl:apply-templates><xsl:with-parameter name="modus" select="case1"/>
<h1>Case 2</h1>
<xsl:apply-templates><xsl:with-parameter name="modus" select="case2"/>
</body>
So. How can I make sure, that the parameter reaches the template for x?
Of course, I could replace all
<xsl:apply-templates/>
calls within the templates for every single ancestor element of x by
<xsl:param name="modus">
<!-- What ever content here -->
<xsl:apply-templates><xsl:with-parameter name="modus" select="$modus"/></apply-templates>
But that would mean a lot of effort. Is there a better way to do this?
XSLT 2.0 has tunnel parameters e.g. with
<xsl:apply-templates>
<xsl:with-param name="modus" tunnel="yes" select="'foo'"/>
</xsl:apply-templates>
and
<xsl:template match="bar">
<xsl:param name="modus" tunnel="yes"/>
...
</xsl:template>
you don't have to pass on the parameter explicitly in the templates for ancestors of bar. So using an XSLT 2.0 processor like Saxon 9 you can do that.

xslt any way to get the name of calling template from within a template

I am very new to XSLT. I was wondering if there is any way to get the name of calling template from within a template.
I currently got the following with a little complex structure. One template is included once directly and once via another template. I need to add a new tag to this template only if it is called from a specific template.
<xsl:element name="parent">
<xsl:choose>
<xsl:when test="$myVariable = 'process1'">
<xsl:call-template name="templateA"/>
</xsl:when>
<xsl:otherwise>
<xsl:call-template name="templateB"/>
</xsl:otherwise>
</xsl:choose>
</xsl:element>
<xsl:template name="templateA">
<!-- Some Other Tags Here -->
<xsl:call-template name="templateB />"
</xsl:template>
<xsl:template name="templateb"> <!-- very big template -->
<!-- existing tags here -->
<!-- Add a new tag here only if called via templateA -->
</xsl:template>
To be clear,
As you can see, templateB is included either way, but templateA adds some more tags then includes templateB.
I want to add a new tag to templateB only if it is called from templateA. Is it possible to do?
You could use parameter
<xsl:template name="templateB"> <!-- very big template -->
<xsl:param name="calledFrom" select="" />
<!-- existing tags here -->
<xsl:if test="$calledFrom = 'templateA">
<!-- Add a new tag here only if called via templateA -->
</xsl:if>
</xsl:template>
And then called it in this way
<xsl:call-template name="templateB">
<xsl:with-param name="calledFrom" select="'templateA'" />
</xsl:call-template>
If a function/template needs to know where it was called from, then there's something wrong with the design. Passing a parameter is of course the immediate way to fix the code, but piling on parameters and adding conditional logic based on the parameter values leads to unmaintainable spaghetti.
There's not enough of your code here to assess the design, but I would ask why it's not making more use of template rules rather than named templates. It might well be that judicious use of apply-templates would solve the problem much more naturally.
Passing the parameter is the solution, I was not aware of if they are passed in nested templates.
The solution that proper suits my scenario is tunnel-params.
Parameters are tunneled(passed on ) to template called by default in xslt 2.0, but in xslt 1.0 we need to specify tunnel="yes". With tunelling myVariable can be accessible to the template called.

Generic way to pass XSL param to templates

I have several stylesheets and templates, and I'd like to add some behaviour to all of them.
Let's say something like that :
<xsl:template match="*" priority='10'>
<xsl:apply-templates select="." mode="someFunStuffsToDo"/>
<xsl:next-match/>
</xsl:template>
But, as i need some generic templates, i have issues with params because I don't know all the different types of param I could have.
Is there any 'easy' way to say something like that :
<xsl:template match="*" priority='10'>
<xsl:param select="All the params you get"/>
<xsl:apply-templates select="." mode="someFunStuffsToDo">
<xsl:with-param select="All the params you got"/>
</xsl:apply-templates>
<xsl:next-match/>
</xsl:template>
I could imagine some solution based on generic param which would contain nodes of params but I'd need to rewrite most of my actual templates to switch different specifics params declaration for the generic one...
EDIT :
Ok, I think I just find a solution just before posting my question :
Tunnel parameters.
Is that working for my purpose as I understand it, i mean
<xsl:template match="*" priority='10'>
<xsl:apply-templates select="." mode="someFunStuffsToDo"/>
<xsl:next-match/>
</xsl:template>
will work if i just set my parameters before and after with the attribute tunnel='yes' ?
(I haven't tested yet and shame on me, but i assume that next-match will preserve the current mode)
Yes, tunnel parameters help, you however need to make sure your passing code and your receiving templates have the <xsl:with-param name="foo" tunnel="yes" select="bar"/> respectively <xsl:param name="foo" tunnel="yes"/>. But any templates in between, like the one you have, will then not need a with-param.

Re-using nested XSL templates

I am trying to re-use a XSL template, and place other templates within this template, multiple times.
Here's an example of my code:
<xsl:template name="wrapper">
<div>
<xsl:apply-templates/>
</div>
</xsl:template>
<xsl:template name="template1"></xsl:template>
<xsl:template name="template2"></xsl:template>
So, now i want to apply both template 1 and template 2 inside template 'wrapper', something like this (I know this isn't the right code, but the idea is there):
<xsl:template name="template1">
<xsl:template match="wrapper">
<!--code here-->
</xsl:template>
</xsl:template>
<xsl:template name="template2">
<xsl:template match="wrapper">
<!--code here-->
</xsl:template>
</xsl:template>
Any help on this would be grealty appreciated.
It is syntactically illegal to nest a template definition into another.
As per the W3C XSLT (both 1.0 and 2.0) specification, an xsl:template must be a child of the top element xsl:stylesheet.
This means that all templates in a stylesheet module must be siblings.
The way to invoke a named template is to use the xsl:call-template instruction like this:
<xsl:call-template name="someTemplateName">
<!-- Possibly place one or more `xsl:with-param` elements here -->
</xsl:call-template>
However, beaware that:
It is a good style and more in the spirit of XSLT to use unnamed templates (that have a match attribute) and to select the best matching template with an xsl:apply-templates instruction.
Most of the answers to SO XSLT questions demonstrate the use of xsl:apply-templates.
So, now i want to apply both template 1 and template 2 inside template 'wrapper',
If I take this literally:
<xsl:template name="wrapper">
<xsl:call-template name="template1" />
<xsl:call-template name="template2" />
</xsl:template>
But I have a strong gut feeling that you're somehow shooting yourself in the foot here.

Can an XSL template match in *ALL* modes?

Is there a way to write an XSL 1.0 template which is matching in all modes?
Or do I have to write a separate template for every existing mode (including additional templates for modes being added in the future)?
Here is what I have:
<xsl:apply-templates mode="mode1" />
...
<xsl:apply-templates mode="mode2" />
...
<!-- Do not process text content of nodes no matter in what mode -->
<!-- Is there a way to have only one template here? -->
<xsl:template match="text()" mode="mode1" />
<xsl:template match="text()" mode="mode2" />
The predefined mode: #all (only available in XSLT 2.0 however).
edit: replicating shared mode behaviour with 1.0
<xsl:template match="/">
<xsl:variable name="choice" select="'a'"/><!-- input seed here -->
<xsl:choose>
<xsl:when test="$choice='a'">
<xsl:apply-templates mode="a"/>
</xsl:when>
<xsl:when test="$choice='b'">
<xsl:apply-templates mode="b"/>
</xsl:when>
</xsl:choose>
</xsl:template>
<xsl:template match="*" mode="a">
[A]
<xsl:apply-templates />
</xsl:template>
<xsl:template match="*" mode="b">
[B]
<xsl:apply-templates />
</xsl:template>
<xsl:template match="text()">
[ALL]
</xsl:template>
Is there a way to write an XSL 1.0
template which is matching in all
modes
Yes, in order to do this one should follow these two rules:
Write your template without a mode attribute.
Within the moded templates have an <xsl:apply-templates> instruction without a mode attribute that will result in the template in 1. above being selected for processing
This follows directly from the XSLT 1.0 spec, which says:
If an xsl:apply-templates element has
a mode attribute, then it applies only
to those template rules from
xsl:template elements that have a mode
attribute with the same value; if an
xsl:apply-templates element does not
have a mode attribute, then it applies
only to those template rules from
xsl:template elements that do not have
a mode attribute.
To summarise: A set of templates each in a different mode can still issue <xsl:apply-templates> in such a way (described above), so that the same specific, single template will be selected for processing in each case.
If you want to have the template match in all modes then why are you using mode? If you don't use mode then the template will be used all the time. The reason for mode is to conditionally do different things with the same data type. Seems like you want modeless.