Re: [xsl] To control node duplication

Subject: Re: [xsl] To control node duplication
From: "nick public" <nickpubl@xxxxxxxxx>
Date: Wed, 14 Jan 2009 10:28:34 +0100
Hi George.
Thank you very much for your precious help.

I analyzed your implementation and I realized that the trick to
bypass, in this case, the reset variable limitation is to recall the
processLevel template
passing it a new instance of $states shifting each time the first char
by one to right. Since the processLevel just take the first char by
$states, I have obtained my scope to provide each time a different
value of $states. Great! :-))

>  <xsl:template name="processLevel">
>    <xsl:param name="states"/>
>    <xsl:if test="string-length($states)>0">
>      <xsl:apply-templates mode="copy">
>        <xsl:with-param name="state" select="substring($states, 1, 1)"/>
>      </xsl:apply-templates>
>      <xsl:call-template name="processLevel">
>        <xsl:with-param name="states" select="substring($states, 2,
> string-length($states))"/>
>      </xsl:call-template>
>    </xsl:if>
>  </xsl:template>

The way to exit by the recorsion is the empty $states
<xsl:if test="string-length($states)>0">

The thing that I have not understood is the attribute mode="copy" that
you use in the template statements calling and called. Seems that
works without it too.

Really, thank you very much, George. You have solved my problem and I
have learned something by you about XSLT Language.

Ciao. Nicola



2009/1/13 George Cristian Bina <george@xxxxxxxxxxxxx>:
> For XSLT 1.0 you can replace the for each with a recursive template as
> below:
>
> <?xml version="1.0" encoding="UTF-8"?>
> <xsl:stylesheet version="1.0"
> xmlns:xsl="http://www.w3.org/1999/XSL/Transform";>
>  <xsl:template match="root">
>    <xsl:copy><xsl:apply-templates/></xsl:copy>
>  </xsl:template>
>
>  <xsl:template match="level">
>    <xsl:variable name="level" select="."/>
>    <xsl:call-template name="processLevel">
>      <xsl:with-param name="states" select="states"/>
>    </xsl:call-template>
>  </xsl:template>
>
>  <xsl:template name="processLevel">
>    <xsl:param name="states"/>
>    <xsl:if test="string-length($states)>0">
>      <xsl:apply-templates mode="copy">
>        <xsl:with-param name="state" select="substring($states, 1, 1)"/>
>      </xsl:apply-templates>
>      <xsl:call-template name="processLevel">
>        <xsl:with-param name="states" select="substring($states, 2,
> string-length($states))"/>
>      </xsl:call-template>
>    </xsl:if>
>  </xsl:template>
>
>  <xsl:template match="node() | @*" mode="copy">
>    <xsl:param name="state"/>
>    <xsl:copy>
>      <xsl:apply-templates select="node() | @*" mode="copy">
>        <xsl:with-param name="state" select="$state"/>
>      </xsl:apply-templates>
>    </xsl:copy>
>  </xsl:template>
>
>  <xsl:template match="elem1/text()" mode="copy">
>    <xsl:param name="state"/>
>    <xsl:value-of select="."/>
>    <xsl:text>_</xsl:text>
>    <xsl:value-of select="$state"/>
>  </xsl:template>
> </xsl:stylesheet>
>
> Best Regards,
> George
> --
> George Cristian Bina
> <oXygen/> XML Editor, Schema Editor and XSLT Editor/Debugger
> http://www.oxygenxml.com
>
> nick public wrote:
>>
>> Hi George,
>> I appreciate very much your help.
>> Unfortunately, the middleware that I'm using and in which the XSLT
>> have to run, provide just XSLT version 1.0.
>> Is it possible to adapt your solution for XSLT 1.0?
>>
>> I tried to think to an alternative way but it seems a lot of complicated.
>> I could create three different versions of my script; each version is
>> specialized to work with a single status:
>> 1) script_4.xslt - evaluates the presence of status 4: if yes copies
>> <level> and modifies <elem1> with 4 status
>> 2) script_5.xslt - evaluates the presence of status 5: if yes,
>> appendes to the previous a new <level> copy with <elem1> modified with
>> 5 status. If 5 status doesn't exist, the output provided has to be
>> that of script_4.xslt
>> 3) script_6.xslt - evaluates the presence of status 6: if yes,
>> appendes to the previous a new <level> copy with <elem1> modified with
>> 6 status. If 6 status doesn't exist, the output provided has to be
>> that of script_5.xslt
>>
>> At the end, I could concatenate the three scripts in a job and should
>> obtain (I hope) the same result.
>>
>> Opinion about?
>>
>> Thanks a lot.
>> Ciao. Nicola
>>
>>
>> 2009/1/13 George Cristian Bina <george@xxxxxxxxxxxxx>:
>>>
>>> In XSLT 2.0 you can use something like this:
>>>
>>> <?xml version="1.0" encoding="UTF-8"?>
>>> <xsl:stylesheet version="2.0"
>>> xmlns:xsl="http://www.w3.org/1999/XSL/Transform";>
>>>  <xsl:template match="root">
>>>   <xsl:copy><xsl:apply-templates/></xsl:copy>
>>>  </xsl:template>
>>>
>>>  <xsl:template match="level">
>>>   <xsl:variable name="level" select="."/>
>>>   <xsl:for-each select="string-to-codepoints(states)">
>>>     <xsl:apply-templates select="$level" mode="copy">
>>>       <xsl:with-param name="state" select="."/>
>>>     </xsl:apply-templates>
>>>   </xsl:for-each>
>>>  </xsl:template>
>>>
>>>  <xsl:template match="node() | @*" mode="copy">
>>>   <xsl:param name="state"/>
>>>   <xsl:copy>
>>>     <xsl:apply-templates select="node() | @*" mode="copy">
>>>       <xsl:with-param name="state" select="$state"/>
>>>     </xsl:apply-templates>
>>>   </xsl:copy>
>>>  </xsl:template>
>>>
>>>  <xsl:template match="elem1/text()" mode="copy">
>>>   <xsl:param name="state"/>
>>>   <xsl:value-of select="."/>
>>>   <xsl:text>_</xsl:text>
>>>   <xsl:value-of select="codepoints-to-string($state)"/>
>>>  </xsl:template>
>>> </xsl:stylesheet>
>>>
>>> on your sample input it gives
>>>
>>> <root>
>>>   <level>
>>>       <states>456</states>
>>>       <start>
>>>           <elem1>element_4</elem1>
>>>       </start>
>>>   </level><level>
>>>       <states>456</states>
>>>       <start>
>>>           <elem1>element_5</elem1>
>>>       </start>
>>>   </level><level>
>>>       <states>456</states>
>>>       <start>
>>>           <elem1>element_6</elem1>
>>>       </start>
>>>   </level>
>>> </root>
>>>
>>> Best Regards,
>>> George
>>> --
>>> George Cristian Bina
>>> <oXygen/> XML Editor, Schema Editor and XSLT Editor/Debugger
>>> http://www.oxygenxml.com
>>>
>>> nick public wrote:
>>>>
>>>> Hi people.
>>>> I have the following need.
>>>> I have to duplicate a sub-tree basing of an element <states> contained
>>>> into the source XML.
>>>>
>>>> <root>
>>>>  <level>
>>>>   <states>456</states>
>>>>   <start>
>>>>     <elem1>element</elem1>
>>>>   </start>
>>>>  </level>
>>>> </root>
>>>>
>>>>
>>>> For each <level> copy, the <elem1> element have to contain the
>>>> corresponding states byte.
>>>> In this case I've create a XML target containing 3 <level> element and
>>>> for each copy, the <elem1> element contains the 4, 5 and 6 status.
>>>>
>>>> The target for the example have to be like this:
>>>>
>>>> <root>
>>>>  <level>
>>>>   <control>456</control>
>>>>   <start>
>>>>     <elem1>element_4</elem1>
>>>>   </start>
>>>>  </level>
>>>>  <level>
>>>>   <control>456</control>
>>>>   <start>
>>>>     <elem1>element_5</elem1>
>>>>   </start>
>>>>  </level>
>>>>  <level>
>>>>   <control>456</control>
>>>>   <start>
>>>>     <elem1>element_6</elem1>
>>>>   </start>
>>>>  </level>
>>>> </root>
>>>>
>>>> Now, I learned thanks to you how to copy the sub-tree. I haven't
>>>> problem with string processing.
>>>> My problem is to control the copy.
>>>> I need something like this (please, don't become frightened: it's the
>>>> minimal code and is commented)
>>>>
>>>> <?xml version="1.0" encoding="utf-8"?>
>>>> <xsl:stylesheet version="1.0"
>>>> xmlns:xsl="http://www.w3.org/1999/XSL/Transform";>
>>>>  <xsl:output method="xml" indent="yes"/>
>>>>
>>>>  <!-- Load in a variable the states to manage -->
>>>>  <xsl:variable name='states' select ='root/level/states'/>
>>>>
>>>>  <xsl:template match="level">
>>>>   <!-- Predisposes to manage all the three states
>>>>        passing as parameter each status to check in
>>>>        front to the states variable -->
>>>>
>>>>   <!-- Invokes copyNode for the status 4.
>>>>        In copyNodes, if 4 is contained in states
>>>>        variable the level element will be copied
>>>>        otherwise no -->
>>>>   <xsl:call-template name='copyNode'>
>>>>     <xsl:with-param name='status' select='4'/>
>>>>   </xsl:call-template>
>>>>
>>>>   <xsl:call-template name='copyNode'>
>>>>     <xsl:with-param name='status' select='5'/>
>>>>   </xsl:call-template>
>>>>
>>>>   <xsl:call-template name='copyNode'>
>>>>     <xsl:with-param name='status' select='6'/>
>>>>   </xsl:call-template>
>>>>  </xsl:template>
>>>>
>>>>  <xsl:template match="@*|node()">
>>>>   <xsl:copy>
>>>>     <xsl:apply-templates select="@*|node()"/>
>>>>   </xsl:copy>
>>>>  </xsl:template>
>>>>
>>>>  <xsl:template name="copyNode">
>>>>   <xsl:param name='status'/>
>>>>
>>>>   <!-- Checks if the current $status (4, 5 or 6)
>>>>        is contained in the $states.
>>>>        If yes, can copies the level element -->
>>>>   <xsl:if test='contains($states,$status)'>
>>>>
>>>>     <xsl:choose>
>>>>       <!-- :-(( THIS when SEEMS NEVER PERFORMED :-(( -->
>>>>       <xsl:when test="self::elem1">
>>>>         <elem1>
>>>>           <!-- The elem1 text value have to be manipulated by $status
>>>> -->
>>>>           <xsl:value-of select ='concat(substring(.,1,5),$status)'/>
>>>>         </elem1>
>>>>       </xsl:when>
>>>>
>>>>       <xsl:otherwise>
>>>>         <xsl:copy-of select="."/>
>>>>       </xsl:otherwise>
>>>>     </xsl:choose>
>>>>
>>>>    </xsl:if>
>>>>  </xsl:template>
>>>>
>>>> </xsl:stylesheet>
>>>>
>>>> The critical point is that the  <xsl:when test="self::elem1">  is
>>>> never reached and then I cannot operate the tranformation.
>>>>
>>>>
>>>> Could you help me URGENTLY?
>>>> Thanks a lot in advance.
>>>>
>>>> Ciao. Nicola

Current Thread