I. XSLT 1.0 solution:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:param name="pTimes" select="5"/>
<xsl:template match="node()|@*">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="/*">
<xsl:call-template name="applyNTimes">
<xsl:with-param name="pTimes" select="$pTimes"/>
<xsl:with-param name="pPosition" select="1"/>
</xsl:call-template>
</xsl:template>
<xsl:template name="applyNTimes">
<xsl:param name="pTimes" select="0"/>
<xsl:param name="pPosition" select="1"/>
<xsl:if test="$pTimes > 0">
<xsl:apply-templates select="*">
<xsl:with-param name="pPosition" select="$pPosition"/>
</xsl:apply-templates>
<xsl:call-template name="applyNTimes">
<xsl:with-param name="pTimes" select="$pTimes -1"/>
<xsl:with-param name="pPosition" select="$pPosition+1"/>
</xsl:call-template>
</xsl:if>
</xsl:template>
<xsl:template match="Person">
<xsl:param name="pPosition" select="1"/>
<Person position="{$pPosition}">
<xsl:apply-templates>
<xsl:with-param name="pPosition" select="$pPosition"/>
</xsl:apply-templates>
</Person>
</xsl:template>
<xsl:template match="number">
<xsl:param name="pPosition" select="1"/>
<number><xsl:value-of select="$pPosition"/></number>
</xsl:template>
</xsl:stylesheet>
When this transformation is applied on the following XML document:
<t>
<Person position="1">
<name>John</name>
<number>1</number>
</Person>
</t>
the wanted result is produced:
<Person position="1">
<name>John</name>
<number>1</number>
</Person>
<Person position="2">
<name>John</name>
<number>2</number>
</Person>
<Person position="3">
<name>John</name>
<number>3</number>
</Person>
<Person position="4">
<name>John</name>
<number>4</number>
</Person>
<Person position="5">
<name>John</name>
<number>5</number>
</Person>
II. XSLT 2.0 solution:
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="xs"
>
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:param name="pNumTimes" as="xs:integer" select="5"/>
<xsl:template match="node()|@*">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="/">
<xsl:variable name="vDoc" select="/"/>
<xsl:for-each select="1 to $pNumTimes">
<xsl:apply-templates select="$vDoc/*/*">
<xsl:with-param name="pPosition" select="."/>
</xsl:apply-templates>
</xsl:for-each>
</xsl:template>
<xsl:template match="Person">
<xsl:param name="pPosition" select="1"/>
<Person position="{$pPosition}">
<xsl:apply-templates>
<xsl:with-param name="pPosition" select="$pPosition"/>
</xsl:apply-templates>
</Person>
</xsl:template>
<xsl:template match="number">
<xsl:param name="pPosition" select="1"/>
<number><xsl:value-of select="$pPosition"/></number>
</xsl:template>
</xsl:stylesheet>
III. Using a DVD-style recursion to avoid stack overflow on large N (XSLT 1.0)
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:param name="pTimes" select="5"/>
<xsl:template match="node()|@*">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="/*">
<xsl:call-template name="applyNTimes">
<xsl:with-param name="pTimes" select="$pTimes"/>
<xsl:with-param name="pPosition" select="1"/>
</xsl:call-template>
</xsl:template>
<xsl:template name="applyNTimes">
<xsl:param name="pTimes" select="0"/>
<xsl:param name="pPosition" select="1"/>
<xsl:if test="$pTimes > 0">
<xsl:choose>
<xsl:when test="$pTimes = 1">
<xsl:apply-templates select="*">
<xsl:with-param name="pPosition" select="$pPosition"/>
</xsl:apply-templates>
</xsl:when>
<xsl:otherwise>
<xsl:variable name="vHalf" select="floor($pTimes div 2)"/>
<xsl:call-template name="applyNTimes">
<xsl:with-param name="pTimes" select="$vHalf"/>
<xsl:with-param name="pPosition" select="$pPosition"/>
</xsl:call-template>
<xsl:call-template name="applyNTimes">
<xsl:with-param name="pTimes" select="$pTimes - $vHalf"/>
<xsl:with-param name="pPosition" select="$pPosition + $vHalf"/>
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</xsl:if>
</xsl:template>
<xsl:template match="Person">
<xsl:param name="pPosition" select="1"/>
<Person position="{$pPosition}">
<xsl:apply-templates>
<xsl:with-param name="pPosition" select="$pPosition"/>
</xsl:apply-templates>
</Person>
</xsl:template>
<xsl:template match="number">
<xsl:param name="pPosition" select="1"/>
<number><xsl:value-of select="$pPosition"/></number>
</xsl:template>
</xsl:stylesheet>
The problem with deep recursion is the exhaustion of the call stack resulting in stack overflow. On different systems this happens with different levels of nesting, but this is typical at around N = 1000.
The above transformation practically has no such problem. The maximum recursion depth with DVC is log2(N), which means that if we need to repeat the action 1000000 (1M) times, the maximum recursion depth is only 19.
IV. Finally, how to avoid recursion at all (XSLT 1.0)
For known, not too big N, the following non-recursive technique works well:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:param name="pTimes" select="5"/>
<xsl:template match="node()|@*">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="/*">
<xsl:call-template name="applyNTimes">
<xsl:with-param name="pTimes" select="$pTimes"/>
</xsl:call-template>
</xsl:template>
<xsl:template name="applyNTimes">
<xsl:param name="pTimes" select="0"/>
<xsl:variable name="vCurrent" select="."/>
<xsl:if test="$pTimes > 0">
<xsl:for-each select=
"document('')//node() | document('')//@* | document('')//namespace::*">
<xsl:if test="not( position() > $pTimes )">
<xsl:apply-templates select="$vCurrent/*">
<xsl:with-param name="pPosition" select="position()"/>
</xsl:apply-templates>
</xsl:if>
</xsl:for-each>
</xsl:if>
</xsl:template>
<xsl:template match="Person">
<xsl:param name="pPosition" select="1"/>
<Person position="{$pPosition}">
<xsl:apply-templates>
<xsl:with-param name="pPosition" select="$pPosition"/>
</xsl:apply-templates>
</Person>
</xsl:template>
<xsl:template match="number">
<xsl:param name="pPosition" select="1"/>
<number><xsl:value-of select="$pPosition"/></number>
</xsl:template>
</xsl:stylesheet>