<?xml version="1.0"?>
<?xml-stylesheet href="http://xslt.fourthought.com/api.xslt" type="text/xsl"?>
<tech:ApiSpec title="4XSLT Processor"
  xmlns:tech="http://namespaces.fourthought.com/technical"
  xmlns:dc= "http://purl.org/dc/elements/1.1"
  xmlns="http://docbook.org/docbook/xml/4.0/namespace"
>

<artheader>
<title>4XSLT Extension Elements: Built in extensions, and how to Implement your own</title>
<author>Fourthought, Inc.</author>
</artheader>

<dc:Title>4XSLT Extensions</dc:Title>
<dc:Subject>4XSLT</dc:Subject>
<dc:Subject>XSLT</dc:Subject>
<dc:Subject>Extension Elements</dc:Subject>
<dc:Description>Information on implementing XSLT extension elements in 4XSLT.</dc:Description>

<sect1>
<title>Built-in Extension Elements</title>

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

<sect2>

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

<para>
This is just like <code>xsl:apply-templates</code>, 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.
</para>

</sect2>

<sect2>

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

<para>
This redirects a portion of the output to a file.  The <code>name</code> attribute is interpreted as an attribute value template.  The string resulting from the AVT is the name of the output file.  The <code>overwrite</code> 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.  <emphasis><emphasis role="bold">Warning:</emphasis> ft:write-file is deprecated in favor of exslt:document.  Please see the 4Suite EXSLT documentation.</emphasis>
</para>

<para>
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 <code>ft:output</code> elements as immediate children of <code>ft:write-file</code>.  ft:output are empty elements with the same range of attributes as <code>xsl:output</code> and the same semantics and defaulting for each attribute.  All <code>ft:output</code> elements are combined, and the output parameters determined by each attribute is applied to the redirect file.
</para>

<para>
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.
</para>

</sect2>

<sect2>

<screen>
<![CDATA[
<ft:message-output
  file = { string-expression }
  overwrite = { string-expression }
  disable = { string-expression } />
]]>
</screen>

<para>
ft:message-output allows redirection of messages generated by xsl:message instructions to a file.  The <code>file</code> attribute is interpreted as an attribute value template.  The string resulting from the AVT is the name of the output file.  The <code>overwrite</code> 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 <code>disable</code> attribute is interpreted as an attribute value template.  If the resulting string is "yes", then output from <code>xsl:message</code> is disabled altogether.
</para>

</sect2>


</sect1>

<sect1>
<title>User-Defined Extension Elements</title>

<para>
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.
</para>

<para>
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.
</para>

<para>
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
</para>

<para>
For example:
</para>

<screen>
<![CDATA[
#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
]]>
</screen>

<para>
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.
</para>
<para>
Note the call to <screen>registerExtensionModules</screen> 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.
</para>

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

</sect1>


<sect1>
<title>Extension Element API</title>

<sect2>
<title>Context</title>

<para>
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 <tech:Reference document="4XSLT-Api.html">xml.xslt.XsltContext</tech:Reference>.
</para>

</sect2>

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

<tech:Module>
  <tech:Name>xml.xslt</tech:Name>
  <tech:Class>
  <tech:Name>XsltElement</tech:Name>

  <tech:Method>
  <tech:Name>__init__</tech:Name>
  <tech:Desc>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.</tech:Desc>
  <tech:Arg><tech:Name>doc</tech:Name><tech:Type>xml.dom.Document.Document</tech:Type><tech:Desc>The stylesheet document node</tech:Desc></tech:Arg>
  <tech:Arg><tech:Name>uri</tech:Name><tech:Type>xml.dom.Document.Document</tech:Type><tech:Desc>The element namespace URI</tech:Desc></tech:Arg>
  <tech:Arg><tech:Name>localName</tech:Name><tech:Type>xml.dom.Document.Document</tech:Type><tech:Desc>The local element node name</tech:Desc></tech:Arg>
  <tech:Arg><tech:Name>prefix</tech:Name><tech:Type>xml.dom.Document.Document</tech:Type><tech:Desc>The namespace prefix from the element qualified name</tech:Desc></tech:Arg>
  </tech:Method>

  <tech:Method>
  <tech:Name>setup</tech:Name>
  <tech:Desc>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.</tech:Desc>
  </tech:Method>

  <tech:Method>
  <tech:Name>instantiate</tech:Name>
  <tech:Desc></tech:Desc>
  <tech:Arg><tech:Name>context</tech:Name><tech:Type>xml.xslt.XsltContext</tech:Type><tech:Desc>The current context, not yet updated with the namespace nodes defined on self.</tech:Desc></tech:Arg>
  <tech:Arg><tech:Name>processor</tech:Name><tech:Type>xml.xslt.Processor.Processor</tech:Type><tech:Desc>The processor instance, used for output handler calls.</tech:Desc></tech:Arg>
  <tech:Return><tech:Type>tuple with the passed-on context as the first entry</tech:Type><tech:Desc>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.</tech:Desc></tech:Return>
  </tech:Method>

  </tech:Class>
</tech:Module>

</sect1>

</tech:ApiSpec>
