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 (at) 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 "xslt.h" 25 #include "xsltInternals.h" 26 #include "xsltutils.h" 27 #include "imports.h" 28 #include "templates.h" 29 #include "keys.h" 30 31 #ifdef WITH_XSLT_DEBUG 32 #define WITH_XSLT_DEBUG_KEYS 33 #endif 34 35 static int 36 xsltInitDocKeyTable(xsltTransformContextPtr ctxt, const xmlChar *name, 37 const xmlChar *nameURI); 38 39 /************************************************************************ 40 * * 41 * Type functions * 42 * * 43 ************************************************************************/ 44 45 /** 46 * xsltNewKeyDef: 47 * @name: the key name or NULL 48 * @nameURI: the name URI or NULL 49 * 50 * Create a new XSLT KeyDef 51 * 52 * Returns the newly allocated xsltKeyDefPtr or NULL in case of error 53 */ 54 static xsltKeyDefPtr 55 xsltNewKeyDef(const xmlChar *name, const xmlChar *nameURI) { 56 xsltKeyDefPtr cur; 57 58 cur = (xsltKeyDefPtr) xmlMalloc(sizeof(xsltKeyDef)); 59 if (cur == NULL) { 60 xsltTransformError(NULL, NULL, NULL, 61 "xsltNewKeyDef : malloc failed\n"); 62 return(NULL); 63 } 64 memset(cur, 0, sizeof(xsltKeyDef)); 65 if (name != NULL) 66 cur->name = xmlStrdup(name); 67 if (nameURI != NULL) 68 cur->nameURI = xmlStrdup(nameURI); 69 cur->nsList = NULL; 70 return(cur); 71 } 72 73 /** 74 * xsltFreeKeyDef: 75 * @keyd: an XSLT key definition 76 * 77 * Free up the memory allocated by @keyd 78 */ 79 static void 80 xsltFreeKeyDef(xsltKeyDefPtr keyd) { 81 if (keyd == NULL) 82 return; 83 if (keyd->comp != NULL) 84 xmlXPathFreeCompExpr(keyd->comp); 85 if (keyd->usecomp != NULL) 86 xmlXPathFreeCompExpr(keyd->usecomp); 87 if (keyd->name != NULL) 88 xmlFree(keyd->name); 89 if (keyd->nameURI != NULL) 90 xmlFree(keyd->nameURI); 91 if (keyd->match != NULL) 92 xmlFree(keyd->match); 93 if (keyd->use != NULL) 94 xmlFree(keyd->use); 95 if (keyd->nsList != NULL) 96 xmlFree(keyd->nsList); 97 memset(keyd, -1, sizeof(xsltKeyDef)); 98 xmlFree(keyd); 99 } 100 101 /** 102 * xsltFreeKeyDefList: 103 * @keyd: an XSLT key definition list 104 * 105 * Free up the memory allocated by all the elements of @keyd 106 */ 107 static void 108 xsltFreeKeyDefList(xsltKeyDefPtr keyd) { 109 xsltKeyDefPtr cur; 110 111 while (keyd != NULL) { 112 cur = keyd; 113 keyd = keyd->next; 114 xsltFreeKeyDef(cur); 115 } 116 } 117 118 /** 119 * xsltNewKeyTable: 120 * @name: the key name or NULL 121 * @nameURI: the name URI or NULL 122 * 123 * Create a new XSLT KeyTable 124 * 125 * Returns the newly allocated xsltKeyTablePtr or NULL in case of error 126 */ 127 static xsltKeyTablePtr 128 xsltNewKeyTable(const xmlChar *name, const xmlChar *nameURI) { 129 xsltKeyTablePtr cur; 130 131 cur = (xsltKeyTablePtr) xmlMalloc(sizeof(xsltKeyTable)); 132 if (cur == NULL) { 133 xsltTransformError(NULL, NULL, NULL, 134 "xsltNewKeyTable : malloc failed\n"); 135 return(NULL); 136 } 137 memset(cur, 0, sizeof(xsltKeyTable)); 138 if (name != NULL) 139 cur->name = xmlStrdup(name); 140 if (nameURI != NULL) 141 cur->nameURI = xmlStrdup(nameURI); 142 cur->keys = xmlHashCreate(0); 143 return(cur); 144 } 145 146 /** 147 * xsltFreeKeyTable: 148 * @keyt: an XSLT key table 149 * 150 * Free up the memory allocated by @keyt 151 */ 152 static void 153 xsltFreeKeyTable(xsltKeyTablePtr keyt) { 154 if (keyt == NULL) 155 return; 156 if (keyt->name != NULL) 157 xmlFree(keyt->name); 158 if (keyt->nameURI != NULL) 159 xmlFree(keyt->nameURI); 160 if (keyt->keys != NULL) 161 xmlHashFree(keyt->keys, 162 (xmlHashDeallocator) xmlXPathFreeNodeSet); 163 memset(keyt, -1, sizeof(xsltKeyTable)); 164 xmlFree(keyt); 165 } 166 167 /** 168 * xsltFreeKeyTableList: 169 * @keyt: an XSLT key table list 170 * 171 * Free up the memory allocated by all the elements of @keyt 172 */ 173 static void 174 xsltFreeKeyTableList(xsltKeyTablePtr keyt) { 175 xsltKeyTablePtr cur; 176 177 while (keyt != NULL) { 178 cur = keyt; 179 keyt = keyt->next; 180 xsltFreeKeyTable(cur); 181 } 182 } 183 184 /************************************************************************ 185 * * 186 * The interpreter for the precompiled patterns * 187 * * 188 ************************************************************************/ 189 190 191 /** 192 * xsltFreeKeys: 193 * @style: an XSLT stylesheet 194 * 195 * Free up the memory used by XSLT keys in a stylesheet 196 */ 197 void 198 xsltFreeKeys(xsltStylesheetPtr style) { 199 if (style->keys) 200 xsltFreeKeyDefList((xsltKeyDefPtr) style->keys); 201 } 202 203 /** 204 * skipString: 205 * @cur: the current pointer 206 * @end: the current offset 207 * 208 * skip a string delimited by " or ' 209 * 210 * Returns the byte after the string or -1 in case of error 211 */ 212 static int 213 skipString(const xmlChar *cur, int end) { 214 xmlChar limit; 215 216 if ((cur == NULL) || (end < 0)) return(-1); 217 if ((cur[end] == '\'') || (cur[end] == '"')) limit = cur[end]; 218 else return(end); 219 end++; 220 while (cur[end] != 0) { 221 if (cur[end] == limit) 222 return(end + 1); 223 end++; 224 } 225 return(-1); 226 } 227 228 /** 229 * skipPredicate: 230 * @cur: the current pointer 231 * @end: the current offset 232 * 233 * skip a predicate 234 * 235 * Returns the byte after the predicate or -1 in case of error 236 */ 237 static int 238 skipPredicate(const xmlChar *cur, int end) { 239 if ((cur == NULL) || (end < 0)) return(-1); 240 if (cur[end] != '[') return(end); 241 end++; 242 while (cur[end] != 0) { 243 if ((cur[end] == '\'') || (cur[end] == '"')) { 244 end = skipString(cur, end); 245 if (end <= 0) 246 return(-1); 247 continue; 248 } else if (cur[end] == '[') { 249 end = skipPredicate(cur, end); 250 if (end <= 0) 251 return(-1); 252 continue; 253 } else if (cur[end] == ']') 254 return(end + 1); 255 end++; 256 } 257 return(-1); 258 } 259 260 /** 261 * xsltAddKey: 262 * @style: an XSLT stylesheet 263 * @name: the key name or NULL 264 * @nameURI: the name URI or NULL 265 * @match: the match value 266 * @use: the use value 267 * @inst: the key instruction 268 * 269 * add a key definition to a stylesheet 270 * 271 * Returns 0 in case of success, and -1 in case of failure. 272 */ 273 int 274 xsltAddKey(xsltStylesheetPtr style, const xmlChar *name, 275 const xmlChar *nameURI, const xmlChar *match, 276 const xmlChar *use, xmlNodePtr inst) { 277 xsltKeyDefPtr key; 278 xmlChar *pattern = NULL; 279 int current, end, start, i = 0; 280 281 if ((style == NULL) || (name == NULL) || (match == NULL) || (use == NULL)) 282 return(-1); 283 284 #ifdef WITH_XSLT_DEBUG_KEYS 285 xsltGenericDebug(xsltGenericDebugContext, 286 "Add key %s, match %s, use %s\n", name, match, use); 287 #endif 288 289 key = xsltNewKeyDef(name, nameURI); 290 key->match = xmlStrdup(match); 291 key->use = xmlStrdup(use); 292 key->inst = inst; 293 key->nsList = xmlGetNsList(inst->doc, inst); 294 if (key->nsList != NULL) { 295 while (key->nsList[i] != NULL) 296 i++; 297 } 298 key->nsNr = i; 299 300 /* 301 * Split the | and register it as as many keys 302 */ 303 current = end = 0; 304 while (match[current] != 0) { 305 start = current; 306 while (IS_BLANK_CH(match[current])) 307 current++; 308 end = current; 309 while ((match[end] != 0) && (match[end] != '|')) { 310 if (match[end] == '[') { 311 end = skipPredicate(match, end); 312 if (end <= 0) { 313 xsltTransformError(NULL, style, inst, 314 "key pattern is malformed: %s", 315 key->match); 316 if (style != NULL) style->errors++; 317 goto error; 318 } 319 } else 320 end++; 321 } 322 if (current == end) { 323 xsltTransformError(NULL, style, inst, 324 "key pattern is empty\n"); 325 if (style != NULL) style->errors++; 326 goto error; 327 } 328 if (match[start] != '/') { 329 pattern = xmlStrcat(pattern, (xmlChar *)"//"); 330 if (pattern == NULL) { 331 if (style != NULL) style->errors++; 332 goto error; 333 } 334 } 335 pattern = xmlStrncat(pattern, &match[start], end - start); 336 if (pattern == NULL) { 337 if (style != NULL) style->errors++; 338 goto error; 339 } 340 341 if (match[end] == '|') { 342 pattern = xmlStrcat(pattern, (xmlChar *)"|"); 343 end++; 344 } 345 current = end; 346 } 347 #ifdef WITH_XSLT_DEBUG_KEYS 348 xsltGenericDebug(xsltGenericDebugContext, 349 " resulting pattern %s\n", pattern); 350 #endif 351 /* 352 * XSLT-1: "It is an error for the value of either the use 353 * attribute or the match attribute to contain a 354 * VariableReference." 355 * TODO: We should report a variable-reference at compile-time. 356 * Maybe a search for "$", if it occurs outside of quotation 357 * marks, could be sufficient. 358 */ 359 key->comp = xsltXPathCompile(style, pattern); 360 if (key->comp == NULL) { 361 xsltTransformError(NULL, style, inst, 362 "xsl:key : XPath pattern compilation failed '%s'\n", 363 pattern); 364 if (style != NULL) style->errors++; 365 } 366 key->usecomp = xsltXPathCompile(style, use); 367 if (key->usecomp == NULL) { 368 xsltTransformError(NULL, style, inst, 369 "xsl:key : XPath pattern compilation failed '%s'\n", 370 use); 371 if (style != NULL) style->errors++; 372 } 373 374 /* 375 * Sometimes the stylesheet writer use the order to ease the 376 * resolution of keys when they are dependant, keep the provided 377 * order so add the new one at the end. 378 */ 379 if (style->keys == NULL) { 380 style->keys = key; 381 } else { 382 xsltKeyDefPtr prev = style->keys; 383 384 while (prev->next != NULL) 385 prev = prev->next; 386 387 prev->next = key; 388 } 389 key->next = NULL; 390 391 error: 392 if (pattern != NULL) 393 xmlFree(pattern); 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 xpctxt->node = cur; 748 /* 749 * Process the 'use' of the xsl:key. 750 * SPEC XSLT 1.0: 751 * "The use attribute is an expression specifying the values of 752 * the key; the expression is evaluated once for each node that 753 * matches the pattern." 754 */ 755 if (useRes != NULL) 756 xmlXPathFreeObject(useRes); 757 useRes = xmlXPathCompiledEval(keyDef->usecomp, xpctxt); 758 if (useRes == NULL) { 759 xsltTransformError(ctxt, NULL, keyDef->inst, 760 "Failed to evaluate the 'use' expression.\n"); 761 ctxt->state = XSLT_STATE_STOPPED; 762 break; 763 } 764 if (useRes->type == XPATH_NODESET) { 765 if ((useRes->nodesetval != NULL) && 766 (useRes->nodesetval->nodeNr != 0)) 767 { 768 len = useRes->nodesetval->nodeNr; 769 str = xmlXPathCastNodeToString(useRes->nodesetval->nodeTab[0]); 770 } else { 771 continue; 772 } 773 } else { 774 len = 1; 775 if (useRes->type == XPATH_STRING) { 776 /* 777 * Consume the string value. 778 */ 779 str = useRes->stringval; 780 useRes->stringval = NULL; 781 } else { 782 str = xmlXPathCastToString(useRes); 783 } 784 } 785 /* 786 * Process all strings. 787 */ 788 k = 0; 789 while (1) { 790 if (str == NULL) 791 goto next_string; 792 793 #ifdef WITH_XSLT_DEBUG_KEYS 794 XSLT_TRACE(ctxt,XSLT_TRACE_KEYS,xsltGenericDebug(xsltGenericDebugContext, 795 "xsl:key : node associated to ('%s', '%s')\n", keyDef->name, str)); 796 #endif 797 798 keylist = xmlHashLookup(table->keys, str); 799 if (keylist == NULL) { 800 keylist = xmlXPathNodeSetCreate(cur); 801 if (keylist == NULL) 802 goto error; 803 xmlHashAddEntry(table->keys, str, keylist); 804 } else { 805 /* 806 * TODO: How do we know if this function failed? 807 */ 808 xmlXPathNodeSetAdd(keylist, cur); 809 } 810 switch (cur->type) { 811 case XML_ELEMENT_NODE: 812 case XML_TEXT_NODE: 813 case XML_CDATA_SECTION_NODE: 814 case XML_PI_NODE: 815 case XML_COMMENT_NODE: 816 cur->psvi = keyDef; 817 break; 818 case XML_ATTRIBUTE_NODE: 819 ((xmlAttrPtr) cur)->psvi = keyDef; 820 break; 821 case XML_DOCUMENT_NODE: 822 case XML_HTML_DOCUMENT_NODE: 823 ((xmlDocPtr) cur)->psvi = keyDef; 824 break; 825 default: 826 break; 827 } 828 xmlFree(str); 829 str = NULL; 830 831 next_string: 832 k++; 833 if (k >= len) 834 break; 835 str = xmlXPathCastNodeToString(useRes->nodesetval->nodeTab[k]); 836 } 837 } 838 839 exit: 840 error: 841 ctxt->keyInitLevel--; 842 /* 843 * Restore context state. 844 */ 845 xpctxt->doc = oldXPDoc; 846 xpctxt->nsNr = oldXPNsNr; 847 xpctxt->namespaces = oldXPNamespaces; 848 xpctxt->proximityPosition = oldXPPos; 849 xpctxt->contextSize = oldXPSize; 850 851 ctxt->node = oldContextNode; 852 ctxt->document = oldDocInfo; 853 ctxt->inst = oldInst; 854 855 if (str) 856 xmlFree(str); 857 if (useRes != NULL) 858 xmlXPathFreeObject(useRes); 859 if (matchRes != NULL) 860 xmlXPathFreeObject(matchRes); 861 return(0); 862 } 863 864 /** 865 * xsltInitCtxtKeys: 866 * @ctxt: an XSLT transformation context 867 * @idoc: a document info 868 * 869 * Computes all the keys tables for the current input document. 870 * Should be done before global varibales are initialized. 871 * NOTE: Not used anymore in the refactored code. 872 */ 873 void 874 xsltInitCtxtKeys(xsltTransformContextPtr ctxt, xsltDocumentPtr idoc) { 875 xsltStylesheetPtr style; 876 xsltKeyDefPtr keyDef; 877 878 if ((ctxt == NULL) || (idoc == NULL)) 879 return; 880 881 #ifdef KEY_INIT_DEBUG 882 fprintf(stderr, "xsltInitCtxtKeys on document\n"); 883 #endif 884 885 #ifdef WITH_XSLT_DEBUG_KEYS 886 if ((idoc->doc != NULL) && (idoc->doc->URL != NULL)) 887 XSLT_TRACE(ctxt,XSLT_TRACE_KEYS,xsltGenericDebug(xsltGenericDebugContext, "Initializing keys on %s\n", 888 idoc->doc->URL)); 889 #endif 890 style = ctxt->style; 891 while (style != NULL) { 892 keyDef = (xsltKeyDefPtr) style->keys; 893 while (keyDef != NULL) { 894 xsltInitCtxtKey(ctxt, idoc, keyDef); 895 896 keyDef = keyDef->next; 897 } 898 899 style = xsltNextImport(style); 900 } 901 902 #ifdef KEY_INIT_DEBUG 903 fprintf(stderr, "xsltInitCtxtKeys on document: done\n"); 904 #endif 905 906 } 907 908 /** 909 * xsltFreeDocumentKeys: 910 * @idoc: a XSLT document 911 * 912 * Free the keys associated to a document 913 */ 914 void 915 xsltFreeDocumentKeys(xsltDocumentPtr idoc) { 916 if (idoc != NULL) 917 xsltFreeKeyTableList(idoc->keys); 918 } 919 920