[xsl] Partial Implementation of XInclude include element

Subject: [xsl] Partial Implementation of XInclude include element
From: "W. Eliot Kimber" <eliot@xxxxxxxxxx>
Date: Sun, 22 Sep 2002 17:00:28 -0500
I have developed a simple technique for implementing XInclude's include
element as part of a larger style sheet so that you don't have a to do a
two-stage transform. I haven't tested this extensively, but I don't see
any reason why it shouldn't work for any document set, although I may
have overlooked some subtle issue.

The code is shown below. This is a partial implementation in that it
does not handle any possible XPointer, only document-level URLs.
However, if you hooked in my XPointer implementation it should be
possible to have a complete implementation. However, as XInclude is
largely intended to replace the use of external parsed entities it's
probably rare that people would use it to include subtrees of documents
in most cases (although that happens to be a very useful feature in
certain re-use and document management use cases).

Note also that it is not sufficient to simply resolve includes in the
normal course of processing in the general case--this is because the
XSLT processor will not resolve the effective ancestry of an element,
only its original ancestry. Thus, if you have a document that starts
with <division> that is included in <body> by an <xi:include> and you
have a rule that matches "body/division", the included division *will
not match* because it's context is "/division" (because the included
division is the document element of its containing document). That's why
you must perform this two-stage process.

The only big limitation I've found so far is that unparsed entity
resolution doesn't work, presumably because there's no way to relate the
nodes in the result tree fragment back to the documents they were copied
from in order to lookup entity URIs. But the easy answer there is to not
use external unparsed entities but direct URI references. I suppose
there's probably a way to build up a mapping of entity names to URIs for
later use but I haven't put any effort into it. One challenge there is
that there's no way I know of to determine if a given attribute is of
type ENTITY or ENTITIES from the XSLT style sheet--if there is, then the
fix is pretty easy.

Here's the implementing code (note that you must declare the XInclude
name space in your style sheet, here I've used the prefix "xi" for it--

<!-- ============================================================== 
     XInclude implementation 

     Implements XInclude by processing the entire compound document set
     to produce a single result tree with all the includes resolved
     and then applies the normal template processing to that document.

<xsl:template match="/">
 <xsl:variable name="resolved-doc">
   <xsl:apply-templates  mode="xinclude"/>
 <xsl:apply-templates select="$resolved-doc" mode="normal"/>

<xsl:template match="/" mode="normal">

<xsl:template match="*"  mode="xinclude">
  <xsl:variable name="tagname" select="name()"/>
  <xsl:element name="{$tagname}">
    <xsl:for-each select="attribute::*">
      <xsl:variable name="attname" select="name()"/>
      <xsl:attribute name="{$attname}"><xsl:value-of
    <xsl:apply-templates mode="xinclude"/>

<xsl:template match="xi:include" mode="xinclude">
  <xsl:variable name="xpath" select="@xi:href"/>
    <xsl:when test="$xpath != ''">
      <xsl:message>Including <xsl:value-of
      <xsl:apply-templates select="document($xpath)" mode="xinclude"/>
      <xsl:message>Xinclude: Failed to get a value for the xi:href=
attribute of xi:include element.</xsl:message>

<!-- End of XInclude support -->

[As an FYI, note that XInclude is *semantically* equivalent to using
external parsed entities in that the result of including something must
be valid *as if the included thing were a syntactic part of the
including document*. In particular, IDs and entity names must be unique
across all the members of a compound document composed using XInclude.
If you need to do re-use where this requirement cannot be met, then you
need to use XLink or HyTime to define your transclusion and go to extra
effort to produce a usable result. In particular, the issue of how to
manage links across the members of a compound document gets a bit
tricky--for XInclude you have to write your pointers as though all the
targets were part of the top-level document, which will not in fact be
true until the includes are resolved at processing time, making editing
and management of these documents a bit dicy. Or you must create all
your links as external links that are bound to the top-level document,
not the included members. 

[With HyTime-style transclusion (the "value reference" facility of
HyTime), the transcluded documents maintain their separate existence
following transclusion, meaning that there is no requirement, for
example, that IDs be unique across all the members. It also means that
links across members are always unambiguous. However, it does not avoid
the problem posed by re-use such that an embedded link from one
component to another component of a single compound document might not
be meaningful in another re-use context if the second component isn't
re-used as well. For example, a task that is re-used in several
maintenance manuals might need to be linked to a second task in manual A
but not in manual B. In that case, the only way to really handle the
problem is to again store all the links as external links tied to the
top-level document. Also, the HyTime approach imposes some difficult
implementation details on processors--for example, traversing up a
included element's effective ancestor list following inclusion. XInclude
avoids this second problem by simplifying it away.]


W. Eliot Kimber, eliot@xxxxxxxxxx
Consultant, ISOGEN International

1016 La Posada Dr., Suite 240
Austin, TX  78752 Phone: 512.656.4139

 XSL-List info and archive:  http://www.mulberrytech.com/xsl/xsl-list

Current Thread