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 <libxml/xpath.h>
25 #include "xslt.h"
26 #include "xsltInternals.h"
27 #include "xsltutils.h"
28 #include "imports.h"
29 #include "templates.h"
30 #include "keys.h"
31
32 #ifdef WITH_XSLT_DEBUG
33 #define WITH_XSLT_DEBUG_KEYS
34 #endif
35
36 static int
37 xsltInitDocKeyTable(xsltTransformContextPtr ctxt, const xmlChar *name,
38 const xmlChar *nameURI);
39
40 /************************************************************************
41 * *
42 * Type functions *
43 * *
44 ************************************************************************/
45
46 /**
47 * xsltNewKeyDef:
48 * @name: the key name or NULL
49 * @nameURI: the name URI or NULL
50 *
51 * Create a new XSLT KeyDef
52 *
53 * Returns the newly allocated xsltKeyDefPtr or NULL in case of error
54 */
55 static xsltKeyDefPtr
xsltNewKeyDef(const xmlChar * name,const xmlChar * nameURI)56 xsltNewKeyDef(const xmlChar *name, const xmlChar *nameURI) {
57 xsltKeyDefPtr cur;
58
59 cur = (xsltKeyDefPtr) xmlMalloc(sizeof(xsltKeyDef));
60 if (cur == NULL) {
61 xsltTransformError(NULL, NULL, NULL,
62 "xsltNewKeyDef : malloc failed\n");
63 return(NULL);
64 }
65 memset(cur, 0, sizeof(xsltKeyDef));
66 if (name != NULL)
67 cur->name = xmlStrdup(name);
68 if (nameURI != NULL)
69 cur->nameURI = xmlStrdup(nameURI);
70 cur->nsList = NULL;
71 return(cur);
72 }
73
74 /**
75 * xsltFreeKeyDef:
76 * @keyd: an XSLT key definition
77 *
78 * Free up the memory allocated by @keyd
79 */
80 static void
xsltFreeKeyDef(xsltKeyDefPtr keyd)81 xsltFreeKeyDef(xsltKeyDefPtr keyd) {
82 if (keyd == NULL)
83 return;
84 if (keyd->comp != NULL)
85 xmlXPathFreeCompExpr(keyd->comp);
86 if (keyd->usecomp != NULL)
87 xmlXPathFreeCompExpr(keyd->usecomp);
88 if (keyd->name != NULL)
89 xmlFree(keyd->name);
90 if (keyd->nameURI != NULL)
91 xmlFree(keyd->nameURI);
92 if (keyd->match != NULL)
93 xmlFree(keyd->match);
94 if (keyd->use != NULL)
95 xmlFree(keyd->use);
96 if (keyd->nsList != NULL)
97 xmlFree(keyd->nsList);
98 memset(keyd, -1, sizeof(xsltKeyDef));
99 xmlFree(keyd);
100 }
101
102 /**
103 * xsltFreeKeyDefList:
104 * @keyd: an XSLT key definition list
105 *
106 * Free up the memory allocated by all the elements of @keyd
107 */
108 static void
xsltFreeKeyDefList(xsltKeyDefPtr keyd)109 xsltFreeKeyDefList(xsltKeyDefPtr keyd) {
110 xsltKeyDefPtr cur;
111
112 while (keyd != NULL) {
113 cur = keyd;
114 keyd = keyd->next;
115 xsltFreeKeyDef(cur);
116 }
117 }
118
119 /**
120 * xsltNewKeyTable:
121 * @name: the key name or NULL
122 * @nameURI: the name URI or NULL
123 *
124 * Create a new XSLT KeyTable
125 *
126 * Returns the newly allocated xsltKeyTablePtr or NULL in case of error
127 */
128 static xsltKeyTablePtr
xsltNewKeyTable(const xmlChar * name,const xmlChar * nameURI)129 xsltNewKeyTable(const xmlChar *name, const xmlChar *nameURI) {
130 xsltKeyTablePtr cur;
131
132 cur = (xsltKeyTablePtr) xmlMalloc(sizeof(xsltKeyTable));
133 if (cur == NULL) {
134 xsltTransformError(NULL, NULL, NULL,
135 "xsltNewKeyTable : malloc failed\n");
136 return(NULL);
137 }
138 memset(cur, 0, sizeof(xsltKeyTable));
139 if (name != NULL)
140 cur->name = xmlStrdup(name);
141 if (nameURI != NULL)
142 cur->nameURI = xmlStrdup(nameURI);
143 cur->keys = xmlHashCreate(0);
144 return(cur);
145 }
146
147 static void
xsltFreeNodeSetEntry(void * payload,const xmlChar * name ATTRIBUTE_UNUSED)148 xsltFreeNodeSetEntry(void *payload, const xmlChar *name ATTRIBUTE_UNUSED) {
149 xmlXPathFreeNodeSet((xmlNodeSetPtr) payload);
150 }
151
152 /**
153 * xsltFreeKeyTable:
154 * @keyt: an XSLT key table
155 *
156 * Free up the memory allocated by @keyt
157 */
158 static void
xsltFreeKeyTable(xsltKeyTablePtr keyt)159 xsltFreeKeyTable(xsltKeyTablePtr keyt) {
160 if (keyt == NULL)
161 return;
162 if (keyt->name != NULL)
163 xmlFree(keyt->name);
164 if (keyt->nameURI != NULL)
165 xmlFree(keyt->nameURI);
166 if (keyt->keys != NULL)
167 xmlHashFree(keyt->keys, xsltFreeNodeSetEntry);
168 memset(keyt, -1, sizeof(xsltKeyTable));
169 xmlFree(keyt);
170 }
171
172 /**
173 * xsltFreeKeyTableList:
174 * @keyt: an XSLT key table list
175 *
176 * Free up the memory allocated by all the elements of @keyt
177 */
178 static void
xsltFreeKeyTableList(xsltKeyTablePtr keyt)179 xsltFreeKeyTableList(xsltKeyTablePtr keyt) {
180 xsltKeyTablePtr cur;
181
182 while (keyt != NULL) {
183 cur = keyt;
184 keyt = keyt->next;
185 xsltFreeKeyTable(cur);
186 }
187 }
188
189 /************************************************************************
190 * *
191 * The interpreter for the precompiled patterns *
192 * *
193 ************************************************************************/
194
195
196 /**
197 * xsltFreeKeys:
198 * @style: an XSLT stylesheet
199 *
200 * Free up the memory used by XSLT keys in a stylesheet
201 */
202 void
xsltFreeKeys(xsltStylesheetPtr style)203 xsltFreeKeys(xsltStylesheetPtr style) {
204 if (style->keys)
205 xsltFreeKeyDefList((xsltKeyDefPtr) style->keys);
206 }
207
208 /**
209 * skipString:
210 * @cur: the current pointer
211 * @end: the current offset
212 *
213 * skip a string delimited by " or '
214 *
215 * Returns the byte after the string or -1 in case of error
216 */
217 static int
skipString(const xmlChar * cur,int end)218 skipString(const xmlChar *cur, int end) {
219 xmlChar limit;
220
221 if ((cur == NULL) || (end < 0)) return(-1);
222 if ((cur[end] == '\'') || (cur[end] == '"')) limit = cur[end];
223 else return(end);
224 end++;
225 while (cur[end] != 0) {
226 if (cur[end] == limit)
227 return(end + 1);
228 end++;
229 }
230 return(-1);
231 }
232
233 /**
234 * skipPredicate:
235 * @cur: the current pointer
236 * @end: the current offset
237 *
238 * skip a predicate
239 *
240 * Returns the byte after the predicate or -1 in case of error
241 */
242 static int
skipPredicate(const xmlChar * cur,int end)243 skipPredicate(const xmlChar *cur, int end) {
244 int level = 0;
245
246 if ((cur == NULL) || (end < 0)) return(-1);
247 if (cur[end] != '[') return(end);
248 end++;
249 while (cur[end] != 0) {
250 if ((cur[end] == '\'') || (cur[end] == '"')) {
251 end = skipString(cur, end);
252 if (end <= 0)
253 return(-1);
254 continue;
255 } else if (cur[end] == '[') {
256 level += 1;
257 } else if (cur[end] == ']') {
258 if (level == 0)
259 return(end + 1);
260 level -= 1;
261 }
262 end++;
263 }
264 return(-1);
265 }
266
267 /**
268 * xsltAddKey:
269 * @style: an XSLT stylesheet
270 * @name: the key name or NULL
271 * @nameURI: the name URI or NULL
272 * @match: the match value
273 * @use: the use value
274 * @inst: the key instruction
275 *
276 * add a key definition to a stylesheet
277 *
278 * Returns 0 in case of success, and -1 in case of failure.
279 */
280 int
xsltAddKey(xsltStylesheetPtr style,const xmlChar * name,const xmlChar * nameURI,const xmlChar * match,const xmlChar * use,xmlNodePtr inst)281 xsltAddKey(xsltStylesheetPtr style, const xmlChar *name,
282 const xmlChar *nameURI, const xmlChar *match,
283 const xmlChar *use, xmlNodePtr inst) {
284 xsltKeyDefPtr key;
285 xmlChar *pattern = NULL;
286 int current, end, start, i = 0;
287
288 if ((style == NULL) || (name == NULL) || (match == NULL) || (use == NULL))
289 return(-1);
290
291 #ifdef WITH_XSLT_DEBUG_KEYS
292 xsltGenericDebug(xsltGenericDebugContext,
293 "Add key %s, match %s, use %s\n", name, match, use);
294 #endif
295
296 key = xsltNewKeyDef(name, nameURI);
297 key->match = xmlStrdup(match);
298 key->use = xmlStrdup(use);
299 key->inst = inst;
300 key->nsList = xmlGetNsList(inst->doc, inst);
301 if (key->nsList != NULL) {
302 while (key->nsList[i] != NULL)
303 i++;
304 }
305 key->nsNr = i;
306
307 /*
308 * Split the | and register it as as many keys
309 */
310 current = end = 0;
311 while (match[current] != 0) {
312 start = current;
313 while (IS_BLANK_CH(match[current]))
314 current++;
315 end = current;
316 while ((match[end] != 0) && (match[end] != '|')) {
317 if (match[end] == '[') {
318 end = skipPredicate(match, end);
319 if (end <= 0) {
320 xsltTransformError(NULL, style, inst,
321 "xsl:key : 'match' pattern is malformed: %s",
322 key->match);
323 if (style != NULL) style->errors++;
324 goto error;
325 }
326 } else
327 end++;
328 }
329 if (current == end) {
330 xsltTransformError(NULL, style, inst,
331 "xsl:key : 'match' pattern is empty\n");
332 if (style != NULL) style->errors++;
333 goto error;
334 }
335 if (match[start] != '/') {
336 pattern = xmlStrcat(pattern, (xmlChar *)"//");
337 if (pattern == NULL) {
338 if (style != NULL) style->errors++;
339 goto error;
340 }
341 }
342 pattern = xmlStrncat(pattern, &match[start], end - start);
343 if (pattern == NULL) {
344 if (style != NULL) style->errors++;
345 goto error;
346 }
347
348 if (match[end] == '|') {
349 pattern = xmlStrcat(pattern, (xmlChar *)"|");
350 end++;
351 }
352 current = end;
353 }
354 if (pattern == NULL) {
355 xsltTransformError(NULL, style, inst,
356 "xsl:key : 'match' pattern is empty\n");
357 if (style != NULL) style->errors++;
358 goto error;
359 }
360 #ifdef WITH_XSLT_DEBUG_KEYS
361 xsltGenericDebug(xsltGenericDebugContext,
362 " resulting pattern %s\n", pattern);
363 #endif
364 /*
365 * XSLT-1: "It is an error for the value of either the use
366 * attribute or the match attribute to contain a
367 * VariableReference."
368 * TODO: We should report a variable-reference at compile-time.
369 * Maybe a search for "$", if it occurs outside of quotation
370 * marks, could be sufficient.
371 */
372 #ifdef XML_XPATH_NOVAR
373 key->comp = xsltXPathCompileFlags(style, pattern, XML_XPATH_NOVAR);
374 #else
375 key->comp = xsltXPathCompile(style, pattern);
376 #endif
377 if (key->comp == NULL) {
378 xsltTransformError(NULL, style, inst,
379 "xsl:key : 'match' pattern compilation failed '%s'\n",
380 pattern);
381 if (style != NULL) style->errors++;
382 }
383 #ifdef XML_XPATH_NOVAR
384 key->usecomp = xsltXPathCompileFlags(style, use, XML_XPATH_NOVAR);
385 #else
386 key->usecomp = xsltXPathCompile(style, use);
387 #endif
388 if (key->usecomp == NULL) {
389 xsltTransformError(NULL, style, inst,
390 "xsl:key : 'use' expression compilation failed '%s'\n",
391 use);
392 if (style != NULL) style->errors++;
393 }
394
395 /*
396 * Sometimes the stylesheet writer use the order to ease the
397 * resolution of keys when they are dependant, keep the provided
398 * order so add the new one at the end.
399 */
400 if (style->keys == NULL) {
401 style->keys = key;
402 } else {
403 xsltKeyDefPtr prev = style->keys;
404
405 while (prev->next != NULL)
406 prev = prev->next;
407
408 prev->next = key;
409 }
410 key->next = NULL;
411 key = NULL;
412
413 error:
414 if (pattern != NULL)
415 xmlFree(pattern);
416 if (key != NULL)
417 xsltFreeKeyDef(key);
418 return(0);
419 }
420
421 /**
422 * xsltGetKey:
423 * @ctxt: an XSLT transformation context
424 * @name: the key name or NULL
425 * @nameURI: the name URI or NULL
426 * @value: the key value to look for
427 *
428 * Looks up a key of the in current source doc (the document info
429 * on @ctxt->document). Computes the key if not already done
430 * for the current source doc.
431 *
432 * Returns the nodeset resulting from the query or NULL
433 */
434 xmlNodeSetPtr
xsltGetKey(xsltTransformContextPtr ctxt,const xmlChar * name,const xmlChar * nameURI,const xmlChar * value)435 xsltGetKey(xsltTransformContextPtr ctxt, const xmlChar *name,
436 const xmlChar *nameURI, const xmlChar *value) {
437 xmlNodeSetPtr ret;
438 xsltKeyTablePtr table;
439 int init_table = 0;
440
441 if ((ctxt == NULL) || (name == NULL) || (value == NULL) ||
442 (ctxt->document == NULL))
443 return(NULL);
444
445 #ifdef WITH_XSLT_DEBUG_KEYS
446 xsltGenericDebug(xsltGenericDebugContext,
447 "Get key %s, value %s\n", name, value);
448 #endif
449
450 /*
451 * keys are computed only on-demand on first key access for a document
452 */
453 if ((ctxt->document->nbKeysComputed < ctxt->nbKeys) &&
454 (ctxt->keyInitLevel == 0)) {
455 /*
456 * If non-recursive behaviour, just try to initialize all keys
457 */
458 if (xsltInitAllDocKeys(ctxt))
459 return(NULL);
460 }
461
462 retry:
463 table = (xsltKeyTablePtr) ctxt->document->keys;
464 while (table != NULL) {
465 if (((nameURI != NULL) == (table->nameURI != NULL)) &&
466 xmlStrEqual(table->name, name) &&
467 xmlStrEqual(table->nameURI, nameURI))
468 {
469 ret = (xmlNodeSetPtr)xmlHashLookup(table->keys, value);
470 return(ret);
471 }
472 table = table->next;
473 }
474
475 if ((ctxt->keyInitLevel != 0) && (init_table == 0)) {
476 /*
477 * Apparently one key is recursive and this one is needed,
478 * initialize just it, that time and retry
479 */
480 xsltInitDocKeyTable(ctxt, name, nameURI);
481 init_table = 1;
482 goto retry;
483 }
484
485 return(NULL);
486 }
487
488
489 /**
490 * xsltInitDocKeyTable:
491 *
492 * INTERNAL ROUTINE ONLY
493 *
494 * Check if any keys on the current document need to be computed
495 */
496 static int
xsltInitDocKeyTable(xsltTransformContextPtr ctxt,const xmlChar * name,const xmlChar * nameURI)497 xsltInitDocKeyTable(xsltTransformContextPtr ctxt, const xmlChar *name,
498 const xmlChar *nameURI)
499 {
500 xsltStylesheetPtr style;
501 xsltKeyDefPtr keyd = NULL;
502 int found = 0;
503
504 #ifdef KEY_INIT_DEBUG
505 fprintf(stderr, "xsltInitDocKeyTable %s\n", name);
506 #endif
507
508 style = ctxt->style;
509 while (style != NULL) {
510 keyd = (xsltKeyDefPtr) style->keys;
511 while (keyd != NULL) {
512 if (((keyd->nameURI != NULL) ==
513 (nameURI != NULL)) &&
514 xmlStrEqual(keyd->name, name) &&
515 xmlStrEqual(keyd->nameURI, nameURI))
516 {
517 xsltInitCtxtKey(ctxt, ctxt->document, keyd);
518 if (ctxt->document->nbKeysComputed == ctxt->nbKeys)
519 return(0);
520 found = 1;
521 }
522 keyd = keyd->next;
523 }
524 style = xsltNextImport(style);
525 }
526 if (found == 0) {
527 #ifdef WITH_XSLT_DEBUG_KEYS
528 XSLT_TRACE(ctxt,XSLT_TRACE_KEYS,xsltGenericDebug(xsltGenericDebugContext,
529 "xsltInitDocKeyTable: did not found %s\n", name));
530 #endif
531 xsltTransformError(ctxt, NULL, keyd? keyd->inst : NULL,
532 "Failed to find key definition for %s\n", name);
533 ctxt->state = XSLT_STATE_STOPPED;
534 return(-1);
535 }
536 #ifdef KEY_INIT_DEBUG
537 fprintf(stderr, "xsltInitDocKeyTable %s done\n", name);
538 #endif
539 return(0);
540 }
541
542 /**
543 * xsltInitAllDocKeys:
544 * @ctxt: transformation context
545 *
546 * INTERNAL ROUTINE ONLY
547 *
548 * Check if any keys on the current document need to be computed
549 *
550 * Returns 0 in case of success, -1 in case of failure
551 */
552 int
xsltInitAllDocKeys(xsltTransformContextPtr ctxt)553 xsltInitAllDocKeys(xsltTransformContextPtr ctxt)
554 {
555 xsltStylesheetPtr style;
556 xsltKeyDefPtr keyd;
557 xsltKeyTablePtr table;
558
559 if (ctxt == NULL)
560 return(-1);
561
562 #ifdef KEY_INIT_DEBUG
563 fprintf(stderr, "xsltInitAllDocKeys %d %d\n",
564 ctxt->document->nbKeysComputed, ctxt->nbKeys);
565 #endif
566
567 if (ctxt->document->nbKeysComputed == ctxt->nbKeys)
568 return(0);
569
570
571 /*
572 * TODO: This could be further optimized
573 */
574 style = ctxt->style;
575 while (style) {
576 keyd = (xsltKeyDefPtr) style->keys;
577 while (keyd != NULL) {
578 #ifdef KEY_INIT_DEBUG
579 fprintf(stderr, "Init key %s\n", keyd->name);
580 #endif
581 /*
582 * Check if keys with this QName have been already
583 * computed.
584 */
585 table = (xsltKeyTablePtr) ctxt->document->keys;
586 while (table) {
587 if (((keyd->nameURI != NULL) == (table->nameURI != NULL)) &&
588 xmlStrEqual(keyd->name, table->name) &&
589 xmlStrEqual(keyd->nameURI, table->nameURI))
590 {
591 break;
592 }
593 table = table->next;
594 }
595 if (table == NULL) {
596 /*
597 * Keys with this QName have not been yet computed.
598 */
599 xsltInitDocKeyTable(ctxt, keyd->name, keyd->nameURI);
600 }
601 keyd = keyd->next;
602 }
603 style = xsltNextImport(style);
604 }
605 #ifdef KEY_INIT_DEBUG
606 fprintf(stderr, "xsltInitAllDocKeys: done\n");
607 #endif
608 return(0);
609 }
610
611 /**
612 * xsltInitCtxtKey:
613 * @ctxt: an XSLT transformation context
614 * @idoc: the document information (holds key values)
615 * @keyDef: the key definition
616 *
617 * Computes the key tables this key and for the current input document.
618 *
619 * Returns: 0 on success, -1 on error
620 */
621 int
xsltInitCtxtKey(xsltTransformContextPtr ctxt,xsltDocumentPtr idoc,xsltKeyDefPtr keyDef)622 xsltInitCtxtKey(xsltTransformContextPtr ctxt, xsltDocumentPtr idoc,
623 xsltKeyDefPtr keyDef)
624 {
625 int i, len, k;
626 xmlNodeSetPtr matchList = NULL, keylist;
627 xmlXPathObjectPtr matchRes = NULL, useRes = NULL;
628 xmlChar *str = NULL;
629 xsltKeyTablePtr table;
630 xmlNodePtr oldInst, cur;
631 xmlNodePtr oldContextNode;
632 xsltDocumentPtr oldDocInfo;
633 int oldXPPos, oldXPSize;
634 xmlNodePtr oldXPNode;
635 xmlDocPtr oldXPDoc;
636 int oldXPNsNr;
637 xmlNsPtr *oldXPNamespaces;
638 xmlXPathContextPtr xpctxt;
639
640 #ifdef KEY_INIT_DEBUG
641 fprintf(stderr, "xsltInitCtxtKey %s : %d\n", keyDef->name, ctxt->keyInitLevel);
642 #endif
643
644 if ((keyDef->comp == NULL) || (keyDef->usecomp == NULL))
645 return(-1);
646
647 /*
648 * Detect recursive keys
649 */
650 if (ctxt->keyInitLevel > ctxt->nbKeys) {
651 #ifdef WITH_XSLT_DEBUG_KEYS
652 XSLT_TRACE(ctxt,XSLT_TRACE_KEYS,
653 xsltGenericDebug(xsltGenericDebugContext,
654 "xsltInitCtxtKey: key definition of %s is recursive\n",
655 keyDef->name));
656 #endif
657 xsltTransformError(ctxt, NULL, keyDef->inst,
658 "Key definition for %s is recursive\n", keyDef->name);
659 ctxt->state = XSLT_STATE_STOPPED;
660 return(-1);
661 }
662 ctxt->keyInitLevel++;
663
664 xpctxt = ctxt->xpathCtxt;
665 idoc->nbKeysComputed++;
666 /*
667 * Save context state.
668 */
669 oldInst = ctxt->inst;
670 oldDocInfo = ctxt->document;
671 oldContextNode = ctxt->node;
672
673 oldXPNode = xpctxt->node;
674 oldXPDoc = xpctxt->doc;
675 oldXPPos = xpctxt->proximityPosition;
676 oldXPSize = xpctxt->contextSize;
677 oldXPNsNr = xpctxt->nsNr;
678 oldXPNamespaces = xpctxt->namespaces;
679
680 /*
681 * Set up contexts.
682 */
683 ctxt->document = idoc;
684 ctxt->node = (xmlNodePtr) idoc->doc;
685 ctxt->inst = keyDef->inst;
686
687 xpctxt->doc = idoc->doc;
688 xpctxt->node = (xmlNodePtr) idoc->doc;
689 /* TODO : clarify the use of namespaces in keys evaluation */
690 xpctxt->namespaces = keyDef->nsList;
691 xpctxt->nsNr = keyDef->nsNr;
692
693 /*
694 * Evaluate the 'match' expression of the xsl:key.
695 * TODO: The 'match' is a *pattern*.
696 */
697 matchRes = xmlXPathCompiledEval(keyDef->comp, xpctxt);
698 if (matchRes == NULL) {
699
700 #ifdef WITH_XSLT_DEBUG_KEYS
701 XSLT_TRACE(ctxt,XSLT_TRACE_KEYS,xsltGenericDebug(xsltGenericDebugContext,
702 "xsltInitCtxtKey: %s evaluation failed\n", keyDef->match));
703 #endif
704 xsltTransformError(ctxt, NULL, keyDef->inst,
705 "Failed to evaluate the 'match' expression.\n");
706 ctxt->state = XSLT_STATE_STOPPED;
707 goto error;
708 } else {
709 if (matchRes->type == XPATH_NODESET) {
710 matchList = matchRes->nodesetval;
711
712 #ifdef WITH_XSLT_DEBUG_KEYS
713 if (matchList != NULL)
714 XSLT_TRACE(ctxt,XSLT_TRACE_KEYS,xsltGenericDebug(xsltGenericDebugContext,
715 "xsltInitCtxtKey: %s evaluates to %d nodes\n",
716 keyDef->match, matchList->nodeNr));
717 #endif
718 } else {
719 /*
720 * Is not a node set, but must be.
721 */
722 #ifdef WITH_XSLT_DEBUG_KEYS
723 XSLT_TRACE(ctxt,XSLT_TRACE_KEYS,xsltGenericDebug(xsltGenericDebugContext,
724 "xsltInitCtxtKey: %s is not a node set\n", keyDef->match));
725 #endif
726 xsltTransformError(ctxt, NULL, keyDef->inst,
727 "The 'match' expression did not evaluate to a node set.\n");
728 ctxt->state = XSLT_STATE_STOPPED;
729 goto error;
730 }
731 }
732 if ((matchList == NULL) || (matchList->nodeNr <= 0))
733 goto exit;
734
735 /**
736 * Multiple key definitions for the same name are allowed, so
737 * we must check if the key is already present for this doc
738 */
739 table = (xsltKeyTablePtr) idoc->keys;
740 while (table != NULL) {
741 if (xmlStrEqual(table->name, keyDef->name) &&
742 (((keyDef->nameURI == NULL) && (table->nameURI == NULL)) ||
743 ((keyDef->nameURI != NULL) && (table->nameURI != NULL) &&
744 (xmlStrEqual(table->nameURI, keyDef->nameURI)))))
745 break;
746 table = table->next;
747 }
748 /**
749 * If the key was not previously defined, create it now and
750 * chain it to the list of keys for the doc
751 */
752 if (table == NULL) {
753 table = xsltNewKeyTable(keyDef->name, keyDef->nameURI);
754 if (table == NULL)
755 goto error;
756 table->next = idoc->keys;
757 idoc->keys = table;
758 }
759
760 /*
761 * SPEC XSLT 1.0 (XSLT 2.0 does not clarify the context size!)
762 * "...the use attribute of the xsl:key element is evaluated with x as
763 " the current node and with a node list containing just x as the
764 * current node list"
765 */
766 xpctxt->contextSize = 1;
767 xpctxt->proximityPosition = 1;
768
769 for (i = 0; i < matchList->nodeNr; i++) {
770 cur = matchList->nodeTab[i];
771 if (! IS_XSLT_REAL_NODE(cur))
772 continue;
773 ctxt->node = cur;
774 xpctxt->node = cur;
775 /*
776 * Process the 'use' of the xsl:key.
777 * SPEC XSLT 1.0:
778 * "The use attribute is an expression specifying the values of
779 * the key; the expression is evaluated once for each node that
780 * matches the pattern."
781 */
782 if (useRes != NULL)
783 xmlXPathFreeObject(useRes);
784 useRes = xmlXPathCompiledEval(keyDef->usecomp, xpctxt);
785 if (useRes == NULL) {
786 xsltTransformError(ctxt, NULL, keyDef->inst,
787 "Failed to evaluate the 'use' expression.\n");
788 ctxt->state = XSLT_STATE_STOPPED;
789 break;
790 }
791 if (useRes->type == XPATH_NODESET) {
792 if ((useRes->nodesetval != NULL) &&
793 (useRes->nodesetval->nodeNr != 0))
794 {
795 len = useRes->nodesetval->nodeNr;
796 str = xmlXPathCastNodeToString(useRes->nodesetval->nodeTab[0]);
797 } else {
798 continue;
799 }
800 } else {
801 len = 1;
802 if (useRes->type == XPATH_STRING) {
803 /*
804 * Consume the string value.
805 */
806 str = useRes->stringval;
807 useRes->stringval = NULL;
808 } else {
809 str = xmlXPathCastToString(useRes);
810 }
811 }
812 /*
813 * Process all strings.
814 */
815 k = 0;
816 while (1) {
817 if (str == NULL)
818 goto next_string;
819
820 #ifdef WITH_XSLT_DEBUG_KEYS
821 XSLT_TRACE(ctxt,XSLT_TRACE_KEYS,xsltGenericDebug(xsltGenericDebugContext,
822 "xsl:key : node associated to ('%s', '%s')\n", keyDef->name, str));
823 #endif
824
825 keylist = xmlHashLookup(table->keys, str);
826 if (keylist == NULL) {
827 keylist = xmlXPathNodeSetCreate(cur);
828 if (keylist == NULL)
829 goto error;
830 xmlHashAddEntry(table->keys, str, keylist);
831 } else {
832 /*
833 * TODO: How do we know if this function failed?
834 */
835 xmlXPathNodeSetAdd(keylist, cur);
836 }
837 switch (cur->type) {
838 case XML_ELEMENT_NODE:
839 case XML_TEXT_NODE:
840 case XML_CDATA_SECTION_NODE:
841 case XML_PI_NODE:
842 case XML_COMMENT_NODE:
843 cur->psvi = keyDef;
844 break;
845 case XML_ATTRIBUTE_NODE:
846 ((xmlAttrPtr) cur)->psvi = keyDef;
847 break;
848 case XML_DOCUMENT_NODE:
849 case XML_HTML_DOCUMENT_NODE:
850 ((xmlDocPtr) cur)->psvi = keyDef;
851 break;
852 default:
853 break;
854 }
855 xmlFree(str);
856 str = NULL;
857
858 next_string:
859 k++;
860 if (k >= len)
861 break;
862 str = xmlXPathCastNodeToString(useRes->nodesetval->nodeTab[k]);
863 }
864 }
865
866 exit:
867 error:
868 ctxt->keyInitLevel--;
869 /*
870 * Restore context state.
871 */
872 xpctxt->node = oldXPNode;
873 xpctxt->doc = oldXPDoc;
874 xpctxt->nsNr = oldXPNsNr;
875 xpctxt->namespaces = oldXPNamespaces;
876 xpctxt->proximityPosition = oldXPPos;
877 xpctxt->contextSize = oldXPSize;
878
879 ctxt->node = oldContextNode;
880 ctxt->document = oldDocInfo;
881 ctxt->inst = oldInst;
882
883 if (str)
884 xmlFree(str);
885 if (useRes != NULL)
886 xmlXPathFreeObject(useRes);
887 if (matchRes != NULL)
888 xmlXPathFreeObject(matchRes);
889 return(0);
890 }
891
892 /**
893 * xsltInitCtxtKeys:
894 * @ctxt: an XSLT transformation context
895 * @idoc: a document info
896 *
897 * Computes all the keys tables for the current input document.
898 * Should be done before global varibales are initialized.
899 * NOTE: Not used anymore in the refactored code.
900 */
901 void
xsltInitCtxtKeys(xsltTransformContextPtr ctxt,xsltDocumentPtr idoc)902 xsltInitCtxtKeys(xsltTransformContextPtr ctxt, xsltDocumentPtr idoc) {
903 xsltStylesheetPtr style;
904 xsltKeyDefPtr keyDef;
905
906 if ((ctxt == NULL) || (idoc == NULL))
907 return;
908
909 #ifdef KEY_INIT_DEBUG
910 fprintf(stderr, "xsltInitCtxtKeys on document\n");
911 #endif
912
913 #ifdef WITH_XSLT_DEBUG_KEYS
914 if ((idoc->doc != NULL) && (idoc->doc->URL != NULL))
915 XSLT_TRACE(ctxt,XSLT_TRACE_KEYS,xsltGenericDebug(xsltGenericDebugContext, "Initializing keys on %s\n",
916 idoc->doc->URL));
917 #endif
918 style = ctxt->style;
919 while (style != NULL) {
920 keyDef = (xsltKeyDefPtr) style->keys;
921 while (keyDef != NULL) {
922 xsltInitCtxtKey(ctxt, idoc, keyDef);
923
924 keyDef = keyDef->next;
925 }
926
927 style = xsltNextImport(style);
928 }
929
930 #ifdef KEY_INIT_DEBUG
931 fprintf(stderr, "xsltInitCtxtKeys on document: done\n");
932 #endif
933
934 }
935
936 /**
937 * xsltFreeDocumentKeys:
938 * @idoc: a XSLT document
939 *
940 * Free the keys associated to a document
941 */
942 void
xsltFreeDocumentKeys(xsltDocumentPtr idoc)943 xsltFreeDocumentKeys(xsltDocumentPtr idoc) {
944 if (idoc != NULL)
945 xsltFreeKeyTableList(idoc->keys);
946 }
947
948