• Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 841
  • Last Modified:

Insert a counter in XSL

Hi, i must insert ,a counter in a for-each xsl function.
This is part of the code:
counter0 = 0
counter1 = 0
<xsl:for-each select="Component/item">
<TABLE WIDTH="100%" BORDER="0" CELLSPACING="0" CELLPADDING="0">
<TR>
      <TD onclick="showMovieTags()" style="cursor:hand"><SPAN class="openTag"><![CDATA[<]]></SPAN><SPAN class="tagName">item</SPAN><SPAN class="openTag">></SPAN></TD>
      <TD>
counter0++

 </TD>
</TR>
<xsl:for-each select="sub_item">
<TR id="tagTR" name="tagTR" style="display:block">
      <TD>
      <xsl:value-of select="."/>
      </TD>
counter1++      
</TR>
.......

Is very urgent...
Thank you.
0
damianbek
Asked:
damianbek
3 Solutions
 
jkmyoungCommented:
To do this easily, either write an extension function or use a parser dependent function.
eg. if you're using saxon:  
<xsl:variable name="counter0" select="0" saxon:assignable="yes"/>
and
<saxon:assign name="counter0" select="$counter0 + 1"/>

or if you want to use an extension function, call a static function, or something that uses a static variable. not sure how much you already know about that.
0
 
jkmyoungCommented:
If you simply want a final count of all of them use the count function!!!
eg. select="count(Component/item)"
select="count(Component/item/sub_item)"

However, if you want to keep track of each, and those other 2 ways are not options, you could always do it using xpath: the count and position functions with conditions.
eg partial pseudocode:
<xsl:for-each select="Component">
<xsl:variable name="pos1" select="position()"/>
<xsl:for-each select="item">
<xsl:variable name="pos2" select="position()"/>

counter0: select="count(../../Component[position() &lt; $pos1]/item) + count(../item[position() &lt; $pos2])"

<xsl:for-each select="sub_item">
<xsl:variable name="pos3" select="position()"/>

counter1: select="count(../../../Component[position() &lt; $pos1]/item/sub_item)
                         + count(../../item[position() &lt; $pos2]/sub_item)
                         + count(../sub_item[position() &lt; $pos3])"
...
This won't be fast though...
0
 
damianbekAuthor Commented:
They are a more simpler method, like define variables, or insert javascript code in XSL??
0
Independent Software Vendors: We Want Your Opinion

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

 
MogalManicCommented:
This should work
...
<xsl:for-each select="Component/item">
    <TABLE WIDTH="100%" BORDER="0" CELLSPACING="0" CELLPADDING="0">
        <xsl:variable name='itemPos' select='position()'/>
        <TR>
            <TD onclick="showMovieTags()" style="cursor:hand">
                <SPAN class="openTag"><![CDATA[<]]></SPAN>
                <SPAN class="tagName">item</SPAN>
                <SPAN class="openTag">></SPAN>
            </TD>
            <TD>
                <xsl:variable select='$itemPos'/>
            </TD>
        </TR>
        <xsl:for-each select="sub_item">
            <xsl:variable name='subPos' select='position()'/>
            <TR id="tagTR" name="tagTR" style="display:block">
                <TD>
                    <xsl:value-of select="."/>
                </TD>
            </TR>
        </xsl:for-each>
    </TABLE>
</xsl:for-each>
...
0
 
rdcproCommented:
A couple comments:

it's not necessary to write extensions or parser dependant functions--a counter is easily created with recursive template:

http://rdcpro.com/Members/rdcpro/snippets/recursionandcounting/

If you do use an extension function to implement a counter, remember that it is very dangerous to set global variables.  You cannot count on the order of execution of your templates.  XSLT is supposed to be a side-effects-free programming language, but using extensions makes it possible to code them anyway.  That doesn't mean you should! ;^)   Remember, XSLT variables are immutable for a reason.  Don't create scripting-based variables within XSLT just to get around that fact--it's only going to get you in trouble.

Using position() works only if the count you want relates to the nodeset, and if the nodeset contains only the nodes you're counting.  That said, its almost always better to do it that way, if you can.  In other words, do the filtering of the nodes in the apply-templates or for-each, not by conditionals in the target template.  This way it's a lot easier to do things like alternate table row background colors etc.

Judging by the poster's example, I would say you're trying to use counters in the wrong way.  XSLT is a lot different than most programming languages--it's declarative, not procedural.  There's probably a much better way of accomplishing the poster's real goal.

Regards,
Mike Sharp

0
 
rdcproCommented:
Oh, I wanted to add that count() can be improved by the use of xsl:key to index the nodes you want to count.  This provides a much faster response when you have to do a lot of calculating on nodesets, say perhaps in some sort of report.
ex:  
<xsl:key name="myKey" match="myNode" use="filterConditionNode"/>
[...]
<xsl:value-of select="count(key('myKey', filterCondition)[additionalFilterCondition])"/>

This way the processor doesn't have to keep processing the entire nodeset for each calculation.

Regards,
Mike Sharp
0
 
jkmyoungCommented:
Looking back I'm a little unsure what you want on the counter1, is there a seperate table data cell for it? eg:
<TD>
counter1++
</TD>
really, xslt isn't designed to use counters. I'm guessing you're using the browser xslt processor, which means the saxon:assign function is out (although it is cheating somewhat anyways).

???? don't understand how that recursive counter applies in this case.

'insert javascript code in XSL': as noted before, the order of processing is supposed to be independent, but since most computers don't have multi-processors as of yet, IE processing is linear and probably will be for awhile at least. Dangerous, but works for now. calling a global function seems to be your best bet, given what you've told us you know so far.

I suppose you could speed up the count/position method by using keys, or even a secondary variable(s); but this is definitely more complicated.
0
 
J_MakCommented:
I agree with rdcpro. You should use recursive templates. It is the safest way to update and change a variable, which is usually passed as a parameter.

CHeers.
0
 
rdcproCommented:
There are other optimizations that can occur.  For example, the XSLT processor is not required to execute a template that does not produce output.  This can be determined when the XSLT is compiled, so you'd find that the template isn't even *in* the compiled XSLT.  

And there's no guarantee that successive versions of the same processor won't suddenly break an extension function that is not a pure function.  There's no reason that I can think of to use xsl:script extensions that are not pure functions (ie: have no side-effects):

http://www.biglist.com/lists/xsl-list/archives/200102/msg00903.html

But, in any case, it looks to me like the poster is trying to use counters to build a table with an arbitrary number of cells (or some similar purpose).  Going down this path leads one to do silly things like:

BadExample.xslt:
=====================================
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
      xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
      xmlns:msxsl="urn:schemas-microsoft-com:xslt"
      xmlns:bad="urn:rdcpro-com:worstpractice"
      exclude-result-prefixes="bad msxsl">
      <xsl:output method="xml" encoding="UTF-8"/>
      <msxsl:script language="JScript" implements-prefix="bad"><![CDATA[  
            var iCounter
            iCounter = 0
            function getCounter()
            {
                  return parseInt(iCounter)
            }
            function incrementCounter()
            {
                  iCounter++
                  return true
            }
                  
      ]]></msxsl:script>
      <xsl:template match="root">
            <table>
                  <tr>
                        <xsl:for-each select="myNode">
                              <xsl:if test="bad:incrementCounter()">
                                    <td>
                                          <xsl:value-of select="."/>
                                    </td>
                              </xsl:if>
                              <xsl:if test="bad:getCounter() mod 4 = 0">
                                    <xsl:text disable-output-escaping="yes">
                                        &lt;/tr&gt;
                                        &lt;tr&gt;
                                    </xsl:text>
                              </xsl:if>
                        </xsl:for-each>
                  </tr>
            </table>
      </xsl:template>
</xsl:stylesheet>


instead of constructing a table the right way.  It's possible to build the table using recursive templates and safe counters, but even that isn't necessary.  

Regards,
Mike Sharp  
0

Featured Post

Concerto's Cloud Advisory Services

Want to avoid the missteps to gaining all the benefits of the cloud? Learn more about the different assessment options from our Cloud Advisory team.

Tackle projects and never again get stuck behind a technical roadblock.
Join Now