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]
Other format: [Raw text]

Re: Parameter substitution


Hi Curtis,

> Below is given a fragment from an XML document. Of note are the
> contents of the <ErrorText> nodes; the first one has a value of "%1
> %2 is not a valid date format.".
>
> My task, (you guessed it) is to replace each %1, %2 etc.. with a
> string built from the correspondong ErrorParameter (formatted nicely
> of course) . Being relatively new to XSLT, I can't think of a way to
> accomplish this in a generic manner. Has anyone got any ideas? I
> can't think of an approach that will actually work!

It's a little tedious because XPath string-handling isn't the best
(roll on regexps), but it's certainly possible. To work through the
string, you need a recursive template; it needs to take the string in
which you're replacing parameter references, and a list of parameters
that you want to replace the references with:

<xsl:template name="substituteParameters">
  <xsl:param name="string" />
  <xsl:param name="parameters" select="/.." />
  ...
</xsl:template>

You need to work through the string a bit at a time. If the string
doesn't contain any % characters, then you know that you can just
output the string with no changes, so that's the basic test on which
the template hinges:

<xsl:template name="substituteParameters">
  <xsl:param name="string" />
  <xsl:param name="parameters" select="/.." />
  <xsl:choose>
    <xsl:when test="contains($string, '%')">
      ...
    </xsl:when>
    <xsl:otherwise>
      <xsl:value-of select="." />
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

Now, given that you have a string with a % in it, you want to output:

 - the string up to the %
 - the parameter in the position indicated by the number after the %
 - the result of calling this same template on the rest of the string
   (after the number)

The first bit's easy - you can use the substring-before() function to
get the bit before the %:

<xsl:template name="substituteParameters">
  <xsl:param name="string" />
  <xsl:param name="parameters" select="/.." />
  <xsl:choose>
    <xsl:when test="contains($string, '%')">
      <xsl:value-of select="substring-before($string, '%')" />
      ...
    </xsl:when>
    <xsl:otherwise>
      <xsl:value-of select="." />
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

The second bit's a little more complicated - you need to get the
number after the %. If you can guarantee that there aren't going to be
more than 9 parameters, then you only need to get the first character
after the %, so I'll assume that's the case (let me know if it's not).
So you can use the following to get the first character after the %:

  substring(substring-after($string, '%'), 1, 1)

That gives you (a string representation of) a number, which you can
use to index into the parameters passed as the value of the
$parameters parameter:

  $parameters[position() =
              substring(substring-after($string, '%'), 1, 1)]

or:

  $parameters[number(substring(substring-after($string, '%'), 1, 1))]

I'd apply templates to this parameter - you can have a template
matching ErrorParameter elements that does the relevant pretty
formatting:

<xsl:template name="substituteParameters">
  <xsl:param name="string" />
  <xsl:param name="parameters" select="/.." />
  <xsl:choose>
    <xsl:when test="contains($string, '%')">
      <xsl:value-of select="substring-before($string, '%')" />
      <xsl:apply-templates
        select="$parameters[number(substring(
                                     substring-after($string, '%'),
                                     1, 1))]" />
        ...
    </xsl:when>
    <xsl:otherwise>
      <xsl:value-of select="." />
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

Finally, the recursive call to the template needs to pass in the same
set of parameters whilst amending the string to being the 'rest' of
the substring after the '%':

<xsl:template name="substituteParameters">
  <xsl:param name="string" />
  <xsl:param name="parameters" select="/.." />
  <xsl:choose>
    <xsl:when test="contains($string, '%')">
      <xsl:value-of select="substring-before($string, '%')" />
      <xsl:apply-templates
        select="$parameters[number(substring(
                                     substring-after($string, '%'),
                                     1, 1))]" />
      <xsl:call-template name="substituteParameters">
        <xsl:with-param name="string"
          select="substring(substring-after($string, '%'), 2)" />
        <xsl:with-param name="parameters" select="$parameters" />
      </xsl:call-template>
    </xsl:when>
    <xsl:otherwise>
      <xsl:value-of select="." />
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

To call it, you'd do something like the following (assuming that
SynchError is the current node):

  <xsl:call-template name="substituteParameters">
    <xsl:with-param name="string" select="ErrorText" />
    <xsl:with-param name="parameters" select="ErrorParameter" />
  </xsl:call-template>

Having said all of that, you would be a lot better off if you could
change the XML structure so that you used elements to indicate where
the parameters should be inserted. Something like:

  <ErrorText>
    <Insert param="1" /> <Insert param="2" /> is not a valid date
    format.
  </ErrorText>

That way, you could just apply templates to the content of the
ErrorText element and use templates matching Insert elements to insert
the value of the relevant parameter.

I hope that helps,

Jeni

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


 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]