source: trunk/third/libxslt/libxslt/templates.c @ 18543

Revision 18543, 13.2 KB checked in by ghudson, 21 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r18542, which included commits to RCS files with non-trunk default branches.
Line 
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        xsltGenericDebug(xsltGenericDebugContext,
82             "xsltEvalXPathPredicate: returns %d\n", ret);
83#endif
84    } else {
85#ifdef WITH_XSLT_DEBUG_TEMPLATES
86        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    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    insert = xmlNewDocNode(ctxt->output, NULL,
206                           (const xmlChar *)"fake", NULL);
207    if (insert == NULL)
208        return(NULL);
209    oldInsert = ctxt->insert;
210    ctxt->insert = insert;
211
212    xsltApplyOneTemplate(ctxt, node, parent->children, NULL, NULL);
213
214    ctxt->insert = oldInsert;
215
216    ret = xmlNodeGetContent(insert);
217    if (insert != NULL)
218        xmlFreeNode(insert);
219    return(ret);
220}
221
222/**
223 * xsltAttrTemplateValueProcessNode:
224 * @ctxt:  the XSLT transformation context
225 * @str:  the attribute template node value
226 * @node:  the node hosting the attribute
227 *
228 * Process the given string, allowing to pass a namespace mapping
229 * context and return the new string value.
230 *
231 * Returns the computed string value or NULL, must be deallocated by the
232 *    caller.
233 */
234xmlChar *
235xsltAttrTemplateValueProcessNode(xsltTransformContextPtr ctxt,
236          const xmlChar *str, xmlNodePtr node) {
237    xmlChar *ret = NULL;
238    const xmlChar *cur;
239    xmlChar *expr, *val;
240    xmlNsPtr *nsList = NULL;
241    int nsNr = 0;
242
243    if (str == NULL) return(NULL);
244    if (*str == 0)
245        return(xmlStrndup((xmlChar *)"", 0));
246
247    cur = str;
248    while (*cur != 0) {
249        if (*cur == '{') {
250            ret = xmlStrncat(ret, str, cur - str);
251            str = cur;
252            cur++;
253            while ((*cur != 0) && (*cur != '}')) cur++;
254            if (*cur == 0) {
255                ret = xmlStrncat(ret, str, cur - str);
256                return(ret);
257            }
258            str++;
259            expr = xmlStrndup(str, cur - str);
260            if (expr == NULL)
261                return(ret);
262            else if (*expr == '{') {
263                ret = xmlStrcat(ret, expr);
264                xmlFree(expr);
265            } else {
266                xmlXPathCompExprPtr comp;
267                /*
268                 * TODO: keep precompiled form around
269                 */
270                if ((nsList == NULL) && (node != NULL)) {
271                    int i = 0;
272
273                    nsList = xmlGetNsList(node->doc, node);
274                    if (nsList != NULL) {
275                        while (nsList[i] != NULL)
276                            i++;
277                        nsNr = i;
278                    }
279                }
280                comp = xmlXPathCompile(expr);
281                val = xsltEvalXPathStringNs(ctxt, comp, nsNr, nsList);
282                xmlXPathFreeCompExpr(comp);
283                xmlFree(expr);
284                if (val != NULL) {
285                    ret = xmlStrcat(ret, val);
286                    xmlFree(val);
287                }
288            }
289            cur++;
290            str = cur;
291        } else
292            cur++;
293    }
294    if (cur != str) {
295        ret = xmlStrncat(ret, str, cur - str);
296    }
297
298    if (nsList != NULL)
299        xmlFree(nsList);
300
301    return(ret);
302}
303
304/**
305 * xsltAttrTemplateValueProcess:
306 * @ctxt:  the XSLT transformation context
307 * @str:  the attribute template node value
308 *
309 * Process the given node and return the new string value.
310 *
311 * Returns the computed string value or NULL, must be deallocated by the
312 *    caller.
313 */
314xmlChar *
315xsltAttrTemplateValueProcess(xsltTransformContextPtr ctxt, const xmlChar *str) {
316    return(xsltAttrTemplateValueProcessNode(ctxt, str, NULL));
317}
318
319/**
320 * xsltEvalAttrValueTemplate:
321 * @ctxt:  the XSLT transformation context
322 * @node:  the stylesheet node
323 * @name:  the attribute QName
324 * @ns:  the attribute namespace URI
325 *
326 * Evaluate a attribute value template, i.e. the attribute value can
327 * contain expressions contained in curly braces ({}) and those are
328 * substituted by they computed value.
329 *
330 * Returns the computed string value or NULL, must be deallocated by the
331 *    caller.
332 */
333xmlChar *
334xsltEvalAttrValueTemplate(xsltTransformContextPtr ctxt, xmlNodePtr node,
335                          const xmlChar *name, const xmlChar *ns) {
336    xmlChar *ret;
337    xmlChar *expr;
338
339    if ((ctxt == NULL) || (node == NULL) || (name == NULL))
340        return(NULL);
341
342    expr = xsltGetNsProp(node, name, ns);
343    if (expr == NULL)
344        return(NULL);
345
346    /*
347     * TODO: though now {} is detected ahead, it would still be good to
348     *       optimize both functions to keep the splitted value if the
349     *       attribute content and the XPath precompiled expressions around
350     */
351
352    ret = xsltAttrTemplateValueProcessNode(ctxt, expr, node);
353#ifdef WITH_XSLT_DEBUG_TEMPLATES
354    xsltGenericDebug(xsltGenericDebugContext,
355         "xsltEvalAttrValueTemplate: %s returns %s\n", expr, ret);
356#endif
357    if (expr != NULL)
358        xmlFree(expr);
359    return(ret);
360}
361
362/**
363 * xsltEvalStaticAttrValueTemplate:
364 * @style:  the XSLT stylesheet
365 * @node:  the stylesheet node
366 * @name:  the attribute Name
367 * @ns:  the attribute namespace URI
368 * @found:  indicator whether the attribute is present
369 *
370 * Check if an attribute value template has a static value, i.e. the
371 * attribute value does not contain expressions contained in curly braces ({})
372 *
373 * Returns the static string value or NULL, must be deallocated by the
374 *    caller.
375 */
376xmlChar *
377xsltEvalStaticAttrValueTemplate(xsltStylesheetPtr style, xmlNodePtr node,
378                        const xmlChar *name, const xmlChar *ns, int *found) {
379    const xmlChar *ret;
380    xmlChar *expr;
381
382    if ((style == NULL) || (node == NULL) || (name == NULL))
383        return(NULL);
384
385    expr = xsltGetNsProp(node, name, ns);
386    if (expr == NULL) {
387        *found = 0;
388        return(NULL);
389    }
390    *found = 1;
391
392    ret = xmlStrchr(expr, '{');
393    if (ret != NULL) {
394        xmlFree(expr);
395        return(NULL);
396    }
397    return(expr);
398}
399
400/**
401 * xsltAttrTemplateProcess:
402 * @ctxt:  the XSLT transformation context
403 * @target:  the result node
404 * @cur:  the attribute template node
405 *
406 * Process the given attribute and return the new processed copy.
407 *
408 * Returns the attribute replacement.
409 */
410xmlAttrPtr
411xsltAttrTemplateProcess(xsltTransformContextPtr ctxt, xmlNodePtr target,
412                        xmlAttrPtr cur) {
413    xmlNsPtr ns;
414    xmlAttrPtr ret;
415    if ((ctxt == NULL) || (cur == NULL))
416        return(NULL);
417   
418    if (cur->type != XML_ATTRIBUTE_NODE)
419        return(NULL);
420
421    if ((cur->ns != NULL) &&
422        (xmlStrEqual(cur->ns->href, XSLT_NAMESPACE))) {
423        if (xmlStrEqual(cur->name, (const xmlChar *)"use-attribute-sets")) {
424            xmlChar *in;
425
426            in = xmlNodeListGetString(ctxt->document->doc, cur->children, 1);
427            if (in != NULL) {
428                xsltApplyAttributeSet(ctxt, ctxt->node, NULL, in);
429                xmlFree(in);
430            }
431        }
432        return(NULL);
433    }
434    if (cur->ns != NULL)
435        ns = xsltGetNamespace(ctxt, cur->parent, cur->ns, target);
436    else
437        ns = NULL;
438
439    if (cur->children != NULL) {
440        xmlChar *in = xmlNodeListGetString(ctxt->document->doc,
441                                           cur->children, 1);
442        xmlChar *out;
443
444        /* TODO: optimize if no template value was detected */
445        if (in != NULL) {
446            out = xsltAttrTemplateValueProcessNode(ctxt, in, cur->parent);
447            ret = xmlSetNsProp(target, ns, cur->name, out);
448            if (out != NULL)
449                xmlFree(out);
450            xmlFree(in);
451        } else
452            ret = xmlSetNsProp(target, ns, cur->name, (const xmlChar *)"");
453       
454    } else
455        ret = xmlSetNsProp(target, ns, cur->name, (const xmlChar *)"");
456    return(ret);
457}
458
459
460/**
461 * xsltAttrListTemplateProcess:
462 * @ctxt:  the XSLT transformation context
463 * @target:  the element where the attributes will be grafted
464 * @cur:  the first attribute
465 *
466 * Do a copy of an attribute list with attribute template processing
467 *
468 * Returns: a new xmlAttrPtr, or NULL in case of error.
469 */
470xmlAttrPtr
471xsltAttrListTemplateProcess(xsltTransformContextPtr ctxt,
472                            xmlNodePtr target, xmlAttrPtr cur) {
473    xmlAttrPtr ret = NULL;
474    xmlAttrPtr q;
475    xmlNodePtr oldInsert;
476
477    oldInsert = ctxt->insert;
478    ctxt->insert = target;
479    while (cur != NULL) {
480        q = xsltAttrTemplateProcess(ctxt, target, cur);
481        if (q != NULL) {
482            q->parent = target;
483            q->doc = ctxt->output;
484            if (ret == NULL) {
485                ret = q;
486            }
487        }
488        cur = cur->next;
489    }
490    ctxt->insert = oldInsert;
491    return(ret);
492}
493
494
495/**
496 * xsltTemplateProcess:
497 * @ctxt:  the XSLT transformation context
498 * @node:  the attribute template node
499 *
500 * Process the given node and return the new string value.
501 *
502 * Returns the computed tree replacement
503 */
504xmlNodePtr *
505xsltTemplateProcess(xsltTransformContextPtr ctxt ATTRIBUTE_UNUSED, xmlNodePtr node) {
506    if (node == NULL)
507        return(NULL);
508   
509    return(0);
510}
511
512
Note: See TracBrowser for help on using the repository browser.