mirror of
https://gitlab.gnome.org/GNOME/libxslt
synced 2025-11-06 23:49:25 +03:00
* doc/extensions.html: fixed a number of typo found by Dan York * libxslt/xsltutils.c: improved the profiling ouput, added the average value too. Daniel
443 lines
17 KiB
HTML
443 lines
17 KiB
HTML
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
|
|
"http://www.w3.org/TR/html4/loose.dtd">
|
|
<html>
|
|
<head>
|
|
<title>Writing extensions for XSLT C library for Gnome</title>
|
|
<meta name="GENERATOR" content="amaya V5.0">
|
|
<meta http-equiv="Content-Type" content="text/html">
|
|
</head>
|
|
|
|
<body bgcolor="#ffffff">
|
|
<p><a href="http://www.gnome.org/"><img src="smallfootonly.gif"
|
|
alt="Gnome Logo"></a><a href="http://www.redhat.com/"><img src="redhat.gif"
|
|
alt="Red Hat Logo"></a></p>
|
|
|
|
<h1 align="center">Writing extensions for XSLT C library for Gnome</h1>
|
|
|
|
<p></p>
|
|
|
|
<p>Location: <a
|
|
href="http://xmlsoft.org/XSLT/extensions.html">http://xmlsoft.org/XSLT/extensions.html</a></p>
|
|
|
|
<p>Libxslt home page: <a
|
|
href="http://xmlsoft.org/XSLT/">http://xmlsoft.org/XSLT/</a></p>
|
|
|
|
<p>mailing-list archives: <a
|
|
href="http://mail.gnome.org/archives/xslt/">http://mail.gnome.org/archives/xslt/</a></p>
|
|
|
|
<p>Version: $Revision$</p>
|
|
|
|
<h2>Table of content</h2>
|
|
<ul>
|
|
<li><a href="#Introducti">Introduction</a></li>
|
|
<li><a href="#Basics">Basics</a></li>
|
|
<li><a href="#Keep">Extension modules</a></li>
|
|
<li><a href="#Registerin">Registering a module</a></li>
|
|
<li><a href="#libxml">Loading a module</a></li>
|
|
<li><a href="#Registerin1">Registering an extension function</a></li>
|
|
<li><a href="#Implementi">Implementing an extension function</a></li>
|
|
<li><a href="#Examples">Examples for extension functions</a></li>
|
|
<li><a href="#Registerin2">Registering an extension element</a></li>
|
|
<li><a href="#Implementi1">Implementing an extension element</a></li>
|
|
<li><a href="#Example">Example for extension elements</a></li>
|
|
<li><a href="#shutdown">The shutdown of a module</a></li>
|
|
<li><a href="#Future">Future work</a></li>
|
|
</ul>
|
|
|
|
<h2><a name="Introducti">Introduction</a></h2>
|
|
|
|
<p>This document describes the work needed to write extensions to the
|
|
standard XSLT library for use with <a
|
|
href="http://xmlsoft.org/XSLT/">libxslt</a>, the <a
|
|
href="http://www.w3.org/TR/xslt">XSLT</a> C library developped for the <a
|
|
href="http://www.gnome.org/">Gnome</a> project.</p>
|
|
|
|
<p>Before starting reading this document it is highly recommended to get
|
|
familiar with <a href="internals.html">the libxslt internals</a>.</p>
|
|
|
|
<p>Note: this documentation is by definition incomplete and I am not good at
|
|
spelling, grammar, so patches and suggestions are <a
|
|
href="mailto:veillard@redhat.com">really welcome</a>.</p>
|
|
|
|
<h2><a name="Basics">Basics</a></h2>
|
|
|
|
<p>The <a href="http://www.w3.org/TR/xslt">XSLT specification</a> provides
|
|
two <a href="http://www.w3.org/TR/xslt">ways to extend an XSLT engine</a>:</p>
|
|
<ul>
|
|
<li>providing <a href="http://www.w3.org/TR/xslt">new extension
|
|
functions</a> which can be called from XPath expressions</li>
|
|
<li>providing <a href="http://www.w3.org/TR/xslt">new extension
|
|
elements</a> which can be inserted in stylesheets</li>
|
|
</ul>
|
|
|
|
<p>In both cases the extensions need to be associated to a new namespace,
|
|
i.e. an URI used as the name for the extension's namespace (there is no need
|
|
to have a resource there for this to work).</p>
|
|
|
|
<p>libxslt provides a few extensions itself, either in libxslt namespace
|
|
"http://xmlsoft.org/XSLT/" or in other namespace for well known extensions
|
|
provided by other XSLT processors like Saxon, Xalan or XT.</p>
|
|
|
|
<h2><a name="Keep">Extension modules</a></h2>
|
|
|
|
<p>Since extensions are bound to a namespace name, usually sets of extensions
|
|
coming from a given source are using the same namespace name defining in
|
|
practice a group of extensions providing elements, functions or both. From
|
|
libxslt point of view those are considered as an "extension module", and most
|
|
of the APIs work at a module point of view.</p>
|
|
|
|
<p>Registration of new functions or elements are bound to the activation of
|
|
the module, this is currently done by declaring the namespace as an extension
|
|
by using the attribute <code>extension-element-prefixes</code> on the
|
|
<code><a href="http://www.w3.org/TR/xslt">xsl:stylesheet</a></code>
|
|
element.</p>
|
|
|
|
<p>And extension module is defined by 3 objects:</p>
|
|
<ul>
|
|
<li>the namespace name associated</li>
|
|
<li>an initialization function</li>
|
|
<li>a shutdown function</li>
|
|
</ul>
|
|
|
|
<h2><a name="Registerin">Registering a module</a></h2>
|
|
|
|
<p>Currently a libxslt module has to be compiled within the application using
|
|
libxslt, there is no code to load dynamically shared libraries associated to
|
|
namespace (this may be added but is likely to become a portability
|
|
nightmare).</p>
|
|
|
|
<p>So the current way to register a module is to link the code implementing
|
|
it with the application and to call a registration function:</p>
|
|
<pre>int xsltRegisterExtModule(const xmlChar *URI,
|
|
xsltExtInitFunction initFunc,
|
|
xsltExtShutdownFunction shutdownFunc);</pre>
|
|
|
|
<p>The associated header is read by:</p>
|
|
<pre>#include<libxslt/extensions.h></pre>
|
|
|
|
<p>which also defines the type for the initialization and shutdown
|
|
functions</p>
|
|
|
|
<h2><a name="libxml">Loading a module</a></h2>
|
|
|
|
<p>Once the module URI has been registered and if the XSLT processor detects
|
|
that a given stylesheet needs the functionalities of an extended module, this
|
|
one is initialized.</p>
|
|
|
|
<p>The xsltExtInitFunction type defines the interface for an initialization
|
|
function:</p>
|
|
<pre>/**
|
|
* xsltExtInitFunction:
|
|
* @ctxt: an XSLT transformation context
|
|
* @URI: the namespace URI for the extension
|
|
*
|
|
* A function called at initialization time of an XSLT
|
|
* extension module
|
|
*
|
|
* Returns a pointer to the module specific data for this
|
|
* transformation
|
|
*/
|
|
typedef void *(*xsltExtInitFunction)(xsltTransformContextPtr ctxt,
|
|
const xmlChar *URI);</pre>
|
|
|
|
<p>There are 3 things to notice:</p>
|
|
<ul>
|
|
<li>the function gets passed the namespace name URI as an argument, this
|
|
allow a single function to provide the initialization for multiple
|
|
logical modules</li>
|
|
<li>it also gets passed a transformation context, the initialization is
|
|
done at run time before any processing occurs on the stylesheet but it
|
|
will be invoked separately each time for each transformation</li>
|
|
<li>it returns a pointer, this can be used to store module specific
|
|
informations which can be retrieved later when a function or an element
|
|
from the extension are used, an obvious example is a connection to a
|
|
database which should be kept and reused along the transformation. NULL
|
|
is a perfectly valid return, there is no way to indicate a failure at
|
|
this level</li>
|
|
</ul>
|
|
|
|
<p>What this function is expected to do is:</p>
|
|
<ul>
|
|
<li>prepare the context for this module (like opening the database
|
|
connection)</li>
|
|
<li>register the extensions specific to this module</li>
|
|
</ul>
|
|
|
|
<h2><a name="Registerin1">Registering an extension function</a></h2>
|
|
|
|
<p>There is a single call to do this registration:</p>
|
|
<pre>int xsltRegisterExtFunction(xsltTransformContextPtr ctxt,
|
|
const xmlChar *name,
|
|
const xmlChar *URI,
|
|
xmlXPathEvalFunc function);</pre>
|
|
|
|
<p>The registration is bound to a single transformation instance referred by
|
|
ctxt, name is the UTF8 encoded name for the NCName of the function, and URI
|
|
is the namespace name for the extension (no checking is done, a module could
|
|
register functions or elements from a different namespace, but it is not
|
|
recommended).</p>
|
|
|
|
<h2><a name="Implementi">Implementing an extension function</a></h2>
|
|
|
|
<p>The implementation of the function must have the signature of a libxml
|
|
XPath function:</p>
|
|
<pre>/**
|
|
* xmlXPathEvalFunc:
|
|
* @ctxt: an XPath parser context
|
|
* @nargs: the number of arguments passed to the function
|
|
*
|
|
* an XPath evaluation function, the parameters are on the
|
|
* XPath context stack
|
|
*/
|
|
|
|
typedef void (*xmlXPathEvalFunc)(xmlXPathParserContextPtr ctxt,
|
|
int nargs);</pre>
|
|
|
|
<p>The context passed to an XPath function is not an XSLT context but an <a
|
|
href="internals.html#XPath1">XPath context</a>. However it is possible to
|
|
find one from the other:</p>
|
|
<ul>
|
|
<li>The function xsltXPathGetTransformContext provide this lookup facility:
|
|
<pre>xsltTransformContextPtr
|
|
xsltXPathGetTransformContext
|
|
(xmlXPathParserContextPtr ctxt);</pre>
|
|
</li>
|
|
<li>The <code>xmlXPathContextPtr</code> associated to an
|
|
<code>xsltTransformContext</code> is stored in the <code>xpathCtxt</code>
|
|
field.</li>
|
|
</ul>
|
|
|
|
<p>The first thing an extension function may want to do is to check the
|
|
arguments passed on the stack, the <code>nargs</code> will precise how many
|
|
of them were provided on the XPath expression. The macros valuePop will
|
|
extract them from the XPath stack:</p>
|
|
<pre>#include <libxml/xpath.h>
|
|
#include <libxml/xpathInternals.h>
|
|
|
|
xmlXPathObjectPtr obj = valuePop(ctxt); </pre>
|
|
|
|
<p>Note that <code>ctxt</code> is the XPath context not the XSLT one. It is
|
|
then possible to examine the content of the value. Check <a
|
|
href="internals.html#Descriptio">the description of XPath objects</a> if
|
|
necessary. The following is a common sequcnce checking whether the argument
|
|
passed is a string and converting it using the built-in XPath
|
|
<code>string()</code> function if this is not the case:</p>
|
|
<pre>if (obj->type != XPATH_STRING) {
|
|
valuePush(ctxt, obj);
|
|
xmlXPathStringFunction(ctxt, 1);
|
|
obj = valuePop(ctxt);
|
|
}</pre>
|
|
|
|
<p>Most common XPath functions are available directly at the C level and are
|
|
exported either in <code><libxml/xpath.h></code> or in
|
|
<code><libxml/xpathInternals.h></code>.</p>
|
|
|
|
<p>The extension function may also need to retrieve the data associated to
|
|
this module instance (the database connection in the previous example) this
|
|
can be done using the xsltGetExtData:</p>
|
|
<pre>void * xsltGetExtData(xsltTransformContextPtr ctxt,
|
|
const xmlChar *URI);</pre>
|
|
|
|
<p>again the URI to be provided is the one used which was used when
|
|
registering the module.</p>
|
|
|
|
<p>Once the function finishes, don't forget to:</p>
|
|
<ul>
|
|
<li>push the return value on the stack using <code>valuePush(ctxt,
|
|
obj)</code></li>
|
|
<li>deallocate the parameters passed to the function using
|
|
<code>xmlXPathFreeObject(obj)</code></li>
|
|
</ul>
|
|
|
|
<h2><a name="Examples">Examples for extension functions</a></h2>
|
|
|
|
<p>The module libxslt/functions.c containsthe sources of the XSLT built-in
|
|
functions, including document(), key(), generate-id(), etc. as well as a full
|
|
example module at the end. Here is the test function implementation for the
|
|
libxslt:test function:</p>
|
|
<pre>/**
|
|
* xsltExtFunctionTest:
|
|
* @ctxt: the XPath Parser context
|
|
* @nargs: the number of arguments
|
|
*
|
|
* function libxslt:test() for testing the extensions support.
|
|
*/
|
|
static void
|
|
xsltExtFunctionTest(xmlXPathParserContextPtr ctxt, int nargs)
|
|
{
|
|
xsltTransformContextPtr tctxt;
|
|
void *data;
|
|
|
|
tctxt = xsltXPathGetTransformContext(ctxt);
|
|
if (tctxt == NULL) {
|
|
xsltGenericError(xsltGenericErrorContext,
|
|
"xsltExtFunctionTest: failed to get the transformation context\n");
|
|
return;
|
|
}
|
|
data = xsltGetExtData(tctxt, (const xmlChar *) XSLT_DEFAULT_URL);
|
|
if (data == NULL) {
|
|
xsltGenericError(xsltGenericErrorContext,
|
|
"xsltExtFunctionTest: failed to get module data\n");
|
|
return;
|
|
}
|
|
#ifdef WITH_XSLT_DEBUG_FUNCTION
|
|
xsltGenericDebug(xsltGenericDebugContext,
|
|
"libxslt:test() called with %d args\n", nargs);
|
|
#endif
|
|
}</pre>
|
|
|
|
<h2><a name="Registerin2">Registering an extension function</a></h2>
|
|
|
|
<p>There is a single call to do this registration:</p>
|
|
<pre>int xsltRegisterExtElement(xsltTransformContextPtr ctxt,
|
|
const xmlChar *name,
|
|
const xmlChar *URI,
|
|
xsltTransformFunction function);</pre>
|
|
|
|
<p>It is similar to the mechanism used to register an extension function,
|
|
except that the signature of an extension element implementation is
|
|
different.</p>
|
|
|
|
<p>The registration is bound to a single transformation instance referred by
|
|
ctxt, name is the UTF8 encoded name for the NCName of the element, and URI is
|
|
the namespace name for the extension (no checking is done, a module could
|
|
register elements for a different namespace, but it is not recommended).</p>
|
|
|
|
<h2><a name="Implementi1">Implementing an extension element</a></h2>
|
|
|
|
<p>The implementation of the element must have the signature of an XSLT
|
|
transformation function:</p>
|
|
<pre>/**
|
|
* xsltTransformFunction:
|
|
* @ctxt: the XSLT transformation context
|
|
* @node: the input node
|
|
* @inst: the stylesheet node
|
|
* @comp: the compiled information from the stylesheet
|
|
*
|
|
* signature of the function associated to elements part of the
|
|
* stylesheet language like xsl:if or xsl:apply-templates.
|
|
*/
|
|
typedef void (*xsltTransformFunction)
|
|
(xsltTransformContextPtr ctxt,
|
|
xmlNodePtr node,
|
|
xmlNodePtr inst,
|
|
xsltStylePreCompPtr comp);</pre>
|
|
|
|
<p>The first argument is the XSLT transformation context. The second and
|
|
third arguments are xmlNodePtr i.e. internal memory <a
|
|
href="internals.html#libxml">representation of XML nodes</a>. They are
|
|
respectively <code>node</code> from the the input document being transformed
|
|
by the stylesheet and <code>inst</code> the extension element in the
|
|
stylesheet. The last argument is <code>comp</code> a pointer to a precompiled
|
|
representation of <code>inst</code> but usually for extension function this
|
|
value is <code>NULL</code> by default (it could be added and associated to
|
|
the instruction in <code>inst->_private</code>).</p>
|
|
|
|
<p>The same functions are available from a function implementing an extension
|
|
element as in an extension function, including
|
|
<code>xsltGetExtData()</code>.</p>
|
|
|
|
<p>The goal of extension element being usually to enrich the generated
|
|
output, it is expected that they will grow the currently generated output
|
|
tree, this can be done by grabbing ctxt->insert which is the current
|
|
libxml node being generated (Note this can also be the intermediate value
|
|
tree being built for example to initialize a variable, the processing should
|
|
be similar). The functions for libxml tree manipulation from <a
|
|
href="http://xmlsoft.org/html/libxml-tree.html"><libxml/tree.h></a> can
|
|
be employed to extend or modify the tree, but it is required to preserve the
|
|
insertion node and its ancestors since there is existing pointers to those
|
|
elements still in use in the XSLT template execution stack.</p>
|
|
|
|
<h2><a name="Example">Example for extension elements</a></h2>
|
|
|
|
<p>The module libxslt/transform.c containsthe sources of the XSLT built-in
|
|
elements, including xsl:element, xsl:attribute, xsl:if, etc. There is a small
|
|
but full example in functions.c providing the implementation for the
|
|
libxslt:test element, it will output a comment in the result tree:</p>
|
|
<pre>/**
|
|
* xsltExtElementTest:
|
|
* @ctxt: an XSLT processing context
|
|
* @node: The current node
|
|
* @inst: the instruction in the stylesheet
|
|
* @comp: precomputed informations
|
|
*
|
|
* Process a libxslt:test node
|
|
*/
|
|
static void
|
|
xsltExtElementTest(xsltTransformContextPtr ctxt, xmlNodePtr node,
|
|
xmlNodePtr inst,
|
|
xsltStylePreCompPtr comp)
|
|
{
|
|
xmlNodePtr comment;
|
|
|
|
if (ctxt == NULL) {
|
|
xsltGenericError(xsltGenericErrorContext,
|
|
"xsltExtElementTest: no transformation context\n");
|
|
return;
|
|
}
|
|
if (node == NULL) {
|
|
xsltGenericError(xsltGenericErrorContext,
|
|
"xsltExtElementTest: no current node\n");
|
|
return;
|
|
}
|
|
if (inst == NULL) {
|
|
xsltGenericError(xsltGenericErrorContext,
|
|
"xsltExtElementTest: no instruction\n");
|
|
return;
|
|
}
|
|
if (ctxt->insert == NULL) {
|
|
xsltGenericError(xsltGenericErrorContext,
|
|
"xsltExtElementTest: no insertion point\n");
|
|
return;
|
|
}
|
|
comment =
|
|
xmlNewComment((const xmlChar *)
|
|
"libxslt:test element test worked");
|
|
xmlAddChild(ctxt->insert, comment);
|
|
}</pre>
|
|
|
|
<h2><a name="shutdown">The shutdown of a module</a></h2>
|
|
|
|
<p>When the XSLT processor ends a transformation, the shutdown function (if
|
|
it exists) of all the modules initialized are called.The
|
|
xsltExtShutdownFunction type defines the interface for a shutdown
|
|
function:</p>
|
|
<pre>/**
|
|
* xsltExtShutdownFunction:
|
|
* @ctxt: an XSLT transformation context
|
|
* @URI: the namespace URI for the extension
|
|
* @data: the data associated to this module
|
|
*
|
|
* A function called at shutdown time of an XSLT extension module
|
|
*/
|
|
typedef void (*xsltExtShutdownFunction) (xsltTransformContextPtr ctxt,
|
|
const xmlChar *URI,
|
|
void *data);</pre>
|
|
|
|
<p>this is really similar to a module initialization function except a third
|
|
argument is passed, it's the value that was returned by the initialization
|
|
function. This allow to deallocate resources from the module for example
|
|
close the connection to the database to keep the same example.</p>
|
|
|
|
<h2><a name="Future">Future work</a></h2>
|
|
|
|
<p>Well some of the pieces missing:</p>
|
|
<ul>
|
|
<li>a way to load shared libraries to instanciate new modules</li>
|
|
<li>a better detection of extension function usage and their registration
|
|
without having to use the extension prefix which ought to be reserved to
|
|
element extensions.</li>
|
|
<li>more examples</li>
|
|
<li>implementations of the <a href="http://www.exslt.org/">EXSLT</a> common
|
|
extension libraries, I probably won't have the time needed to do this but
|
|
this would be a great contribution.
|
|
<p></p>
|
|
</li>
|
|
</ul>
|
|
|
|
<p><a href="mailto:daniel@veillard.com">Daniel Veillard</a></p>
|
|
|
|
<p>$Id$</p>
|
|
</body>
|
|
</html>
|