count recursiv name-templated calls

P

Philipp Kraus

Hello,

I would like to work with XSLT (2.0). I have got code like

<xsl:template name="item">
<xsl:text>?? test</xsl:text>
</xsl:template>


and different matches in another file

<xsl:template match="node1"
<xsl:call-template name="item"/>
</xsl:template>

<xsl:template match="node2"
<xsl:call-template name="item"/>
</xsl:template>

my XML document shows eg

<node1>
</node1>

<node1>
<node2>
<node1/>
</node2>
</node1>

I would like to push the number of the recursiv-template matches of the
item template
on the position of ??. In the example XML I will get the numbers 1, 1, 2, 3
The item template should count itself the number of its recursiv calls

Thanks a lot

Phil
 
J

Joe Kesselman

<xsl:text>?? test</xsl:text>
I would like to push the number of the recursiv-template matches of the
item template
The item template should count itself the number of its recursiv calls

http://www.w3.org/TR/xslt20/#parameters

(That's supported in XSLT 1.0 as well, of course.)

Note that it's your responsibility to explicitly pass the parameter
value down the template call/apply tree.

I haven't checked, but examples can be undoubtedly be found at
http://www.dpawson.co.uk/xsl/sect2/N5982.html

For other kinds of counting, see
http://www.dpawson.co.uk/xsl/sect2/N2018.html

--
Joe Kesselman,
http://www.love-song-productions.com/people/keshlam/index.html

{} ASCII Ribbon Campaign | "may'ron DaroQbe'chugh vaj bIrIQbej" --
/\ Stamp out HTML mail! | "Put down the squeezebox & nobody gets hurt."
 
P

Philipp Kraus

http://www.w3.org/TR/xslt20/#parameters

(That's supported in XSLT 1.0 as well, of course.)

Note that it's your responsibility to explicitly pass the parameter
value down the template call/apply tree.

I haven't checked, but examples can be undoubtedly be found at
http://www.dpawson.co.uk/xsl/sect2/N5982.html

For other kinds of counting, see
http://www.dpawson.co.uk/xsl/sect2/N2018.html

I have found the "tunnel" argument on the template, IMHO this can count
the template calls,
so I have modified my template to

<xsl:template name="item">
<xsl:param name="depth" select="$depth+1" tunnel="yes"/>
<xsl:value-of select="$deph"/>
<xsl:text> test</xsl:text>
</xsl:template>

so now I get the error, that depth is not initialized. How can I
initialize and increment
the tunnel parameter?

Phil
 
P

Philipp Kraus

http://www.w3.org/TR/xslt20/#parameters

(That's supported in XSLT 1.0 as well, of course.)

Note that it's your responsibility to explicitly pass the parameter
value down the template call/apply tree.

I haven't checked, but examples can be undoubtedly be found at
http://www.dpawson.co.uk/xsl/sect2/N5982.html

For other kinds of counting, see
http://www.dpawson.co.uk/xsl/sect2/N2018.html

I have got some problems to modify the code.
I have a complex XSL file, in which are a lot of apply templates and a lot of
call of my "item" template.

I must create a plain text structure

# section1
## section 2
### section 3

# section 4

On each call of my item template a counter must be increment, that counts the
recursiv calls of the item template. Different XML nodes can be call
the item template
from different nodes on the XSL document, so IMHO the best solution is
that my item
template counts the recursiv calls.
I can not use the XML structure, because my template calls are
different because in some case
if / chose calls are enable / disable the item template call.

Phil
 
A

Alain Ketterlin

Philipp Kraus said:
[...]

I have found the "tunnel" argument on the template,

Note to self: Oh my God... dynamic scoping in XSLT. It's turning mad...
IMHO this can count the template calls, so I have modified my template
to

<xsl:template name="item">
<xsl:param name="depth" select="$depth+1" tunnel="yes"/>
<xsl:value-of select="$deph"/>
<xsl:text> test</xsl:text>
</xsl:template>

select provides a default value, in case none is passed. So this won't
work.

You haven't shown us how you call the templates on node1 or node2.
That's where all the work would go. AFAIU there is no need of tunneling
here, or at least tunneling doesn't fit your requirements (tunnel just
passes the values, you need them to be incremented).

So, how do the recursive calls happen? In the following, I assume you
have something like that:

<xsl:template match="node1">
<xsl:call-template name="item" select="."/>
<xsl:apply-templates select="*"/> <!-- recurse -->
</xsl:template>

and the same for node2 (or match="node1|node2" on this template).

Now let's add the "depth" parameter:

<xsl:template match="node1">
<xsl:param name="depth" select="0"/>
<xsl:call-template name="item" select=".">
<xsl:with-param name="depth" select="$depth"/>
</xsl:call-template>
<xsl:apply-templates select="*">
<xsl:with-param name="depth" select="$depth+1"/>
</xsl:template>
</xsl:template>

Note that select is used to provide zero if no parameter is passed to
the "node1" template". This does the initialization. Of course you're
free to pass whatever else you want (which is what happens for the
potentially recursive calls done by <xsl:apply-templates>).

AFAIU, using tunneling could save the first <xsl:with-param>. I'm not
sure this is worth the trouble; you decide.

BTW, if you want to know how many node1 or node2 are above the current
node, you can use:

<xsl:value-of
select="count(ancestor::*[name()="node1" or name()="node2"])"/>

or

<xsl:value-of select="count(ancestor::node1)+count(ancestor::node2)"/>

This is off by 1, use ancestor-or-self to consider also the current
node. It also assumes no namespace is in use.

-- Alain.
 
M

Martin Honnen

Alain said:
So, how do the recursive calls happen? In the following, I assume you
have something like that:

<xsl:template match="node1">
<xsl:call-template name="item" select="."/>
<xsl:apply-templates select="*"/> <!-- recurse -->
</xsl:template>

and the same for node2 (or match="node1|node2" on this template).

Now let's add the "depth" parameter:

<xsl:template match="node1">
<xsl:param name="depth" select="0"/>
<xsl:call-template name="item" select=".">
<xsl:with-param name="depth" select="$depth"/>
</xsl:call-template>

I am confused, since then does "call-template" take a "select" attribute?

http://www.w3.org/TR/xslt20/#named-templates does not show any.
 
A

Alain Ketterlin

Martin Honnen said:
Alain Ketterlin wrote:

I am confused, since then does "call-template" take a "select"
attribute?

You know the answer: I've just invented this nonsense. Even if it
existed it would be useless anyway since call-template doesn't change
the current node.

-- Alain.
 
P

Philipp Kraus

Philipp Kraus said:
On 2/26/2013 12:25 PM, Philipp Kraus wrote:

I would like to push the number of the recursiv-template matches of
the item template The item template should count itself the number
of its recursiv calls

http://www.w3.org/TR/xslt20/#parameters
[...]

I have found the "tunnel" argument on the template,

Note to self: Oh my God... dynamic scoping in XSLT. It's turning mad...
IMHO this can count the template calls, so I have modified my template
to

<xsl:template name="item">
<xsl:param name="depth" select="$depth+1" tunnel="yes"/>
<xsl:value-of select="$deph"/>
<xsl:text> test</xsl:text>
</xsl:template>

select provides a default value, in case none is passed. So this won't
work.

You haven't shown us how you call the templates on node1 or node2.
That's where all the work would go. AFAIU there is no need of tunneling
here, or at least tunneling doesn't fit your requirements (tunnel just
passes the values, you need them to be incremented).

Yes, I have seen at night. I have found a working solution

http://saxon.sourceforge.net/saxon7.9.1/extensions.html#assignable

so I can do this
<xsl:variable name="listindent" select="0" as="xs:integer"
saxon:assignable="yes"/>

<xsl:template name="listitem">
<xsl:param name="value"/>
<xsl:param name="item" select="'*'"/>

<xsl:call-template name="listindent"/>
<saxon:assign name="listindent" select="$listindent+1"/>

<xsl:value-of select="$item"/>
<xsl:text> </xsl:text>
<xsl:value-of select="$value"/>
<xsl:call-template name="nl"/>

<saxon:assign name="listindent" select="-1+$listindent"/>
</xsl:template>

If the $value also a listitem, the listindent stores the correct value,
but it is a non-generic

Phil
 
P

Philipp Kraus

Philipp Kraus said:
On 2/26/2013 12:25 PM, Philipp Kraus wrote:

I would like to push the number of the recursiv-template matches of
the item template The item template should count itself the number
of its recursiv calls

http://www.w3.org/TR/xslt20/#parameters
[...]

[...]


<xsl:template match="node1">
<xsl:param name="depth" select="0"/>
<xsl:call-template name="item" select=".">
<xsl:with-param name="depth" select="$depth"/>
</xsl:call-template>
<xsl:apply-templates select="*">
<xsl:with-param name="depth" select="$depth+1"/>
</xsl:template>
</xsl:template>

Note that select is used to provide zero if no parameter is passed to
the "node1" template". This does the initialization. Of course you're
free to pass whatever else you want (which is what happens for the
potentially recursive calls done by <xsl:apply-templates>).

This would be my first try but I have posted a short example of my problem.
The calls for the "item" template are within another XSL file, which can not be
changed (at the moment), so I can modify only the item template and need
the depth counter in this template.

With your solution i must modify each item call and each apply call, is
this correct?
But in this way on each apply the depth counter will be increment, or does it?
So $depth stores not the recursion depth of item but rather the depth
of the apply calls,
do I understand it correct?

BTW, if you want to know how many node1 or node2 are above the current
node, you can use:

<xsl:value-of
select="count(ancestor::*[name()="node1" or name()="node2"])"/>

or

<xsl:value-of select="count(ancestor::node1)+count(ancestor::node2)"/>

If I understand it correct, this value returns the depth of the "node
recursion of my XML tree".

In my problem, I want to count the recursiv call of a single template
here item, so if the parser
runs over my XML nodes and calls the item template and within the
descent of the XML node item
again, I will get the value 2, because item is recursive called twice.

Phil
 
J

Joe Kesselman

If I understand it correct, this value returns the depth of the "node
recursion of my XML tree".
In my problem, I want to count the recursiv call of a single template

That was my understanding, which is why I pointed you to parameters.

Many "counting" tasks in XSLT really are better handled by using the
numbering facilities designed into the language -- but those are
generally focused on enumerating positions in the document tree rather
than call depth, so they wouldn't have been the right answer for the
question you asked.

--
Joe Kesselman,
http://www.love-song-productions.com/people/keshlam/index.html

{} ASCII Ribbon Campaign | "may'ron DaroQbe'chugh vaj bIrIQbej" --
/\ Stamp out HTML mail! | "Put down the squeezebox & nobody gets hurt."
 
P

Philipp Kraus

That was my understanding, which is why I pointed you to parameters.

Many "counting" tasks in XSLT really are better handled by using the
numbering facilities designed into the language -- but those are
generally focused on enumerating positions in the document tree rather
than call depth, so they wouldn't have been the right answer for the
question you asked.

Do you know another possibility without parameter, because I try to
decide about a redesign over the "calling XSL" and change all apply
calls
with the depth paramer. IMHO this shoud be the "optimal solution" but I
think it's a lot of work

Thanks

Phil
 
A

Alain Ketterlin

Philipp Kraus said:
But in this way on each apply the depth counter will be increment, or
does it? So $depth stores not the recursion depth of item but rather
the depth of the apply calls, do I understand it correct?

I'm not sure I understand correctly. On this fragment:

<item>
<item>...</item>
<item>...</item>
</item>

You'll get 0, 1, 1.

I now realize that maybe you want 0,1,2 (or 1,2,3). If that is the case,
sorry, I was misled by your use of the term "recursive calls". You want
the number of past calls, right? Can you give us a short example,
please?

If you want sequential numbering in document order, you can use
select="count(preceding::item)" (or whatever you node types are).

If you sort adjacent <item> during traversal, it gets more complex
because you can't rely on document order. In this case I would suggest
to first traverse your structure to generate a table associating numbers
to nodes inside a result tree fragment, use exslt:document() to get a
usable form of the table, and then retraverse the document to output the
result.

-- Alain.
 
P

Philipp Kraus

I'm not sure I understand correctly. On this fragment:

<item>
<item>...</item>
<item>...</item>
</item>

You'll get 0, 1, 1.

I now realize that maybe you want 0,1,2 (or 1,2,3). If that is the case,
sorry, I was misled by your use of the term "recursive calls". You want
the number of past calls, right? Can you give us a short example,
please?

You're right, but I show you an example

I have got a XML tree like
<component>
<subcomponent>
<subsubcomponent>
</subcomponent>

<componentdef>
<subcomponent>
....
</.>
</.>
</.>


my XSL will first match the component, so that I can create the first
section header
#
the next it will match the subcomponent twice
##
##

But the exact matches are in real depend on different if-else / choose
structures, so
that I have eg
<xsl:template match="subcomponent">
<xsl:if text="...">
<xsl:call-template name="header">
...
</xsl:call-template>
</xsl:if>
</xsl:template>

So not every match creates a header in the output (here I create
plain-text output),
eg I can have also

<xsl:template match="something-else">
<xsl:choose>
<xsl:when test="first">
<xsl:call-template name="header">
...
</xsl:call-template>
</xsl:when>
<xsl:when test="second"
do something without header
</xsl:when>
<xsl:when test="third">
do something
<xsl:call-template name="header">
...
</xsl:call-template>
</xsl:when>
</xsl:choose>
</xsl:template>

All templates matches, that creates a headline call one central template
<xsl:template name="header">
<xsl:param name="value" required="yes"/>

<xsl:for-each select="1 to $indent">
<xsl:call-template name="head"/>
</xsl:for-each>

<xsl:text> </xsl:text>
<xsl:value-of select="$value"/>
<xsl:call-template name="nl"/>
</xsl:template>

so my problem is, that I need a value for the $indent variable, that I can
create the correct number of header indents ($value is the header title).

So I need the recursion depth of my header template, that depends on
various call on the input XML tree. The header calls are on different
position in the XSL, that analyses the XML tree.
So the efficient way is, that my header template can detect how often it is
called recursiv.

If you sort adjacent <item> during traversal, it gets more complex
because you can't rely on document order. In this case I would suggest
to first traverse your structure to generate a table associating numbers
to nodes inside a result tree fragment, use exslt:document() to get a
usable form of the table, and then retraverse the document to output the
result.

That's new for me, I can traversel first over my XML tree, create a table with
data and after that the normal XSL traversel starts, so that I can read data
from the table? Do you have an example or a link?

In my case I would traversel the XML nodes, catch the depth of my headers
and will set the $indent var in the second run from the table.

Thanks a lot

Phil
 
A

Alain Ketterlin

[...]
That's new for me, I can traversel first over my XML tree, create a
table with data and after that the normal XSL traversel starts, so
that I can read data from the table?

Yes. Table is an inadequate word, sorry, actually you can produce an
intermediate document, that you then process in a second phase.
Conceptually it is equivalent to have two transformations in sequence,
the second one operating on the result of the first one.

I was mentioning exsl:document(), this was completely wrong, I meant
exsl:node-set() (a more or less standard extension function for XSLT1)
but if you use XSLT2 it looks like this has been integrated (search for
"temporary tree" in the XSLT2 recomm).
Do you have an example or a link?

The relevant section of XSLT2 rec. is
http://www.w3.org/TR/xslt20/#temporary-trees
It looks like this replaces the "result-tree-fragment" thing of XSLT1.
This section has en example, whose relevant part is:

<xsl:variable name="intermediate">
<xsl:apply-templates select="/" mode="phase1"/>
</xsl:variable>

<xsl:template match="/">
<xsl:apply-templates select="$intermediate" mode="phase2"/>
</xsl:template>

i.e., you put the result of the first traversal in a variable, and then
you launch the second traversal on the contents of this variable
(instead of on some part of the original document).

If you're stuck with XSLT1, you can use exsl:node-set() instead

<xsl:template match="/">
<xsl:apply-templates select="exsl:node-set($intermediate)" mode="phase2"/>
In my case I would traversel the XML nodes, catch the depth of my
headers and will set the $indent var in the second run from the table.

Yes, probably.

-- Alain.
 
P

Philipp Kraus

On 2013-02-28 15:35:25 +0100, Alain Ketterlin said:

[..]
The relevant section of XSLT2 rec. is
http://www.w3.org/TR/xslt20/#temporary-trees
It looks like this replaces the "result-tree-fragment" thing of XSLT1.
This section has en example, whose relevant part is:

<xsl:variable name="intermediate">
<xsl:apply-templates select="/" mode="phase1"/>
</xsl:variable>

<xsl:template match="/">
<xsl:apply-templates select="$intermediate" mode="phase2"/>
</xsl:template>

I have got only XSL 2 documents. You differ with the mode attribute the two
traverls. How can I tell the XSL processor, that it should start with phase1,
IMHO the position of my first match is the correct definition, or does it?

Do I need a duplicated rule set? I have got a lot of matches without a mode
attribute, so IMHO I need only the "phase1" mode and must assure that is run
first, after that I can use my normal nodes and get the data from the variable.
So how can I tell the processor, that it run the phase1 first and do
the non-phase1
nodes?

Thanks for your great information and discussion, there are many new
informations,
to understand XSLT better

Phil
 
J

Joe Kesselman

Do you know another possibility without parameter

Not in XSLT. Because XSLT may execute out of order, stateful extension
functions wouldn't do it -- unless you passed parameters to set up data
dependencies to control the order, in which case you might as well just
use the params.

Of course you could step outside XSLT entirely and handcode XML processing.

The right answer may be to ask why you think you need this
recursion-depth value at all, and whether you can restructure the
problem to avoid it.

--
Joe Kesselman,
http://www.love-song-productions.com/people/keshlam/index.html

{} ASCII Ribbon Campaign | "may'ron DaroQbe'chugh vaj bIrIQbej" --
/\ Stamp out HTML mail! | "Put down the squeezebox & nobody gets hurt."
 
A

Alain Ketterlin

[...]
I have got only XSL 2 documents. You differ with the mode attribute the two
traverls. How can I tell the XSL processor, that it should start with phase1,
IMHO the position of my first match is the correct definition, or does it?

Do I need a duplicated rule set?
Yes.

I have got a lot of matches without a mode attribute, so IMHO I need
only the "phase1" mode and must assure that is run first, after that I
can use my normal nodes and get the data from the variable. So how can
I tell the processor, that it run the phase1 first and do the
non-phase1 nodes?

phase1 will run first, you can simply remove mode="phase2" in
<xsl:apply-templates> to have the default-mode templates apply.

Note that the variable is global in the example, but that's an artefact
of the example. You could have:

<xsl:template match="topmost-element-of-interest">
<xsl:variable name="intermediate">
<xsl:apply-templates select="." mode="phase1"/>
</xsl:variable>
... do whatever is needed on the elements, using $intermediate ...
</xsl:template>

The variable is a node-set, you can apply-templates to it, pass it as
parameter, etc. The elements it contains can follow any structure that
is convenient for its later usage.

-- Alain.
 
P

Philipp Kraus

That was my understanding, which is why I pointed you to parameters.

Many "counting" tasks in XSLT really are better handled by using the
numbering facilities designed into the language -- but those are
generally focused on enumerating positions in the document tree rather
than call depth, so they wouldn't have been the right answer for the
question you asked.

Thank you too all helping people. I have start to restructer my calling
XSL, so that I can pass a parameter from the apply rules to
the name templates.

Phil
 
P

Philipp Kraus

[...]
I have got only XSL 2 documents. You differ with the mode attribute the two
traverls. How can I tell the XSL processor, that it should start with phase1,
IMHO the position of my first match is the correct definition, or does it?

Do I need a duplicated rule set?
Yes.

I have got a lot of matches without a mode attribute, so IMHO I need
only the "phase1" mode and must assure that is run first, after that I
can use my normal nodes and get the data from the variable. So how can
I tell the processor, that it run the phase1 first and do the
non-phase1 nodes?

phase1 will run first, you can simply remove mode="phase2" in
<xsl:apply-templates> to have the default-mode templates apply.

Note that the variable is global in the example, but that's an artefact
of the example. You could have:

<xsl:template match="topmost-element-of-interest">
<xsl:variable name="intermediate">
<xsl:apply-templates select="." mode="phase1"/>
</xsl:variable>
... do whatever is needed on the elements, using $intermediate ...
</xsl:template>

The variable is a node-set, you can apply-templates to it, pass it as
parameter, etc. The elements it contains can follow any structure that
is convenient for its later usage.

Thanks for your great explanation, I have started to restructure the XSL for
using parameter, but this information is very exciting

Phil
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,828
Messages
2,569,736
Members
45,520
Latest member
JudithMorf

Latest Threads

Top