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))
189 for (i = ctxt->varsNr - 1;i >= 0;i--) {
190 cur = ctxt->varsTab[i];
191 while (cur != NULL) {
192 if (xmlStrEqual(cur->name, name)) {
193 if (nameURI == NULL) {
194 if (cur->nameURI == NULL) {
198 if ((cur->nameURI != NULL) &&
199 (xmlStrEqual(cur->nameURI, nameURI))) {
211 /************************************************************************
213 * Module interfaces *
215 ************************************************************************/
219 * @ctxt: the XSLT transformation context
220 * @elem: the variable or parameter.
222 * Evaluate a variable value.
224 * Returns 0 in case of success, -1 in case of error
227 xsltEvalVariables(xsltTransformContextPtr ctxt, xsltStackElemPtr elem) {
228 xmlXPathParserContextPtr xpathParserCtxt;
230 if ((ctxt == NULL) || (elem == NULL))
233 #ifdef DEBUG_VARIABLE
234 xsltGenericDebug(xsltGenericDebugContext,
235 "Evaluating variable %s\n", elem->name);
237 if (elem->select != NULL) {
238 xmlXPathObjectPtr result, tmp;
240 xpathParserCtxt = xmlXPathNewParserContext(elem->select,
242 if (xpathParserCtxt == NULL)
244 ctxt->xpathCtxt->node = (xmlNodePtr) ctxt->node;
245 xmlXPathEvalExpr(xpathParserCtxt);
246 result = valuePop(xpathParserCtxt);
248 tmp = valuePop(xpathParserCtxt);
250 xmlXPathFreeObject(tmp);
252 } while (tmp != NULL);
254 if (result == NULL) {
255 xsltGenericError(xsltGenericErrorContext,
256 "Evaluating global variable %s failed\n");
258 if (xpathParserCtxt != NULL)
259 xmlXPathFreeParserContext(xpathParserCtxt);
260 if (result != NULL) {
261 #ifdef DEBUG_VARIABLE
262 if ((xsltGenericDebugContext == stdout) ||
263 (xsltGenericDebugContext == stderr))
264 xmlXPathDebugDumpObject((FILE *)xsltGenericDebugContext,
267 if (elem->value != NULL)
268 xmlXPathFreeObject(elem->value);
269 elem->value = result;
273 if (elem->tree == NULL) {
274 elem->value = xmlXPathNewCString("");
278 * This is a result tree fragment.
280 xmlNodePtr container;
281 xmlNodePtr oldInsert, oldNode;
283 container = xmlNewDocNode(ctxt->output, NULL,
284 (const xmlChar *) "fake", NULL);
285 if (container == NULL)
287 oldInsert = ctxt->insert;
288 oldNode = ctxt->node;
289 ctxt->insert = container;
291 xsltApplyOneTemplate(ctxt, ctxt->node, elem->tree, 0);
293 ctxt->insert = oldInsert;
294 ctxt->node = oldNode;
295 elem->value = xmlXPathNewValueTree(container);
302 * xsltEvalGlobalVariables:
303 * @ctxt: the XSLT transformation context
305 * Evaluate the global variables of a stylesheet. This need to be
306 * done on parsed stylesheets before starting to apply transformations
308 * Returns 0 in case of success, -1 in case of error
311 xsltEvalGlobalVariables(xsltTransformContextPtr ctxt) {
312 xsltStackElemPtr elem;
313 xsltStylesheetPtr style;
318 #ifdef DEBUG_VARIABLE
319 xsltGenericDebug(xsltGenericDebugContext,
320 "Evaluating global variables\n");
322 ctxt->node = (xmlNodePtr) ctxt->document->doc;
324 /* TODO: handle the stylesheet cascade */
326 elem = style->variables;
328 while (elem != NULL) {
329 xsltEvalVariables(ctxt, elem);
337 * xsltRegisterGlobalVariable:
338 * @style: the XSLT transformation context
339 * @name: the variable name
340 * @ns_uri: the variable namespace URI
341 * @select: the expression which need to be evaluated to generate a value
342 * @tree: the subtree if select is NULL
343 * @param: this is a parameter actually
345 * Register a new variable value. If @value is NULL it unregisters
348 * Returns 0 in case of success, -1 in case of error
351 xsltRegisterGlobalVariable(xsltStylesheetPtr style, const xmlChar *name,
352 const xmlChar *ns_uri, const xmlChar *select,
353 xmlNodePtr tree, int param) {
354 xsltStackElemPtr elem;
360 #ifdef DEBUG_VARIABLE
362 xsltGenericDebug(xsltGenericDebugContext,
363 "Defineing global param %s\n", name);
365 xsltGenericDebug(xsltGenericDebugContext,
366 "Defineing global variable %s\n", name);
368 elem = xsltNewStackElem();
372 elem->type = XSLT_ELEM_PARAM;
374 elem->type = XSLT_ELEM_VARIABLE;
375 elem->name = xmlStrdup(name);
376 elem->select = xmlStrdup(select);
378 elem->nameURI = xmlStrdup(ns_uri);
380 elem->next = style->variables;
381 style->variables = elem;
386 * xsltRegisterVariable:
387 * @ctxt: the XSLT transformation context
388 * @name: the variable name
389 * @ns_uri: the variable namespace URI
390 * @select: the expression which need to be evaluated to generate a value
391 * @tree: the tree if select is NULL
392 * @param: this is a parameter actually
394 * Register a new variable value. If @value is NULL it unregisters
397 * Returns 0 in case of success, -1 in case of error
400 xsltRegisterVariable(xsltTransformContextPtr ctxt, const xmlChar *name,
401 const xmlChar *ns_uri, const xmlChar *select,
402 xmlNodePtr tree, int param) {
403 xsltStackElemPtr elem;
409 if (xsltCheckStackElem(ctxt, name, ns_uri) != 0) {
411 xsltGenericError(xsltGenericErrorContext,
412 "xsl:variable : redefining %s\n", name);
414 #ifdef DEBUG_VARIABLE
416 xsltGenericDebug(xsltGenericDebugContext,
417 "param %s defined by caller", name);
421 #ifdef DEBUG_VARIABLE
422 xsltGenericDebug(xsltGenericDebugContext,
423 "Defineing variable %s", name);
425 xsltGenericDebug(xsltGenericDebugContext,
426 " select %s", select);
427 xsltGenericDebug(xsltGenericDebugContext, "\n");
429 elem = xsltNewStackElem();
433 elem->type = XSLT_ELEM_PARAM;
435 elem->type = XSLT_ELEM_VARIABLE;
436 elem->name = xmlStrdup(name);
438 elem->select = xmlStrdup(select);
442 elem->nameURI = xmlStrdup(ns_uri);
444 xsltEvalVariables(ctxt, elem);
445 xsltAddStackElem(ctxt, elem);
450 * xsltGlobalVariableLookup:
451 * @ctxt: the XSLT transformation context
452 * @name: the variable name
453 * @ns_uri: the variable namespace URI
455 * Search in the Variable array of the context for the given
458 * Returns the value or NULL if not found
461 xsltGlobalVariableLookup(xsltTransformContextPtr ctxt, const xmlChar *name,
462 const xmlChar *ns_uri) {
463 xsltStylesheetPtr style;
464 xsltStackElemPtr elem = NULL;
467 while (style != NULL) {
468 elem = style->variables;
470 while (elem != NULL) {
471 if (xmlStrEqual(elem->name, name)) {
472 /* TODO: double check param binding */
473 if (ns_uri == NULL) {
474 if (elem->nameURI == NULL)
477 if ((elem->nameURI != NULL) &&
478 (xmlStrEqual(elem->nameURI, ns_uri)))
486 style = xsltNextImport(style);
491 if (!elem->computed) {
492 #ifdef DEBUG_VARIABLE
493 xsltGenericDebug(xsltGenericDebugContext,
494 "uncomputed global variable %s\n", name);
496 xsltEvalVariables(ctxt, elem);
498 if (elem->value != NULL)
499 return(xmlXPathObjectCopy(elem->value));
500 #ifdef DEBUG_VARIABLE
501 xsltGenericDebug(xsltGenericDebugContext,
502 "global variable not found %s\n", name);
508 * xsltVariableLookup:
509 * @ctxt: the XSLT transformation context
510 * @name: the variable name
511 * @ns_uri: the variable namespace URI
513 * Search in the Variable array of the context for the given
516 * Returns the value or NULL if not found
519 xsltVariableLookup(xsltTransformContextPtr ctxt, const xmlChar *name,
520 const xmlChar *ns_uri) {
521 xsltStackElemPtr elem;
526 elem = xsltStackLookup(ctxt, name, ns_uri);
528 return(xsltGlobalVariableLookup(ctxt, name, ns_uri));
530 if (!elem->computed) {
531 #ifdef DEBUG_VARIABLE
532 xsltGenericDebug(xsltGenericDebugContext,
533 "uncomputed variable %s\n", name);
535 xsltEvalVariables(ctxt, elem);
537 if (elem->value != NULL)
538 return(xmlXPathObjectCopy(elem->value));
539 #ifdef DEBUG_VARIABLE
540 xsltGenericDebug(xsltGenericDebugContext,
541 "variable not found %s\n", name);
547 * xsltParseStylesheetParam:
548 * @ctxt: the XSLT transformation context
549 * @cur: the "param" element
551 * parse an XSLT transformation param declaration and record
556 xsltParseStylesheetParam(xsltTransformContextPtr ctxt, xmlNodePtr cur) {
557 xmlChar *name, *ncname, *prefix;
559 xmlNodePtr tree = NULL;
561 if ((cur == NULL) || (ctxt == NULL))
564 name = xmlGetNsProp(cur, (const xmlChar *)"name", XSLT_NAMESPACE);
566 xsltGenericError(xsltGenericErrorContext,
567 "xsl:param : missing name attribute\n");
571 #ifdef DEBUG_VARIABLE
572 xsltGenericDebug(xsltGenericDebugContext,
573 "Parsing param %s\n", name);
576 select = xmlGetNsProp(cur, (const xmlChar *)"select", XSLT_NAMESPACE);
577 if (select == NULL) {
578 tree = cur->children;
580 #ifdef DEBUG_VARIABLE
581 xsltGenericDebug(xsltGenericDebugContext,
582 " select %s\n", select);
584 if (cur->children != NULL)
585 xsltGenericError(xsltGenericErrorContext,
586 "xsl:param : content shuld be empty since select is present \n");
589 ncname = xmlSplitQName2(name, &prefix);
591 if (ncname != NULL) {
592 if (prefix != NULL) {
595 ns = xmlSearchNs(cur->doc, cur, prefix);
597 xsltGenericError(xsltGenericErrorContext,
598 "xsl:param : no namespace bound to prefix %s\n", prefix);
600 xsltRegisterVariable(ctxt, ncname, ns->href, select, tree, 1);
604 xsltRegisterVariable(ctxt, ncname, NULL, select, tree, 1);
608 xsltRegisterVariable(ctxt, name, NULL, select, tree, 1);
618 * xsltParseGlobalVariable:
619 * @style: the XSLT stylesheet
620 * @cur: the "variable" element
622 * parse an XSLT transformation variable declaration and record
627 xsltParseGlobalVariable(xsltStylesheetPtr style, xmlNodePtr cur) {
628 xmlChar *name, *ncname, *prefix;
630 xmlNodePtr tree = NULL;
632 if ((cur == NULL) || (style == NULL))
635 name = xmlGetNsProp(cur, (const xmlChar *)"name", XSLT_NAMESPACE);
637 xsltGenericError(xsltGenericErrorContext,
638 "xsl:variable : missing name attribute\n");
642 #ifdef DEBUG_VARIABLE
643 xsltGenericDebug(xsltGenericDebugContext,
644 "Parsing global variable %s\n", name);
647 select = xmlGetNsProp(cur, (const xmlChar *)"select", XSLT_NAMESPACE);
648 if (select == NULL) {
649 tree = cur->children;
651 if (cur->children != NULL)
652 xsltGenericError(xsltGenericErrorContext,
653 "xsl:variable : content shuld be empty since select is present \n");
656 ncname = xmlSplitQName2(name, &prefix);
658 if (ncname != NULL) {
659 if (prefix != NULL) {
662 ns = xmlSearchNs(cur->doc, cur, prefix);
664 xsltGenericError(xsltGenericErrorContext,
665 "xsl:variable : no namespace bound to prefix %s\n", prefix);
667 xsltRegisterGlobalVariable(style, ncname, ns->href, select, tree, 0);
671 xsltRegisterGlobalVariable(style, ncname, NULL, select, tree, 0);
675 xsltRegisterGlobalVariable(style, name, NULL, select, tree, 0);
685 * xsltParseGlobalParam:
686 * @style: the XSLT stylesheet
687 * @cur: the "param" element
689 * parse an XSLT transformation param declaration and record
694 xsltParseGlobalParam(xsltStylesheetPtr style, xmlNodePtr cur) {
695 xmlChar *name, *ncname, *prefix;
697 xmlNodePtr tree = NULL;
699 if ((cur == NULL) || (style == NULL))
702 name = xmlGetNsProp(cur, (const xmlChar *)"name", XSLT_NAMESPACE);
704 xsltGenericError(xsltGenericErrorContext,
705 "xsl:param : missing name attribute\n");
709 #ifdef DEBUG_VARIABLE
710 xsltGenericDebug(xsltGenericDebugContext,
711 "Parsing global param %s\n", name);
714 select = xmlGetNsProp(cur, (const xmlChar *)"select", XSLT_NAMESPACE);
715 if (select == NULL) {
716 tree = cur->children;
718 if (cur->children != NULL)
719 xsltGenericError(xsltGenericErrorContext,
720 "xsl:param : content shuld be empty since select is present \n");
723 ncname = xmlSplitQName2(name, &prefix);
725 if (ncname != NULL) {
726 if (prefix != NULL) {
729 ns = xmlSearchNs(cur->doc, cur, prefix);
731 xsltGenericError(xsltGenericErrorContext,
732 "xsl:param : no namespace bound to prefix %s\n", prefix);
734 xsltRegisterGlobalVariable(style, ncname, ns->href, select, tree, 1);
738 xsltRegisterGlobalVariable(style, ncname, NULL, select, tree, 1);
742 xsltRegisterGlobalVariable(style, name, NULL, select, tree, 1);
751 * xsltParseStylesheetVariable:
752 * @ctxt: the XSLT transformation context
753 * @cur: the "variable" element
755 * parse an XSLT transformation variable declaration and record
760 xsltParseStylesheetVariable(xsltTransformContextPtr ctxt, xmlNodePtr cur) {
761 xmlChar *name, *ncname, *prefix;
763 xmlNodePtr tree = NULL;
765 if ((cur == NULL) || (ctxt == NULL))
768 name = xmlGetNsProp(cur, (const xmlChar *)"name", XSLT_NAMESPACE);
770 xsltGenericError(xsltGenericErrorContext,
771 "xsl:variable : missing name attribute\n");
775 #ifdef DEBUG_VARIABLE
776 xsltGenericDebug(xsltGenericDebugContext,
777 "Parsing variable %s\n", name);
780 select = xmlGetNsProp(cur, (const xmlChar *)"select", XSLT_NAMESPACE);
781 if (select == NULL) {
782 tree = cur->children;
784 if (cur->children != NULL)
785 xsltGenericError(xsltGenericErrorContext,
786 "xsl:variable : content should be empty since select is present \n");
789 ncname = xmlSplitQName2(name, &prefix);
791 if (ncname != NULL) {
792 if (prefix != NULL) {
795 ns = xmlSearchNs(cur->doc, cur, prefix);
797 xsltGenericError(xsltGenericErrorContext,
798 "xsl:variable : no namespace bound to prefix %s\n", prefix);
800 xsltRegisterVariable(ctxt, ncname, ns->href, select, tree, 0);
804 xsltRegisterVariable(ctxt, ncname, NULL, select, tree, 0);
808 xsltRegisterVariable(ctxt, name, NULL, select, tree, 0);
818 * xsltVariableLookup:
819 * @ctxt: a void * but the the XSLT transformation context actually
820 * @name: the variable name
821 * @ns_uri: the variable namespace URI
823 * This is the entry point when a varibale is needed by the XPath
826 * Returns the value or NULL if not found
829 xsltXPathVariableLookup(void *ctxt, const xmlChar *name,
830 const xmlChar *ns_uri) {
831 xsltTransformContextPtr context;
832 xmlXPathObjectPtr ret;
834 if ((ctxt == NULL) || (name == NULL))
837 #ifdef DEBUG_VARIABLE
838 xsltGenericDebug(xsltGenericDebugContext,
839 "Lookup variable %s\n", name);
841 context = (xsltTransformContextPtr) ctxt;
842 ret = xsltVariableLookup(context, name, ns_uri);
844 xsltGenericError(xsltGenericErrorContext,
845 "unregistered variable %s\n", name);
847 #ifdef DEBUG_VARIABLE
849 xsltGenericDebug(xsltGenericDebugContext,
850 "found variable %s\n", name);