To define your own extension elements, define a class derived from Ft.Xml.Xslt.XsltElement. The module in which it is defined must have global dictionary named "ExtElements" mapping element expanded names to element class objects. The expanded names 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 can be listed in the environment variable "EXTMODULES". "EXTMODULES" is a colon-separated list of Python modules names.
(2) They are registered with the processor using the registerExtensionModules() method, which takes a list of module names. You can actually also use the related method registerExtensionElement (and take note of the registerExtentionFunction method), but registerExtensionModules() is recommended.
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 extelement.py and is in the directory called "localext", then using approach (1) you could the variables to your environment like this:
export PYTHONPATH=localext export EXTMODULES=extelement
For example:
#extelement.py import os from Ft.Xml.Xslt import XSL_NAMESPACE, XsltElement, XsltException, Error from Ft.Xml.Xslt import ContentInfo, AttributeInfo EXT_NAMESPACE = 'http://foo.org/namespaces/ext-xslt' class SystemElement(XsltElement): """ Execute an arbitrary operating system command. Because of the security issues, use at your risk :-) """ content = ContentInfo.Empty #Specify that this must be an empty element legalAttrs = { 'command': AttributeInfo.StringAvt(description='The command to be executed'), } def instantiate(self, context, processor): command = self._command.evaluate(context) os.system(command) return (context,) #The global dictionary that must be present in all extensions ExtElements = { (EXT_NAMESPACE, 'system'): SystemElement, } #And optional dictionary, purely for documentation purposes, #Which gives the prefix to use for extension namespaces in documentation ExtNamespaces = { EXT_NAMESPACE : 'e', }
And a little script that demonstrates using it (with registration method (2):
#useext.py TRANSFORM = """<?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 Ft.Xml.Xslt import Processor processor = Processor.Processor() #Register the extension element processor.registerExtensionModules(['extelement']) from Ft.Xml import InputSource transform = InputSource.DefaultFactory.fromString(TRANSFORM, "http://foo.com") source = InputSource.DefaultFactory.fromString(SOURCE, "http://foo.com") processor.appendStylesheet(transform) result = processor.run(source) print result
==Extension Element API==
There are several aspects of the extension element API worth discussing in more detail.
The class-level "content" variable specifies a content model to be enforced by the XSLT processor. If the element is used in a way that doesn't meet the specified content model, the user will get an error message. The content model is a structure that uses certain special classes, including:
- ContentInfo.Empty - matches no content at all (empty element)
- ContentInfo.Text - matches plain text content
- ContentInfo.Seq - matches the given sequence of sub-patterns
- ContentInfo.Alt - matches one of the given choice of sub-patterns
- ContentInfo.Rep - matches 0 or more repeated instances of the given sub-pattern
- ContentInfo.Rep1 - matches 0 or more repeated instances of the given sub-pattern
- ContentInfo.Opt - matches zero or one of the given sub-pattern
- ContentInfo.ResultElements - matches elements not in the XSL namespace
- ContentInfo.Instructions - matches any sequence of XSLT elements categorized as instructions in the spec
- ContentInfo.Template - matches an XSLT template body according to the spec
- ContentInfo.TopLevelElements - matches any sequence of XSLT elements categorized as top level in the spec
- ContentInfo.QName - matches a particular element by giving its namespace and node name (the prefix in the node name is only used for documentation and error messages)
So, for instance, the xsl:choose element would be described as
content = ContentInfo.Seq(
ContentInfo.Rep1(ContentInfo.QName(XSL_NAMESPACE, 'xsl:when')),
ContentInfo.Opt(ContentInfo.QName(XSL_NAMESPACE, 'xsl:otherwise')),
)
The class-level "legalAttrs" variable specifies the attributes allowed or required on the element. It is a Python dictionary mapping attribute name to its specification. The specification is a class according o the type of attribute.
The following are the supported attribute classes. The parameters specified are for the initializer. Note that most general patterns have a plain variant and an attribute value template (AVT) variant:
- AttributeInfo.String - any XPath string
- AttributeInfo.StringAvt - an AVT yielding any string
- AttributeInfo.Char - any XPath string of length 1
- AttributeInfo.CharAvt - AVT version of Char
- AttributeInfo.Choice - a string which must be one of a number of given values. The values are given by a list of strings with is the first parameter
- AttributeInfo.ChoiceAvt - AVT version of Choice
- AttributeInfo.YesNo - Abbreviation for AttributeInfo.Choice([No title found])
- AttributeInfo.YesNoAvt - AVT version of YesNo
- AttributeInfo.Number - any XPath number
- AttributeInfo.NumberAvt - AVT version of Number
- AttributeInfo.UriReference - XPath string that is syntactically a URI reference
- AttributeInfo.UriReferenceAvt - AVT version of UriReference
- AttributeInfo.Id - XPath string that is syntactically an XML ID
- AttributeInfo.IdAvt - AVT version of Id
- AttributeInfo.QName - XPath string that is syntactically an XML namespaces qualified name
- AttributeInfo.QNameAvt - AVT version of QName
- AttributeInfo.NCName - XPath string that is syntactically an XML namespaces "no colon" name
- AttributeInfo.NCNameAvt - AVT version of NCName
- AttributeInfo.Prefix - Same as NCName
- AttributeInfo.PrefixAvt - Same as NCNameAvt
- AttributeInfo.NMToken - XPath string that is syntactically an XML Name token
- AttributeInfo.NMTokenAvt - AVT version of NMToken
- AttributeInfo.QNameButNotNCName - A QName that contains a colon
- AttributeInfo.QNameButNotNCNameAvt - AVT version of QNameButNotNCName
- AttributeInfo.Token - XPath string that is syntactically an XPath name test (i.e. "foo", "ns:foo", ns:" or "")
- AttributeInfo.TokenAvt - AVT version of Token
- AttributeInfo.Expression - XPath string that is syntactically an XPath expression
- AttributeInfo.ExpressionAvt - AVT version of Expression
- AttributeInfo.StringExpression - XPath string that is syntactically an XPath expression, which would be expected to return a string value
- AttributeInfo.StringExpressionAvt - AVT version of StringExpression
- AttributeInfo.NodeSetExpression - XPath string that is syntactically an XPath expression, which would be expected to return a node set value
- AttributeInfo.NodeSetExpressionAvt - AVT version of NodeSetExpression
- AttributeInfo.NumberExpression - XPath string that is syntactically an XPath expression, which would be expected to return a number value
- AttributeInfo.NumberExpressionAvt - AVT version of NumberExpression
- AttributeInfo.BooleanExpression - XPath string that is syntactically an XPath expression, which would be expected to return a boolean value
- AttributeInfo.BooleanExpressionAvt - AVT version of BooleanExpression
- AttributeInfo.Pattern - XPath string that is syntactically an XSLY pattern
- AttributeInfo.PatternAvt - AVT version of Pattern
- AttributeInfo.Tokens - XPath string that is syntactically a space-delimited series of tokens
- AttributeInfo.TokensAvt - AVT version of Tokens
- AttributeInfo.QNames - XPath string that is syntactically a space-delimited series of QNames
- AttributeInfo.QNamesAvt - AVT version of QNames
- AttributeInfo.Prefixes - XPath string that is syntactically a space-delimited series of NCNames
- AttributeInfo.PrefixesAvt - AVT version of Prefixes
All of these classes take the following optional keyword parameters:
- description - for documentation
- default - the default value of the attribute to be used if omitted
Extension elements can also be given a category, which indicates whether they are to be used top-level, or as instructions
Some examples from the XSLT spec:
xsl:output
category = CategoryTypes.TOP_LEVEL_ELEMENT
content = ContentInfo.Empty
legalAttrs = {
'method' : AttributeInfo.QName(),
'version' : AttributeInfo.NMToken(),
'encoding' : AttributeInfo.String(),
'omit-xml-declaration' : AttributeInfo.YesNo(),
'standalone' : AttributeInfo.YesNo(),
'doctype-public' : AttributeInfo.String(),
'doctype-system' : AttributeInfo.String(),
'cdata-section-elements' : AttributeInfo.QNames(),
'indent' : AttributeInfo.YesNo(),
'media-type' : AttributeInfo.String(),
}
xsl:sort
category = None
content = ContentInfo.Empty
legalAttrs = {
'select' : AttributeInfo.StringExpression(default='.'),
'lang' : AttributeInfo.NMTokenAvt(),
# We don't support any additional data-types, hence no
# AttributeInfo.QNameButNotNCName()
'data-type' : AttributeInfo.ChoiceAvt(['text', 'number'],
default='text'),
'order' : AttributeInfo.ChoiceAvt(['ascending', 'descending'],
default='ascending'),
'case-order' : AttributeInfo.ChoiceAvt(['upper-first', 'lower-first']),
}
xsl:number
category = CategoryTypes.INSTRUCTION
content = ContentInfo.Empty
legalAttrs = {
'level' : AttributeInfo.Choice(['single', 'multiple', 'any'],
default='single'),
'count' : AttributeInfo.Pattern(),
'from' : AttributeInfo.Pattern(),
'value' : AttributeInfo.Expression(),
'format' : AttributeInfo.StringAvt(default='1'),
'lang' : AttributeInfo.NMToken(),
'letter-value' : AttributeInfo.ChoiceAvt(['alphabetic', 'traditional']), 'grouping-separator' : AttributeInfo.CharAvt(),
'grouping-size' : AttributeInfo.NumberAvt(default=0),
}
Of course, it's always a good idea to use descriptions, which the above do not.
For good examples of modules with extension elements, see Ft.Xml.Xslt.BuiltInExtElements and Ft.Xml.Xslt.Exslt.Common . The various modules in Ft.Xml.Xslt.Exslt have a strong diversity and make good examples, especially given their detailed specifications at exslt.org
