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.
17 #include <libxml/xmlmemory.h>
18 #include <libxml/tree.h>
19 #include <libxml/valid.h>
20 #include <libxml/hash.h>
21 #include <libxml/xmlerror.h>
22 #include <libxml/xpath.h>
23 #include <libxml/xpathInternals.h>
24 #include <libxml/parserInternals.h>
25 #include <libxml/dict.h>
27 #include "xsltInternals.h"
28 #include "xsltutils.h"
29 #include "variables.h"
30 #include "transform.h"
35 #ifdef WITH_XSLT_DEBUG
36 #define WITH_XSLT_DEBUG_VARIABLE
39 #ifdef XSLT_REFACTORED
40 const xmlChar
*xsltDocFragFake
= (const xmlChar
*) " fake node libxslt";
43 const xmlChar
*xsltComputingGlobalVarMarker
=
44 (const xmlChar
*) " var/param being computed";
46 #define XSLT_VAR_GLOBAL 1<<0
47 #define XSLT_VAR_IN_SELECT 1<<1
48 #define XSLT_TCTXT_VARIABLE(c) ((xsltStackElemPtr) (c)->contextVariable)
50 /************************************************************************
52 * Result Value Tree (Result Tree Fragment) interfaces *
54 ************************************************************************/
57 * @ctxt: an XSLT transformation context
59 * Creates a Result Value Tree
60 * (the XSLT 1.0 term for this is "Result Tree Fragment")
62 * Returns the result value tree or NULL in case of API or internal errors.
65 xsltCreateRVT(xsltTransformContextPtr ctxt
)
70 * Question: Why is this function public?
71 * Answer: It is called by the EXSLT module.
77 * Reuse a RTF from the cache if available.
79 if (ctxt
->cache
->RVT
) {
80 container
= ctxt
->cache
->RVT
;
81 ctxt
->cache
->RVT
= (xmlDocPtr
) container
->next
;
82 /* clear the internal pointers */
83 container
->next
= NULL
;
84 container
->prev
= NULL
;
85 if (ctxt
->cache
->nbRVT
> 0)
87 #ifdef XSLT_DEBUG_PROFILE_CACHE
88 ctxt
->cache
->dbgReusedRVTs
++;
93 container
= xmlNewDoc(NULL
);
94 if (container
== NULL
)
96 container
->dict
= ctxt
->dict
;
97 xmlDictReference(container
->dict
);
98 XSLT_MARK_RES_TREE_FRAG(container
);
99 container
->doc
= container
;
100 container
->parent
= NULL
;
105 * xsltRegisterTmpRVT:
106 * @ctxt: an XSLT transformation context
107 * @RVT: a result value tree (Result Tree Fragment)
109 * Registers the result value tree (XSLT 1.0 term: Result Tree Fragment)
110 * in the garbage collector.
111 * The fragment will be freed at the exit of the currently
112 * instantiated xsl:template.
113 * Obsolete; this function might produce massive memory overhead,
114 * since the fragment is only freed when the current xsl:template
115 * exits. Use xsltRegisterLocalRVT() instead.
117 * Returns 0 in case of success and -1 in case of API or internal errors.
120 xsltRegisterTmpRVT(xsltTransformContextPtr ctxt
, xmlDocPtr RVT
)
122 if ((ctxt
== NULL
) || (RVT
== NULL
))
126 * We'll restrict the lifetime of user-created fragments
127 * insinde an xsl:variable and xsl:param to the lifetime of the
130 if (ctxt
->contextVariable
!= NULL
) {
131 RVT
->next
= (xmlNodePtr
) XSLT_TCTXT_VARIABLE(ctxt
)->fragment
;
132 XSLT_TCTXT_VARIABLE(ctxt
)->fragment
= RVT
;
136 RVT
->next
= (xmlNodePtr
) ctxt
->tmpRVT
;
137 if (ctxt
->tmpRVT
!= NULL
)
138 ctxt
->tmpRVT
->prev
= (xmlNodePtr
) RVT
;
144 * xsltRegisterLocalRVT:
145 * @ctxt: an XSLT transformation context
146 * @RVT: a result value tree (Result Tree Fragment; xmlDocPtr)
148 * Registers a result value tree (XSLT 1.0 term: Result Tree Fragment)
149 * in the RVT garbage collector.
150 * The fragment will be freed when the instruction which created the
153 * Returns 0 in case of success and -1 in case of API or internal errors.
156 xsltRegisterLocalRVT(xsltTransformContextPtr ctxt
,
159 if ((ctxt
== NULL
) || (RVT
== NULL
))
163 * When evaluating "select" expressions of xsl:variable
164 * and xsl:param, we need to bind newly created tree fragments
165 * to the variable itself; otherwise the tragment will be
166 * freed before we leave the scope of a var.
168 if ((ctxt
->contextVariable
!= NULL
) &&
169 (XSLT_TCTXT_VARIABLE(ctxt
)->flags
& XSLT_VAR_IN_SELECT
))
171 RVT
->next
= (xmlNodePtr
) XSLT_TCTXT_VARIABLE(ctxt
)->fragment
;
172 XSLT_TCTXT_VARIABLE(ctxt
)->fragment
= RVT
;
176 * Store the fragment in the scope of the current instruction.
177 * If not reference by a returning instruction (like EXSLT's function),
178 * then this fragment will be freed, when the instruction exits.
180 RVT
->next
= (xmlNodePtr
) ctxt
->localRVT
;
181 if (ctxt
->localRVT
!= NULL
)
182 ctxt
->localRVT
->prev
= (xmlNodePtr
) RVT
;
183 ctxt
->localRVT
= RVT
;
185 * We need to keep track of the first registered fragment
186 * for extension instructions which return fragments
187 * (e.g. EXSLT'S function), in order to let
188 * xsltExtensionInstructionResultFinalize() clear the
189 * preserving flag on the fragments.
191 if (ctxt
->localRVTBase
== NULL
)
192 ctxt
->localRVTBase
= RVT
;
197 * xsltExtensionInstructionResultFinalize:
198 * @ctxt: an XSLT transformation context
200 * Finalizes the data (e.g. result tree fragments) created
201 * within a value-returning process (e.g. EXSLT's function).
202 * Tree fragments marked as being returned by a function are
203 * set to normal state, which means that the fragment garbage
204 * collector will free them after the function-calling process exits.
206 * Returns 0 in case of success and -1 in case of API or internal errors.
209 xsltExtensionInstructionResultFinalize(xsltTransformContextPtr ctxt
)
215 if (ctxt
->localRVTBase
== NULL
)
218 * Enable remaining local tree fragments to be freed
219 * by the fragment garbage collector.
221 cur
= ctxt
->localRVTBase
;
224 cur
= (xmlDocPtr
) cur
->next
;
225 } while (cur
!= NULL
);
230 * xsltExtensionInstructionResultRegister:
231 * @ctxt: an XSLT transformation context
232 * @obj: an XPath object to be inspected for result tree fragments
234 * Marks the result of a value-returning extension instruction
235 * in order to avoid it being garbage collected before the
236 * extension instruction exits.
237 * Note that one still has to additionally register any newly created
238 * tree fragments (via xsltCreateRVT()) with xsltRegisterLocalRVT().
240 * Returns 0 in case of success and -1 in case of error.
243 xsltExtensionInstructionResultRegister(xsltTransformContextPtr ctxt
,
244 xmlXPathObjectPtr obj
)
250 if ((ctxt
== NULL
) || (obj
== NULL
))
254 * OPTIMIZE TODO: If no local variables/params and no local tree
255 * fragments were created, then we don't need to analyse the XPath
256 * objects for tree fragments.
259 if ((obj
->type
!= XPATH_NODESET
) && (obj
->type
!= XPATH_XSLT_TREE
))
261 if ((obj
->nodesetval
== NULL
) || (obj
->nodesetval
->nodeNr
== 0))
264 for (i
= 0; i
< obj
->nodesetval
->nodeNr
; i
++) {
265 cur
= obj
->nodesetval
->nodeTab
[i
];
266 if (cur
->type
== XML_NAMESPACE_DECL
) {
268 * The XPath module sets the owner element of a ns-node on
269 * the ns->next field.
271 if ((((xmlNsPtr
) cur
)->next
!= NULL
) &&
272 (((xmlNsPtr
) cur
)->next
->type
== XML_ELEMENT_NODE
))
274 cur
= (xmlNodePtr
) ((xmlNsPtr
) cur
)->next
;
277 xsltTransformError(ctxt
, NULL
, ctxt
->inst
,
279 "xsltExtensionInstructionResultRegister(): "
280 "Cannot retrieve the doc of a namespace node.\n");
287 xsltTransformError(ctxt
, NULL
, ctxt
->inst
,
289 "xsltExtensionInstructionResultRegister(): "
290 "Cannot retrieve the doc of a node.\n");
293 if (doc
->name
&& (doc
->name
[0] == ' ')) {
295 * This is a result tree fragment.
296 * We'll use the @psvi field for reference counting.
297 * TODO: How do we know if this is a value of a
298 * global variable or a doc acquired via the
299 * document() function?
301 doc
->psvi
= (void *) ((long) 1);
312 * @ctxt: an XSLT transformation context
313 * @RVT: a result value tree (Result Tree Fragment)
315 * Either frees the RVT (which is an xmlDoc) or stores
316 * it in the context's cache for later reuse.
319 xsltReleaseRVT(xsltTransformContextPtr ctxt
, xmlDocPtr RVT
)
324 if (ctxt
&& (ctxt
->cache
->nbRVT
< 40)) {
326 * Store the Result Tree Fragment.
327 * Free the document info.
329 if (RVT
->_private
!= NULL
) {
330 xsltFreeDocumentKeys((xsltDocumentPtr
) RVT
->_private
);
331 xmlFree(RVT
->_private
);
332 RVT
->_private
= NULL
;
335 * Clear the document tree.
336 * REVISIT TODO: Do we expect ID/IDREF tables to be existent?
338 if (RVT
->children
!= NULL
) {
339 xmlFreeNodeList(RVT
->children
);
340 RVT
->children
= NULL
;
343 if (RVT
->ids
!= NULL
) {
344 xmlFreeIDTable((xmlIDTablePtr
) RVT
->ids
);
347 if (RVT
->refs
!= NULL
) {
348 xmlFreeRefTable((xmlRefTablePtr
) RVT
->refs
);
353 * Reset the reference counter.
357 RVT
->next
= (xmlNodePtr
) ctxt
->cache
->RVT
;
358 ctxt
->cache
->RVT
= RVT
;
360 ctxt
->cache
->nbRVT
++;
362 #ifdef XSLT_DEBUG_PROFILE_CACHE
363 ctxt
->cache
->dbgCachedRVTs
++;
370 if (RVT
->_private
!= NULL
) {
371 xsltFreeDocumentKeys((xsltDocumentPtr
) RVT
->_private
);
372 xmlFree(RVT
->_private
);
378 * xsltRegisterPersistRVT:
379 * @ctxt: an XSLT transformation context
380 * @RVT: a result value tree (Result Tree Fragment)
382 * Register the result value tree (XSLT 1.0 term: Result Tree Fragment)
383 * in the fragment garbage collector.
384 * The fragment will be freed when the transformation context is
387 * Returns 0 in case of success and -1 in case of error.
390 xsltRegisterPersistRVT(xsltTransformContextPtr ctxt
, xmlDocPtr RVT
)
392 if ((ctxt
== NULL
) || (RVT
== NULL
)) return(-1);
394 RVT
->next
= (xmlNodePtr
) ctxt
->persistRVT
;
395 if (ctxt
->persistRVT
!= NULL
)
396 ctxt
->persistRVT
->prev
= (xmlNodePtr
) RVT
;
397 ctxt
->persistRVT
= RVT
;
403 * @ctxt: an XSLT transformation context
405 * Frees all registered result value trees (Result Tree Fragments)
406 * of the transformation. Internal function; should not be called
410 xsltFreeRVTs(xsltTransformContextPtr ctxt
)
419 cur
= ctxt
->localRVT
;
420 while (cur
!= NULL
) {
421 next
= (xmlDocPtr
) cur
->next
;
422 if (cur
->_private
!= NULL
) {
423 xsltFreeDocumentKeys(cur
->_private
);
424 xmlFree(cur
->_private
);
429 ctxt
->localRVT
= NULL
;
431 * User-created per-template fragments.
434 while (cur
!= NULL
) {
435 next
= (xmlDocPtr
) cur
->next
;
436 if (cur
->_private
!= NULL
) {
437 xsltFreeDocumentKeys(cur
->_private
);
438 xmlFree(cur
->_private
);
447 cur
= ctxt
->persistRVT
;
448 while (cur
!= NULL
) {
449 next
= (xmlDocPtr
) cur
->next
;
450 if (cur
->_private
!= NULL
) {
451 xsltFreeDocumentKeys(cur
->_private
);
452 xmlFree(cur
->_private
);
457 ctxt
->persistRVT
= NULL
;
460 /************************************************************************
462 * Module interfaces *
464 ************************************************************************/
469 * Create a new XSLT ParserContext
471 * Returns the newly allocated xsltParserStackElem or NULL in case of error
473 static xsltStackElemPtr
474 xsltNewStackElem(xsltTransformContextPtr ctxt
)
476 xsltStackElemPtr ret
;
478 * Reuse a stack item from the cache if available.
480 if (ctxt
&& ctxt
->cache
->stackItems
) {
481 ret
= ctxt
->cache
->stackItems
;
482 ctxt
->cache
->stackItems
= ret
->next
;
484 ctxt
->cache
->nbStackItems
--;
485 #ifdef XSLT_DEBUG_PROFILE_CACHE
486 ctxt
->cache
->dbgReusedVars
++;
490 ret
= (xsltStackElemPtr
) xmlMalloc(sizeof(xsltStackElem
));
492 xsltTransformError(NULL
, NULL
, NULL
,
493 "xsltNewStackElem : malloc failed\n");
496 memset(ret
, 0, sizeof(xsltStackElem
));
503 * @elem: an XSLT stack element
505 * Makes a copy of the stack element
507 * Returns the copy of NULL
509 static xsltStackElemPtr
510 xsltCopyStackElem(xsltStackElemPtr elem
) {
511 xsltStackElemPtr cur
;
513 cur
= (xsltStackElemPtr
) xmlMalloc(sizeof(xsltStackElem
));
515 xsltTransformError(NULL
, NULL
, NULL
,
516 "xsltCopyStackElem : malloc failed\n");
519 memset(cur
, 0, sizeof(xsltStackElem
));
520 cur
->context
= elem
->context
;
521 cur
->name
= elem
->name
;
522 cur
->nameURI
= elem
->nameURI
;
523 cur
->select
= elem
->select
;
524 cur
->tree
= elem
->tree
;
525 cur
->comp
= elem
->comp
;
531 * @elem: an XSLT stack element
533 * Free up the memory allocated by @elem
536 xsltFreeStackElem(xsltStackElemPtr elem
) {
539 if (elem
->value
!= NULL
)
540 xmlXPathFreeObject(elem
->value
);
542 * Release the list of temporary Result Tree Fragments.
544 if (elem
->fragment
) {
547 while (elem
->fragment
!= NULL
) {
548 cur
= elem
->fragment
;
549 elem
->fragment
= (xmlDocPtr
) cur
->next
;
552 (cur
->psvi
== (void *) ((long) 1)))
555 * This fragment is a result of an extension instruction
556 * (e.g. XSLT's function) and needs to be preserved until
557 * the instruction exits.
558 * Example: The fragment of the variable must not be freed
559 * since it is returned by the EXSLT function:
560 * <f:function name="foo">
561 * <xsl:variable name="bar">
564 * <f:result select="$bar"/>
568 xsltRegisterLocalRVT(elem
->context
, cur
);
570 xsltReleaseRVT((xsltTransformContextPtr
) elem
->context
,
576 * Cache or free the variable structure.
578 if (elem
->context
&& (elem
->context
->cache
->nbStackItems
< 50)) {
580 * Store the item in the cache.
582 xsltTransformContextPtr ctxt
= elem
->context
;
583 memset(elem
, 0, sizeof(xsltStackElem
));
584 elem
->context
= ctxt
;
585 elem
->next
= ctxt
->cache
->stackItems
;
586 ctxt
->cache
->stackItems
= elem
;
587 ctxt
->cache
->nbStackItems
++;
588 #ifdef XSLT_DEBUG_PROFILE_CACHE
589 ctxt
->cache
->dbgCachedVars
++;
597 * xsltFreeStackElemList:
598 * @elem: an XSLT stack element
600 * Free up the memory allocated by @elem
603 xsltFreeStackElemList(xsltStackElemPtr elem
) {
604 xsltStackElemPtr next
;
606 while (elem
!= NULL
) {
608 xsltFreeStackElem(elem
);
615 * @ctxt: an XSLT transformation context
616 * @name: the local part of the name
617 * @nameURI: the URI part of the name
619 * Locate an element in the stack based on its name.
621 #if 0 /* TODO: Those seem to have been used for debugging. */
622 static int stack_addr
= 0;
623 static int stack_cmp
= 0;
626 static xsltStackElemPtr
627 xsltStackLookup(xsltTransformContextPtr ctxt
, const xmlChar
*name
,
628 const xmlChar
*nameURI
) {
630 xsltStackElemPtr cur
;
632 if ((ctxt
== NULL
) || (name
== NULL
) || (ctxt
->varsNr
== 0))
636 * Do the lookup from the top of the stack, but
637 * don't use params being computed in a call-param
638 * First lookup expects the variable name and URI to
639 * come from the disctionnary and hence pointer comparison.
641 for (i
= ctxt
->varsNr
; i
> ctxt
->varsBase
; i
--) {
642 cur
= ctxt
->varsTab
[i
-1];
643 while (cur
!= NULL
) {
644 if ((cur
->name
== name
) && (cur
->nameURI
== nameURI
)) {
655 * Redo the lookup with interned string compares
656 * to avoid string compares.
658 name
= xmlDictLookup(ctxt
->dict
, name
, -1);
660 nameURI
= xmlDictLookup(ctxt
->dict
, nameURI
, -1);
662 for (i
= ctxt
->varsNr
; i
> ctxt
->varsBase
; i
--) {
663 cur
= ctxt
->varsTab
[i
-1];
664 while (cur
!= NULL
) {
665 if ((cur
->name
== name
) && (cur
->nameURI
== nameURI
)) {
678 #ifdef XSLT_REFACTORED
682 * xsltCheckStackElem:
683 * @ctxt: xn XSLT transformation context
684 * @name: the variable name
685 * @nameURI: the variable namespace URI
687 * Checks whether a variable or param is already defined.
689 * URGENT TODO: Checks for redefinition of vars/params should be
690 * done only at compilation time.
692 * Returns 1 if variable is present, 2 if param is present, 3 if this
693 * is an inherited param, 0 if not found, -1 in case of failure.
696 xsltCheckStackElem(xsltTransformContextPtr ctxt
, const xmlChar
*name
,
697 const xmlChar
*nameURI
) {
698 xsltStackElemPtr cur
;
700 if ((ctxt
== NULL
) || (name
== NULL
))
703 cur
= xsltStackLookup(ctxt
, name
, nameURI
);
706 if (cur
->comp
!= NULL
) {
707 if (cur
->comp
->type
== XSLT_FUNC_WITHPARAM
)
709 else if (cur
->comp
->type
== XSLT_FUNC_PARAM
)
716 #endif /* XSLT_REFACTORED */
720 * @ctxt: xn XSLT transformation context
721 * @elem: a stack element
723 * Push an element (or list) onto the stack.
724 * In case of a list, each member will be pushed into
725 * a seperate slot; i.e. there's always 1 stack entry for
728 * Returns 0 in case of success, -1 in case of failure.
731 xsltAddStackElem(xsltTransformContextPtr ctxt
, xsltStackElemPtr elem
)
733 if ((ctxt
== NULL
) || (elem
== NULL
))
737 if (ctxt
->varsMax
== 0) {
740 (xsltStackElemPtr
*) xmlMalloc(ctxt
->varsMax
*
741 sizeof(ctxt
->varsTab
[0]));
742 if (ctxt
->varsTab
== NULL
) {
743 xmlGenericError(xmlGenericErrorContext
, "malloc failed !\n");
747 if (ctxt
->varsNr
>= ctxt
->varsMax
) {
750 (xsltStackElemPtr
*) xmlRealloc(ctxt
->varsTab
,
752 sizeof(ctxt
->varsTab
[0]));
753 if (ctxt
->varsTab
== NULL
) {
754 xmlGenericError(xmlGenericErrorContext
, "realloc failed !\n");
758 ctxt
->varsTab
[ctxt
->varsNr
++] = elem
;
762 } while (elem
!= NULL
);
768 * xsltAddStackElemList:
769 * @ctxt: xn XSLT transformation context
770 * @elems: a stack element list
772 * Push an element list onto the stack.
774 * Returns 0 in case of success, -1 in case of failure.
777 xsltAddStackElemList(xsltTransformContextPtr ctxt
, xsltStackElemPtr elems
)
779 return(xsltAddStackElem(ctxt
, elems
));
782 /************************************************************************
784 * Module interfaces *
786 ************************************************************************/
790 * @ctxt: the XSLT transformation context
791 * @variable: the variable or parameter item
792 * @comp: the compiled XSLT instruction
794 * Evaluate a variable value.
796 * Returns the XPath Object value or NULL in case of error
798 static xmlXPathObjectPtr
799 xsltEvalVariable(xsltTransformContextPtr ctxt
, xsltStackElemPtr variable
,
800 xsltStylePreCompPtr castedComp
)
802 #ifdef XSLT_REFACTORED
803 xsltStyleItemVariablePtr comp
=
804 (xsltStyleItemVariablePtr
) castedComp
;
806 xsltStylePreCompPtr comp
= castedComp
;
808 xmlXPathObjectPtr result
= NULL
;
811 if ((ctxt
== NULL
) || (variable
== NULL
))
815 * A variable or parameter are evaluated on demand; thus the
816 * context (of XSLT and XPath) need to be temporarily adjusted and
819 oldInst
= ctxt
->inst
;
821 #ifdef WITH_XSLT_DEBUG_VARIABLE
822 XSLT_TRACE(ctxt
,XSLT_TRACE_VARIABLES
,xsltGenericDebug(xsltGenericDebugContext
,
823 "Evaluating variable '%s'\n", variable
->name
));
825 if (variable
->select
!= NULL
) {
826 xmlXPathCompExprPtr xpExpr
= NULL
;
828 xmlNodePtr oldXPContextNode
;
829 int oldXPProximityPosition
, oldXPContextSize
, oldXPNsNr
;
830 xmlNsPtr
*oldXPNamespaces
;
831 xmlXPathContextPtr xpctxt
= ctxt
->xpathCtxt
;
832 xsltStackElemPtr oldVar
= ctxt
->contextVariable
;
834 if ((comp
!= NULL
) && (comp
->comp
!= NULL
)) {
837 xpExpr
= xmlXPathCompile(variable
->select
);
842 * Save context states.
844 oldXPDoc
= xpctxt
->doc
;
845 oldXPContextNode
= xpctxt
->node
;
846 oldXPProximityPosition
= xpctxt
->proximityPosition
;
847 oldXPContextSize
= xpctxt
->contextSize
;
848 oldXPNamespaces
= xpctxt
->namespaces
;
849 oldXPNsNr
= xpctxt
->nsNr
;
851 xpctxt
->node
= ctxt
->node
;
853 * OPTIMIZE TODO: Lame try to set the context doc.
854 * Get rid of this somehow in xpath.c.
856 if ((ctxt
->node
->type
!= XML_NAMESPACE_DECL
) &&
858 xpctxt
->doc
= ctxt
->node
->doc
;
860 * BUG TODO: The proximity position and the context size will
861 * potentially be wrong.
863 * <xsl:template select="foo">
864 * <xsl:variable name="pos" select="position()"/>
865 * <xsl:for-each select="bar">
866 * <xsl:value-of select="$pos"/>
869 * Here the proximity position and context size are changed
870 * to the context of <xsl:for-each select="bar">, but
871 * the variable needs to be evaluated in the context of
872 * <xsl:template select="foo">.
876 #ifdef XSLT_REFACTORED
877 if (comp
->inScopeNs
!= NULL
) {
878 xpctxt
->namespaces
= comp
->inScopeNs
->list
;
879 xpctxt
->nsNr
= comp
->inScopeNs
->xpathNumber
;
881 xpctxt
->namespaces
= NULL
;
885 xpctxt
->namespaces
= comp
->nsList
;
886 xpctxt
->nsNr
= comp
->nsNr
;
889 xpctxt
->namespaces
= NULL
;
894 * We need to mark that we are "selecting" a var's value;
895 * if any tree fragments are created inside the expression,
896 * then those need to be stored inside the variable; otherwise
897 * we'll eventually free still referenced fragments, before
898 * we leave the scope of the variable.
900 ctxt
->contextVariable
= variable
;
901 variable
->flags
|= XSLT_VAR_IN_SELECT
;
903 result
= xmlXPathCompiledEval(xpExpr
, xpctxt
);
905 variable
->flags
^= XSLT_VAR_IN_SELECT
;
907 * Restore Context states.
909 ctxt
->contextVariable
= oldVar
;
911 xpctxt
->doc
= oldXPDoc
;
912 xpctxt
->node
= oldXPContextNode
;
913 xpctxt
->contextSize
= oldXPContextSize
;
914 xpctxt
->proximityPosition
= oldXPProximityPosition
;
915 xpctxt
->namespaces
= oldXPNamespaces
;
916 xpctxt
->nsNr
= oldXPNsNr
;
918 if ((comp
== NULL
) || (comp
->comp
== NULL
))
919 xmlXPathFreeCompExpr(xpExpr
);
920 if (result
== NULL
) {
921 xsltTransformError(ctxt
, NULL
,
922 (comp
!= NULL
) ? comp
->inst
: NULL
,
923 "Failed to evaluate the expression of variable '%s'.\n",
925 ctxt
->state
= XSLT_STATE_STOPPED
;
927 #ifdef WITH_XSLT_DEBUG_VARIABLE
928 #ifdef LIBXML_DEBUG_ENABLED
930 if ((xsltGenericDebugContext
== stdout
) ||
931 (xsltGenericDebugContext
== stderr
))
932 xmlXPathDebugDumpObject((FILE *)xsltGenericDebugContext
,
938 if (variable
->tree
== NULL
) {
939 result
= xmlXPathNewCString("");
941 if (variable
->tree
) {
943 xmlNodePtr oldInsert
;
945 xsltStackElemPtr oldVar
= ctxt
->contextVariable
;
948 * Generate a result tree fragment.
950 container
= xsltCreateRVT(ctxt
);
951 if (container
== NULL
)
954 * NOTE: Local Result Tree Fragments of params/variables
955 * are not registered globally anymore; the life-time
956 * is not directly dependant of the param/variable itself.
958 * OLD: xsltRegisterTmpRVT(ctxt, container);
961 * Attach the Result Tree Fragment to the variable;
962 * when the variable is freed, it will also free
963 * the Result Tree Fragment.
965 variable
->fragment
= container
;
967 oldOutput
= ctxt
->output
;
968 oldInsert
= ctxt
->insert
;
970 ctxt
->output
= container
;
971 ctxt
->insert
= (xmlNodePtr
) container
;
972 ctxt
->contextVariable
= variable
;
974 * Process the sequence constructor (variable->tree).
975 * The resulting tree will be held by @container.
977 xsltApplyOneTemplate(ctxt
, ctxt
->node
, variable
->tree
,
980 ctxt
->contextVariable
= oldVar
;
981 ctxt
->insert
= oldInsert
;
982 ctxt
->output
= oldOutput
;
984 result
= xmlXPathNewValueTree((xmlNodePtr
) container
);
986 if (result
== NULL
) {
987 result
= xmlXPathNewCString("");
990 * Freeing is not handled there anymore.
991 * QUESTION TODO: What does the above comment mean?
995 #ifdef WITH_XSLT_DEBUG_VARIABLE
996 #ifdef LIBXML_DEBUG_ENABLED
998 if ((xsltGenericDebugContext
== stdout
) ||
999 (xsltGenericDebugContext
== stderr
))
1000 xmlXPathDebugDumpObject((FILE *)xsltGenericDebugContext
,
1008 ctxt
->inst
= oldInst
;
1013 * xsltEvalGlobalVariable:
1014 * @elem: the variable or parameter
1015 * @ctxt: the XSLT transformation context
1017 * Evaluates a the value of a global xsl:variable or
1018 * xsl:param declaration.
1020 * Returns the XPath Object value or NULL in case of error
1022 static xmlXPathObjectPtr
1023 xsltEvalGlobalVariable(xsltStackElemPtr elem
, xsltTransformContextPtr ctxt
)
1025 xmlXPathObjectPtr result
= NULL
;
1027 const xmlChar
* oldVarName
;
1029 #ifdef XSLT_REFACTORED
1030 xsltStyleBasicItemVariablePtr comp
;
1032 xsltStylePreCompPtr comp
;
1035 if ((ctxt
== NULL
) || (elem
== NULL
))
1038 return(elem
->value
);
1041 #ifdef WITH_XSLT_DEBUG_VARIABLE
1042 XSLT_TRACE(ctxt
,XSLT_TRACE_VARIABLES
,xsltGenericDebug(xsltGenericDebugContext
,
1043 "Evaluating global variable %s\n", elem
->name
));
1046 #ifdef WITH_DEBUGGER
1047 if ((ctxt
->debugStatus
!= XSLT_DEBUG_NONE
) &&
1048 elem
->comp
&& elem
->comp
->inst
)
1049 xslHandleDebugger(elem
->comp
->inst
, NULL
, NULL
, ctxt
);
1052 oldInst
= ctxt
->inst
;
1053 #ifdef XSLT_REFACTORED
1054 comp
= (xsltStyleBasicItemVariablePtr
) elem
->comp
;
1058 oldVarName
= elem
->name
;
1059 elem
->name
= xsltComputingGlobalVarMarker
;
1061 * OPTIMIZE TODO: We should consider instantiating global vars/params
1062 * on-demand. The vars/params don't need to be evaluated if never
1063 * called; and in the case of global params, if values for such params
1064 * are provided by the user.
1066 if (elem
->select
!= NULL
) {
1067 xmlXPathCompExprPtr xpExpr
= NULL
;
1069 xmlNodePtr oldXPContextNode
;
1070 int oldXPProximityPosition
, oldXPContextSize
, oldXPNsNr
;
1071 xmlNsPtr
*oldXPNamespaces
;
1072 xmlXPathContextPtr xpctxt
= ctxt
->xpathCtxt
;
1074 if ((comp
!= NULL
) && (comp
->comp
!= NULL
)) {
1075 xpExpr
= comp
->comp
;
1077 xpExpr
= xmlXPathCompile(elem
->select
);
1084 ctxt
->inst
= comp
->inst
;
1089 * "At top-level, the expression or template specifying the
1090 * variable value is evaluated with the same context as that used
1091 * to process the root node of the source document: the current
1092 * node is the root node of the source document and the current
1093 * node list is a list containing just the root node of the source
1097 * Save context states.
1099 oldXPDoc
= xpctxt
->doc
;
1100 oldXPContextNode
= xpctxt
->node
;
1101 oldXPProximityPosition
= xpctxt
->proximityPosition
;
1102 oldXPContextSize
= xpctxt
->contextSize
;
1103 oldXPNamespaces
= xpctxt
->namespaces
;
1104 oldXPNsNr
= xpctxt
->nsNr
;
1106 xpctxt
->node
= ctxt
->initialContextNode
;
1107 xpctxt
->doc
= ctxt
->initialContextDoc
;
1108 xpctxt
->contextSize
= 1;
1109 xpctxt
->proximityPosition
= 1;
1113 #ifdef XSLT_REFACTORED
1114 if (comp
->inScopeNs
!= NULL
) {
1115 xpctxt
->namespaces
= comp
->inScopeNs
->list
;
1116 xpctxt
->nsNr
= comp
->inScopeNs
->xpathNumber
;
1118 xpctxt
->namespaces
= NULL
;
1122 xpctxt
->namespaces
= comp
->nsList
;
1123 xpctxt
->nsNr
= comp
->nsNr
;
1126 xpctxt
->namespaces
= NULL
;
1130 result
= xmlXPathCompiledEval(xpExpr
, xpctxt
);
1133 * Restore Context states.
1135 xpctxt
->doc
= oldXPDoc
;
1136 xpctxt
->node
= oldXPContextNode
;
1137 xpctxt
->contextSize
= oldXPContextSize
;
1138 xpctxt
->proximityPosition
= oldXPProximityPosition
;
1139 xpctxt
->namespaces
= oldXPNamespaces
;
1140 xpctxt
->nsNr
= oldXPNsNr
;
1142 if ((comp
== NULL
) || (comp
->comp
== NULL
))
1143 xmlXPathFreeCompExpr(xpExpr
);
1144 if (result
== NULL
) {
1146 xsltTransformError(ctxt
, NULL
, NULL
,
1147 "Evaluating global variable %s failed\n", elem
->name
);
1149 xsltTransformError(ctxt
, NULL
, comp
->inst
,
1150 "Evaluating global variable %s failed\n", elem
->name
);
1151 ctxt
->state
= XSLT_STATE_STOPPED
;
1152 #ifdef WITH_XSLT_DEBUG_VARIABLE
1153 #ifdef LIBXML_DEBUG_ENABLED
1155 if ((xsltGenericDebugContext
== stdout
) ||
1156 (xsltGenericDebugContext
== stderr
))
1157 xmlXPathDebugDumpObject((FILE *)xsltGenericDebugContext
,
1163 if (elem
->tree
== NULL
) {
1164 result
= xmlXPathNewCString("");
1166 xmlDocPtr container
;
1167 xmlNodePtr oldInsert
;
1168 xmlDocPtr oldOutput
, oldXPDoc
;
1170 * Generate a result tree fragment.
1172 container
= xsltCreateRVT(ctxt
);
1173 if (container
== NULL
)
1176 * Let the lifetime of the tree fragment be handled by
1177 * the Libxslt's garbage collector.
1179 xsltRegisterPersistRVT(ctxt
, container
);
1181 oldOutput
= ctxt
->output
;
1182 oldInsert
= ctxt
->insert
;
1184 oldXPDoc
= ctxt
->xpathCtxt
->doc
;
1186 ctxt
->output
= container
;
1187 ctxt
->insert
= (xmlNodePtr
) container
;
1189 ctxt
->xpathCtxt
->doc
= ctxt
->initialContextDoc
;
1191 * Process the sequence constructor.
1193 xsltApplyOneTemplate(ctxt
, ctxt
->node
, elem
->tree
, NULL
, NULL
);
1195 ctxt
->xpathCtxt
->doc
= oldXPDoc
;
1197 ctxt
->insert
= oldInsert
;
1198 ctxt
->output
= oldOutput
;
1200 result
= xmlXPathNewValueTree((xmlNodePtr
) container
);
1201 if (result
== NULL
) {
1202 result
= xmlXPathNewCString("");
1204 result
->boolval
= 0; /* Freeing is not handled there anymore */
1206 #ifdef WITH_XSLT_DEBUG_VARIABLE
1207 #ifdef LIBXML_DEBUG_ENABLED
1208 if ((xsltGenericDebugContext
== stdout
) ||
1209 (xsltGenericDebugContext
== stderr
))
1210 xmlXPathDebugDumpObject((FILE *)xsltGenericDebugContext
,
1218 elem
->name
= oldVarName
;
1219 ctxt
->inst
= oldInst
;
1220 if (result
!= NULL
) {
1221 elem
->value
= result
;
1228 * xsltEvalGlobalVariables:
1229 * @ctxt: the XSLT transformation context
1231 * Evaluates all global variables and parameters of a stylesheet.
1232 * For internal use only. This is called at start of a transformation.
1234 * Returns 0 in case of success, -1 in case of error
1237 xsltEvalGlobalVariables(xsltTransformContextPtr ctxt
) {
1238 xsltStackElemPtr elem
;
1239 xsltStylesheetPtr style
;
1241 if ((ctxt
== NULL
) || (ctxt
->document
== NULL
))
1244 #ifdef WITH_XSLT_DEBUG_VARIABLE
1245 XSLT_TRACE(ctxt
,XSLT_TRACE_VARIABLES
,xsltGenericDebug(xsltGenericDebugContext
,
1246 "Registering global variables\n"));
1249 * Walk the list from the stylesheets and populate the hash table
1251 style
= ctxt
->style
;
1252 while (style
!= NULL
) {
1253 elem
= style
->variables
;
1255 #ifdef WITH_XSLT_DEBUG_VARIABLE
1256 if ((style
->doc
!= NULL
) && (style
->doc
->URL
!= NULL
)) {
1257 XSLT_TRACE(ctxt
,XSLT_TRACE_VARIABLES
,xsltGenericDebug(xsltGenericDebugContext
,
1258 "Registering global variables from %s\n",
1263 while (elem
!= NULL
) {
1264 xsltStackElemPtr def
;
1267 * Global variables are stored in the variables pool.
1269 def
= (xsltStackElemPtr
)
1270 xmlHashLookup2(ctxt
->globalVars
,
1271 elem
->name
, elem
->nameURI
);
1274 def
= xsltCopyStackElem(elem
);
1275 xmlHashAddEntry2(ctxt
->globalVars
,
1276 elem
->name
, elem
->nameURI
, def
);
1277 } else if ((elem
->comp
!= NULL
) &&
1278 (elem
->comp
->type
== XSLT_FUNC_VARIABLE
)) {
1280 * Redefinition of variables from a different stylesheet
1281 * should not generate a message.
1283 if ((elem
->comp
->inst
!= NULL
) &&
1284 (def
->comp
!= NULL
) && (def
->comp
->inst
!= NULL
) &&
1285 (elem
->comp
->inst
->doc
== def
->comp
->inst
->doc
))
1287 xsltTransformError(ctxt
, style
, elem
->comp
->inst
,
1288 "Global variable %s already defined\n", elem
->name
);
1289 if (style
!= NULL
) style
->errors
++;
1295 style
= xsltNextImport(style
);
1299 * This part does the actual evaluation
1301 xmlHashScan(ctxt
->globalVars
,
1302 (xmlHashScanner
) xsltEvalGlobalVariable
, ctxt
);
1308 * xsltRegisterGlobalVariable:
1309 * @style: the XSLT transformation context
1310 * @name: the variable name
1311 * @ns_uri: the variable namespace URI
1312 * @sel: the expression which need to be evaluated to generate a value
1313 * @tree: the subtree if sel is NULL
1314 * @comp: the precompiled value
1315 * @value: the string value if available
1317 * Register a new variable value. If @value is NULL it unregisters
1320 * Returns 0 in case of success, -1 in case of error
1323 xsltRegisterGlobalVariable(xsltStylesheetPtr style
, const xmlChar
*name
,
1324 const xmlChar
*ns_uri
, const xmlChar
*sel
,
1325 xmlNodePtr tree
, xsltStylePreCompPtr comp
,
1326 const xmlChar
*value
) {
1327 xsltStackElemPtr elem
, tmp
;
1335 #ifdef WITH_XSLT_DEBUG_VARIABLE
1336 if (comp
->type
== XSLT_FUNC_PARAM
)
1337 xsltGenericDebug(xsltGenericDebugContext
,
1338 "Defining global param %s\n", name
);
1340 xsltGenericDebug(xsltGenericDebugContext
,
1341 "Defining global variable %s\n", name
);
1344 elem
= xsltNewStackElem(NULL
);
1348 elem
->name
= xmlDictLookup(style
->dict
, name
, -1);
1349 elem
->select
= xmlDictLookup(style
->dict
, sel
, -1);
1351 elem
->nameURI
= xmlDictLookup(style
->dict
, ns_uri
, -1);
1353 tmp
= style
->variables
;
1356 style
->variables
= elem
;
1358 while (tmp
!= NULL
) {
1359 if ((elem
->comp
->type
== XSLT_FUNC_VARIABLE
) &&
1360 (tmp
->comp
->type
== XSLT_FUNC_VARIABLE
) &&
1361 (xmlStrEqual(elem
->name
, tmp
->name
)) &&
1362 ((elem
->nameURI
== tmp
->nameURI
) ||
1363 (xmlStrEqual(elem
->nameURI
, tmp
->nameURI
))))
1365 xsltTransformError(NULL
, style
, comp
->inst
,
1366 "redefinition of global variable %s\n", elem
->name
);
1369 if (tmp
->next
== NULL
)
1376 if (value
!= NULL
) {
1378 elem
->value
= xmlXPathNewString(value
);
1384 * xsltProcessUserParamInternal
1386 * @ctxt: the XSLT transformation context
1387 * @name: a null terminated parameter name
1388 * @value: a null terminated value (may be an XPath expression)
1389 * @eval: 0 to treat the value literally, else evaluate as XPath expression
1391 * If @eval is 0 then @value is treated literally and is stored in the global
1392 * parameter/variable table without any change.
1394 * Uf @eval is 1 then @value is treated as an XPath expression and is
1395 * evaluated. In this case, if you want to pass a string which will be
1396 * interpreted literally then it must be enclosed in single or double quotes.
1397 * If the string contains single quotes (double quotes) then it cannot be
1398 * enclosed single quotes (double quotes). If the string which you want to
1399 * be treated literally contains both single and double quotes (e.g. Meet
1400 * at Joe's for "Twelfth Night" at 7 o'clock) then there is no suitable
1401 * quoting character. You cannot use ' or " inside the string
1402 * because the replacement of character entities with their equivalents is
1403 * done at a different stage of processing. The solution is to call
1404 * xsltQuoteUserParams or xsltQuoteOneUserParam.
1406 * This needs to be done on parsed stylesheets before starting to apply
1407 * transformations. Normally this will be called (directly or indirectly)
1408 * only from xsltEvalUserParams, xsltEvalOneUserParam, xsltQuoteUserParams,
1409 * or xsltQuoteOneUserParam.
1411 * Returns 0 in case of success, -1 in case of error
1416 xsltProcessUserParamInternal(xsltTransformContextPtr ctxt
,
1417 const xmlChar
* name
,
1418 const xmlChar
* value
,
1421 xsltStylesheetPtr style
;
1422 const xmlChar
*prefix
;
1423 const xmlChar
*href
;
1424 xmlXPathCompExprPtr xpExpr
;
1425 xmlXPathObjectPtr result
;
1427 xsltStackElemPtr elem
;
1438 style
= ctxt
->style
;
1440 #ifdef WITH_XSLT_DEBUG_VARIABLE
1441 XSLT_TRACE(ctxt
,XSLT_TRACE_VARIABLES
,xsltGenericDebug(xsltGenericDebugContext
,
1442 "Evaluating user parameter %s=%s\n", name
, value
));
1449 name
= xsltSplitQName(ctxt
->dict
, name
, &prefix
);
1451 if (prefix
!= NULL
) {
1454 ns
= xmlSearchNs(style
->doc
, xmlDocGetRootElement(style
->doc
),
1457 xsltTransformError(ctxt
, style
, NULL
,
1458 "user param : no namespace bound to prefix %s\n", prefix
);
1468 res_ptr
= xmlHashLookup2(ctxt
->globalVars
, name
, href
);
1470 xsltTransformError(ctxt
, style
, NULL
,
1471 "Global parameter %s already defined\n", name
);
1473 if (ctxt
->globalVars
== NULL
)
1474 ctxt
->globalVars
= xmlHashCreate(20);
1477 * do not overwrite variables with parameters from the command line
1479 while (style
!= NULL
) {
1480 elem
= ctxt
->style
->variables
;
1481 while (elem
!= NULL
) {
1482 if ((elem
->comp
!= NULL
) &&
1483 (elem
->comp
->type
== XSLT_FUNC_VARIABLE
) &&
1484 (xmlStrEqual(elem
->name
, name
)) &&
1485 (xmlStrEqual(elem
->nameURI
, href
))) {
1490 style
= xsltNextImport(style
);
1492 style
= ctxt
->style
;
1496 * Do the evaluation if @eval is non-zero.
1501 xpExpr
= xmlXPathCompile(value
);
1502 if (xpExpr
!= NULL
) {
1504 xmlNodePtr oldXPContextNode
;
1505 int oldXPProximityPosition
, oldXPContextSize
, oldXPNsNr
;
1506 xmlNsPtr
*oldXPNamespaces
;
1507 xmlXPathContextPtr xpctxt
= ctxt
->xpathCtxt
;
1510 * Save context states.
1512 oldXPDoc
= xpctxt
->doc
;
1513 oldXPContextNode
= xpctxt
->node
;
1514 oldXPProximityPosition
= xpctxt
->proximityPosition
;
1515 oldXPContextSize
= xpctxt
->contextSize
;
1516 oldXPNamespaces
= xpctxt
->namespaces
;
1517 oldXPNsNr
= xpctxt
->nsNr
;
1521 * "At top-level, the expression or template specifying the
1522 * variable value is evaluated with the same context as that used
1523 * to process the root node of the source document: the current
1524 * node is the root node of the source document and the current
1525 * node list is a list containing just the root node of the source
1528 xpctxt
->doc
= ctxt
->initialContextDoc
;
1529 xpctxt
->node
= ctxt
->initialContextNode
;
1530 xpctxt
->contextSize
= 1;
1531 xpctxt
->proximityPosition
= 1;
1533 * There is really no in scope namespace for parameters on the
1536 xpctxt
->namespaces
= NULL
;
1539 result
= xmlXPathCompiledEval(xpExpr
, xpctxt
);
1542 * Restore Context states.
1544 xpctxt
->doc
= oldXPDoc
;
1545 xpctxt
->node
= oldXPContextNode
;
1546 xpctxt
->contextSize
= oldXPContextSize
;
1547 xpctxt
->proximityPosition
= oldXPProximityPosition
;
1548 xpctxt
->namespaces
= oldXPNamespaces
;
1549 xpctxt
->nsNr
= oldXPNsNr
;
1551 xmlXPathFreeCompExpr(xpExpr
);
1553 if (result
== NULL
) {
1554 xsltTransformError(ctxt
, style
, NULL
,
1555 "Evaluating user parameter %s failed\n", name
);
1556 ctxt
->state
= XSLT_STATE_STOPPED
;
1562 * If @eval is 0 then @value is to be taken literally and result is NULL
1564 * If @eval is not 0, then @value is an XPath expression and has been
1565 * successfully evaluated and result contains the resulting value and
1568 * Now create an xsltStackElemPtr for insertion into the context's
1569 * global variable/parameter hash table.
1572 #ifdef WITH_XSLT_DEBUG_VARIABLE
1573 #ifdef LIBXML_DEBUG_ENABLED
1574 if ((xsltGenericDebugContext
== stdout
) ||
1575 (xsltGenericDebugContext
== stderr
))
1576 xmlXPathDebugDumpObject((FILE *)xsltGenericDebugContext
,
1581 elem
= xsltNewStackElem(NULL
);
1584 elem
->select
= xmlDictLookup(ctxt
->dict
, value
, -1);
1586 elem
->nameURI
= xmlDictLookup(ctxt
->dict
, href
, -1);
1590 elem
->value
= xmlXPathNewString(value
);
1593 elem
->value
= result
;
1598 * Global parameters are stored in the XPath context variables pool.
1601 res
= xmlHashAddEntry2(ctxt
->globalVars
, name
, href
, elem
);
1603 xsltFreeStackElem(elem
);
1604 xsltTransformError(ctxt
, style
, NULL
,
1605 "Global parameter %s already defined\n", name
);
1611 * xsltEvalUserParams:
1613 * @ctxt: the XSLT transformation context
1614 * @params: a NULL terminated array of parameters name/value tuples
1616 * Evaluate the global variables of a stylesheet. This needs to be
1617 * done on parsed stylesheets before starting to apply transformations.
1618 * Each of the parameters is evaluated as an XPath expression and stored
1619 * in the global variables/parameter hash table. If you want your
1620 * parameter used literally, use xsltQuoteUserParams.
1622 * Returns 0 in case of success, -1 in case of error
1626 xsltEvalUserParams(xsltTransformContextPtr ctxt
, const char **params
) {
1628 const xmlChar
*name
;
1629 const xmlChar
*value
;
1633 while (params
[indx
] != NULL
) {
1634 name
= (const xmlChar
*) params
[indx
++];
1635 value
= (const xmlChar
*) params
[indx
++];
1636 if (xsltEvalOneUserParam(ctxt
, name
, value
) != 0)
1643 * xsltQuoteUserParams:
1645 * @ctxt: the XSLT transformation context
1646 * @params: a NULL terminated arry of parameters names/values tuples
1648 * Similar to xsltEvalUserParams, but the values are treated literally and
1649 * are * *not* evaluated as XPath expressions. This should be done on parsed
1650 * stylesheets before starting to apply transformations.
1652 * Returns 0 in case of success, -1 in case of error.
1656 xsltQuoteUserParams(xsltTransformContextPtr ctxt
, const char **params
) {
1658 const xmlChar
*name
;
1659 const xmlChar
*value
;
1663 while (params
[indx
] != NULL
) {
1664 name
= (const xmlChar
*) params
[indx
++];
1665 value
= (const xmlChar
*) params
[indx
++];
1666 if (xsltQuoteOneUserParam(ctxt
, name
, value
) != 0)
1673 * xsltEvalOneUserParam:
1674 * @ctxt: the XSLT transformation context
1675 * @name: a null terminated string giving the name of the parameter
1676 * @value: a null terminated string giving the XPath expression to be evaluated
1678 * This is normally called from xsltEvalUserParams to process a single
1679 * parameter from a list of parameters. The @value is evaluated as an
1680 * XPath expression and the result is stored in the context's global
1681 * variable/parameter hash table.
1683 * To have a parameter treated literally (not as an XPath expression)
1684 * use xsltQuoteUserParams (or xsltQuoteOneUserParam). For more
1685 * details see description of xsltProcessOneUserParamInternal.
1687 * Returns 0 in case of success, -1 in case of error.
1691 xsltEvalOneUserParam(xsltTransformContextPtr ctxt
,
1692 const xmlChar
* name
,
1693 const xmlChar
* value
) {
1694 return xsltProcessUserParamInternal(ctxt
, name
, value
,
1695 1 /* xpath eval ? */);
1699 * xsltQuoteOneUserParam:
1700 * @ctxt: the XSLT transformation context
1701 * @name: a null terminated string giving the name of the parameter
1702 * @value: a null terminated string giving the parameter value
1704 * This is normally called from xsltQuoteUserParams to process a single
1705 * parameter from a list of parameters. The @value is stored in the
1706 * context's global variable/parameter hash table.
1708 * Returns 0 in case of success, -1 in case of error.
1712 xsltQuoteOneUserParam(xsltTransformContextPtr ctxt
,
1713 const xmlChar
* name
,
1714 const xmlChar
* value
) {
1715 return xsltProcessUserParamInternal(ctxt
, name
, value
,
1716 0 /* xpath eval ? */);
1720 * xsltBuildVariable:
1721 * @ctxt: the XSLT transformation context
1722 * @comp: the precompiled form
1723 * @tree: the tree if select is NULL
1725 * Computes a new variable value.
1727 * Returns the xsltStackElemPtr or NULL in case of error
1729 static xsltStackElemPtr
1730 xsltBuildVariable(xsltTransformContextPtr ctxt
,
1731 xsltStylePreCompPtr castedComp
,
1734 #ifdef XSLT_REFACTORED
1735 xsltStyleBasicItemVariablePtr comp
=
1736 (xsltStyleBasicItemVariablePtr
) castedComp
;
1738 xsltStylePreCompPtr comp
= castedComp
;
1740 xsltStackElemPtr elem
;
1742 #ifdef WITH_XSLT_DEBUG_VARIABLE
1743 XSLT_TRACE(ctxt
,XSLT_TRACE_VARIABLES
,xsltGenericDebug(xsltGenericDebugContext
,
1744 "Building variable %s", comp
->name
));
1745 if (comp
->select
!= NULL
)
1746 XSLT_TRACE(ctxt
,XSLT_TRACE_VARIABLES
,xsltGenericDebug(xsltGenericDebugContext
,
1747 " select %s", comp
->select
));
1748 XSLT_TRACE(ctxt
,XSLT_TRACE_VARIABLES
,xsltGenericDebug(xsltGenericDebugContext
, "\n"));
1751 elem
= xsltNewStackElem(ctxt
);
1754 elem
->comp
= (xsltStylePreCompPtr
) comp
;
1755 elem
->name
= comp
->name
;
1756 elem
->select
= comp
->select
;
1757 elem
->nameURI
= comp
->ns
;
1759 elem
->value
= xsltEvalVariable(ctxt
, elem
,
1760 (xsltStylePreCompPtr
) comp
);
1761 if (elem
->value
!= NULL
)
1767 * xsltRegisterVariable:
1768 * @ctxt: the XSLT transformation context
1769 * @comp: the compiled XSLT-variable (or param) instruction
1770 * @tree: the tree if select is NULL
1771 * @isParam: indicates if this is a parameter
1773 * Computes and registers a new variable.
1775 * Returns 0 in case of success, -1 in case of error
1778 xsltRegisterVariable(xsltTransformContextPtr ctxt
,
1779 xsltStylePreCompPtr castedComp
,
1780 xmlNodePtr tree
, int isParam
)
1782 #ifdef XSLT_REFACTORED
1783 xsltStyleBasicItemVariablePtr comp
=
1784 (xsltStyleBasicItemVariablePtr
) castedComp
;
1786 xsltStylePreCompPtr comp
= castedComp
;
1789 xsltStackElemPtr variable
;
1791 #ifdef XSLT_REFACTORED
1793 * REFACTORED NOTE: Redefinitions of vars/params are checked
1794 * at compilation time in the refactored code.
1795 * xsl:with-param parameters are checked in xsltApplyXSLTTemplate().
1798 present
= xsltCheckStackElem(ctxt
, comp
->name
, comp
->ns
);
1800 if ((present
!= 0) && (present
!= 3)) {
1801 /* TODO: report QName. */
1802 xsltTransformError(ctxt
, NULL
, comp
->inst
,
1803 "XSLT-variable: Redefinition of variable '%s'.\n", comp
->name
);
1806 } else if (present
!= 0) {
1807 if ((present
== 1) || (present
== 2)) {
1808 /* TODO: report QName. */
1809 xsltTransformError(ctxt
, NULL
, comp
->inst
,
1810 "XSLT-param: Redefinition of parameter '%s'.\n", comp
->name
);
1813 #ifdef WITH_XSLT_DEBUG_VARIABLE
1814 XSLT_TRACE(ctxt
,XSLT_TRACE_VARIABLES
,xsltGenericDebug(xsltGenericDebugContext
,
1815 "param %s defined by caller\n", comp
->name
));
1819 #endif /* else of XSLT_REFACTORED */
1821 variable
= xsltBuildVariable(ctxt
, (xsltStylePreCompPtr
) comp
, tree
);
1822 xsltAddStackElem(ctxt
, variable
);
1827 * xsltGlobalVariableLookup:
1828 * @ctxt: the XSLT transformation context
1829 * @name: the variable name
1830 * @ns_uri: the variable namespace URI
1832 * Search in the Variable array of the context for the given
1835 * Returns the value or NULL if not found
1837 static xmlXPathObjectPtr
1838 xsltGlobalVariableLookup(xsltTransformContextPtr ctxt
, const xmlChar
*name
,
1839 const xmlChar
*ns_uri
) {
1840 xsltStackElemPtr elem
;
1841 xmlXPathObjectPtr ret
= NULL
;
1844 * Lookup the global variables in XPath global variable hash table
1846 if ((ctxt
->xpathCtxt
== NULL
) || (ctxt
->globalVars
== NULL
))
1848 elem
= (xsltStackElemPtr
)
1849 xmlHashLookup2(ctxt
->globalVars
, name
, ns_uri
);
1851 #ifdef WITH_XSLT_DEBUG_VARIABLE
1852 XSLT_TRACE(ctxt
,XSLT_TRACE_VARIABLES
,xsltGenericDebug(xsltGenericDebugContext
,
1853 "global variable not found %s\n", name
));
1858 * URGENT TODO: Move the detection of recursive definitions
1861 if (elem
->computed
== 0) {
1862 if (elem
->name
== xsltComputingGlobalVarMarker
) {
1863 xsltTransformError(ctxt
, NULL
, elem
->comp
->inst
,
1864 "Recursive definition of %s\n", name
);
1867 ret
= xsltEvalGlobalVariable(elem
, ctxt
);
1870 return(xmlXPathObjectCopy(ret
));
1874 * xsltVariableLookup:
1875 * @ctxt: the XSLT transformation context
1876 * @name: the variable name
1877 * @ns_uri: the variable namespace URI
1879 * Search in the Variable array of the context for the given
1882 * Returns the value or NULL if not found
1885 xsltVariableLookup(xsltTransformContextPtr ctxt
, const xmlChar
*name
,
1886 const xmlChar
*ns_uri
) {
1887 xsltStackElemPtr elem
;
1892 elem
= xsltStackLookup(ctxt
, name
, ns_uri
);
1894 return(xsltGlobalVariableLookup(ctxt
, name
, ns_uri
));
1896 if (elem
->computed
== 0) {
1897 #ifdef WITH_XSLT_DEBUG_VARIABLE
1898 XSLT_TRACE(ctxt
,XSLT_TRACE_VARIABLES
,xsltGenericDebug(xsltGenericDebugContext
,
1899 "uncomputed variable %s\n", name
));
1901 elem
->value
= xsltEvalVariable(ctxt
, elem
, NULL
);
1904 if (elem
->value
!= NULL
)
1905 return(xmlXPathObjectCopy(elem
->value
));
1906 #ifdef WITH_XSLT_DEBUG_VARIABLE
1907 XSLT_TRACE(ctxt
,XSLT_TRACE_VARIABLES
,xsltGenericDebug(xsltGenericDebugContext
,
1908 "variable not found %s\n", name
));
1914 * xsltParseStylesheetCallerParam:
1915 * @ctxt: the XSLT transformation context
1916 * @inst: the xsl:with-param instruction element
1918 * Processes an xsl:with-param instruction at transformation time.
1919 * The value is compute, but not recorded.
1920 * NOTE that this is also called with an *xsl:param* element
1921 * from exsltFuncFunctionFunction().
1923 * Returns the new xsltStackElemPtr or NULL
1927 xsltParseStylesheetCallerParam(xsltTransformContextPtr ctxt
, xmlNodePtr inst
)
1929 #ifdef XSLT_REFACTORED
1930 xsltStyleBasicItemVariablePtr comp
;
1932 xsltStylePreCompPtr comp
;
1934 xmlNodePtr tree
= NULL
; /* The first child node of the instruction or
1935 the instruction itself. */
1936 xsltStackElemPtr param
= NULL
;
1938 if ((ctxt
== NULL
) || (inst
== NULL
) || (inst
->type
!= XML_ELEMENT_NODE
))
1941 #ifdef XSLT_REFACTORED
1942 comp
= (xsltStyleBasicItemVariablePtr
) inst
->psvi
;
1944 comp
= (xsltStylePreCompPtr
) inst
->psvi
;
1948 xsltTransformError(ctxt
, NULL
, inst
,
1949 "Internal error in xsltParseStylesheetCallerParam(): "
1950 "The XSLT 'with-param' instruction was not compiled.\n");
1953 if (comp
->name
== NULL
) {
1954 xsltTransformError(ctxt
, NULL
, inst
,
1955 "Internal error in xsltParseStylesheetCallerParam(): "
1956 "XSLT 'with-param': The attribute 'name' was not compiled.\n");
1960 #ifdef WITH_XSLT_DEBUG_VARIABLE
1961 XSLT_TRACE(ctxt
,XSLT_TRACE_VARIABLES
,xsltGenericDebug(xsltGenericDebugContext
,
1962 "Handling xsl:with-param %s\n", comp
->name
));
1965 if (comp
->select
== NULL
) {
1966 tree
= inst
->children
;
1968 #ifdef WITH_XSLT_DEBUG_VARIABLE
1969 XSLT_TRACE(ctxt
,XSLT_TRACE_VARIABLES
,xsltGenericDebug(xsltGenericDebugContext
,
1970 " select %s\n", comp
->select
));
1975 param
= xsltBuildVariable(ctxt
, (xsltStylePreCompPtr
) comp
, tree
);
1981 * xsltParseGlobalVariable:
1982 * @style: the XSLT stylesheet
1983 * @cur: the "variable" element
1985 * Parses a global XSLT 'variable' declaration at compilation time
1989 xsltParseGlobalVariable(xsltStylesheetPtr style
, xmlNodePtr cur
)
1991 #ifdef XSLT_REFACTORED
1992 xsltStyleItemVariablePtr comp
;
1994 xsltStylePreCompPtr comp
;
1997 if ((cur
== NULL
) || (style
== NULL
) || (cur
->type
!= XML_ELEMENT_NODE
))
2000 #ifdef XSLT_REFACTORED
2002 * Note that xsltStylePreCompute() will be called from
2005 comp
= (xsltStyleItemVariablePtr
) cur
->psvi
;
2007 xsltStylePreCompute(style
, cur
);
2008 comp
= (xsltStylePreCompPtr
) cur
->psvi
;
2011 xsltTransformError(NULL
, style
, cur
,
2012 "xsl:variable : compilation failed\n");
2016 if (comp
->name
== NULL
) {
2017 xsltTransformError(NULL
, style
, cur
,
2018 "xsl:variable : missing name attribute\n");
2023 * Parse the content (a sequence constructor) of xsl:variable.
2025 if (cur
->children
!= NULL
) {
2026 #ifdef XSLT_REFACTORED
2027 xsltParseSequenceConstructor(XSLT_CCTXT(style
), cur
->children
);
2029 xsltParseTemplateContent(style
, cur
);
2032 #ifdef WITH_XSLT_DEBUG_VARIABLE
2033 xsltGenericDebug(xsltGenericDebugContext
,
2034 "Registering global variable %s\n", comp
->name
);
2037 xsltRegisterGlobalVariable(style
, comp
->name
, comp
->ns
,
2038 comp
->select
, cur
->children
, (xsltStylePreCompPtr
) comp
,
2043 * xsltParseGlobalParam:
2044 * @style: the XSLT stylesheet
2045 * @cur: the "param" element
2047 * parse an XSLT transformation param declaration and record
2052 xsltParseGlobalParam(xsltStylesheetPtr style
, xmlNodePtr cur
) {
2053 #ifdef XSLT_REFACTORED
2054 xsltStyleItemParamPtr comp
;
2056 xsltStylePreCompPtr comp
;
2059 if ((cur
== NULL
) || (style
== NULL
) || (cur
->type
!= XML_ELEMENT_NODE
))
2062 #ifdef XSLT_REFACTORED
2064 * Note that xsltStylePreCompute() will be called from
2067 comp
= (xsltStyleItemParamPtr
) cur
->psvi
;
2069 xsltStylePreCompute(style
, cur
);
2070 comp
= (xsltStylePreCompPtr
) cur
->psvi
;
2073 xsltTransformError(NULL
, style
, cur
,
2074 "xsl:param : compilation failed\n");
2078 if (comp
->name
== NULL
) {
2079 xsltTransformError(NULL
, style
, cur
,
2080 "xsl:param : missing name attribute\n");
2085 * Parse the content (a sequence constructor) of xsl:param.
2087 if (cur
->children
!= NULL
) {
2088 #ifdef XSLT_REFACTORED
2089 xsltParseSequenceConstructor(XSLT_CCTXT(style
), cur
->children
);
2091 xsltParseTemplateContent(style
, cur
);
2095 #ifdef WITH_XSLT_DEBUG_VARIABLE
2096 xsltGenericDebug(xsltGenericDebugContext
,
2097 "Registering global param %s\n", comp
->name
);
2100 xsltRegisterGlobalVariable(style
, comp
->name
, comp
->ns
,
2101 comp
->select
, cur
->children
, (xsltStylePreCompPtr
) comp
,
2106 * xsltParseStylesheetVariable:
2107 * @ctxt: the XSLT transformation context
2108 * @inst: the xsl:variable instruction element
2110 * Registers a local XSLT 'variable' instruction at transformation time
2111 * and evaluates its value.
2114 xsltParseStylesheetVariable(xsltTransformContextPtr ctxt
, xmlNodePtr inst
)
2116 #ifdef XSLT_REFACTORED
2117 xsltStyleItemVariablePtr comp
;
2119 xsltStylePreCompPtr comp
;
2122 if ((inst
== NULL
) || (ctxt
== NULL
) || (inst
->type
!= XML_ELEMENT_NODE
))
2127 xsltTransformError(ctxt
, NULL
, inst
,
2128 "Internal error in xsltParseStylesheetVariable(): "
2129 "The XSLT 'variable' instruction was not compiled.\n");
2132 if (comp
->name
== NULL
) {
2133 xsltTransformError(ctxt
, NULL
, inst
,
2134 "Internal error in xsltParseStylesheetVariable(): "
2135 "The attribute 'name' was not compiled.\n");
2139 #ifdef WITH_XSLT_DEBUG_VARIABLE
2140 XSLT_TRACE(ctxt
,XSLT_TRACE_VARIABLES
,xsltGenericDebug(xsltGenericDebugContext
,
2141 "Registering variable '%s'\n", comp
->name
));
2144 xsltRegisterVariable(ctxt
, (xsltStylePreCompPtr
) comp
, inst
->children
, 0);
2148 * xsltParseStylesheetParam:
2149 * @ctxt: the XSLT transformation context
2150 * @cur: the XSLT 'param' element
2152 * Registers a local XSLT 'param' declaration at transformation time and
2153 * evaluates its value.
2156 xsltParseStylesheetParam(xsltTransformContextPtr ctxt
, xmlNodePtr cur
)
2158 #ifdef XSLT_REFACTORED
2159 xsltStyleItemParamPtr comp
;
2161 xsltStylePreCompPtr comp
;
2164 if ((cur
== NULL
) || (ctxt
== NULL
) || (cur
->type
!= XML_ELEMENT_NODE
))
2168 if ((comp
== NULL
) || (comp
->name
== NULL
)) {
2169 xsltTransformError(ctxt
, NULL
, cur
,
2170 "Internal error in xsltParseStylesheetParam(): "
2171 "The XSLT 'param' declaration was not compiled correctly.\n");
2175 #ifdef WITH_XSLT_DEBUG_VARIABLE
2176 XSLT_TRACE(ctxt
,XSLT_TRACE_VARIABLES
,xsltGenericDebug(xsltGenericDebugContext
,
2177 "Registering param %s\n", comp
->name
));
2180 xsltRegisterVariable(ctxt
, (xsltStylePreCompPtr
) comp
, cur
->children
, 1);
2184 * xsltFreeGlobalVariables:
2185 * @ctxt: the XSLT transformation context
2187 * Free up the data associated to the global variables
2192 xsltFreeGlobalVariables(xsltTransformContextPtr ctxt
) {
2193 xmlHashFree(ctxt
->globalVars
, (xmlHashDeallocator
) xsltFreeStackElem
);
2197 * xsltXPathVariableLookup:
2198 * @ctxt: a void * but the the XSLT transformation context actually
2199 * @name: the variable name
2200 * @ns_uri: the variable namespace URI
2202 * This is the entry point when a varibale is needed by the XPath
2205 * Returns the value or NULL if not found
2208 xsltXPathVariableLookup(void *ctxt
, const xmlChar
*name
,
2209 const xmlChar
*ns_uri
) {
2210 xsltTransformContextPtr tctxt
;
2211 xmlXPathObjectPtr valueObj
= NULL
;
2213 if ((ctxt
== NULL
) || (name
== NULL
))
2216 #ifdef WITH_XSLT_DEBUG_VARIABLE
2217 XSLT_TRACE(((xsltTransformContextPtr
)ctxt
),XSLT_TRACE_VARIABLES
,xsltGenericDebug(xsltGenericDebugContext
,
2218 "Lookup variable '%s'\n", name
));
2221 tctxt
= (xsltTransformContextPtr
) ctxt
;
2223 * Local variables/params ---------------------------------------------
2225 * Do the lookup from the top of the stack, but
2226 * don't use params being computed in a call-param
2227 * First lookup expects the variable name and URI to
2228 * come from the disctionnary and hence pointer comparison.
2230 if (tctxt
->varsNr
!= 0) {
2232 xsltStackElemPtr variable
= NULL
, cur
;
2234 for (i
= tctxt
->varsNr
; i
> tctxt
->varsBase
; i
--) {
2235 cur
= tctxt
->varsTab
[i
-1];
2236 if ((cur
->name
== name
) && (cur
->nameURI
== ns_uri
)) {
2241 goto local_variable_found
;
2246 * Redo the lookup with interned strings to avoid string comparison.
2248 * OPTIMIZE TODO: The problem here is, that if we request a
2249 * global variable, then this will be also executed.
2252 const xmlChar
*tmpName
= name
, *tmpNsName
= ns_uri
;
2254 name
= xmlDictLookup(tctxt
->dict
, name
, -1);
2256 ns_uri
= xmlDictLookup(tctxt
->dict
, ns_uri
, -1);
2257 if ((tmpName
!= name
) || (tmpNsName
!= ns_uri
)) {
2258 for (i
= tctxt
->varsNr
; i
> tctxt
->varsBase
; i
--) {
2259 cur
= tctxt
->varsTab
[i
-1];
2260 if ((cur
->name
== name
) && (cur
->nameURI
== ns_uri
)) {
2265 goto local_variable_found
;
2271 local_variable_found
:
2274 if (variable
->computed
== 0) {
2276 #ifdef WITH_XSLT_DEBUG_VARIABLE
2277 XSLT_TRACE(tctxt
,XSLT_TRACE_VARIABLES
,xsltGenericDebug(xsltGenericDebugContext
,
2278 "uncomputed variable '%s'\n", name
));
2280 variable
->value
= xsltEvalVariable(tctxt
, variable
, NULL
);
2281 variable
->computed
= 1;
2283 if (variable
->value
!= NULL
) {
2284 valueObj
= xmlXPathObjectCopy(variable
->value
);
2290 * Global variables/params --------------------------------------------
2292 if (tctxt
->globalVars
) {
2293 valueObj
= xsltGlobalVariableLookup(tctxt
, name
, ns_uri
);
2296 if (valueObj
== NULL
) {
2298 #ifdef WITH_XSLT_DEBUG_VARIABLE
2299 XSLT_TRACE(tctxt
,XSLT_TRACE_VARIABLES
,xsltGenericDebug(xsltGenericDebugContext
,
2300 "variable not found '%s'\n", name
));
2304 xsltTransformError(tctxt
, NULL
, tctxt
->inst
,
2305 "Variable '{%s}%s' has not been declared.\n", ns_uri
, name
);
2307 xsltTransformError(tctxt
, NULL
, tctxt
->inst
,
2308 "Variable '%s' has not been declared.\n", name
);
2312 #ifdef WITH_XSLT_DEBUG_VARIABLE
2313 XSLT_TRACE(tctxt
,XSLT_TRACE_VARIABLES
,xsltGenericDebug(xsltGenericDebugContext
,
2314 "found variable '%s'\n", name
));