This is the mail archive of the xsl-list@mulberrytech.com mailing list .


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]

Re: Best way to handle multiple string replacements?


Warren,

>Does anyone have any cool templates that take a hashtable of
>find and replace strings that are all applied to one piece of
>input text?

Here is *a* way to do it.  I'm not sure that it's the most efficient, but
it's the principal that counts, and the principal is to XMLise the
information about what you want to find and replace, or the characters that
need to be escaped.

So, make up a namespace for this information and include it in your file.
For the replacements that you're using, I created two sets of elements:

<foo:special_characters>
  <foo:char>_</foo:char>
  <foo:char>%</foo:char>
  <foo:char>$</foo:char>
  <foo:char>{</foo:char>
  <foo:char>}</foo:char>
  <foo:char>&amp;</foo:char>
</foo:special_characters>

<foo:string_replacement>
  <foo:search>
    <foo:find>&#0177;</foo:find>
    <foo:replace>$\pm$</foo:replace>
  </foo:search>
  <foo:search>
    <foo:find>&#0176;</foo:find>
    <foo:replace>$\degree$</foo:replace>
  </foo:search>
  <foo:search>
    <foo:find>&#0169;</foo:find>
    <foo:replace>\copyright</foo:replace>
  </foo:search>
  <foo:search>
    <foo:find>&#0182;</foo:find>
    <foo:replace>$\mathbb{P}$</foo:replace>
  </foo:search>
</foo:string_replacement>

This separates out the data about the replacements that you want to make
(the what).  Now you want to specify the procedure about how to do those
replacements (the how).  I've called your existing templates to actually do
the replacement, and focussed on identifying what you want to exchange.

First, then, the 'escape_special_characters' template.  Basically, you want
to first replace the characters on the $input_text, then replace the
strings on the output from that:

<xsl:template name="replace_special_characters">
  <xsl:with-param name="input_text" />
  <xsl:variable name="replaced_text">
    <xsl:call-template name="replace_characters">
      <xsl:with-param name="input_text" select="$input_text" />
    </xsl:call-template>
  </xsl:variable>
  <xsl:call-template name="replace_strings">
    <xsl:with-param name="input_text" select="$replaced_text" />
  </xsl:call-template>
</xsl:template>

The two templates for replacing the characters and replacing the strings
are much the same, so I'll only go through the one replacing the characters
in detail.

<xsl:template name="replace_characters">
...
</xsl:template>

First, we need to declare a couple of parameters that we're going to use.
One is the text that we need to replace and the other is one to keep track
of where we are in the set of replacements that we need to make.  I've done
this second using an index number, defaulting it to 1 as the initial value.

  <xsl:with-param name="input_text" />
  <xsl:with-param name="char">1</xsl:with-param>

Then we need to create the new string, with the replacements made.  We do
this by calling your put_slash_in_front_of template, with the $input_text
that we already have and the $special_char that is identified by the index
number.  We get at the character by getting the nth foo:char within the
current document (the stylesheet), i.e. document('')//foo:char[$char].

  <xsl:variable name="replaced_text">
    <xsl:call-template name="put_slash_in_front_of">
      <xsl:with-param name="input_text" select="$input_text" />
      <xsl:with-param name="special_char" 
        select="document('')//foo:char[$char]" />
    </xsl:call-template>
  </xsl:variable>

Now the recursive part.  If we haven't got to the end of the list of
characters that need to be escaped, then we have to move on to the next
one, calling this same template with the next index, and with the text that
we've created (i.e. that's already been escaped).  If we've run out of
foo:char, then we just return the escaped text.

  <xsl:choose>
    <xsl:when test="$char &lt; count(document('')//foo:char)">
      <xsl:call-template name="replace_characters">
        <xsl:with-param name="input_text" select="$replaced_text" />
        <xsl:with-param name="char" select="$char + 1" />
      </xsl:call-template>
    </xsl:when>
    <xsl:otherwise>
      <xsl:value-of select="$replaced_text" />
    </xsl:otherwise>
  </xsl:choose>

And that's it.  The other one in full is:

<xsl:template name="replace_strings">
  <xsl:param name="input_text" />
  <xsl:param name="search">1</xsl:param>
  <xsl:variable name="replaced_text">
    <xsl:call-template name="latex_string_replace">
      <xsl:with-param name="input_text" select="$input_text" />
      <xsl:with-param name="find" 
        select="document('')//foo:search[$search]/foo:find" />
      <xsl:with-param name="replace" 
        select="document('')//foo:search[$search]/foo:replace" />
    </xsl:call-template>
  </xsl:variable>
  <xsl:choose>
    <xsl:when test="$search &lt; count(document('')//foo:search)">
      <xsl:call-template name="replace_strings">
        <xsl:with-param name="input_text" select="$replaced_text" />
        <xsl:with-param name="search" select="$search + 1" />
      </xsl:call-template>
    </xsl:when>
    <xsl:otherwise>
      <xsl:value-of select="$replaced_text" />
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

This is tested in SAXON and gives the same results as your original
approach, but it is much easier to extend.

You could probably apply templates to the foo:char and foo:search nodes as
an alternative approach - the important thing is to separate out the what
from the how, not the how you do the how :)

I hope that helps,

Jeni

Dr Jeni Tennison
Epistemics Ltd, Strelley Hall, Nottingham, NG8 6PE
Telephone 0115 9061301 • Fax 0115 9061304 • Email
jeni.tennison@epistemics.co.uk



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

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]