Re: [xsl] Stylesheet from a stylesheet

Subject: Re: [xsl] Stylesheet from a stylesheet
From: Midsummer Sun <midsummer.sun@xxxxxxxxx>
Date: Mon, 14 Mar 2005 13:47:47 +0530
Thank you so much! It looks great. I'll test it.. and see if I need
anything else.

One requirement is left - mapping namespaces in two XMLs. I'll
probably take care of it later. If you have time, you may give a
modified version of your XSLT to take care of namespaces as well. Or
probably somebody else could..

I am wondering if the tool you have written is able to generate
optimized xsl:template definitions(just like we write XSLT files
manually) and also takes care of overlapping (nested tags).. I'll have
to test it and say further..

Thanks a lot!

Best regards,

On Mon, 14 Mar 2005 07:57:40 +0000, Aron Bock <aronbock@xxxxxxxxxxx> wrote:
> Midsummer Sun,
>
> Here's how I approached it.  At first blush it seems extensible, but your
> mileage may vary.  The idea is to design a "little-language", with a
grammar
> and a processor.  Also, unfortunately, this post is rather long, and I
> couldn't make it any shorter.  While one could argue that some of the
> templates in the "processor" below could be combined, I prefer to start
with
> multiple points of abstraction.
>
> Anyway, here's our input file: x1.xml
>
>
> <x>
> <p a="1">1</p>
> <q b="2">2</q>
> </x>
>
> We want to turn it into this: x2.xml
>
> <x>
> <u><w m="1">1</w></u>
> <v n="2">2</v>
> </x>
>
> First I manually wrote a stylesheet to do this.  We start with an "identity
> transform", then supply templates to manipulate specific nodes.  Here it
is:
> x.xsl
>
>
> <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform";
> version="1.0">
>    <!-- Start with identity transform: copies input to output -->
>    <xsl:template match="node() | @*">
>        <xsl:copy>
>            <xsl:apply-templates select="node() | @*"/>
>        </xsl:copy>
>    </xsl:template>
>
>    <xsl:template match="x/p">
>        <u>
>            <w>
>                <xsl:apply-templates select="@*"/>
>                <xsl:apply-templates/>
>            </w>
>        </u>
>    </xsl:template>
>
>    <xsl:template match="x/p/@a">
>        <xsl:attribute name="m">
>            <xsl:value-of select="."/>
>        </xsl:attribute>
>    </xsl:template>
>
>    <xsl:template match="x/q">
>        <v>
>            <xsl:apply-templates select="@*"/>
>            <xsl:apply-templates/>
>        </v>
>    </xsl:template>
>
>    <xsl:template match="x/q/@b">
>        <xsl:attribute name="n">
>            <xsl:value-of select="."/>
>        </xsl:attribute>
>    </xsl:template>
> </xsl:stylesheet>
>
> Then I created a "mapping file" with enough information so that I could
> mechanixally generate the stylesheet above.  I opted to go with flat <map>
> elements, and to have element and attribute maps at the same level.  That's
> because I tend not to like special cases, but again this is just my
> preference.  Note that element @to mappings show just what the terminal
> element will be replaced; not the new path-from-root.  Here it is: map.xml
>
> <maps>
>    <map from="/x/p" to="u/w"/>
>    <map from="/x/p/@a" to="@m"/>
>
>    <map from="/x/q" to="v"/>
>    <map from="/x/q/@b" to="@n"/>
> </maps>
>
> Lastly I wrote a stylesheet to take map.xml as input, and to generate an
> output xml file, like x.xsl, that's a stylesheet capable of transforming
> x1.xml to x2.xml.  It imports another stylesheet; see below.  Here is
> map.xsl
>
>
> <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform";
> version="1.0"
>    xmlns:xalan="http://xml.apache.org/xalan";
>    exclude-result-prefixes="xalan">
>
>    <xsl:import href="str.tokenize.xsl"/>
>
>    <xsl:output indent="yes"/>
>
>    <xsl:template match="/">
>        <xsl:call-template name="write-stylesheet"/>
>    </xsl:template>
>
>    <xsl:template name="write-stylesheet">
>        <xsl:element name="xsl:stylesheet">
>            <xsl:attribute
> name="xmlns:xsl">http://www.w3.org/1999/XSL/Transform</xsl:attribute>
>            <xsl:attribute name="version">1.0</xsl:attribute>
>            <xsl:element name="xsl:output">
>                <xsl:attribute
> name="omit-xml-declaration">yes</xsl:attribute>
>            </xsl:element>
>
>            <xsl:call-template name="write-identity"/>
>            <xsl:apply-templates select="/maps/map"/>
>
>        </xsl:element>
>    </xsl:template>
>
>    <!-- Writes identity-transform template -->
>    <xsl:template name="write-identity">
>        <xsl:element name="xsl:template">
>            <xsl:attribute name="match">node() | @*</xsl:attribute>
>            <xsl:element name="xsl:copy">
>                <xsl:element name="xsl:apply-templates">
>                    <xsl:attribute name="select">node() | @*</xsl:attribute>
>                </xsl:element>
>            </xsl:element>
>        </xsl:element>
>    </xsl:template>
>
>    <!-- Processes each mapping -->
>    <xsl:template match="/maps/map">
>        <xsl:element name="xsl:template">
>            <xsl:attribute name="match"><xsl:value-of
> select="@from"/></xsl:attribute>
>            <xsl:call-template name="write-to">
>                <xsl:with-param name="to" select="@to"/>
>            </xsl:call-template>
>        </xsl:element>
>    </xsl:template>
>
>    <!-- Writes the "to" part of each mapping -->
>    <xsl:template name="write-to">
>        <xsl:param name="to" select="/.."/>
>        <xsl:variable name="path">
>            <xsl:call-template name="tokenize-path">
>                <xsl:with-param name="path" select="$to"/>
>            </xsl:call-template>
>        </xsl:variable>
>
>        <xsl:call-template name="write-to-path">
>            <xsl:with-param name="path"
> select="xalan:nodeset($path)/token"/>
>        </xsl:call-template>
>    </xsl:template>
>
>    <!--
>    Continuation of template "write-to"; writes the templates
>    that creates the "to" elements/attributes
>    -->
>    <xsl:template name="write-to-path">
>        <xsl:param name="path" select="/.."/>
>
>        <xsl:choose>
>            <xsl:when test="not($path)">
>                <!-- Nothing to do (not needed; here just as safeguard) -->
>            </xsl:when>
>            <!-- If attribute, this is a 1:1 transform -->
>            <xsl:when test="starts-with($path[1], '@')">
>                <xsl:element name="xsl:attribute">
>                    <xsl:attribute name="name">
>                        <xsl:value-of select="substring($path[1], 2)"/>
>                    </xsl:attribute>
>                    <xsl:element name="xsl:value-of">
>                        <xsl:attribute name="select">.</xsl:attribute>
>                    </xsl:element>
>                </xsl:element>
>            </xsl:when>
>            <!-- Must be (we assume) an element; recursively write target
> nodes, in order -->
>            <xsl:otherwise>
>                <xsl:element name="{$path[1]}">
>                    <xsl:choose>
>                        <xsl:when test="not($path[position() != 1])">
>                            <xsl:element name="xsl:apply-templates">
>                                <xsl:attribute
> name="select">@*</xsl:attribute>
>                            </xsl:element>
>                            <xsl:element name="xsl:apply-templates"/>
>                        </xsl:when>
>                        <xsl:otherwise>
>                            <xsl:call-template name="write-to-path">
>                                <xsl:with-param name="path"
> select="$path[position() != 1]"/>
>                            </xsl:call-template>
>                        </xsl:otherwise>
>                    </xsl:choose>
>                </xsl:element>
>            </xsl:otherwise>
>        </xsl:choose>
>
>    </xsl:template>
>
>    <!-- Calls an utility tokenizing template -->
>    <xsl:template name="tokenize-path">
>        <xsl:param name="path" select="/.."/>
>        <xsl:call-template name="tokenize">
>            <xsl:with-param name="string" select="$path"/>
>            <xsl:with-param name="delimiters" select="'/'"/>
>        </xsl:call-template>
>    </xsl:template>
>
> </xsl:stylesheet>
>
> The stylesheet it imports is str.tokenize.xsl.  I got it from Sal Mangano's
> "XSLT Cookbook", and he attributes it to Jeni Tennison.  Here is
> str.tokenize.xsl
>
>
> <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform";
> version="1.0">
> <xsl:template name="tokenize">
>     <xsl:param name="string" select="''" />
>     <xsl:param name="delimiters" select="'
> '" />
>
>  <xsl:choose>
>     <!-- Nothing to do if empty string -->
>    <xsl:when test="not($string)" />
>
>     <!-- No delimiters signals character level tokenization. -->
>    <xsl:when test="not($delimiters)">
>      <xsl:call-template name="_tokenize-characters">
>        <xsl:with-param name="string" select="$string" />
>      </xsl:call-template>
>    </xsl:when>
>    <xsl:otherwise>
>      <xsl:call-template name="_tokenize-delimiters">
>        <xsl:with-param name="string" select="$string" />
>        <xsl:with-param name="delimiters" select="$delimiters" />
>      </xsl:call-template>
>    </xsl:otherwise>
>  </xsl:choose>
> </xsl:template>
>
> <xsl:template name="_tokenize-characters">
>  <xsl:param name="string" />
>  <xsl:if test="$string">
>    <token><xsl:value-of select="substring($string, 1, 1)" /></token>
>    <xsl:call-template name="_tokenize-characters">
>      <xsl:with-param name="string" select="substring($string, 2)" />
>    </xsl:call-template>
>  </xsl:if>
> </xsl:template>
>
> <xsl:template name="_tokenize-delimiters">
>  <xsl:param name="string" />
>  <xsl:param name="delimiters" />
>  <xsl:param name="last-delimit"/>
>  <!-- Extract a delimiter -->
>  <xsl:variable name="delimiter" select="substring($delimiters, 1, 1)" />
>  <xsl:choose>
>     <!-- If the delimiter is empty we have a token -->
>    <xsl:when test="not($delimiter)">
>      <token><xsl:value-of select="$string"/></token>
>    </xsl:when>
>     <!-- If the string contains at least one delimiter we must split it -->
>    <xsl:when test="contains($string, $delimiter)">
>      <!-- If it starts with the delimiter we don't need to handle the -->
>       <!-- before part -->
>      <xsl:if test="not(starts-with($string, $delimiter))">
>         <!-- Handle the part that comes befor the current delimiter -->
>         <!-- with the next delimiter. If ther is no next the first test -->
>         <!-- in this template will detect the token -->
>        <xsl:call-template name="_tokenize-delimiters">
>          <xsl:with-param name="string"
>                          select="substring-before($string, $delimiter)" />
>          <xsl:with-param name="delimiters"
>                          select="substring($delimiters, 2)" />
>        </xsl:call-template>
>      </xsl:if>
>       <!-- Handle the part that comes after the delimiter using the -->
>       <!-- current delimiter -->
>      <xsl:call-template name="_tokenize-delimiters">
>        <xsl:with-param name="string"
>                        select="substring-after($string, $delimiter)" />
>        <xsl:with-param name="delimiters" select="$delimiters" />
>      </xsl:call-template>
>    </xsl:when>
>    <xsl:otherwise>
>       <!-- No occurances of current delimiter so move on to next -->
>      <xsl:call-template name="_tokenize-delimiters">
>        <xsl:with-param name="string"
>                        select="$string" />
>        <xsl:with-param name="delimiters"
>                        select="substring($delimiters, 2)" />
>      </xsl:call-template>
>    </xsl:otherwise>
>  </xsl:choose>
> </xsl:template>
> </xsl:stylesheet>
>
> Regards,
>
> --A
>
> _________________________________________________________________
> Is your PC infected? Get a FREE online computer virus scan from McAfee.
> Security. http://clinic.mcafee.com/clinic/ibuy/campaign.asp?cid=3963

Current Thread