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