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 key = NULL; 388 389 error: 390 if (pattern != NULL) 391 xmlFree(pattern); 392 if (key != NULL) 393 xsltFreeKeyDef(key); 394 return(0); 395 } 396 397 /** 398 * xsltGetKey: 399 * @ctxt: an XSLT transformation context 400 * @name: the key name or NULL 401 * @nameURI: the name URI or NULL 402 * @value: the key value to look for 403 * 404 * Looks up a key of the in current source doc (the document info 405 * on @ctxt->document). Computes the key if not already done 406 * for the current source doc. 407 * 408 * Returns the nodeset resulting from the query or NULL 409 */ 410 xmlNodeSetPtr 411 xsltGetKey(xsltTransformContextPtr ctxt, const xmlChar *name, 412 const xmlChar *nameURI, const xmlChar *value) { 413 xmlNodeSetPtr ret; 414 xsltKeyTablePtr table; 415 int init_table = 0; 416 417 if ((ctxt == NULL) || (name == NULL) || (value == NULL) || 418 (ctxt->document == NULL)) 419 return(NULL); 420 421 #ifdef WITH_XSLT_DEBUG_KEYS 422 xsltGenericDebug(xsltGenericDebugContext, 423 "Get key %s, value %s\n", name, value); 424 #endif 425 426 /* 427 * keys are computed only on-demand on first key access for a document 428 */ 429 if ((ctxt->document->nbKeysComputed < ctxt->nbKeys) && 430 (ctxt->keyInitLevel == 0)) { 431 /* 432 * If non-recursive behaviour, just try to initialize all keys 433 */ 434 if (xsltInitAllDocKeys(ctxt)) 435 return(NULL); 436 } 437 438 retry: 439 table = (xsltKeyTablePtr) ctxt->document->keys; 440 while (table != NULL) { 441 if (((nameURI != NULL) == (table->nameURI != NULL)) && 442 xmlStrEqual(table->name, name) && 443 xmlStrEqual(table->nameURI, nameURI)) 444 { 445 ret = (xmlNodeSetPtr)xmlHashLookup(table->keys, value); 446 return(ret); 447 } 448 table = table->next; 449 } 450 451 if ((ctxt->keyInitLevel != 0) && (init_table == 0)) { 452 /* 453 * Apparently one key is recursive and this one is needed, 454 * initialize just it, that time and retry 455 */ 456 xsltInitDocKeyTable(ctxt, name, nameURI); 457 init_table = 1; 458 goto retry; 459 } 460 461 return(NULL); 462 } 463 464 465 /** 466 * xsltInitDocKeyTable: 467 * 468 * INTERNAL ROUTINE ONLY 469 * 470 * Check if any keys on the current document need to be computed 471 */ 472 static int 473 xsltInitDocKeyTable(xsltTransformContextPtr ctxt, const xmlChar *name, 474 const xmlChar *nameURI) 475 { 476 xsltStylesheetPtr style; 477 xsltKeyDefPtr keyd = NULL; 478 int found = 0; 479 480 #ifdef KEY_INIT_DEBUG 481 fprintf(stderr, "xsltInitDocKeyTable %s\n", name); 482 #endif 483 484 style = ctxt->style; 485 while (style != NULL) { 486 keyd = (xsltKeyDefPtr) style->keys; 487 while (keyd != NULL) { 488 if (((keyd->nameURI != NULL) == 489 (nameURI != NULL)) && 490 xmlStrEqual(keyd->name, name) && 491 xmlStrEqual(keyd->nameURI, nameURI)) 492 { 493 xsltInitCtxtKey(ctxt, ctxt->document, keyd); 494 if (ctxt->document->nbKeysComputed == ctxt->nbKeys) 495 return(0); 496 found = 1; 497 } 498 keyd = keyd->next; 499 } 500 style = xsltNextImport(style); 501 } 502 if (found == 0) { 503 #ifdef WITH_XSLT_DEBUG_KEYS 504 XSLT_TRACE(ctxt,XSLT_TRACE_KEYS,xsltGenericDebug(xsltGenericDebugContext, 505 "xsltInitDocKeyTable: did not found %s\n", name)); 506 #endif 507 xsltTransformError(ctxt, NULL, keyd? keyd->inst : NULL, 508 "Failed to find key definition for %s\n", name); 509 ctxt->state = XSLT_STATE_STOPPED; 510 return(-1); 511 } 512 #ifdef KEY_INIT_DEBUG 513 fprintf(stderr, "xsltInitDocKeyTable %s done\n", name); 514 #endif 515 return(0); 516 } 517 518 /** 519 * xsltInitAllDocKeys: 520 * @ctxt: transformation context 521 * 522 * INTERNAL ROUTINE ONLY 523 * 524 * Check if any keys on the current document need to be computed 525 * 526 * Returns 0 in case of success, -1 in case of failure 527 */ 528 int 529 xsltInitAllDocKeys(xsltTransformContextPtr ctxt) 530 { 531 xsltStylesheetPtr style; 532 xsltKeyDefPtr keyd; 533 xsltKeyTablePtr table; 534 535 if (ctxt == NULL) 536 return(-1); 537 538 #ifdef KEY_INIT_DEBUG 539 fprintf(stderr, "xsltInitAllDocKeys %d %d\n", 540 ctxt->document->nbKeysComputed, ctxt->nbKeys); 541 #endif 542 543 if (ctxt->document->nbKeysComputed == ctxt->nbKeys) 544 return(0); 545 546 547 /* 548 * TODO: This could be further optimized 549 */ 550 style = ctxt->style; 551 while (style) { 552 keyd = (xsltKeyDefPtr) style->keys; 553 while (keyd != NULL) { 554 #ifdef KEY_INIT_DEBUG 555 fprintf(stderr, "Init key %s\n", keyd->name); 556 #endif 557 /* 558 * Check if keys with this QName have been already 559 * computed. 560 */ 561 table = (xsltKeyTablePtr) ctxt->document->keys; 562 while (table) { 563 if (((keyd->nameURI != NULL) == (table->nameURI != NULL)) && 564 xmlStrEqual(keyd->name, table->name) && 565 xmlStrEqual(keyd->nameURI, table->nameURI)) 566 { 567 break; 568 } 569 table = table->next; 570 } 571 if (table == NULL) { 572 /* 573 * Keys with this QName have not been yet computed. 574 */ 575 xsltInitDocKeyTable(ctxt, keyd->name, keyd->nameURI); 576 } 577 keyd = keyd->next; 578 } 579 style = xsltNextImport(style); 580 } 581 #ifdef KEY_INIT_DEBUG 582 fprintf(stderr, "xsltInitAllDocKeys: done\n"); 583 #endif 584 return(0); 585 } 586 587 /** 588 * xsltInitCtxtKey: 589 * @ctxt: an XSLT transformation context 590 * @idoc: the document information (holds key values) 591 * @keyDef: the key definition 592 * 593 * Computes the key tables this key and for the current input document. 594 * 595 * Returns: 0 on success, -1 on error 596 */ 597 int 598 xsltInitCtxtKey(xsltTransformContextPtr ctxt, xsltDocumentPtr idoc, 599 xsltKeyDefPtr keyDef) 600 { 601 int i, len, k; 602 xmlNodeSetPtr matchList = NULL, keylist; 603 xmlXPathObjectPtr matchRes = NULL, useRes = NULL; 604 xmlChar *str = NULL; 605 xsltKeyTablePtr table; 606 xmlNodePtr oldInst, cur; 607 xmlNodePtr oldContextNode; 608 xsltDocumentPtr oldDocInfo; 609 int oldXPPos, oldXPSize; 610 xmlDocPtr oldXPDoc; 611 int oldXPNsNr; 612 xmlNsPtr *oldXPNamespaces; 613 xmlXPathContextPtr xpctxt; 614 615 #ifdef KEY_INIT_DEBUG 616 fprintf(stderr, "xsltInitCtxtKey %s : %d\n", keyDef->name, ctxt->keyInitLevel); 617 #endif 618 619 if ((keyDef->comp == NULL) || (keyDef->usecomp == NULL)) 620 return(-1); 621 622 /* 623 * Detect recursive keys 624 */ 625 if (ctxt->keyInitLevel > ctxt->nbKeys) { 626 #ifdef WITH_XSLT_DEBUG_KEYS 627 XSLT_TRACE(ctxt,XSLT_TRACE_KEYS, 628 xsltGenericDebug(xsltGenericDebugContext, 629 "xsltInitCtxtKey: key definition of %s is recursive\n", 630 keyDef->name)); 631 #endif 632 xsltTransformError(ctxt, NULL, keyDef->inst, 633 "Key definition for %s is recursive\n", keyDef->name); 634 ctxt->state = XSLT_STATE_STOPPED; 635 return(-1); 636 } 637 ctxt->keyInitLevel++; 638 639 xpctxt = ctxt->xpathCtxt; 640 idoc->nbKeysComputed++; 641 /* 642 * Save context state. 643 */ 644 oldInst = ctxt->inst; 645 oldDocInfo = ctxt->document; 646 oldContextNode = ctxt->node; 647 648 oldXPDoc = xpctxt->doc; 649 oldXPPos = xpctxt->proximityPosition; 650 oldXPSize = xpctxt->contextSize; 651 oldXPNsNr = xpctxt->nsNr; 652 oldXPNamespaces = xpctxt->namespaces; 653 654 /* 655 * Set up contexts. 656 */ 657 ctxt->document = idoc; 658 ctxt->node = (xmlNodePtr) idoc->doc; 659 ctxt->inst = keyDef->inst; 660 661 xpctxt->doc = idoc->doc; 662 xpctxt->node = (xmlNodePtr) idoc->doc; 663 /* TODO : clarify the use of namespaces in keys evaluation */ 664 xpctxt->namespaces = keyDef->nsList; 665 xpctxt->nsNr = keyDef->nsNr; 666 667 /* 668 * Evaluate the 'match' expression of the xsl:key. 669 * TODO: The 'match' is a *pattern*. 670 */ 671 matchRes = xmlXPathCompiledEval(keyDef->comp, xpctxt); 672 if (matchRes == NULL) { 673 674 #ifdef WITH_XSLT_DEBUG_KEYS 675 XSLT_TRACE(ctxt,XSLT_TRACE_KEYS,xsltGenericDebug(xsltGenericDebugContext, 676 "xsltInitCtxtKey: %s evaluation failed\n", keyDef->match)); 677 #endif 678 xsltTransformError(ctxt, NULL, keyDef->inst, 679 "Failed to evaluate the 'match' expression.\n"); 680 ctxt->state = XSLT_STATE_STOPPED; 681 goto error; 682 } else { 683 if (matchRes->type == XPATH_NODESET) { 684 matchList = matchRes->nodesetval; 685 686 #ifdef WITH_XSLT_DEBUG_KEYS 687 if (matchList != NULL) 688 XSLT_TRACE(ctxt,XSLT_TRACE_KEYS,xsltGenericDebug(xsltGenericDebugContext, 689 "xsltInitCtxtKey: %s evaluates to %d nodes\n", 690 keyDef->match, matchList->nodeNr)); 691 #endif 692 } else { 693 /* 694 * Is not a node set, but must be. 695 */ 696 #ifdef WITH_XSLT_DEBUG_KEYS 697 XSLT_TRACE(ctxt,XSLT_TRACE_KEYS,xsltGenericDebug(xsltGenericDebugContext, 698 "xsltInitCtxtKey: %s is not a node set\n", keyDef->match)); 699 #endif 700 xsltTransformError(ctxt, NULL, keyDef->inst, 701 "The 'match' expression did not evaluate to a node set.\n"); 702 ctxt->state = XSLT_STATE_STOPPED; 703 goto error; 704 } 705 } 706 if ((matchList == NULL) || (matchList->nodeNr <= 0)) 707 goto exit; 708 709 /** 710 * Multiple key definitions for the same name are allowed, so 711 * we must check if the key is already present for this doc 712 */ 713 table = (xsltKeyTablePtr) idoc->keys; 714 while (table != NULL) { 715 if (xmlStrEqual(table->name, keyDef->name) && 716 (((keyDef->nameURI == NULL) && (table->nameURI == NULL)) || 717 ((keyDef->nameURI != NULL) && (table->nameURI != NULL) && 718 (xmlStrEqual(table->nameURI, keyDef->nameURI))))) 719 break; 720 table = table->next; 721 } 722 /** 723 * If the key was not previously defined, create it now and 724 * chain it to the list of keys for the doc 725 */ 726 if (table == NULL) { 727 table = xsltNewKeyTable(keyDef->name, keyDef->nameURI); 728 if (table == NULL) 729 goto error; 730 table->next = idoc->keys; 731 idoc->keys = table; 732 } 733 734 /* 735 * SPEC XSLT 1.0 (XSLT 2.0 does not clarify the context size!) 736 * "...the use attribute of the xsl:key element is evaluated with x as 737 " the current node and with a node list containing just x as the 738 * current node list" 739 */ 740 xpctxt->contextSize = 1; 741 xpctxt->proximityPosition = 1; 742 743 for (i = 0; i < matchList->nodeNr; i++) { 744 cur = matchList->nodeTab[i]; 745 if (! IS_XSLT_REAL_NODE(cur)) 746 continue; 747 ctxt->node = cur; 748 xpctxt->node = cur; 749 /* 750 * Process the 'use' of the xsl:key. 751 * SPEC XSLT 1.0: 752 * "The use attribute is an expression specifying the values of 753 * the key; the expression is evaluated once for each node that 754 * matches the pattern." 755 */ 756 if (useRes != NULL) 757 xmlXPathFreeObject(useRes); 758 useRes = xmlXPathCompiledEval(keyDef->usecomp, xpctxt); 759 if (useRes == NULL) { 760 xsltTransformError(ctxt, NULL, keyDef->inst, 761 "Failed to evaluate the 'use' expression.\n"); 762 ctxt->state = XSLT_STATE_STOPPED; 763 break; 764 } 765 if (useRes->type == XPATH_NODESET) { 766 if ((useRes->nodesetval != NULL) && 767 (useRes->nodesetval->nodeNr != 0)) 768 { 769 len = useRes->nodesetval->nodeNr; 770 str = xmlXPathCastNodeToString(useRes->nodesetval->nodeTab[0]); 771 } else { 772 continue; 773 } 774 } else { 775 len = 1; 776 if (useRes->type == XPATH_STRING) { 777 /* 778 * Consume the string value. 779 */ 780 str = useRes->stringval; 781 useRes->stringval = NULL; 782 } else { 783 str = xmlXPathCastToString(useRes); 784 } 785 } 786 /* 787 * Process all strings. 788 */ 789 k = 0; 790 while (1) { 791 if (str == NULL) 792 goto next_string; 793 794 #ifdef WITH_XSLT_DEBUG_KEYS 795 XSLT_TRACE(ctxt,XSLT_TRACE_KEYS,xsltGenericDebug(xsltGenericDebugContext, 796 "xsl:key : node associated to ('%s', '%s')\n", keyDef->name, str)); 797 #endif 798 799 keylist = xmlHashLookup(table->keys, str); 800 if (keylist == NULL) { 801 keylist = xmlXPathNodeSetCreate(cur); 802 if (keylist == NULL) 803 goto error; 804 xmlHashAddEntry(table->keys, str, keylist); 805 } else { 806 /* 807 * TODO: How do we know if this function failed? 808 */ 809 xmlXPathNodeSetAdd(keylist, cur); 810 } 811 switch (cur->type) { 812 case XML_ELEMENT_NODE: 813 case XML_TEXT_NODE: 814 case XML_CDATA_SECTION_NODE: 815 case XML_PI_NODE: 816 case XML_COMMENT_NODE: 817 cur->psvi = keyDef; 818 break; 819 case XML_ATTRIBUTE_NODE: 820 ((xmlAttrPtr) cur)->psvi = keyDef; 821 break; 822 case XML_DOCUMENT_NODE: 823 case XML_HTML_DOCUMENT_NODE: 824 ((xmlDocPtr) cur)->psvi = keyDef; 825 break; 826 default: 827 break; 828 } 829 xmlFree(str); 830 str = NULL; 831 832 next_string: 833 k++; 834 if (k >= len) 835 break; 836 str = xmlXPathCastNodeToString(useRes->nodesetval->nodeTab[k]); 837 } 838 } 839 840 exit: 841 error: 842 ctxt->keyInitLevel--; 843 /* 844 * Restore context state. 845 */ 846 xpctxt->doc = oldXPDoc; 847 xpctxt->nsNr = oldXPNsNr; 848 xpctxt->namespaces = oldXPNamespaces; 849 xpctxt->proximityPosition = oldXPPos; 850 xpctxt->contextSize = oldXPSize; 851 852 ctxt->node = oldContextNode; 853 ctxt->document = oldDocInfo; 854 ctxt->inst = oldInst; 855 856 if (str) 857 xmlFree(str); 858 if (useRes != NULL) 859 xmlXPathFreeObject(useRes); 860 if (matchRes != NULL) 861 xmlXPathFreeObject(matchRes); 862 return(0); 863 } 864 865 /** 866 * xsltInitCtxtKeys: 867 * @ctxt: an XSLT transformation context 868 * @idoc: a document info 869 * 870 * Computes all the keys tables for the current input document. 871 * Should be done before global varibales are initialized. 872 * NOTE: Not used anymore in the refactored code. 873 */ 874 void 875 xsltInitCtxtKeys(xsltTransformContextPtr ctxt, xsltDocumentPtr idoc) { 876 xsltStylesheetPtr style; 877 xsltKeyDefPtr keyDef; 878 879 if ((ctxt == NULL) || (idoc == NULL)) 880 return; 881 882 #ifdef KEY_INIT_DEBUG 883 fprintf(stderr, "xsltInitCtxtKeys on document\n"); 884 #endif 885 886 #ifdef WITH_XSLT_DEBUG_KEYS 887 if ((idoc->doc != NULL) && (idoc->doc->URL != NULL)) 888 XSLT_TRACE(ctxt,XSLT_TRACE_KEYS,xsltGenericDebug(xsltGenericDebugContext, "Initializing keys on %s\n", 889 idoc->doc->URL)); 890 #endif 891 style = ctxt->style; 892 while (style != NULL) { 893 keyDef = (xsltKeyDefPtr) style->keys; 894 while (keyDef != NULL) { 895 xsltInitCtxtKey(ctxt, idoc, keyDef); 896 897 keyDef = keyDef->next; 898 } 899 900 style = xsltNextImport(style); 901 } 902 903 #ifdef KEY_INIT_DEBUG 904 fprintf(stderr, "xsltInitCtxtKeys on document: done\n"); 905 #endif 906 907 } 908 909 /** 910 * xsltFreeDocumentKeys: 911 * @idoc: a XSLT document 912 * 913 * Free the keys associated to a document 914 */ 915 void 916 xsltFreeDocumentKeys(xsltDocumentPtr idoc) { 917 if (idoc != NULL) 918 xsltFreeKeyTableList(idoc->keys); 919 } 920 921