# HG changeset patch # User Daniel Veillard <veillard@src.gnome.org> # Date 980010136 0 # Sat Jan 20 17:02:16 2001 +0000 # Node ID 9f140b4e08ad47b826b2ae3e76283d3f52bc80f6 # Parent 633bc556eed679d8b110123258f2cedb2a0c1246 Working on variables implementation: - libxslt/transform.c libxslt/variables.[ch] libxslt/xslt.c libxslt/xsltInternals.h libxslt/xsltutils.h: changed a few structure to add an execution stack with variables. Tree valued variables still missing. - TODO: updated Daniel diff --git a/ChangeLog b/ChangeLog --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,11 @@ +Sat Jan 20 17:59:20 CET 2001 Daniel Veillard <Daniel.Veillard@imag.fr> + + * libxslt/transform.c libxslt/variables.[ch] libxslt/xslt.c + libxslt/xsltInternals.h libxslt/xsltutils.h: changed a few + structure to add an execution stack with variables. Tree + valued variables still missing. + * TODO: updated + Fri Jan 19 13:16:57 CET 2001 Daniel Veillard <Daniel.Veillard@imag.fr> * libxslt/xslt.c: check version on stylesheets diff --git a/TODO b/TODO --- a/TODO +++ b/TODO @@ -2,6 +2,7 @@ - should transforms for a given stylesheet be thread clean, or can a stylesheet be enriched with document specific informations and cleaned up later ? + => currently stylesheet manipulation is not reentrant. - seems that saving back XSLT stylesheet from a compiled form might be a bit ugly ... diff --git a/libxslt/transform.c b/libxslt/transform.c --- a/libxslt/transform.c +++ b/libxslt/transform.c @@ -29,6 +29,7 @@ #include "xsltutils.h" #include "pattern.h" #include "transform.h" +#include "variables.h" #define DEBUG_PROCESS @@ -39,31 +40,6 @@ #define IS_BLANK_NODE(n) \ (((n)->type == XML_TEXT_NODE) && (xsltIsBlank((n)->content))) -/* - * Types are private: - */ - -typedef enum xsltOutputType { - XSLT_OUTPUT_XML = 0, - XSLT_OUTPUT_HTML, - XSLT_OUTPUT_TEXT -} xsltOutputType; - -typedef struct _xsltTransformContext xsltTransformContext; -typedef xsltTransformContext *xsltTransformContextPtr; -struct _xsltTransformContext { - xsltStylesheetPtr style; /* the stylesheet used */ - xsltOutputType type; /* the type of output */ - - xmlDocPtr doc; /* the current doc */ - xmlNodePtr node; /* the current node */ - xmlNodeSetPtr nodeList; /* the current node list */ - - xmlDocPtr output; /* the resulting document */ - xmlNodePtr insert; /* the insertion node */ - - xmlXPathContextPtr xpathCtxt; /* the XPath context */ -}; /************************************************************************ * * @@ -104,6 +80,7 @@ return; if (ctxt->xpathCtxt != NULL) xmlXPathFreeContext(ctxt->xpathCtxt); + xsltFreeVariableHashes(ctxt); memset(ctxt, -1, sizeof(xsltTransformContext)); xmlFree(ctxt); } @@ -330,7 +307,7 @@ /* TODO: attribute value template */ if (ns) { #if LIBXML_VERSION > 20211 - attr = xmlSetNsProp(ctxt->insert, ncname, ns->href, value); + attr = xmlSetNsProp(ctxt->insert, ns, ncname, value); #else xsltGenericError(xsltGenericErrorContext, "xsl:attribute: recompile against newer libxml version\n"); @@ -403,6 +380,7 @@ ctxt->xpathCtxt = xmlXPathNewContext(ctxt->doc); if (ctxt->xpathCtxt == NULL) goto error; + XSLT_REGISTER_VARIABLE_LOOKUP(ctxt); } xpathParserCtxt = xmlXPathNewParserContext(prop, ctxt->xpathCtxt); @@ -643,6 +621,7 @@ ctxt->xpathCtxt = xmlXPathNewContext(ctxt->doc); if (ctxt->xpathCtxt == NULL) goto error; + XSLT_REGISTER_VARIABLE_LOOKUP(ctxt); } xpathParserCtxt = xmlXPathNewParserContext(prop, ctxt->xpathCtxt); @@ -775,6 +754,7 @@ xmlNodePtr list) { xmlNodePtr cur, insert, copy; xmlNodePtr oldInsert; + int has_variables = 0; oldInsert = insert = ctxt->insert; /* @@ -818,6 +798,11 @@ ctxt->insert = insert; xsltAttribute(ctxt, node, cur); ctxt->insert = oldInsert; + } else if (IS_XSLT_NAME(cur, "variable")) { + if (has_variables == 0) { + xsltPushStack(ctxt); + } + xsltParseStylesheetVariable(ctxt, cur); } else { #ifdef DEBUG_PROCESS xsltGenericError(xsltGenericDebugContext, @@ -889,6 +874,9 @@ } } while (cur != NULL); } + if (has_variables != 0) { + xsltPopStack(ctxt); + } } /** @@ -926,6 +914,7 @@ ctxt->xpathCtxt = xmlXPathNewContext(ctxt->doc); if (ctxt->xpathCtxt == NULL) goto error; + XSLT_REGISTER_VARIABLE_LOOKUP(ctxt); } xpathParserCtxt = xmlXPathNewParserContext(prop, ctxt->xpathCtxt); if (xpathParserCtxt == NULL) @@ -1008,6 +997,7 @@ ctxt->xpathCtxt = xmlXPathNewContext(ctxt->doc); if (ctxt->xpathCtxt == NULL) goto error; + XSLT_REGISTER_VARIABLE_LOOKUP(ctxt); } xpathParserCtxt = xmlXPathNewParserContext(prop, ctxt->xpathCtxt); if (xpathParserCtxt == NULL) diff --git a/libxslt/variables.c b/libxslt/variables.c --- a/libxslt/variables.c +++ b/libxslt/variables.c @@ -32,6 +32,238 @@ */ +typedef enum { + XSLT_ELEM_VARIABLE=1, + XSLT_ELEM_PARAM +} xsltElem; + +typedef struct _xsltStackElem xsltStackElem; +typedef xsltStackElem *xsltStackElemPtr; +struct _xsltStackElem { + struct _xsltStackElem *next;/* chained list */ + xsltElem elem; /* type of the element */ + int computed; /* was the evaluation done */ + xmlChar *name; /* the local part of the name QName */ + xmlChar *nameURI; /* the URI part of the name QName */ + xmlXPathObjectPtr value; /* The value if computed */ + xmlChar *select; /* the eval string */ +}; + +typedef struct _xsltStack xsltStack; +typedef xsltStack *xsltStackPtr; +struct _xsltStack { + int cur; + int max; + xsltStackElemPtr elems[50]; +}; + + +/************************************************************************ + * * + * Module interfaces * + * * + ************************************************************************/ + +/** + * xsltNewParserContext: + * + * Create a new XSLT ParserContext + * + * Returns the newly allocated xsltParserStackElem or NULL in case of error + */ +xsltStackElemPtr +xsltNewStackElem(void) { + xsltStackElemPtr cur; + + cur = (xsltStackElemPtr) xmlMalloc(sizeof(xsltStackElem)); + if (cur == NULL) { + xsltGenericError(xsltGenericErrorContext, + "xsltNewStackElem : malloc failed\n"); + return(NULL); + } + memset(cur, 0, sizeof(xsltStackElem)); + cur->computed = 0; + return(cur); +} + +/** + * xsltFreeStackElem: + * @elem: an XSLT stack element + * + * Free up the memory allocated by @elem + */ +void +xsltFreeStackElem(xsltStackElemPtr elem) { + if (elem == NULL) + return; + if (elem->name != NULL) + xmlFree(elem->name); + if (elem->nameURI != NULL) + xmlFree(elem->nameURI); + if (elem->select != NULL) + xmlFree(elem->select); + if (elem->value != NULL) + xmlXPathFreeObject(elem->value); + + memset(elem, -1, sizeof(xsltStackElem)); + xmlFree(elem); +} + +/** + * xsltFreeStackElemList: + * @elem: an XSLT stack element + * + * Free up the memory allocated by @elem + */ +void +xsltFreeStackElemList(xsltStackElemPtr elem) { + xsltStackElemPtr next; + + while(elem != NULL) { + next = elem->next; + xsltFreeStackElem(elem); + elem = next; + } +} + +/** + * xsltAddStackElem: + * @ctxt: xn XSLT transformation context + * @elem: a stack element + * + * add a new element at this level of the stack. + * + * Returns 0 in case of success, -1 in case of failure. + */ +int +xsltAddStackElem(xsltTransformContextPtr ctxt, xsltStackElemPtr elem) { + xsltStackPtr stack; + + if ((ctxt == NULL) || (elem == NULL)) + return(-1); + + stack = ctxt->variablesHash; + if (stack == NULL) { + /* TODO make stack size dynamic !!! */ + stack = xmlMalloc(sizeof (xsltStack)); + if (stack == NULL) { + xsltGenericError(xsltGenericErrorContext, + "xsltPushStack : malloc failed\n"); + return(-1); + } + memset(stack, 0, sizeof(xsltStack)); + ctxt->variablesHash = stack; + stack->cur = 0; + stack->max = 50; + } + /* TODO: check that there is no conflict with existing values + * at that level */ + elem->next = stack->elems[stack->cur]; + stack->elems[stack->cur] = elem; + return(0); +} + +/** + * xsltPushStack: + * @ctxt: xn XSLT transformation context + * + * Push a new level on the ctxtsheet interprestation stack + */ +void +xsltPushStack(xsltTransformContextPtr ctxt) { + xsltStackPtr stack; + + if (ctxt == NULL) + return; + + stack = ctxt->variablesHash; + if (stack == NULL) { + /* TODO make stack size dynamic !!! */ + stack = xmlMalloc(sizeof (xsltStack)); + if (stack == NULL) { + xsltGenericError(xsltGenericErrorContext, + "xsltPushStack : malloc failed\n"); + return; + } + memset(stack, 0, sizeof(xsltStack)); + ctxt->variablesHash = stack; + stack->cur = 0; + stack->max = 50; + } + if (stack->cur >= stack->max + 1) { + TODO /* make stack size dynamic !!! */ + xsltGenericError(xsltGenericErrorContext, + "xsltPushStack : overflow\n"); + return; + } + stack->cur++; + stack->elems[stack->cur] = NULL; +} + +/** + * xsltPopStack: + * @ctxt: an XSLT transformation context + * + * Pop a level on the ctxtsheet interprestation stack + */ +void +xsltPopStack(xsltTransformContextPtr ctxt) { + xsltStackPtr stack; + + if (ctxt == NULL) + return; + + stack = ctxt->variablesHash; + if (stack == NULL) + return; + + xsltFreeStackElemList(stack->elems[stack->cur]); + stack->elems[stack->cur] = NULL; + stack->cur--; +} + +/** + * xsltStackLookup: + * @ctxt: an XSLT transformation context + * @name: the local part of the name + * @nameURI: the URI part of the name + * + * Locate an element in the stack based on its name. + */ +xsltStackElemPtr +xsltStackLookup(xsltTransformContextPtr ctxt, const xmlChar *name, + const xmlChar *nameURI) { + xsltStackPtr stack; + int i; + xsltStackElemPtr cur; + + if ((ctxt == NULL) || (name == NULL)) + return(NULL); + + stack = ctxt->variablesHash; + if (stack == NULL) + return(NULL); + + for (i = stack->cur;i >= 0;i--) { + cur = stack->elems[i]; + while (cur != NULL) { + if (xmlStrEqual(cur->name, name)) { + if (nameURI == NULL) { + if (cur->nameURI == NULL) + return(cur); + } else { + if ((cur->nameURI != NULL) && + (xmlStrEqual(cur->nameURI, nameURI))) + return(cur); + } + + } + cur = cur->next; + } + } + return(NULL); +} + /************************************************************************ * * * Module interfaces * @@ -40,10 +272,11 @@ /** * xsltRegisterVariable: - * @style: the XSLT stylesheet + * @ctxt: the XSLT transformation context * @name: the variable name * @ns_uri: the variable namespace URI - * @value: the variable value or NULL + * @select: the expression which need to be evaluated to generate a value + * @value: the variable value if select is NULL * * Register a new variable value. If @value is NULL it unregisters * the variable @@ -51,26 +284,63 @@ * Returns 0 in case of success, -1 in case of error */ int -xsltRegisterVariable(xsltStylesheetPtr style, const xmlChar *name, - const xmlChar *ns_uri, xmlXPathObjectPtr value) { - if (style == NULL) +xsltRegisterVariable(xsltTransformContextPtr ctxt, const xmlChar *name, + const xmlChar *ns_uri, const xmlChar *select, + xmlXPathObjectPtr value) { + xsltStackElemPtr elem; + if (ctxt == NULL) return(-1); if (name == NULL) return(-1); - if (style->variablesHash == NULL) - style->variablesHash = xmlHashCreate(0); - if (style->variablesHash == NULL) + elem = xsltNewStackElem(); + if (elem == NULL) return(-1); - return(xmlHashUpdateEntry2((xmlHashTablePtr) style->variablesHash, - name, ns_uri, - (void *) value, - (xmlHashDeallocator) xmlXPathFreeObject)); + elem->name = xmlStrdup(name); + elem->select = xmlStrdup(select); + if (ns_uri) + elem->nameURI = xmlStrdup(ns_uri); + elem->value = value; + xsltAddStackElem(ctxt, elem); + if (elem->select != NULL) { + xmlXPathObjectPtr result, tmp; + xmlXPathParserContextPtr xpathParserCtxt; + + xpathParserCtxt = xmlXPathNewParserContext(elem->select, + ctxt->xpathCtxt); + if (xpathParserCtxt == NULL) + goto error; + xmlXPathEvalExpr(xpathParserCtxt); + result = valuePop(xpathParserCtxt); + do { + tmp = valuePop(xpathParserCtxt); + if (tmp != NULL) { + xmlXPathFreeObject(tmp); + } + } while (tmp != NULL); + + if (result == NULL) { +#ifdef DEBUG_PROCESS + xsltGenericDebug(xsltGenericDebugContext, + "Evaluating variable %s failed\n"); +#endif + } + if (xpathParserCtxt != NULL) + xmlXPathFreeParserContext(xpathParserCtxt); + if (result != NULL) { + if (elem->value != NULL) + xmlXPathFreeObject(elem->value); + elem->value = result; + elem->computed = 1; + } + } +error: + return(0); } /** * xsltVariableLookup: - * @style: the XSLT stylesheet + * @ctxt: the XSLT transformation context * @name: the variable name * @ns_uri: the variable namespace URI * @@ -80,32 +350,162 @@ * Returns the value or NULL if not found */ xmlXPathObjectPtr -xsltVariableLookup(xsltStylesheetPtr style, const xmlChar *name, +xsltVariableLookup(xsltTransformContextPtr ctxt, const xmlChar *name, const xmlChar *ns_uri) { - if (style == NULL) + xsltStackElemPtr elem; + + if (ctxt == NULL) return(NULL); - if (style->variablesHash == NULL) - return(NULL); - if (name == NULL) + elem = xsltStackLookup(ctxt, name, ns_uri); + if (elem == NULL) { + TODO /* searching on other ctxtsheets ? */ return(NULL); - - return((xmlXPathObjectPtr) - xmlHashLookup2((xmlHashTablePtr) style->variablesHash, - name, ns_uri)); + } + if (!elem->computed) { +#ifdef DEBUG_VARIABLE + xsltGenericDebug(xsltGenericDebugContext, + "uncomputed variable %s\n", name); +#endif + TODO /* Variable value computation needed */ + } +error: + if (elem->value != NULL) + return(xmlXPathObjectCopy(elem->value)); + return(NULL); } /** * xsltFreeVariableHashes: - * @style: an XSLT stylesheet + * @ctxt: an XSLT transformation context * * Free up the memory used by xsltAddVariable/xsltGetVariable mechanism */ void -xsltFreeVariableHashes(xsltStylesheetPtr style) { - if (style->variablesHash != NULL) - xmlHashFree((xmlHashTablePtr) style->variablesHash, - (xmlHashDeallocator) xmlXPathFreeObject); +xsltFreeVariableHashes(xsltTransformContextPtr ctxt) { + xsltStackPtr stack; + int i; + + if (ctxt == NULL) + return; + + stack = ctxt->variablesHash; + if (stack == NULL) + return; + + for (i = 0; i <= stack->cur;i++) { + xsltFreeStackElemList(stack->elems[i]); + } + xmlFree(stack); } +/** + * xsltParseStylesheetVariable: + * @ctxt: the XSLT transformation context + * @template: the "variable" name + * + * parse an XSLT transformation context variable name and record + * its value. + */ + +void +xsltParseStylesheetVariable(xsltTransformContextPtr ctxt, xmlNodePtr cur) { + xmlChar *name, *ncname, *prefix; + xmlChar *select; + xmlXPathObjectPtr value = NULL; + + if ((cur == NULL) || (ctxt == NULL)) + return; + + name = xmlGetNsProp(cur, (const xmlChar *)"name", XSLT_NAMESPACE); + if (name == NULL) { + xsltGenericError(xsltGenericErrorContext, + "xsl:variable : missing name attribute\n"); + return; + } + +#ifdef DEBUG_VARIABLE + if (ret != NULL) + xsltGenericDebug(xsltGenericDebugContext, + "Parsing variable %s\n", name); +#endif + + select = xmlGetNsProp(cur, (const xmlChar *)"select", XSLT_NAMESPACE); + if (select == NULL) { + if (cur->children == NULL) + value = xmlXPathNewCString(""); + else + value = xmlXPathNewNodeSet(cur->children); + } else { + if (cur->children != NULL) + xsltGenericError(xsltGenericErrorContext, + "xsl:variable : content shuld be empty since select is present \n"); + } + + ncname = xmlSplitQName2(name, &prefix); + + if (ncname != NULL) { + if (prefix != NULL) { + xmlNsPtr ns; + + ns = xmlSearchNs(cur->doc, cur, prefix); + if (ns == NULL) { + xsltGenericError(xsltGenericErrorContext, + "xsl:variable : no namespace bound to prefix %s\n", prefix); + } else { + xsltRegisterVariable(ctxt, ncname, ns->href, select, value); + } + xmlFree(prefix); + } else { + xsltRegisterVariable(ctxt, ncname, NULL, select, value); + } + xmlFree(ncname); + } else { + xsltRegisterVariable(ctxt, name, NULL, select, value); + } + + xmlFree(name); + if (select != NULL) + xmlFree(select); + +} + +/** + * xsltVariableLookup: + * @ctxt: a void * but the the XSLT transformation context actually + * @name: the variable name + * @ns_uri: the variable namespace URI + * + * This is the entry point when a varibale is needed by the XPath + * interpretor. + * + * Returns the value or NULL if not found + */ +xmlXPathObjectPtr +xsltXPathVariableLookup(void *ctxt, const xmlChar *name, + const xmlChar *ns_uri) { + xsltTransformContextPtr context; + xmlXPathObjectPtr ret; + + if ((ctxt == NULL) || (name == NULL)) + return(NULL); + +#ifdef DEBUG_VARIABLE + xsltGenericDebug(xsltGenericDebugContext, + "Lookup variable %s\n", name); +#endif + context = (xsltTransformContextPtr) ctxt; + ret = xsltVariableLookup(context, name, ns_uri); + if (ret == NULL) { + xsltGenericError(xsltGenericErrorContext, + "unregistered variable %s\n", name); + } +#ifdef DEBUG_VARIABLE + if (ret != NULL) + xsltGenericDebug(xsltGenericDebugContext, + "found variable %s\n", name); +#endif + return(ret); +} + diff --git a/libxslt/variables.h b/libxslt/variables.h --- a/libxslt/variables.h +++ b/libxslt/variables.h @@ -10,20 +10,37 @@ #define __XML_XSLT_VARIABLES_H__ #include <libxml/xpath.h> +#include <libxml/xpathInternals.h> #include "xsltInternals.h" #ifdef __cplusplus extern "C" { #endif -void xsltFreeVariableHashes (xsltStylesheetPtr style); -xmlXPathObjectPtr xsltVariableLookup (xsltStylesheetPtr style, +#define XSLT_REGISTER_VARIABLE_LOOKUP(ctxt) \ + xmlXPathRegisterVariableLookup((ctxt)->xpathCtxt, \ + xsltXPathVariableLookup, (void *)(ctxt)) + +/* + * Interfaces for the variable module. + */ + +void xsltPushStack (xsltTransformContextPtr ctxt); +void xsltPopStack (xsltTransformContextPtr ctxt); +void xsltParseStylesheetVariable (xsltTransformContextPtr ctxt, + xmlNodePtr cur); +void xsltFreeVariableHashes (xsltTransformContextPtr ctxt); +xmlXPathObjectPtr xsltVariableLookup (xsltTransformContextPtr ctxt, const xmlChar *name, const xmlChar *ns_uri); -int xsltRegisterVariable (xsltStylesheetPtr style, +int xsltRegisterVariable (xsltTransformContextPtr ctxt, const xmlChar *name, const xmlChar *ns_uri, + const xmlChar *select, xmlXPathObjectPtr value); +xmlXPathObjectPtr xsltXPathVariableLookup (void *ctxt, + const xmlChar *name, + const xmlChar *ns_uri); #ifdef __cplusplus } #endif diff --git a/libxslt/xslt.c b/libxslt/xslt.c --- a/libxslt/xslt.c +++ b/libxslt/xslt.c @@ -162,7 +162,6 @@ return; xsltFreeTemplateHashes(sheet); - xsltFreeVariableHashes(sheet); xsltFreeTemplateList(sheet->templates); if (sheet->doc != NULL) xmlFreeDoc(sheet->doc); diff --git a/libxslt/xsltInternals.h b/libxslt/xsltInternals.h --- a/libxslt/xsltInternals.h +++ b/libxslt/xsltInternals.h @@ -12,6 +12,7 @@ #include <libxml/tree.h> #include <libxml/hash.h> +#include <libxml/xpath.h> #include <libxslt/xslt.h> #ifdef __cplusplus @@ -65,12 +66,6 @@ void *templatesHash; /* hash table or wherever compiled templates informations are stored */ /* - * Variable descriptions - */ - void *variablesHash; /* hash table or wherever variables - informations are stored */ - - /* * Output related stuff. */ xmlChar *method; /* the output method */ @@ -87,6 +82,33 @@ /* + * The in-memory structure corresponding to an XSLT Transformation + */ +typedef enum xsltOutputType { + XSLT_OUTPUT_XML = 0, + XSLT_OUTPUT_HTML, + XSLT_OUTPUT_TEXT +} xsltOutputType; + +typedef struct _xsltTransformContext xsltTransformContext; +typedef xsltTransformContext *xsltTransformContextPtr; +struct _xsltTransformContext { + xsltStylesheetPtr style; /* the stylesheet used */ + xsltOutputType type; /* the type of output */ + + xmlDocPtr doc; /* the current doc */ + xmlNodePtr node; /* the current node */ + xmlNodeSetPtr nodeList; /* the current node list */ + + xmlDocPtr output; /* the resulting document */ + xmlNodePtr insert; /* the insertion node */ + + xmlXPathContextPtr xpathCtxt; /* the XPath context */ + void *variablesHash; /* hash table or wherever variables + informations are stored */ +}; + +/* * Functions associated to the internal types */ xsltStylesheetPtr xsltParseStylesheetFile (const xmlChar* filename); diff --git a/libxslt/xsltutils.h b/libxslt/xsltutils.h --- a/libxslt/xsltutils.h +++ b/libxslt/xsltutils.h @@ -24,6 +24,10 @@ void xmlXPathBooleanFunction(xmlXPathParserContextPtr ctxt, int nargs); xmlAttrPtr xmlSetNsProp (xmlNodePtr node, xmlNsPtr ns, const xmlChar *name, const xmlChar *value); +/********* +void xmlXPathRegisterVariableLookup(xmlXPathContextPtr ctxt, + xmlXPathVariableLookupFunc f, void *data) + *********/ /* * Useful macros