You can include the same content in more than one place in a document as long as it is valid. One thing that can make a document invalid is duplicate ID names. If any duplicated content has an ID attribute on any of the elements in it, then you will have duplicate ID names and your document will not be valid. One drastic solution is to eliminate all ID values from modular doc. But that prevents you from forming cross references to that content. If you use IDs to form HTML filenames in your output, then you will not have that feature for such modules and they will have generated filenames.
If you absolutely must have duplicated content with IDs, then you have to figure out how to get different ID values. The following is one example of how XInclude can give you a different ID value if you are using xsltproc.
Let's say your modular file has a single section you want to include twice, but it has an ID value.
<?xml version="1.0"?> <!DOCTYPE section SYSTEM "docbook.dtd"> <section id="original-id"> <para> blah blah </para> </section>
Form your first XInclude normally:
<xi:include href="module.xml" xmlns:xi="http://www.w3.org/2001/XInclude"/>
This pulls in the entire <section id="original-id">
element, including its children.
Put your second XInclude inside its own section
element with a different ID value. And avoid the original ID value by selecting the children of the section
element with an XPointer expression. The following is the complete example with both includes.
<book> <chapter> blah blah <xi:include href="module.xml" xmlns:xi="http://www.w3.org/2001/XInclude"/> </chapter> <appendix> blah blah <section id="appendix-id"> <xi:include href="module.xml" xpointer="xpointer(/section/node())" xmlns:xi="http://www.w3.org/2001/XInclude"/> </section> </appendix> </book>
When processed with xsltproc, this example results in the second instance being <section id="appendix-id">
, containing the same set of child elements, comments, and text nodes, and processing instructions (all are selected by the node()
syntax). Since the new section
wrapper has a different ID value, it will validate. You can even form a cross reference to either of the instances of the section. Just make sure none of the children have ID values or it will still not validate. Only xsltproc supports the xpointer()
scheme used in this example. See this Note for background on xpointer()
.
Sometimes you need to reuse content at a different level from its original source. For example, a chapter
in one book might need to be reused as a section
in another book. You can reuse content with XInclude, but you cannot always place the same element in a new location. For example, if you are reusing a chapter as a section, you cannot just XInclude a chapter
element inside another chapter
, as it will not be valid and will not format properly.
The XInclude xpointer()
scheme described in the previous section can be used for this purpose. The idea is to create a new container element, and then XInclude all the content of another element into it, without bringing along the other element itself. The section
element in the following example does that.
<chapter id="container-chap">
...
<section id="chapter-as-section">
<xi:include href="userguide.xml"
xpointer="xpointer(//chapter[@id = 'intro']/node() )"
xmlns:xi="http://www.w3.org/2001/XInclude"/>
</section>
...
</chapter>
The path //chapter[@id = 'intro']/node()
finds the chapter in userguide.xml
whose id
attribute is intro
, and selects all the content of the element. The node()
syntax in the path selects child elements, text nodes, processing instructions, and comments.
The content to be included must fit into its new location. You may have to alter the included chapter a bit to do this. For example, if the title is in a chapterinfo
element, then the XSL templates processing the new section
will not look for the title there.
Only xsltproc supports the xpointer()
scheme used in this example. See this Note for background on xpointer()
Another approach to the problem of duplicate id
values on repeated XInclude elements is to modify the id
values in the output. You can modify the template named object.id
, which is used to assign id
values for all output elements. It usually just copies an input element's id
value, or generates a string if it does not have an id
attribute. But you can customize the template to solve this problem.
This solution does not modify the id
attributes in the source file, so the file will still be invalid if it has duplicate ids. But if you do not need to validate your resolved files, then this solution will at least let you build output and have functioning tables of contents.
The object.id
template is used to generate the output link id references for any element as well
as the id
attribute itself. As long as it produces consistent output for
the same element, your links should work.
In this customization, the template counts the number of preceding elements with the
same id value. If the count is greater than zero, then it appends the count to
the output id
value. The customization also works with DocBook 5 documents that use xml:id
.
<xsl:template name="object.id"> <xsl:param name="object" select="."/> <xsl:variable name="id" select="@id"/> <xsl:variable name="xid" select="@xml:id"/> <xsl:variable name="preceding.id" select="count(preceding::*[@id = $id])"/> <xsl:variable name="preceding.xid" select="count(preceding::*[@xml:id = $xid])"/> <xsl:choose> <xsl:when test="$object/@id and $preceding.id != 0"> <xsl:value-of select="concat($object/@id, $preceding.id)"/> </xsl:when> <xsl:when test="$object/@id"> <xsl:value-of select="$object/@id"/> </xsl:when> <xsl:when test="$object/@xml:id and $preceding.xid != 0"> <xsl:value-of select="concat($object/@id, $preceding.xid)"/> </xsl:when> <xsl:when test="$object/@xml:id"> <xsl:value-of select="$object/@xml:id"/> </xsl:when> <xsl:otherwise> <xsl:value-of select="generate-id($object)"/> </xsl:otherwise> </xsl:choose> </xsl:template>
With this template in place, when the id
of a repeated element is usage
, for example, then the first instance in the output will be usage
. But the second instance will instead be usage1
, the third instance usage2
, etc. This process ensures that each output id is unique.
Note that any instances of xref
or link
or olink
that reference the original id
value will not be altered. They will always point to the first instance in the output.
DocBook XSL: The Complete Guide - 4th Edition | PDF version available | Copyright © 2002-2007 Sagehill Enterprises |