Making Cross-References


If you read W3C working drafts and recommendations online, you'll notice that they are very heavily hyperlinked. Terms with special meanings are linked to their definitions; cross-references from one section of the specification to another are represented by hyperlinks ; references to other documents are represented first by a link to the bibliography, and then from the bibliography to the external document on the Web if it is available; there are references from a document to previous versions of the document, and so on. In the XML specification, every use of a grammar symbol such as «elementdec1 » is linked to the grammar rule where it is defined. Similarly, in the XSLT specification, every use of an XSLT element name such as «xsl:sequence » is linked to its definition. In this section, we will look at the rules that are used to create these links. There are many of these, and I'll pick a selection that illustrates the techniques used.

Let's take the linking of term references to term definitions. Here is an example of a term definition from the XML specification that defines one term and contains two references to terms defined elsewhere in the specification:

  <p><termdef id="dt-xml-doc" term="XML Document">A data object is an   <term>XML document</term> if it is   <termref def="dt-wellformed">well-formed</termref>, as defined in this   specification. A well-formed XML document may in addition be   <termref def="dt-valid">valid</termref> if it meets certain further   constraints.</termdef></p>  

(A curious definition, because having said that all XML documents are "well-formed", it seems rather odd to use the phrase well-formed XML document in the very next sentence , as if there were any other kind. But we are not here to criticize the prose .)

The <termdef> element identifies this as a term definition. The «id » attribute identifies this term definition uniquely within the document. The «term » attribute is the term being defined. This is also tagged using the <term> element where it appears in the text. This might appear redundant, but the DTD requires it. There are some cases where the two differ , for example the XML specification states:

  <p><termdef id="dt-root" term="Root Element">There is exactly one   element, called the <term>root</term>, or document element, no part of   which appears in the <termref def="dt-content">content</termref> of   any other element.</termdef>  

I'm afraid I've never been sure as to whether the term being defined here is root or root element.

The <termref> element has a «def » attribute that must match the «id » attribute of some <termdef> . You find that confusing? Well so do I.

The template rule for the <termdef> marks the definition as such, and generates an HTML anchor, like this:

  <!-- termdef: sentence or phrase defining a term -->   <xsl:template match="termdef">   <xsl:text>[</xsl:text>   <a name="{@id}" id="{@id}" title="{@term}">   <xsl:text>Definition</xsl:text>   </a>   <xsl:text>: </xsl:text>   <xsl:apply-templates/>   <xsl:text>]</xsl:text>   </xsl:template>  

The «id » and «name » attributes of the HTML <a> element are both used (by various browsers) to identify the element, and the «title » attribute identifies its role: it is not used by a conventional browser, but may be used, for example, by audio browsers.

The corresponding rule for the <termref> element generates a link to this anchor:

  <!-- termref: reference to a defined term -->   <xsl:template match="termref">   <a title="{key('ids', @def)/@term}">   <xsl:attribute name="href">   <xsl:call-template name="href.target">   <xsl:with-param name="target" select="key('ids', @def)"/>   </xsl:call-template>   </xsl:attribute>   <xsl:apply-templates/>   </a>   </xsl:template>  

This calls the named template «href.target » to produce the content of the «href » attribute. We've already seen this named template on page 663. Note the use of the key() function to enable quick access to the target of the link: scanning the whole document, by using an expression such as «//termdef[@id=current()/@def] » , would be hopelessly slow.

The other rules that generate internal links are all very similar to this pair.

The links to a section are a little more complex because they require the section number to be computed. In the source XML document, the links look like this:

  <p>Full definitions of the specific characters in each class   are given in <specref ref="CharClasses"/>.</p>  

Here, «CharClasses » must match the «id » attribute of an element such as <div1> , <div2> , or <div3> . In fact, quite a range of different elements can act as the target of a <specref> , for example, an <issue> or a <vcnote> . The form of the link depends on the type of target, so the template rule contains a big <xsl:choose> instruction that handles all the possibilities. It reads as follows :

  <xsl:template match="specref">   <xsl:variable name="target" select="key('ids', @ref)[1]"/>   <xsl:choose>   <xsl:when test="local-name($target)='issue'">   <xsl:text>[</xsl:text>   <a>   <xsl:attribute name="href">   <xsl:call-template name="href.target">   <xsl:with-param name="target" select="key('ids', @ref)"/>   </xsl:call-template>   </xsl:attribute>   <b>   <xsl:text>Issue </xsl:text>   <xsl:apply-templates select="key('ids', @ref)" mode="number"/>   <xsl:text>: </xsl:text>   <xsl:for-each select="key('ids', @ref)/head">   <xsl:apply-templates/>   </xsl:for-each>   </b>   </a>   <xsl:text>]</xsl:text>   </xsl:when>  

This code handles the case where the target is an <issue> (used to flag a known problem in a working draft). The «href » attribute is generated using the named template «href.target » , as before. The text of the hyperlink contains the issue number and the title (the <head> element) of the issue.

The next section handles <div1> , <div2> , and so on. In this case, the text of the link contains the section number and section heading of the target section. Note the use of <xsl:apply-templates> here, to process an element in a particular mode, to get first the section number and then the section title (from its <head> element):

  <xsl:when test="starts-with(local-name($target), 'div')">   <a>   <xsl:attribute name="href">   <xsl:call-template name="href.target">   <xsl:with-param name="target" select="key('ids', @ref)"/>   </xsl:call-template>   </xsl:attribute>   <b>   <xsl:apply-templates select="key('ids', @ref)" mode="divnum"/>   <xsl:apply-templates select="key('ids', @ref)/head" mode="text"/>   </b>   </a>   </xsl:when>  

Appendices are handled using identical code as normal sections, and the code could have easily been made common by using an «or » in the <xsl:when> condition:

  <xsl:when test="starts-with(local-name($target), 'inform-div')">   <a>   <xsl:attribute name="href">   <xsl:call-template name="href.target">   <xsl:with-param name="target" select="key('ids', @ref)"/>   </xsl:call-template>   </xsl:attribute>   <b>   <xsl:apply-templates select="key('ids', @ref)" mode="divnum"/>   <xsl:apply-templates select="key('ids', @ref)/head" mode="text"/>   </b>   </a>   </xsl:when>  

Code follows for several more possible target elements, including <vcnote> , <prod> , and <label> , but it's almost identical in each case.

Now, we see something unusual: the template rule does some checking to ensure that the target element is one of the types of element that a <specref> can point to. Generally, this stylesheet does not do much validation of this kind, and it would probably be a good thing if it did more. Many errors in source documents, if they pass the checks performed by the DTD, are detected only because the HTML that's generated turns out to be invalid.

  <xsl:otherwise> <xsl:message>   <xsl:text>Unsupported specref to </xsl:text>   <xsl:value-of select="local-name($target)"/>   <xsl:text> [</xsl:text>   <xsl:value-of select="@ref"/>   <xsl:text>] </xsl:text>   <xsl:text> (Contact stylesheet maintainer).</xsl:text>   </xsl:message>   <b>   <a>   <xsl:attribute name="href">   <xsl:call-template name="href.target">   <xsl:with-param name="target" select="key('ids', @ref)"/>   </xsl:call-template>   </xsl:attribute>   <xsl:text>???</xsl:text>   </a>   </b>   </xsl:otherwise>   </xsl:choose>   </xsl:template>  

I'm not very fond of tests like «local-name($target) = 'issue' » or «name($target) = 'issue' » . I think it's better to write «test="$target[self::issue ]" », because neither of the first two tests properly takes the namespace into account. The fact that both the local-name() and name() tests are incorrect (both allow expanded QNames other than the one that the user was looking for) makes them difficult to optimize. Some systems index elements by name, and it might be impossible to use the index because the condition is capable of matching names in other namespaces.




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