From bdffe03d8d210d8389839433b0f687715a05c555 Mon Sep 17 00:00:00 2001 From: Daniel Veillard Date: Fri, 6 Jul 2001 15:44:44 +0000 Subject: [PATCH] Writing documentation blows but is really needed * doc/extensions.html doc/internals.html doc/xslt.html: added a documentation on writing libxslt extensions, and added links to the main page * libxslt/functions.c libxslt/xsltInternals.h Daniel --- ChangeLog | 7 + doc/extensions.html | 420 ++++++++++++++++++++++++++++++++++++++++++++++++ doc/internals.html | 19 ++- doc/xslt.html | 4 + libxslt/functions.c | 2 +- libxslt/xsltInternals.h | 3 +- 6 files changed, 449 insertions(+), 6 deletions(-) create mode 100644 doc/extensions.html diff --git a/ChangeLog b/ChangeLog index 0bee06b..c72b614 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,10 @@ +Fri Jul 6 17:42:06 CEST 2001 Daniel Veillard + + * doc/extensions.html doc/internals.html doc/xslt.html: + added a documentation on writing libxslt extensions, and + added links to the main page + * libxslt/functions.c libxslt/xsltInternals.h + Fri Jul 6 14:30:00 HKT 2001 William Brack * cleaned up many comments and error messages diff --git a/doc/extensions.html b/doc/extensions.html new file mode 100644 index 0000000..a866acc --- /dev/null +++ b/doc/extensions.html @@ -0,0 +1,420 @@ + + + Writing extensions for XSLT C library for Gnome + + + + + +

+ +

Writing extensions for XSLT C library for Gnome

+ +

+ +

Location: http://xmlsoft.org/XSLT/extensions.html

+ +

Libxslt home page: http://xmlsoft.org/XSLT/

+ +

mailing-list archives: http://mail.gnome.org/archives/xslt/

+ +

Version: $Revision$

+ +

Table of content

+ + +

Introduction

+ +

This document describes the work needed to write extensions to the standard +XSLT library for use with libxslt, the +XSLT C library developped for the Gnome project.

+ +

Before starting reading this document it is highly recommended to get +familiar with the libxslt internals.

+ +

Note: this documentation is by definition incomplete and I am not good at +spelling, grammar, so patches and suggestions are really welcome.

+ +

Basics

+ +

The XSLT specification provides two +ways to extend an XSLT engine:

+ + +

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).

+ +

libxslt provides a few extensions itself, either in libxslt namespace +"http://xmlsoft.org/XSLT/" or in other namepsace for well known extensions +provided by other XSLT processors like Saxon, Xalan or XT.

+ +

Extension modules

+ +

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.

+ +

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 extension-element-prefixes on the +xsl:stylesheet +element.

+ +

And extension module is defined by 3 objects:

+
    +
  • the namespace name associated
  • +
  • an initialization function
  • +
  • a shutdown function
  • +
+ +

Registering a module

+ +

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).

+ +

So the current way to register a module is to link the code implementing it +with the application and to call a registration function:

+
int xsltRegisterExtModule(const xmlChar *URI,
+                          xsltExtInitFunction initFunc,
+                          xsltExtShutdownFunction shutdownFunc);
+ +

The associated header is read by:

+
#include<libxslt/extensions.h>
+ +

which also defines the type for the initialization and shutdown +functions

+
    +

  • +
  • +
  • +
+ +

Loading a module

+ +

Once the module URI has been registered and if the XSLT processor detects +that a given stylesheet need the functionalities of an extended module, this +one is initialized.

+ +

The xsltExtInitFunction type defines the interface for an initialization +function:

+
/**
+ * 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);
+ +

There is 3 things to notice:

+
    +
  • the function gets passed the namespace name URI as an argument, this + allow a single function to provide the initialization for multiple logical + modules
  • +
  • it also get 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
  • +
  • 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
  • +
+ +

What this function is expected to do is:

+
    +
  • prepare the context for this module (like opening the database + connection)
  • +
  • registers the extensions specific to this module
  • +
+ +

Registering an extension function

+ +

There is a single call to do this registration:

+
int xsltRegisterExtFunction(xsltTransformContextPtr ctxt,
+                            const xmlChar *name,
+                            const xmlChar *URI,
+                            xmlXPathEvalFunc function);
+ +

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).

+ +

Implementing an extension function

+ +

The implementation of the function must have the signature of a libxml +XPath function:

+
/**
+ * 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);
+ +

The context passed to an XPath function is not an XSLT context but an XPath context. However it is possible to find +one from the other:

+
    +
  • The function xsltXPathGetTransformContext provide this lookup facility: +
    xsltTransformContextPtr
    +         xsltXPathGetTransformContext
    +                          (xmlXPathParserContextPtr ctxt);
    +
  • +
  • The xmlXPathContextPtr associated to an + xsltTransformContext is stored in the xpathCtxt + field.
  • +
+ +

The first thing an extension function may want to do is to check the +arguments passed on the stack, the nargs will precise how many of +them were provided on the XPath expression. The macros valuePop will extract +them from the XPath stack:

+
#include <libxml/xpath.h>
+#include <libxml/xpathInternals.h>
+
+xmlXPathObjectPtr obj = valuePop(ctxt); 
+ +

Note that ctxt is the XPath context not the XSLT one. It is +then possible to examine the content of the value. Check the description of XPath objects if +necessary. The following is a common sequcnce checking whether the argument +passed is a string and converting it using the built-in XPath +string() function if this is not the case:

+
if (obj->type != XPATH_STRING) {
+    valuePush(ctxt, obj);
+    xmlXPathStringFunction(ctxt, 1);
+    obj = valuePop(ctxt);
+}
+ +

Most common XPath function are availbale directly at the C level and are +exported either in <libxml/xpath.h> or in +<libxml/xpathInternals.h>.

+ +

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:

+
void * xsltGetExtData(xsltTransformContextPtr ctxt,
+                      const xmlChar *URI);
+ +

again the URI to be provided is the one used which was used when +registering the module.

+ +

Once the function finishes, don't forget to:

+
    +
  • push the return value on the stack using valuePush(ctxt, + obj)
  • +
  • deallocate the parameters passed to the function using + xmlXPathFreeObject(obj)
  • +
+ +

Examples for extension functions

+ +

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:

+
/**
+ * 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
+}
+ +

Registering an extension function

+ +

There is a single call to do this registration:

+
int xsltRegisterExtElement(xsltTransformContextPtr ctxt,
+                           const xmlChar *name,
+                           const xmlChar *URI,
+                           xsltTransformFunction function);
+ +

It is similar to the mechanism used to register an extension function, +except that the signature of an extension element implementation is different. +

+ +

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).

+ +

Implementing an extension element

+ +

The implementation of the element must have the signature of an XSLT +transformation function:

+
/** 
+ * 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);
+ +

The first argument is the XSLT transformation context. The second and third +arguments are xmlNodePtr i.e. internal memory representation of XML nodes. They are +respectively node from the the input document being transformed +by the stylesheet and inst the extension element in the +stylesheet. The last argument is comp a pointer to a precompiled +representation of inst but usually for extension function this +value is NULL by default (it could be added and associated to the +instruction in inst->_private).

+ +

The same functions are available from a function implementing an extension +element as in an extension function, including +xsltGetExtData().

+ +

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 <libxml/tree.h> 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.

+ +

Example for extension elements

+ +

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:

+
/**
+ * 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);
+}
+ +

Future work

+ +

Well some of the pieces missing:

+
    +
  • a way to load shared libraries to instanciate new modules
  • +
  • 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.
  • +
  • more examples
  • +
  • implementations of the EXSLT common + extension libraries, I probably won't have the time needed to do this this + would be a great contribution. +

    +
  • +
+ +

Daniel Veillard

+ +

$Id$

+ + diff --git a/doc/internals.html b/doc/internals.html index 0aa9b5d..21dc722 100644 --- a/doc/internals.html +++ b/doc/internals.html @@ -1,5 +1,3 @@ - The XSLT C library for Gnome explained @@ -17,6 +15,19 @@ alt="Red Hat Logo">

How does it work ?

+ +

Location: http://xmlsoft.org/XSLT/internals.html

+ +

Libxslt home page: http://xmlsoft.org/XSLT/

+ +

mailing-list archives: http://mail.gnome.org/archives/xslt/

+ +

Version: $Revision$

+ +

Table of content

diff --git a/libxslt/functions.c b/libxslt/functions.c index 5006416..3c66905 100644 --- a/libxslt/functions.c +++ b/libxslt/functions.c @@ -722,7 +722,7 @@ xsltExtFunctionTest(xmlXPathParserContextPtr ctxt, int nargs) * @inst: the instruction in the stylesheet * @comp: precomputed informations * - * Process an debug node + * Process a libxslt:test node */ static void xsltExtElementTest(xsltTransformContextPtr ctxt, xmlNodePtr node, diff --git a/libxslt/xsltInternals.h b/libxslt/xsltInternals.h index 45709af..0d7a663 100644 --- a/libxslt/xsltInternals.h +++ b/libxslt/xsltInternals.h @@ -120,7 +120,8 @@ typedef xsltStylePreComp *xsltStylePreCompPtr; * stylesheet language like xsl:if or xsl:apply-templates. */ typedef void (*xsltTransformFunction) (xsltTransformContextPtr ctxt, - xmlNodePtr node, xmlNodePtr inst, + xmlNodePtr node, + xmlNodePtr inst, xsltStylePreCompPtr comp); typedef enum { -- 2.7.4