OASIS Mailing List ArchivesView the OASIS mailing list archive below
or browse/search using MarkMail.

 


Help: OASIS Mailing Lists Help | MarkMail Help

docbook-tc message

[Date Prev] | [Thread Prev] | [Thread Next] | [Date Next] -- [Date Index] | [Thread Index] | [List Home]


Subject: Re: [docbook-tc] Very rough proposal for DocBook transclusions


Jirka Kosek wrote:

> Thanks for very useful insight. I will try to digest all input in next
> draft.

Hi folks,

please find attached next draft of tranclusion proposal. I tried to
address all issues that have arisen during call and in email
communications. Instead of providing some formal definition I have
focused on providing examples -- I think that from them it is easier to
see how this is supposed to work. Once we are sure about how
transclusions should behave I can prepare more formal definition
appropriate for TDG.

I think that proposal is now quite developed regarding this "duplicate
ID" issue. However as I don't have personal experience with sort of
manual solution to problem using prefixes I would especially ask Larry
if he can check whether proposal is addressing everything he had in mind.

I'm also attaching sample files and very sketchy XSLT 2.0 based
transclusion implementation if anyone is interested.

I would like to start discussion now on email, so we can develop
proposal way further before next telcon.

Thanks and have a nice day,

				Jirka

-- 
------------------------------------------------------------------
  Jirka Kosek      e-mail: jirka@kosek.cz      http://xmlguru.cz
------------------------------------------------------------------
       Professional XML consulting and training services
  DocBook customization, custom XSLT/XSL-FO document processing
------------------------------------------------------------------
 OASIS DocBook TC member, W3C Invited Expert, ISO JTC1/SC34 member
------------------------------------------------------------------
Title: Requirements for transclusion in DocBook

Requirements for transclusion in DocBook

Jirka Kosek

07/29/2010

Abstract

This document summarizes uses-cases for transclusion of content in DocBook documents. This document should help DocBook TC in deciding whether existing standards like XInclude are sufficient for the task or whether DocBook specific mechanism should be designed.

Requirements for transclusion capability are repeating over the time, the last one which was discussed is http://sourceforge.net/tracker/index.php?func=detail&aid=2820947&group_id=21935&atid=384107.


Use-cases

UC-1: Shared strings

Many technical publication use repeating boilerplate text for things like product names or product versions. It is useful if such text is defined only once per document (or set of documents) and then just referenced. This approach prevents typos and makes updates and rebranding of content very easy.

Current solutions

Entities

This use-case can be solved by using internal entities. This is well supported in processing tools and to some extent in authoring tools. However entities are somewhat hard to maintain when you use modular documents at the same time. Also entities and their references are not part of XDM so they do not survive XSLT processing which makes various document massaging tasks challenging.

Example 1. Using entities for shared strings

<!ENTITY product-version "3.14">
<!ENTITY product-name "FooWiz">
<!ENTITY corp-name "ACME Inc.">


<para>The latest version of <application>&product-name;</application>
from &corp-name; is &product-version;.</para>

XInclude

XInclude when combined with proper XPointer schema can be used for transclusion of single element or even only its text content. In this setup there can be separate file with definition of shared texts which are then reused in other documents.

Example 2. Definition file for further XInclude referencing

<article xmlns="http://docbook.org/ns/docbook" version="5.0">
  <title>Shared texts</title>
  <para><phrase xml:id="product-version">3.14</phrase></para>
  <para><phrase xml:id="product-name">FooWiz</phrase></para>
  <para><phrase xml:id="corp-name">ACME Inc.</phrase></para>
</article>

The problem is that support for different XPointer schemes varies between implementations. Almost all implementations support referencing to element specified by its ID as shown in the following example.

Example 3. Using XIncludes for shared texts

<… xmlns:xi="http://www.w3.org/2001/XInclude"><para>The latest version of <application><xi:include href="shared-texts.xml" xpointer="product-name"/></application>
    from <xi:include href="shared-texts.xml" xpointer="corp-name"/>
    is <xi:include href="shared-texts.xml" xpointer="product-version"/>.</para>

This solution has two problems. The actual reference to shared texts is very verbose because each xi:include element contains reference to the file with shared text definitions. Second problem is that such reference doesn't include only text of referenced element but whole element including xml:id attribute. This leads to excessive markup and duplicated IDs in the composed document.

Solution is to use more advanced XPointer scheme. This leads to the more arcane markup, support in tools is very bad, but there are no duplicated IDs and excessive markup.

Example 4. Using XIncludes for shared texts without duplicating source elements

<… xmlns:xi="http://www.w3.org/2001/XInclude"><para>The latest version of <application><xi:include href="shared-texts.xml" xpointer="xpath(id('product-name')/text())"/></application>
    from <xi:include href="shared-texts.xml" xpointer="xpath(id('corp-name')/text())"/>
    is <xi:include href="shared-texts.xml" xpointer="xpath(id('product-version')/text())"/>.</para>

UC-2: Shared strings in attribute values

This use-cases is similar to UC-1. The only difference now is that shared text can appear in an attribute value. This is not very common requirement for DocBook document, but because of differences between element and attribute values in XML separate use-cases is created.

Current solutions

Entities

This use-case can be solved only by using internal entities. This is well supported in processing tools and to some extent in authoring tools.

Example 5. Using entities for shared strings in attribute values

<!ENTITY product-version "3.14">
<!ENTITY product-name "FooWiz">

<section xreflabel="Installation of &product-name; &product-version;">

UC-3: Conditional shared text

Some documents use conditional content. Shared text then can be also subject to conditional processing.

Current solutions

Conditional sections

This use-case can be solved by using conditional sections which can enclose entity definitions.

Do we need example here? It is rarely used feature.

XInclude and profiling

As typical XInclude scenarios include complete elements not just their content, it is possible to define several parallel elements with different profiling attributes set, enclose them with another element and then do inclusion.

Example 6. Definition file for further XInclude referencing with conditional text

<article xmlns="http://docbook.org/ns/docbook" version="5.0">
  <title>Shared texts</title><para><phrase xml:id="product-name"><phrase os="win">Windows
        Protector</phrase><phrase os="linux">Linux
        Protector</phrase></phrase></para></article>

UC-4: Modularized documents

For large documents it is often impractical to edit them as a single large XML file. It is practical to split large document (e.g. book) into few smaller ones (e.g. chapters) and just include them into master document. This approach is also useful for reusing of boilerplate texts like legalnotices.

Current solutions

Entities

This use-case can be solved by using external entities. This is well supported in processing tools and to some extent in authoring tools. Also entities and their references are not part of XDM so they do not survive XSLT processing which makes various document massaging task challenging. External entities doesn't work very well if there are multiple levels of inclusions as entities can be declared only in the main document.

Example 7. Using entities for modularized documents

<!ENTITY chapter1 SYSTEM "chapter1.xml">
<!ENTITY chapter2 SYSTEM "chapter2.xml">

<book …>
  <info>
    <title>A Book</title>
  </info>
  
  &chapter1;
  &chapter2;

  <index/>
</book>

XInclude

XInclude works very well for this use-case.

Example 8. Using XIncludes for modularized documents

<book  xmlns:xi="http://www.w3.org/2001/XInclude">
  <info>
    <title>A Book</title>
  </info>
  
  <xi:include href="chapter1.xml"/>
  <xi:include href="chapter2.xml"/>

  <index/>
</book>

UC-5: Repeated transclusion in one master document

Some types of documentation are highly modular and composed from large number of small units. These units can be whole chapters, smaller self-standing units like section or topic, or it can be quite small chunk of content like admonition. If each such unit is used only once in document then there is no problem and we are working only with highly modular document (see UC-4). But if one unit is included more then once then we can face several problems.

If elements in units have assigned unique IDs (by using xml:id attribute) then resulting document after transclusion contains duplicate IDs. This makes file technically invalid and processing tools have difficulties in interpreting cross-references found in document.

There are different strategies which can be used to managed units and IDs which are transcluded more then once:

  • Do nothing. This of course doesn't solve problem and result of processing is unpredictable.

  • Preserve duplicated IDs only on their first occurrence (in document order). Other then first occurrences of ID are deleted. All links will then point to the first location in final composed document.

  • Make each ID unique within transcluded unit (for example by prepending unit specific prefix to each ID). In this cases targets of all cross-references have to be adjusted. Several adjustment strategies are possible – link to the first occurrence, link to the closest occurrence (e.g. find minimum subtree rooted at ancestors that contains link target).

Current solution

Unfortunately there is no satisfying solution to this use-case. DocBook XSL stylesheet can be customized to link only to the first occurrence of ID (see http://www.sagehill.net/docbookxsl/DuplicateIDs.html).

UC-6: Transclusion of foreign content

Sometimes it is necessary to include foreign content into DocBook document. Examples of such foreign content are:

  • listing of program source;

  • content in different vocabulary like DITA or TEI which has to be transformed into DocBook prior transclusion.

Current solution

Text files with programlistings can be transcluded by DocBook textdata element or using parse="text" functionality of XInclude. There is no standardized way of transclusion of different vocabularies. Currently this has to be solved on application level.

Evaluation of current technologies

It is evident that the existing technologies are not able to handle use-cases UC-5 and UC-6. All other use-cases except UC-2 can be technically solved using XInclude. However XInclude usage is quite cumbersome. It seems that DocBook specific transclusion mechanism is needed.

DocBook Transclusion Proposal

This section describes syntax, semantics and processing model of DocBook transclusion mechanism. Please be aware that this is early stage draft – everything described below might change or disappear completely.

Transclusion in document is described by ref element which references content to transclude. There are two basic types of reference – inline and external. Inline references reference content which is defined in some other place using definitions element. External reference references some external content which might or might not be written using DocBook vocabulary.

Inline reference

Inline reference is denoted by ref element with an mandatory name attribute which contains name of referenced content. This name is case-sensitive and document must contain definition of referenced content in the definitions element.

Example 9. Simple usage of inline references and defintions

<?xml version="1.0" encoding="UTF-8"?>
<article xmlns="http://docbook.org/ns/docbook">
  <info>
    <title>Transclusions demo</title>
    <definitions>
      <def name="product-version">3.14</def>
      <def name="product-name">FooWiz</def>
      <def name="corp-name">ACME Inc.</def>
    </definitions>
  </info>
  <para>The latest version of <application><ref name="product-name"/></application>
    from <ref name="corp-name"/> is <ref name="product-version"/>.</para>
</article>

Result of transclusion:

<?xml version="1.0" encoding="UTF-8"?>
<article xmlns="http://docbook.org/ns/docbook">
  <info>
    <title>Transclusions demo</title>
  </info>
  <para>The latest version of <application>FooWiz</application> from ACME Inc. is 3.14.</para>
</article>


Definition can contain arbitrary markup not just text. All child nodes of def element are transcluded by corresponding ref element.

Example 10. Inline definition with a nested markup

<?xml version="1.0" encoding="UTF-8"?>
<article xmlns="http://docbook.org/ns/docbook">
  <info>
    <title>Transclusions demo</title>
    <author><ref name="jirka"/></author>
    <definitions>
      <def name="jirka">
        <personname><firstname>Jirka</firstname><surname>Kosek</surname></personname>
      </def>
    </definitions>
  </info>
  <para>This article was written by <ref name="jirka"/> when there was 25C outside.</para>
</article>

Result of transclusion:

<?xml version="1.0" encoding="UTF-8"?>
<article xmlns="http://docbook.org/ns/docbook">
  <info>
    <title>Transclusions demo</title>
    <author>
      <personname><firstname>Jirka</firstname><surname>Kosek</surname></personname>
    </author>
  </info>
  <para>This article was written by 
        <personname><firstname>Jirka</firstname><surname>Kosek</surname></personname>
       when there was 25C outside.</para>
</article>

Definitions can be placed directly inside info element or they can be stored in a separate file which can be referenced by multiple documents.

Example 11. Definitions stored in a separate document (definitions.001.xml)

<?xml version="1.0" encoding="UTF-8"?>
<definitions xmlns="http://docbook.org/ns/docbook">
  <def name="product-version">3.14</def>
  <def name="product-name">FooWiz</def>
  <def name="corp-name">ACME Inc.</def>
</definitions>


Example 12. Usage of definitions stored in a separate document

<?xml version="1.0" encoding="UTF-8"?>
<article xmlns="http://docbook.org/ns/docbook">
  <info>
    <title>Transclusions demo</title>
    <!-- Definitions are loaded from external file -->
    <definitions definitionfile="definitions.001.xml"/>    
  </info>
  <para>The latest version of <application><ref name="product-name"/></application>
    from <ref name="corp-name"/> is <ref name="product-version"/>.</para>
</article>

Result of transclusion:

<?xml version="1.0" encoding="UTF-8"?>
<article xmlns="http://docbook.org/ns/docbook">
  <info>
    <title>Transclusions demo</title>
  </info>
  <para>The latest version of <application>FooWiz</application>
    from ACME Inc. is 3.14.</para>
</article>

Definitions from external file can be locally redefined.

Example 13. Redefinition of definition provided in an external file

<?xml version="1.0" encoding="UTF-8"?>
<article xmlns="http://docbook.org/ns/docbook">
  <info>
    <title>Transclusions demo</title>
    <definitions definitionfile="definitions.001.xml">
      <!-- Local definition will override definition from external definitions file -->
      <def name="product-version">4.01</def>
    </definitions>    
  </info>
  <para>The latest version of <application><ref name="product-name"/></application>
    from <ref name="corp-name"/> is <ref name="product-version"/>.</para>
</article>

Result of transclusion:

<?xml version="1.0" encoding="UTF-8"?>
<article xmlns="http://docbook.org/ns/docbook">
  <info>
    <title>Transclusions demo</title>
  </info>
  <para>The latest version of <application>FooWiz</application>
    from ACME Inc. is 4.01.</para>
</article>

Definitions can be conditional. All effectivity attributes can be used to set conditions on definitions.

Example 14. Conditional definitions

<?xml version="1.0" encoding="UTF-8"?>
<article xmlns="http://docbook.org/ns/docbook">
  <info>
    <title>Transclusions demo</title>
    <definitions>
      <def name="product-version">3.14</def>
      <!-- Conditional definitions -->
      <def name="product-name" os="win">Windows Protector</def>
      <def name="product-name" os="linux">Linux Protector</def>
      <def name="corp-name">ACME Inc.</def>
    </definitions>
  </info>
  <para>The latest version of <application><ref name="product-name"/></application>
    from <ref name="corp-name"/> is <ref name="product-version"/>.</para>
</article>

Result of transclusion:

<?xml version="1.0" encoding="UTF-8"?>
<article xmlns="http://docbook.org/ns/docbook">
  <info>
    <title>Transclusions demo</title>
  </info>
  <para>The latest version of <application>Linux Protector</application>
    from ACME Inc. is 3.14.</para>
</article>

Effectivity attributes can be also used on references itself.

Example 15. Conditional references

<?xml version="1.0" encoding="UTF-8"?>
<article xmlns="http://docbook.org/ns/docbook">
  <info>
    <title>Transclusions demo</title>
    <definitions>
      <def name="product-version">3.14</def>      
      <def name="product-name-win">Windows Protector</def>
      <def name="product-name-lin">Linux Protector</def>
      <def name="corp-name">ACME Inc.</def>
    </definitions>
  </info>
  <para>The latest version of
    <!-- Conditional references -->    
    <application><ref os="win" name="product-name-win"/><ref os="linux" name="product-name-lin"/></application>
    from <ref name="corp-name"/> is <ref name="product-version"/>.</para>
</article>


Definitions are valid only in subtree rooted at parent of info element containing definitions.

Example 16. Scope of defintions

<?xml version="1.0" encoding="UTF-8"?>
<book xmlns="http://docbook.org/ns/docbook">
  <info>
    <title><ref name="corp-name"/> company product guide</title>
    <!-- Multiple definitions in single document -->
    <definitions>
      <def name="corp-name">ACME Inc.</def>
    </definitions>
  </info>
  <article>
    <info>
      <title><ref name="product-name"/> Guide</title>
      <definitions>
        <def name="product-version">3.14</def>
        <def name="product-name">FooWiz</def>
      </definitions>
    </info>
    <para>The latest version of <application><ref name="product-name"/></application>
      from <ref name="corp-name"/> is <ref name="product-version"/>.</para>
  </article>
  <article>
    <info>
      <title><ref name="product-name"/> Guide</title>
      <definitions>
        <def name="product-version">4.2</def>
        <def name="product-name">Knit-o-Matic</def>
      </definitions>
    </info>
    <para>The latest version of <application><ref name="product-name"/></application>
      from <ref name="corp-name"/> is <ref name="product-version"/>.</para>
  </article>
</book>

Result of transclusion:

<?xml version="1.0" encoding="UTF-8"?>
<book xmlns="http://docbook.org/ns/docbook">
  <info>
    <title>ACME Inc. company product guide</title>
  </info>
  <article>
    <info>
      <title>FooWiz Guide</title>
    </info>
    <para>The latest version of <application>FooWiz</application>
      from ACME Inc. is 3.14.</para>
  </article>
  <article>
    <info>
      <title>Knit-o-Matic Guide</title>
    </info>
    <para>The latest version of <application>Knit-o-Matic</application>
      from ACME Inc. is 4.2.</para>
  </article>
</book>

Reference can point to particular external definitions file not to all in-scope definitions.

Example 17. Reference to particular definition file

<?xml version="1.0" encoding="UTF-8"?>
<article xmlns="http://docbook.org/ns/docbook">
  <info>
    <title>Transclusions demo</title>
  </info>
  <para>This is article about history of <ref definitionfile="definitions.001.xml" name="corp-name"/>.</para>
</article>

Result of transclusion:

<?xml version="1.0" encoding="UTF-8"?>
<article xmlns="http://docbook.org/ns/docbook">
  <info>
    <title>Transclusions demo</title>
  </info>
  <para>This is article about history of ACME Inc..</para>
</article>

Procedure 1. Finding reference definition for ref element with just name attribute

  1. Transclusion processor implementation might provide mechanism for overriding any definition. This mechanism is used first when finding definition of reference.

  2. The closest element containing info/definitions is found on ancestor-or-self:: XPath axis.

    1. If definitions contains definitionfile attribute then content of referenced file is treated as if it was inserted before the respective definitions element.

    2. If there are multiple matching info/definitions/def elements then the last one is used. If there is no matching definition then we continue with Step 2.

  3. If no matching definition was find so far, error is raised.

Procedure 2. Finding reference definition for ref element with definitionfile attribute

  1. Referenced definition file is searched for definition (def element with matching value in a name attribute).

    Should be definitionfile attribute supported here for chaining of definitions? Probably yes.
  2. If no matching definition is found, error is raised.

External references

External reference is denoted by ref element with a mandatory fileref attribute which contains URI of referenced document. Reference is replaced with content of referenced document. The outermost transcluded element is augmented with xml:base attribute in order to retain relative references.

Example 18. External reference

<?xml version="1.0" encoding="UTF-8"?>
<article xmlns="http://docbook.org/ns/docbook">
  <title>Sample article</title>
  <para>Leading text</para>
  <ref fileref="module.001.xml"/>
</article>

Result of transclusion:

<?xml version="1.0" encoding="UTF-8"?>
<article xmlns="http://docbook.org/ns/docbook">
  <title>Sample article</title>
  <para>Leading text</para>
  <section xml:base="file:/e:/texts/docbook/tc/transclusions/module.001.xml">
    <title>Module which is going to be shared across several documents</title>
    <para>Bla bla bla</para>
  </section>
</article>


It is possible to specify additional attributes xpointer, parse and encoding – their meaning is identical to corresponding attributes from XInclude.

Please note that while process similar to XInclude Base URI Fixup is performed, Language Fixup is not performed.[1]

Example 19. Transclusion of document which contains further references

<?xml version="1.0" encoding="UTF-8"?>
<book xmlns="http://docbook.org/ns/docbook">
  <title>Sample book</title>
  <ref fileref="article.001.xml"/>
  <ref fileref="shared-texts.002.xml"/>
</book>

Result of transclusion:

<?xml version="1.0" encoding="UTF-8"?>
<book xmlns="http://docbook.org/ns/docbook">
  <title>Sample book</title>
  <article xmlns:ta="http://docbook.org/xslt/ns/transclusion-annotation"
    xml:base="file:/e:/texts/docbook/tc/transclusions/article.001.xml">
    <title>Sample article</title>
    <para>Leading text</para>
    <section xml:base="file:/e:/texts/docbook/tc/transclusions/module.001.xml">
      <title>Module which is going to be shared across several documents</title>
      <para>Bla bla bla</para>
    </section>
  </article>
  <article xmlns:ta="http://docbook.org/xslt/ns/transclusion-annotation"
    xml:base="file:/e:/texts/docbook/tc/transclusions/shared-texts.002.xml">
    <info>
      <title>Transclusions demo</title>
      <author>
        <personname><firstname>Jirka</firstname><surname>Kosek</surname></personname>
      </author>
    </info>
    <para>This article was written by
          <personname><firstname>Jirka</firstname><surname>Kosek</surname></personname> when there
      was 25C outside.</para>
  </article>
</book>


Special ID/IDREF processing

Transcluded content can contain xml:id attributes. If one fragment is transcluded more then once resulting document after transclusion will contain duplicate IDs. Same problem may arise if IDs are colliding in transcluded modules. To overcome this problem all IDs and references to them can be adjusted during the transclusion process.

If there is xml:id attribute present on ref element, then this ID will replace xml:id on the outermost element of transcluded content. It is error to transclude content which is not enclosed in a single element and specifying xml:id attribute on ref element.

How IDs are going to be adjusted during transclusion is controlled by idfixup attribute on ref element. It can have one of the following values.

none

No ID adjustment is done

strip

All IDs are stripped (except xml:id inherited from ref element)

prefix

All IDs are prefixed with a value specified in prefix attribute

auto

All IDs are prefixed with a value which is unique for each ref element[2]

This is default behavior if attribute is not specified.

Of course if IDs are adjusted then corresponding references has to be also corrected. This is controlled by linkscope attribute. It can have one of the following values.

user

No IDREF adjustment is done

local

All IDREFs in transcluded content are prefixed by user specified prefix (when idfixup="prefix") or auto-generated prefix (when idfixup="auto").

Using this value with other idfixup values is an error.Maybe raising error is to strict approach.

near

All IDREFs in transcluded content are adjusted to point to the closest element which has matching ID. Matching ID doesn't mean string equality between ID and IDREF values – it is sufficient if second part of ID and IDREF after removal of possibly added prefixes is matching.

When searching for closest element ancestor elements of element with IDREF attribute are gradually inspected and matching ID is searched between all descendants. If there is no matching ID, then parent is inspected and so on until match is found or document root is reached.

global

All IDREFs in transcluded content are adjusted to point to the first element in document order which has matching ID. Matching ID doesn't mean string equality between ID and IDREF values – it is sufficient if second part of ID and IDREF after removal of possibly added prefixes is matching.

By using various combinations of idfixup and linkscope attributes we can achieve different linking behavior. Following examples show effect of using those two attributes. Examples are transcluding the following procedure which contains one internal link and one external (points to target outside of this module).

Example 20. Module with sample procedure

<?xml version="1.0" encoding="UTF-8"?>
<procedure xmlns="http://docbook.org/ns/docbook" xml:id="paper-insert">
  <title>Inserting paper into printer</title>
  <para>This procedure is targeted to printer owners.
    If you don't have printer, consider <link linkend="buy">buying one</link>.</para>  
  <step xml:id="s1"><para>Make sure that you have paper.</para></step>
  <step><para>Insert paper into printer. If you don't have paper consult <xref linkend="s1"/></para></step>
</procedure>


Now lets assume that we want to transclude this module twice to show how we can deal with duplicate IDs problem.

Example 21. Automatic ID/IDREF adjustment

<?xml version="1.0" encoding="UTF-8"?>
<book xmlns="http://docbook.org/ns/docbook">
  <title>Definitive Printer Guide</title>
  <chapter xml:id="buy">
    <title>Buying printer</title>
    <para>Grab money, go to shop, ...</para>
  </chapter>
  <chapter>
    <title>Quick installation guide</title>
    <para>Carefully follow all procedures bellow.</para>
    <ref fileref="procedure.001.xml"/>
  </chapter>
  <chapter>
    <title>Maintenance</title>
    <para>Be friendly to your printer when you speak to it.</para>
    <para>If green led is blinking, please add missing paper using the following procedure.</para>
    <ref fileref="procedure.001.xml"/>
  </chapter>
</book>

Result of transclusion:

<?xml version="1.0" encoding="UTF-8"?>
<book xmlns="http://docbook.org/ns/docbook">
  <title>Definitive Printer Guide</title>
  <chapter xml:id="buy">
    <title>Buying printer</title>
    <para>Grab money, go to shop, ...</para>
  </chapter>
  <chapter>
    <title>Quick installation guide</title>
    <para>Carefully follow all procedures bellow.</para>
    <procedure xml:id="d2e22---paper-insert"
      xml:base="file:/e:/texts/docbook/tc/transclusions/procedure.001.xml">
      <title>Inserting paper into printer</title>
      <para>This procedure is targeted to printer owners. If you don't have printer, consider <link
          linkend="buy">buying one</link>.</para>
      <step xml:id="d2e22---s1">
        <para>Make sure that you have paper.</para>
      </step>
      <step>
        <para>Insert paper into printer. If you don't have paper consult <xref linkend="d2e22---s1"
          /></para>
      </step>
    </procedure>
  </chapter>
  <chapter>
    <title>Maintenance</title>
    <para>Be friendly to your printer when you speak to it.</para>
    <para>If green led is blinking, please add missing paper using the following procedure.</para>
    <procedure xml:id="d2e36---paper-insert"
      xml:base="file:/e:/texts/docbook/tc/transclusions/procedure.001.xml">
      <title>Inserting paper into printer</title>
      <para>This procedure is targeted to printer owners. If you don't have printer, consider <link
          linkend="buy">buying one</link>.</para>
      <step xml:id="d2e36---s1">
        <para>Make sure that you have paper.</para>
      </step>
      <step>
        <para>Insert paper into printer. If you don't have paper consult <xref linkend="d2e36---s1"
          /></para>
      </step>
    </procedure>
  </chapter>
</book>

We haven't specified idfixup and linkscope attributes so default behavior is triggered. All IDs in transcluded modules are automatically prefixed to prevent ID collisions. Then IDREFs are fixed so links point to the nearest possible target. For example link from step 2 to step 1 in procedure always points to the same instance of procedure. However buy link is pointing correctly to target in the main document.


Example 22. Global linkscope

<?xml version="1.0" encoding="UTF-8"?>
<book xmlns="http://docbook.org/ns/docbook">
  <title>Definitive Printer Guide</title>
  <chapter xml:id="buy">
    <title>Buying printer</title>
    <para>Grab money, go to shop, ...</para>
  </chapter>
  <chapter>
    <title>Quick installation guide</title>
    <para>Carefully follow all procedures bellow.</para>
    <ref fileref="procedure.001.xml"/>
  </chapter>
  <chapter>
    <title>Maintenance</title>
    <para>Be friendly to your printer when you speak to it.</para>
    <para>If green led is blinking, please add missing paper using the following procedure.</para>
    <ref fileref="procedure.001.xml" linkscope="global"/>
  </chapter>
</book>

Result of transclusion:

<?xml version="1.0" encoding="UTF-8"?>
<book xmlns="http://docbook.org/ns/docbook">
  <title>Definitive Printer Guide</title>
  <chapter xml:id="buy">
    <title>Buying printer</title>
    <para>Grab money, go to shop, ...</para>
  </chapter>
  <chapter>
    <title>Quick installation guide</title>
    <para>Carefully follow all procedures bellow.</para>
    <procedure xml:id="d2e22---paper-insert"
      xml:base="file:/e:/texts/docbook/tc/transclusions/procedure.001.xml">
      <title>Inserting paper into printer</title>
      <para>This procedure is targeted to printer owners. If you don't have printer, consider <link
          linkend="buy">buying one</link>.</para>
      <step xml:id="d2e22---s1">
        <para>Make sure that you have paper.</para>
      </step>
      <step>
        <para>Insert paper into printer. If you don't have paper consult <xref linkend="d2e22---s1"
          /></para>
      </step>
    </procedure>
  </chapter>
  <chapter>
    <title>Maintenance</title>
    <para>Be friendly to your printer when you speak to it.</para>
    <para>If green led is blinking, please add missing paper using the following procedure.</para>
    <procedure xml:id="d2e36---paper-insert"
      xml:base="file:/e:/texts/docbook/tc/transclusions/procedure.001.xml">
      <title>Inserting paper into printer</title>
      <para>This procedure is targeted to printer owners. If you don't have printer, consider <link
          linkend="buy">buying one</link>.</para>
      <step xml:id="d2e36---s1">
        <para>Make sure that you have paper.</para>
      </step>
      <step>
        <para>Insert paper into printer. If you don't have paper consult <xref linkend="d2e22---s1"
          /></para>
      </step>
    </procedure>
  </chapter>
</book>

We used linkscope="global" on the second transclusion. Result is that link from step 2 in the second procedure now links to step 1 in the first procedure.


Example 23. Local linkscope

<?xml version="1.0" encoding="UTF-8"?>
<book xmlns="http://docbook.org/ns/docbook">
  <title>Definitive Printer Guide</title>
  <chapter xml:id="buy">
    <title>Buying printer</title>
    <para>Grab money, go to shop, ...</para>
  </chapter>
  <chapter>
    <title>Quick installation guide</title>
    <para>Carefully follow all procedures bellow.</para>
    <ref fileref="procedure.001.xml" linkscope="local"/>
  </chapter>
  <chapter>
    <title>Maintenance</title>
    <para>Be friendly to your printer when you speak to it.</para>
    <para>If green led is blinking, please add missing paper using the following procedure.</para>
    <ref fileref="procedure.001.xml"/>
  </chapter>
</book>

Result of transclusion:

<?xml version="1.0" encoding="UTF-8"?>
<book xmlns="http://docbook.org/ns/docbook">
  <title>Definitive Printer Guide</title>
  <chapter xml:id="buy">
    <title>Buying printer</title>
    <para>Grab money, go to shop, ...</para>
  </chapter>
  <chapter>
    <title>Quick installation guide</title>
    <para>Carefully follow all procedures bellow.</para>
    <procedure xml:id="d2e22---paper-insert"
      xml:base="file:/e:/texts/docbook/tc/transclusions/procedure.001.xml">
      <title>Inserting paper into printer</title>
      <para>This procedure is targeted to printer owners. If you don't have printer, consider <link
          linkend="d2e22---buy">buying one</link>.</para>
      <step xml:id="d2e22---s1">
        <para>Make sure that you have paper.</para>
      </step>
      <step>
        <para>Insert paper into printer. If you don't have paper consult <xref linkend="d2e22---s1"
          /></para>
      </step>
    </procedure>
  </chapter>
  <chapter>
    <title>Maintenance</title>
    <para>Be friendly to your printer when you speak to it.</para>
    <para>If green led is blinking, please add missing paper using the following procedure.</para>
    <procedure xml:id="d2e36---paper-insert"
      xml:base="file:/e:/texts/docbook/tc/transclusions/procedure.001.xml">
      <title>Inserting paper into printer</title>
      <para>This procedure is targeted to printer owners. If you don't have printer, consider <link
          linkend="buy">buying one</link>.</para>
      <step xml:id="d2e36---s1">
        <para>Make sure that you have paper.</para>
      </step>
      <step>
        <para>Insert paper into printer. If you don't have paper consult <xref linkend="d2e36---s1"
          /></para>
      </step>
    </procedure>
  </chapter>
</book>

We used linkscope="local" on the first transclusion. This means that no link from this transclusion can point outside of this transclusion. Because there was such link (buy link), result of transclusion is broken because there is no corresponding target for IDREF d2e22---buy.


Example 24. Manually assigned prefix

<?xml version="1.0" encoding="UTF-8"?>
<book xmlns="http://docbook.org/ns/docbook">
  <title>Definitive Printer Guide</title>
  <chapter xml:id="buy">
    <title>Buying printer</title>
    <para>Grab money, go to shop, ...</para>
  </chapter>
  <chapter>
    <title>Quick installation guide</title>
    <para>Carefully follow all procedures bellow.</para>
    <ref fileref="procedure.001.xml" xml:id="install-proc" idfixup="prefix" prefix="install-proc_"/>
  </chapter>
  <chapter>
    <title>Maintenance</title>
    <para>Be friendly to your printer when you speak to it.</para>
    <para>If green led is blinking, please add missing paper using the following procedure.</para>
    <ref fileref="procedure.001.xml" xml:id="maintain-proc" idfixup="prefix" prefix="maintain-proc_"/>
  </chapter>
</book>

Result of transclusion:

<?xml version="1.0" encoding="UTF-8"?>
<book xmlns="http://docbook.org/ns/docbook">
  <title>Definitive Printer Guide</title>
  <chapter xml:id="buy">
    <title>Buying printer</title>
    <para>Grab money, go to shop, ...</para>
  </chapter>
  <chapter>
    <title>Quick installation guide</title>
    <para>Carefully follow all procedures bellow.</para>
    <procedure xml:id="install-proc_paper-insert"
      xml:base="file:/e:/texts/docbook/tc/transclusions/procedure.001.xml">
      <title>Inserting paper into printer</title>
      <para>This procedure is targeted to printer owners. If you don't have printer, consider <link
          linkend="buy">buying one</link>.</para>
      <step xml:id="install-proc_s1">
        <para>Make sure that you have paper.</para>
      </step>
      <step>
        <para>Insert paper into printer. If you don't have paper consult <xref
            linkend="install-proc_s1"/></para>
      </step>
    </procedure>
  </chapter>
  <chapter>
    <title>Maintenance</title>
    <para>Be friendly to your printer when you speak to it.</para>
    <para>If green led is blinking, please add missing paper using the following procedure.</para>
    <procedure xml:id="maintain-proc_paper-insert"
      xml:base="file:/e:/texts/docbook/tc/transclusions/procedure.001.xml">
      <title>Inserting paper into printer</title>
      <para>This procedure is targeted to printer owners. If you don't have printer, consider <link
          linkend="buy">buying one</link>.</para>
      <step xml:id="maintain-proc_s1">
        <para>Make sure that you have paper.</para>
      </step>
      <step>
        <para>Insert paper into printer. If you don't have paper consult <xref
            linkend="maintain-proc_s1"/></para>
      </step>
    </procedure>
  </chapter>
</book>

If we care about resulting IDs after transclusion we can manually assign some meaningful prefix before IDs in transcluded document.


Example 25. Disabling ID fixup

<?xml version="1.0" encoding="UTF-8"?>
<book xmlns="http://docbook.org/ns/docbook">
  <title>Definitive Printer Guide</title>
  <chapter xml:id="buy">
    <title>Buying printer</title>
    <para>Grab money, go to shop, ...</para>
  </chapter>
  <chapter>
    <title>Quick installation guide</title>
    <para>Carefully follow all procedures bellow.</para>
    <ref fileref="procedure.001.xml" idfixup="none"/>
  </chapter>
  <chapter>
    <title>Maintenance</title>
    <para>Be friendly to your printer when you speak to it.</para>
    <para>If green led is blinking, please add missing paper using the following procedure.</para>
    <ref fileref="procedure.001.xml" idfixup="none"/>
  </chapter>
</book>

Result of transclusion:

<?xml version="1.0" encoding="UTF-8"?>
<book xmlns="http://docbook.org/ns/docbook">
  <title>Definitive Printer Guide</title>
  <chapter xml:id="buy">
    <title>Buying printer</title>
    <para>Grab money, go to shop, ...</para>
  </chapter>
  <chapter>
    <title>Quick installation guide</title>
    <para>Carefully follow all procedures bellow.</para>
    <procedure xml:id="paper-insert"
      xml:base="file:/e:/texts/docbook/tc/transclusions/procedure.001.xml">
      <title>Inserting paper into printer</title>
      <para>This procedure is targeted to printer owners. If you don't have printer, consider <link
          linkend="buy">buying one</link>.</para>
      <step xml:id="s1">
        <para>Make sure that you have paper.</para>
      </step>
      <step>
        <para>Insert paper into printer. If you don't have paper consult <xref linkend="s1"/></para>
      </step>
    </procedure>
  </chapter>
  <chapter>
    <title>Maintenance</title>
    <para>Be friendly to your printer when you speak to it.</para>
    <para>If green led is blinking, please add missing paper using the following procedure.</para>
    <procedure xml:id="paper-insert"
      xml:base="file:/e:/texts/docbook/tc/transclusions/procedure.001.xml">
      <title>Inserting paper into printer</title>
      <para>This procedure is targeted to printer owners. If you don't have printer, consider <link
          linkend="buy">buying one</link>.</para>
      <step xml:id="s1">
        <para>Make sure that you have paper.</para>
      </step>
      <step>
        <para>Insert paper into printer. If you don't have paper consult <xref linkend="s1"/></para>
      </step>
    </procedure>
  </chapter>
</book>

We have disabled ID fixup by idfixup="none". The resulting document thus contain duplicated IDs.


Example 26. Stripping IDs

<?xml version="1.0" encoding="UTF-8"?>
<book xmlns="http://docbook.org/ns/docbook">
  <title>Definitive Printer Guide</title>
  <chapter xml:id="buy">
    <title>Buying printer</title>
    <para>Grab money, go to shop, ...</para>
  </chapter>
  <chapter>
    <title>Quick installation guide</title>
    <para>Carefully follow all procedures bellow.</para>
    <ref fileref="procedure.001.xml"/>
  </chapter>
  <chapter>
    <title>Maintenance</title>
    <para>Be friendly to your printer when you speak to it.</para>
    <para>If green led is blinking, please add missing paper using the following procedure.</para>
    <ref fileref="procedure.001.xml" idfixup="strip"/>
  </chapter>
</book>

Result of transclusion:

<?xml version="1.0" encoding="UTF-8"?>
<book xmlns="http://docbook.org/ns/docbook">
  <title>Definitive Printer Guide</title>
  <chapter xml:id="buy">
    <title>Buying printer</title>
    <para>Grab money, go to shop, ...</para>
  </chapter>
  <chapter>
    <title>Quick installation guide</title>
    <para>Carefully follow all procedures bellow.</para>
    <procedure xml:id="d2e22---paper-insert"
      xml:base="file:/e:/texts/docbook/tc/transclusions/procedure.001.xml">
      <title>Inserting paper into printer</title>
      <para>This procedure is targeted to printer owners. If you don't have printer, consider <link
          linkend="buy">buying one</link>.</para>
      <step xml:id="d2e22---s1">
        <para>Make sure that you have paper.</para>
      </step>
      <step>
        <para>Insert paper into printer. If you don't have paper consult <xref linkend="d2e22---s1"
          /></para>
      </step>
    </procedure>
  </chapter>
  <chapter>
    <title>Maintenance</title>
    <para>Be friendly to your printer when you speak to it.</para>
    <para>If green led is blinking, please add missing paper using the following procedure.</para>
    <procedure
      xml:base="file:/e:/texts/docbook/tc/transclusions/procedure.001.xml">
      <title>Inserting paper into printer</title>
      <para>This procedure is targeted to printer owners. If you don't have printer, consider <link
          linkend="buy">buying one</link>.</para>
      <step>
        <para>Make sure that you have paper.</para>
      </step>
      <step>
        <para>Insert paper into printer. If you don't have paper consult <xref linkend="d2e22---s1"
          /></para>
      </step>
    </procedure>
  </chapter>
</book>

We have stripped all IDs from the second transcluded procedure by idfixup="strip". Thus there are no duplicate IDs in the resulting document and links from second procedure always target first one. However IDs in the first procedure were automatically prefixed.


Example 27. Retaining original IDs

<?xml version="1.0" encoding="UTF-8"?>
<book xmlns="http://docbook.org/ns/docbook">
  <title>Definitive Printer Guide</title>
  <chapter xml:id="buy">
    <title>Buying printer</title>
    <para>Grab money, go to shop, ...</para>
  </chapter>
  <chapter>
    <title>Quick installation guide</title>
    <para>Carefully follow all procedures bellow.</para>
    <ref fileref="procedure.001.xml" idfixup="none" linkscope="user"/>
  </chapter>
  <chapter>
    <title>Maintenance</title>
    <para>Be friendly to your printer when you speak to it.</para>
    <para>If green led is blinking, please add missing paper using the following procedure.</para>
    <ref fileref="procedure.001.xml" idfixup="strip" linkscope="user"/>
  </chapter>
</book>

Result of transclusion:

<?xml version="1.0" encoding="UTF-8"?>
<book xmlns="http://docbook.org/ns/docbook">
  <title>Definitive Printer Guide</title>
  <chapter xml:id="buy">
    <title>Buying printer</title>
    <para>Grab money, go to shop, ...</para>
  </chapter>
  <chapter>
    <title>Quick installation guide</title>
    <para>Carefully follow all procedures bellow.</para>
    <procedure xml:id="paper-insert"
      xml:base="file:/e:/texts/docbook/tc/transclusions/procedure.001.xml">
      <title>Inserting paper into printer</title>
      <para>This procedure is targeted to printer owners. If you don't have printer, consider <link
          linkend="buy">buying one</link>.</para>
      <step xml:id="s1">
        <para>Make sure that you have paper.</para>
      </step>
      <step>
        <para>Insert paper into printer. If you don't have paper consult <xref linkend="s1"/></para>
      </step>
    </procedure>
  </chapter>
  <chapter>
    <title>Maintenance</title>
    <para>Be friendly to your printer when you speak to it.</para>
    <para>If green led is blinking, please add missing paper using the following procedure.</para>
    <procedure xml:base="file:/e:/texts/docbook/tc/transclusions/procedure.001.xml">
      <title>Inserting paper into printer</title>
      <para>This procedure is targeted to printer owners. If you don't have printer, consider <link
          linkend="buy">buying one</link>.</para>
      <step>
        <para>Make sure that you have paper.</para>
      </step>
      <step>
        <para>Insert paper into printer. If you don't have paper consult <xref linkend="s1"/></para>
      </step>
    </procedure>
  </chapter>
</book>

We have stripped all IDs from the second transcluded procedure by idfixup="strip". Thus there are no duplicate IDs in the resulting document and links from second procedure always target first one. IDs in the first procedure were not automatically prefixed since we have specified idfixup="none".


Should be there option to preserve original IDs (without prefix) in output when doing prefixes?

Transformations

FIXME: This should provide some very generic mechanism for applying various transformations on referenced content before actual transclusion. For example DITA -> DocBook, V4.x -> V5.0 conversion and so on.

Evaluation

The above presented DocBook transclusion proposal solves all use cases except UC-2. Transclusions are more usable and can solve various problems introduced by multiple inclusion of the same content. Some of those problems can be solved by using XInclude – but syntax is cumbersome and there is no good interoperability between various XInclude implementations.

For above stated reasons I think that transclusions should be added into core DocBook. I don't think that making them separate XML transclusion standard is viable approach. Transclusion processor has to know which attributes are of ID/IDREF type for each document type. History shown that generic standards relying on access to a schema are not successful.

DocBook schema with support for transclusions

# TODO
# - use DocBook schema coding style

default namespace db = "http://docbook.org/ns/docbook"

include "docbook.rnc"

# element for referencing content
db.ref = 
  element ref { 
    (db.ref.inline | db.ref.external)
  }

# common content
db.ref.common =
  db.effectivity.attributes,
  attribute xml:id { xsd:ID }?,
  attribute linkscope { "user" | "local" | "near" | "global"}?,
  attribute idfixup { "none" | "strip" | "prefix" | "auto" }?,
  attribute prefix { xsd:NCName }?,
  (db.transform*)

# inline content is grabed from local definitions or from external definitions file
db.ref.inline =
  attribute definitionfile { xsd:anyURI }?,
  attribute name { xsd:NCName },
  db.ref.common

# content from external sources
db.ref.external =
  attribute fileref { xsd:anyURI },
  attribute xpointer { text }?,
  attribute encoding { text }?,
  attribute parse { "xml" | "text" }?,
  db.ref.common
  
# FIXME: specify all kinds of possible transformations
db.transform = empty  

# element for defitions
db.definitions =
  element definitions {
    attribute definitionfile { xsd:anyURI }?,
    db.def*
  }
  
# single definition
db.def = 
  element def {
    attribute name { xsd:NCName },
    db.effectivity.attributes,
    (text & db._any*)
  }
  
# hooks into standard schema

# allow references to appear almost everywhere
db.ubiq.inlines |= db.ref*

db.divisions |= db.ref
db.components |= db.ref

db.toplevel.sections |=
   (db.section|db.ref)+ | (db.simplesect|db.ref)+

db.recursive.sections |=
   (db.section|db.ref)+ | (db.simplesect|db.ref)+

db.part.components |= db.ref

db.reference.components |= db.ref

db.all.blocks |= db.ref

db.info.elements |= db.ref

# definitions can be only inside info elements
db.info.elements |= db.definitions

# or they can be provided in a separate file
start |= db.definitions


Should we allow multiple file references in @definitionfile?

Should we allow ref inside def? (Probably yes).

Sample transclusion processor written in XSLT 2.0

Please note that this sample transclusion processor is not yet feature complete. It supports only subset of proposal.

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0"
		xmlns:f="http://docbook.org/xslt/ns/extension"
		xmlns:mp="http://docbook.org/xslt/ns/mode/private"
		xmlns:db="http://docbook.org/ns/docbook"
		xmlns:xs="http://www.w3.org/2001/XMLSchema"
		xmlns:ta="http://docbook.org/xslt/ns/transclusion-annotation"
		exclude-result-prefixes="f mp xs">

<xsl:template match="/">

  <xsl:variable name="expanded" select="f:expand-definitions(/)"/>

  <xsl:variable name="resolved" select="f:resolve-references($expanded)"/>

  <xsl:variable name="result" select="f:adjust-idrefs($resolved)"/>

  <xsl:sequence select="$result"/>

</xsl:template>

<xsl:function name="f:transclude" as="node()+">
  <xsl:param name="doc" as="node()+"/>

  <xsl:variable name="expanded" select="f:expand-definitions($doc)"/>
  <xsl:variable name="resolved" select="f:resolve-references($expanded)"/>
  <xsl:sequence select="$resolved"/>
</xsl:function>

<xsl:function name="f:expand-definitions" as="node()+">
  <xsl:param name="doc" as="node()+"/>

  <xsl:apply-templates select="$doc" mode="mp:expand-definitions"/>
</xsl:function>

<xsl:template match="node()" mode="mp:expand-definitions">
  <xsl:copy>
    <xsl:copy-of select="@*"/>
    <xsl:apply-templates mode="mp:expand-definitions"/>
  </xsl:copy>
</xsl:template>

<xsl:template match="db:definitions[@definitionfile]" mode="mp:expand-definitions">
  <xsl:copy-of select="f:expand-definitions(doc(@definitionfile))"/>
  <xsl:copy>
    <xsl:copy-of select="@* except @definitionfile"/>
    <xsl:apply-templates mode="mp:expand-definitions"/>
  </xsl:copy>
</xsl:template>

<xsl:function name="f:resolve-references" as="node()+">
  <xsl:param name="doc" as="node()+"/>

  <xsl:apply-templates select="$doc" mode="mp:transclude"/>
</xsl:function>

<xsl:template match="node()" mode="mp:transclude">
  <xsl:param name="idfixup" select="'auto'" tunnel="yes"/>
  <xsl:param name="prefix" tunnel="yes"/>
  <xsl:copy>
    <xsl:copy-of select="@* except @xml:id"/>
    <xsl:if test="@xml:id">
      <xsl:choose>
	<xsl:when test="$idfixup = 'none'">
	  <xsl:copy-of select="@xml:id"/>
	</xsl:when>
	<xsl:when test="$idfixup = 'strip'">
	</xsl:when>
	<xsl:otherwise>
	  <xsl:attribute name="xml:id" select="concat($prefix, @xml:id)"/>
	</xsl:otherwise>
      </xsl:choose>
    </xsl:if>
    <xsl:apply-templates mode="mp:transclude"/>
  </xsl:copy>
</xsl:template>

<!-- FIMXE: this stripping is there just to have more compact output -->
<xsl:template match="db:definitions" mode="mp:transclude" priority="1">
</xsl:template>

<!-- FIXME: add idfixup to inline references -->
<xsl:template match="db:ref[@name and not(@definitionfile)]" mode="mp:transclude">
  <xsl:copy-of select="f:definition-for-name(@name, .)"/>
</xsl:template>

<xsl:template match="db:ref[@name and @definitionfile]" mode="mp:transclude">
  <xsl:variable name="name" select="@name"/>
  <xsl:variable name="defs" select="f:expand-definitions(doc(@definitionfile))"/>

  <xsl:choose>
    <xsl:when test="$defs/db:def[@name = $name]">
      <xsl:sequence select="($defs/db:def[@name = $name])[last()]/node()"/>
    </xsl:when>
    <xsl:otherwise>
      <xsl:message>Error: definition of "<xsl:value-of select="$name"/>" was not found.</xsl:message>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

<xsl:function name="f:definition-for-name" as="node()*">
  <xsl:param name="name" as="xs:string"/>
  <xsl:param name="context" as="node()"/>

  <xsl:variable name="closest-info-with-defs" select="$context/ancestor-or-self::*/db:info[db:definitions][1]"/>

  <xsl:choose>
    <xsl:when test="$closest-info-with-defs">
      <xsl:choose>
	<xsl:when test="$closest-info-with-defs/db:definitions/db:def[@name = $name]">
	  <xsl:sequence select="($closest-info-with-defs/db:definitions/db:def[@name = $name])[last()]/node()"/>
	</xsl:when>
	<xsl:otherwise>
	  <xsl:sequence select="f:definition-for-name($name, $closest-info-with-defs/../..)"/>
	</xsl:otherwise>
      </xsl:choose>
    </xsl:when>
    <xsl:otherwise>
      <xsl:message>Error: definition of "<xsl:value-of select="$name"/>" was not found.</xsl:message>
    </xsl:otherwise>
  </xsl:choose>
</xsl:function>

<xsl:template match="db:ref[@xpointer or @parse or @encoding]" mode="mp:transclude">
  <xsl:message>@xpointer or @parse or @encoding are not yet supported</xsl:message>
  <xsl:copy>
    <xsl:copy-of select="@*"/>
    <xsl:apply-templates mode="mp:transclude"/>
  </xsl:copy>
</xsl:template>

<xsl:param name="psep" select="'---'"/>

<xsl:template match="db:ref[@fileref]" mode="mp:transclude">
  <xsl:variable name="doc" select="f:transclude(doc(@fileref))"/>
  <xsl:variable name="baseuri" select="resolve-uri(@fileref, base-uri(.))"/>

  <xsl:variable name="idfixup" select="if (@idfixup) then @idfixup else 'auto'"/>

  <xsl:variable name="linkscope" select="if (@linkscope) then @linkscope else 'near'"/>

  <xsl:variable name="prefix">
    <xsl:choose>
      <xsl:when test="$idfixup = 'auto'">
	<xsl:sequence select="concat(generate-id(.), $psep)"/>
      </xsl:when>
      <xsl:when test="$idfixup = 'prefix'">
	<xsl:sequence select="string(@prefix)"/>
      </xsl:when>
      <xsl:otherwise></xsl:otherwise>
    </xsl:choose>
  </xsl:variable>

  <xsl:variable name="ref-xmlid" select="@xml:id"/>

  <xsl:for-each select="$doc">
    <xsl:copy-of select="preceding-sibling::node()"/>
    <xsl:copy>
      <xsl:variable name="xmlid" select="if ($ref-xmlid) then $ref-xmlid else @xml:id"/>
      <xsl:copy-of select="@* except @xml:id"/>
      <xsl:choose>
	<xsl:when test="$idfixup = 'none' and $xmlid">
	  <xsl:attribute name="xml:id" select="$xmlid"/>
	</xsl:when>
	<xsl:when test="$idfixup = 'strip' and not($ref-xmlid)">
	</xsl:when>
	<xsl:otherwise>
	  <xsl:attribute name="xml:id" select="concat($prefix, @xml:id)"/>
	</xsl:otherwise>
      </xsl:choose>
      <xsl:attribute name="xml:base" select="$baseuri"/>
      <xsl:attribute name="ta:linkscope" select="$linkscope"/>
      <xsl:attribute name="ta:prefix" select="$prefix"/>
      <xsl:apply-templates mode="mp:transclude">
	<xsl:with-param name="idfixup" select="$idfixup" tunnel="yes"/>
	<xsl:with-param name="prefix" select="$prefix" tunnel="yes"/>
      </xsl:apply-templates>
    </xsl:copy>
    <xsl:copy-of select="following-sibling::node()"/>
  </xsl:for-each>
</xsl:template>

<xsl:function name="f:adjust-idrefs" as="node()+">
  <xsl:param name="doc" as="node()+"/>

  <xsl:apply-templates select="$doc" mode="mp:adjust-idrefs"/>
</xsl:function>

<xsl:template match="node()|@*" mode="mp:adjust-idrefs">
  <xsl:copy>
    <xsl:apply-templates select="@* | node()" mode="mp:adjust-idrefs"/>
  </xsl:copy>
</xsl:template>

<!-- FIXME: put all IDREF attributes in match -->
<xsl:template match="@linkend" mode="mp:adjust-idrefs">
  <xsl:variable name="idref" select="."/>

  <xsl:variable name="annotation" select="ancestor-or-self::*[@ta:linkscope][1]"/>
  <xsl:variable name="linkscope" select="($annotation/@ta:linkscope, 'near')[1]"/>
  <xsl:variable name="prefix" select="$annotation/@ta:prefix"/>

  <xsl:attribute name="{local-name(.)}">
    <xsl:choose>
      <xsl:when test="$linkscope = 'user'">
	<xsl:value-of select="$idref"/>
      </xsl:when>
      <xsl:when test="$linkscope = 'local'">
	<xsl:value-of select="concat($prefix, $idref)"/>
      </xsl:when>
      <xsl:when test="$linkscope = 'near'">
	<xsl:value-of select="f:nearest-matching-id($idref, ..)"/>
      </xsl:when>
      <xsl:when test="$linkscope = 'global'">
	<xsl:value-of select="f:nearest-matching-id($idref, root(.))"/>
      </xsl:when>
    </xsl:choose>
  </xsl:attribute>
</xsl:template>

<xsl:function name="f:nearest-matching-id" as="xs:string">
  <xsl:param name="idref" as="xs:string"/>
  <xsl:param name="context" as="node()"/>

  <!-- FIXME: key() requires document-node() rooted subtree -->
  <!--  <xsl:variable name="targets" select="key('unprefixed-id', f:unprefixed-id($idref, $context), $context)"/> -->
  <xsl:variable name="targets" select="$context//*[@xml:id][f:unprefixed-id(@xml:id, .) eq f:unprefixed-id($idref, $context)]"/> 

  <xsl:choose>
    <xsl:when test="not($targets) and $context/..">
      <xsl:sequence select="f:nearest-matching-id($idref, $context/..)"/>
    </xsl:when>
    <xsl:when test="$targets">
      <xsl:sequence select="$targets[1]/string(@xml:id)"/>
    </xsl:when>
    <xsl:otherwise>
      <xsl:message>Error: no matching ID for reference "<xsl:value-of select="$idref"/>" was found.</xsl:message>
    </xsl:otherwise>
  </xsl:choose>
</xsl:function>

<!-- FIXME: type annotation should be without ?, find why it is called with empty sequence -->
<xsl:function name="f:unprefixed-id" as="xs:string?">
  <xsl:param name="id" as="xs:string?"/>
  <xsl:param name="context" as="node()"/>
  
  <xsl:variable name="prefix" select="$context/ancestor-or-self::*[@ta:prefix][1]/@ta:prefix"/>

  <xsl:sequence select="if ($prefix) then substring-after($id, $prefix) else $id"/>
</xsl:function>

<!--
<xsl:key name="unprefixed-id" match="*[@xml:id]" use="f:unprefixed-id(@xml:id, .)"/>
-->

</xsl:stylesheet>


[1] This effectively means that it is not necessary to specify xml:lang on each module.

[2] For example XSLT based implementations can use generate-id() on ref element to generate such unique prefix.

transclusions.zip

OpenPGP digital signature



[Date Prev] | [Thread Prev] | [Thread Next] | [Date Next] -- [Date Index] | [Thread Index] | [List Home]