Click to See Complete Forum and Search --> : [RESOLVED] Recursive function in XSLT


eeb3
05-03-2009, 12:13 PM
I am trying to make a recursive function in XSLT, since it is the only way I seem to be able to do this in the V1.0 form, (and that the XSLT V2.0 isn't supported by IE7 and FF3 as of this point)

This is the code so far that I have:


<div class="TandB" style="top: 0px; width:{$TandBWidth+60}px;">
<xsl:for-each select="Map">
<xsl:sort data-type="number" order="descending" select="Map/Row/Column/@horz" />
<xsl:variable name="Counters" select="Row/Column/@horz[not(. &lt; //Column/@horz)][1]" />
<xsl:apply-templates select="Decrease">
 <xsl:with-param name="UnCounters" select="$Counters" />
</xsl:apply-templates>
</xsl:for-each>
</div>

<xsl:template name="Decrease">
<xsl:if test="$Counters - $Uncounters &lt; $Counters">
<xsl:variable name="UnCounters" select="$Uncounters - 1" />
<xsl:text> | </xsl:text><xsl:value-of select="$Counters - $Uncounters" />
<xsl:apply-templates select="Decrease" />
</xsl:if>
<xsl:if test="$Counters - $UnCounters = $Counters">
<xsl:text> | </xsl:text><xsl:value-of select="$Counters" /><xsl:text> |</xsl:text>
</xsl:if>
</xsl:template>


What I am doing here is that trying to sort the entire XML list so it can find the highest numbered attribute of all the Columns and assigned that number to the variable Counters.

Then this is where the recursive function comes in, when it calls for the template "Decrease" setting at the same time, the para/Variable "UnCounter" the same as Counters.

What I am trying to get this template is to basically count up to what the Counters equals to.

For example the highest Column number was 6 it would display:
| 1 | 2 | 3 | 4 | 5 | 6 |
If the highest was 21:
| 1 | 2 | 3 | ... | 19 | 20 | 21 |

Unfortunately the problem I have is that currently IE gives me the error "xsl:apply-template can not contain PCDATA nodes"
Another error I had (but not with this specific code) is that I couldn't assign the same Variable twice.

If someone could help me with this, I would appreciate it.
(The only thing though if this is able to work, would be if there is some way to be able to get numbers that are 1~9 to show up as 01, 02, ~, 08, 09)

eeb3
05-03-2009, 06:32 PM
I think I may have found possible way to make it recursive, at least in the form that IE doesn't give out an error (which is better then before when all it was giving me was errors), but there is also no output yet. (And I have figured out how to get leading zeros as well) this is what I have


<!-- These are set as global -->
<xsl:param name="MaxColumn">
<xsl:value-of select="count(//@horz) div count(//@nvert)" />
</xsl:param>
<xsl:param name="X" select="'0'" />

...

<xsl:template match="/">

...

<div class="TandB" style="top:0px; width:{$TandBWidth}px; padding-left:30px;">
<xsl:apply-templates select="Recursive" />
</div>

...

</xsl:template>
<xsl:template match="Recursive">
<xsl:if test="$MaxColumn - $X != 0">
<xsl:variable name="X" select="$X + 1" />
<xsl:text> | </xsl:text><xsl:value-of select="format-number($X,00)" />
<xsl:apply-templates select="Recursive" />
</xsl:if>
</xsl:template>
</xsl:stylesheet>


The only problem I have with this code is that it would probably easily break if the appearances @horz doesn't divide evenly with @nvert .

eeb3
05-11-2009, 10:06 PM
After nearly spending a week of trying to get this to work I think I've been able to figure out half of what I was trying to do, But it still doesn't work right, and after reading a bit about the usage of templates, I don't know if I have the usage of it correct in this case.
It doesn't print out anything in the final product, but there is also no errors popping up saying something is wrong (in either FF or IE)

<xsl:variable name="MaxColumn">
<xsl:for-each select="/Map/Row/Column">
<xsl:sort select="@horz" data-type="number" order="descending" />
<xsl:if test="position() = 1">
<xsl:value-of select="@horz" />
</xsl:if>
</xsl:for-each>
</xsl:variable>

<xsl:param name="CountingDown" select="$MaxColumn" />

<xsl:template match="/">
...
<div class="TandB" style="top:0px; width:{$TandBWidth+10}px; padding-left:30px;"><xsl:apply-templates select="Decrease" />
</div>
<div class="TandB" style="top:{$LandRHeight+40}px; width:{$TandBWidth+10}px; padding-left:50px;"><xsl:apply-templates select="Decrease" />
</div>
...
</xsl:template>

<xsl:template name="Decrease">
<xsl:if test="$MaxColumn - $CountingDown &lt; $MaxColumn">
<xsl:value-of select='format-number($MaxColumn - ($CountingDown - 1),"00")' /><xsl:value-of select="$MaxColumn" /><xsl:text> | </xsl:text>
<xsl:call-template name="Decrease">
<xsl:with-param name="CountingDown" select="$CountingDown - 1" />
</xsl:call-template>
</xsl:if>
</xsl:template>

If anyone could help, I'd appreciate it. Thanks.

jkmyoung
05-12-2009, 06:03 PM
conditionals:

<xsl:variable name="MaxColumn" select="/Map/Row/Column/@horz[not(. &lt; /Map/Row/Column/@horz)][1]"/>


Another thing that might have worked for you would be the count() function, eg:

<xsl:variable name="MaxColumn" select="count(/Map/Row/Column)"/>


Further, your template isn't quite set up correctly. I would probably do it like so:

<xsl:template match="Decrease">
<xsl:param name="CountingDown" select="$MaxColumn"/><!-- start with $MaxColumn as the first value -->
<xsl:if test="$CountingDown &gt;= 1">
...

eeb3
05-12-2009, 07:50 PM
Thanks, jkmyoung, I used the first suggestion you made:
<xsl:variable name="MaxColumn" select="/Map/Row/Column/@horz[not(. &lt; /Map/Row/Column/@horz)][1]"/>
The reason why I wouldn't use:
<xsl:variable name="MaxColumn" select="count(/Map/Row/Column)"/>
Is because @horz will eventually not be a continuous set of numbers, even though there might be 20 Columns per Row, the highest occurrence of @horz may be a value of 32 or some other random number.

-*-*-

Now I have been able to finally get the template to be called up, I just had to replace <xsl:apply-templates select="Decrease" /> with <xsl:call-template name="Decrease" />

At this point I started to get a XSL overflow (infinite recursive) error dealing with the recursive function.

<xsl:template name="Decrease">
<xsl:if test="$MaxColumn - $CountingDown &lt; $MaxColumn">
<xsl:value-of select='format-number($MaxColumn - ($CountingDown - 1),"00")' /><xsl:text> | </xsl:text>
<xsl:call-template name="Decrease">
<xsl:with-param name="CountingDown" select="$CountingDown - 1" />
</xsl:call-template>
</xsl:if>
</xsl:template>


Which I kinda expected to occur, so I have tried this method instead


<xsl:template name="Decrease">
<xsl:choose>
<xsl:when test="$MaxColumn - $CountingDown &lt; $MaxColumn">
<xsl:variable name="Stringed">
<xsl:value-of select='format-number($MaxColumn - ($CountingDown - 1),"00")' /><xsl:text> | </xsl:text>
</xsl:variable>
<xsl:call-template name="Decrease">
<xsl:with-param name="CountingDown" select="$CountingDown - 1" />
<xsl:with-param name="Stringer" select="concat(Stringer, Stringed)" />
</xsl:call-template>
</xsl:when>
<xsl:when test="$MaxColumn - $CountingDown = $MaxColumn">
<xsl:value-of select="$Stringer" />
<xsl:call-template name="Decrease">
<xsl:with-param name="CountingDown" select="$CountingDown - 1" />
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$Stringer" />
</xsl:otherwise>
</xsl:choose>
</xsl:template>

Which also got an Overflow error.

-*-*-

Now while I was writing this post I finally got the entire page to work, using your 3rd suggestion, moving the declaration of the $CountingDown param to the "Decrease" template instead of having it set globally.

So the final product is:

<xsl:template name="Decrease">
<xsl:param name="CountingDown" select="$MaxColumn" />
<xsl:param name="Stringer" />
<xsl:choose>
<xsl:when test="$MaxColumn - $CountingDown &lt; $MaxColumn">
<xsl:variable name="Stringed">
<xsl:value-of select='format-number($MaxColumn - ($CountingDown - 1),"00")' /><xsl:text> | </xsl:text>
</xsl:variable>
<xsl:call-template name="Decrease">
<xsl:with-param name="CountingDown" select="$CountingDown - 1" />
<xsl:with-param name="Stringer" select="concat($Stringer, $Stringed)" />
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$Stringer" />
</xsl:otherwise>
</xsl:choose>
</xsl:template>


Something that I was worried about was that when it would it would get to the <xsl:otherwise> statement, it would print out $Stringer X amount of times it took to make the <xsl:when> statement false. Which it didn't and glad that it didn't.

Thank You very much jkmyoung.