[XSL-LIST Mailing List Archive Home] [By Thread] [By Date]

[xsl] The notion of inheritance - An implementation


Subject: [xsl] The notion of inheritance - An implementation
From: "Steve Van Hoyweghen" <svhoy@xxxxxxxxxxx>
Date: Sat, 29 Sep 2001 17:39:34 +0200

Hi,

Some time ago I found a question on the notion of Inheritance from William Bagby on this

mailing list. (http://www.biglist.com/lists/xsl-list/archives/200103/msg01509.html)

I have also XML inheritance requirements. I used the basic inputs from Jeni and Michael
and a lot of XSLT tinkering to come up with a far more general solution.


I think that this style sheet is usable as a general purpose tool in many
projects.

The solution works well. The only problem I can't solve is how to get rid of
the namespace declaration
xmlns:ooxml="http://www.barcoview.com/ooxml/inheritance" in the out.xml result
file. I think there is no solution. Has anyone any suggestions?


I would also appreciate any feedback, remarks or enhancements to this solution.

Steve Van Hoyweghen

************************** Start of file transform.xsl *************************

<?xml version="1.0" encoding="utf-8"?>

<!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

file : transform.xsl
date : 29 Sep 2001
version : 1.0
author : Steve Van Hoyweghen
use : An inheritance implementation for XML elements.
remarks : This is only tested with the Microsoft MSXML3 XSLT processor. There
is only one dependency on this processor which is clearly marked.
It should be easy to adapt to another XSLT processor implementation.
See comments for more information.


This style sheet implements an inheritance mechanism for XML fragments of
selected XML elements and their associated children, attributes and text nodes.
A prototype-based single inheritance approach is chosen.


Principle
~~~~~~~~~
Assume two XML fragments A and A'. The precondition is that the element name
of both fragments are identical. Assume also that fragment A' inherits from
fragment A. All elements, attributes and text nodes from the subtree modeled by
fragment A' are copied without further processing. All elements, attributes
and text nodes not present in fragment A', but with an occurrence in fragment A,
are added to the resulting instantiation.
The inheritance tree is recursively traveled from a leaf of the inheritance
tree up to the root. This implementation supports inheritance chains of any
length, e.g. A <- A' <- A''.


Remarks
~~~~~~~
- Please note that the ordering of elements is defined by the ordering of the
elements from fragment A'', followed by the ordering from the elements from
fragment A', followed by the ordering from the elements from fragment A.
- This implementation allows to spread the inheritance invocations across
different files to maximize the reuse of XML fragments.
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
- Please do not define a circular inheritance definition because this will
result in an infinite recursive invocation during the generation process.
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!


Implementation details
~~~~~~~~~~~~~~~~~~~~~~
There are four reserved attributes in a dedicated namespace (ooxml) that are
used to implement the inheritance.
These are:
- ooxml:href is optional and refers to another file where the implementations
to inherit from can be found. If not defined, the current file is assumed.
- ooxml:extends is optional and refers to the implementation where is inherited
from. This implementation can be found in the same file or in another file,
depending on the optional ooxml:href attribute.
- ooxml:implements is optional and defines the name of the implementation where
other XML fragments can inherit from.
- ooxml:instantiate is mandatory. The values of this attribute are 'yes' or
'no'.
- 'yes' means that the inheritance instantiation is written to the output.
- 'no' means that the inheritance instantiation is suppressed and not written
to the output.


Example:
~~~~~~~~
This is a small example about a fictious HRM department. The generation is
invoked by the command line "msxsl in.xml transform.xsl -o out.xml"

********************************************************************************
************* file in.xml ***************
********************************************************************************


<?xml version="1.0" encoding="utf-8"?>
<hrm xmlns:ooxml="http://www.barcoview.com/ooxml/inheritance">
 <person id="1"
         ooxml:extends="defaultPerson"
         ooxml:instantiate="yes">
   <first-name>Mary</first-name>
   <last-name>Jacobs</last-name>
   <physical>
     <hair>black</hair>
   </physical>
		<vehicle id="2"/>
 </person>

 <person id="2"
         manager="yes"
         ooxml:extends="defaultPerson"
         ooxml:instantiate="yes">
   <first-name>Joe</first-name>
   <physical>
     <eyes>blue<comment>with some green spots</comment></eyes>
			<weight unit="kg">82</weight>
   </physical>
   <title>Sales Manager</title>
		<hrmRemark authorInitials="JC" date="20010929">CEO profile</hrmRemark>
 </person>

 <person id="unknown"
         manager="no"
         ooxml:implements="defaultPerson"
         ooxml:instantiate="no">
   <first-name>Unknown</first-name>
   <last-name>Unknown</last-name>
   <physical>
     <hair>Unknown</hair>
     <eyes>Unknown</eyes>
     <handicap>None</handicap>
   </physical>
   <title>Clerk</title>
		<vehicle id="unknown"/>
 </person>

 <vehicle id="1"
          ooxml:href="in2.xml"
          ooxml:extends="defaultTruck"
          ooxml:instantiate="yes">
   <licencePlate>XSL-402</licencePlate>
   <make>Volvo</make>
   <status>New</status>
   <color>Green</color>
 </vehicle>

 <vehicle ooxml:href="in2.xml"
          ooxml:extends="defaultCar"
          ooxml:instantiate="yes">
   <licencePlate>XML-008</licencePlate>
   <make>Ford</make>
   <color>Red</color>
 </vehicle>

<anyOtherNode>anyOtherNode</anyOtherNode>

</hrm>

********************************************************************************
************* file in2.xml ***************
********************************************************************************


<?xml version="1.0" encoding="utf-8"?>
<vehicleDefaults xmlns:ooxml="http://www.barcoview.com/ooxml/inheritance">
 <vehicle ooxml:implements="defaultTruck"
          ooxml:extends="defaultVehicle"
          ooxml:instantiate="yes">
	  <type>Truck</type>
	  <maxLoad>100</maxLoad>
 </vehicle>

 <vehicle ooxml:implements="defaultCar"
          ooxml:extends="defaultVehicle"
          ooxml:instantiate="yes">
	  <type>Car</type>
	  <fuel ron="95">Petrol</fuel>
 </vehicle>

 <vehicle ooxml:implements="defaultVehicle"
          ooxml:extends="defaultFuel"
          ooxml:instantiate="yes">
   <licencePlate>Unknown</licencePlate>
   <make>Unknown</make>
   <year>Unknown</year>
   <type>Unknown</type>
   <status>Used</status>
	</vehicle>

 <vehicle ooxml:implements="defaultFuel"
          ooxml:instantiate="yes" id="undefined">
	  <fuel>Gasoline</fuel>
	</vehicle>

 <anyOtherNode>Any other node from in2.xml</anyOtherNode>
</vehicleDefaults>

********************************************************************************
************* file out.xml (after reformatting) ***************
********************************************************************************


<?xml version="1.0" encoding="utf-8"?>
<hrm xmlns:ooxml="http://www.barcoview.com/ooxml/inheritance">
 <person id="1" manager="no">
   <first-name>Mary</first-name>
   <last-name>Jacobs</last-name>
   <physical>
     <hair>black</hair>
     <eyes>Unknown</eyes>
     <handicap>None</handicap>
   </physical>
   <vehicle id="2" />
   <title>Clerk</title>
 </person>

 <person id="2" manager="yes">
   <first-name>Joe</first-name>
   <physical>
     <eyes>blue
       <comment>with some green spots</comment>
     </eyes>
     <weight unit="kg">82</weight>
     <hair>Unknown</hair>
     <handicap>None</handicap>
   </physical>
   <title>Sales Manager</title>
   <hrmRemark authorInitials="JC" date="20010929">CEO profile</hrmRemark>
   <last-name>Unknown</last-name>
   <vehicle id="unknown" />
 </person>

 <vehicle id="1">
   <licencePlate>XSL-402</licencePlate>
   <make>Volvo</make>
   <status>New</status>
   <color>Green</color>
   <type>Truck</type>
   <maxLoad>100</maxLoad>
   <year>Unknown</year>
   <fuel>Gasoline</fuel>
 </vehicle>

 <vehicle id="undefined">
   <licencePlate>XML-008</licencePlate>
   <make>Ford</make>
   <color>Red</color>
   <type>Car</type>
   <fuel ron="95">Petrol</fuel>
   <year>Unknown</year>
   <status>Used</status>
 </vehicle>

 <anyOtherNode>anyOtherNode</anyOtherNode>
</hrm>

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->

<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:ooxml="http://www.barcoview.com/ooxml/inheritance"
xmlns:msxsl="urn:schemas-microsoft-com:xslt"
exclude-result-prefixes="ooxml">

<xsl:output method="xml" indent="yes" encoding="utf-8"/>

<!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->

<!--
Only inherit and instantiate nodes from the the source file where the
ooxml:instantiate attribute has the value 'yes'.
-->
<xsl:template match="*[@ooxml:instantiate='yes']">
 <xsl:variable name="result">
   <xsl:call-template name="instantiate">
     <xsl:with-param name="instance" select="."/>
   </xsl:call-template>
 </xsl:variable>
 <xsl:copy-of select="$result"/>
</xsl:template>

<!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->

<!--
Absorb all nodes from the source file where the attribute ooxml:instantiate has
the value 'no'.
-->
<xsl:template match="*[@ooxml:instantiate='no']"/>


<!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->

<!--
Identity transformation for all the other nodes with their associated
attributes. Also comments are copied to the output.

All nodes from the source file without an attribute ooxml:instantiate or with
an attribute ooxml:instantiate with values different from 'yes' or 'no' are
copied to the output without any further processing.
-->
<xsl:template match="@*|*|comment()">
<xsl:copy>
<xsl:apply-templates select="@*|node()|comment()"/>
</xsl:copy>
</xsl:template>


<!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->

<!--
Instantiate the selected node with single inheritance.
The inheritance tree is traveled from an arbitrary child of the inheritance
tree till the root. A leaf is represented by an XML element with the attribute
ooxml:instantiate = 'yes' and possibly the ooxml:extends and ooxml:href
attributes witch refers to an implementation to inherit from.
-->
<xsl:template name="instantiate">
<xsl:param name="instance"/>


 <xsl:variable name="temp">
   <xsl:choose>
     <!--
     Is there an ooxml:href attribute associated with the ooxml:extends
     attribute definition?
     -->
     <xsl:when test="string-length($instance/@ooxml:href) &gt; 0">
       <!--
       Yes, there is an ooxml:href attribute. Retrieve the extending XML
       fragment from this file.
       -->
       <xsl:call-template name="inherit">
         <xsl:with-param name="extends" select=
"document($instance/@ooxml:href)//*[@ooxml:implements=$instance/@ooxml:extends]"/>
         <xsl:with-param name="implements" select="$instance"/>
       </xsl:call-template>
     </xsl:when>
     <xsl:otherwise>
       <!--
       No, there is no ooxml:href attribute. Retrieve the extending XML
       fragment the current file.
       -->
       <xsl:call-template name="inherit">
         <xsl:with-param name="extends"
              select="//*[@ooxml:implements = $instance/@ooxml:extends]"/>
         <xsl:with-param name="implements" select="$instance"/>
       </xsl:call-template>
     </xsl:otherwise>
   </xsl:choose>
 </xsl:variable>

<!--
Convert $temp to a node-set.
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
This is the only MSXML3 dependency. This isn't too bad because an equivalent
functionality is found in almost any 1.0 compliant XSLT processor.
This is not needed for 1.1 and future higher XSLT processor implementations.
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
-->
<xsl:variable name="result" select="msxsl:node-set($temp)"/>


<xsl:choose>
<!--
Still another extension of the inheritance tree to process?
-->
<xsl:when test="string-length($result/*/@ooxml:extends) &gt; 0">
<!--
Yes. Recurse until the inheritance root is reached. The root is an XML
fragment without an ooxml:extends attribute or with an empty
ooxml:extends attribute.
-->
<xsl:call-template name="instantiate">
<xsl:with-param name="instance" select="$result/*"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<!--
No. Stop recursion and copy the result from the inheritance instantiation
to the output.
-->
<xsl:element name="{name()}">
<!--
Suppress all ooxml attributes to the output.
-->
<xsl:for-each select=
"$result/*/@*[namespace-uri()!='http://www.barcoview.com/ooxml/inheritance']">
<xsl:attribute name="{name()}">
<xsl:value-of select="."/>
</xsl:attribute>
</xsl:for-each>
<xsl:copy-of select="$result/*/*"/>
</xsl:element>
</xsl:otherwise>
</xsl:choose>
</xsl:template>


<!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->

<!--
The inherit template implements one inheritance resolution step of the complete
inheritance chain resolution process.
-->
<xsl:template name="inherit">
<!--
The inherit function receives two parameters:
- $extends holds the current node where is inherited from at this stage of
the inheritance resolution process.
- $implements holds the current instance of the inheritance as processed at
this stage of the recursive invocation.
-->
<xsl:param name="extends"/>
<xsl:param name="implements"/>
<xsl:choose>
<!--
Has $extends any children?
-->
<xsl:when test="$extends[*]">
<!--
Yes, $extends has one or more child elements. Only copy the root element
from $extends with some selected attributes to keep the inheritance chain
intact.
-->
<xsl:element name="{name()}">
<!--
If the current $extends has an ooxml:extends attribute, meaning that
the inheritance chain continuous, get this ooxml:extends attribute
together with the associated optional ooxml:href attribute. If not, the
inheritance chain would be broken.
The ooxml:implements and ooxml:instantiate attributes are explicit
copied. All other attributes are copied.
-->
<xsl:for-each select=
"$extends/@*[not(name()=ooxml:implements and name()=ooxml:instantiate)]">
<xsl:attribute name="{name()}">
<xsl:value-of select="."/>
</xsl:attribute>
</xsl:for-each>


<!--
If there is none or an empty $extends/@ooxml:href attribute, use the
$implements/@ooxml:href attribute, if available to keep track of the
current referenced file.
-->
<xsl:if test=
"string-length($extends/@ooxml:href)=0 and string-length($implements/@ooxml:href)!=0">
<xsl:attribute name="ooxml:href">
<xsl:value-of select="$implements/@ooxml:href"/>
</xsl:attribute>
</xsl:if>


<!--
Copy all the none ooxml attributes from the root element from
$implements. Please, keep in mind that earlier copied attributes from
the root element of $extends with the same names as the $implements
root element attributes are overwritten. This approach implements
the inheritance resolution of the attributes.
-->
<xsl:for-each select=
"$implements/@*[namespace-uri()!='http://www.barcoview.com/ooxml/inheritance']">
<xsl:attribute name="{name()}">
<xsl:value-of select="."/>
</xsl:attribute>
</xsl:for-each>


       <!--
       Copy the text node from the current instantiation.
       -->
       <xsl:copy-of select="$implements/text()"/>

       <!--
       Get all children elements with their descendants for the current
       instantiation so far. This is implemented by invoking inherit
       recursively until all descendants of a child are processed. This
       process is repeated for each child.
       -->
       <xsl:for-each select="$implements/*">
         <xsl:call-template name="inherit">
           <xsl:with-param name="extends"
                select="$extends/*[name()=name(current())]"/>
           <xsl:with-param name="implements" select="."/>
         </xsl:call-template>
       </xsl:for-each>

<!--
Get all children elements with their descendants from the extending
XML fragment, on the condition that these elements with the same name
don't occur in the instantiation so far. This is implemented by copying
all these children with their descendants to the instantiation.
-->
<xsl:for-each select="$extends/*">
<xsl:if test="not($implements/*[name()=name(current())])">
<xsl:copy-of select="."/>
</xsl:if>
</xsl:for-each>
</xsl:element>
</xsl:when>
<xsl:otherwise>
<!--
No, $extends has no child elements any more. Stop the recursion and
return the result of this stage of the inheritance resolution process.
-->
<xsl:element name="{name()}">
<xsl:for-each select="$extends/@*">
<xsl:attribute name="{name()}">
<xsl:value-of select="."/>
</xsl:attribute>
</xsl:for-each>
<xsl:for-each select="$implements/@*">
<xsl:attribute name="{name()}">
<xsl:value-of select="."/>
</xsl:attribute>
</xsl:for-each>
<xsl:copy-of select="$implements/* | $implements/text()"/>
</xsl:element>
</xsl:otherwise>
</xsl:choose>
</xsl:template>


<!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->

</xsl:stylesheet>


_________________________________________________________________ Get your FREE download of MSN Explorer at http://explorer.msn.com/intl.asp


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




Current Thread
Keywords