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