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