Object-Based Extensions in XSL Transformations
Version 1.0 (Draft)
6 October 2024

Abstract

This specification defines an extension of XSL Transformations (XSLT) . It describes a set of XSLT extension elements and functions, designed to access, in the platform-independent way, programming resources implemented using various object-based technologies.

The main goal of this specification is to provide the universal extensibility mechanism for XSLT and to support interoperability between XSLT and various existing object-based technologies.

Status of this document

This is the second working draft of the specification. It was developed as part of Unicorn XSL World (UXSL) programme.

Comments on this specification may be sent to [email protected]

Table of contents

Appendices


1 Introduction

This specification defines an extension of XSL Transformations (XSLT). It describes a set of XSLT extension elements and functions, designed to access, in the platform-independent way, programming resources implemented using various object-based technologies.

The main goal of this specification is to provide the universal extensibility mechanism for XSLT and to support interoperability between XSLT and various existing object-based technologies.

Extension elements and functions defined by this specification belong to specific namespaces and are integrated into XSLT transformation environment using the extension mechanism specified in W3C XSLT recommendation.

Extension elements defined by this specification may be embedded in XSLT stylesheets and intermixed with the standard XSLT instructions. The special version of XSLT processor is required to process such stylesheets. When an instruction corresponding to the object-based extension element is encountered, it is instantiated as described in this specification. During the instantiation, the appropriate object access operations are performed.

This specification also defines the set of extension functions that perform various object access operations. These functions may be used in XSLT expressions and attribute value templates in all contexts where functions are allowed. Values returned by object access extension functions may be inserted into the generated content or used as intermediate parameters during the transformation.

2 Abstract Object Model

According to this specification, XSLT stylesheets can access extension objects that belong to abstract object worlds. Object worlds represent distinct dynamic collections of objects and can be implemented using various object-based technologies (e.g., ECMAScript, C++, Java, COM, CORBA, etc). The implementation of each particular object world is hidden from XSLT stylesheet authors. All object worlds expose their interfaces in the abstract, platform-independent way.

Each object world represents a collection of objects. Some objects might have unique object names, represented as Unicode strings. These names can be used to reference objects in XSLT stylesheets. For each object world there must be an object with the name equal to an empty string. This object is called the global object.

All objects may be referenced in XSLT stylesheets using the dynamic object references. These references can be obtained during XSLT processing as results of operations performed on other objects.

To hold dynamic object references in XSLT, the extension object reference data type is introduced. Values of this type can be returned by extension functions defined by this specification, stored as values of XSLT variables and used in XSLT expressions. There are strict limitations on the use of object reference values in expressions. Object reference values cannot be converted to the other data types, neither explicitly, nor implicitly. As a consequence, they cannot be placed into generated content (e.g., using the xsl:value-of instruction). The typical scenario assumes that object reference values are obtained as results of calling object-based extension functions, and then are supplied as parameters to various object-based extension instructions and functions, probably being stored using XSLT variables or parameters.

Null object references are not allowed by this specification.

For each object there exists a collection of object properties. Each property has a name. The property name is a Unicode string. Different properties of the same object must have different names.

Each property of each object must be a value property, or a function property, or both.

A property is treated as the value property, if it has the certain value associated with it. The value must have one of the following data types: boolean, number, string or object reference. Node-set property values are not allowed by this specification.

Three abstract operations are defined for value properties: get property, put property and delete property.

The get property operation is used to obtain the current value of the certain property of the certain object. The operation fails if there if no such object, or if the object has no such property.

The put property operation is used to assign a new value to the certain property of the certain object. The property is not required to be assigned to that object prior to the operation. The put property operation might be forbidden for some properties of some objects; in such cases the put property operation will fail.

The delete property operation is used to delete the certain property of the certain object. If the property was not assigned to that object prior to the operation, nothing happens. The delete property operation might be forbidden for some properties of some objects; in such cases the delete property operation will fail.

The property is treated as the function property, if it has a certain function associated with it. Functions can be invoked using the call function abstract operation. Zero or more parameter values may be passed when the function is invoked. Parameter values must have one of the following data types: boolean, number, string or object reference. Node-set parameter values are not allowed by this specification. When the function which is a property of a certain object is invoked, reference to this object is passed to the function as an implicit argument.

Each function must return a single value containing the result of the function call. This value must have one of the following data types: boolean, number, string or object reference. Node-set return values are not allowed by this specification. If the result value is irrelevant, the function may return an empty string. Function calls may cause various side effects. In particular, objects in the corresponding object world can be created or deleted and properties of some objects may be created, modified or deleted as the side effect of a function call.

For each object world the individual syntax rules may be specified for object and property names. It is required by this specification that any object world must allow object and property names that consist of the sequences of upper-case Latin letters from A to Z, lower-case Latin letters from a to z and decimal digits from 0 to 9, the first character being a letter. Up to 31 character must be allowed in the name string.

For each object world, binding of the abstract interface to the underlying platform should be specified individually. This specification describes binding for two object worlds, one based on the ECMAScript platform, and another based on the Microsoft ActiveX Automation platform. Bindings for other object worlds may be defined in the following editions of this specification or in separate documents.

It is important to note that each object world might support the specific set of the native data types. The native data types must be, however, completely hidden from XSLT processor. When a value is passed from an object world to an XSLT stylesheet, the native data type must be mapped to one of the valid data types: boolean, number, string or object reference. When a value is passed from an XSLT stylesheet to an object world, the value type must be mapped to the appropriate native data type supported by this object world. Bindings for each object world must specify the exact mapping rules.

Some object worlds may support the aggregate native data types (e.g., arrays, structures, etc), which cannot be naturally mapped to simple data types like boolean, number or string. It is possible, however, to represent values of these types logically as object references, providing value and function properties to access components of the aggregate values. These objects might be handled by the corresponding object world bindings together with the "true" native objects. From the point of view of the stylesheet author, there must be no difference between the "true" native object and the native aggregate data type implemented as the logical object.

For each object world, one or more scripting languages may be specified. The scripting language allows creation of scripts. Scripts describe sequences of operations performed on objects which belong to corresponding object worlds. It is assumed that scripts can be downloaded and interpreted dynamically. Bindings for each object world must specify, which scripting languages are supported.

According to this specification, scripts are stored as separate resources. At certain points, these scripts can be imported (downloaded and interpreted) by XSLT processor. This specification does not allow embedding scripts directly into XSLT stylesheets.

3 Extension Namespaces

For each object world a unique extension namespace must be reserved. For each particular object world, the binding specification must define the appropriate extension namespace URI.

In this specification, the prefix obj: is used for referring to object-based extension elements and functions. Authors of XSLT stylesheets are free to use any other prefixes, provided that the appropriate namespace declarations are present in the stylesheets.

4 Extension Elements

Extension elements defined by this specification are used to implement various object access operations. They may be embedded in XSLT stylesheets and intermixed with the standard XSLT instructions.

The following extension elements implement extension instructions:

  • obj:import
  • obj:get
  • obj:put
  • obj:delete
  • obj:call
  • obj:while

When any of these instructions is encountered, it is instantiated as described in this specification. During the instantiation, the transformation engine performs appropriate object access operations.

The following extension element is not treated as instruction:

  • obj:with-param

This element is used to supply additional information for object access extension instructions.

4.1 Import a Script

The obj:import extension element can be used to load and execute scripts. Scripts define and initialize objects and their properties, which can be later accessed by XSLT stylesheets.

The obj:import extension element has the following syntax:

<obj:import
  href = uri-reference />

The required attribute href is used to specify the URI reference to the resource containing a script. Relative URI references are resolved using the base URI of the obj:import stylesheet element.

It is an error if the specified script does not exist or if errors were encountered during the script processing.

4.2 Get Value Property

The obj:get extension element can be used to obtain a value of a property of an object, and put it to the generated content.

The obj:get extension element has the following syntax:

<obj:get
  object = expression
  property = { string } />

The optional object attribute is an expression. If this expression evaluates to the object reference, the corresponding object is used. Otherwise the result of expression evaluation is converted to a string as if by the call to the XPath string function and the conversion result is interpreted as an object name. If this attribute is missing, or the object name is an empty string, the global object is assumed.

The required property attribute is an attribute value template, which should evaluate to a string containing the property name.

The property value is converted to a string as if by the call to the XPath string function. The text node is created in the result tree. The converted property value is assigned as a string-value to this text node. If conversion result is an empty string, no text node is created. The created text node will be merged with any adjacent text nodes. It is an error if the property value has the object reference type.

It is an error if the specified object does not exist or does not belong to the given object world or has no specified property. It is also an error if the specified property of the specified object is not a value property.

4.3 Put Value Property

The obj:put extension element can be used to assign a value to a property of an object.

The obj:put extension element has the following syntax:

<obj:put
  object = expression
  property = { string }
  value = expression />

The optional object attribute is an expression. If this expression evaluates to the object reference, the corresponding object is used. Otherwise the result of expression evaluation is converted to a string as if by the call to the XPath string function and the conversion result is interpreted as an object name. If this attribute is missing, or the object name is an empty string, the global object is assumed.

The required property attribute is an attribute value template, which should evaluate to a string containing the property name.

The required value attribute is an expression. This expression is evaluated; the resulting value must be of boolean, number, string or object reference type. The resulting value is assigned to the specified property of the specified object. If the specified property does not exist, it is created.

It is an error if the specified object does not exist or does not belong to the given object world. It is an error if the put operation is not allowed for the specified property of the specified object.

4.4 Delete Property

The obj:delete extension element can be used to delete a property of an object.

The obj:delete extension element has the following syntax:

<obj:delete
  object = expression
  property = { string } />

The optional object attribute is an expression. If this expression evaluates to the object reference, the corresponding object is used. Otherwise the result of expression evaluation is converted to a string as if by the call to the XPath string function and the conversion result is interpreted as an object name. If this attribute is missing, or the object name is an empty string, the global object is assumed.

The required property attribute is an attribute value template, which should evaluate to a string containing the property name.

It is an error if the specified object does not exist or does not belong to the given object world. It is not an error if the specified object has no specified property; in this case the delete request is silently ignored. It is an error if the delete operation is not allowed for the specified property of the specified object.

4.5 Call Function Property

The obj:call extension element can be used to call a function property of an object, with or without arguments, and to put result of the call to the generated content.

The obj:call extension element has the following syntax:

<obj:call
  object = expression
  property = { string }>
  <!-- Content: (obj:with-param*) -->
</obj:call>

The optional object attribute is an expression. If this expression evaluates to the object reference, the corresponding object is used. Otherwise the result of expression evaluation is converted to a string as if by the call to the XPath string function and the conversion result is interpreted as an object name. If this attribute is missing, or the object name is an empty string, the global object is assumed.

The required property attribute is an attribute value template, which should evaluate to a string containing the function property name.

If necessary, actual parameter values may be specified using obj:with-param children of the obj:call element. The order of obj:with-param children elements is significant and must correspond to the order of parameters.

The specified function is called; the returned value is converted to a string as if by the call to the XPath string function. The text node is created in the result tree. The converted resulting value is assigned as a string-value to this text node. If conversion result is an empty string, no text node is created. The created text node will be merged with any adjacent text nodes. It is an error if the value returned by the function call has the object reference type.

It is an error if the specified object does not exist or does not belong to the given object world or has no specified property. It is also an error if the specified property of the specified object is not a function.

4.6 Call Function Property in a Loop

The obj:while extension element can be used to call repeatedly a function property of an object and to instantiate the element content while the result returned by the function is true.

The obj:while extension element has the following syntax:

<obj:while
  object = expression
  property = { string }>
  <!-- Content: (obj:with-param*, template) -->
</obj:while>

The optional object attribute is an expression. If this expression evaluates to the object reference, the corresponding object is used. Otherwise the result of expression evaluation is converted to a string as if by the call to the XPath string function and the conversion result is interpreted as an object name. If this attribute is missing, or the object name is an empty string, the global object is assumed.

The required property attribute is an attribute value template, which should evaluate to a string containing the function property name.

If necessary, actual parameter values may be specified using obj:with-param children of the obj:while element. The order of obj:with-param children elements is significant and must correspond to the order of parameters.

The specified function is called; the returned value is converted to a boolean as if by the call to the XPath boolean function. If the converted result value if equal to false, the instantiation of the obj:while element at finished at this point. Otherwise the template that forms content of the obj:while element is instantiated, and the function call is repeated again. It is an error if the value returned by the function call has the object reference type.

It is an error if the specified object does not exist or does not belong to the given object world or has no specified property. It is also an error if the specified property of the specified object is not a function.

4.7 Parameter Values

The obj:with-param extension element can be used to specify actual parameter values for function calls. The element must be a child of the obj:call or obj:while element. The order of obj:with-param children elements is significant and must correspond to the order of parameters.

The obj:with-param extension element has the following syntax:

<obj:with-param
  value = expression />

The required value attribute is an expression. This expression is evaluated; the resulting value must be of boolean, number, string or object reference type. The resulting value is passed as the parameter to the corresponding function call.

5 Extension Functions

Extension functions may appear in XSLT expressions and attribute value templates in all contexts where functions are allowed. These functions are typically used to obtain results of object access operations.

5.1 Get Value Property

The obj:get function provides an alternative way to obtain the object property value.

The obj:get function has the following syntax:

any obj:get(any, string)

Both arguments are required. If the first argument is an object reference, the corresponding object is used. Otherwise the argument value is converted to a string as if by the call to the XPath string function and the conversion result is interpreted as an object name. If the first argument is equal to an empty string, the global object is assumed. The second argument is converted to a string as if by the call to the XPath string function and the conversion result is interpreted as a property name.

The obj:get function returns the value of the specified property of the specified object.

It is an error if the specified object does not exist or does not belong to the given object world or has no specified property. It is also an error if the specified property of the specified object is not a value property.

5.2 Call Function Property

The obj:call function provides an alternative way to call the function property.

The obj:call function has the following syntax:

any obj:call(any, string, any*)

First two arguments are required. If the first argument is an object reference, the corresponding object is used. Otherwise the argument value is converted to a string as if by the call to the XPath string function and the conversion result is interpreted as an object name. If the first argument is equal to an empty string, the global object is assumed. The second argument is converted to a string as if by the call to the XPath string function and the conversion result is interpreted as a property name.

Additional arguments, if present, specify the actual parameter values that must be passed to the function. Each parameter value must be of boolean, number, string or object reference type.

The obj:call function calls the specified function property of the specified object and returns the value equal to the function call result.

It is an error if the specified object does not exist or does not belong to the given object world or has no specified property. It is also an error if the specified property of the specified object is not a function.

6 ECMAScript Object World

In this section bindings for the ECMAScript object world are specified.

The extension namespace for the ECMAScript object world is

https://www.unicorn-enterprises.com/XSLT/Extensions/ECMAScript/1.0

A script in the ECMAScript object world must contain code corresponding to the Program production defined in ECMA-262. When the script is imported by XSLT stylesheet, the contained program is executed; results of the script execution may affect the subsequent stylesheet processing.

All objects of the ECMAScript object world correspond to ECMAScript objects. The global object of the object world corresponds to the ECMAScript global object. All named objects of the object world correspond to the object properties of the global object, with the names equal to the names of corresponding properties. For instance, in the ECMAScript object world the String and Date objects are predefined, and correspond to the String and Date properties of the global object maintained by the ECMAScript interpreter.

All properties of ECMAScript objects are treated as value properties in the ECMAScript object world. Furthermore, properties of ECMAScript objects that are ECMAScript functions are treated also as function properties in the ECMAScript object world.

When values are passed from XSLT stylesheets to the ECMAScript object world, the value types are mapped as follows:

    XSLT                    ECMAScript
   ---------------------------------------------
    boolean                 boolean
    number                  number
    string                  string
    object reference        object

When values are passed from the ECMAScript object world to XSLT stylesheets, the value types are mapped as follows:

    ECMAScript              XSLT
   ---------------------------------------------
    undefined               empty string
    null                    empty string
    boolean                 boolean
    number                  number
    string                  string
    object                  see prose

When passed from the ECMAScript object world to an XSLT stylesheet, each value having ECMAScript object type and the value of the internal [[Class]] property of this object equal to one of "String", "Boolean", "Number" or "Date", is substituted with the value of the internal [[Value]] property of this object; the former value is mapped according to the scheme defined above. All other ECMAScript object values are mapped to object references.

The property get, property put, property delete and function call operations of the ECMAScript object world are based on the internal ECMAScript [[Get]], [[Put]], [[Delete]] and [[Call]] properties respectively.

7 Automation Object World

In this section object world bindings for Microsoft ActiveX Automation are specified.

The extension namespace for the Automation object world is

https://www.unicorn-enterprises.com/XSLT/Extensions/COM/1.0

Objects in the Automation object world correspond to the dispatch interface pointers. The only exception is the global object which encapsulates the certain functionality of the Automation platform itself. Except the global object, there are no other named objects in the Automation object world.

The global object cannot have value properties. It may have, however, the unspecified number of function properties. Names of the global object function properties are interpreted as program identifiers (ProgID) of available automation components. Calling the function property will create an instance of the automation object based on the corresponding program identifier, as if with the call to the CoCreateInstance defined by Win32 SDK. The object reference encapsulating the dispatch interface of the created object is returned as the function call result.

All other objects may have value and function properties. Value properties correspond to the properties exposed by the corresponding dispatch interface. Function properties correspond to the methods exposed by the corresponding dispatch interface.

When values are passed from XSLT stylesheets to the Automation object world, the value types are mapped as follows:

    XSLT                    Automation
   ---------------------------------------------
    boolean                 VT_BOOL
    number                  VT_R8
    string                  VT_BSTR
    object reference        VT_DISPATCH

When values are passed from the Automation object world to XSLT stylesheets, the value types are mapped as follows:

    Automation              XSLT
   ---------------------------------------------
    VT_R4                   number
    VT_R8                   number
    VT_I4                   number
    VT_I2                   number
    VT_BOOL                 boolean
    VT_BSTR                 string
    VT_DISPATCH             object reference

Automation values of other types are mapped to XSLT empty string when passed from the Automation object World to XSLT stylesheets.

No support is currently provided for complex automation data types: variants and safe arrays. There is also no support for enumerator objects that can be used to iterate through collections (however, collections can be enumerated using the standard Count property and Item method). The support for complex data types and enumerators may be introduced in the following editions of this specification.

This specification does not define any scripting languages for the Automation object world. It is assumed that the scripting of automation objects can be supported via ECMAScript object world, in case the corresponding ECMAScript engine implements support for automation.

8 Notation

The specification of each object-based extension element contains a summary of its syntax. The notation is similar to that used in XSL Transformations (XSLT) Version 1.0 recommendation.

The names of required attributes are given in bold. Strings that occur in place of attribute values specify the value type of those attributes. Strings surrounded by curly braces indicate that the corresponding attribute values are interpreted as attribute value templates. Elements allowed not to be empty contain comments specifying the allowed content.


Appendices

A References

[ECMA-262] ECMA. Standard ECMA-262 2nd Edition - August 1998 ECMAScript Language Specification. See http://www.ecma.ch
[XPath] World Wide Web Consortium. XML Path Language (XPath) Version 1.0. W3C Recommendation. See http://www.w3.org/TR/1999/REC-xpath-19991116
[XSLT] World Wide Web Consortium. XSL Transformations (XSLT) Version 1.0. W3C Recommendation. See http://www.w3.org/TR/1999/REC-xslt-19991116

B Element Syntax Summary

<obj:import
  href = uri-reference />

<obj:get
  object = expression
  property = { string } />

<obj:put
  object = expression
  property = { string }
  value = expression />

<obj:delete
  object = expression
  property = { string } />

<obj:call
  object = expression
  property = { string }>
  <!-- Content: (obj:with-param*) -->
</obj:call>

<obj:while
  object = expression
  property = { string }>
  <!-- Content: (obj:with-param*, template) -->
</obj:while>

<obj:with-param
  value = expression />

C Reference Implementation

The software product developed by Unicorn Enterprises SA, Unicorn XSLT Processor, Professional Edition serves as the reference implementation for this specification.

The recent version of this product is available at https://www.unicorn-enterprises.com .

D Examples

This section contains fragments of XSLT stylesheets that demonstrate usage of object-based extension elements defined in this specification. The full working versions of these and other examples can be found in the recent distribution of Unicorn XSLT Processor, Professional Edition available at https://www.unicorn-enterprises.com .

The first four examples demonstrate object-based extensions implemented using the ECMAScript object world. The last two examples are more sophisticated; they show how the external applications can be controlled from XSLT stylesheets using interfaces to the Automation object world.

Example 1.

This example demonstrates how built-in and user-defined ECMAScript objects can be accessed from XSLT sthylesheet.

Following is the XSLT stylesheet (note the additional single quotation marks surrounding values of the object attribute):

<?xml version="1.0"?>
<xsl:stylesheet 
    version="1.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:obj="https://www.unicorn-enterprises.com/XSLT/Extensions/ECMAScript/1.0"
    extension-element-prefixes="obj">
  <xsl:output
      method="xml"
      indent="yes"
      encoding="UTF-8"/>
  <xsl:template match='/'>
    <obj:import href="demo01.es"/>
    <html>
      <head>
        <title>New Product</title>
      </head>
      <body>
        <xsl:apply-templates/>
      </body>
    </html>
  </xsl:template>
  <xsl:template match="head">
    <h3><xsl:apply-templates/></h3>
  </xsl:template>
  <xsl:template match="p">
    <p><xsl:apply-templates/></p>
  </xsl:template>
  <xsl:template match="date">
    <em><obj:call property="Date"/></em>
  </xsl:template>
  <xsl:template match="product-name">
    <obj:get object="'Product'" property="name"/>
  </xsl:template>
  <xsl:template match="product-vendor">
    <obj:get object="'Product'" property="vendor"/>
  </xsl:template>
  <xsl:template match="product-platform">
    <obj:get object="'Product'" property="platform"/>
  </xsl:template>
  <xsl:template match="product-language">
    <obj:get object="'Product'" property="language"/>
  </xsl:template>
</xsl:stylesheet>

Following is the script demo01.es

  var Product = new Object();
  Product.name = "Unicorn XSLT Processor, Professional Edition";
  Product.vendor = "Unicorn Enterprises SA";
  Product.platform = "Windows NT 4.0";
  Product.language = "C++";

Following is the example of a source XML document.

<?xml version="1.0"?>
<root>
  <div>
    <head>What is New</head>
    <p><date/></p>
    <p><product-vendor/> announced availability of <product-name/>.</p>
    <p>The product is implemented in <product-language/> 
      and is running on <product-platform/>.</p>
  </div>
</root>

Example 2.

This example demonstrates how object-based extensions can be used to implement simple text processing functions that are not directly available in XSLT. The following stylesheet reads the source XML document and inserts '\' character before each occurrence of '.', ';' and '\' characters in the content of all <data> elements.

<?xml version="1.0"?>
<xsl:stylesheet 
    version="1.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:obj="https://www.unicorn-enterprises.com/XSLT/Extensions/ECMAScript/1.0"
    extension-element-prefixes="obj">
  <xsl:output
      method="xml"
      indent="yes"
      encoding="UTF-8"/>
  <xsl:strip-space elements="*"/>
  <xsl:template match='/'>
    <obj:import href="demo02.es"/>
    <html>
      <head>
        <title>Abrakadabra</title>
      </head>
      <body>
        <xsl:apply-templates/>
      </body>
    </html>
  </xsl:template>
  <xsl:template match="data">
    <p>
      <obj:call property="convert">
        <obj:with-param value="string(.)"/>
      </obj:call>
    </p>
  </xsl:template>
</xsl:stylesheet>

Following is the script demo02.es

  function convert(source) {
      var result = "";
      for (var i = 0; i < source.length; i++) {
          var ch = source.charAt(i);
          if (ch == "." || ch == ";" || ch == "\\")
              result += "\\";
          result += ch;
          }
      return result;
      }

Following is the example of a source XML document.

<?xml version="1.0"?>
<root>
  <data>Hello; \\World.</data>
  <data>The. quick \brown; fox \jumps over... the;; \lazy dog;</data>
  <data>The \five; boxing. wizards; \jump quickly.</data>
</root>

Example 3.

This example demonstrates how object-based extensions can be used to accumulate and output statistics based on data extracted from the multiple elements of the source XML document.

Following is the XSLT stylesheet:

<?xml version="1.0"?>
<xsl:stylesheet 
    version="1.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:obj="https://www.unicorn-enterprises.com/XSLT/Extensions/ECMAScript/1.0"
    extension-element-prefixes="obj">
  <xsl:output
      method="xml"
      indent="yes"
      encoding="UTF-8"/>
  <xsl:strip-space elements="*"/>
  <xsl:template match='/'>
    <obj:import href="demo03.es"/>
    <html>
      <head>
        <title>Sales Statistics</title>
      </head>
      <body>
        <obj:call object="'Stat'" property="reset"/>
        <xsl:apply-templates/>
        <p>Sales total: USD <obj:get object="'Stat'" property="total"/></p>
        <p>Best results: <obj:get object="'Stat'" property="maxMonth"/>
          (USD <obj:get object="'Stat'" property="maxValue"/>)</p>
        <p>Worst results: <obj:get object="'Stat'" property="minMonth"/>
          (USD <obj:get object="'Stat'" property="minValue"/>)</p>
      </body>
    </html>
  </xsl:template>
  <xsl:template match="sales">
    <obj:call object="'Stat'" property="store">
    <!-- Note the 'string()' function: -->
      <obj:with-param value="string(month)"/>
      <obj:with-param value="string(value)"/>
    </obj:call>
  </xsl:template>
</xsl:stylesheet>

Following is the script demo03.es

  function Stat_reset() {
      this.total = 0;
      this.count = 0;
      this.maxMonth = null;
      this.minMonth = null;
      this.maxValue = 0;
      this.minValue = 0;
      }
  function Stat_store(month, value) {
      var temp = Number(value);
      this.total += temp;
      this.count++;
      if (this.maxMonth == null || temp > this.maxValue) {
          this.maxMonth = month;
          this.maxValue = temp;
          }
      if (this.minMonth == null || temp < this.minValue) {
          this.minMonth = month;
          this.minValue = temp;
          }
      }
  var Stat = new Object();
  Stat.reset = Stat_reset;
  Stat.store = Stat_store;

Following is the example of a source XML document.

<?xml version="1.0"?>
<root>
  <sales>
    <month>January</month>
    <value>235500</value>
  </sales>
  <sales>
    <month>February</month>
    <value>176700</value>
  </sales>
  <sales>
    <month>March</month>
    <value>198000</value>
  </sales>
  <sales>
    <month>April</month>
    <value>257600</value>
  </sales>
  <sales>
    <month>May</month>
    <value>288900</value>
  </sales>
  <sales>
    <month>June</month>
    <value>223100</value>
  </sales>
  <sales>
    <month>July</month>
    <value>181200</value>
  </sales>
  <sales>
    <month>August</month>
    <value>151400</value>
  </sales>
  <sales>
    <month>September</month>
    <value>318000</value>
  </sales>
  <sales>
    <month>October</month>
    <value>354700</value>
  </sales>
  <sales>
    <month>November</month>
    <value>343200</value>
  </sales>
  <sales>
    <month>December</month>
    <value>321800</value>
  </sales>
</root>

Example 4.

This example demonstrates how iterators can be implemented using object-based extensions. The following stylesheet generates a table of values for two math functions.

<?xml version="1.0"?>
<xsl:stylesheet 
    version="1.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:obj="https://www.unicorn-enterprises.com/XSLT/Extensions/ECMAScript/1.0"
    extension-element-prefixes="obj">
  <xsl:output
      method="xml"
      indent="yes"
      encoding="UTF-8"/>
  <xsl:strip-space elements="*"/>
  <xsl:template match='/'>
    <obj:import href="demo04.es"/>
    <html>
      <head>
        <title>Math Functions</title>
      </head>
      <body>
        <table border="border" cellpadding="10">
          <tr><th>Argument</th><th>SQRT</th><th>LOG</th></tr>
          <obj:call object="'Mtab'" property="reset">
            <obj:with-param value="1"/>
            <obj:with-param value="10"/>
            <obj:with-param value="1"/>
          </obj:call>
          <obj:while object="'Mtab'" property="iterate">
            <tr>
              <th><xsl:value-of select="obj:call('Mtab', 'getValue')"/></th>
              <td><xsl:value-of select="obj:call('Mtab', 'getSqrt')"/></td>
              <td><xsl:value-of select="obj:call('Mtab', 'getLog')"/></td>
            </tr>
          </obj:while>
        </table>
      </body>
    </html>
  </xsl:template>
</xsl:stylesheet>

Following is the script demo04.es

  function Mtab_reset(start, end, step) {
      this.value = start - step;
      this.end = end;
      this.step = step;
      this.sqrt = 0;
      this.log = 0;
      }
  function Mtab_getValue() {
      return this.value.toString();
      }
  function Mtab_getSqrt() {
      return this.sqrt.toString();
      }
  function Mtab_getLog() {
      return this.log.toString();
      }
  function Mtab_iterate() {
      this.value += this.step;
      if (this.value > this.end)
          return false;
      this.sqrt = Math.sqrt(this.value);
      this.log = Math.log(this.value);
      this.sqrt = Math.round(100000*this.sqrt)/100000;
      this.log = Math.round(100000*this.log)/100000;
      return true;
      }
  var Mtab = new Object();
  Mtab.reset = Mtab_reset;
  Mtab.getValue = Mtab_getValue;
  Mtab.getSqrt = Mtab_getSqrt;
  Mtab.getLog = Mtab_getLog;
  Mtab.iterate = Mtab_iterate;

The source XML document is irrelevant in this example.

Example 5.

This example demonstrates how external applications can be controlled from XSLT stylesheets using the interface to the Automation object world. Microsoft Word 97 is used here as an example of an external application.

The XSLT stylesheet presented here is designed to convert the source Word 97 document into an XML format. Paragraphs in the source document are converted to XML elements marked with <p> tag. Individual words in the source document are copied to the output as character data. Each word that has to be rendered using the font other than Times New Roman 12pt, no bold, no italic is wrapped in <hi> element with the attributes font-family, font-size, font-weight and font-style that specify the required font.

The name of the Word 97 document must be passed to the XSLT processor as an external parameter. The source XML document is irrelevant in this example (the document that consists of a single document element can be used as the nominal source).

Please note that this example was created only to demonstrate the power of XSLT object-based extensions. No attempt was made to build the universal Word 97 to XML format converter.

This example demonstrates also how two different object worlds can be accessed from the same stylesheet. In this example an ECMAScript program is used to implement iterator objects needed to enumerate Automation collections. (Next editions of this specification may introduce feature similar to the Microsoft Visual Basic For Each statement, eliminating need in such iterators).

This example also demonstrates the extensive use of dynamic object references.

Following is the XSLT stylesheet:

<?xml version='1.0'?>
<xsl:stylesheet 
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
    xmlns:es="https://www.unicorn-enterprises.com/XSLT/Extensions/ECMAScript/1.0"
    xmlns:com="https://www.unicorn-enterprises.com/XSLT/Extensions/COM/1.0"
    extension-element-prefixes="es com"
    version="1.0">
  <xsl:output 
      method="xml"
      indent="yes"
      version="1.0"
      encoding="UTF-8"/>
  <xsl:strip-space elements="*"/>
<!--
    The following parameter must be passed to XSLT processor
-->
  <xsl:param name="fileName"/>
  <xsl:template match="/">
    <es:import href="word01.es"/>
    <xsl:variable name="application" 
        select="com:call('', 'Word.Application')"/>
    <xsl:variable name="documents" 
        select="com:get($application, 'Documents')"/>
    <xsl:variable name="document" 
        select="com:call($documents, 'Open', $fileName)"/>
    <root>
      <xsl:variable name="paragraphs"
          select="com:get($document, 'Paragraphs')"/>
      <xsl:variable name="paragraphsCount"
          select="com:get($paragraphs, 'Count')"/>
      <xsl:variable name="iterator"
          select="es:call('', 'newIterator', 1, $paragraphsCount)"/>
      <es:while object="$iterator" property="iterate">
        <xsl:call-template name="paragraph">
          <xsl:with-param name="paragraph"
              select="com:call($paragraphs, 
                          'Item', es:get($iterator, 'index'))"/>
        </xsl:call-template>
      </es:while>
      <com:call object="$document" property="Close"/>
    </root>
    <com:call object="$application" property="Quit"/>
  </xsl:template>
  <xsl:template name="paragraph">
    <xsl:param name="paragraph"/>
    <p>
      <xsl:variable name="words"
          select="com:get(com:get($paragraph, 'Range'), 'Words')"/>
      <xsl:variable name="wordsCount"
          select="com:get($words, 'Count')"/>
      <xsl:variable name="iterator"
          select="es:call('', 'newIterator', 1, $wordsCount)"/>
      <es:while object="$iterator" property="iterate">
        <xsl:call-template name="word">
          <xsl:with-param name="word"
              select="com:call($words, 'Item', es:get($iterator, 'index'))"/>
        </xsl:call-template>
      </es:while>
    </p>
  </xsl:template>
  <xsl:template name="word">
    <xsl:param name="word"/>
    <xsl:variable name="font" 
        select="com:get($word, 'Font')"/>
    <xsl:variable name="fontName"
        select="com:get($font, 'Name')"/>
    <xsl:variable name="fontSize"
        select="com:get($font, 'Size')"/>
    <xsl:variable name="fontBold" 
        select="com:get($font, 'Bold')"/>
    <xsl:variable name="fontItalic" 
        select="com:get($font, 'Italic')"/>
    <xsl:choose>
      <xsl:when 
          test="$fontName!='Times New Roman' or 
                    $fontSize!=12 or $fontBold or $fontItalic">
        <hi>
          <xsl:if test="$fontName!='Times New Roman'">
            <xsl:attribute name="font-family">
              <xsl:value-of select="$fontName"/>
            </xsl:attribute>
          </xsl:if>
          <xsl:if test="$fontSize!=12">
            <xsl:attribute name="font-size">
              <xsl:value-of select="$fontSize"/>
              <xsl:text>pt</xsl:text>
            </xsl:attribute>
          </xsl:if>
          <xsl:if test="$fontBold">
            <xsl:attribute name="font-weight">bold</xsl:attribute>
          </xsl:if>
          <xsl:if test="$fontItalic">
            <xsl:attribute name="font-style">italic</xsl:attribute>
          </xsl:if>
          <com:get object="$word" property="Text"/>
        </hi>
      </xsl:when>
      <xsl:otherwise>
        <com:get object="$word" property="Text"/>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:template>
</xsl:stylesheet>

Following is the ECMAScript program word01.es that implements iterators:

  function _iterate() {
      return (++this.index <= this.end);
      }
  function newIterator(start, end) {
      iterator = new Object();
      iterator.index = start - 1;
      iterator.end = end;
      iterator.iterate = _iterate;
      return iterator;
      }

Example 6.

The following XSLT stylesheet performs the function opposite to one presented in the previous example. It reads the source XML document and converts it to Word 97 format.

The name of the Word 97 document must be passed to the XSLT processor as an external parameter. The dummy XML document that consists of a single document element will be produced as the nominal result of processing.

<?xml version='1.0'?>
<xsl:stylesheet 
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
    xmlns:es="https://www.unicorn-enterprises.com/XSLT/Extensions/ECMAScript/1.0"
    xmlns:com="https://www.unicorn-enterprises.com/XSLT/Extensions/COM/1.0"
    extension-element-prefixes="es com"
    version="1.0">
  <xsl:output 
      method="xml"
      indent="yes"
      encoding="UTF-8"/>
  <xsl:strip-space elements="*"/>
<!--
    The following parameter must be passed to XSLT processor
-->
  <xsl:param name="fileName"/>
  <xsl:template match="/">
    <xsl:variable name="application" 
        select="com:call('', 'Word.Application')"/>
    <xsl:variable name="documents" 
        select="com:get($application, 'Documents')"/>
    <xsl:variable name="document"
        select="com:call($documents, 'Add')"/>
    <xsl:apply-templates>
      <xsl:with-param name="document" select="$document"/>
    </xsl:apply-templates>
    <com:call object="$document" property="SaveAs">
      <com:with-param value="$fileName"/>
    </com:call>
    <com:call object="$document" property="Close"/>
    <com:call object="$application" property="Quit"/>
  </xsl:template>
  <xsl:template match="root">
    <xsl:param name="document"/>
    <xsl:apply-templates>
      <xsl:with-param name="document" select="$document"/>
    </xsl:apply-templates>
  </xsl:template>
  <xsl:template match="p">
    <xsl:param name="document"/>
    <xsl:variable name="paragraphs" 
        select="com:get($document, 'Paragraphs')"/>
    <xsl:variable name="paragraph"
        select="com:call($paragraphs, 'Add')"/>
    <xsl:apply-templates>
      <xsl:with-param name="document" select="$document"/>
      <xsl:with-param name="range"
          select="com:get($paragraph, 'Range')"/>
    </xsl:apply-templates>
  </xsl:template>
  <xsl:template match="hi">
    <xsl:param name="document"/>
    <xsl:param name="range"/>
    <xsl:variable name="start"
        select="com:get($range, 'End')"/>
    <xsl:apply-templates>
      <xsl:with-param name="document" select="$document"/>
      <xsl:with-param name="range" select="$range"/>
    </xsl:apply-templates>
    <xsl:variable name="end"
        select="com:get($range, 'End')"/>
    <xsl:variable name="subrange"
        select="com:call($document, 'Range', $start, $end)"/>
    <xsl:variable name="font"
        select="com:get($subrange, 'Font')"/>
    <xsl:choose>
      <xsl:when test="@font-family">
        <com:put 
            object="$font" 
            property="Name" 
            value="string(@font-family)"/>
      </xsl:when>
      <xsl:otherwise>
        <com:put 
            object="$font" 
            property="Name" 
            value="'Times New Roman'"/>
      </xsl:otherwise>
    </xsl:choose>
    <xsl:choose>
      <xsl:when test="@font-size">
        <com:put 
            object="$font" 
            property="Size" 
            value="translate(@font-size,'pt','')"/>
      </xsl:when>
      <xsl:otherwise>
        <com:put 
            object="$font" 
            property="Size" 
            value="12"/>
      </xsl:otherwise>
    </xsl:choose>
    <com:put 
        object="$font" 
        property="Bold" 
        value="@font-weight='bold'"/>
    <com:put 
        object="$font" 
        property="Italic" 
        value="@font-style='italic'"/>
  </xsl:template>
  <xsl:template match="p/text()">
    <xsl:param name="document"/>
    <xsl:param name="range"/>
    <xsl:variable name="start"
        select="com:get($range, 'End')"/>
    <com:call object="$range" property="InsertAfter">
      <com:with-param value="string(.)"/>
    </com:call>
    <xsl:variable name="end"
        select="com:get($range, 'End')"/>
    <xsl:variable name="subrange"
        select="com:call($document, 'Range', $start, $end)"/>
    <xsl:variable name="font"
        select="com:get($subrange, 'Font')"/>
    <com:put object="$font" property="Name" value="'Times New Roman'"/>
    <com:put object="$font" property="Size" value="12"/>
    <com:put object="$font" property="Bold" value="false()"/>
    <com:put object="$font" property="Italic" value="false()"/>
  </xsl:template>
  <xsl:template match="text()">
    <xsl:param name="range"/>
    <com:call object="$range" property="InsertAfter">
      <com:with-param value="string(.)"/>
    </com:call>
  </xsl:template>
</xsl:stylesheet>