count recursiv name-templated calls

Discussion in 'XML' started by Philipp Kraus, Feb 26, 2013.

  1. Hello,

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

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

    and different matches in another file

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

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

    my XML document shows eg



    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

    Philipp Kraus, Feb 26, 2013
    1. Advertisements


    (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

    For other kinds of counting, see

    Joe Kesselman,

    {} ASCII Ribbon Campaign | "may'ron DaroQbe'chugh vaj bIrIQbej" --
    /\ Stamp out HTML mail! | "Put down the squeezebox & nobody gets hurt."
    Joe Kesselman, Feb 26, 2013
    1. Advertisements

  3. 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>

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

    Philipp Kraus, Feb 26, 2013
  4. 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.

    Philipp Kraus, Feb 26, 2013
  5. Note to self: Oh my God... dynamic scoping in XSLT. It's turning mad...
    select provides a default value, in case none is passed. So this won't

    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 -->

    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:apply-templates select="*">
    <xsl:with-param name="depth" select="$depth+1"/>

    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:

    select="count(ancestor::*[name()="node1" or name()="node2"])"/>


    <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.
    Alain Ketterlin, Feb 27, 2013
  6. I am confused, since then does "call-template" take a "select" attribute? does not show any.
    Martin Honnen, Feb 27, 2013
  7. 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.
    Alain Ketterlin, Feb 27, 2013
  8. Yes, I have seen at night. I have found a working solution

    so I can do this
    <xsl:variable name="listindent" select="0" as="xs:integer"

    <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"/>

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

    Philipp Kraus, Feb 27, 2013
  9. 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?

    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.

    Philipp Kraus, Feb 28, 2013
  10. 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,

    {} ASCII Ribbon Campaign | "may'ron DaroQbe'chugh vaj bIrIQbej" --
    /\ Stamp out HTML mail! | "Put down the squeezebox & nobody gets hurt."
    Joe Kesselman, Feb 28, 2013
  11. Do you know another possibility without parameter, because I try to
    decide about a redesign over the "calling XSL" and change all apply
    with the depth paramer. IMHO this shoud be the "optimal solution" but I
    think it's a lot of work


    Philipp Kraus, Feb 28, 2013
  12. I'm not sure I understand correctly. On this fragment:


    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,

    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

    -- Alain.
    Alain Ketterlin, Feb 28, 2013
  13. You're right, but I show you an example

    I have got a XML tree like


    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">

    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:when test="first">
    <xsl:call-template name="header">
    <xsl:when test="second"
    do something without header
    <xsl:when test="third">
    do something
    <xsl:call-template name="header">

    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:text> </xsl:text>
    <xsl:value-of select="$value"/>
    <xsl:call-template name="nl"/>

    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.

    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

    Philipp Kraus, Feb 28, 2013
  14. [...]
    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).
    The relevant section of XSLT2 rec. is
    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:template match="/">
    <xsl:apply-templates select="$intermediate" mode="phase2"/>

    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"/>
    Yes, probably.

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

    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

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

    Philipp Kraus, Feb 28, 2013
  16. 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,

    {} ASCII Ribbon Campaign | "may'ron DaroQbe'chugh vaj bIrIQbej" --
    /\ Stamp out HTML mail! | "Put down the squeezebox & nobody gets hurt."
    Joe Kesselman, Feb 28, 2013
  17. [...]
    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"/>
    ... do whatever is needed on the elements, using $intermediate ...

    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.
    Alain Ketterlin, Feb 28, 2013
  18. 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.

    Philipp Kraus, Mar 1, 2013
  19. Thanks for your great explanation, I have started to restructure the XSL for
    using parameter, but this information is very exciting

    Philipp Kraus, Mar 1, 2013
    1. Advertisements

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 (here). After that, you can post your question and our members will help you out.