064394e537b1bcea7c7b548a3208246c85fb4743
[platform/upstream/libxslt.git] / libxslt / templates.c
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 #include "libxslt.h"
13
14 #include <string.h>
15
16 #include <libxml/xmlmemory.h>
17 #include <libxml/globals.h>
18 #include <libxml/xmlerror.h>
19 #include <libxml/tree.h>
20 #include <libxml/xpathInternals.h>
21 #include <libxml/parserInternals.h>
22 #include "xslt.h"
23 #include "xsltInternals.h"
24 #include "xsltutils.h"
25 #include "variables.h"
26 #include "functions.h"
27 #include "templates.h"
28 #include "transform.h"
29 #include "namespaces.h"
30 #include "attributes.h"
31
32 #ifdef WITH_XSLT_DEBUG
33 #define WITH_XSLT_DEBUG_TEMPLATES
34 #endif
35
36 /************************************************************************
37  *                                                                      *
38  *                      Module interfaces                               *
39  *                                                                      *
40  ************************************************************************/
41  
42 /**
43  * xsltEvalXPathPredicate:
44  * @ctxt:  the XSLT transformation context
45  * @comp:  the XPath compiled expression
46  * @nsList:  the namespaces in scope
47  * @nsNr:  the number of namespaces in scope
48  *
49  * Process the expression using XPath and evaluate the result as
50  * an XPath predicate
51  *
52  * Returns 1 is the predicate was true, 0 otherwise
53  */
54 int
55 xsltEvalXPathPredicate(xsltTransformContextPtr ctxt, xmlXPathCompExprPtr comp,
56                        xmlNsPtr *nsList, int nsNr) {
57     int ret;
58     xmlXPathObjectPtr res;
59     int oldNsNr;
60     xmlNsPtr *oldNamespaces;
61     xmlNodePtr oldInst;
62     int oldProximityPosition, oldContextSize;
63
64     oldContextSize = ctxt->xpathCtxt->contextSize;
65     oldProximityPosition = ctxt->xpathCtxt->proximityPosition;
66     oldNsNr = ctxt->xpathCtxt->nsNr;
67     oldNamespaces = ctxt->xpathCtxt->namespaces;
68     oldInst = ctxt->inst;
69
70     ctxt->xpathCtxt->node = ctxt->node;
71     ctxt->xpathCtxt->namespaces = nsList;
72     ctxt->xpathCtxt->nsNr = nsNr;
73
74     res = xmlXPathCompiledEval(comp, ctxt->xpathCtxt);
75
76     if (res != NULL) {
77         ret = xmlXPathEvalPredicate(ctxt->xpathCtxt, res);
78         xmlXPathFreeObject(res);
79 #ifdef WITH_XSLT_DEBUG_TEMPLATES
80         xsltGenericDebug(xsltGenericDebugContext,
81              "xsltEvalXPathPredicate: returns %d\n", ret);
82 #endif
83     } else {
84 #ifdef WITH_XSLT_DEBUG_TEMPLATES
85         xsltGenericDebug(xsltGenericDebugContext,
86              "xsltEvalXPathPredicate: failed\n");
87 #endif
88         ctxt->state = XSLT_STATE_STOPPED;
89         ret = 0;
90     }
91     ctxt->xpathCtxt->nsNr = oldNsNr;
92
93     ctxt->xpathCtxt->namespaces = oldNamespaces;
94     ctxt->inst = oldInst;
95     ctxt->xpathCtxt->contextSize = oldContextSize;
96     ctxt->xpathCtxt->proximityPosition = oldProximityPosition;
97
98     return(ret);
99 }
100
101 /**
102  * xsltEvalXPathString:
103  * @ctxt:  the XSLT transformation context
104  * @comp:  the compiled XPath expression
105  *
106  * Process the expression using XPath and get a string
107  *
108  * Returns the computed string value or NULL, must be deallocated by the
109  *    caller.
110  */
111 xmlChar *
112 xsltEvalXPathString(xsltTransformContextPtr ctxt, xmlXPathCompExprPtr comp) {
113     xmlChar *ret = NULL;
114     xmlXPathObjectPtr res;
115     xmlNodePtr oldInst;
116     xmlNodePtr oldNode;
117     int oldPos, oldSize;
118     int oldNsNr;
119     xmlNsPtr *oldNamespaces;
120
121     oldInst = ctxt->inst;
122     oldNode = ctxt->node;
123     oldPos = ctxt->xpathCtxt->proximityPosition;
124     oldSize = ctxt->xpathCtxt->contextSize;
125     oldNsNr = ctxt->xpathCtxt->nsNr;
126     oldNamespaces = ctxt->xpathCtxt->namespaces;
127
128     ctxt->xpathCtxt->node = ctxt->node;
129     /* TODO: do we need to propagate the namespaces here ? */
130     ctxt->xpathCtxt->namespaces = NULL;
131     ctxt->xpathCtxt->nsNr = 0;
132     res = xmlXPathCompiledEval(comp, ctxt->xpathCtxt);
133     if (res != NULL) {
134         if (res->type != XPATH_STRING)
135             res = xmlXPathConvertString(res);
136         if (res->type == XPATH_STRING) {
137             ret = res->stringval;
138             res->stringval = NULL;
139         } else {
140             xsltPrintErrorContext(ctxt, NULL, NULL);
141             xsltGenericError(xsltGenericErrorContext,
142                  "xpath : string() function didn't return a String\n");
143         }
144         xmlXPathFreeObject(res);
145     } else {
146         ctxt->state = XSLT_STATE_STOPPED;
147     }
148 #ifdef WITH_XSLT_DEBUG_TEMPLATES
149     xsltGenericDebug(xsltGenericDebugContext,
150          "xsltEvalXPathString: returns %s\n", ret);
151 #endif
152     ctxt->inst = oldInst;
153     ctxt->node = oldNode;
154     ctxt->xpathCtxt->contextSize = oldSize;
155     ctxt->xpathCtxt->proximityPosition = oldPos;
156     ctxt->xpathCtxt->nsNr = oldNsNr;
157     ctxt->xpathCtxt->namespaces = oldNamespaces;
158     return(ret);
159 }
160
161 /**
162  * xsltEvalTemplateString:
163  * @ctxt:  the XSLT transformation context
164  * @node:  the stylesheet node
165  * @parent:  the content parent
166  *
167  * Evaluate a template string value, i.e. the parent list is interpreter
168  * as template content and the resulting tree string value is returned
169  * This is needed for example by xsl:comment and xsl:processing-instruction
170  *
171  * Returns the computed string value or NULL, must be deallocated by the
172  *    caller.
173  */
174 xmlChar *
175 xsltEvalTemplateString(xsltTransformContextPtr ctxt, xmlNodePtr node,
176                        xmlNodePtr parent) {
177     xmlNodePtr oldInsert, insert = NULL;
178     xmlChar *ret;
179
180     if ((ctxt == NULL) || (node == NULL) || (parent == NULL))
181         return(NULL);
182
183     if (parent->children == NULL)
184         return(NULL);
185
186     insert = xmlNewDocNode(ctxt->output, NULL,
187                            (const xmlChar *)"fake", NULL);
188     if (insert == NULL)
189         return(NULL);
190     oldInsert = ctxt->insert;
191     ctxt->insert = insert;
192
193     xsltApplyOneTemplate(ctxt, node, parent->children, NULL, NULL);
194
195     ctxt->insert = oldInsert;
196
197     ret = xmlNodeGetContent(insert);
198     if (insert != NULL)
199         xmlFreeNode(insert);
200     return(ret);
201 }
202
203 /**
204  * xsltAttrTemplateValueProcess:
205  * @ctxt:  the XSLT transformation context
206  * @str:  the attribute template node value
207  *
208  * Process the given node and return the new string value.
209  *
210  * Returns the computed string value or NULL, must be deallocated by the
211  *    caller.
212  */
213 xmlChar *
214 xsltAttrTemplateValueProcess(xsltTransformContextPtr ctxt, const xmlChar *str) {
215     xmlChar *ret = NULL;
216     const xmlChar *cur;
217     xmlChar *expr, *val;
218
219     if (str == NULL) return(NULL);
220     if (*str == 0)
221         return(xmlStrndup((xmlChar *)"", 0));
222
223     cur = str;
224     while (*cur != 0) {
225         if (*cur == '{') {
226             ret = xmlStrncat(ret, str, cur - str);
227             str = cur;
228             cur++;
229             while ((*cur != 0) && (*cur != '}')) cur++;
230             if (*cur == 0) {
231                 ret = xmlStrncat(ret, str, cur - str);
232                 return(ret);
233             }
234             str++;
235             expr = xmlStrndup(str, cur - str);
236             if (expr == NULL)
237                 return(ret);
238             else if (*expr == '{') {
239                 ret = xmlStrcat(ret, expr);
240                 xmlFree(expr);
241             } else {
242                 xmlXPathCompExprPtr comp;
243                 /*
244                  * TODO: keep precompiled form around
245                  */
246                 comp = xmlXPathCompile(expr);
247                 val = xsltEvalXPathString(ctxt, comp);
248                 xmlXPathFreeCompExpr(comp);
249                 xmlFree(expr);
250                 if (val != NULL) {
251                     ret = xmlStrcat(ret, val);
252                     xmlFree(val);
253                 }
254             }
255             cur++;
256             str = cur;
257         } else
258             cur++;
259     }
260     if (cur != str) {
261         ret = xmlStrncat(ret, str, cur - str);
262     }
263
264     return(ret);
265 }
266
267 /**
268  * xsltEvalAttrValueTemplate:
269  * @ctxt:  the XSLT transformation context
270  * @node:  the stylesheet node
271  * @name:  the attribute QName
272  * @ns:  the attribute namespace URI
273  *
274  * Evaluate a attribute value template, i.e. the attribute value can
275  * contain expressions contained in curly braces ({}) and those are
276  * substituted by they computed value.
277  *
278  * Returns the computed string value or NULL, must be deallocated by the
279  *    caller.
280  */
281 xmlChar *
282 xsltEvalAttrValueTemplate(xsltTransformContextPtr ctxt, xmlNodePtr node,
283                           const xmlChar *name, const xmlChar *ns) {
284     xmlChar *ret;
285     xmlChar *expr;
286
287     if ((ctxt == NULL) || (node == NULL) || (name == NULL))
288         return(NULL);
289
290     expr = xsltGetNsProp(node, name, ns);
291     if (expr == NULL)
292         return(NULL);
293
294     /*
295      * TODO: though now {} is detected ahead, it would still be good to
296      *       optimize both functions to keep the splitted value if the
297      *       attribute content and the XPath precompiled expressions around
298      */
299
300     ret = xsltAttrTemplateValueProcess(ctxt, expr);
301 #ifdef WITH_XSLT_DEBUG_TEMPLATES
302     xsltGenericDebug(xsltGenericDebugContext,
303          "xsltEvalAttrValueTemplate: %s returns %s\n", expr, ret);
304 #endif
305     if (expr != NULL)
306         xmlFree(expr);
307     return(ret);
308 }
309
310 /**
311  * xsltEvalStaticAttrValueTemplate:
312  * @style:  the XSLT stylesheet
313  * @node:  the stylesheet node
314  * @name:  the attribute Name
315  * @ns:  the attribute namespace URI
316  * @found:  indicator whether the attribute is present
317  *
318  * Check if an attribute value template has a static value, i.e. the
319  * attribute value does not contain expressions contained in curly braces ({})
320  *
321  * Returns the static string value or NULL, must be deallocated by the
322  *    caller.
323  */
324 xmlChar *
325 xsltEvalStaticAttrValueTemplate(xsltStylesheetPtr style, xmlNodePtr node,
326                         const xmlChar *name, const xmlChar *ns, int *found) {
327     const xmlChar *ret;
328     xmlChar *expr;
329
330     if ((style == NULL) || (node == NULL) || (name == NULL))
331         return(NULL);
332
333     expr = xsltGetNsProp(node, name, ns);
334     if (expr == NULL) {
335         *found = 0;
336         return(NULL);
337     }
338     *found = 1;
339
340     ret = xmlStrchr(expr, '{');
341     if (ret != NULL) {
342         xmlFree(expr);
343         return(NULL);
344     }
345     return(expr);
346 }
347
348 /**
349  * xsltAttrTemplateProcess:
350  * @ctxt:  the XSLT transformation context
351  * @target:  the result node
352  * @cur:  the attribute template node
353  *
354  * Process the given attribute and return the new processed copy.
355  *
356  * Returns the attribute replacement.
357  */
358 xmlAttrPtr
359 xsltAttrTemplateProcess(xsltTransformContextPtr ctxt, xmlNodePtr target,
360                         xmlAttrPtr cur) {
361     xmlNsPtr ns;
362     xmlAttrPtr ret;
363     if ((ctxt == NULL) || (cur == NULL))
364         return(NULL);
365     
366     if (cur->type != XML_ATTRIBUTE_NODE)
367         return(NULL);
368
369     if ((cur->ns != NULL) &&
370         (xmlStrEqual(cur->ns->href, XSLT_NAMESPACE))) {
371         if (xmlStrEqual(cur->name, (const xmlChar *)"use-attribute-sets")) {
372             xmlChar *in;
373
374             in = xmlNodeListGetString(ctxt->document->doc, cur->children, 1);
375             if (in != NULL) {
376                 xsltApplyAttributeSet(ctxt, ctxt->node, NULL, in);
377                 xmlFree(in);
378             }
379         }
380         return(NULL);
381     }
382     if (cur->ns != NULL)
383         ns = xsltGetNamespace(ctxt, cur->parent, cur->ns, target);
384     else
385         ns = NULL;
386
387     if (cur->children != NULL) {
388         xmlChar *in = xmlNodeListGetString(ctxt->document->doc,
389                                            cur->children, 1);
390         xmlChar *out;
391
392         /* TODO: optimize if no template value was detected */
393         if (in != NULL) {
394             out = xsltAttrTemplateValueProcess(ctxt, in);
395             ret = xmlSetNsProp(target, ns, cur->name, out);
396             if (out != NULL)
397                 xmlFree(out);
398             xmlFree(in);
399         } else
400             ret = xmlSetNsProp(target, ns, cur->name, (const xmlChar *)"");
401        
402     } else 
403         ret = xmlSetNsProp(target, ns, cur->name, (const xmlChar *)"");
404     return(ret);
405 }
406
407
408 /**
409  * xsltAttrListTemplateProcess:
410  * @ctxt:  the XSLT transformation context
411  * @target:  the element where the attributes will be grafted
412  * @cur:  the first attribute
413  *
414  * Do a copy of an attribute list with attribute template processing
415  *
416  * Returns: a new xmlAttrPtr, or NULL in case of error.
417  */
418 xmlAttrPtr
419 xsltAttrListTemplateProcess(xsltTransformContextPtr ctxt, 
420                             xmlNodePtr target, xmlAttrPtr cur) {
421     xmlAttrPtr ret = NULL;
422     xmlAttrPtr q;
423     xmlNodePtr oldInsert;
424
425     oldInsert = ctxt->insert;
426     ctxt->insert = target;
427     while (cur != NULL) {
428         q = xsltAttrTemplateProcess(ctxt, target, cur);
429         if (q != NULL) {
430             q->parent = target;
431             q->doc = ctxt->output;
432             if (ret == NULL) {
433                 ret = q;
434             }
435         }
436         cur = cur->next;
437     }
438     ctxt->insert = oldInsert;
439     return(ret);
440 }
441
442
443 /**
444  * xsltTemplateProcess:
445  * @ctxt:  the XSLT transformation context
446  * @node:  the attribute template node
447  *
448  * Process the given node and return the new string value.
449  *
450  * Returns the computed tree replacement
451  */
452 xmlNodePtr *
453 xsltTemplateProcess(xsltTransformContextPtr ctxt ATTRIBUTE_UNUSED, xmlNodePtr node) {
454     if (node == NULL)
455         return(NULL);
456     
457     return(0);
458 }
459
460