XSLT problem

Here should go questions about transforming XML with XSLT and FOP.
panpilulu
Posts: 4
Joined: Thu Aug 10, 2006 1:44 pm
Contact:

XSLT problem

Post by panpilulu »

Hello,

I have this input file that I want to transform:

Code: Select all

<definition name="fr.inria.test.clientServerComp" extends="fr.inria.test.rootType">

<component name="client" definition="fr.inria.test.client" />
<component name="more" definition="fr.inria.test.more" >
<component name="inMore">
<interface name="blabla" signature="olala"></interface>
<component name="inInMore-1">
<interface name="blabla" signature="olala"></interface>
</component>
<component name="inInMore-2">
<interface name="blabla" signature="olala"></interface>
</component>
</component>
<component name="inMore-second">
<interface name="blabla" signature="olala"></interface>
</component>
</component>
<component name="server" definition="fr.inria.test.server" />
<component name="inMore" definition="fr.inria.test.more"/>
</component>

<binding client="this.runnableItf" server="client.runnableItf" />
<binding client="client.print" server="server.print" />

</definition>

I have this additional xml file:

Code: Select all

<fractal-deployment>

<real-host name="Belote">
<virtual-host name="VH2" type="jvm">
<execution-context name="EC2" >
<content>
<component-name>server</component-name>
</content>
</execution-context>
</virtual-host>
<ip>138.96.64.61</ip>
</real-host >

<real-host name="Parlote">
<virtual-host name="VH1" type="jvm">
<execution-context name="EC1" >
<content>
<component-name>client</component-name>
<component-name>inMore</component-name>
</content>
</execution-context>
</virtual-host>
</real-host>

</fractal-deployment>
The goal of the XSLT is to insert virtual-node element with attribute name and value of this attribute equal of the corresponding execution context, in the component elements. The result after my XSLT transformation is:

Code: Select all

<definition name="fr.inria.test.clientServerComp" extends="fr.inria.test.rootType">

<component name="client" definition="fr.inria.test.client" >
<virtual-node name="EC1">
</component>
<component name="more" definition="fr.inria.test.more" >
<component name="inMore">
<interface name="blabla" signature="olala"></interface>
<component name="inInMore-1">
<interface name="blabla" signature="olala"></interface>
</component>
<component name="inInMore-2">
<interface name="blabla" signature="olala"></interface>
</component>
<virtual-node name="EC1">
</component>
<component name="inMore-second">
<interface name="blabla" signature="olala"></interface>
</component>
</component>
<component name="server" definition="fr.inria.test.server" />
<component name="inMore" definition="fr.inria.test.more"/>
<virtual-node name="EC2">
</component>

<binding client="this.runnableItf" server="client.runnableItf" />
<binding client="client.print" server="server.print" />

</definition>

That is what i managed to do with the folowing XSL :

Code: Select all

<xsl:transform  xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:variable name="depl_file" select="document('../data/deployfractal.xml')"/>
<xsl:template match="node()|@*">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="//component">
<xsl:copy>
<xsl:apply-templates select="@* | node()"/>
<xsl:variable name="comp_name" select="$depl_file//real-host/virtual-host/execution-context/content/component-name/text()"/>
<xsl:if test="current()/@name=$comp_name">
<xsl:element name="virtual-node">
<xsl:attribute name="name">
<xsl:value-of select="$depl_file//real-host/virtual-host/execution-context[content/component-name/text() = current()/@name]/@name"/>
</xsl:attribute>
</xsl:element>
</xsl:if>
</xsl:copy>
</xsl:template>
</xsl:transform>


But the problem is that there is difference between the component inMore in more and the component inMore in server. So I want to reference the components in the deployment xml file like this :

<component-name>server/inMore</component-name>
<component-name>more/inMore</component-name>
<component-name>more/inMore/inInMore-2</component-name>

The problem is that I dont know how to modify the XSLT to do now the insertion of virtual-node-s correctly.
Do you have any suggestions?
One particular problem that I have is that I dont know how to reference for example ONLY the second child component of the root element. "child::component[position()=2]" returns to me not only the component "more" but also the second component in it "inMore-second".

Violeta
jkmyoung
Posts: 89
Joined: Mon Mar 06, 2006 10:13 pm

Post by jkmyoung »

/*/*[2]

Does this xpath work?
panpilulu
Posts: 4
Joined: Thu Aug 10, 2006 1:44 pm
Contact:

Post by panpilulu »

Almost!
More precisely:
/*/component[2]

Thank you!
sorin_ristache
Posts: 4141
Joined: Fri Mar 28, 2003 2:12 pm

Re: XSLT problem

Post by sorin_ristache »

Hello,
panpilulu wrote:One particular problem that I have is that I dont know how to reference for example ONLY the second child component of the root element. "child::component[position()=2]" returns to me not only the component "more" but also the second component in it "inMore-second".
I am not sure what you mean by only the second child component of the root element. A node returned by an XPath expression always includes all the child nodes of that node. That is the "more" component (<component name="more" definition="fr.inria.test.more">) always includes the "inMore-second" component (<component name="inMore-second">).
panpilulu wrote:I have this input file that I want to transform:
...
I have this additional xml file:
...
The result after my XSLT transformation is:
...
That is what i managed to do with the folowing XSL :
...
The result that I get with your sample files has a couple of differences. It contains

Code: Select all

    <component name="server" definition="fr.inria.test.server">
<virtual-node name="EC2"/>
</component>
<component name="inMore" definition="fr.inria.test.more">
<virtual-node name="EC1"/>
</component>
instead of

Code: Select all

    <component name="server" definition="fr.inria.test.server"/>
<component name="inMore" definition="fr.inria.test.more"/>
<virtual-node name="EC2"/>
Please post the expected result file for the 2 given XML files and the XSL file.
panpilulu wrote:So I want to reference the components in the deployment xml file like this :

<component-name>server/inMore</component-name>
<component-name>more/inMore</component-name>
<component-name>more/inMore/inInMore-2</component-name>
Also post the final form of the input files including deployfractal.xml.

Regards,
Sorin
panpilulu
Posts: 4
Joined: Thu Aug 10, 2006 1:44 pm
Contact:

Post by panpilulu »

Sorry my first post is not very clear and even contains an error.
Thank you Sorin. I will explain. So we keep the first file.

Code: Select all

<definition name="fr.inria.test.clientServerComp" extends="fr.inria.test.rootType">

<component name="client" definition="fr.inria.test.client" />
<component name="more" definition="fr.inria.test.more" >
<component name="inMore">
<interface name="blabla" signature="olala"></interface>
<component name="inInMore-1">
<interface name="blabla" signature="olala"></interface>
</component>
<component name="inInMore-2">
<interface name="blabla" signature="olala"></interface>
</component>
</component>
<component name="inMore-second">
<interface name="blabla" signature="olala"></interface>
</component>
</component>
<component name="server" definition="fr.inria.test.server" />
<component name="inMore" definition="fr.inria.test.more"/>
</component>

<binding client="this.runnableItf" server="client.runnableItf" />
<binding client="client.print" server="server.print" />

</definition>
We modify the second

Code: Select all

<fractal-deployment>

<real-host name="Belote">
<virtual-host name="VH2" type="jvm">
<execution-context name="EC2" >
<content>
<component-name>server/inMore</component-name>
</content>
</execution-context>
</virtual-host>
<ip>138.96.64.61</ip>
</real-host >

<real-host name="Parlote">
<virtual-host name="VH1" type="jvm">
<execution-context name="EC1" >
<content>
<component-name>client</component-name>
<component-name>more/inMore/inInMore-1</component-name>
</content>
</execution-context>
</virtual-host>
</real-host>

</fractal-deployment>
The expected result

Code: Select all

<definition name="fr.inria.test.clientServerComp" extends="fr.inria.test.rootType">

<component name="client" definition="fr.inria.test.client" />
<virtual-node name="EC1">
</component>
<component name="more" definition="fr.inria.test.more" >
<component name="inMore">
<interface name="blabla" signature="olala"></interface>
<component name="inInMore-1">
<interface name="blabla" signature="olala"></interface>
<virtual-node name="EC1">
</component>
<component name="inInMore-2">
<interface name="blabla" signature="olala"></interface>
</component>
</component>
<component name="inMore-second">
<interface name="blabla" signature="olala"></interface>
</component>
</component>
<component name="server" definition="fr.inria.test.server" />
<component name="inMore" definition="fr.inria.test.more"/>
<virtual-node name="EC2">
</component>
</component>

<binding client="this.runnableItf" server="client.runnableItf" />
<binding client="client.print" server="server.print" />

</definition>
I wold like to modify the xsl file to do this....
panpilulu
Posts: 4
Joined: Thu Aug 10, 2006 1:44 pm
Contact:

Post by panpilulu »

I think I found a way :lol:

Code: Select all

<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform"  version="1.0">

<xsl:variable name="depl_file" select="document('../data/deployfractal.xml')"/>

<xsl:template match="node()|@*">

<xsl:copy>

<xsl:apply-templates select="node()|@*"/>

</xsl:copy>

</xsl:template>

<xsl:template match="//component">

<xsl:copy>

<xsl:apply-templates select="@* | node()"/>

<xsl:variable name="comp_name_depl" select="$depl_file//real-host/virtual-host/execution-context/content/component-name/text()"/>

<xsl:variable name="comp_name_fract">

<xsl:if test="current()/parent::component">

<xsl:for-each select="current()/ancestor::component">

<xsl:value-of select="concat(current()/@name,'/')"/>

</xsl:for-each>

</xsl:if>

<xsl:value-of select="current()/@name"/>

</xsl:variable>

<xsl:if test="$comp_name_fract=$comp_name_depl">

<xsl:element name="virtual-node">

<xsl:attribute name="name">

<xsl:value-of select="$depl_file//real-host/virtual-host/execution-context[content/component-name/text() = $comp_name_fract]/@name"/>

</xsl:attribute>

</xsl:element>

</xsl:if>

</xsl:copy>

</xsl:template>

</xsl:transform>

:lol:

Thanks to all of you!
sorin_ristache
Posts: 4141
Joined: Fri Mar 28, 2003 2:12 pm

Post by sorin_ristache »

Hello Violeta,

You can find below a more compact and simple stylesheet which uses XSLT 2.0. I made the comp_name variable global because it does not depend on the context element of the template so it should be computed only once. You have to apply an XSLT 2.0 processor, for example Saxon 8B.

Code: Select all

<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">

<xsl:variable name="depl_file" select="document('../data/deployfractal.xml')"/>
<xsl:variable name="comp_name" select="$depl_file//real-host/virtual-host/execution-context/content/component-name"/>

<xsl:template match="node()|@*">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>

<xsl:template match="component">
<xsl:copy>
<xsl:apply-templates select="@* | node()"/>
<xsl:variable name="complete_name"
select="string-join(for $i in ancestor-or-self::component return $i/@name, '/')"/>
<xsl:if test="$complete_name=$comp_name">
<xsl:variable name="virtual_node_name"
select="for $i in $comp_name return if ($i=$complete_name) then $i/../../@name else ()">
</xsl:variable>
<virtual-node name="{$virtual_node_name}"/>
</xsl:if>
</xsl:copy>
</xsl:template>
</xsl:transform>
Regards,
Sorin
Post Reply