Re: using xsl:number

Subject: Re: using xsl:number
From: Jeni Tennison <mail@xxxxxxxxxxxxxxxx>
Date: Fri, 25 Aug 2000 10:57:03 +0100
Dhruv,

>          I am trying to format my table of contents using xsl:number.
>however the concept is not very clear to me and hence i am unable to get the
>output i want. I hope somebody will be able to help me with this.

Thank you for asking this question - it's given me a good opportunity to
play around with xsl:number.

You can do this with just xsl:number, but you need to understand what it's
doing to know how.  In your first cut at it, you specified:

<xsl:number format="1.1.1"
            level="multiple"
            from="pub/toc/toc_num_list"/>

As you haven't specified a 'count' attribute, the XSLT processor will
assume that you are after 'topic' elements (because that's the kind of node
that you're looking at).  For each topic element that you're look at, it
constructs a list of all the ancestor elements for that topic.  So let's
use 'Installation' as an example:

<toc>
  <title>SUB-COMPONENTS</title>
  <break/>
  <toc_num_list>
    <topic toc1="#arcchute">ARC CHUTE</topic>
    <toc_num_list>
      <topic toc1="#arc_insp">Inspection</topic>
      <topic toc1="#arc_maint">Routine Maintenance</topic>
      <toc_num_list>
        <topic toc1="#arc_rem">Removal</topic>
        <topic toc1="#arc_inst">Installation</topic>
        <break/>
      </toc_num_list>
    </toc_num_list>
    ...
  </toc_num_list>
</toc>

The ancestor hierarchy for 'Installation' looks like:

<toc>
  <toc_num_list>
    <toc_num_list>
      <toc_num_list>
        <topic toc1="#arc_inst">Installation</topic>
      </toc_num_list>
    </toc_num_list>
  </toc_num_list>
</toc>

The XSLT processor looks for 'topic' elements within that hierarchy, and
the only 'topic' element it can find is the Installation topic itself.
That's why you only get single numbers.

The XSLT processor needs to understand the hierarchical structure of your
XML in order to number it properly.  You can tell it a little about the
hierarchical structure of your document by saying that 'toc_num_list'
elements are involved in that hierarchy, and that it should count them as
well.  You tell it that by setting the 'count' attribute on xsl:number to
include 'toc_num_list' elements as well as 'topic' elements:

<xsl:number format="1.1.1"
            level="multiple"
            from="pub/toc/toc_num_list"
            count="topic | toc_num_list" />

However, this gives you the output (extract):

	1 ARC CHUTE 
			2.1 Inspection 
			2.2 Routine Maintenance 
				2.3.1 Removal 
				2.3.2 Installation 

The reason is that when the XSLT processor tries to find a number for a
toc_num_list it does so by counting all the topics (and toc_num_lists)
before it, and adding one to the result.  For example, in numbering the
toc_num_list that's the parent of 'Installation', it counts 'Inspection'=1,
'Routine Maintenance'=2, so this toc_num_list=3.

What we want to do when it comes to counting is ignore the 'topic' that
immediately precedes the 'toc_num_list'.  In other words, we're not
interested in a 'topic' element whose immediate following sibling is a
'toc_num_list' element:

<xsl:number format="1.1.1"
            level="multiple"
            from="pub/toc/toc_num_list"
            count="topic[not(following-sibling::*[1][name() =
'toc_num_list'])] 
                   | toc_num_list" />

As expected, this gives you the output (extract):

	ARC CHUTE 
			2.1 Inspection 
			2 Routine Maintenance 
				2.3.1 Removal 
				2.3.2 Installation 

It declines to number any of those 'topics' that immediately precede a
toc_num_list (as expected).  We *do* want those to be numbered, but only
when they are the current topic.  So, we have to set a variable that holds
the id of the current topic (I've used generate-id() for generality but you
could use '@toc1' instead) and select nodes that match that id as well:

<xsl:template match="topic">
  <xsl:variable name="topic-id" select="generate-id()" />
  <xsl:number format="1.1"
              level="multiple"
              from="toc/toc_num_list"
              count="topic[generate-id() = $topic-id or
                           not(following-sibling::*[1][name() = 
                                                       'toc_num_list'])] | 
                     toc_num_list/>
  <a href="{@toc1}">
    <xsl:value-of select="."/>
  </a>
 <br/>
</xsl:template>

This gives the numbering you're after in SAXON, Xalan and MSXML (July).

I've made a few other minor changes in the template above:

1. format="1.1" - you don't need to have '1.1.1' as the XSLT processor will
automatically extend the format expression to that.
2. from="toc/toc_num_list" - you don't need to specify 'pub' as being a
parent of 'toc' as all 'toc's are children of 'pub'.
3. using attribute value template to set the href attribute on the link -
it's just shorter, and you're not doing anything complex in identifying
what to link to, so there's no need to use xsl:attribute.

I hope that this helps - it's certainly helped me :)

Jeni

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



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


Current Thread