|
[XSL-LIST Mailing List Archive Home] [By Thread] [By Date] [Recent Entries] [Reply To This Message] RE: Stylesheet from a stylesheet
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
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
<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 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>
<!-- 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>
<xsl:call-template name="write-to-path"> <xsl:with-param name="path" select="xalan:nodeset($path)/token"/> </xsl:call-template> </xsl:template>
<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>
</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
|
PURCHASE STYLUS STUDIO ONLINE TODAY!Purchasing Stylus Studio from our online shop is Easy, Secure and Value Priced! Download The World's Best XML IDE!Accelerate XML development with our award-winning XML IDE - Download a free trial today! Subscribe in XML format
|

Cart








