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.
14 #include <libxml/globals.h>
16 #ifdef WITH_XSLT_DEBUG
17 #define WITH_XSLT_DEBUG_TEMPLATES
20 /************************************************************************
24 ************************************************************************/
27 * xsltEvalXPathPredicate:
28 * @ctxt: the XSLT transformation context
29 * @comp: the XPath compiled expression
30 * @nsList: the namespaces in scope
31 * @nsNr: the number of namespaces in scope
33 * Process the expression using XPath and evaluate the result as
36 * Returns 1 is the predicate was true, 0 otherwise
39 xsltEvalXPathPredicate(xsltTransformContextPtr ctxt
, xmlXPathCompExprPtr comp
,
40 xmlNsPtr
*nsList
, int nsNr
) {
42 xmlXPathObjectPtr res
;
44 xmlNsPtr
*oldNamespaces
;
46 int oldProximityPosition
, oldContextSize
;
48 oldContextSize
= ctxt
->xpathCtxt
->contextSize
;
49 oldProximityPosition
= ctxt
->xpathCtxt
->proximityPosition
;
50 oldNsNr
= ctxt
->xpathCtxt
->nsNr
;
51 oldNamespaces
= ctxt
->xpathCtxt
->namespaces
;
54 ctxt
->xpathCtxt
->node
= ctxt
->node
;
55 ctxt
->xpathCtxt
->namespaces
= nsList
;
56 ctxt
->xpathCtxt
->nsNr
= nsNr
;
58 res
= xmlXPathCompiledEval(comp
, ctxt
->xpathCtxt
);
61 ret
= xmlXPathEvalPredicate(ctxt
->xpathCtxt
, res
);
62 xmlXPathFreeObject(res
);
63 #ifdef WITH_XSLT_DEBUG_TEMPLATES
64 XSLT_TRACE(ctxt
,XSLT_TRACE_TEMPLATES
,xsltGenericDebug(xsltGenericDebugContext
,
65 "xsltEvalXPathPredicate: returns %d\n", ret
));
68 #ifdef WITH_XSLT_DEBUG_TEMPLATES
69 XSLT_TRACE(ctxt
,XSLT_TRACE_TEMPLATES
,xsltGenericDebug(xsltGenericDebugContext
,
70 "xsltEvalXPathPredicate: failed\n"));
72 ctxt
->state
= XSLT_STATE_STOPPED
;
75 ctxt
->xpathCtxt
->nsNr
= oldNsNr
;
77 ctxt
->xpathCtxt
->namespaces
= oldNamespaces
;
79 ctxt
->xpathCtxt
->contextSize
= oldContextSize
;
80 ctxt
->xpathCtxt
->proximityPosition
= oldProximityPosition
;
86 * xsltEvalXPathStringNs:
87 * @ctxt: the XSLT transformation context
88 * @comp: the compiled XPath expression
89 * @nsNr: the number of namespaces in the list
90 * @nsList: the list of in-scope namespaces to use
92 * Process the expression using XPath, allowing to pass a namespace mapping
93 * context and get a string
95 * Returns the computed string value or NULL, must be deallocated by the
99 xsltEvalXPathStringNs(xsltTransformContextPtr ctxt
, xmlXPathCompExprPtr comp
,
100 int nsNr
, xmlNsPtr
*nsList
) {
102 xmlXPathObjectPtr res
;
107 xmlNsPtr
*oldNamespaces
;
109 oldInst
= ctxt
->inst
;
110 oldNode
= ctxt
->node
;
111 oldPos
= ctxt
->xpathCtxt
->proximityPosition
;
112 oldSize
= ctxt
->xpathCtxt
->contextSize
;
113 oldNsNr
= ctxt
->xpathCtxt
->nsNr
;
114 oldNamespaces
= ctxt
->xpathCtxt
->namespaces
;
116 ctxt
->xpathCtxt
->node
= ctxt
->node
;
117 /* TODO: do we need to propagate the namespaces here ? */
118 ctxt
->xpathCtxt
->namespaces
= nsList
;
119 ctxt
->xpathCtxt
->nsNr
= nsNr
;
120 res
= xmlXPathCompiledEval(comp
, ctxt
->xpathCtxt
);
122 if (res
->type
!= XPATH_STRING
)
123 res
= xmlXPathConvertString(res
);
124 if (res
->type
== XPATH_STRING
) {
125 ret
= res
->stringval
;
126 res
->stringval
= NULL
;
128 xsltTransformError(ctxt
, NULL
, NULL
,
129 "xpath : string() function didn't return a String\n");
131 xmlXPathFreeObject(res
);
133 ctxt
->state
= XSLT_STATE_STOPPED
;
135 #ifdef WITH_XSLT_DEBUG_TEMPLATES
136 XSLT_TRACE(ctxt
,XSLT_TRACE_TEMPLATES
,xsltGenericDebug(xsltGenericDebugContext
,
137 "xsltEvalXPathString: returns %s\n", ret
));
139 ctxt
->inst
= oldInst
;
140 ctxt
->node
= oldNode
;
141 ctxt
->xpathCtxt
->contextSize
= oldSize
;
142 ctxt
->xpathCtxt
->proximityPosition
= oldPos
;
143 ctxt
->xpathCtxt
->nsNr
= oldNsNr
;
144 ctxt
->xpathCtxt
->namespaces
= oldNamespaces
;
149 * xsltEvalXPathString:
150 * @ctxt: the XSLT transformation context
151 * @comp: the compiled XPath expression
153 * Process the expression using XPath and get a string
155 * Returns the computed string value or NULL, must be deallocated by the
159 xsltEvalXPathString(xsltTransformContextPtr ctxt
, xmlXPathCompExprPtr comp
) {
160 return(xsltEvalXPathStringNs(ctxt
, comp
, 0, NULL
));
164 * xsltEvalTemplateString:
165 * @ctxt: the XSLT transformation context
166 * @contextNode: the current node in the source tree
167 * @inst: the XSLT instruction (xsl:comment, xsl:processing-instruction)
169 * Processes the sequence constructor of the given instruction on
170 * @contextNode and converts the resulting tree to a string.
171 * This is needed by e.g. xsl:comment and xsl:processing-instruction.
173 * Returns the computed string value or NULL; it's up to the caller to
177 xsltEvalTemplateString(xsltTransformContextPtr ctxt
,
178 xmlNodePtr contextNode
,
181 xmlNodePtr oldInsert
, insert
= NULL
;
184 if ((ctxt
== NULL
) || (contextNode
== NULL
) || (inst
== NULL
) ||
185 (inst
->type
!= XML_ELEMENT_NODE
))
188 if (inst
->children
== NULL
)
192 * This creates a temporary element-node to add the resulting
194 * OPTIMIZE TODO: Keep such an element-node in the transformation
195 * context to avoid creating it every time.
197 insert
= xmlNewDocNode(ctxt
->output
, NULL
,
198 (const xmlChar
*)"fake", NULL
);
199 if (insert
== NULL
) {
200 xsltTransformError(ctxt
, NULL
, contextNode
,
201 "Failed to create temporary node\n");
204 oldInsert
= ctxt
->insert
;
205 ctxt
->insert
= insert
;
207 * OPTIMIZE TODO: if inst->children consists only of text-nodes.
209 xsltApplyOneTemplate(ctxt
, contextNode
, inst
->children
, NULL
, NULL
);
211 ctxt
->insert
= oldInsert
;
213 ret
= xmlNodeGetContent(insert
);
220 * xsltAttrTemplateValueProcessNode:
221 * @ctxt: the XSLT transformation context
222 * @str: the attribute template node value
223 * @inst: the instruction (or LRE) in the stylesheet holding the
224 * attribute with an AVT
226 * Process the given string, allowing to pass a namespace mapping
227 * context and return the new string value.
230 * - xsltAttrTemplateValueProcess() (templates.c)
231 * - xsltEvalAttrValueTemplate() (templates.c)
233 * QUESTION: Why is this function public? It is not used outside
236 * Returns the computed string value or NULL, must be deallocated by the
240 xsltAttrTemplateValueProcessNode(xsltTransformContextPtr ctxt
,
241 const xmlChar
*str
, xmlNodePtr inst
)
246 xmlNsPtr
*nsList
= NULL
;
249 if (str
== NULL
) return(NULL
);
251 return(xmlStrndup((xmlChar
*)"", 0));
256 if (*(cur
+1) == '{') { /* escaped '{' */
258 ret
= xmlStrncat(ret
, str
, cur
- str
);
263 ret
= xmlStrncat(ret
, str
, cur
- str
);
266 while ((*cur
!= 0) && (*cur
!= '}')) {
267 /* Need to check for literal (bug539741) */
268 if ((*cur
== '\'') || (*cur
== '"')) {
269 char delim
= *(cur
++);
270 while ((*cur
!= 0) && (*cur
!= delim
))
273 cur
++; /* skip the ending delimiter */
278 xsltTransformError(ctxt
, NULL
, inst
,
279 "xsltAttrTemplateValueProcessNode: unmatched '{'\n");
280 ret
= xmlStrncat(ret
, str
, cur
- str
);
284 expr
= xmlStrndup(str
, cur
- str
);
287 else if (*expr
== '{') {
288 ret
= xmlStrcat(ret
, expr
);
291 xmlXPathCompExprPtr comp
;
293 * TODO: keep precompiled form around
295 if ((nsList
== NULL
) && (inst
!= NULL
)) {
298 nsList
= xmlGetNsList(inst
->doc
, inst
);
299 if (nsList
!= NULL
) {
300 while (nsList
[i
] != NULL
)
305 comp
= xmlXPathCompile(expr
);
306 val
= xsltEvalXPathStringNs(ctxt
, comp
, nsNr
, nsList
);
307 xmlXPathFreeCompExpr(comp
);
310 ret
= xmlStrcat(ret
, val
);
316 } else if (*cur
== '}') {
318 if (*cur
== '}') { /* escaped '}' */
319 ret
= xmlStrncat(ret
, str
, cur
- str
);
324 xsltTransformError(ctxt
, NULL
, inst
,
325 "xsltAttrTemplateValueProcessNode: unmatched '}'\n");
331 ret
= xmlStrncat(ret
, str
, cur
- str
);
341 * xsltAttrTemplateValueProcess:
342 * @ctxt: the XSLT transformation context
343 * @str: the attribute template node value
345 * Process the given node and return the new string value.
347 * Returns the computed string value or NULL, must be deallocated by the
351 xsltAttrTemplateValueProcess(xsltTransformContextPtr ctxt
, const xmlChar
*str
) {
352 return(xsltAttrTemplateValueProcessNode(ctxt
, str
, NULL
));
356 * xsltEvalAttrValueTemplate:
357 * @ctxt: the XSLT transformation context
358 * @inst: the instruction (or LRE) in the stylesheet holding the
359 * attribute with an AVT
360 * @name: the attribute QName
361 * @ns: the attribute namespace URI
363 * Evaluate a attribute value template, i.e. the attribute value can
364 * contain expressions contained in curly braces ({}) and those are
365 * substituted by they computed value.
367 * Returns the computed string value or NULL, must be deallocated by the
371 xsltEvalAttrValueTemplate(xsltTransformContextPtr ctxt
, xmlNodePtr inst
,
372 const xmlChar
*name
, const xmlChar
*ns
)
377 if ((ctxt
== NULL
) || (inst
== NULL
) || (name
== NULL
) ||
378 (inst
->type
!= XML_ELEMENT_NODE
))
381 expr
= xsltGetNsProp(inst
, name
, ns
);
386 * TODO: though now {} is detected ahead, it would still be good to
387 * optimize both functions to keep the splitted value if the
388 * attribute content and the XPath precompiled expressions around
391 ret
= xsltAttrTemplateValueProcessNode(ctxt
, expr
, inst
);
392 #ifdef WITH_XSLT_DEBUG_TEMPLATES
393 XSLT_TRACE(ctxt
,XSLT_TRACE_TEMPLATES
,xsltGenericDebug(xsltGenericDebugContext
,
394 "xsltEvalAttrValueTemplate: %s returns %s\n", expr
, ret
));
402 * xsltEvalStaticAttrValueTemplate:
403 * @style: the XSLT stylesheet
404 * @inst: the instruction (or LRE) in the stylesheet holding the
405 * attribute with an AVT
406 * @name: the attribute Name
407 * @ns: the attribute namespace URI
408 * @found: indicator whether the attribute is present
410 * Check if an attribute value template has a static value, i.e. the
411 * attribute value does not contain expressions contained in curly braces ({})
413 * Returns the static string value or NULL, must be deallocated by the
417 xsltEvalStaticAttrValueTemplate(xsltStylesheetPtr style
, xmlNodePtr inst
,
418 const xmlChar
*name
, const xmlChar
*ns
, int *found
) {
422 if ((style
== NULL
) || (inst
== NULL
) || (name
== NULL
) ||
423 (inst
->type
!= XML_ELEMENT_NODE
))
426 expr
= xsltGetNsProp(inst
, name
, ns
);
433 ret
= xmlStrchr(expr
, '{');
438 ret
= xmlDictLookup(style
->dict
, expr
, -1);
444 * xsltAttrTemplateProcess:
445 * @ctxt: the XSLT transformation context
446 * @target: the element where the attribute will be grafted
447 * @attr: the attribute node of a literal result element
449 * Process one attribute of a Literal Result Element (in the stylesheet).
450 * Evaluates Attribute Value Templates and copies the attribute over to
451 * the result element.
452 * This does *not* process attribute sets (xsl:use-attribute-set).
455 * Returns the generated attribute node.
458 xsltAttrTemplateProcess(xsltTransformContextPtr ctxt
, xmlNodePtr target
,
461 const xmlChar
*value
;
464 if ((ctxt
== NULL
) || (attr
== NULL
) || (target
== NULL
) ||
465 (target
->type
!= XML_ELEMENT_NODE
))
468 if (attr
->type
!= XML_ATTRIBUTE_NODE
)
472 * Skip all XSLT attributes.
474 #ifdef XSLT_REFACTORED
475 if (attr
->psvi
== xsltXSLTAttrMarker
)
478 if ((attr
->ns
!= NULL
) && xmlStrEqual(attr
->ns
->href
, XSLT_NAMESPACE
))
484 if (attr
->children
!= NULL
) {
485 if ((attr
->children
->type
!= XML_TEXT_NODE
) ||
486 (attr
->children
->next
!= NULL
))
488 xsltTransformError(ctxt
, NULL
, attr
->parent
,
489 "Internal error: The children of an attribute node of a "
490 "literal result element are not in the expected form.\n");
493 value
= attr
->children
->content
;
495 value
= xmlDictLookup(ctxt
->dict
, BAD_CAST
"", 0);
497 value
= xmlDictLookup(ctxt
->dict
, BAD_CAST
"", 0);
499 * Overwrite duplicates.
501 ret
= target
->properties
;
502 while (ret
!= NULL
) {
503 if (((attr
->ns
!= NULL
) == (ret
->ns
!= NULL
)) &&
504 xmlStrEqual(ret
->name
, attr
->name
) &&
505 ((attr
->ns
== NULL
) || xmlStrEqual(ret
->ns
->href
, attr
->ns
->href
)))
512 /* free the existing value */
513 xmlFreeNodeList(ret
->children
);
514 ret
->children
= ret
->last
= NULL
;
516 * Adjust ns-prefix if needed.
518 if ((ret
->ns
!= NULL
) &&
519 (! xmlStrEqual(ret
->ns
->prefix
, attr
->ns
->prefix
)))
521 ret
->ns
= xsltGetNamespace(ctxt
, attr
->parent
, attr
->ns
, target
);
524 /* create a new attribute */
525 if (attr
->ns
!= NULL
)
526 ret
= xmlNewNsProp(target
,
527 xsltGetNamespace(ctxt
, attr
->parent
, attr
->ns
, target
),
530 ret
= xmlNewNsProp(target
, NULL
, attr
->name
, NULL
);
538 text
= xmlNewText(NULL
);
540 ret
->last
= ret
->children
= text
;
541 text
->parent
= (xmlNodePtr
) ret
;
542 text
->doc
= ret
->doc
;
544 if (attr
->psvi
!= NULL
) {
546 * Evaluate the Attribute Value Template.
549 val
= xsltEvalAVT(ctxt
, attr
->psvi
, attr
->parent
);
552 * TODO: Damn, we need an easy mechanism to report
556 xsltTransformError(ctxt
, NULL
, attr
->parent
,
557 "Internal error: Failed to evaluate the AVT "
558 "of attribute '{%s}%s'.\n",
559 attr
->ns
->href
, attr
->name
);
561 xsltTransformError(ctxt
, NULL
, attr
->parent
,
562 "Internal error: Failed to evaluate the AVT "
563 "of attribute '%s'.\n",
566 text
->content
= xmlStrdup(BAD_CAST
"");
570 } else if ((ctxt
->internalized
) && (target
!= NULL
) &&
571 (target
->doc
!= NULL
) &&
572 (target
->doc
->dict
== ctxt
->dict
) &&
573 xmlDictOwns(ctxt
->dict
, value
)) {
574 text
->content
= (xmlChar
*) value
;
576 text
->content
= xmlStrdup(value
);
581 xsltTransformError(ctxt
, NULL
, attr
->parent
,
582 "Internal error: Failed to create attribute '{%s}%s'.\n",
583 attr
->ns
->href
, attr
->name
);
585 xsltTransformError(ctxt
, NULL
, attr
->parent
,
586 "Internal error: Failed to create attribute '%s'.\n",
595 * xsltAttrListTemplateProcess:
596 * @ctxt: the XSLT transformation context
597 * @target: the element where the attributes will be grafted
598 * @attrs: the first attribute
600 * Processes all attributes of a Literal Result Element.
601 * Attribute references are applied via xsl:use-attribute-set
603 * Copies all non XSLT-attributes over to the @target element
604 * and evaluates Attribute Value Templates.
606 * Called by xsltApplySequenceConstructor() (transform.c).
608 * Returns a new list of attribute nodes, or NULL in case of error.
609 * (Don't assign the result to @target->properties; if
610 * the result is NULL, you'll get memory leaks, since the
611 * attributes will be disattached.)
614 xsltAttrListTemplateProcess(xsltTransformContextPtr ctxt
,
615 xmlNodePtr target
, xmlAttrPtr attrs
)
617 xmlAttrPtr attr
, copy
, last
;
618 xmlNodePtr oldInsert
, text
;
619 xmlNsPtr origNs
= NULL
, copyNs
= NULL
;
620 const xmlChar
*value
;
623 if ((ctxt
== NULL
) || (target
== NULL
) || (attrs
== NULL
) ||
624 (target
->type
!= XML_ELEMENT_NODE
))
627 oldInsert
= ctxt
->insert
;
628 ctxt
->insert
= target
;
631 * Instantiate LRE-attributes.
633 if (target
->properties
) {
634 last
= target
->properties
;
635 while (last
->next
!= NULL
)
643 * Skip XSLT attributes.
645 #ifdef XSLT_REFACTORED
646 if (attr
->psvi
== xsltXSLTAttrMarker
) {
650 if ((attr
->ns
!= NULL
) &&
651 xmlStrEqual(attr
->ns
->href
, XSLT_NAMESPACE
))
659 if (attr
->children
!= NULL
) {
660 if ((attr
->children
->type
!= XML_TEXT_NODE
) ||
661 (attr
->children
->next
!= NULL
))
663 xsltTransformError(ctxt
, NULL
, attr
->parent
,
664 "Internal error: The children of an attribute node of a "
665 "literal result element are not in the expected form.\n");
668 value
= attr
->children
->content
;
670 value
= xmlDictLookup(ctxt
->dict
, BAD_CAST
"", 0);
672 value
= xmlDictLookup(ctxt
->dict
, BAD_CAST
"", 0);
675 * Create a new attribute.
677 copy
= xmlNewDocProp(target
->doc
, attr
->name
, NULL
);
680 xsltTransformError(ctxt
, NULL
, attr
->parent
,
681 "Internal error: Failed to create attribute '{%s}%s'.\n",
682 attr
->ns
->href
, attr
->name
);
684 xsltTransformError(ctxt
, NULL
, attr
->parent
,
685 "Internal error: Failed to create attribute '%s'.\n",
691 * Attach it to the target element.
693 copy
->parent
= target
;
695 target
->properties
= copy
;
703 * Set the namespace. Avoid lookups of same namespaces.
705 if (attr
->ns
!= origNs
) {
707 if (attr
->ns
!= NULL
) {
708 #ifdef XSLT_REFACTORED
709 copyNs
= xsltGetSpecialNamespace(ctxt
, attr
->parent
,
710 attr
->ns
->href
, attr
->ns
->prefix
, target
);
712 copyNs
= xsltGetNamespace(ctxt
, attr
->parent
,
725 text
= xmlNewText(NULL
);
727 copy
->last
= copy
->children
= text
;
728 text
->parent
= (xmlNodePtr
) copy
;
729 text
->doc
= copy
->doc
;
731 if (attr
->psvi
!= NULL
) {
733 * Evaluate the Attribute Value Template.
735 valueAVT
= xsltEvalAVT(ctxt
, attr
->psvi
, attr
->parent
);
736 if (valueAVT
== NULL
) {
738 * TODO: Damn, we need an easy mechanism to report
742 xsltTransformError(ctxt
, NULL
, attr
->parent
,
743 "Internal error: Failed to evaluate the AVT "
744 "of attribute '{%s}%s'.\n",
745 attr
->ns
->href
, attr
->name
);
747 xsltTransformError(ctxt
, NULL
, attr
->parent
,
748 "Internal error: Failed to evaluate the AVT "
749 "of attribute '%s'.\n",
752 text
->content
= xmlStrdup(BAD_CAST
"");
755 text
->content
= valueAVT
;
757 } else if ((ctxt
->internalized
) &&
758 (target
->doc
!= NULL
) &&
759 (target
->doc
->dict
== ctxt
->dict
) &&
760 xmlDictOwns(ctxt
->dict
, value
))
762 text
->content
= (xmlChar
*) value
;
764 text
->content
= xmlStrdup(value
);
766 if ((copy
!= NULL
) && (text
!= NULL
) &&
767 (xmlIsID(copy
->doc
, copy
->parent
, copy
)))
768 xmlAddID(NULL
, copy
->doc
, text
->content
, copy
);
773 } while (attr
!= NULL
);
776 * Apply attribute-sets.
777 * The creation of such attributes will not overwrite any existing
782 #ifdef XSLT_REFACTORED
783 if ((attr
->psvi
== xsltXSLTAttrMarker
) &&
784 xmlStrEqual(attr
->name
, (const xmlChar
*)"use-attribute-sets"))
786 xsltApplyAttributeSet(ctxt
, ctxt
->node
, (xmlNodePtr
) attr
, NULL
);
789 if ((attr
->ns
!= NULL
) &&
790 xmlStrEqual(attr
->name
, (const xmlChar
*)"use-attribute-sets") &&
791 xmlStrEqual(attr
->ns
->href
, XSLT_NAMESPACE
))
793 xsltApplyAttributeSet(ctxt
, ctxt
->node
, (xmlNodePtr
) attr
, NULL
);
797 } while (attr
!= NULL
);
799 ctxt
->insert
= oldInsert
;
800 return(target
->properties
);
803 ctxt
->insert
= oldInsert
;
809 * xsltTemplateProcess:
810 * @ctxt: the XSLT transformation context
811 * @node: the attribute template node
813 * Obsolete. Don't use it.
818 xsltTemplateProcess(xsltTransformContextPtr ctxt ATTRIBUTE_UNUSED
, xmlNodePtr node
) {