4XSLT Processor

# 4XSLT Extension Elements: Built in extensions, and how to Implement your own

Fourthought, Inc.

# 4XSLT Extensions

4XSLT
XSLT
Extension Elements
Information on implementing XSLT extension elements in 4XSLT.

# Built-in Extension Elements

4XSLT supports user-defined extension elements as specified by the XSLT Recommendation. There are several extension elements built in, which are documented here.

    <ft:apply-templates
      select = node-set-expression
      mode = string-expression>
      <!-- Content: (xsl:sort | xsl:with-param)* -->
    </ft:apply-templates>

This is just like `xsl:apply-templates`, except that you can set the mode using an XPath expression which resolves to a string which must be a qname. This qname is then used to select the mode as described in section 5.7 of the XSLT spec.

    <ft:write-file
      name = { string-expression }
      overwrite = { string expression }>
      <!-- Content: (ft:output, template) -->
    </ft:write-file>

This redirects a portion of the output to a file. The `name` attribute is interpreted as an attribute value template. The string resulting from the AVT is the name of the output file. The `overwrite` attribute is interpreted as an attribute value template. If the resulting string is "yes", then the file is overwritten, otherwise the output is appended to the file, if it exists. Warning: ft:write-file is deprecated in favor of exslt:document. Please see the 4Suite EXSLT documentation.

The body of the element is a template, with all output generated by this templatemredirected to the file. The template may have embedded one or more `ft:output` elements as immediate children of `ft:write-file`. ft:output are empty elements with the same range of attributes as `xsl:output` and the same semantics and defaulting for each attribute. All `ft:output` elements are combined, and the output parameters determined by each attribute is applied to the redirect file.

For example, this means that a 4XSLT transform can use the XML output method, but it can redirect sub-sections of its output to separate files using the HTML method for redirected output.

    <ft:message-output
      file = { string-expression }
      overwrite = { string-expression }
      disable = { string-expression } />

ft:message-output allows redirection of messages generated by xsl:message instructions to a file. The `file` attribute is interpreted as an attribute value template. The string resulting from the AVT is the name of the output file. The `overwrite` attribute is interpreted as an attribute value template. If the resulting string is "yes", then the file is overwritten, otherwise the output is appended to the file, if it exists. The `disable` attribute is interpreted as an attribute value template. If the resulting string is "yes", then output from `xsl:message` is disabled altogether.

# User-Defined Extension Elements

To define your own extension element, define an element derived from xml.xslt.XsltElement. The module in which it is defined must have global dictionary named "ExtElements" mapping element qualified names to element class objects. The element qnames are in the form of a tuple of two strings, the first being the namespace URI for the element, and the second being the local name. The namespace URI must be a valid, identifying (but not necessarily addressable) URI, and in particular, it cannot be an empty string.

Finally, modules containing any extension elements used must be indicated as such to the processor in one of two ways. (1) They are listed in the environment variable "EXTMODULES". "EXTMODULES" is a colon-separated list of modules. (2) They are registered with the processor using the registerExtensionModule() method, which takes a list of module names. In either case, all extension modules must be in the "PYTHONPATH". Note that extension modules will automatically be searched for XPath extension functions as well as Extension elements.

If your extension is called xslt-extension.py and is in the directory called "python", then add the variables to your environment like this:

export PYTHONPATH=python export EXTMODULES=xslt-extensions

For example:

    #extelement.py

    import string, os
    import xml.dom.ext
    from xml.xslt import XsltElement, XSL_NAMESPACE, AttributeValueTemplate

    class SystemElement(XsltElement):
        def __init__(self, doc, uri='http://foo.org/namespaces/ext-xslt', localName='system', prefix='xsl', baseUri=''):
            XsltElement.__init__(self, doc, uri, localName, prefix, baseUri)

        def setup(self):
            self.__dict__['_nss'] = xml.dom.ext.GetAllNs(self)
            self.__dict__['_command'] = AttributeValueTemplate.AttributeValueTemplate(self.getAttributeNS('', 'command'))
            return

        def instantiate(self, context, processor):
            origState = context.copy()
            context.setNamespaces(self._nss)
            command = self._command.evaluate(context)
            os.system(command)
            context.set(origState)
            return (context,)

    ExtElements = {
        ('http://foo.org/namespaces/ext-xslt', 'system'): SystemElement
        }

    #useext.py

    sheet = """<?xml version="1.0"?>
    <xsl:stylesheet
      xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
      xmlns:ext="http://foo.org/namespaces/ext-xslt"
      extension-element-prefixes="ext"
      version="1.0">

      <xsl:template match="execute-command">
        <ext:system command="{@cmd}"/>
      </xsl:template>

    </xsl:stylesheet>
    """

    source = """<execute-command cmd="dir"/>"""

    from xml.xslt import Processor
    processor = Processor.Processor()
    processor.registerExtensionModules(['extelement'])
    processor.appendStylesheetString(sheet)
    result = processor.runString(source, ignorePis=1)
    print result

This example is somewhat involved, and shows how to allow an extension element use attribute value templates. If you don't need AVTs, you can just use an attribute's value directly.

Note the call to

    registerExtensionModules

in useext. This can be omitted if "extelement" (the module name where the extension element is defined) is in the EXTMODULES environment variable. This call should precede the appending of any stylesheet that uses the extension elements.

Note that 4XSLT supports fallback through the standard XSLT fallback mechanism. It also supports the element-available extension function.

# Extension Element API

## Context

Most likely, the processing you'll do in extension code will reference the current XSLT context, which is passed into the instantiate method of all XSLT elements. This class is defined at xml.xslt.XsltContext.

As mentioned, XSLT Extension Elements should be derived from xml.xslt.XsltElement (or at least follow its protocol).

xml.xslt
XsltElement
__init__
Called as the stylesheet is being built. The DOM tree structure is not guarenteed to be set up in the initializer, so be careful of using node operations on self.
doc
xml.dom.Document.Document The stylesheet document node
uri
xml.dom.Document.Document The element namespace URI
localName
xml.dom.Document.Document The local element node name
prefix
xml.dom.Document.Document The namespace prefix from the element qualified name
setup
Called after the stylesheet DOM structure has been set up, but before the processor has begun a run on any XML source. All node operations on self should have the expected result.
instantiate
context
xml.xslt.XsltContext The current context, not yet updated with the namespace nodes defined on self.
processor
xml.xslt.Processor.Processor The processor instance, used for output handler calls.
tuple with the passed-on context as the first entry This tuple is typically processed by the parent element. The only requirement is that the first argument be an XsltContext object, which can be the same as was passed in unless your element is intended to change any data in the context. Other items can be added to the tuple as needed to return data.