2 * templates.c: Implementation of the template processing
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/globals.h>
19 #include <libxml/xmlerror.h>
20 #include <libxml/tree.h>
21 #include <libxml/dict.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 "functions.h"
29 #include "templates.h"
30 #include "transform.h"
31 #include "namespaces.h"
32 #include "attributes.h"
34 #ifdef WITH_XSLT_DEBUG
35 #define WITH_XSLT_DEBUG_TEMPLATES
38 /************************************************************************
42 ************************************************************************/
45 * xsltEvalXPathPredicate:
46 * @ctxt: the XSLT transformation context
47 * @comp: the XPath compiled expression
48 * @nsList: the namespaces in scope
49 * @nsNr: the number of namespaces in scope
51 * Process the expression using XPath and evaluate the result as
54 * Returns 1 is the predicate was true, 0 otherwise
57 xsltEvalXPathPredicate(xsltTransformContextPtr ctxt
, xmlXPathCompExprPtr comp
,
58 xmlNsPtr
*nsList
, int nsNr
) {
60 xmlXPathObjectPtr res
;
62 xmlNsPtr
*oldNamespaces
;
64 int oldProximityPosition
, oldContextSize
;
66 oldContextSize
= ctxt
->xpathCtxt
->contextSize
;
67 oldProximityPosition
= ctxt
->xpathCtxt
->proximityPosition
;
68 oldNsNr
= ctxt
->xpathCtxt
->nsNr
;
69 oldNamespaces
= ctxt
->xpathCtxt
->namespaces
;
72 ctxt
->xpathCtxt
->node
= ctxt
->node
;
73 ctxt
->xpathCtxt
->namespaces
= nsList
;
74 ctxt
->xpathCtxt
->nsNr
= nsNr
;
76 res
= xmlXPathCompiledEval(comp
, ctxt
->xpathCtxt
);
79 ret
= xmlXPathEvalPredicate(ctxt
->xpathCtxt
, res
);
80 xmlXPathFreeObject(res
);
81 #ifdef WITH_XSLT_DEBUG_TEMPLATES
82 XSLT_TRACE(ctxt
,XSLT_TRACE_TEMPLATES
,xsltGenericDebug(xsltGenericDebugContext
,
83 "xsltEvalXPathPredicate: returns %d\n", ret
));
86 #ifdef WITH_XSLT_DEBUG_TEMPLATES
87 XSLT_TRACE(ctxt
,XSLT_TRACE_TEMPLATES
,xsltGenericDebug(xsltGenericDebugContext
,
88 "xsltEvalXPathPredicate: failed\n"));
90 ctxt
->state
= XSLT_STATE_STOPPED
;
93 ctxt
->xpathCtxt
->nsNr
= oldNsNr
;
95 ctxt
->xpathCtxt
->namespaces
= oldNamespaces
;
97 ctxt
->xpathCtxt
->contextSize
= oldContextSize
;
98 ctxt
->xpathCtxt
->proximityPosition
= oldProximityPosition
;
104 * xsltEvalXPathStringNs:
105 * @ctxt: the XSLT transformation context
106 * @comp: the compiled XPath expression
107 * @nsNr: the number of namespaces in the list
108 * @nsList: the list of in-scope namespaces to use
110 * Process the expression using XPath, allowing to pass a namespace mapping
111 * context and get a string
113 * Returns the computed string value or NULL, must be deallocated by the
117 xsltEvalXPathStringNs(xsltTransformContextPtr ctxt
, xmlXPathCompExprPtr comp
,
118 int nsNr
, xmlNsPtr
*nsList
) {
120 xmlXPathObjectPtr res
;
125 xmlNsPtr
*oldNamespaces
;
127 oldInst
= ctxt
->inst
;
128 oldNode
= ctxt
->node
;
129 oldPos
= ctxt
->xpathCtxt
->proximityPosition
;
130 oldSize
= ctxt
->xpathCtxt
->contextSize
;
131 oldNsNr
= ctxt
->xpathCtxt
->nsNr
;
132 oldNamespaces
= ctxt
->xpathCtxt
->namespaces
;
134 ctxt
->xpathCtxt
->node
= ctxt
->node
;
135 /* TODO: do we need to propagate the namespaces here ? */
136 ctxt
->xpathCtxt
->namespaces
= nsList
;
137 ctxt
->xpathCtxt
->nsNr
= nsNr
;
138 res
= xmlXPathCompiledEval(comp
, ctxt
->xpathCtxt
);
140 if (res
->type
!= XPATH_STRING
)
141 res
= xmlXPathConvertString(res
);
142 if (res
->type
== XPATH_STRING
) {
143 ret
= res
->stringval
;
144 res
->stringval
= NULL
;
146 xsltTransformError(ctxt
, NULL
, NULL
,
147 "xpath : string() function didn't return a String\n");
149 xmlXPathFreeObject(res
);
151 ctxt
->state
= XSLT_STATE_STOPPED
;
153 #ifdef WITH_XSLT_DEBUG_TEMPLATES
154 XSLT_TRACE(ctxt
,XSLT_TRACE_TEMPLATES
,xsltGenericDebug(xsltGenericDebugContext
,
155 "xsltEvalXPathString: returns %s\n", ret
));
157 ctxt
->inst
= oldInst
;
158 ctxt
->node
= oldNode
;
159 ctxt
->xpathCtxt
->contextSize
= oldSize
;
160 ctxt
->xpathCtxt
->proximityPosition
= oldPos
;
161 ctxt
->xpathCtxt
->nsNr
= oldNsNr
;
162 ctxt
->xpathCtxt
->namespaces
= oldNamespaces
;
167 * xsltEvalXPathString:
168 * @ctxt: the XSLT transformation context
169 * @comp: the compiled XPath expression
171 * Process the expression using XPath and get a string
173 * Returns the computed string value or NULL, must be deallocated by the
177 xsltEvalXPathString(xsltTransformContextPtr ctxt
, xmlXPathCompExprPtr comp
) {
178 return(xsltEvalXPathStringNs(ctxt
, comp
, 0, NULL
));
182 * xsltEvalTemplateString:
183 * @ctxt: the XSLT transformation context
184 * @contextNode: the current node in the source tree
185 * @inst: the XSLT instruction (xsl:comment, xsl:processing-instruction)
187 * Processes the sequence constructor of the given instruction on
188 * @contextNode and converts the resulting tree to a string.
189 * This is needed by e.g. xsl:comment and xsl:processing-instruction.
191 * Returns the computed string value or NULL; it's up to the caller to
195 xsltEvalTemplateString(xsltTransformContextPtr ctxt
,
196 xmlNodePtr contextNode
,
199 xmlNodePtr oldInsert
, insert
= NULL
;
202 if ((ctxt
== NULL
) || (contextNode
== NULL
) || (inst
== NULL
) ||
203 (inst
->type
!= XML_ELEMENT_NODE
))
206 if (inst
->children
== NULL
)
210 * This creates a temporary element-node to add the resulting
212 * OPTIMIZE TODO: Keep such an element-node in the transformation
213 * context to avoid creating it every time.
215 insert
= xmlNewDocNode(ctxt
->output
, NULL
,
216 (const xmlChar
*)"fake", NULL
);
217 if (insert
== NULL
) {
218 xsltTransformError(ctxt
, NULL
, contextNode
,
219 "Failed to create temporary node\n");
222 oldInsert
= ctxt
->insert
;
223 ctxt
->insert
= insert
;
225 * OPTIMIZE TODO: if inst->children consists only of text-nodes.
227 xsltApplyOneTemplate(ctxt
, contextNode
, inst
->children
, NULL
, NULL
);
229 ctxt
->insert
= oldInsert
;
231 ret
= xmlNodeGetContent(insert
);
238 * xsltAttrTemplateValueProcessNode:
239 * @ctxt: the XSLT transformation context
240 * @str: the attribute template node value
241 * @inst: the instruction (or LRE) in the stylesheet holding the
242 * attribute with an AVT
244 * Process the given string, allowing to pass a namespace mapping
245 * context and return the new string value.
248 * - xsltAttrTemplateValueProcess() (templates.c)
249 * - xsltEvalAttrValueTemplate() (templates.c)
251 * QUESTION: Why is this function public? It is not used outside
254 * Returns the computed string value or NULL, must be deallocated by the
258 xsltAttrTemplateValueProcessNode(xsltTransformContextPtr ctxt
,
259 const xmlChar
*str
, xmlNodePtr inst
)
264 xmlNsPtr
*nsList
= NULL
;
267 if (str
== NULL
) return(NULL
);
269 return(xmlStrndup((xmlChar
*)"", 0));
274 if (*(cur
+1) == '{') { /* escaped '{' */
276 ret
= xmlStrncat(ret
, str
, cur
- str
);
281 ret
= xmlStrncat(ret
, str
, cur
- str
);
284 while ((*cur
!= 0) && (*cur
!= '}')) {
285 /* Need to check for literal (bug539741) */
286 if ((*cur
== '\'') || (*cur
== '"')) {
287 char delim
= *(cur
++);
288 while ((*cur
!= 0) && (*cur
!= delim
))
291 cur
++; /* skip the ending delimiter */
296 xsltTransformError(ctxt
, NULL
, inst
,
297 "xsltAttrTemplateValueProcessNode: unmatched '{'\n");
298 ret
= xmlStrncat(ret
, str
, cur
- str
);
302 expr
= xmlStrndup(str
, cur
- str
);
305 else if (*expr
== '{') {
306 ret
= xmlStrcat(ret
, expr
);
309 xmlXPathCompExprPtr comp
;
311 * TODO: keep precompiled form around
313 if ((nsList
== NULL
) && (inst
!= NULL
)) {
316 nsList
= xmlGetNsList(inst
->doc
, inst
);
317 if (nsList
!= NULL
) {
318 while (nsList
[i
] != NULL
)
323 comp
= xmlXPathCompile(expr
);
324 val
= xsltEvalXPathStringNs(ctxt
, comp
, nsNr
, nsList
);
325 xmlXPathFreeCompExpr(comp
);
328 ret
= xmlStrcat(ret
, val
);
334 } else if (*cur
== '}') {
336 if (*cur
== '}') { /* escaped '}' */
337 ret
= xmlStrncat(ret
, str
, cur
- str
);
342 xsltTransformError(ctxt
, NULL
, inst
,
343 "xsltAttrTemplateValueProcessNode: unmatched '}'\n");
349 ret
= xmlStrncat(ret
, str
, cur
- str
);
359 * xsltAttrTemplateValueProcess:
360 * @ctxt: the XSLT transformation context
361 * @str: the attribute template node value
363 * Process the given node and return the new string value.
365 * Returns the computed string value or NULL, must be deallocated by the
369 xsltAttrTemplateValueProcess(xsltTransformContextPtr ctxt
, const xmlChar
*str
) {
370 return(xsltAttrTemplateValueProcessNode(ctxt
, str
, NULL
));
374 * xsltEvalAttrValueTemplate:
375 * @ctxt: the XSLT transformation context
376 * @inst: the instruction (or LRE) in the stylesheet holding the
377 * attribute with an AVT
378 * @name: the attribute QName
379 * @ns: the attribute namespace URI
381 * Evaluate a attribute value template, i.e. the attribute value can
382 * contain expressions contained in curly braces ({}) and those are
383 * substituted by they computed value.
385 * Returns the computed string value or NULL, must be deallocated by the
389 xsltEvalAttrValueTemplate(xsltTransformContextPtr ctxt
, xmlNodePtr inst
,
390 const xmlChar
*name
, const xmlChar
*ns
)
395 if ((ctxt
== NULL
) || (inst
== NULL
) || (name
== NULL
) ||
396 (inst
->type
!= XML_ELEMENT_NODE
))
399 expr
= xsltGetNsProp(inst
, name
, ns
);
404 * TODO: though now {} is detected ahead, it would still be good to
405 * optimize both functions to keep the splitted value if the
406 * attribute content and the XPath precompiled expressions around
409 ret
= xsltAttrTemplateValueProcessNode(ctxt
, expr
, inst
);
410 #ifdef WITH_XSLT_DEBUG_TEMPLATES
411 XSLT_TRACE(ctxt
,XSLT_TRACE_TEMPLATES
,xsltGenericDebug(xsltGenericDebugContext
,
412 "xsltEvalAttrValueTemplate: %s returns %s\n", expr
, ret
));
420 * xsltEvalStaticAttrValueTemplate:
421 * @style: the XSLT stylesheet
422 * @inst: the instruction (or LRE) in the stylesheet holding the
423 * attribute with an AVT
424 * @name: the attribute Name
425 * @ns: the attribute namespace URI
426 * @found: indicator whether the attribute is present
428 * Check if an attribute value template has a static value, i.e. the
429 * attribute value does not contain expressions contained in curly braces ({})
431 * Returns the static string value or NULL, must be deallocated by the
435 xsltEvalStaticAttrValueTemplate(xsltStylesheetPtr style
, xmlNodePtr inst
,
436 const xmlChar
*name
, const xmlChar
*ns
, int *found
) {
440 if ((style
== NULL
) || (inst
== NULL
) || (name
== NULL
) ||
441 (inst
->type
!= XML_ELEMENT_NODE
))
444 expr
= xsltGetNsProp(inst
, name
, ns
);
451 ret
= xmlStrchr(expr
, '{');
456 ret
= xmlDictLookup(style
->dict
, expr
, -1);
462 * xsltAttrTemplateProcess:
463 * @ctxt: the XSLT transformation context
464 * @target: the element where the attribute will be grafted
465 * @attr: the attribute node of a literal result element
467 * Process one attribute of a Literal Result Element (in the stylesheet).
468 * Evaluates Attribute Value Templates and copies the attribute over to
469 * the result element.
470 * This does *not* process attribute sets (xsl:use-attribute-set).
473 * Returns the generated attribute node.
476 xsltAttrTemplateProcess(xsltTransformContextPtr ctxt
, xmlNodePtr target
,
479 const xmlChar
*value
;
482 if ((ctxt
== NULL
) || (attr
== NULL
) || (target
== NULL
) ||
483 (target
->type
!= XML_ELEMENT_NODE
))
486 if (attr
->type
!= XML_ATTRIBUTE_NODE
)
490 * Skip all XSLT attributes.
492 #ifdef XSLT_REFACTORED
493 if (attr
->psvi
== xsltXSLTAttrMarker
)
496 if ((attr
->ns
!= NULL
) && xmlStrEqual(attr
->ns
->href
, XSLT_NAMESPACE
))
502 if (attr
->children
!= NULL
) {
503 if ((attr
->children
->type
!= XML_TEXT_NODE
) ||
504 (attr
->children
->next
!= NULL
))
506 xsltTransformError(ctxt
, NULL
, attr
->parent
,
507 "Internal error: The children of an attribute node of a "
508 "literal result element are not in the expected form.\n");
511 value
= attr
->children
->content
;
513 value
= xmlDictLookup(ctxt
->dict
, BAD_CAST
"", 0);
515 value
= xmlDictLookup(ctxt
->dict
, BAD_CAST
"", 0);
517 * Overwrite duplicates.
519 ret
= target
->properties
;
520 while (ret
!= NULL
) {
521 if (((attr
->ns
!= NULL
) == (ret
->ns
!= NULL
)) &&
522 xmlStrEqual(ret
->name
, attr
->name
) &&
523 ((attr
->ns
== NULL
) || xmlStrEqual(ret
->ns
->href
, attr
->ns
->href
)))
530 /* free the existing value */
531 xmlFreeNodeList(ret
->children
);
532 ret
->children
= ret
->last
= NULL
;
534 * Adjust ns-prefix if needed.
536 if ((ret
->ns
!= NULL
) &&
537 (! xmlStrEqual(ret
->ns
->prefix
, attr
->ns
->prefix
)))
539 ret
->ns
= xsltGetNamespace(ctxt
, attr
->parent
, attr
->ns
, target
);
542 /* create a new attribute */
543 if (attr
->ns
!= NULL
)
544 ret
= xmlNewNsProp(target
,
545 xsltGetNamespace(ctxt
, attr
->parent
, attr
->ns
, target
),
548 ret
= xmlNewNsProp(target
, NULL
, attr
->name
, NULL
);
556 text
= xmlNewText(NULL
);
558 ret
->last
= ret
->children
= text
;
559 text
->parent
= (xmlNodePtr
) ret
;
560 text
->doc
= ret
->doc
;
562 if (attr
->psvi
!= NULL
) {
564 * Evaluate the Attribute Value Template.
567 val
= xsltEvalAVT(ctxt
, attr
->psvi
, attr
->parent
);
570 * TODO: Damn, we need an easy mechanism to report
574 xsltTransformError(ctxt
, NULL
, attr
->parent
,
575 "Internal error: Failed to evaluate the AVT "
576 "of attribute '{%s}%s'.\n",
577 attr
->ns
->href
, attr
->name
);
579 xsltTransformError(ctxt
, NULL
, attr
->parent
,
580 "Internal error: Failed to evaluate the AVT "
581 "of attribute '%s'.\n",
584 text
->content
= xmlStrdup(BAD_CAST
"");
588 } else if ((ctxt
->internalized
) && (target
!= NULL
) &&
589 (target
->doc
!= NULL
) &&
590 (target
->doc
->dict
== ctxt
->dict
) &&
591 xmlDictOwns(ctxt
->dict
, value
)) {
592 text
->content
= (xmlChar
*) value
;
594 text
->content
= xmlStrdup(value
);
599 xsltTransformError(ctxt
, NULL
, attr
->parent
,
600 "Internal error: Failed to create attribute '{%s}%s'.\n",
601 attr
->ns
->href
, attr
->name
);
603 xsltTransformError(ctxt
, NULL
, attr
->parent
,
604 "Internal error: Failed to create attribute '%s'.\n",
613 * xsltAttrListTemplateProcess:
614 * @ctxt: the XSLT transformation context
615 * @target: the element where the attributes will be grafted
616 * @attrs: the first attribute
618 * Processes all attributes of a Literal Result Element.
619 * Attribute references are applied via xsl:use-attribute-set
621 * Copies all non XSLT-attributes over to the @target element
622 * and evaluates Attribute Value Templates.
624 * Called by xsltApplySequenceConstructor() (transform.c).
626 * Returns a new list of attribute nodes, or NULL in case of error.
627 * (Don't assign the result to @target->properties; if
628 * the result is NULL, you'll get memory leaks, since the
629 * attributes will be disattached.)
632 xsltAttrListTemplateProcess(xsltTransformContextPtr ctxt
,
633 xmlNodePtr target
, xmlAttrPtr attrs
)
635 xmlAttrPtr attr
, copy
, last
;
636 xmlNodePtr oldInsert
, text
;
637 xmlNsPtr origNs
= NULL
, copyNs
= NULL
;
638 const xmlChar
*value
;
641 if ((ctxt
== NULL
) || (target
== NULL
) || (attrs
== NULL
) ||
642 (target
->type
!= XML_ELEMENT_NODE
))
645 oldInsert
= ctxt
->insert
;
646 ctxt
->insert
= target
;
649 * Instantiate LRE-attributes.
651 if (target
->properties
) {
652 last
= target
->properties
;
653 while (last
->next
!= NULL
)
661 * Skip XSLT attributes.
663 #ifdef XSLT_REFACTORED
664 if (attr
->psvi
== xsltXSLTAttrMarker
) {
668 if ((attr
->ns
!= NULL
) &&
669 xmlStrEqual(attr
->ns
->href
, XSLT_NAMESPACE
))
677 if (attr
->children
!= NULL
) {
678 if ((attr
->children
->type
!= XML_TEXT_NODE
) ||
679 (attr
->children
->next
!= NULL
))
681 xsltTransformError(ctxt
, NULL
, attr
->parent
,
682 "Internal error: The children of an attribute node of a "
683 "literal result element are not in the expected form.\n");
686 value
= attr
->children
->content
;
688 value
= xmlDictLookup(ctxt
->dict
, BAD_CAST
"", 0);
690 value
= xmlDictLookup(ctxt
->dict
, BAD_CAST
"", 0);
693 * Create a new attribute.
695 copy
= xmlNewDocProp(target
->doc
, attr
->name
, NULL
);
698 xsltTransformError(ctxt
, NULL
, attr
->parent
,
699 "Internal error: Failed to create attribute '{%s}%s'.\n",
700 attr
->ns
->href
, attr
->name
);
702 xsltTransformError(ctxt
, NULL
, attr
->parent
,
703 "Internal error: Failed to create attribute '%s'.\n",
709 * Attach it to the target element.
711 copy
->parent
= target
;
713 target
->properties
= copy
;
721 * Set the namespace. Avoid lookups of same namespaces.
723 if (attr
->ns
!= origNs
) {
725 if (attr
->ns
!= NULL
) {
726 #ifdef XSLT_REFACTORED
727 copyNs
= xsltGetSpecialNamespace(ctxt
, attr
->parent
,
728 attr
->ns
->href
, attr
->ns
->prefix
, target
);
730 copyNs
= xsltGetNamespace(ctxt
, attr
->parent
,
743 text
= xmlNewText(NULL
);
745 copy
->last
= copy
->children
= text
;
746 text
->parent
= (xmlNodePtr
) copy
;
747 text
->doc
= copy
->doc
;
749 if (attr
->psvi
!= NULL
) {
751 * Evaluate the Attribute Value Template.
753 valueAVT
= xsltEvalAVT(ctxt
, attr
->psvi
, attr
->parent
);
754 if (valueAVT
== NULL
) {
756 * TODO: Damn, we need an easy mechanism to report
760 xsltTransformError(ctxt
, NULL
, attr
->parent
,
761 "Internal error: Failed to evaluate the AVT "
762 "of attribute '{%s}%s'.\n",
763 attr
->ns
->href
, attr
->name
);
765 xsltTransformError(ctxt
, NULL
, attr
->parent
,
766 "Internal error: Failed to evaluate the AVT "
767 "of attribute '%s'.\n",
770 text
->content
= xmlStrdup(BAD_CAST
"");
773 text
->content
= valueAVT
;
775 } else if ((ctxt
->internalized
) &&
776 (target
->doc
!= NULL
) &&
777 (target
->doc
->dict
== ctxt
->dict
) &&
778 xmlDictOwns(ctxt
->dict
, value
))
780 text
->content
= (xmlChar
*) value
;
782 text
->content
= xmlStrdup(value
);
784 if ((copy
!= NULL
) && (text
!= NULL
) &&
785 (xmlIsID(copy
->doc
, copy
->parent
, copy
)))
786 xmlAddID(NULL
, copy
->doc
, text
->content
, copy
);
791 } while (attr
!= NULL
);
794 * Apply attribute-sets.
795 * The creation of such attributes will not overwrite any existing
800 #ifdef XSLT_REFACTORED
801 if ((attr
->psvi
== xsltXSLTAttrMarker
) &&
802 xmlStrEqual(attr
->name
, (const xmlChar
*)"use-attribute-sets"))
804 xsltApplyAttributeSet(ctxt
, ctxt
->node
, (xmlNodePtr
) attr
, NULL
);
807 if ((attr
->ns
!= NULL
) &&
808 xmlStrEqual(attr
->name
, (const xmlChar
*)"use-attribute-sets") &&
809 xmlStrEqual(attr
->ns
->href
, XSLT_NAMESPACE
))
811 xsltApplyAttributeSet(ctxt
, ctxt
->node
, (xmlNodePtr
) attr
, NULL
);
815 } while (attr
!= NULL
);
817 ctxt
->insert
= oldInsert
;
818 return(target
->properties
);
821 ctxt
->insert
= oldInsert
;
827 * xsltTemplateProcess:
828 * @ctxt: the XSLT transformation context
829 * @node: the attribute template node
831 * Obsolete. Don't use it.
836 xsltTemplateProcess(xsltTransformContextPtr ctxt ATTRIBUTE_UNUSED
, xmlNodePtr node
) {