1/*
2 * templates.c: Implementation of the template processing
3 *
4 * Reference:
5 *   http://www.w3.org/TR/1999/REC-xslt-19991116
6 *
7 * See Copyright for the status of this software.
8 *
9 * daniel@veillard.com
10 */
11
12#define IN_LIBXSLT
13#include "libxslt.h"
14
15#include <string.h>
16
17#include <libxml/xmlmemory.h>
18#include <libxml/globals.h>
19#include <libxml/xmlerror.h>
20#include <libxml/tree.h>
21#include <libxml/xpathInternals.h>
22#include <libxml/parserInternals.h>
23#include "xslt.h"
24#include "xsltInternals.h"
25#include "xsltutils.h"
26#include "variables.h"
27#include "functions.h"
28#include "templates.h"
29#include "transform.h"
30#include "namespaces.h"
31#include "attributes.h"
32
33#ifdef WITH_XSLT_DEBUG
34#define WITH_XSLT_DEBUG_TEMPLATES
35#endif
36
37/************************************************************************
38 *									*
39 *			Module interfaces				*
40 *									*
41 ************************************************************************/
42
43/**
44 * xsltEvalXPathPredicate:
45 * @ctxt:  the XSLT transformation context
46 * @comp:  the XPath compiled expression
47 * @nsList:  the namespaces in scope
48 * @nsNr:  the number of namespaces in scope
49 *
50 * Process the expression using XPath and evaluate the result as
51 * an XPath predicate
52 *
53 * Returns 1 is the predicate was true, 0 otherwise
54 */
55int
56xsltEvalXPathPredicate(xsltTransformContextPtr ctxt, xmlXPathCompExprPtr comp,
57		       xmlNsPtr *nsList, int nsNr) {
58    int ret;
59    xmlXPathObjectPtr res;
60    int oldNsNr;
61    xmlNsPtr *oldNamespaces;
62    xmlNodePtr oldInst;
63    int oldProximityPosition, oldContextSize;
64
65    oldContextSize = ctxt->xpathCtxt->contextSize;
66    oldProximityPosition = ctxt->xpathCtxt->proximityPosition;
67    oldNsNr = ctxt->xpathCtxt->nsNr;
68    oldNamespaces = ctxt->xpathCtxt->namespaces;
69    oldInst = ctxt->inst;
70
71    ctxt->xpathCtxt->node = ctxt->node;
72    ctxt->xpathCtxt->namespaces = nsList;
73    ctxt->xpathCtxt->nsNr = nsNr;
74
75    res = xmlXPathCompiledEval(comp, ctxt->xpathCtxt);
76
77    if (res != NULL) {
78	ret = xmlXPathEvalPredicate(ctxt->xpathCtxt, res);
79	xmlXPathFreeObject(res);
80#ifdef WITH_XSLT_DEBUG_TEMPLATES
81	XSLT_TRACE(ctxt,XSLT_TRACE_TEMPLATES,xsltGenericDebug(xsltGenericDebugContext,
82	     "xsltEvalXPathPredicate: returns %d\n", ret));
83#endif
84    } else {
85#ifdef WITH_XSLT_DEBUG_TEMPLATES
86	XSLT_TRACE(ctxt,XSLT_TRACE_TEMPLATES,xsltGenericDebug(xsltGenericDebugContext,
87	     "xsltEvalXPathPredicate: failed\n"));
88#endif
89	ctxt->state = XSLT_STATE_STOPPED;
90	ret = 0;
91    }
92    ctxt->xpathCtxt->nsNr = oldNsNr;
93
94    ctxt->xpathCtxt->namespaces = oldNamespaces;
95    ctxt->inst = oldInst;
96    ctxt->xpathCtxt->contextSize = oldContextSize;
97    ctxt->xpathCtxt->proximityPosition = oldProximityPosition;
98
99    return(ret);
100}
101
102/**
103 * xsltEvalXPathStringNs:
104 * @ctxt:  the XSLT transformation context
105 * @comp:  the compiled XPath expression
106 * @nsNr:  the number of namespaces in the list
107 * @nsList:  the list of in-scope namespaces to use
108 *
109 * Process the expression using XPath, allowing to pass a namespace mapping
110 * context and get a string
111 *
112 * Returns the computed string value or NULL, must be deallocated by the
113 *    caller.
114 */
115xmlChar *
116xsltEvalXPathStringNs(xsltTransformContextPtr ctxt, xmlXPathCompExprPtr comp,
117	              int nsNr, xmlNsPtr *nsList) {
118    xmlChar *ret = NULL;
119    xmlXPathObjectPtr res;
120    xmlNodePtr oldInst;
121    xmlNodePtr oldNode;
122    int	oldPos, oldSize;
123    int oldNsNr;
124    xmlNsPtr *oldNamespaces;
125
126    oldInst = ctxt->inst;
127    oldNode = ctxt->node;
128    oldPos = ctxt->xpathCtxt->proximityPosition;
129    oldSize = ctxt->xpathCtxt->contextSize;
130    oldNsNr = ctxt->xpathCtxt->nsNr;
131    oldNamespaces = ctxt->xpathCtxt->namespaces;
132
133    ctxt->xpathCtxt->node = ctxt->node;
134    /* TODO: do we need to propagate the namespaces here ? */
135    ctxt->xpathCtxt->namespaces = nsList;
136    ctxt->xpathCtxt->nsNr = nsNr;
137    res = xmlXPathCompiledEval(comp, ctxt->xpathCtxt);
138    if (res != NULL) {
139	if (res->type != XPATH_STRING)
140	    res = xmlXPathConvertString(res);
141	if (res->type == XPATH_STRING) {
142            ret = res->stringval;
143	    res->stringval = NULL;
144	} else {
145	    xsltTransformError(ctxt, NULL, NULL,
146		 "xpath : string() function didn't return a String\n");
147	}
148	xmlXPathFreeObject(res);
149    } else {
150	ctxt->state = XSLT_STATE_STOPPED;
151    }
152#ifdef WITH_XSLT_DEBUG_TEMPLATES
153    XSLT_TRACE(ctxt,XSLT_TRACE_TEMPLATES,xsltGenericDebug(xsltGenericDebugContext,
154	 "xsltEvalXPathString: returns %s\n", ret));
155#endif
156    ctxt->inst = oldInst;
157    ctxt->node = oldNode;
158    ctxt->xpathCtxt->contextSize = oldSize;
159    ctxt->xpathCtxt->proximityPosition = oldPos;
160    ctxt->xpathCtxt->nsNr = oldNsNr;
161    ctxt->xpathCtxt->namespaces = oldNamespaces;
162    return(ret);
163}
164
165/**
166 * xsltEvalXPathString:
167 * @ctxt:  the XSLT transformation context
168 * @comp:  the compiled XPath expression
169 *
170 * Process the expression using XPath and get a string
171 *
172 * Returns the computed string value or NULL, must be deallocated by the
173 *    caller.
174 */
175xmlChar *
176xsltEvalXPathString(xsltTransformContextPtr ctxt, xmlXPathCompExprPtr comp) {
177    return(xsltEvalXPathStringNs(ctxt, comp, 0, NULL));
178}
179
180/**
181 * xsltEvalTemplateString:
182 * @ctxt:  the XSLT transformation context
183 * @node:  the stylesheet node
184 * @parent:  the content parent
185 *
186 * Evaluate a template string value, i.e. the parent list is interpreter
187 * as template content and the resulting tree string value is returned
188 * This is needed for example by xsl:comment and xsl:processing-instruction
189 *
190 * Returns the computed string value or NULL, must be deallocated by the
191 *    caller.
192 */
193xmlChar *
194xsltEvalTemplateString(xsltTransformContextPtr ctxt, xmlNodePtr node,
195	               xmlNodePtr parent) {
196    xmlNodePtr oldInsert, insert = NULL;
197    xmlChar *ret;
198
199    if ((ctxt == NULL) || (node == NULL) || (parent == NULL))
200	return(NULL);
201
202    if (parent->children == NULL)
203	return(NULL);
204
205    /*
206    * This creates a temporary element-node to add the resulting
207    * text content to.
208    * OPTIMIZE TODO: Keep such an element-node in the transformation
209    *  context to avoid creating it every time.
210    */
211    insert = xmlNewDocNode(ctxt->output, NULL,
212	                   (const xmlChar *)"fake", NULL);
213    if (insert == NULL) {
214	xsltTransformError(ctxt, NULL, node,
215		"Failed to create temporary node\n");
216	return(NULL);
217    }
218    oldInsert = ctxt->insert;
219    ctxt->insert = insert;
220    /* OPTIMIZE TODO: if parent->children consists only of text-nodes. */
221    xsltApplyOneTemplate(ctxt, node, parent->children, NULL, NULL);
222
223    ctxt->insert = oldInsert;
224
225    ret = xmlNodeGetContent(insert);
226    if (insert != NULL)
227	xmlFreeNode(insert);
228    return(ret);
229}
230
231/**
232 * xsltAttrTemplateValueProcessNode:
233 * @ctxt:  the XSLT transformation context
234 * @str:  the attribute template node value
235 * @node:  the node hosting the attribute
236 *
237 * Process the given string, allowing to pass a namespace mapping
238 * context and return the new string value.
239 *
240 * Returns the computed string value or NULL, must be deallocated by the
241 *    caller.
242 */
243xmlChar *
244xsltAttrTemplateValueProcessNode(xsltTransformContextPtr ctxt,
245	  const xmlChar *str, xmlNodePtr node) {
246    xmlChar *ret = NULL;
247    const xmlChar *cur;
248    xmlChar *expr, *val;
249    xmlNsPtr *nsList = NULL;
250    int nsNr = 0;
251
252    if (str == NULL) return(NULL);
253    if (*str == 0)
254	return(xmlStrndup((xmlChar *)"", 0));
255
256    cur = str;
257    while (*cur != 0) {
258	if (*cur == '{') {
259	    if (*(cur+1) == '{') {	/* escaped '{' */
260	        cur++;
261		ret = xmlStrncat(ret, str, cur - str);
262		cur++;
263		str = cur;
264		continue;
265	    }
266	    ret = xmlStrncat(ret, str, cur - str);
267	    str = cur;
268	    cur++;
269	    while ((*cur != 0) && (*cur != '}')) cur++;
270	    if (*cur == 0) {
271	        xsltTransformError(ctxt, NULL, NULL,
272			"xsltAttrTemplateValueProcessNode: unmatched '{'\n");
273		ret = xmlStrncat(ret, str, cur - str);
274		return(ret);
275	    }
276	    str++;
277	    expr = xmlStrndup(str, cur - str);
278	    if (expr == NULL)
279		return(ret);
280	    else if (*expr == '{') {
281		ret = xmlStrcat(ret, expr);
282		xmlFree(expr);
283	    } else {
284		xmlXPathCompExprPtr comp;
285		/*
286		 * TODO: keep precompiled form around
287		 */
288		if ((nsList == NULL) && (node != NULL)) {
289		    int i = 0;
290
291		    nsList = xmlGetNsList(node->doc, node);
292		    if (nsList != NULL) {
293			while (nsList[i] != NULL)
294			    i++;
295			nsNr = i;
296		    }
297		}
298		comp = xmlXPathCompile(expr);
299                val = xsltEvalXPathStringNs(ctxt, comp, nsNr, nsList);
300		xmlXPathFreeCompExpr(comp);
301		xmlFree(expr);
302		if (val != NULL) {
303		    ret = xmlStrcat(ret, val);
304		    xmlFree(val);
305		}
306	    }
307	    cur++;
308	    str = cur;
309	} else if (*cur == '}') {
310	    cur++;
311	    if (*cur == '}') {	/* escaped '}' */
312		ret = xmlStrncat(ret, str, cur - str);
313		cur++;
314		str = cur;
315		continue;
316	    } else {
317	        xsltTransformError(ctxt, NULL, NULL,
318		     "xsltAttrTemplateValueProcessNode: unmatched '}'\n");
319	    }
320	} else
321	    cur++;
322    }
323    if (cur != str) {
324	ret = xmlStrncat(ret, str, cur - str);
325    }
326
327    if (nsList != NULL)
328	xmlFree(nsList);
329
330    return(ret);
331}
332
333/**
334 * xsltAttrTemplateValueProcess:
335 * @ctxt:  the XSLT transformation context
336 * @str:  the attribute template node value
337 *
338 * Process the given node and return the new string value.
339 *
340 * Returns the computed string value or NULL, must be deallocated by the
341 *    caller.
342 */
343xmlChar *
344xsltAttrTemplateValueProcess(xsltTransformContextPtr ctxt, const xmlChar *str) {
345    return(xsltAttrTemplateValueProcessNode(ctxt, str, NULL));
346}
347
348/**
349 * xsltEvalAttrValueTemplate:
350 * @ctxt:  the XSLT transformation context
351 * @node:  the stylesheet node
352 * @name:  the attribute QName
353 * @ns:  the attribute namespace URI
354 *
355 * Evaluate a attribute value template, i.e. the attribute value can
356 * contain expressions contained in curly braces ({}) and those are
357 * substituted by they computed value.
358 *
359 * Returns the computed string value or NULL, must be deallocated by the
360 *    caller.
361 */
362xmlChar *
363xsltEvalAttrValueTemplate(xsltTransformContextPtr ctxt, xmlNodePtr node,
364	                  const xmlChar *name, const xmlChar *ns) {
365    xmlChar *ret;
366    xmlChar *expr;
367
368    if ((ctxt == NULL) || (node == NULL) || (name == NULL))
369	return(NULL);
370
371    expr = xsltGetNsProp(node, name, ns);
372    if (expr == NULL)
373	return(NULL);
374
375    /*
376     * TODO: though now {} is detected ahead, it would still be good to
377     *       optimize both functions to keep the splitted value if the
378     *       attribute content and the XPath precompiled expressions around
379     */
380
381    ret = xsltAttrTemplateValueProcessNode(ctxt, expr, node);
382#ifdef WITH_XSLT_DEBUG_TEMPLATES
383    XSLT_TRACE(ctxt,XSLT_TRACE_TEMPLATES,xsltGenericDebug(xsltGenericDebugContext,
384	 "xsltEvalAttrValueTemplate: %s returns %s\n", expr, ret));
385#endif
386    if (expr != NULL)
387	xmlFree(expr);
388    return(ret);
389}
390
391/**
392 * xsltEvalStaticAttrValueTemplate:
393 * @style:  the XSLT stylesheet
394 * @node:  the stylesheet node
395 * @name:  the attribute Name
396 * @ns:  the attribute namespace URI
397 * @found:  indicator whether the attribute is present
398 *
399 * Check if an attribute value template has a static value, i.e. the
400 * attribute value does not contain expressions contained in curly braces ({})
401 *
402 * Returns the static string value or NULL, must be deallocated by the
403 *    caller.
404 */
405const xmlChar *
406xsltEvalStaticAttrValueTemplate(xsltStylesheetPtr style, xmlNodePtr node,
407			const xmlChar *name, const xmlChar *ns, int *found) {
408    const xmlChar *ret;
409    xmlChar *expr;
410
411    if ((style == NULL) || (node == NULL) || (name == NULL))
412	return(NULL);
413
414    expr = xsltGetNsProp(node, name, ns);
415    if (expr == NULL) {
416	*found = 0;
417	return(NULL);
418    }
419    *found = 1;
420
421    ret = xmlStrchr(expr, '{');
422    if (ret != NULL) {
423	xmlFree(expr);
424	return(NULL);
425    }
426    ret = xmlDictLookup(style->dict, expr, -1);
427    xmlFree(expr);
428    return(ret);
429}
430
431/**
432 * xsltAttrTemplateProcess:
433 * @ctxt:  the XSLT transformation context
434 * @target:  the result node
435 * @cur:  the attribute template node
436 *
437 * Process the given attribute and return the new processed copy.
438 *
439 * Returns the attribute replacement.
440 */
441xmlAttrPtr
442xsltAttrTemplateProcess(xsltTransformContextPtr ctxt, xmlNodePtr target,
443	                xmlAttrPtr cur) {
444    const xmlChar *value;
445    xmlNsPtr ns;
446    xmlAttrPtr ret;
447    if ((ctxt == NULL) || (cur == NULL) || (target == NULL))
448	return(NULL);
449
450    if (cur->type != XML_ATTRIBUTE_NODE)
451	return(NULL);
452
453    if ((cur->children == NULL) || (cur->children->type != XML_TEXT_NODE) ||
454        (cur->children->next != NULL)) {
455	xsltTransformError(ctxt, NULL, cur->parent,
456		"attribute %s content problem\n", cur->name);
457        return(NULL);
458    }
459    value = cur->children->content;
460    if (value == NULL)
461        value = xmlDictLookup(ctxt->dict, BAD_CAST "", 0);
462    if ((cur->ns != NULL) &&
463	(xmlStrEqual(cur->ns->href, XSLT_NAMESPACE))) {
464	if (xmlStrEqual(cur->name, (const xmlChar *)"use-attribute-sets")) {
465	    xsltApplyAttributeSet(ctxt, ctxt->node, NULL, value);
466	}
467	return(NULL);
468    }
469
470    ret = target->properties;
471    while (ret != NULL) {
472        if (xmlStrEqual(ret->name, cur->name)) {
473	    if (cur->ns == NULL) {
474	        if (ret->ns == NULL)
475		    break;
476	    } else {
477	        if ((ret->ns != NULL) &&
478		    (xmlStrEqual(ret->ns->href, cur->ns->href)))
479		    break;
480	    }
481	}
482        ret = ret->next;
483    }
484    if (ret != NULL) {
485        /* free the existing value */
486	xmlFreeNodeList(ret->children);
487	ret->children = ret->last = NULL;
488    } else {
489        /* create a new attribute */
490	if (cur->ns != NULL)
491	    ns = xsltGetPlainNamespace(ctxt, cur->parent, cur->ns, target);
492	else
493	    ns = NULL;
494	ret = xmlNewNsProp(target, ns, cur->name, NULL);
495    }
496    if (ret != NULL) {
497        xmlNodePtr text;
498
499        text = xmlNewText(NULL);
500	if (text != NULL) {
501	    ret->last = ret->children = text;
502	    text->parent = (xmlNodePtr) ret;
503	    text->doc = ret->doc;
504	    if (cur->psvi != NULL) {
505		xmlChar *val;
506		val = xsltEvalAVT(ctxt, cur->psvi, cur->parent);
507		if (val == NULL) {
508		    text->content = xmlStrdup(BAD_CAST "runtime error");
509		} else {
510		    text->content = val;
511		}
512	    } else if ((ctxt->internalized) && (target != NULL) &&
513	               (target->doc != NULL) &&
514		       (target->doc->dict == ctxt->dict)) {
515		text->content = (xmlChar *) value;
516	    } else {
517		text->content = xmlStrdup(value);
518	    }
519	}
520    } else {
521	xsltTransformError(ctxt, NULL, cur->parent,
522		"Failed to create attribute %s\n", cur->name);
523    }
524    return(ret);
525}
526
527
528/**
529 * xsltAttrListTemplateProcess:
530 * @ctxt:  the XSLT transformation context
531 * @target:  the element where the attributes will be grafted
532 * @cur:  the first attribute
533 *
534 * Do a copy of an attribute list with attribute template processing
535 *
536 * Returns: a new xmlAttrPtr, or NULL in case of error.
537 */
538xmlAttrPtr
539xsltAttrListTemplateProcess(xsltTransformContextPtr ctxt,
540	                    xmlNodePtr target, xmlAttrPtr cur) {
541    xmlAttrPtr ret = NULL;
542    xmlAttrPtr q;
543    xmlNodePtr oldInsert;
544
545    oldInsert = ctxt->insert;
546    ctxt->insert = target;
547    while (cur != NULL) {
548        q = xsltAttrTemplateProcess(ctxt, target, cur);
549	if (q != NULL) {
550	    q->parent = target;
551	    q->doc = target->doc;
552	    if (ret == NULL) {
553		ret = q;
554	    }
555	}
556	cur = cur->next;
557    }
558    ctxt->insert = oldInsert;
559    return(ret);
560}
561
562
563/**
564 * xsltTemplateProcess:
565 * @ctxt:  the XSLT transformation context
566 * @node:  the attribute template node
567 *
568 * Process the given node and return the new string value.
569 *
570 * Returns the computed tree replacement
571 */
572xmlNodePtr *
573xsltTemplateProcess(xsltTransformContextPtr ctxt ATTRIBUTE_UNUSED, xmlNodePtr node) {
574    if (node == NULL)
575	return(NULL);
576
577    return(0);
578}
579
580
581