Solved

Increment/decrement variable in XSL

Posted on 2004-08-17
5
2,958 Views
Last Modified: 2010-05-18
How do you increment or decrement a variable in XSL, when there is no upper or lower bound known? There exists a recursive solution explained in http://www.biglist.com/lists/xsl-list/archives/200403/msg00915.html, but I am not talking about this. I don't want to just print the value of the variable given that its upper bound is 10 or 5 or whatever. I want to retain the value of the variable and use it to call other templates with parameters.

Rephrasing my problem in a more specific way: what I want to do is when there is a call to a certain template, a variable needs to be incremented (doesn't seem like this can be local to the template, but the incremented value may be passed as parameter). This incremented value is used to print something out. This template is not recursive but there's no problem to have a recursive counter template if it can work for this case.

An example of such a callee template is below. We use a parameter called 'sequence' to print a sequence number out in front of a printed XPath expression. However, this sequence number must be incremented every time the template "xpath" is called.

                 <xsl:template name="xpath">
            <xsl:param name="sequence"/>
            <xsl:value-of select="$sequence"/>
            <xsl:for-each select="ancestor-or-self::node()[attribute::name]">/<xsl:value-of select="@name"/>
            </xsl:for-each>
      </xsl:template>

Thanks a lot in advance.
0
Comment
Question by:xtremebytes2002
  • 2
5 Comments
 
LVL 26

Expert Comment

by:rdcpro
ID: 11823110
This is a procedural concept, not a declarative one.  You should not create "global" variable in this way.  This is commonly known as a side-effect, and it is not guaranteed to work as expected.  The reason is the XSLT processor is free to make whatever optimizations it likes, including out of order execution.

There is a correct approach for this, but I don't know precisely what it is you are trying to accomplish.  In most cases, an incrementing counter variable is created with recursion, and then you can use that recursive template to call whatever other templates you want.  Or embed test conditions in a template that's already processing.  For example, suppose I have some text I want to process with line numbers.  The text has embedded newlines in it (the \n character sequence) but I have no idea how many or where.

This XML:

<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="textParser2.xslt"?>
<root>
      <Data author="Abraham Lincoln">Four Score, and Seven Years ago,\nOur fathers brought forth upon this continent\na new nation, conceived in liberty\nand dedicated to the proposition that all men are created equal.\n[...]</Data>
      <Data author="Robert Frost">Two roads diverged in a yellow wood\nAnd sorry I could not travel both\nAnd be one traveler, long I stood\nAnd looked down one as far as I could\nTo where it bent in the undergrowth\n\nThen took the other as just as fair\nAnd having perhaps the better claim\nBecause it was grassy and wanted wear\nThough as for that, the passing there\nHad worn them really about the same\n\nAnd both that morning equally lay\nIn leaves no step had trodden black\nOh, I kept the first for another day!\nYet, knowing how way leads onto way\nI doubted if I should ever come back\n\nI shall be telling this with a sigh\nSomewhere ages and ages hence\nTwo roads diverged in a wood\nAnd I took the one less traveled by\nAnd that has made all the difference</Data>
</root>


with this XSLT:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="html" version="1.0" encoding="UTF-8" indent="yes"/>
    <xsl:template match="Data">
          <xsl:param name="pCounter" select="1"/>
        <strong><xsl:value-of select="@author"/></strong>
        <p><span style="width:50px;">[<xsl:value-of select="$pCounter"/>]</span>
            <xsl:call-template name="tTextParser">
                <xsl:with-param name="pText" select="text()"/>
                <xsl:with-param name="pCounter" select="$pCounter + 1"/>
            </xsl:call-template>
        </p>
    </xsl:template>
   
    <xsl:template name="tTextParser">
          <xsl:param name="pCounter"/>
        <xsl:param name="pText"/>
        <xsl:choose>
            <!-- If the current string starts with an "escape" character, AND there's at least 2 characters -->
            <xsl:when test="starts-with($pText, '\') and string-length($pText) &gt; 1">
                <xsl:choose>
                    <!-- Test to see if it's a newline.  Add other rules as appropriate -->
                    <xsl:when test="substring($pText, 2, 1) = 'n'">
                        <br/><span style="width:50px;">[<xsl:value-of select="$pCounter"/>]</span>
                        <xsl:if test="string-length($pText) &gt; 2">
                            <xsl:call-template name="tTextParser">
                                <xsl:with-param name="pText" select="substring($pText, 3)"/>
                            <xsl:with-param name="pCounter" select="$pCounter + 1"/>
                            </xsl:call-template>
                        </xsl:if>
                    </xsl:when>
                    <!-- This is the case where a backslash is escaped, and should be replaced with "\" -->
                    <xsl:when test="substring($pText, 2, 1) = '\'">
                        <xsl:text>\</xsl:text>
                        <xsl:if test="string-length($pText) &gt; 2">
                            <xsl:call-template name="tTextParser">
                                <xsl:with-param name="pText" select="substring($pText, 3)"/>
                            <xsl:with-param name="pCounter" select="$pCounter"/>
                            </xsl:call-template>
                        </xsl:if>
                    </xsl:when>
                    <xsl:otherwise>
                        <!-- It must just be an odd "\" or it doesn't fit an escape sequence we test for
                                This could be an error condition, depending on the rules  -->
                        <xsl:value-of select="substring($pText, 1, 1)"/>
                        <xsl:if test="string-length($pText) &gt; 1">
                            <xsl:call-template name="tTextParser">
                                <xsl:with-param name="pText" select="substring($pText, 2)"/>
                            <xsl:with-param name="pCounter" select="$pCounter"/>
                            </xsl:call-template>
                        </xsl:if>
                    </xsl:otherwise>
                </xsl:choose>
            </xsl:when>
            <xsl:otherwise>
                <!-- Output the character.  If it's important to NOT end the line with a single backslash,
                        test for this condition too. -->
                <xsl:value-of select="substring($pText, 1, 1)"/>
                <xsl:if test="string-length($pText) &gt; 1">
                    <xsl:call-template name="tTextParser">
                        <xsl:with-param name="pText" select="substring($pText, 2)"/>
                      <xsl:with-param name="pCounter" select="$pCounter"/>
                    </xsl:call-template>
                </xsl:if>
            </xsl:otherwise>
        </xsl:choose>
    </xsl:template>
</xsl:stylesheet>



produces the following:

Abraham Lincoln
[1]     Four Score, and Seven Years ago,
[2]     Our fathers brought forth upon this continent
[3]     a new nation, conceived in liberty
[4]     and dedicated to the proposition that all men are created equal.
[5]     [...]

Robert Frost
[1]     Two roads diverged in a yellow wood
[2]     And sorry I could not travel both
[3]     And be one traveler, long I stood
[4]     And looked down one as far as I could
[5]     To where it bent in the undergrowth
[6]
[7]     Then took the other as just as fair
[8]     And having perhaps the better claim
[9]     Because it was grassy and wanted wear
[10]   Though as for that, the passing there
[11]   Had worn them really about the same
[12]
[13]   And both that morning equally lay
[14]   In leaves no step had trodden black
[15]   Oh, I kept the first for another day!
[16]   Yet, knowing how way leads onto way
[17]   I doubted if I should ever come back
[18]
[19]   I shall be telling this with a sigh
[20]   Somewhere ages and ages hence
[21]   Two roads diverged in a wood
[22]   And I took the one less traveled by
[23]   And that has made all the difference

Regards,
Mike Sharp
0
 
LVL 1

Author Comment

by:xtremebytes2002
ID: 11828860
Thanks for your response but we cannot use recursion even in your suggested way because of the way our templates work. With the position() function it is possible to find the position of a node in a nodelist returned in a xsl:for-each for example. How do you get a depth while doing a search? Suppose the situation is as follows.

+element1_1
       +element1_2
       +element2_2
+element2_1

where the first number indicates position() and the second (after the "_") indicates the depth. Is it possible to get the depth through any XSL function or a template?

Thanks in advance.
0
 
LVL 26

Accepted Solution

by:
rdcpro earned 125 total points
ID: 11833933
If you want depth, you can always count the nodes in the ancestor or else the ancestor-or-self axis:

<xsl:value-of select="count(ancestor::*)"/>

If you want to count nodes in preceding document order, use the preceding::* axis.  

If you have a nodelist, the position() function is useful, as well.  But you have to be careful how you construct the nodelist, and the position() refers to the position in the original, unsorted, nodelist.  If you need to sort a nodelist, it's best to create a variable with the sorted nodes as a result tree fragment, and using the node-list extension function, create a nodelist your position will work on.  

Regards,
Mike Sharp
0

Featured Post

Maximize Your Threat Intelligence Reporting

Reporting is one of the most important and least talked about aspects of a world-class threat intelligence program. Here’s how to do it right.

Join & Write a Comment

Introduction In my previous article (http://www.experts-exchange.com/Microsoft/Development/MS-SQL-Server/SSIS/A_9150-Loading-XML-Using-SSIS.html) I showed you how the XML Source component can be used to load XML files into a SQL Server database, us…
Browsing the questions asked to the Experts of this forum, you will be amazed to see how many times people are headaching about monster regular expressions (regex) to select that specific part of some HTML or XML file they want to extract. The examp…
Excel styles will make formatting consistent and let you apply and change formatting faster. In this tutorial, you'll learn how to use Excel's built-in styles, how to modify styles, and how to create your own. You'll also learn how to use your custo…
Illustrator's Shape Builder tool will let you combine shapes visually and interactively. This video shows the Mac version, but the tool works the same way in Windows. To follow along with this video, you can draw your own shapes or download the file…

706 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

22 Experts available now in Live!

Get 1:1 Help Now