Given input file
<propertyDescriptor>
<name repeat="1">enable_decline_rule</name>
<description>text</description>
<type>BOOLEAN</type>
<defaultValue>false</defaultValue>
<dependentPropertyDescriptors>
<dependentPropertyDescriptor>
<value>true</value>
<propertyDescriptor>
<name repeat="1">decline_rule_identifier</name>
<description>This is some random text.</description>
<type>STRING</type>
</propertyDescriptor>
</dependentPropertyDescriptor>
</dependentPropertyDescriptors>
</propertyDescriptor>
and stylesheet
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:output method="xml" indent="yes" standalone="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="node()|@*">
<xsl:copy>
<xsl:apply-templates/>
</xsl:copy>
</xsl:template>
<!-- match the tree that needs to be repeated -->
<xsl:template match="propertyDescriptor[name='enable_decline_rule']">
<xsl:call-template name="block-generator">
<xsl:with-param name="N" select="1"/>
</xsl:call-template>
</xsl:template>
<xsl:template name="block-generator">
<xsl:param name="N"/>
<xsl:param name="i" select="1"/>
<xsl:if test="$N >= $i">
<!-- generate a block -->
<xsl:copy>
<xsl:call-template name="new-descriptor">
<xsl:with-param name="N" select="$i"/>
</xsl:call-template>
</xsl:copy>
<!-- recursive call -->
<xsl:call-template name="block-generator">
<xsl:with-param name="N" select="$N"/>
<xsl:with-param name="i" select="$i + 1"/>
</xsl:call-template>
</xsl:if>
</xsl:template>
<xsl:template name="new-descriptor">
<xsl:param name="N"/>
<xsl:for-each select="node()">
<xsl:call-template name="append_name">
<xsl:with-param name="N" select="$N"/>
</xsl:call-template>
</xsl:for-each>
</xsl:template>
<xsl:template name="append_name" >
<xsl:param name="N"/>
<xsl:choose>
<!-- tags with a repeat attribute get a _N -->
<xsl:when test="$N >= 1 and @repeat">
<name>
<xsl:value-of select="text()"/>_<xsl:value-of select="$N"/>
</name>
</xsl:when>
<xsl:when test="$N >= 1">
<xsl:copy>
<xsl:value-of select="node()"/>
<xsl:for-each select="./*">
<xsl:call-template name="append_name">
<xsl:with-param name="N" select="$N"/>
</xsl:call-template>
</xsl:for-each>
</xsl:copy>
</xsl:when>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
I get this output
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<propertyDescriptor>
<name>enable_decline_rule_1</name>
<description>text</description>
<type>BOOLEAN</type>
<defaultValue>false</defaultValue>
<dependentPropertyDescriptors>
truedecline_rule_identifierThis is some random text.STRING
<dependentPropertyDescriptor>
true
<value>true</value>
<propertyDescriptor>
decline_rule_identifier
<name>decline_rule_identifier_1</name>
<description>This is some random text.</description>
<type>STRING</type>
</propertyDescriptor>
</dependentPropertyDescriptor>
</dependentPropertyDescriptors>
</propertyDescriptor>
But remove the <xsl:strip-space elements="*"/>
tag and the output is clean.
I also get corrupted data if I use net.sf.saxon:Saxon-HE:12.0
UPDATE
To clarify my requirements, I have to duplicate the propertyDescriptor element (which element to duplicate by how many times is fed to the xslt as 2 params. I hardcoded these to simply the problem)
It not enough to simply duplicate propertyDescriptor elements, the name must also be unique. Expected output for 2 counts:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<propertyDescriptor>
<name>enable_decline_rule_1</name>
<description>text</description>
<type>BOOLEAN</type>
<defaultValue>false</defaultValue>
<dependentPropertyDescriptors>
<dependentPropertyDescriptor>
<value>true</value>
<propertyDescriptor>
<name>decline_rule_identifier_1</name>
<description>This is some random text.</description>
<type>STRING</type>
</propertyDescriptor>
</dependentPropertyDescriptor>
</dependentPropertyDescriptors>
</propertyDescriptor>
<propertyDescriptor>
<name>enable_decline_rule_2</name>
<description>text</description>
<type>BOOLEAN</type>
<defaultValue>false</defaultValue>
<dependentPropertyDescriptors>
<dependentPropertyDescriptor>
<value>true</value>
<propertyDescriptor>
<name>decline_rule_identifier_2</name>
<description>This is some random text.</description>
<type>STRING</type>
</propertyDescriptor>
</dependentPropertyDescriptor>
</dependentPropertyDescriptors>
</propertyDescriptor>
It seems with XSLT 3 you can use the to
operator e.g.
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="#all"
expand-text="yes"
version="3.0">
<xsl:mode on-no-match="shallow-copy"/>
<xsl:template match="propertyDescriptor[name/@repeat]">
<xsl:for-each select="(0 to name/@repeat)!current()">
<xsl:apply-templates select="." mode="copy">
<xsl:with-param name="id" select="position()" tunnel="yes"/>
</xsl:apply-templates>
</xsl:for-each>
</xsl:template>
<xsl:mode name="copy" on-no-match="shallow-copy"/>
<xsl:template mode="copy" match="name[@repeat]">
<xsl:param name="id" tunnel="yes"/>
<xsl:copy>{.}_{$id}</xsl:copy>
</xsl:template>
<xsl:output indent="yes"/>
</xsl:stylesheet>
will produce the output
<propertyDescriptor>
<name>enable_decline_rule_1</name>
<description>text</description>
<type>BOOLEAN</type>
<defaultValue>false</defaultValue>
<dependentPropertyDescriptors>
<dependentPropertyDescriptor>
<value>true</value>
<propertyDescriptor>
<name>decline_rule_identifier_1</name>
<description>This is some random text.</description>
<type>STRING</type>
</propertyDescriptor>
</dependentPropertyDescriptor>
</dependentPropertyDescriptors>
</propertyDescriptor>
<propertyDescriptor>
<name>enable_decline_rule_2</name>
<description>text</description>
<type>BOOLEAN</type>
<defaultValue>false</defaultValue>
<dependentPropertyDescriptors>
<dependentPropertyDescriptor>
<value>true</value>
<propertyDescriptor>
<name>decline_rule_identifier_2</name>
<description>This is some random text.</description>
<type>STRING</type>
</propertyDescriptor>
</dependentPropertyDescriptor>
</dependentPropertyDescriptors>
</propertyDescriptor>
from the input
<propertyDescriptor>
<name repeat="1">enable_decline_rule</name>
<description>text</description>
<type>BOOLEAN</type>
<defaultValue>false</defaultValue>
<dependentPropertyDescriptors>
<dependentPropertyDescriptor>
<value>true</value>
<propertyDescriptor>
<name repeat="1">decline_rule_identifier</name>
<description>This is some random text.</description>
<type>STRING</type>
</propertyDescriptor>
</dependentPropertyDescriptor>
</dependentPropertyDescriptors>
</propertyDescriptor>