preparing 1.0.14 updated rebuilt implemented the IN_LIBXSLT and
[platform/upstream/libxslt.git] / libxslt / keys.c
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
35 typedef struct _xsltKeyDef xsltKeyDef;
36 typedef xsltKeyDef *xsltKeyDefPtr;
37 struct _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
48 typedef struct _xsltKeyTable xsltKeyTable;
49 typedef xsltKeyTable *xsltKeyTablePtr;
50 struct _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  */
73 static xsltKeyDefPtr
74 xsltNewKeyDef(const xmlChar *name, const xmlChar *nameURI) {
75     xsltKeyDefPtr cur;
76
77     cur = (xsltKeyDefPtr) xmlMalloc(sizeof(xsltKeyDef));
78     if (cur == NULL) {
79         xsltPrintErrorContext(NULL, NULL, NULL);
80         xsltGenericError(xsltGenericErrorContext,
81                 "xsltNewKeyDef : malloc failed\n");
82         return(NULL);
83     }
84     memset(cur, 0, sizeof(xsltKeyDef));
85     if (name != NULL)
86         cur->name = xmlStrdup(name);
87     if (nameURI != NULL)
88         cur->nameURI = xmlStrdup(nameURI);
89     return(cur);
90 }
91
92 /**
93  * xsltFreeKeyDef:
94  * @keyd:  an XSLT key definition
95  *
96  * Free up the memory allocated by @keyd
97  */
98 static void
99 xsltFreeKeyDef(xsltKeyDefPtr keyd) {
100     if (keyd == NULL)
101         return;
102     if (keyd->comp != NULL)
103         xmlXPathFreeCompExpr(keyd->comp);
104     if (keyd->usecomp != NULL)
105         xmlXPathFreeCompExpr(keyd->usecomp);
106     if (keyd->name != NULL)
107         xmlFree(keyd->name);
108     if (keyd->nameURI != NULL)
109         xmlFree(keyd->nameURI);
110     if (keyd->match != NULL)
111         xmlFree(keyd->match);
112     if (keyd->use != NULL)
113         xmlFree(keyd->use);
114     memset(keyd, -1, sizeof(xsltKeyDef));
115     xmlFree(keyd);
116 }
117
118 /**
119  * xsltFreeKeyDefList:
120  * @keyd:  an XSLT key definition list
121  *
122  * Free up the memory allocated by all the elements of @keyd
123  */
124 static void
125 xsltFreeKeyDefList(xsltKeyDefPtr keyd) {
126     xsltKeyDefPtr cur;
127
128     while (keyd != NULL) {
129         cur = keyd;
130         keyd = keyd->next;
131         xsltFreeKeyDef(cur);
132     }
133 }
134
135 /**
136  * xsltNewKeyTable:
137  * @name:  the key name or NULL
138  * @nameURI:  the name URI or NULL
139  *
140  * Create a new XSLT KeyTable
141  *
142  * Returns the newly allocated xsltKeyTablePtr or NULL in case of error
143  */
144 static xsltKeyTablePtr
145 xsltNewKeyTable(const xmlChar *name, const xmlChar *nameURI) {
146     xsltKeyTablePtr cur;
147
148     cur = (xsltKeyTablePtr) xmlMalloc(sizeof(xsltKeyTable));
149     if (cur == NULL) {
150         xsltPrintErrorContext(NULL, NULL, NULL);
151         xsltGenericError(xsltGenericErrorContext,
152                 "xsltNewKeyTable : malloc failed\n");
153         return(NULL);
154     }
155     memset(cur, 0, sizeof(xsltKeyTable));
156     if (name != NULL)
157         cur->name = xmlStrdup(name);
158     if (nameURI != NULL)
159         cur->nameURI = xmlStrdup(nameURI);
160     cur->keys = xmlHashCreate(0);
161     return(cur);
162 }
163
164 /**
165  * xsltFreeKeyTable:
166  * @keyt:  an XSLT key table
167  *
168  * Free up the memory allocated by @keyt
169  */
170 static void
171 xsltFreeKeyTable(xsltKeyTablePtr keyt) {
172     if (keyt == NULL)
173         return;
174     if (keyt->name != NULL)
175         xmlFree(keyt->name);
176     if (keyt->nameURI != NULL)
177         xmlFree(keyt->nameURI);
178     if (keyt->keys != NULL)
179         xmlHashFree(keyt->keys, 
180                     (xmlHashDeallocator) xmlXPathFreeNodeSet);
181     memset(keyt, -1, sizeof(xsltKeyTable));
182     xmlFree(keyt);
183 }
184
185 /**
186  * xsltFreeKeyTableList:
187  * @keyt:  an XSLT key table list
188  *
189  * Free up the memory allocated by all the elements of @keyt
190  */
191 static void
192 xsltFreeKeyTableList(xsltKeyTablePtr keyt) {
193     xsltKeyTablePtr cur;
194
195     while (keyt != NULL) {
196         cur = keyt;
197         keyt = keyt->next;
198         xsltFreeKeyTable(cur);
199     }
200 }
201
202 /************************************************************************
203  *                                                                      *
204  *              The interpreter for the precompiled patterns            *
205  *                                                                      *
206  ************************************************************************/
207
208
209 /**
210  * xsltFreeKeys:
211  * @style: an XSLT stylesheet
212  *
213  * Free up the memory used by XSLT keys in a stylesheet
214  */
215 void
216 xsltFreeKeys(xsltStylesheetPtr style) {
217     if (style->keys)
218         xsltFreeKeyDefList((xsltKeyDefPtr) style->keys);
219 }
220
221 /**
222  * xsltAddKey:
223  * @style: an XSLT stylesheet
224  * @name:  the key name or NULL
225  * @nameURI:  the name URI or NULL
226  * @match:  the match value
227  * @use:  the use value
228  * @inst: the key instruction
229  *
230  * add a key definition to a stylesheet
231  *
232  * Returns 0 in case of success, and -1 in case of failure.
233  */
234 int     
235 xsltAddKey(xsltStylesheetPtr style, const xmlChar *name,
236            const xmlChar *nameURI, const xmlChar *match,
237            const xmlChar *use, xmlNodePtr inst) {
238     xsltKeyDefPtr key;
239     xmlChar *pattern = NULL;
240     int current, end, start;
241
242     if ((style == NULL) || (name == NULL) || (match == NULL) || (use == NULL))
243         return(-1);
244
245 #ifdef WITH_XSLT_DEBUG_KEYS
246     xsltGenericDebug(xsltGenericDebugContext,
247         "Add key %s, match %s, use %s\n", name, match, use);
248 #endif
249
250     key = xsltNewKeyDef(name, nameURI);
251     key->match = xmlStrdup(match);
252     key->use = xmlStrdup(use);
253     key->inst = inst;
254
255     /*
256      * Split the | and register it as as many keys
257      */
258     current = end = 0;
259     while (match[current] != 0) {
260         start = current;
261         while (IS_BLANK(match[current]))
262             current++;
263         end = current;
264         while ((match[end] != 0) && (match[end] != '|')) {
265             end++;
266         }
267         if (current == end) {
268             xsltPrintErrorContext(NULL, style, inst);
269             xsltGenericError(xsltGenericErrorContext,
270                              "key pattern is empty\n");
271             style->errors++;
272             goto error;
273         }
274         if (match[start] != '/') {
275             pattern = xmlStrcat(pattern, (xmlChar *)"//");
276             if (pattern == NULL) {
277                 style->errors++;
278                 goto error;
279             }
280         }
281         pattern = xmlStrncat(pattern, &match[start], end - start);
282         if (pattern == NULL) {
283             style->errors++;
284             goto error;
285         }
286
287         if (match[end] == '|') {
288             pattern = xmlStrcat(pattern, (xmlChar *)"|");
289             end++;
290         }
291         current = end;
292     }
293 #ifdef WITH_XSLT_DEBUG_KEYS
294     xsltGenericDebug(xsltGenericDebugContext,
295         "   resulting pattern %s\n", pattern);
296 #endif
297     key->comp = xmlXPathCompile(pattern);
298     if (key->comp == NULL) {
299         xsltPrintErrorContext(NULL, style, inst);
300         xsltGenericError(xsltGenericErrorContext,
301                 "xsl:key : XPath pattern compilation failed '%s'\n",
302                          pattern);
303         style->errors++;
304     }
305     key->usecomp = xmlXPathCompile(use);
306     if (key->usecomp == NULL) {
307         xsltPrintErrorContext(NULL, style, inst);
308         xsltGenericError(xsltGenericErrorContext,
309                 "xsl:key : XPath pattern compilation failed '%s'\n",
310                          use);
311         style->errors++;
312     }
313     key->next = style->keys;
314     style->keys = key;
315 error:
316     if (pattern != NULL)
317         xmlFree(pattern);
318     return(0);
319 }
320
321 /**
322  * xsltGetKey:
323  * @ctxt: an XSLT transformation context
324  * @name:  the key name or NULL
325  * @nameURI:  the name URI or NULL
326  * @value:  the key value to look for
327  *
328  * Lookup a key
329  *
330  * Returns the nodeset resulting from the query or NULL
331  */
332 xmlNodeSetPtr
333 xsltGetKey(xsltTransformContextPtr ctxt, const xmlChar *name,
334            const xmlChar *nameURI, const xmlChar *value) {
335     xmlNodeSetPtr ret;
336     xsltKeyTablePtr table;
337
338     if ((ctxt == NULL) || (name == NULL) || (value == NULL))
339         return(NULL);
340
341 #ifdef WITH_XSLT_DEBUG_KEYS
342     xsltGenericDebug(xsltGenericDebugContext,
343         "Get key %s, value %s\n", name, value);
344 #endif
345     table = (xsltKeyTablePtr) ctxt->document->keys;
346     while (table != NULL) {
347         if (xmlStrEqual(table->name, name) &&
348             (((nameURI == NULL) && (table->nameURI == NULL)) ||
349              ((nameURI != NULL) && (table->nameURI != NULL) &&
350               (xmlStrEqual(table->nameURI, nameURI))))) {
351             ret = (xmlNodeSetPtr)xmlHashLookup(table->keys, value);
352             return(ret);
353         }
354         table = table->next;
355     }
356     return(NULL);
357 }
358
359 /**
360  * xsltInitCtxtKey:
361  * @ctxt: an XSLT transformation context
362  * @doc:  an XSLT document
363  * @keyd: the key definition
364  *
365  * Computes the key tables this key and for the current input document.
366  */
367 static void
368 xsltInitCtxtKey(xsltTransformContextPtr ctxt, xsltDocumentPtr doc,
369                 xsltKeyDefPtr keyd) {
370     int i;
371     xmlNodeSetPtr nodelist = NULL, keylist;
372     xmlXPathObjectPtr res = NULL;
373     xmlChar *str;
374     xsltKeyTablePtr table;
375     int oldPos, oldSize;
376     xmlNodePtr oldInst;
377     xsltDocumentPtr oldDoc;
378     xmlDocPtr oldXDoc;
379     int oldNsNr;
380     xmlNsPtr *oldNamespaces;
381
382     /*
383      * Evaluate the nodelist
384      */
385
386     oldXDoc= ctxt->xpathCtxt->doc;
387     oldPos = ctxt->xpathCtxt->proximityPosition;
388     oldSize = ctxt->xpathCtxt->contextSize;
389     oldInst = ctxt->inst;
390     oldDoc = ctxt->document;
391     oldNsNr = ctxt->xpathCtxt->nsNr;
392     oldNamespaces = ctxt->xpathCtxt->namespaces;
393
394     if (keyd->comp == NULL)
395         goto error;
396     if (keyd->usecomp == NULL)
397         goto error;
398
399     ctxt->document = doc;
400     ctxt->xpathCtxt->doc = doc->doc;
401     ctxt->xpathCtxt->node = (xmlNodePtr) doc->doc;
402     ctxt->node = (xmlNodePtr) doc->doc;
403     /* TODO : clarify the use of namespaces in keys evaluation */
404     ctxt->xpathCtxt->namespaces = NULL;
405     ctxt->xpathCtxt->nsNr = 0;
406     ctxt->inst = keyd->inst;
407     res = xmlXPathCompiledEval(keyd->comp, ctxt->xpathCtxt);
408     ctxt->xpathCtxt->contextSize = oldSize;
409     ctxt->xpathCtxt->proximityPosition = oldPos;
410     ctxt->inst = oldInst;
411
412     if (res != NULL) {
413         if (res->type == XPATH_NODESET) {
414             nodelist = res->nodesetval;
415 #ifdef WITH_XSLT_DEBUG_KEYS
416             if (nodelist != NULL)
417                 xsltGenericDebug(xsltGenericDebugContext,
418                      "xsltInitCtxtKey: %s evaluates to %d nodes\n",
419                                  keyd->match, nodelist->nodeNr);
420 #endif
421         } else {
422 #ifdef WITH_XSLT_DEBUG_KEYS
423             xsltGenericDebug(xsltGenericDebugContext,
424                  "xsltInitCtxtKey: %s is not a node set\n", keyd->match);
425 #endif
426             goto error;
427         }
428     } else {
429 #ifdef WITH_XSLT_DEBUG_KEYS
430         xsltGenericDebug(xsltGenericDebugContext,
431              "xsltInitCtxtKey: %s evaluation failed\n", keyd->match);
432 #endif
433         ctxt->state = XSLT_STATE_STOPPED;
434         goto error;
435     }
436
437     /*
438      * for each node in the list evaluate the key and insert the node
439      */
440     if ((nodelist == NULL) || (nodelist->nodeNr <= 0))
441         goto error;
442
443     table = xsltNewKeyTable(keyd->name, keyd->nameURI);
444     if (table == NULL)
445         goto error;
446
447     for (i = 0;i < nodelist->nodeNr;i++) {
448         if (IS_XSLT_REAL_NODE(nodelist->nodeTab[i])) {
449             ctxt->node = nodelist->nodeTab[i];
450             str = xsltEvalXPathString(ctxt, keyd->usecomp);
451             if (str != NULL) {
452 #ifdef WITH_XSLT_DEBUG_KEYS
453                 xsltGenericDebug(xsltGenericDebugContext,
454                      "xsl:key : node associated to(%s,%s)\n",
455                                  keyd->name, str);
456 #endif
457                 keylist = xmlHashLookup(table->keys, str);
458                 if (keylist == NULL) {
459                     keylist = xmlXPathNodeSetCreate(nodelist->nodeTab[i]);
460                     xmlHashAddEntry(table->keys, str, keylist);
461                 } else {
462                     xmlXPathNodeSetAdd(keylist, nodelist->nodeTab[i]);
463                 }
464                 nodelist->nodeTab[i]->_private = keyd;
465                 xmlFree(str);
466 #ifdef WITH_XSLT_DEBUG_KEYS
467             } else {
468                 xsltGenericDebug(xsltGenericDebugContext,
469                      "xsl:key : use %s failed to return a string\n",
470                                  keyd->use);
471 #endif
472             }
473         }
474     }
475
476     table->next = doc->keys;
477     doc->keys = table;
478
479 error:
480     ctxt->document = oldDoc;
481     ctxt->xpathCtxt->doc = oldXDoc;
482     ctxt->xpathCtxt->nsNr = oldNsNr;
483     ctxt->xpathCtxt->namespaces = oldNamespaces;
484     if (res != NULL)
485         xmlXPathFreeObject(res);
486 }
487
488 /**
489  * xsltInitCtxtKeys:
490  * @ctxt:  an XSLT transformation context
491  * @doc:  an XSLT document
492  *
493  * Computes all the keys tables for the current input document.
494  * Should be done before global varibales are initialized.
495  */
496 void
497 xsltInitCtxtKeys(xsltTransformContextPtr ctxt, xsltDocumentPtr doc) {
498     xsltStylesheetPtr style;
499     xsltKeyDefPtr keyd;
500
501     if ((ctxt == NULL) || (doc == NULL))
502         return;
503 #ifdef WITH_XSLT_DEBUG_KEYS
504     if ((doc->doc != NULL) && (doc->doc->URL != NULL))
505         xsltGenericDebug(xsltGenericDebugContext, "Initializing keys on %s\n",
506                      doc->doc->URL);
507 #endif
508     style = ctxt->style;
509     while (style != NULL) {
510         keyd = (xsltKeyDefPtr) style->keys;
511         while (keyd != NULL) {
512             xsltInitCtxtKey(ctxt, doc, keyd);
513
514             keyd = keyd->next;
515         }
516
517         style = xsltNextImport(style);
518     }
519 }
520
521 /**
522  * xsltFreeDocumentKeys:
523  * @doc: a XSLT document
524  *
525  * Free the keys associated to a document
526  */
527 void    
528 xsltFreeDocumentKeys(xsltDocumentPtr doc) {
529     if (doc != NULL)
530         xsltFreeKeyTableList(doc->keys);
531 }
532