xsl:number Recomendation Number 2 (sequence-name Attribute)

I think it would be a useful enhancement if there were a way to generate sequential numbers using the xsl:number construct.  In my application I am applying a template to all the PCDATA that searches it for any characters matching an input parameter SearchText.  If there is a match the template should highlight the text that matches (it's an html stylesheet) and also create an anchor tag within the html output.  I implemented it as follows:

<!--
    template for how to present any element that we
    allow searches on... Number, Quantity, Description, Date
 -->
<xsl:template name="SearchElement">
   <xsl:variable name="LowerDot">
      <xsl:value-of select="translate(.
                                    , 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
                                    , 'abcdefghijklmnopqrstuvwxyz')"/>
   </xsl:variable>

   <xsl:choose>
   <xsl:when test="contains($LowerDot
                          , $SearchText) ='true'
                   and $SearchText != ''
                   and $LowerDot != ''">
      <xsl:variable name="StartPosition">
         <xsl:value-of select="string-length(substring-before($LowerDot
                                                      , $SearchText)) + 1"/>
      </xsl:variable>

      <xsl:variable name="AfterLen">
         <xsl:value-of select="string-length(substring-after($LowerDot
                                                     , $SearchText))"/>
      </xsl:variable>

      <xsl:value-of select="substring(., 0, $StartPosition)"/>
      <A name="M{$AnchorNumber}">
      <B style="color:#000000;background-color:#ffff66">
      <xsl:value-of select="substring(., $StartPosition
                                    , string-length($SearchText))"/>
      </B>
      </A>
      <xsl:value-of select="substring(.
                                    , $StartPosition + string-length($SearchText)
                                    , $AfterLen)"/>
   </xsl:when>
   <xsl:otherwise>
      <xsl:value-of select="."/>
   </xsl:otherwise>
   </xsl:choose>

</xsl:template>

So, if SearchText was 'er' the following PCDATA:

<Description>ABC1234,ERASER,DRY ERS,WHTBRD</Description>

would be output as:

...
ABC1234,<A name="M1"><B style="color:#000000;background-color:#ffff66">ER</B></A>ASER,DRY ERS,WHTBRD
...

And subsequent matches could be M2, M3 and so on.  Unfortunately, this is how I had to implement the calls to the SearchElement template...

<!--
    template for how to present Money
 -->
<xsl:template match="Money">
   <xsl:choose>
   <!-- the IMPLIED value of currency is USD -->
   <xsl:when test="@currency='USD' or not(@currency)">
      <xsl:text>$ </xsl:text>
   </xsl:when>
   <xsl:otherwise>
      <xsl:value-of select="@currency"/>
   </xsl:otherwise>
   </xsl:choose>
   <xsl:call-template name="SearchElement">
      <xsl:with-param name="AnchorNumber">
         <xsl:number level="any" count="*" grouping-separator=" " grouping-size="10000"/>
      </xsl:with-param>
   </xsl:call-template>
</xsl:template>

<!--
    template for how to present any element that we
    allow searches on... Number, Quantity, Description, Date
 -->
<xsl:template match="*">
   <xsl:call-template name="SearchElement">
      <xsl:with-param name="AnchorNumber">
         <xsl:number level="any" count="*" grouping-separator=" " grouping-size="10000"/>
      </xsl:with-param>
   </xsl:call-template>
</xsl:template>

I had to use <xsl:number level="any" count="*" ...> to get unique values for my AnchorNumber parameter.  And, I had to replicate this in both places from which I called SearchElement.

While this does give unique values for AnchorNumber they typically look like M37, M99, M116, M117, M176 etc...  In our application it would be cleaner if we could generate the html and then set the frame for online viewing of the xml doc to {url-of html-output}#M1 and then the < > buttons in our navigation frame could switch it to #M2, #M3 etc...  Instead we must build a list from the resulting html output.

So what I am proposing is that in the SearchElement template we generate the numbers within the xsl:choose when we decide to mark something up as follows:

   <xsl:choose>
   <xsl:when test="contains($LowerDot
                          , $SearchText) ='true'
                   and $SearchText != ''
                   and $LowerDot != ''">
      <xsl:variable name="StartPosition">
         <xsl:value-of select="string-length(substring-before($LowerDot
                                                      , $SearchText)) + 1"/>
      </xsl:variable>

      <xsl:variable name="AfterLen">
         <xsl:value-of select="string-length(substring-after($LowerDot
                                                     , $SearchText))"/>
      </xsl:variable>

      <xsl:value-of select="substring(., 0, $StartPosition)"/>
      <xsl:variable name="AnchorNumber>
         <xsl:number sequence-name="foo" value="++foo"/>
      </xsl:variable>

      <A name="M{$AnchorNumber}">
      <B style="color:#000000;background-color:#ffff66">
      <xsl:value-of select="substring(., $StartPosition
                                    , string-length($SearchText))"/>
      </B>
      </A>
      <xsl:value-of select="substring(.
                                    , $StartPosition + string-length($SearchText)
                                    , $AfterLen)"/>
   </xsl:when>
   <xsl:otherwise>
      <xsl:value-of select="."/>
   </xsl:otherwise>
   </xsl:choose>

Of course it might work well if sequence-name="foo" value="+1" were allowed.  It would be my expectation that the foo sequence-name would only have scope within the SearchElement template so you would have to output it with an xsl:with-param construct if you needed to do so.

Since the logic for numbering the anchors is in the SearchElement template where you do the matching you the simplify the call as:

   <xsl:call-template name="SearchElement"/>

As before, I hope this was helpful (and not too long) and please let me know what you think.

- Mike

Received on Sunday, 25 June 2000 21:02:52 UTC