XSLT 1.0 recursion
I'm stuck with recursion, was wondering if anyone can help me out with it.
I have <Receipts>
and <Deposits>
elements, that are not verbose, i.e. that a <Receipts>
element doesn't have an attribute to show what <Deposit>
it is towards. I need to figure out <Deposits>
"still amount due" and when a last receipt towards it was paid if any.
I'm trying to do it with the following code:
The idea was to take 1st deposit and see if there are receipts. If the deposit isn't fully paid and there are more receipts - call that recorsive function with all the same params except now count in following receipt.
If there aren't any more receipts or deposit is payed - process it correctly (add required attributes). Otherwise proceed with 2nd deposit. And so on.
However, the XSLT crashes with error message that "a processor stack has overflowed - possible cause is infinite template recursion"
I would really appreciate any help/teps... I'm not that great with recursion and can't understand why mine here doesn't work.
Thanks! :)
<!-- Accumulate all the deposits with @DueAmount attribute -->
<xsl:variable name="depositsClassified">
<xsl:call-template name="classifyDeposits">
<!-- a node-list of all Deposits elements ordered by DueDate Acs -->
<xsl:with-param name="depositsAll" select="$deposits"/>
<xsl:with-param name="depositPrevAmount" select="'0'"/>
<!-- a node-list of all Receipts elements ordered by ReceivedDate Acs -->
<xsl:with-param name="receiptsAll" select="$receiptsAsc"/>
<xsl:with-param name="receiptCount" select="'1'"/>
</xsl:call-template>
</xsl:variable>
<xsl:template name="classifyDeposits">
<xsl:param name="depositsAl开发者_JS百科l"/>
<xsl:param name="depositPrevAmount" select="'0'"/>
<xsl:param name="receiptsAll"/>
<xsl:param name="receiptCount"/>
<xsl:if test="$depositsAll">
<!-- Do required operations for the 1st deposit -->
<xsl:variable name="depositFirst" select="$depositsAll[1]"/>
<xsl:variable name="receiptSum">
<xsl:choose>
<xsl:when test="$receiptsAll">
<xsl:value-of select="sum($receiptsAll[position() <= $receiptCount]/@ActionAmount)"/>
</xsl:when>
<xsl:otherwise>0</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:variable name="diff" select="$depositPrevAmount + $depositFirst/@DepositTotalAmount - $receiptSum"/>
<xsl:choose>
<xsl:when test="$diff > 0 and
$receiptCount < $receiptsQuantityOf">
<xsl:call-template name="classifyDeposits">
<xsl:with-param name="depositsAll" select="$depositsAll"/>
<xsl:with-param name="depositPrevAmount" select="$depositPrevAmount"/>
<xsl:with-param name="receiptsAll" select="$receiptsAll"/>
<xsl:with-param name="receiptCount" select="$receiptCount + 1"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<!-- Record changes to the deposit (@DueAmount and receipt ReceivedDate) -->
<xsl:apply-templates select="$depositFirst" mode="defineDeposit">
<xsl:with-param name="diff" select="$diff"/>
<xsl:with-param name="latestReceiptForDeposit" select="$receiptsAll[position() = $receiptCount]"/>
</xsl:apply-templates>
<!-- Recursive call to the next deposit -->
<xsl:call-template name="classifyDeposits">
<xsl:with-param name="depositsAll" select="$depositsAll[position() > 1]"/>
<xsl:with-param name="depositPrevAmount" select="$depositPrevAmount + $depositFirst/@DepositTotalAmount"/>
<xsl:with-param name="receiptsAll" select="$receiptsAll"/>
<xsl:with-param name="receiptCount" select="'1'"/>
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</xsl:if>
</xsl:template>
<!-- Determine deposit's status, due amount and payment received date if any -->
<xsl:template match="Deposits" mode="defineDeposit">
<xsl:param name="diff"/>
<xsl:param name="latestReceiptForDeposit"/>
<xsl:choose>
<xsl:when test="$diff <= 0">
<xsl:apply-templates select="." mode="addAttrs">
<xsl:with-param name="status" select="'paid'"/>
<xsl:with-param name="dueAmount" select="'0'"/>
<xsl:with-param name="receipt" select="$latestReceiptForDeposit"/>
</xsl:apply-templates>
</xsl:when>
<xsl:when test="$diff = ./@DepositTotalAmount">
<xsl:apply-templates select="." mode="addAttrs">
<xsl:with-param name="status" select="'due'"/>
<xsl:with-param name="dueAmount" select="$diff"/>
</xsl:apply-templates>
</xsl:when>
<xsl:when test="$diff < ./@DepositTotalAmount">
<xsl:apply-templates select="." mode="addAttrs">
<xsl:with-param name="status" select="'outstanding'"/>
<xsl:with-param name="dueAmount" select="$diff"/>
<xsl:with-param name="receipt" select="$latestReceiptForDeposit"/>
</xsl:apply-templates>
</xsl:when>
<xsl:otherwise/>
</xsl:choose>
</xsl:template>
<xsl:template match="Deposits" mode="addAttrs">
<xsl:param name="status"/>
<xsl:param name="dueAmount"/>
<xsl:param name="receipt" select="''"/>
<!-- Constract a new MultiDeposits element with required info -->
<xsl:copy>
<xsl:copy-of select="./@*"/>
<xsl:attribute name="Status"><xsl:value-of select="$status"/></xsl:attribute>
<xsl:attribute name="DueAmount"><xsl:value-of select="$dueAmount"/></xsl:attribute>
<xsl:if test="$receipt">
<xsl:attribute name="latestReceiptDate">
<xsl:value-of select="$receipt/@ActionDate"/>
</xsl:attribute>
</xsl:if>
<xsl:copy-of select="./*"/>
</xsl:copy>
</xsl:template>
Your named template classifyDeposits
is definitely recursing infinitely. It has no base case. In the <xsl:choose>
section you have two conditions: <xsl:when>
and <xsl:otherwise>
, however you call classifyDeposits
in both conditions.
So no matter what you pass to this template, it will call itself. You need to have a base case: a condition in which the template doesn't call itself.
In pseudocode, you're essentially doing this:
function recursive
if (A>B) then
call recursive
else
call recursive
You need to add another branch, somewhere, that does not call itself.
function recursive
if (C=0) then
return
else if (A>B) then
call recursive
else
call recursive
Of course, just having the condition isn't good enough. It needs to be reachable.
One thing i've learned about concerning recursion is that you need to make sure that you have a condition that will trigger when you are done.
example:
decrement_me_until_zero(int i)
{
if(i ==0) return; //we're done here, roll on back up!
else
{
i = i-1;
decrement_me_until_zero(i);
return;
}
}
I don't know xlst at all, but this can be a simple reason why recursion can go into an infinite loop. Consider instead:
decrement_me_until_zero(int i)
{
if(i ==0) return; //we're done here, roll on back up!
else
{
decrement_me_until_zero(i);
i=i-1; //oops! we decrement after we pass the variable down!
return;
}
}
I hope that helps
精彩评论