Re: Char node-type

Subject: Re: Char node-type
From: Jeni Tennison <mail@xxxxxxxxxxxxxxxx>
Date: Thu, 23 Nov 2000 11:21:20 +0000
Richard,

> Since the translate function only deals with one-to-one character
> mappings, we have written a template to process text() nodes which
> processed the first character, then calls itself recursively to
> process the rest of the string. This works fine so long as you don't
> have more than about 600 characters in a single element - with a
> long enough string you get stack overflows.

The way that I usually do this is to define a set of replacements that
I want to have made:

<xsl:variable name="replacements-rtf">
   <replace>
      <from>&lt;</from>
      <to><span class="entity">&amp;lt;</span></to>
   </replace>
   <replace>
      <from>&gt;</from>
      <to><span class="entity">&amp;gt;</span></to>
   </replace>
   ...
</xsl:variable>

<xsl:variable name="replacements"
              select="saxon:node-set($replacements-rtf)/replace" />
<!--
  or          select="msxsl:node-set($replacements-rtf)/replace" />
  ...etc for other XSLT Processors' node-set extension functions...
  or          select="document('')/*/xsl:variable[@name =
                                    'replacements-rtf']/replace" />
-->

then have a template that performs substitution based on this set of
replacements:

<xsl:template name="substitute">
   <xsl:param name="string" />
   <xsl:variable name="sub"
                 select="$replacements[contains($string, from)][1]" />
   <xsl:choose>
      <xsl:when test="$sub">
         <xsl:variable name="before"
                       select="substring-before($string, $sub/from)" />
         <xsl:variable name="after"
                       select="substring-after($string, $sub/from)" />
         <xsl:if test="$before">
            <xsl:call-template name="substitute">
               <xsl:with-param name="string" select="$before" />
            </xsl:call-template>
         </xsl:if>
         <xsl:copy-of select="$sub/to" />
         <xsl:if test="$after">
            <xsl:call-template name="substitute">
               <xsl:with-param name="string" select="$after" />
            </xsl:call-template>
         </xsl:if>
      </xsl:when>
      <xsl:otherwise><xsl:value-of select="$string" /></xsl:otherwise>
   </xsl:choose>
</xsl:template>

which is then called from a template that matches any text that is
found:

<xsl:template match="text()">
   <xsl:call-template name="substitute">
      <xsl:with-param name="string" select="." />
   </xsl:call-template>
</xsl:template>

Now, this has disadvantages: each bit of the string gets examined
multiple times - the more replacements you have the worse it gets -
but if having to make a replacement is fairly rare, then the higher
reliance of XPath functions might mean it's better for you?

> It wouldn't be necessary to use this unpleasant and inefficient
> technique if XPath defined in its data model, and XSLT supported, a
> char node-type. (Isolating a single character would also allow us to
> access its character value via the number function, which would be
> handy for processing classes of Unicode characters.)

XPointer has something like this with its string ranges.  For example,
in XPointer I could do:

  string-range(text(), '&lt;')

to get a number of string ranges representing all instances of the
character '&lt;' within the text()'s value. You could imagine a future
version of XSLT incorporating XPointer and having a function to
perform substitution on a set of string ranges.

Sorry I can't be more help,

Jeni

---
Jeni Tennison
http://www.jenitennison.com/



 XSL-List info and archive:  http://www.mulberrytech.com/xsl/xsl-list


Current Thread