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