[XSL-LIST Mailing List Archive Home] [By Thread] [By Date] [Recent Entries] [Reply To This Message]

Re: String replacement in included content

Subject: Re: String replacement in included content
From: Rush Manbert <rush@xxxxxxxxxxx>
Date: Wed, 12 Oct 2005 17:11:49 -0700
included content text
David,

Your suggestion of doing this transformation in 2 passes was brilliant. I have the whole thing working now, and it didn't require too much code or change to the existing stylesheet.

A couple of global variables:
<!-- At the outermost level, we want an empty replace string and a proper search string -->
<xsl:variable name="globalFind">
<str>%CT%</str>
</xsl:variable>
<xsl:variable name="globalReplace">
<rep></rep>
</xsl:variable>


My component match template (I haven't edited it, these things are really called <imlcomponent>:
<xsl:template priority="1.0" match="//imlcomponent">
<xsl:variable name="componentFilename">
<xsl:value-of select="@href" />
</xsl:variable>
<xsl:variable name="repl">
<xsl:choose>
<xsl:when test="@tag">
<rep><xsl:value-of select="@tag" /></rep>
</xsl:when>
<xsl:otherwise>
<!-- No tag is equivalent to removing all occurrences of %CT% -->
<rep></rep>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>


<xsl:choose>
<!-- The cases where we never expand the imlcomponent -->
<xsl:when test="ancestor::imltemplatehead or ancestor::imlheadcontentinsert">
<xsl:if test="$v='y'"><xsl:comment>
imlcomponent is NEVER expanded inside imltemplatehead. Just copy element.</xsl:comment></xsl:if>
<xsl:copy-of select="." />
</xsl:when>
<!-- In all other cases we will bring in the right content from the component file -->
<xsl:otherwise>
<xsl:choose>
<!-- Decide what mode we need to use for the template processing -->
<xsl:when test="ancestor::form or $templateBodyContentIsInsideForm != ''">
<!-- The form//include case -->
<xsl:if test="$v='y'"><xsl:comment>
imlcomponent inside a form: Replace with all body/form content of: <xsl:value-of select="$componentFilename" /></xsl:comment></xsl:if>
<xsl:variable name="textReplacedContents">
<xsl:apply-templates select="document(string($componentFilename))//body/form/*" mode="r">
<xsl:with-param name="replace" select="exslt:node-set($repl)" />
</xsl:apply-templates>
</xsl:variable>
<xsl:apply-templates select="exslt:node-set($textReplacedContents)" />
</xsl:when>
<xsl:otherwise>
<!-- The include case outside of a form -->
<xsl:if test="$v='y'"><xsl:comment>
imlcomponent outside a form: Replace with body element content of:
<xsl:value-of select="$componentFilename" />, excluding all imltemplate elements.</xsl:comment></xsl:if>
<xsl:variable name="textReplacedContents">
<xsl:apply-templates
select="document(string($componentFilename))//body/*[not(local-name()='imltemplate')]"


mode="r">
<xsl:with-param name="replace" select="exslt:node-set($repl)" />
</xsl:apply-templates>
</xsl:variable>
<xsl:apply-templates select="exslt:node-set($textReplacedContents)" />
</xsl:otherwise>
</xsl:choose>
</xsl:otherwise>
</xsl:choose>
</xsl:template>


The key here was to apply the text replacement templates first to the node set, then to apply-templates to it.

The replacement templates:
<!--=======================================================================================
Template for processing a non-text node. Replaces all occurrences of the search string
terms in the element's attribute values with their corresponding replacement text, then
recursively processes all children for the same text replacement.
========================================================================================-->
<xsl:template match="node()" mode="r">
<xsl:comment>Template match for node() mode="r"</xsl:comment>
<xsl:param name="search" select="exslt:node-set($globalFind)" />
<xsl:param name="replace" select="exslt:node-set($globalReplace)" />
<xsl:copy>
<!-- String replacement for each attribute value -->
<xsl:for-each select="@*">
<xsl:attribute name="{name()}">
<xsl:call-template name="str:replace">
<xsl:with-param name="string" select="string(.)" />
<xsl:with-param name="search" select="$search" />
<xsl:with-param name="replace" select="$replace" />
</xsl:call-template>
</xsl:attribute>
</xsl:for-each>
<!-- Recurse over children -->
<xsl:apply-templates select="node()" mode="r">
<xsl:with-param name="search" select="$search" />
<xsl:with-param name="replace" select="$replace" />
</xsl:apply-templates>
</xsl:copy>
</xsl:template>


<!--=======================================================================================
Template for processing a text node. Replaces all occurrences of the search string
terms in the text string with their corresponding replacement text.
========================================================================================-->
<xsl:template match="text()" mode="r">
<xsl:comment>Template match for text() mode="r"</xsl:comment>
<xsl:param name="search" select="exslt:node-set($globalFind)" />
<xsl:param name="replace" select="exslt:node-set($globalReplace)" />
<xsl:variable name="x">
<xsl:value-of select="." />
</xsl:variable>
<xsl:call-template name="str:replace">
<xsl:with-param name="string" select="$x" />
<xsl:with-param name="search" select="$search" />
<xsl:with-param name="replace" select="$replace" />
</xsl:call-template>
</xsl:template>


This took care of the included components. To handle the case where a document can be used as either a component or a top level doc, I needed to find a way to do the equivalent transformation on the top level doc. I already had a template that matched a naked <HTML> element (which all of my documents contain), so I added the processing there:
<xsl:template match="html[not(@xmlns)]">
<!-- Do string replacement on all children -->
<xsl:variable name="temp">
<xsl:apply-templates select="@*|node()" mode="r">
</xsl:apply-templates>
</xsl:variable>
<!-- transform the html element as required -->
<xsl:element name="{name()}">
<xsl:attribute name="xmlns">
<xsl:text>http://www.w3.org/1999/xhtml</xsl:text>
</xsl:attribute> <xsl:copy-of select="@*" />
<!-- Process the text replaced nodeset -->
<xsl:apply-templates select="exslt:node-set($temp)" />
</xsl:element>
</xsl:template>


There's probably a better way to handle this, but the things I tried didn't work.

I think that's everything that is relevant.

Now I can handle:
<imlcomponent href="file" />
<imlcomponent href="file" tag="" />
<imlcomponent href="file" tag="FixedTag">
<imlcomponent href="file" tag="%CT%_AddedTag" />
where the last version allows me to "chain" the tags through a series of nested includes. Very cool.


Thank you again for your help. I would have flopped around for a long time without it.

Best regards,
Rush

Current Thread

PURCHASE STYLUS STUDIO ONLINE TODAY!

Purchasing Stylus Studio from our online shop is Easy, Secure and Value Priced!

Buy Stylus Studio Now

Download The World's Best XML IDE!

Accelerate XML development with our award-winning XML IDE - Download a free trial today!

Don't miss another message! Subscribe to this list today.
Email
First Name
Last Name
Company
Subscribe in XML format
RSS 2.0
Atom 0.3
Site Map | Privacy Policy | Terms of Use | Trademarks
Free Stylus Studio XML Training:
W3C Member
Stylus Studio® and DataDirect XQuery ™are products from DataDirect Technologies, is a registered trademark of Progress Software Corporation, in the U.S. and other countries. © 2004-2013 All Rights Reserved.