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