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
--~------------------------------------------------------------------
XSL-List info and archive: http://www.mulberrytech.com/xsl/xsl-list
To unsubscribe, go to: http://lists.mulberrytech.com/xsl-list/
or e-mail: <mailto:xsl-list-unsubscribe(_at_)lists(_dot_)mulberrytech(_dot_)com>
--~--