[xsl] Run-time polymorphism in XSL 1.0

Subject: [xsl] Run-time polymorphism in XSL 1.0
From: "Andrey Basko" <andrey_basko@xxxxxxxxxxxxxxxx>
Date: Sun, 20 Mar 2005 18:51:21 -0500
Hi, All!

Some time ago I tried to find reliable approach how to implement run-time polymorphism in XSL 1.0. Initially I used inbuilt polymorphism-like template overriding mechanism based on import precedence. However I encountered the issues described in: ("Reliance on import precedence considered dangerous" http://sources.redhat.com/ml/xsl-list/2001-02/msg00996.html, and http://sources.redhat.com/ml/xsl-list/2001-02/msg01003.html).

In XSL forums I found several messages that mentioned how to implement polymorphism in XSL 1.0 based on generic templates proposed by Dmity Novatchev (http://lists.fourthought.com/pipermail/exslt/2001-May/000169.html).

Eventually I came up with some approach that I would like to share with the XSL community. The idea how to implement run-time polymorphism in XSL is similar to approach used in generic templates:

1. Declare special customized data structures (under non-xsl namespace, embedded in the stylesheet) which define:
a. Virtual methods in logically defined class.
b. Virtual methods table variable by inheriting methods from virtual methods table of base "class".

2. Pass reference to virtual methods table to all virtual methods as a parameter.

Below I describe several "how to" steps in order to explain how to declare two classes: Base "class" and Child "class" that inherits from Base "class".

1. How to declare Base class.

a. Declare Base class namespace:

<xsl:stylesheet [...] xmlns:Base="Base">

b. Declare Base class methods and virtual table:

  <Base:Method1 method="Method1"/>
  <Base:Method2 method="Method2"/>
</ Base:class>

<xsl:variable name="Base:class" select="document('')// Base:class"/>
<xsl:variable name="Base:vtable" select="$Base:class/*"/>

The variable "$Base:vtable" is a virtual table for Base class.

2. How to declare Base class method handlers.

<xsl:template match="Base:Method1">
  <xsl:param name="this"/>
  <xsl:param name="vtable"/>
  <!-- code is here -->

Each method should declare "vtable" parameter. The "this" parameter is supposed to be used as a node reference to real XML data.

3. How to declare Child class

a. Declare Base and Child class namespaces:

<xsl:stylesheet [...] xmlns:Child="Child" xmlns:Base="Base">

b. Use <xsl:include> if necessary:

<xsl:include href="Base.xsl"/>

c. Declare Child class methods and inherit virtual methods from Base class:

  <Child:Method1 method="Method1"/>
  <Child:NewMethod3 method="NewMethod3"/>

<xsl:variable name="Child:class" select="document('')//Child:class"/>
<xsl:variable name="Child:BaseMethods" select="$Base:vtable[not(@method=$Child:class/*/@method)]"/>

<xsl:variable name="Child:vtable" select="$Child:class/* | $Child:BaseMethods"/>

Variable "$Child:BaseMethods" contains only those methods that are declared in the Base class but not declared in the Child class.

So, each class declares its own virtual table that is accessible by "${ClassNamespace}:vtable" variable. In this example virtual tables are: "$Base:vtable" and "$Child:vtable".

4. How to make virtual method call:

<xsl:template match="Base:Method1">
  <xsl:param name="this"/>
  <xsl:param name="vtable"/>

  <xsl:apply-templates select="$vtable[local-name()='Method2']">
     <xsl:with-param name="this" select="$this"/>
     <xsl:with-param name="vtable" select="$vtable"/>
     <!-- Other Method2 parameters -->

5. How to call base class methods from child classes:

<xsl:template match="Child:Method1"> <!-- declaration of Child:Method1 handler -->
<xsl:param name="this"/>
<xsl:param name="vtable"/>
<xsl:apply-templates select="$Base:vtable[local-name()='Method1']">
<xsl:with-param name="this" select="$this"/>
<xsl:with-param name="vtable" select="$vtable"/>

It means that in order to call base class methods you need to select method from concrete class virtual table. But it is still necessary to pass source $vtable parameter to it.

6. How to define special handler for abstract methods:

It is easy to force overriding of all abstract methods by declaring the following template in corresponding class (in this case it is Base class):

<xsl:template match="*[namespace-uri()='Base']" priority="-1">
<xsl:message terminate="yes">
Abstract method "Base:<xsl:value-of select="local-name()"/>" was called

Hope somebody will enjoy this approach while developing with XSL.

Best Regards,
Andrey Basko

Current Thread