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
33 /************************************************************************
37 ************************************************************************/
40 * xsltNewParserContext:
42 * Create a new XSLT ParserContext
44 * Returns the newly allocated xsltParserStackElem or NULL in case of error
47 xsltNewStackElem(void) {
50 cur = (xsltStackElemPtr) xmlMalloc(sizeof(xsltStackElem));
52 xsltGenericError(xsltGenericErrorContext,
53 "xsltNewStackElem : malloc failed\n");
56 memset(cur, 0, sizeof(xsltStackElem));
63 * @elem: an XSLT stack element
65 * Free up the memory allocated by @elem
68 xsltFreeStackElem(xsltStackElemPtr elem) {
71 if (elem->name != NULL)
73 if (elem->nameURI != NULL)
74 xmlFree(elem->nameURI);
75 if (elem->select != NULL)
76 xmlFree(elem->select);
77 if (elem->value != NULL)
78 xmlXPathFreeObject(elem->value);
80 memset(elem, -1, sizeof(xsltStackElem));
85 * xsltFreeStackElemList:
86 * @elem: an XSLT stack element
88 * Free up the memory allocated by @elem
91 xsltFreeStackElemList(xsltStackElemPtr elem) {
92 xsltStackElemPtr next;
96 xsltFreeStackElem(elem);
102 * xsltCheckStackElem:
103 * @ctxt: xn XSLT transformation context
104 * @name: the variable name
105 * @nameURI: the variable namespace URI
107 * check wether the variable or param is already defined
109 * Returns 1 if present, 0 if not, -1 in case of failure.
112 xsltCheckStackElem(xsltTransformContextPtr ctxt, const xmlChar *name,
113 const xmlChar *nameURI) {
114 xsltStackElemPtr cur;
116 if ((ctxt == NULL) || (name == NULL))
120 while (cur != NULL) {
121 if (xmlStrEqual(name, cur->name)) {
122 if (((nameURI == NULL) && (cur->nameURI == NULL)) ||
123 ((nameURI != NULL) && (cur->nameURI != NULL) &&
124 (xmlStrEqual(nameURI, cur->nameURI)))) {
135 * @ctxt: xn XSLT transformation context
136 * @elem: a stack element
138 * add a new element at this level of the stack.
140 * Returns 0 in case of success, -1 in case of failure.
143 xsltAddStackElem(xsltTransformContextPtr ctxt, xsltStackElemPtr elem) {
144 if ((ctxt == NULL) || (elem == NULL))
148 xsltStackElemPtr cur;
151 while (cur != NULL) {
152 if (xmlStrEqual(elem->name, cur->name)) {
153 if (((elem->nameURI == NULL) && (cur->nameURI == NULL)) ||
154 ((elem->nameURI != NULL) && (cur->nameURI != NULL) &&
155 (xmlStrEqual(elem->nameURI, cur->nameURI)))) {
156 xsltGenericError(xsltGenericErrorContext,
157 "redefinition of param or variable %s\n", elem->name);
158 xsltFreeStackElem(elem);
165 elem->next = ctxt->varsTab[ctxt->varsNr - 1];
166 ctxt->varsTab[ctxt->varsNr - 1] = elem;
173 * @ctxt: an XSLT transformation context
174 * @name: the local part of the name
175 * @nameURI: the URI part of the name
177 * Locate an element in the stack based on its name.
180 xsltStackLookup(xsltTransformContextPtr ctxt, const xmlChar *name,
181 const xmlChar *nameURI) {
182 xsltStackElemPtr ret = NULL;
184 xsltStackElemPtr cur;
186 if ((ctxt == NULL) || (name == NULL))
190 * Do the lookup from the top of the stack, but
191 * don't use params being computed in a call-param
193 i = ctxt->varsNr - 1;
194 if (ctxt->varsComputed)
198 cur = ctxt->varsTab[i];
199 while (cur != NULL) {
200 if (xmlStrEqual(cur->name, name)) {
201 if (nameURI == NULL) {
202 if (cur->nameURI == NULL) {
206 if ((cur->nameURI != NULL) &&
207 (xmlStrEqual(cur->nameURI, nameURI))) {
219 /************************************************************************
221 * Module interfaces *
223 ************************************************************************/
227 * @ctxt: the XSLT transformation context
228 * @elem: the variable or parameter.
230 * Evaluate a variable value.
232 * Returns 0 in case of success, -1 in case of error
235 xsltEvalVariables(xsltTransformContextPtr ctxt, xsltStackElemPtr elem) {
236 xmlXPathParserContextPtr xpathParserCtxt;
238 if ((ctxt == NULL) || (elem == NULL))
241 #ifdef DEBUG_VARIABLE
242 xsltGenericDebug(xsltGenericDebugContext,
243 "Evaluating variable %s\n", elem->name);
245 if (elem->select != NULL) {
246 xmlXPathObjectPtr result, tmp;
248 xpathParserCtxt = xmlXPathNewParserContext(elem->select,
250 if (xpathParserCtxt == NULL)
252 ctxt->xpathCtxt->node = (xmlNodePtr) ctxt->node;
253 xmlXPathEvalExpr(xpathParserCtxt);
254 result = valuePop(xpathParserCtxt);
256 tmp = valuePop(xpathParserCtxt);
258 xmlXPathFreeObject(tmp);
260 } while (tmp != NULL);
262 if (result == NULL) {
263 xsltGenericError(xsltGenericErrorContext,
264 "Evaluating global variable %s failed\n");
266 if (xpathParserCtxt != NULL)
267 xmlXPathFreeParserContext(xpathParserCtxt);
268 if (result != NULL) {
269 #ifdef DEBUG_VARIABLE
270 if ((xsltGenericDebugContext == stdout) ||
271 (xsltGenericDebugContext == stderr))
272 xmlXPathDebugDumpObject((FILE *)xsltGenericDebugContext,
275 if (elem->value != NULL)
276 xmlXPathFreeObject(elem->value);
277 elem->value = result;
281 if (elem->tree == NULL) {
282 elem->value = xmlXPathNewCString("");
286 * This is a result tree fragment.
288 xmlNodePtr container;
289 xmlNodePtr oldInsert, oldNode;
291 container = xmlNewDocNode(ctxt->output, NULL,
292 (const xmlChar *) "fake", NULL);
293 if (container == NULL)
295 oldInsert = ctxt->insert;
296 oldNode = ctxt->node;
297 ctxt->insert = container;
299 xsltApplyOneTemplate(ctxt, ctxt->node, elem->tree, 0);
301 ctxt->insert = oldInsert;
302 ctxt->node = oldNode;
303 elem->value = xmlXPathNewValueTree(container);
310 * xsltEvalGlobalVariables:
311 * @ctxt: the XSLT transformation context
313 * Evaluate the global variables of a stylesheet. This need to be
314 * done on parsed stylesheets before starting to apply transformations
316 * Returns 0 in case of success, -1 in case of error
319 xsltEvalGlobalVariables(xsltTransformContextPtr ctxt) {
320 xsltStackElemPtr elem;
321 xsltStylesheetPtr style;
326 #ifdef DEBUG_VARIABLE
327 xsltGenericDebug(xsltGenericDebugContext,
328 "Evaluating global variables\n");
330 ctxt->node = (xmlNodePtr) ctxt->document->doc;
332 /* TODO: handle the stylesheet cascade */
334 elem = style->variables;
336 while (elem != NULL) {
337 xsltEvalVariables(ctxt, elem);
345 * xsltRegisterGlobalVariable:
346 * @style: the XSLT transformation context
347 * @name: the variable name
348 * @ns_uri: the variable namespace URI
349 * @select: the expression which need to be evaluated to generate a value
350 * @tree: the subtree if select is NULL
351 * @param: this is a parameter actually
353 * Register a new variable value. If @value is NULL it unregisters
356 * Returns 0 in case of success, -1 in case of error
359 xsltRegisterGlobalVariable(xsltStylesheetPtr style, const xmlChar *name,
360 const xmlChar *ns_uri, const xmlChar *select,
361 xmlNodePtr tree, int param) {
362 xsltStackElemPtr elem;
368 #ifdef DEBUG_VARIABLE
370 xsltGenericDebug(xsltGenericDebugContext,
371 "Defineing global param %s\n", name);
373 xsltGenericDebug(xsltGenericDebugContext,
374 "Defineing global variable %s\n", name);
376 elem = xsltNewStackElem();
380 elem->type = XSLT_ELEM_PARAM;
382 elem->type = XSLT_ELEM_VARIABLE;
383 elem->name = xmlStrdup(name);
384 elem->select = xmlStrdup(select);
386 elem->nameURI = xmlStrdup(ns_uri);
388 elem->next = style->variables;
389 style->variables = elem;
394 * xsltRegisterVariable:
395 * @ctxt: the XSLT transformation context
396 * @name: the variable name
397 * @ns_uri: the variable namespace URI
398 * @select: the expression which need to be evaluated to generate a value
399 * @tree: the tree if select is NULL
400 * @param: this is a parameter actually
402 * Register a new variable value. If @value is NULL it unregisters
405 * Returns 0 in case of success, -1 in case of error
408 xsltRegisterVariable(xsltTransformContextPtr ctxt, const xmlChar *name,
409 const xmlChar *ns_uri, const xmlChar *select,
410 xmlNodePtr tree, int param) {
411 xsltStackElemPtr elem;
417 if (xsltCheckStackElem(ctxt, name, ns_uri) != 0) {
419 xsltGenericError(xsltGenericErrorContext,
420 "xsl:variable : redefining %s\n", name);
422 #ifdef DEBUG_VARIABLE
424 xsltGenericDebug(xsltGenericDebugContext,
425 "param %s defined by caller", name);
429 #ifdef DEBUG_VARIABLE
430 xsltGenericDebug(xsltGenericDebugContext,
431 "Defineing variable %s", name);
433 xsltGenericDebug(xsltGenericDebugContext,
434 " select %s", select);
435 xsltGenericDebug(xsltGenericDebugContext, "\n");
437 elem = xsltNewStackElem();
441 elem->type = XSLT_ELEM_PARAM;
443 elem->type = XSLT_ELEM_VARIABLE;
444 elem->name = xmlStrdup(name);
446 elem->select = xmlStrdup(select);
450 elem->nameURI = xmlStrdup(ns_uri);
452 xsltEvalVariables(ctxt, elem);
453 xsltAddStackElem(ctxt, elem);
458 * xsltGlobalVariableLookup:
459 * @ctxt: the XSLT transformation context
460 * @name: the variable name
461 * @ns_uri: the variable namespace URI
463 * Search in the Variable array of the context for the given
466 * Returns the value or NULL if not found
469 xsltGlobalVariableLookup(xsltTransformContextPtr ctxt, const xmlChar *name,
470 const xmlChar *ns_uri) {
471 xsltStylesheetPtr style;
472 xsltStackElemPtr elem = NULL;
475 while (style != NULL) {
476 elem = style->variables;
478 while (elem != NULL) {
479 if (xmlStrEqual(elem->name, name)) {
480 /* TODO: double check param binding */
481 if (ns_uri == NULL) {
482 if (elem->nameURI == NULL)
485 if ((elem->nameURI != NULL) &&
486 (xmlStrEqual(elem->nameURI, ns_uri)))
494 style = xsltNextImport(style);
499 if (!elem->computed) {
500 #ifdef DEBUG_VARIABLE
501 xsltGenericDebug(xsltGenericDebugContext,
502 "uncomputed global variable %s\n", name);
504 xsltEvalVariables(ctxt, elem);
506 if (elem->value != NULL)
507 return(xmlXPathObjectCopy(elem->value));
508 #ifdef DEBUG_VARIABLE
509 xsltGenericDebug(xsltGenericDebugContext,
510 "global variable not found %s\n", name);
516 * xsltVariableLookup:
517 * @ctxt: the XSLT transformation context
518 * @name: the variable name
519 * @ns_uri: the variable namespace URI
521 * Search in the Variable array of the context for the given
524 * Returns the value or NULL if not found
527 xsltVariableLookup(xsltTransformContextPtr ctxt, const xmlChar *name,
528 const xmlChar *ns_uri) {
529 xsltStackElemPtr elem;
534 elem = xsltStackLookup(ctxt, name, ns_uri);
536 return(xsltGlobalVariableLookup(ctxt, name, ns_uri));
538 if (!elem->computed) {
539 #ifdef DEBUG_VARIABLE
540 xsltGenericDebug(xsltGenericDebugContext,
541 "uncomputed variable %s\n", name);
543 xsltEvalVariables(ctxt, elem);
545 if (elem->value != NULL)
546 return(xmlXPathObjectCopy(elem->value));
547 #ifdef DEBUG_VARIABLE
548 xsltGenericDebug(xsltGenericDebugContext,
549 "variable not found %s\n", name);
555 * xsltParseStylesheetParam:
556 * @ctxt: the XSLT transformation context
557 * @cur: the "param" element
559 * parse an XSLT transformation param declaration and record
564 xsltParseStylesheetParam(xsltTransformContextPtr ctxt, xmlNodePtr cur) {
565 xmlChar *name, *ncname, *prefix;
567 xmlNodePtr tree = NULL;
569 if ((cur == NULL) || (ctxt == NULL))
572 name = xmlGetNsProp(cur, (const xmlChar *)"name", XSLT_NAMESPACE);
574 xsltGenericError(xsltGenericErrorContext,
575 "xsl:param : missing name attribute\n");
579 #ifdef DEBUG_VARIABLE
580 xsltGenericDebug(xsltGenericDebugContext,
581 "Parsing param %s\n", name);
584 select = xmlGetNsProp(cur, (const xmlChar *)"select", XSLT_NAMESPACE);
585 if (select == NULL) {
586 tree = cur->children;
588 #ifdef DEBUG_VARIABLE
589 xsltGenericDebug(xsltGenericDebugContext,
590 " select %s\n", select);
592 if (cur->children != NULL)
593 xsltGenericError(xsltGenericErrorContext,
594 "xsl:param : content shuld be empty since select is present \n");
597 ncname = xmlSplitQName2(name, &prefix);
599 if (ncname != NULL) {
600 if (prefix != NULL) {
603 ns = xmlSearchNs(cur->doc, cur, prefix);
605 xsltGenericError(xsltGenericErrorContext,
606 "xsl:param : no namespace bound to prefix %s\n", prefix);
608 xsltRegisterVariable(ctxt, ncname, ns->href, select, tree, 1);
612 xsltRegisterVariable(ctxt, ncname, NULL, select, tree, 1);
616 xsltRegisterVariable(ctxt, name, NULL, select, tree, 1);
626 * xsltParseGlobalVariable:
627 * @style: the XSLT stylesheet
628 * @cur: the "variable" element
630 * parse an XSLT transformation variable declaration and record
635 xsltParseGlobalVariable(xsltStylesheetPtr style, xmlNodePtr cur) {
636 xmlChar *name, *ncname, *prefix;
638 xmlNodePtr tree = NULL;
640 if ((cur == NULL) || (style == NULL))
643 name = xmlGetNsProp(cur, (const xmlChar *)"name", XSLT_NAMESPACE);
645 xsltGenericError(xsltGenericErrorContext,
646 "xsl:variable : missing name attribute\n");
650 #ifdef DEBUG_VARIABLE
651 xsltGenericDebug(xsltGenericDebugContext,
652 "Parsing global variable %s\n", name);
655 select = xmlGetNsProp(cur, (const xmlChar *)"select", XSLT_NAMESPACE);
656 if (select == NULL) {
657 tree = cur->children;
659 if (cur->children != NULL)
660 xsltGenericError(xsltGenericErrorContext,
661 "xsl:variable : content shuld be empty since select is present \n");
664 ncname = xmlSplitQName2(name, &prefix);
666 if (ncname != NULL) {
667 if (prefix != NULL) {
670 ns = xmlSearchNs(cur->doc, cur, prefix);
672 xsltGenericError(xsltGenericErrorContext,
673 "xsl:variable : no namespace bound to prefix %s\n", prefix);
675 xsltRegisterGlobalVariable(style, ncname, ns->href, select, tree, 0);
679 xsltRegisterGlobalVariable(style, ncname, NULL, select, tree, 0);
683 xsltRegisterGlobalVariable(style, name, NULL, select, tree, 0);
693 * xsltParseGlobalParam:
694 * @style: the XSLT stylesheet
695 * @cur: the "param" element
697 * parse an XSLT transformation param declaration and record
702 xsltParseGlobalParam(xsltStylesheetPtr style, xmlNodePtr cur) {
703 xmlChar *name, *ncname, *prefix;
705 xmlNodePtr tree = NULL;
707 if ((cur == NULL) || (style == NULL))
710 name = xmlGetNsProp(cur, (const xmlChar *)"name", XSLT_NAMESPACE);
712 xsltGenericError(xsltGenericErrorContext,
713 "xsl:param : missing name attribute\n");
717 #ifdef DEBUG_VARIABLE
718 xsltGenericDebug(xsltGenericDebugContext,
719 "Parsing global param %s\n", name);
722 select = xmlGetNsProp(cur, (const xmlChar *)"select", XSLT_NAMESPACE);
723 if (select == NULL) {
724 tree = cur->children;
726 if (cur->children != NULL)
727 xsltGenericError(xsltGenericErrorContext,
728 "xsl:param : content shuld be empty since select is present \n");
731 ncname = xmlSplitQName2(name, &prefix);
733 if (ncname != NULL) {
734 if (prefix != NULL) {
737 ns = xmlSearchNs(cur->doc, cur, prefix);
739 xsltGenericError(xsltGenericErrorContext,
740 "xsl:param : no namespace bound to prefix %s\n", prefix);
742 xsltRegisterGlobalVariable(style, ncname, ns->href, select, tree, 1);
746 xsltRegisterGlobalVariable(style, ncname, NULL, select, tree, 1);
750 xsltRegisterGlobalVariable(style, name, NULL, select, tree, 1);
759 * xsltParseStylesheetVariable:
760 * @ctxt: the XSLT transformation context
761 * @cur: the "variable" element
763 * parse an XSLT transformation variable declaration and record
768 xsltParseStylesheetVariable(xsltTransformContextPtr ctxt, xmlNodePtr cur) {
769 xmlChar *name, *ncname, *prefix;
771 xmlNodePtr tree = NULL;
773 if ((cur == NULL) || (ctxt == NULL))
776 name = xmlGetNsProp(cur, (const xmlChar *)"name", XSLT_NAMESPACE);
778 xsltGenericError(xsltGenericErrorContext,
779 "xsl:variable : missing name attribute\n");
783 #ifdef DEBUG_VARIABLE
784 xsltGenericDebug(xsltGenericDebugContext,
785 "Parsing variable %s\n", name);
788 select = xmlGetNsProp(cur, (const xmlChar *)"select", XSLT_NAMESPACE);
789 if (select == NULL) {
790 tree = cur->children;
792 if (cur->children != NULL)
793 xsltGenericError(xsltGenericErrorContext,
794 "xsl:variable : content should be empty since select is present \n");
797 ncname = xmlSplitQName2(name, &prefix);
799 if (ncname != NULL) {
800 if (prefix != NULL) {
803 ns = xmlSearchNs(cur->doc, cur, prefix);
805 xsltGenericError(xsltGenericErrorContext,
806 "xsl:variable : no namespace bound to prefix %s\n", prefix);
808 xsltRegisterVariable(ctxt, ncname, ns->href, select, tree, 0);
812 xsltRegisterVariable(ctxt, ncname, NULL, select, tree, 0);
816 xsltRegisterVariable(ctxt, name, NULL, select, tree, 0);
826 * xsltVariableLookup:
827 * @ctxt: a void * but the the XSLT transformation context actually
828 * @name: the variable name
829 * @ns_uri: the variable namespace URI
831 * This is the entry point when a varibale is needed by the XPath
834 * Returns the value or NULL if not found
837 xsltXPathVariableLookup(void *ctxt, const xmlChar *name,
838 const xmlChar *ns_uri) {
839 xsltTransformContextPtr context;
840 xmlXPathObjectPtr ret;
842 if ((ctxt == NULL) || (name == NULL))
845 #ifdef DEBUG_VARIABLE
846 xsltGenericDebug(xsltGenericDebugContext,
847 "Lookup variable %s\n", name);
849 context = (xsltTransformContextPtr) ctxt;
850 ret = xsltVariableLookup(context, name, ns_uri);
852 xsltGenericError(xsltGenericErrorContext,
853 "unregistered variable %s\n", name);
855 #ifdef DEBUG_VARIABLE
857 xsltGenericDebug(xsltGenericDebugContext,
858 "found variable %s\n", name);