This is the mail archive of the xsl-list@mulberrytech.com mailing list .


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]

The notion of inheritance - An implementation


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


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]