source: trunk/third/libxslt/libxslt/keys.c @ 19102

Revision 19102, 15.0 KB checked in by ghudson, 21 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r19101, which included commits to RCS files with non-trunk default branches.
Line 
1/*
2 * keys.c: Implemetation of the keys support
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/tree.h>
19#include <libxml/valid.h>
20#include <libxml/hash.h>
21#include <libxml/xmlerror.h>
22#include <libxml/parserInternals.h>
23#include <libxml/xpathInternals.h>
24#include "xslt.h"
25#include "xsltInternals.h"
26#include "xsltutils.h"
27#include "imports.h"
28#include "templates.h"
29#include "keys.h"
30
31#ifdef WITH_XSLT_DEBUG
32#define WITH_XSLT_DEBUG_KEYS
33#endif
34
35typedef struct _xsltKeyDef xsltKeyDef;
36typedef xsltKeyDef *xsltKeyDefPtr;
37struct _xsltKeyDef {
38    struct _xsltKeyDef *next;
39    xmlNodePtr inst;
40    xmlChar *name;
41    xmlChar *nameURI;
42    xmlChar *match;
43    xmlChar *use;
44    xmlXPathCompExprPtr comp;
45    xmlXPathCompExprPtr usecomp;
46};
47
48typedef struct _xsltKeyTable xsltKeyTable;
49typedef xsltKeyTable *xsltKeyTablePtr;
50struct _xsltKeyTable {
51    struct _xsltKeyTable *next;
52    xmlChar *name;
53    xmlChar *nameURI;
54    xmlHashTablePtr keys;
55};
56
57
58/************************************************************************
59 *                                                                      *
60 *                      Type functions                                  *
61 *                                                                      *
62 ************************************************************************/
63
64/**
65 * xsltNewKeyDef:
66 * @name:  the key name or NULL
67 * @nameURI:  the name URI or NULL
68 *
69 * Create a new XSLT KeyDef
70 *
71 * Returns the newly allocated xsltKeyDefPtr or NULL in case of error
72 */
73static xsltKeyDefPtr
74xsltNewKeyDef(const xmlChar *name, const xmlChar *nameURI) {
75    xsltKeyDefPtr cur;
76
77    cur = (xsltKeyDefPtr) xmlMalloc(sizeof(xsltKeyDef));
78    if (cur == NULL) {
79        xsltTransformError(NULL, NULL, NULL,
80                "xsltNewKeyDef : malloc failed\n");
81        return(NULL);
82    }
83    memset(cur, 0, sizeof(xsltKeyDef));
84    if (name != NULL)
85        cur->name = xmlStrdup(name);
86    if (nameURI != NULL)
87        cur->nameURI = xmlStrdup(nameURI);
88    return(cur);
89}
90
91/**
92 * xsltFreeKeyDef:
93 * @keyd:  an XSLT key definition
94 *
95 * Free up the memory allocated by @keyd
96 */
97static void
98xsltFreeKeyDef(xsltKeyDefPtr keyd) {
99    if (keyd == NULL)
100        return;
101    if (keyd->comp != NULL)
102        xmlXPathFreeCompExpr(keyd->comp);
103    if (keyd->usecomp != NULL)
104        xmlXPathFreeCompExpr(keyd->usecomp);
105    if (keyd->name != NULL)
106        xmlFree(keyd->name);
107    if (keyd->nameURI != NULL)
108        xmlFree(keyd->nameURI);
109    if (keyd->match != NULL)
110        xmlFree(keyd->match);
111    if (keyd->use != NULL)
112        xmlFree(keyd->use);
113    memset(keyd, -1, sizeof(xsltKeyDef));
114    xmlFree(keyd);
115}
116
117/**
118 * xsltFreeKeyDefList:
119 * @keyd:  an XSLT key definition list
120 *
121 * Free up the memory allocated by all the elements of @keyd
122 */
123static void
124xsltFreeKeyDefList(xsltKeyDefPtr keyd) {
125    xsltKeyDefPtr cur;
126
127    while (keyd != NULL) {
128        cur = keyd;
129        keyd = keyd->next;
130        xsltFreeKeyDef(cur);
131    }
132}
133
134/**
135 * xsltNewKeyTable:
136 * @name:  the key name or NULL
137 * @nameURI:  the name URI or NULL
138 *
139 * Create a new XSLT KeyTable
140 *
141 * Returns the newly allocated xsltKeyTablePtr or NULL in case of error
142 */
143static xsltKeyTablePtr
144xsltNewKeyTable(const xmlChar *name, const xmlChar *nameURI) {
145    xsltKeyTablePtr cur;
146
147    cur = (xsltKeyTablePtr) xmlMalloc(sizeof(xsltKeyTable));
148    if (cur == NULL) {
149        xsltTransformError(NULL, NULL, NULL,
150                "xsltNewKeyTable : malloc failed\n");
151        return(NULL);
152    }
153    memset(cur, 0, sizeof(xsltKeyTable));
154    if (name != NULL)
155        cur->name = xmlStrdup(name);
156    if (nameURI != NULL)
157        cur->nameURI = xmlStrdup(nameURI);
158    cur->keys = xmlHashCreate(0);
159    return(cur);
160}
161
162/**
163 * xsltFreeKeyTable:
164 * @keyt:  an XSLT key table
165 *
166 * Free up the memory allocated by @keyt
167 */
168static void
169xsltFreeKeyTable(xsltKeyTablePtr keyt) {
170    if (keyt == NULL)
171        return;
172    if (keyt->name != NULL)
173        xmlFree(keyt->name);
174    if (keyt->nameURI != NULL)
175        xmlFree(keyt->nameURI);
176    if (keyt->keys != NULL)
177        xmlHashFree(keyt->keys,
178                    (xmlHashDeallocator) xmlXPathFreeNodeSet);
179    memset(keyt, -1, sizeof(xsltKeyTable));
180    xmlFree(keyt);
181}
182
183/**
184 * xsltFreeKeyTableList:
185 * @keyt:  an XSLT key table list
186 *
187 * Free up the memory allocated by all the elements of @keyt
188 */
189static void
190xsltFreeKeyTableList(xsltKeyTablePtr keyt) {
191    xsltKeyTablePtr cur;
192
193    while (keyt != NULL) {
194        cur = keyt;
195        keyt = keyt->next;
196        xsltFreeKeyTable(cur);
197    }
198}
199
200/************************************************************************
201 *                                                                      *
202 *              The interpreter for the precompiled patterns            *
203 *                                                                      *
204 ************************************************************************/
205
206
207/**
208 * xsltFreeKeys:
209 * @style: an XSLT stylesheet
210 *
211 * Free up the memory used by XSLT keys in a stylesheet
212 */
213void
214xsltFreeKeys(xsltStylesheetPtr style) {
215    if (style->keys)
216        xsltFreeKeyDefList((xsltKeyDefPtr) style->keys);
217}
218
219/**
220 * xsltAddKey:
221 * @style: an XSLT stylesheet
222 * @name:  the key name or NULL
223 * @nameURI:  the name URI or NULL
224 * @match:  the match value
225 * @use:  the use value
226 * @inst: the key instruction
227 *
228 * add a key definition to a stylesheet
229 *
230 * Returns 0 in case of success, and -1 in case of failure.
231 */
232int     
233xsltAddKey(xsltStylesheetPtr style, const xmlChar *name,
234           const xmlChar *nameURI, const xmlChar *match,
235           const xmlChar *use, xmlNodePtr inst) {
236    xsltKeyDefPtr key;
237    xmlChar *pattern = NULL;
238    int current, end, start;
239
240    if ((style == NULL) || (name == NULL) || (match == NULL) || (use == NULL))
241        return(-1);
242
243#ifdef WITH_XSLT_DEBUG_KEYS
244    xsltGenericDebug(xsltGenericDebugContext,
245        "Add key %s, match %s, use %s\n", name, match, use);
246#endif
247
248    key = xsltNewKeyDef(name, nameURI);
249    key->match = xmlStrdup(match);
250    key->use = xmlStrdup(use);
251    key->inst = inst;
252
253    /*
254     * Split the | and register it as as many keys
255     */
256    current = end = 0;
257    while (match[current] != 0) {
258        start = current;
259        while (IS_BLANK(match[current]))
260            current++;
261        end = current;
262        while ((match[end] != 0) && (match[end] != '|')) {
263            end++;
264        }
265        if (current == end) {
266            xsltTransformError(NULL, style, inst,
267                             "key pattern is empty\n");
268            if (style != NULL) style->errors++;
269            goto error;
270        }
271        if (match[start] != '/') {
272            pattern = xmlStrcat(pattern, (xmlChar *)"//");
273            if (pattern == NULL) {
274                if (style != NULL) style->errors++;
275                goto error;
276            }
277        }
278        pattern = xmlStrncat(pattern, &match[start], end - start);
279        if (pattern == NULL) {
280            if (style != NULL) style->errors++;
281            goto error;
282        }
283
284        if (match[end] == '|') {
285            pattern = xmlStrcat(pattern, (xmlChar *)"|");
286            end++;
287        }
288        current = end;
289    }
290#ifdef WITH_XSLT_DEBUG_KEYS
291    xsltGenericDebug(xsltGenericDebugContext,
292        "   resulting pattern %s\n", pattern);
293#endif
294    key->comp = xmlXPathCompile(pattern);
295    if (key->comp == NULL) {
296        xsltTransformError(NULL, style, inst,
297                "xsl:key : XPath pattern compilation failed '%s'\n",
298                         pattern);
299        if (style != NULL) style->errors++;
300    }
301    key->usecomp = xmlXPathCompile(use);
302    if (key->usecomp == NULL) {
303        xsltTransformError(NULL, style, inst,
304                "xsl:key : XPath pattern compilation failed '%s'\n",
305                         use);
306        if (style != NULL) style->errors++;
307    }
308    key->next = style->keys;
309    style->keys = key;
310error:
311    if (pattern != NULL)
312        xmlFree(pattern);
313    return(0);
314}
315
316/**
317 * xsltGetKey:
318 * @ctxt: an XSLT transformation context
319 * @name:  the key name or NULL
320 * @nameURI:  the name URI or NULL
321 * @value:  the key value to look for
322 *
323 * Lookup a key
324 *
325 * Returns the nodeset resulting from the query or NULL
326 */
327xmlNodeSetPtr
328xsltGetKey(xsltTransformContextPtr ctxt, const xmlChar *name,
329           const xmlChar *nameURI, const xmlChar *value) {
330    xmlNodeSetPtr ret;
331    xsltKeyTablePtr table;
332
333    if ((ctxt == NULL) || (name == NULL) || (value == NULL))
334        return(NULL);
335
336#ifdef WITH_XSLT_DEBUG_KEYS
337    xsltGenericDebug(xsltGenericDebugContext,
338        "Get key %s, value %s\n", name, value);
339#endif
340    table = (xsltKeyTablePtr) ctxt->document->keys;
341    while (table != NULL) {
342        if (xmlStrEqual(table->name, name) &&
343            (((nameURI == NULL) && (table->nameURI == NULL)) ||
344             ((nameURI != NULL) && (table->nameURI != NULL) &&
345              (xmlStrEqual(table->nameURI, nameURI))))) {
346            ret = (xmlNodeSetPtr)xmlHashLookup(table->keys, value);
347            return(ret);
348        }
349        table = table->next;
350    }
351    return(NULL);
352}
353
354/**
355 * xsltEvalXPathKeys:
356 * @ctxt:  the XSLT transformation context
357 * @comp:  the compiled XPath expression
358 *
359 * Process the expression using XPath to get the list of keys
360 *
361 * Returns the array of computed string value or NULL, must be deallocated
362 *         by the caller.
363 */
364static xmlChar **
365xsltEvalXPathKeys(xsltTransformContextPtr ctxt, xmlXPathCompExprPtr comp) {
366    xmlChar **ret = NULL;
367    xmlXPathObjectPtr res;
368    xmlNodePtr oldInst;
369    xmlNodePtr oldNode;
370    int oldPos, oldSize;
371    int oldNsNr;
372    xmlNsPtr *oldNamespaces;
373
374    oldInst = ctxt->inst;
375    oldNode = ctxt->node;
376    oldPos = ctxt->xpathCtxt->proximityPosition;
377    oldSize = ctxt->xpathCtxt->contextSize;
378    oldNsNr = ctxt->xpathCtxt->nsNr;
379    oldNamespaces = ctxt->xpathCtxt->namespaces;
380
381    ctxt->xpathCtxt->node = ctxt->node;
382    /* TODO: do we need to propagate the namespaces here ? */
383    ctxt->xpathCtxt->namespaces = NULL;
384    ctxt->xpathCtxt->nsNr = 0;
385    res = xmlXPathCompiledEval(comp, ctxt->xpathCtxt);
386    if (res != NULL) {
387        if (res->type == XPATH_NODESET) {
388            int len, i, j;
389
390            if (res->nodesetval != NULL)
391                len = res->nodesetval->nodeNr;
392            else
393                len = 0;
394            if (len != 0) {
395                ret = (xmlChar **) xmlMalloc((len + 1) * sizeof(xmlChar *));
396                if (ret != NULL) {
397                    for (i = 0,j = 0;i < len;i++) {
398                        ret[j] = xmlXPathCastNodeToString(
399                                res->nodesetval->nodeTab[i]);
400                        if (ret[j] != NULL)
401                            j++;
402                    }
403                    ret[j] = NULL;
404                }
405            }
406        } else {
407            if (res->type != XPATH_STRING)
408                res = xmlXPathConvertString(res);
409            if (res->type == XPATH_STRING) {
410                ret = (xmlChar **) xmlMalloc(2 * sizeof(xmlChar *));
411                if (ret != NULL) {
412                    ret[0] = res->stringval;
413                    ret[1] = NULL;
414                    res->stringval = NULL;
415                }
416            } else {
417                xsltTransformError(ctxt, NULL, NULL,
418                     "xpath : string() function didn't return a String\n");
419            }
420        }
421        xmlXPathFreeObject(res);
422    } else {
423        ctxt->state = XSLT_STATE_STOPPED;
424    }
425#ifdef WITH_XSLT_DEBUG_TEMPLATES
426    xsltGenericDebug(xsltGenericDebugContext,
427         "xsltEvalXPathString: returns %s\n", ret);
428#endif
429    ctxt->inst = oldInst;
430    ctxt->node = oldNode;
431    ctxt->xpathCtxt->contextSize = oldSize;
432    ctxt->xpathCtxt->proximityPosition = oldPos;
433    ctxt->xpathCtxt->nsNr = oldNsNr;
434    ctxt->xpathCtxt->namespaces = oldNamespaces;
435    return(ret);
436}
437/**
438 * xsltInitCtxtKey:
439 * @ctxt: an XSLT transformation context
440 * @doc:  an XSLT document
441 * @keyd: the key definition
442 *
443 * Computes the key tables this key and for the current input document.
444 */
445static void
446xsltInitCtxtKey(xsltTransformContextPtr ctxt, xsltDocumentPtr doc,
447                xsltKeyDefPtr keyd) {
448    int i;
449    xmlNodeSetPtr nodelist = NULL, keylist;
450    xmlXPathObjectPtr res = NULL;
451    xmlChar *str, **list;
452    xsltKeyTablePtr table;
453    int oldPos, oldSize;
454    xmlNodePtr oldInst;
455    xsltDocumentPtr oldDoc;
456    xmlDocPtr oldXDoc;
457    int oldNsNr;
458    xmlNsPtr *oldNamespaces;
459
460    /*
461     * Evaluate the nodelist
462     */
463
464    oldXDoc= ctxt->xpathCtxt->doc;
465    oldPos = ctxt->xpathCtxt->proximityPosition;
466    oldSize = ctxt->xpathCtxt->contextSize;
467    oldInst = ctxt->inst;
468    oldDoc = ctxt->document;
469    oldNsNr = ctxt->xpathCtxt->nsNr;
470    oldNamespaces = ctxt->xpathCtxt->namespaces;
471
472    if (keyd->comp == NULL)
473        goto error;
474    if (keyd->usecomp == NULL)
475        goto error;
476
477    ctxt->document = doc;
478    ctxt->xpathCtxt->doc = doc->doc;
479    ctxt->xpathCtxt->node = (xmlNodePtr) doc->doc;
480    ctxt->node = (xmlNodePtr) doc->doc;
481    /* TODO : clarify the use of namespaces in keys evaluation */
482    ctxt->xpathCtxt->namespaces = NULL;
483    ctxt->xpathCtxt->nsNr = 0;
484    ctxt->inst = keyd->inst;
485    res = xmlXPathCompiledEval(keyd->comp, ctxt->xpathCtxt);
486    ctxt->xpathCtxt->contextSize = oldSize;
487    ctxt->xpathCtxt->proximityPosition = oldPos;
488    ctxt->inst = oldInst;
489
490    if (res != NULL) {
491        if (res->type == XPATH_NODESET) {
492            nodelist = res->nodesetval;
493#ifdef WITH_XSLT_DEBUG_KEYS
494            if (nodelist != NULL)
495                xsltGenericDebug(xsltGenericDebugContext,
496                     "xsltInitCtxtKey: %s evaluates to %d nodes\n",
497                                 keyd->match, nodelist->nodeNr);
498#endif
499        } else {
500#ifdef WITH_XSLT_DEBUG_KEYS
501            xsltGenericDebug(xsltGenericDebugContext,
502                 "xsltInitCtxtKey: %s is not a node set\n", keyd->match);
503#endif
504            goto error;
505        }
506    } else {
507#ifdef WITH_XSLT_DEBUG_KEYS
508        xsltGenericDebug(xsltGenericDebugContext,
509             "xsltInitCtxtKey: %s evaluation failed\n", keyd->match);
510#endif
511        ctxt->state = XSLT_STATE_STOPPED;
512        goto error;
513    }
514
515    /*
516     * for each node in the list evaluate the key and insert the node
517     */
518    if ((nodelist == NULL) || (nodelist->nodeNr <= 0))
519        goto error;
520
521    table = xsltNewKeyTable(keyd->name, keyd->nameURI);
522    if (table == NULL)
523        goto error;
524
525    for (i = 0;i < nodelist->nodeNr;i++) {
526        if (IS_XSLT_REAL_NODE(nodelist->nodeTab[i])) {
527            ctxt->node = nodelist->nodeTab[i];
528
529            list = xsltEvalXPathKeys(ctxt, keyd->usecomp);
530            if (list != NULL) {
531                int index = 0;
532
533                str = list[index++];
534                while (str != NULL) {
535#ifdef WITH_XSLT_DEBUG_KEYS
536                    xsltGenericDebug(xsltGenericDebugContext,
537                         "xsl:key : node associated to(%s,%s)\n",
538                                     keyd->name, str);
539#endif
540                    keylist = xmlHashLookup(table->keys, str);
541                    if (keylist == NULL) {
542                        keylist = xmlXPathNodeSetCreate(nodelist->nodeTab[i]);
543                        xmlHashAddEntry(table->keys, str, keylist);
544                    } else {
545                        xmlXPathNodeSetAdd(keylist, nodelist->nodeTab[i]);
546                    }
547                    nodelist->nodeTab[i]->_private = keyd;
548                    xmlFree(str);
549                    str = list[index++];
550                }
551                xmlFree(list);
552#ifdef WITH_XSLT_DEBUG_KEYS
553            } else {
554                xsltGenericDebug(xsltGenericDebugContext,
555                     "xsl:key : use %s failed to return strings\n",
556                                 keyd->use);
557#endif
558            }
559        }
560    }
561
562    table->next = doc->keys;
563    doc->keys = table;
564
565error:
566    ctxt->document = oldDoc;
567    ctxt->xpathCtxt->doc = oldXDoc;
568    ctxt->xpathCtxt->nsNr = oldNsNr;
569    ctxt->xpathCtxt->namespaces = oldNamespaces;
570    if (res != NULL)
571        xmlXPathFreeObject(res);
572}
573
574/**
575 * xsltInitCtxtKeys:
576 * @ctxt:  an XSLT transformation context
577 * @doc:  an XSLT document
578 *
579 * Computes all the keys tables for the current input document.
580 * Should be done before global varibales are initialized.
581 */
582void
583xsltInitCtxtKeys(xsltTransformContextPtr ctxt, xsltDocumentPtr doc) {
584    xsltStylesheetPtr style;
585    xsltKeyDefPtr keyd;
586
587    if ((ctxt == NULL) || (doc == NULL))
588        return;
589#ifdef WITH_XSLT_DEBUG_KEYS
590    if ((doc->doc != NULL) && (doc->doc->URL != NULL))
591        xsltGenericDebug(xsltGenericDebugContext, "Initializing keys on %s\n",
592                     doc->doc->URL);
593#endif
594    style = ctxt->style;
595    while (style != NULL) {
596        keyd = (xsltKeyDefPtr) style->keys;
597        while (keyd != NULL) {
598            xsltInitCtxtKey(ctxt, doc, keyd);
599
600            keyd = keyd->next;
601        }
602
603        style = xsltNextImport(style);
604    }
605}
606
607/**
608 * xsltFreeDocumentKeys:
609 * @doc: a XSLT document
610 *
611 * Free the keys associated to a document
612 */
613void   
614xsltFreeDocumentKeys(xsltDocumentPtr doc) {
615    if (doc != NULL)
616        xsltFreeKeyTableList(doc->keys);
617}
618
Note: See TracBrowser for help on using the repository browser.