2 * variables.c: Implementation of the variable storage and lookup
5 * http://www.w3.org/TR/1999/REC-xslt-19991116
7 * See Copyright for the status of this software.
9 * Daniel.Veillard@imag.fr
12 #include "xsltconfig.h"
16 #include <libxml/xmlmemory.h>
17 #include <libxml/tree.h>
18 #include <libxml/valid.h>
19 #include <libxml/hash.h>
20 #include <libxml/xmlerror.h>
21 #include <libxml/xpath.h>
22 #include <libxml/xpathInternals.h>
23 #include <libxml/parserInternals.h>
25 #include "xsltInternals.h"
26 #include "xsltutils.h"
27 #include "variables.h"
28 #include "transform.h"
31 #define DEBUG_VARIABLE
37 typedef struct _xsltStack xsltStack;
38 typedef xsltStack *xsltStackPtr;
42 xsltStackElemPtr elems[50];
46 /************************************************************************
50 ************************************************************************/
53 * xsltNewParserContext:
55 * Create a new XSLT ParserContext
57 * Returns the newly allocated xsltParserStackElem or NULL in case of error
60 xsltNewStackElem(void) {
63 cur = (xsltStackElemPtr) xmlMalloc(sizeof(xsltStackElem));
65 xsltGenericError(xsltGenericErrorContext,
66 "xsltNewStackElem : malloc failed\n");
69 memset(cur, 0, sizeof(xsltStackElem));
76 * @elem: an XSLT stack element
78 * Free up the memory allocated by @elem
81 xsltFreeStackElem(xsltStackElemPtr elem) {
84 if (elem->name != NULL)
86 if (elem->nameURI != NULL)
87 xmlFree(elem->nameURI);
88 if (elem->select != NULL)
89 xmlFree(elem->select);
90 if (elem->value != NULL)
91 xmlXPathFreeObject(elem->value);
93 memset(elem, -1, sizeof(xsltStackElem));
98 * xsltFreeStackElemList:
99 * @elem: an XSLT stack element
101 * Free up the memory allocated by @elem
104 xsltFreeStackElemList(xsltStackElemPtr elem) {
105 xsltStackElemPtr next;
107 while(elem != NULL) {
109 xsltFreeStackElem(elem);
116 * @ctxt: xn XSLT transformation context
117 * @elem: a stack element
119 * add a new element at this level of the stack.
121 * Returns 0 in case of success, -1 in case of failure.
124 xsltAddStackElem(xsltTransformContextPtr ctxt, xsltStackElemPtr elem) {
126 xsltStackElemPtr cur;
128 if ((ctxt == NULL) || (elem == NULL))
131 stack = ctxt->variablesHash;
133 /* TODO make stack size dynamic !!! */
134 stack = xmlMalloc(sizeof (xsltStack));
136 xsltGenericError(xsltGenericErrorContext,
137 "xsltPushStack : malloc failed\n");
140 memset(stack, 0, sizeof(xsltStack));
141 ctxt->variablesHash = stack;
145 /* TODO: check that there is no conflict with existing values
147 cur = stack->elems[stack->cur];
148 while (cur != NULL) {
149 if (xmlStrEqual(elem->name, cur->name)) {
150 if (((elem->nameURI == NULL) && (cur->nameURI == NULL)) ||
151 ((elem->nameURI != NULL) && (cur->nameURI != NULL) &&
152 (xmlStrEqual(elem->nameURI, cur->nameURI)))) {
153 xsltGenericError(xsltGenericErrorContext,
154 "redefinition of param or variable %s\n", elem->name);
155 xsltFreeStackElem(elem);
161 elem->next = stack->elems[stack->cur];
162 stack->elems[stack->cur] = elem;
168 * @ctxt: xn XSLT transformation context
170 * Push a new level on the ctxtsheet interprestation stack
173 xsltPushStack(xsltTransformContextPtr ctxt) {
179 stack = ctxt->variablesHash;
181 /* TODO make stack size dynamic !!! */
182 stack = xmlMalloc(sizeof (xsltStack));
184 xsltGenericError(xsltGenericErrorContext,
185 "xsltPushStack : malloc failed\n");
188 memset(stack, 0, sizeof(xsltStack));
189 ctxt->variablesHash = stack;
193 if (stack->cur >= stack->max + 1) {
194 TODO /* make stack size dynamic !!! */
195 xsltGenericError(xsltGenericErrorContext,
196 "xsltPushStack : overflow\n");
200 stack->elems[stack->cur] = NULL;
205 * @ctxt: an XSLT transformation context
207 * Pop a level on the ctxtsheet interprestation stack
210 xsltPopStack(xsltTransformContextPtr ctxt) {
216 stack = ctxt->variablesHash;
220 xsltFreeStackElemList(stack->elems[stack->cur]);
221 stack->elems[stack->cur] = NULL;
227 * @ctxt: an XSLT transformation context
228 * @name: the local part of the name
229 * @nameURI: the URI part of the name
231 * Locate an element in the stack based on its name.
234 xsltStackLookup(xsltTransformContextPtr ctxt, const xmlChar *name,
235 const xmlChar *nameURI) {
236 xsltStackElemPtr ret = NULL;
239 xsltStackElemPtr cur;
241 if ((ctxt == NULL) || (name == NULL))
244 stack = ctxt->variablesHash;
248 for (i = stack->cur;i >= 0;i--) {
249 cur = stack->elems[i];
250 while (cur != NULL) {
251 if (xmlStrEqual(cur->name, name)) {
252 /* TODO: double check param binding */
253 if (nameURI == NULL) {
254 if (cur->nameURI == NULL) {
255 if (cur->type == XSLT_ELEM_PARAM)
261 if ((cur->nameURI != NULL) &&
262 (xmlStrEqual(cur->nameURI, nameURI))) {
263 if (cur->type == XSLT_ELEM_PARAM)
277 /************************************************************************
279 * Module interfaces *
281 ************************************************************************/
285 * @ctxt: the XSLT transformation context
286 * @elem: the variable or parameter.
288 * Evaluate a variable value.
290 * Returns 0 in case of success, -1 in case of error
293 xsltEvalVariables(xsltTransformContextPtr ctxt, xsltStackElemPtr elem) {
294 xmlXPathParserContextPtr xpathParserCtxt;
296 if ((ctxt == NULL) || (elem == NULL))
299 #ifdef DEBUG_VARIABLE
300 xsltGenericDebug(xsltGenericDebugContext,
301 "Evaluating variable %s\n", elem->name);
303 if (elem->select != NULL) {
304 xmlXPathObjectPtr result, tmp;
306 xpathParserCtxt = xmlXPathNewParserContext(elem->select,
308 if (xpathParserCtxt == NULL)
310 ctxt->xpathCtxt->node = (xmlNodePtr) ctxt->node;
311 xmlXPathEvalExpr(xpathParserCtxt);
312 result = valuePop(xpathParserCtxt);
314 tmp = valuePop(xpathParserCtxt);
316 xmlXPathFreeObject(tmp);
318 } while (tmp != NULL);
320 if (result == NULL) {
321 xsltGenericError(xsltGenericErrorContext,
322 "Evaluating global variable %s failed\n");
324 if (xpathParserCtxt != NULL)
325 xmlXPathFreeParserContext(xpathParserCtxt);
326 if (result != NULL) {
327 #ifdef DEBUG_VARIABLE
328 if ((xsltGenericDebugContext == stdout) ||
329 (xsltGenericDebugContext == stderr))
330 xmlXPathDebugDumpObject((FILE *)xsltGenericDebugContext,
333 if (elem->value != NULL)
334 xmlXPathFreeObject(elem->value);
335 elem->value = result;
339 if (elem->tree == NULL) {
340 elem->value = xmlXPathNewCString("");
344 * This is a result tree fragment.
346 xmlNodePtr container;
347 xmlNodePtr oldInsert, oldNode;
349 container = xmlNewDocNode(ctxt->output, NULL,
350 (const xmlChar *) "fake", NULL);
351 if (container == NULL)
353 oldInsert = ctxt->insert;
354 oldNode = ctxt->node;
355 ctxt->insert = container;
357 xsltApplyOneTemplate(ctxt, ctxt->node, NULL, elem->tree);
359 ctxt->insert = oldInsert;
360 ctxt->node = oldNode;
361 elem->value = xmlXPathNewValueTree(container);
368 * xsltEvalGlobalVariables:
369 * @ctxt: the XSLT transformation context
371 * Evaluate the global variables of a stylesheet. This need to be
372 * done on parsed stylesheets before starting to apply transformations
374 * Returns 0 in case of success, -1 in case of error
377 xsltEvalGlobalVariables(xsltTransformContextPtr ctxt) {
378 xsltStackElemPtr elem;
379 xsltStylesheetPtr style;
384 #ifdef DEBUG_VARIABLE
385 xsltGenericDebug(xsltGenericDebugContext,
386 "Evaluating global variables\n");
388 ctxt->node = (xmlNodePtr) ctxt->document->doc;
390 /* TODO: handle the stylesheet cascade */
392 elem = style->variables;
394 while (elem != NULL) {
395 xsltEvalVariables(ctxt, elem);
403 * xsltRegisterGlobalVariable:
404 * @style: the XSLT transformation context
405 * @name: the variable name
406 * @ns_uri: the variable namespace URI
407 * @select: the expression which need to be evaluated to generate a value
408 * @tree: the subtree if select is NULL
409 * @param: this is a parameter actually
411 * Register a new variable value. If @value is NULL it unregisters
414 * Returns 0 in case of success, -1 in case of error
417 xsltRegisterGlobalVariable(xsltStylesheetPtr style, const xmlChar *name,
418 const xmlChar *ns_uri, const xmlChar *select,
419 xmlNodePtr tree, int param) {
420 xsltStackElemPtr elem;
426 #ifdef DEBUG_VARIABLE
428 xsltGenericDebug(xsltGenericDebugContext,
429 "Defineing global param %s\n", name);
431 xsltGenericDebug(xsltGenericDebugContext,
432 "Defineing global variable %s\n", name);
434 elem = xsltNewStackElem();
438 elem->type = XSLT_ELEM_PARAM;
440 elem->type = XSLT_ELEM_VARIABLE;
441 elem->name = xmlStrdup(name);
442 elem->select = xmlStrdup(select);
444 elem->nameURI = xmlStrdup(ns_uri);
446 elem->next = style->variables;
447 style->variables = elem;
452 * xsltRegisterVariable:
453 * @ctxt: the XSLT transformation context
454 * @name: the variable name
455 * @ns_uri: the variable namespace URI
456 * @select: the expression which need to be evaluated to generate a value
457 * @tree: the tree if select is NULL
458 * @param: this is a parameter actually
460 * Register a new variable value. If @value is NULL it unregisters
463 * Returns 0 in case of success, -1 in case of error
466 xsltRegisterVariable(xsltTransformContextPtr ctxt, const xmlChar *name,
467 const xmlChar *ns_uri, const xmlChar *select,
468 xmlNodePtr tree, int param) {
469 xsltStackElemPtr elem;
475 #ifdef DEBUG_VARIABLE
476 xsltGenericDebug(xsltGenericDebugContext,
477 "Defineing variable %s", name);
479 xsltGenericDebug(xsltGenericDebugContext,
480 " select %s", select);
481 xsltGenericDebug(xsltGenericDebugContext, "\n");
483 elem = xsltNewStackElem();
487 elem->type = XSLT_ELEM_PARAM;
489 elem->type = XSLT_ELEM_VARIABLE;
490 elem->name = xmlStrdup(name);
492 elem->select = xmlStrdup(select);
496 elem->nameURI = xmlStrdup(ns_uri);
498 xsltAddStackElem(ctxt, elem);
499 xsltEvalVariables(ctxt, elem);
504 * xsltGlobalVariableLookup:
505 * @ctxt: the XSLT transformation context
506 * @name: the variable name
507 * @ns_uri: the variable namespace URI
509 * Search in the Variable array of the context for the given
512 * Returns the value or NULL if not found
515 xsltGlobalVariableLookup(xsltTransformContextPtr ctxt, const xmlChar *name,
516 const xmlChar *ns_uri) {
517 xsltStylesheetPtr style;
518 xsltStackElemPtr elem = NULL;
521 while (style != NULL) {
522 elem = style->variables;
524 while (elem != NULL) {
525 if (xmlStrEqual(elem->name, name)) {
526 /* TODO: double check param binding */
527 if (ns_uri == NULL) {
528 if (elem->nameURI == NULL)
531 if ((elem->nameURI != NULL) &&
532 (xmlStrEqual(elem->nameURI, ns_uri)))
540 style = xsltNextImport(style);
545 if (!elem->computed) {
546 #ifdef DEBUG_VARIABLE
547 xsltGenericDebug(xsltGenericDebugContext,
548 "uncomputed global variable %s\n", name);
550 xsltEvalVariables(ctxt, elem);
552 if (elem->value != NULL)
553 return(xmlXPathObjectCopy(elem->value));
554 #ifdef DEBUG_VARIABLE
555 xsltGenericDebug(xsltGenericDebugContext,
556 "global variable not found %s\n", name);
562 * xsltVariableLookup:
563 * @ctxt: the XSLT transformation context
564 * @name: the variable name
565 * @ns_uri: the variable namespace URI
567 * Search in the Variable array of the context for the given
570 * Returns the value or NULL if not found
573 xsltVariableLookup(xsltTransformContextPtr ctxt, const xmlChar *name,
574 const xmlChar *ns_uri) {
575 xsltStackElemPtr elem;
580 elem = xsltStackLookup(ctxt, name, ns_uri);
582 return(xsltGlobalVariableLookup(ctxt, name, ns_uri));
584 if (!elem->computed) {
585 #ifdef DEBUG_VARIABLE
586 xsltGenericDebug(xsltGenericDebugContext,
587 "uncomputed variable %s\n", name);
589 xsltEvalVariables(ctxt, elem);
591 if (elem->value != NULL)
592 return(xmlXPathObjectCopy(elem->value));
593 #ifdef DEBUG_VARIABLE
594 xsltGenericDebug(xsltGenericDebugContext,
595 "variable not found %s\n", name);
602 * xsltFreeVariableHashes:
603 * @ctxt: an XSLT transformation context
605 * Free up the memory used by xsltAddVariable/xsltGetVariable mechanism
608 xsltFreeVariableHashes(xsltTransformContextPtr ctxt) {
615 stack = ctxt->variablesHash;
619 for (i = 0; i <= stack->cur;i++) {
620 xsltFreeStackElemList(stack->elems[i]);
627 * xsltParseStylesheetParam:
628 * @ctxt: the XSLT transformation context
629 * @cur: the "param" element
631 * parse an XSLT transformation param declaration and record
636 xsltParseStylesheetParam(xsltTransformContextPtr ctxt, xmlNodePtr cur) {
637 xmlChar *name, *ncname, *prefix;
639 xmlNodePtr tree = NULL;
641 if ((cur == NULL) || (ctxt == NULL))
644 name = xmlGetNsProp(cur, (const xmlChar *)"name", XSLT_NAMESPACE);
646 xsltGenericError(xsltGenericErrorContext,
647 "xsl:param : missing name attribute\n");
651 #ifdef DEBUG_VARIABLE
652 xsltGenericDebug(xsltGenericDebugContext,
653 "Parsing param %s\n", name);
656 select = xmlGetNsProp(cur, (const xmlChar *)"select", XSLT_NAMESPACE);
657 if (select == NULL) {
658 tree = cur->children;
660 if (cur->children != NULL)
661 xsltGenericError(xsltGenericErrorContext,
662 "xsl:param : content shuld be empty since select is present \n");
665 ncname = xmlSplitQName2(name, &prefix);
667 if (ncname != NULL) {
668 if (prefix != NULL) {
671 ns = xmlSearchNs(cur->doc, cur, prefix);
673 xsltGenericError(xsltGenericErrorContext,
674 "xsl:param : no namespace bound to prefix %s\n", prefix);
676 xsltRegisterVariable(ctxt, ncname, ns->href, select, tree, 1);
680 xsltRegisterVariable(ctxt, ncname, NULL, select, tree, 1);
684 xsltRegisterVariable(ctxt, name, NULL, select, tree, 1);
694 * xsltParseGlobalVariable:
695 * @style: the XSLT stylesheet
696 * @cur: the "variable" element
698 * parse an XSLT transformation variable declaration and record
703 xsltParseGlobalVariable(xsltStylesheetPtr style, xmlNodePtr cur) {
704 xmlChar *name, *ncname, *prefix;
706 xmlNodePtr tree = NULL;
708 if ((cur == NULL) || (style == NULL))
711 name = xmlGetNsProp(cur, (const xmlChar *)"name", XSLT_NAMESPACE);
713 xsltGenericError(xsltGenericErrorContext,
714 "xsl:variable : missing name attribute\n");
718 #ifdef DEBUG_VARIABLE
719 xsltGenericDebug(xsltGenericDebugContext,
720 "Parsing global variable %s\n", name);
723 select = xmlGetNsProp(cur, (const xmlChar *)"select", XSLT_NAMESPACE);
724 if (select == NULL) {
725 tree = cur->children;
727 if (cur->children != NULL)
728 xsltGenericError(xsltGenericErrorContext,
729 "xsl:variable : content shuld be empty since select is present \n");
732 ncname = xmlSplitQName2(name, &prefix);
734 if (ncname != NULL) {
735 if (prefix != NULL) {
738 ns = xmlSearchNs(cur->doc, cur, prefix);
740 xsltGenericError(xsltGenericErrorContext,
741 "xsl:variable : no namespace bound to prefix %s\n", prefix);
743 xsltRegisterGlobalVariable(style, ncname, ns->href, select, tree, 0);
747 xsltRegisterGlobalVariable(style, ncname, NULL, select, tree, 0);
751 xsltRegisterGlobalVariable(style, name, NULL, select, tree, 0);
761 * xsltParseGlobalParam:
762 * @style: the XSLT stylesheet
763 * @cur: the "param" element
765 * parse an XSLT transformation param declaration and record
770 xsltParseGlobalParam(xsltStylesheetPtr style, xmlNodePtr cur) {
771 xmlChar *name, *ncname, *prefix;
773 xmlNodePtr tree = NULL;
775 if ((cur == NULL) || (style == NULL))
778 name = xmlGetNsProp(cur, (const xmlChar *)"name", XSLT_NAMESPACE);
780 xsltGenericError(xsltGenericErrorContext,
781 "xsl:param : missing name attribute\n");
785 #ifdef DEBUG_VARIABLE
786 xsltGenericDebug(xsltGenericDebugContext,
787 "Parsing global param %s\n", name);
790 select = xmlGetNsProp(cur, (const xmlChar *)"select", XSLT_NAMESPACE);
791 if (select == NULL) {
792 tree = cur->children;
794 if (cur->children != NULL)
795 xsltGenericError(xsltGenericErrorContext,
796 "xsl:param : content shuld be empty since select is present \n");
799 ncname = xmlSplitQName2(name, &prefix);
801 if (ncname != NULL) {
802 if (prefix != NULL) {
805 ns = xmlSearchNs(cur->doc, cur, prefix);
807 xsltGenericError(xsltGenericErrorContext,
808 "xsl:param : no namespace bound to prefix %s\n", prefix);
810 xsltRegisterGlobalVariable(style, ncname, ns->href, select, tree, 1);
814 xsltRegisterGlobalVariable(style, ncname, NULL, select, tree, 1);
818 xsltRegisterGlobalVariable(style, name, NULL, select, tree, 1);
827 * xsltParseStylesheetVariable:
828 * @ctxt: the XSLT transformation context
829 * @cur: the "variable" element
831 * parse an XSLT transformation variable declaration and record
836 xsltParseStylesheetVariable(xsltTransformContextPtr ctxt, xmlNodePtr cur) {
837 xmlChar *name, *ncname, *prefix;
839 xmlNodePtr tree = NULL;
841 if ((cur == NULL) || (ctxt == NULL))
844 name = xmlGetNsProp(cur, (const xmlChar *)"name", XSLT_NAMESPACE);
846 xsltGenericError(xsltGenericErrorContext,
847 "xsl:variable : missing name attribute\n");
851 #ifdef DEBUG_VARIABLE
852 xsltGenericDebug(xsltGenericDebugContext,
853 "Parsing variable %s\n", name);
856 select = xmlGetNsProp(cur, (const xmlChar *)"select", XSLT_NAMESPACE);
857 if (select == NULL) {
858 tree = cur->children;
860 if (cur->children != NULL)
861 xsltGenericError(xsltGenericErrorContext,
862 "xsl:variable : content should be empty since select is present \n");
865 ncname = xmlSplitQName2(name, &prefix);
867 if (ncname != NULL) {
868 if (prefix != NULL) {
871 ns = xmlSearchNs(cur->doc, cur, prefix);
873 xsltGenericError(xsltGenericErrorContext,
874 "xsl:variable : no namespace bound to prefix %s\n", prefix);
876 xsltRegisterVariable(ctxt, ncname, ns->href, select, tree, 0);
880 xsltRegisterVariable(ctxt, ncname, NULL, select, tree, 0);
884 xsltRegisterVariable(ctxt, name, NULL, select, tree, 0);
894 * xsltVariableLookup:
895 * @ctxt: a void * but the the XSLT transformation context actually
896 * @name: the variable name
897 * @ns_uri: the variable namespace URI
899 * This is the entry point when a varibale is needed by the XPath
902 * Returns the value or NULL if not found
905 xsltXPathVariableLookup(void *ctxt, const xmlChar *name,
906 const xmlChar *ns_uri) {
907 xsltTransformContextPtr context;
908 xmlXPathObjectPtr ret;
910 if ((ctxt == NULL) || (name == NULL))
913 #ifdef DEBUG_VARIABLE
914 xsltGenericDebug(xsltGenericDebugContext,
915 "Lookup variable %s\n", name);
917 context = (xsltTransformContextPtr) ctxt;
918 ret = xsltVariableLookup(context, name, ns_uri);
920 xsltGenericError(xsltGenericErrorContext,
921 "unregistered variable %s\n", name);
923 #ifdef DEBUG_VARIABLE
925 xsltGenericDebug(xsltGenericDebugContext,
926 "found variable %s\n", name);