Creating the Table of Contents


Immediately after the header, the first part of the body of the document is the table of contents. This is generated from within the template rule for the <body> element, and it is controlled by a parameter $toc.level that defines the number of levels in the table of contents: for example, if this is set to «2 » , then first- and second-level headings will be listed. HTML cannot produce page numbers , so the table of contents instead contains hyperlinks to the headings of the actual sections.

  <xsl:template match="body">   <xsl:if test="$toc.level &gt; 0">   <div class="toc">   <xsl:text>&#10;</xsl:text>   <h2>   <xsl:call-template name="anchor">   <xsl:with-param name="conditional" select="0"/>   <xsl:with-param name="default.id" select="'contents'"/>   </xsl:call-template>   <xsl:text>Table of Contents</xsl:text>   </h2>   <p class="toc">   <xsl:apply-templates select="div1" mode="toc"/>   </p>   <xsl:if test="../back">   <xsl:text>&#10;</xsl:text>   <h3>   <xsl:call-template name="anchor">   <xsl:with-param name="conditional" select="0"/>   <xsl:with-param name="default.id" select="'appendices'"/>   </xsl:call-template>   <xsl:text>Appendi</xsl:text>   <xsl:choose>   <xsl:when test="count(../back/div1  ../back/inform-div1) > 1">   <xsl:text>ces</xsl:text>   </xsl:when>   <xsl:otherwise>   <xsl:text>x</xsl:text>   </xsl:otherwise>   </xsl:choose>   </h3>   <p class="toc">   <xsl:apply-templates mode="toc"   select="../back/div1  ../back/inform-div1"/>   <xsl:call-template name="autogenerated-appendices-toc"/>   </p>   </xsl:if>   <xsl:if test="//footnote[not(ancestor::table)]">   <p class="toc">   <a href="#endnotes">   <xsl:text>End Notes</xsl:text>   </a>   </p>   </xsl:if>   </div>   <hr/>   </xsl:if>   <div class="body">   <xsl:apply-templates/>   </div>   </xsl:template>  

The <body> template rule generates the table of contents, and then it uses <xsl:apply-templates> to process its own children. The table of contents is produced by applying templates to all the top-level ( <div1> ) sections and appendices in a special mode «toc » , and by generating the headings «Table of Contents » , «Appendix » or «Appendices » , and «End Notes » , as required. There is also provision for referencing appendices that are automatically generated by the stylesheet, for example a glossary or index of error codes.

Let's see how the table of contents is produced. The structure of the <body> element, and also of <back> , consists of a sequence of <div1> elements representing top-level sections, like this.

  <div1>   <head>First-level heading</head>   <p>Some text</p>   <div2>   <head>Second-level heading</head>   <p>Some more text</p>   <div3>   <head>Third-level heading</head>   <p>Lots more text</p>   </div3>   </div2>   </div1>  

Each <div1> element contains a <head> element giving its section title, paragraphs of immediate content, and zero or more <div2> elements containing level-2 subsections. The <div2> elements similarly contain a <head> and zero or more <div3> elements for level-3 subsections, and so on.

In the <back> section, a non-normative appendix is represented by an <inform-div1> element instead of the usual <div1> , but otherwise the structure is the same.

Non-normative is jargon meaning "for information only, not officially part of the specification."

The template rule that generates an entry for a top-level section in the table of contents looks like this:

  <!-- mode: toc -->   <xsl:template mode="toc" match="div1">   <xsl:apply-templates select="." mode="divnum"/>   <a>   <xsl:attribute name="href">   <xsl:call-template name="href.target">   <xsl:with-param name="target" select="."/>   </xsl:call-template>   </xsl:attribute>   <xsl:apply-templates select="head" mode="text"/>   </a>   <br/>   <xsl:text>&#10;</xsl:text>   <xsl:if test="$toc.level &gt; 1">   <xsl:apply-templates select="div2" mode="toc"/>   </xsl:if>   </xsl:template>  

This starts by applying templates to itself with mode divnum : this invokes a template to calculate the section number. We'll take a look at this template rule shortly. The rule then generates an HTML <a> element to produce a hyperlink. The href attribute is generated by calling a named template href.target , with the current node (the <div1> element) as a parameter. The content of the <a> element (the displayed text that the user clicks on) is produced by applying templates to the <head> element, with the special mode text . The stylesheet doesn't actually contain a template rule for this mode; it is used simply to invoke the built-in template rule, which returns the textual content of the <head> element, minus any markup.

The «href.target » template looks like this:

  <xsl:template name="href.target">   <xsl:param name="target" select="."/>   <xsl:text>#</xsl:text>   <xsl:choose>   <xsl:when test="$target/@id">   <xsl:value-of select="$target/@id"/>   </xsl:when>   <xsl:otherwise>   <xsl:value-of select="generate-id($target)"/>   </xsl:otherwise>   </xsl :choose>   </xsl:template>  

To my mind this is crying out to be replaced by an XSLT 2.0 function, something like:

  <xsl:function name="f:href.target" as="xs:string">   <xsl:param name="target" as="node()"/>   <xsl:sequence select="concat('#', ($target/@id, generate-id($target))[1]"/>   </xsl:function>  

This would allow the code:

  <a>   <xsl:attribute name="href">   <xsl:call-template name="href.target">   <xsl:with-param name="target" select="."/>   </xsl:call-template>   </xsl:attribute>   <xsl:apply-templates select="head" mode="text"/>   </a>  

to be rewritten as:

  <a href="{f:href.target(.)}">   <xsl:apply-templates select="head" mode="text"/>   </a>  

If the global parameter $toc.level is greater than one, then the <div2> elements that are children of this <div1> are processed in mode «toc » to generate another level in the table of contents.

The template rule for <div2> elements in mode «toc » and the template rules for further levels such as <div3> , are very similar to the <div1> rule. They differ only in that they apply templates to the next level down (the <div2> template processes the <div3> children, and so on) and in the amount of indentation added before the section number-this is added crudely in the form of four nonbreaking spaces per level, using the instruction:

  <xsl:text>&#xa0;&#xa0;&#xa0;&#xa0;</xsl:text>  

Hexadecimal «a0 » (decimal 160) is the Unicode code for the nonbreaking space character, better known to HTML authors as the entity reference «&nbsp; » . This is not available as a built-in entity in XML. It is possible to define it as an entity in the DTD, but the authors of this stylesheet chose to write it explicitly as a numeric character reference.

It wouldn't be difficult to write a parameterized template that handled all the < div N > elements in one rule, but the alternative approach of writing five separate rules is perfectly defensible.

The templates to calculate section numbers have one variant for each level of heading, and also vary depending on whether the section is in the <body> (a main section) or in the <back> matter (an appendix). Here are some of them:

  <!-- mode: divnum -->   <xsl:template mode="divnum" match="div1">   <xsl:number format="1 "/>   </xsl:template>   <xsl:template mode="divnum" match="back/div1  inform-div1">   <xsl:number count="div1  inform-div1" format="A "/>   </xsl:template>   <xsl:template mode="divnum" match="div2">   <xsl:number level="multiple" count="div1  div2" format="1.1 "/>   </xsl:template>   <xsl:template mode="divnum" match="back//div2">   <xsl:number level="multiple" count="div1  div2  inform-div1"   format="A.1 "/>   </xsl:template>   <xsl:template mode="divnum" match="div3">   <xsl:number level="multiple" count="div1  div2  div3"   format="1.1.1 "/>   </xsl:template>   <xsl:template mode="divnum" match="back//div3">   <xsl:number level="multiple"   count="div1  div2  div3  inform-div1" format="A.1.1 "/>   </xsl:template>  

All these templates work by calling <xsl:number> with appropriate parameters. The default «level="single" » is used for the top-level headings, and «level="multiple" » for all other levels, with a «count » attribute that matches that level and all ancestor levels. The format of the numbering is adjusted for appendices (sections with <back> as an ancestor) to use alphabetic identifiers (A, B, C,...) for the first component of the number.

Giving a list of alternatives in the count attribute is a common way of doing multilevel numbering. It means, in effect, outputting a sequence number for each ancestor element that is either an <inform-div1> , or a <div1> , or a <div2> , and so on. Like most template rules in a rule-based stylesheet, it doesn't attempt to do any validation: if the input structure is wrong, it will produce some sort of output nevertheless, and it's up to the document author to work out what the problem is. This raises an interesting question that you need to consider when designing your own stylesheets: is it the job of the stylesheet to detect and report on errors in the source document?

The use of <inform-div1> as a separate tag for non-normative appendices was a pretty clumsy design decision, and the stylesheet author has to pay the price here. It would have been much cleaner to give the <div1> element an attribute «normative="no" » . Sadly, it is often the case that stylesheet authors have to cope with XML structures that could have been designed better. In XSLT 2.0, if this stylesheet were schema aware, it's likely that <inform-div1> would be in the substitution group of <div1> , and it would then be possible to replace all references to «div1 » in these template rules by «schema-element(div1) » , which would pick up the <inform-div1> elements automatically.

It would again be possible to make these template rules generic across levels. In fact, the template rule shown above for <div3> elements would produce exactly the right output if it were applied to a <div1> or <div2> element, because of the way that <xsl:number> is defined. The «format » attribute of <xsl:number> can also be parameterized using an attribute value template: in XSLT 2.0 one could write:

  <xsl:template mode="divnum" match="div3">   <xsl:number level="multiple"   count="div1  div2  div3  inform-div1"   format="{if (ancestor::back) then 'A.1.1' else '1.1.1'}"/>   </xsl:template>  

However, it does no harm for the stylesheet author to spell things out more explicitly.

The templates for producing section numbers in the table of contents are reused, of course, when producing the section numbers in the body of the document. I'll describe how this is done, in the next section.




XSLT 2.0 Programmer's Reference
NetBeansв„ў IDE Field Guide: Developing Desktop, Web, Enterprise, and Mobile Applications (2nd Edition)
ISBN: 764569090
EAN: 2147483647
Year: 2003
Pages: 324

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net