Gary L Peskin
If you nest your toc_num_list
directly within the containing topic, you'd be able to use
level=multiple easily. This way, toc_num_list would directly be the
child of the containing topic (if any) rather than the child of the
containing toc_num_list. For example, your first toc would look like:
<toc>
<title>
MAIN COMPONENT
</title>
<break/>
<toc_num_list>
<topic toc1="#contactor">
DC CONTACTOR, 41A296327AM
<toc_num_list>
<topic toc1="#func_desc">
Functional Description
</topic>
<topic toc1="#clean">
Cleaning
</topic>
<topic toc1="#lube">
Lubrication
</topic>
<topic toc1="#inspect">
Inspection
</topic>
</toc_num_list>
</topic>
</toc_num_list>
</toc>
Then, you'd have to change your "topic" template to add this
apply-templates after the </a>:
<xsl:apply-templates select="toc_num_list"/> Also, you'd have to change your value-of select from "." to "text()".
This is due to the fact that the count attribute of the xsl:number
element is used for two purposes: (1) to select the node of interest in
the ancestor-or-self tree that is built and (2) to select the siblings
of each node in the tree. In your case, you want to build the
ancestor-or-self tree but only count "topic" siblings of either topic
nodes or toc_num_list nodes. I don't think <xsl:number> can do that.
However, to answer your original question, if you're unable to change
the input XML, the following ugly recursive template should work:
Replace your loc_num_list template with:
<xsl:template match="toc_num_list">
<xsl:param name="lvl" />
<!-- lvl is levels above this one -->
<xsl:variable name="sibs" select="count(preceding-sibling::topic)" />
<xsl:variable name="newparm">
<!-- lvl of preceding topic -->
<xsl:choose>
<xsl:when test="$sibs = 0" />
<xsl:when test="$lvl">
<xsl:value-of select="concat($lvl, '.', $sibs)"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$sibs"/>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<ol>
<xsl:apply-templates>
<xsl:with-param name="lvl" select="$newparm"/>
</xsl:apply-templates>
</ol>
</xsl:template>
And make your topic template look like this:
<xsl:template match="topic">
<xsl:param name="lvl" />
<xsl:choose>
<xsl:when test="$lvl">
<xsl:value-of select="concat($lvl, '.', count(. |
preceding-sibling::topic))" />
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="count(. | preceding-sibling::topic)" />
</xsl:otherwise>
</xsl:choose>
<a>
<xsl:attribute name="href"><xsl:value-of
select="@toc1"/></xsl:attribute>
<xsl:value-of select="."/>
</a>
<br/>
</xsl:template>
Jeni Tennison approaches it without modifying the original XML instance.
>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 :)
|