1 /* 2 * attributes.c: Implementation of the XSLT attributes handling 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 #define WITH_XSLT_DEBUG_ATTRIBUTES 15 #ifdef WITH_XSLT_DEBUG 16 #define WITH_XSLT_DEBUG_ATTRIBUTES 17 #endif 18 19 /* 20 * Useful macros 21 */ 22 #ifdef IS_BLANK 23 #undef IS_BLANK 24 #endif 25 26 #define IS_BLANK(c) (((c) == 0x20) || ((c) == 0x09) || ((c) == 0xA) || \ 27 ((c) == 0x0D)) 28 29 #define IS_BLANK_NODE(n) \ 30 (((n)->type == XML_TEXT_NODE) && (xsltIsBlank((n)->content))) 31 32 #define ATTRSET_UNRESOLVED 0 33 #define ATTRSET_RESOLVING 1 34 #define ATTRSET_RESOLVED 2 35 36 37 /* 38 * The in-memory structure corresponding to an XSLT Attribute in 39 * an attribute set 40 */ 41 42 43 typedef struct _xsltAttrElem xsltAttrElem; 44 typedef xsltAttrElem *xsltAttrElemPtr; 45 struct _xsltAttrElem { 46 struct _xsltAttrElem *next;/* chained list */ 47 xmlNodePtr attr; /* the xsl:attribute definition */ 48 }; 49 50 typedef struct _xsltUseAttrSet xsltUseAttrSet; 51 typedef xsltUseAttrSet *xsltUseAttrSetPtr; 52 struct _xsltUseAttrSet { 53 struct _xsltUseAttrSet *next; /* chained list */ 54 const xmlChar *ncname; 55 const xmlChar *ns; 56 }; 57 58 typedef struct _xsltAttrSet xsltAttrSet; 59 typedef xsltAttrSet *xsltAttrSetPtr; 60 struct _xsltAttrSet { 61 int state; 62 xsltAttrElemPtr attrs; /* list head */ 63 xsltUseAttrSetPtr useAttrSets; /* list head */ 64 }; 65 66 typedef struct _xsltAttrSetContext xsltAttrSetContext; 67 typedef xsltAttrSetContext *xsltAttrSetContextPtr; 68 struct _xsltAttrSetContext { 69 xsltStylesheetPtr topStyle; 70 xsltStylesheetPtr style; 71 }; 72 73 static void 74 xsltResolveAttrSet(xsltAttrSetPtr set, xsltStylesheetPtr topStyle, 75 xsltStylesheetPtr style, const xmlChar *name, 76 const xmlChar *ns, int depth); 77 78 /************************************************************************ 79 * * 80 * XSLT Attribute handling * 81 * * 82 ************************************************************************/ 83 84 /** 85 * xsltNewAttrElem: 86 * @attr: the new xsl:attribute node 87 * 88 * Create a new XSLT AttrElem 89 * 90 * Returns the newly allocated xsltAttrElemPtr or NULL in case of error 91 */ 92 static xsltAttrElemPtr 93 xsltNewAttrElem(xmlNodePtr attr) { 94 xsltAttrElemPtr cur; 95 96 cur = (xsltAttrElemPtr) xmlMalloc(sizeof(xsltAttrElem)); 97 if (cur == NULL) { 98 xsltGenericError(xsltGenericErrorContext, 99 "xsltNewAttrElem : malloc failed\n"); 100 return(NULL); 101 } 102 memset(cur, 0, sizeof(xsltAttrElem)); 103 cur->attr = attr; 104 return(cur); 105 } 106 107 /** 108 * xsltFreeAttrElem: 109 * @attr: an XSLT AttrElem 110 * 111 * Free up the memory allocated by @attr 112 */ 113 static void 114 xsltFreeAttrElem(xsltAttrElemPtr attr) { 115 xmlFree(attr); 116 } 117 118 /** 119 * xsltFreeAttrElemList: 120 * @list: an XSLT AttrElem list 121 * 122 * Free up the memory allocated by @list 123 */ 124 static void 125 xsltFreeAttrElemList(xsltAttrElemPtr list) { 126 xsltAttrElemPtr next; 127 128 while (list != NULL) { 129 next = list->next; 130 xsltFreeAttrElem(list); 131 list = next; 132 } 133 } 134 135 /** 136 * xsltAddAttrElemList: 137 * @list: an XSLT AttrElem list 138 * @attr: the new xsl:attribute node 139 * 140 * Add the new attribute to the list. 141 * 142 * Returns the new list pointer 143 */ 144 static xsltAttrElemPtr 145 xsltAddAttrElemList(xsltAttrElemPtr list, xmlNodePtr attr) { 146 xsltAttrElemPtr next, cur; 147 148 if (attr == NULL) 149 return(list); 150 if (list == NULL) 151 return(xsltNewAttrElem(attr)); 152 cur = list; 153 while (cur != NULL) { 154 next = cur->next; 155 if (next == NULL) { 156 cur->next = xsltNewAttrElem(attr); 157 return(list); 158 } 159 cur = next; 160 } 161 return(list); 162 } 163 164 /** 165 * xsltNewUseAttrSet: 166 * @ncname: local name 167 * @ns: namespace URI 168 * 169 * Create a new XSLT UseAttrSet 170 * 171 * Returns the newly allocated xsltUseAttrSetPtr or NULL in case of error. 172 */ 173 static xsltUseAttrSetPtr 174 xsltNewUseAttrSet(const xmlChar *ncname, const xmlChar *ns) { 175 xsltUseAttrSetPtr cur; 176 177 cur = (xsltUseAttrSetPtr) xmlMalloc(sizeof(xsltUseAttrSet)); 178 if (cur == NULL) { 179 xsltGenericError(xsltGenericErrorContext, 180 "xsltNewUseAttrSet : malloc failed\n"); 181 return(NULL); 182 } 183 memset(cur, 0, sizeof(xsltUseAttrSet)); 184 cur->ncname = ncname; 185 cur->ns = ns; 186 return(cur); 187 } 188 189 /** 190 * xsltFreeUseAttrSet: 191 * @use: an XSLT UseAttrSet 192 * 193 * Free up the memory allocated by @use 194 */ 195 static void 196 xsltFreeUseAttrSet(xsltUseAttrSetPtr use) { 197 xmlFree(use); 198 } 199 200 /** 201 * xsltFreeUseAttrSetList: 202 * @list: an XSLT UseAttrSet list 203 * 204 * Free up the memory allocated by @list 205 */ 206 static void 207 xsltFreeUseAttrSetList(xsltUseAttrSetPtr list) { 208 xsltUseAttrSetPtr next; 209 210 while (list != NULL) { 211 next = list->next; 212 xsltFreeUseAttrSet(list); 213 list = next; 214 } 215 } 216 217 /** 218 * xsltAddUseAttrSetList: 219 * @list: a xsltUseAttrSet list 220 * @ncname: local name 221 * @ns: namespace URI 222 * 223 * Add the use-attribute-set name to the list. 224 * 225 * Returns the new list pointer. 226 */ 227 static xsltUseAttrSetPtr 228 xsltAddUseAttrSetList(xsltUseAttrSetPtr list, const xmlChar *ncname, 229 const xmlChar *ns) { 230 xsltUseAttrSetPtr next, cur; 231 232 if (ncname == NULL) 233 return(list); 234 if (list == NULL) 235 return(xsltNewUseAttrSet(ncname, ns)); 236 cur = list; 237 while (cur != NULL) { 238 if ((cur->ncname == ncname) && (cur->ns == ns)) 239 return(list); 240 next = cur->next; 241 if (next == NULL) { 242 cur->next = xsltNewUseAttrSet(ncname, ns); 243 return(list); 244 } 245 cur = next; 246 } 247 return(list); 248 } 249 250 /** 251 * xsltNewAttrSet: 252 * 253 * Create a new attribute set. 254 * 255 * Returns the newly allocated xsltAttrSetPtr or NULL in case of error. 256 */ 257 static xsltAttrSetPtr 258 xsltNewAttrSet() { 259 xsltAttrSetPtr cur; 260 261 cur = (xsltAttrSetPtr) xmlMalloc(sizeof(xsltAttrSet)); 262 if (cur == NULL) { 263 xsltGenericError(xsltGenericErrorContext, 264 "xsltNewAttrSet : malloc failed\n"); 265 return(NULL); 266 } 267 memset(cur, 0, sizeof(xsltAttrSet)); 268 return(cur); 269 } 270 271 /** 272 * xsltFreeAttrSet: 273 * @set: an attribute set 274 * 275 * Free memory allocated by @set 276 */ 277 static void 278 xsltFreeAttrSet(xsltAttrSetPtr set) { 279 if (set == NULL) 280 return; 281 282 xsltFreeAttrElemList(set->attrs); 283 xsltFreeUseAttrSetList(set->useAttrSets); 284 xmlFree(set); 285 } 286 287 /** 288 * xsltMergeAttrSets: 289 * @set: an attribute set 290 * @other: another attribute set 291 * 292 * Add all the attributes from @other to @set, 293 * but drop redefinition of existing values. 294 */ 295 static void 296 xsltMergeAttrSets(xsltAttrSetPtr set, xsltAttrSetPtr other) { 297 xsltAttrElemPtr cur; 298 xsltAttrElemPtr old = other->attrs; 299 int add; 300 301 while (old != NULL) { 302 /* 303 * Check that the attribute is not yet in the list 304 */ 305 cur = set->attrs; 306 add = 1; 307 while (cur != NULL) { 308 xsltStylePreCompPtr curComp = cur->attr->psvi; 309 xsltStylePreCompPtr oldComp = old->attr->psvi; 310 311 if ((curComp->name == oldComp->name) && 312 (curComp->ns == oldComp->ns)) { 313 add = 0; 314 break; 315 } 316 if (cur->next == NULL) 317 break; 318 cur = cur->next; 319 } 320 321 if (add == 1) { 322 if (cur == NULL) { 323 set->attrs = xsltNewAttrElem(old->attr); 324 } else if (add) { 325 cur->next = xsltNewAttrElem(old->attr); 326 } 327 } 328 329 old = old->next; 330 } 331 } 332 333 /************************************************************************ 334 * * 335 * Module interfaces * 336 * * 337 ************************************************************************/ 338 339 /** 340 * xsltParseStylesheetAttributeSet: 341 * @style: the XSLT stylesheet 342 * @cur: the "attribute-set" element 343 * 344 * parse an XSLT stylesheet attribute-set element 345 */ 346 347 void 348 xsltParseStylesheetAttributeSet(xsltStylesheetPtr style, xmlNodePtr cur) { 349 const xmlChar *ncname; 350 const xmlChar *prefix; 351 const xmlChar *nsUri = NULL; 352 xmlChar *value; 353 xmlNodePtr child; 354 xsltAttrSetPtr set; 355 356 if ((cur == NULL) || (style == NULL) || (cur->type != XML_ELEMENT_NODE)) 357 return; 358 359 value = xmlGetNsProp(cur, (const xmlChar *)"name", NULL); 360 if ((value == NULL) || (*value == 0)) { 361 xsltGenericError(xsltGenericErrorContext, 362 "xsl:attribute-set : name is missing\n"); 363 if (value) 364 xmlFree(value); 365 return; 366 } 367 368 if (xmlValidateQName(value, 0)) { 369 xsltTransformError(NULL, style, cur, 370 "xsl:attribute-set : The name '%s' is not a valid QName.\n", 371 value); 372 style->errors++; 373 xmlFree(value); 374 return; 375 } 376 377 ncname = xsltSplitQName(style->dict, value, &prefix); 378 xmlFree(value); 379 value = NULL; 380 if (prefix != NULL) { 381 xmlNsPtr ns = xmlSearchNs(style->doc, cur, prefix); 382 if (ns == NULL) { 383 xsltTransformError(NULL, style, cur, 384 "xsl:attribute-set : No namespace found for QName '%s:%s'\n", 385 prefix, ncname); 386 style->errors++; 387 return; 388 } 389 nsUri = ns->href; 390 } 391 392 if (style->attributeSets == NULL) { 393 #ifdef WITH_XSLT_DEBUG_ATTRIBUTES 394 xsltGenericDebug(xsltGenericDebugContext, 395 "creating attribute set table\n"); 396 #endif 397 style->attributeSets = xmlHashCreate(10); 398 } 399 if (style->attributeSets == NULL) 400 return; 401 402 set = xmlHashLookup2(style->attributeSets, ncname, nsUri); 403 if (set == NULL) { 404 set = xsltNewAttrSet(); 405 if (set == NULL) 406 return; 407 xmlHashAddEntry2(style->attributeSets, ncname, nsUri, set); 408 } 409 410 /* 411 * Parse the content. Only xsl:attribute elements are allowed. 412 */ 413 child = cur->children; 414 while (child != NULL) { 415 /* 416 * Report invalid nodes. 417 */ 418 if ((child->type != XML_ELEMENT_NODE) || 419 (child->ns == NULL) || 420 (! IS_XSLT_ELEM(child))) 421 { 422 if (child->type == XML_ELEMENT_NODE) 423 xsltTransformError(NULL, style, child, 424 "xsl:attribute-set : unexpected child %s\n", 425 child->name); 426 else 427 xsltTransformError(NULL, style, child, 428 "xsl:attribute-set : child of unexpected type\n"); 429 } else if (!IS_XSLT_NAME(child, "attribute")) { 430 xsltTransformError(NULL, style, child, 431 "xsl:attribute-set : unexpected child xsl:%s\n", 432 child->name); 433 } else { 434 #ifdef WITH_XSLT_DEBUG_ATTRIBUTES 435 xsltGenericDebug(xsltGenericDebugContext, 436 "add attribute to list %s\n", ncname); 437 #endif 438 xsltStylePreCompute(style, child); 439 if (child->children != NULL) { 440 #ifdef XSLT_REFACTORED 441 xsltParseSequenceConstructor(XSLT_CCTXT(style), 442 child->children); 443 #else 444 xsltParseTemplateContent(style, child); 445 #endif 446 } 447 if (child->psvi == NULL) { 448 xsltTransformError(NULL, style, child, 449 "xsl:attribute-set : internal error, attribute %s not " 450 "compiled\n", child->name); 451 } 452 else { 453 set->attrs = xsltAddAttrElemList(set->attrs, child); 454 } 455 } 456 457 child = child->next; 458 } 459 460 /* 461 * Process attribute "use-attribute-sets". 462 */ 463 value = xmlGetNsProp(cur, BAD_CAST "use-attribute-sets", NULL); 464 if (value != NULL) { 465 const xmlChar *curval, *endval; 466 curval = value; 467 while (*curval != 0) { 468 while (IS_BLANK(*curval)) curval++; 469 if (*curval == 0) 470 break; 471 endval = curval; 472 while ((*endval != 0) && (!IS_BLANK(*endval))) endval++; 473 curval = xmlDictLookup(style->dict, curval, endval - curval); 474 if (curval) { 475 const xmlChar *ncname2 = NULL; 476 const xmlChar *prefix2 = NULL; 477 const xmlChar *nsUri2 = NULL; 478 479 #ifdef WITH_XSLT_DEBUG_ATTRIBUTES 480 xsltGenericDebug(xsltGenericDebugContext, 481 "xsl:attribute-set : %s adds use %s\n", ncname, curval); 482 #endif 483 484 if (xmlValidateQName(curval, 0)) { 485 xsltTransformError(NULL, style, cur, 486 "xsl:attribute-set : The name '%s' in " 487 "use-attribute-sets is not a valid QName.\n", curval); 488 style->errors++; 489 xmlFree(value); 490 return; 491 } 492 493 ncname2 = xsltSplitQName(style->dict, curval, &prefix2); 494 if (prefix2 != NULL) { 495 xmlNsPtr ns2 = xmlSearchNs(style->doc, cur, prefix2); 496 if (ns2 == NULL) { 497 xsltTransformError(NULL, style, cur, 498 "xsl:attribute-set : No namespace found for QName " 499 "'%s:%s' in use-attribute-sets\n", 500 prefix2, ncname2); 501 style->errors++; 502 xmlFree(value); 503 return; 504 } 505 nsUri2 = ns2->href; 506 } 507 set->useAttrSets = xsltAddUseAttrSetList(set->useAttrSets, 508 ncname2, nsUri2); 509 } 510 curval = endval; 511 } 512 xmlFree(value); 513 value = NULL; 514 } 515 516 #ifdef WITH_XSLT_DEBUG_ATTRIBUTES 517 xsltGenericDebug(xsltGenericDebugContext, 518 "updated attribute list %s\n", ncname); 519 #endif 520 } 521 522 /** 523 * xsltResolveUseAttrSets: 524 * @set: the attribute set 525 * @asctx: the context for attribute set resolution 526 * @depth: recursion depth 527 * 528 * Process "use-attribute-sets". 529 */ 530 static void 531 xsltResolveUseAttrSets(xsltAttrSetPtr set, xsltStylesheetPtr topStyle, 532 int depth) { 533 xsltStylesheetPtr cur; 534 xsltAttrSetPtr other; 535 xsltUseAttrSetPtr use = set->useAttrSets; 536 xsltUseAttrSetPtr next; 537 538 while (use != NULL) { 539 /* 540 * Iterate top stylesheet and all imports. 541 */ 542 cur = topStyle; 543 while (cur != NULL) { 544 if (cur->attributeSets) { 545 other = xmlHashLookup2(cur->attributeSets, use->ncname, 546 use->ns); 547 if (other != NULL) { 548 xsltResolveAttrSet(other, topStyle, cur, use->ncname, 549 use->ns, depth + 1); 550 xsltMergeAttrSets(set, other); 551 break; 552 } 553 } 554 cur = xsltNextImport(cur); 555 } 556 557 next = use->next; 558 /* Free useAttrSets early. */ 559 xsltFreeUseAttrSet(use); 560 use = next; 561 } 562 563 set->useAttrSets = NULL; 564 } 565 566 /** 567 * xsltResolveAttrSet: 568 * @set: the attribute set 569 * @asctx: the context for attribute set resolution 570 * @name: the local name of the attirbute set 571 * @ns: the namespace of the attribute set 572 * @depth: recursion depth 573 * 574 * resolve the references in an attribute set. 575 */ 576 static void 577 xsltResolveAttrSet(xsltAttrSetPtr set, xsltStylesheetPtr topStyle, 578 xsltStylesheetPtr style, const xmlChar *name, 579 const xmlChar *ns, int depth) { 580 xsltStylesheetPtr cur; 581 xsltAttrSetPtr other; 582 583 if (set->state == ATTRSET_RESOLVED) 584 return; 585 if (set->state == ATTRSET_RESOLVING) { 586 xsltTransformError(NULL, topStyle, NULL, 587 "xsl:attribute-set : use-attribute-sets recursion detected" 588 " on %s\n", name); 589 topStyle->errors++; 590 set->state = ATTRSET_RESOLVED; 591 return; 592 } 593 if (depth > 100) { 594 xsltTransformError(NULL, topStyle, NULL, 595 "xsl:attribute-set : use-attribute-sets maximum recursion " 596 "depth exceeded on %s\n", name); 597 topStyle->errors++; 598 return; 599 } 600 601 set->state = ATTRSET_RESOLVING; 602 603 xsltResolveUseAttrSets(set, topStyle, depth); 604 605 /* Merge imported sets. */ 606 cur = xsltNextImport(style); 607 while (cur != NULL) { 608 if (cur->attributeSets != NULL) { 609 other = xmlHashLookup2(cur->attributeSets, name, ns); 610 611 if (other != NULL) { 612 #ifdef WITH_XSLT_DEBUG_ATTRIBUTES 613 xsltGenericDebug(xsltGenericDebugContext, 614 "xsl:attribute-set : merging import for %s\n", name); 615 #endif 616 xsltResolveUseAttrSets(other, topStyle, depth); 617 xsltMergeAttrSets(set, other); 618 xmlHashRemoveEntry2(cur->attributeSets, name, ns, NULL); 619 xsltFreeAttrSet(other); 620 } 621 } 622 623 cur = xsltNextImport(cur); 624 } 625 626 set->state = ATTRSET_RESOLVED; 627 } 628 629 /** 630 * xsltResolveSASCallback: 631 * @set: the attribute set 632 * @asctx: the context for attribute set resolution 633 * @name: the local name of the attirbute set 634 * @ns: the namespace of the attribute set 635 * 636 * resolve the references in an attribute set. 637 */ 638 static void 639 xsltResolveSASCallback(xsltAttrSetPtr set, xsltAttrSetContextPtr asctx, 640 const xmlChar *name, const xmlChar *ns, 641 ATTRIBUTE_UNUSED const xmlChar *ignored) { 642 xsltStylesheetPtr topStyle = asctx->topStyle; 643 xsltStylesheetPtr style = asctx->style; 644 645 xsltResolveAttrSet(set, topStyle, style, name, ns, 1); 646 647 /* Move attribute sets to top stylesheet. */ 648 if (style != topStyle) { 649 /* 650 * This imported stylesheet won't be visited anymore. Don't bother 651 * removing the hash entry. 652 */ 653 if (xmlHashAddEntry2(topStyle->attributeSets, name, ns, set) < 0) { 654 xsltGenericError(xsltGenericErrorContext, 655 "xsl:attribute-set : internal error, can't move imported " 656 " attribute set %s\n", name); 657 } 658 } 659 } 660 661 /** 662 * xsltResolveStylesheetAttributeSet: 663 * @style: the XSLT stylesheet 664 * 665 * resolve the references between attribute sets. 666 */ 667 void 668 xsltResolveStylesheetAttributeSet(xsltStylesheetPtr style) { 669 xsltStylesheetPtr cur; 670 xsltAttrSetContext asctx; 671 672 #ifdef WITH_XSLT_DEBUG_ATTRIBUTES 673 xsltGenericDebug(xsltGenericDebugContext, 674 "Resolving attribute sets references\n"); 675 #endif 676 asctx.topStyle = style; 677 cur = style; 678 while (cur != NULL) { 679 if (cur->attributeSets != NULL) { 680 if (style->attributeSets == NULL) { 681 #ifdef WITH_XSLT_DEBUG_ATTRIBUTES 682 xsltGenericDebug(xsltGenericDebugContext, 683 "creating attribute set table\n"); 684 #endif 685 style->attributeSets = xmlHashCreate(10); 686 } 687 asctx.style = cur; 688 xmlHashScanFull(cur->attributeSets, 689 (xmlHashScannerFull) xsltResolveSASCallback, &asctx); 690 691 if (cur != style) { 692 /* 693 * the attribute lists have either been migrated to style 694 * or freed directly in xsltResolveSASCallback() 695 */ 696 xmlHashFree(cur->attributeSets, NULL); 697 cur->attributeSets = NULL; 698 } 699 } 700 cur = xsltNextImport(cur); 701 } 702 } 703 704 /** 705 * xsltAttribute: 706 * @ctxt: a XSLT process context 707 * @contextNode: the current node in the source tree 708 * @inst: the xsl:attribute element 709 * @castedComp: precomputed information 710 * 711 * Process the xslt attribute node on the source node 712 */ 713 void 714 xsltAttribute(xsltTransformContextPtr ctxt, 715 xmlNodePtr contextNode, 716 xmlNodePtr inst, 717 xsltStylePreCompPtr castedComp) 718 { 719 #ifdef XSLT_REFACTORED 720 xsltStyleItemAttributePtr comp = 721 (xsltStyleItemAttributePtr) castedComp; 722 #else 723 xsltStylePreCompPtr comp = castedComp; 724 #endif 725 xmlNodePtr targetElem; 726 xmlChar *prop = NULL; 727 const xmlChar *name = NULL, *prefix = NULL, *nsName = NULL; 728 xmlChar *value = NULL; 729 xmlNsPtr ns = NULL; 730 xmlAttrPtr attr; 731 732 if ((ctxt == NULL) || (contextNode == NULL) || (inst == NULL) || 733 (inst->type != XML_ELEMENT_NODE) ) 734 return; 735 736 /* 737 * A comp->has_name == 0 indicates that we need to skip this instruction, 738 * since it was evaluated to be invalid already during compilation. 739 */ 740 if (!comp->has_name) 741 return; 742 /* 743 * BIG NOTE: This previously used xsltGetSpecialNamespace() and 744 * xsltGetNamespace(), but since both are not appropriate, we 745 * will process namespace lookup here to avoid adding yet another 746 * ns-lookup function to namespaces.c. 747 */ 748 /* 749 * SPEC XSLT 1.0: Error cases: 750 * - Creating nodes other than text nodes during the instantiation of 751 * the content of the xsl:attribute element; implementations may 752 * either signal the error or ignore the offending nodes." 753 */ 754 755 if (comp == NULL) { 756 xsltTransformError(ctxt, NULL, inst, 757 "Internal error in xsltAttribute(): " 758 "The XSLT 'attribute' instruction was not compiled.\n"); 759 return; 760 } 761 /* 762 * TODO: Shouldn't ctxt->insert == NULL be treated as an internal error? 763 * So report an internal error? 764 */ 765 if (ctxt->insert == NULL) 766 return; 767 /* 768 * SPEC XSLT 1.0: 769 * "Adding an attribute to a node that is not an element; 770 * implementations may either signal the error or ignore the attribute." 771 * 772 * TODO: I think we should signal such errors in the future, and maybe 773 * provide an option to ignore such errors. 774 */ 775 targetElem = ctxt->insert; 776 if (targetElem->type != XML_ELEMENT_NODE) 777 return; 778 779 /* 780 * SPEC XSLT 1.0: 781 * "Adding an attribute to an element after children have been added 782 * to it; implementations may either signal the error or ignore the 783 * attribute." 784 * 785 * TODO: We should decide whether not to report such errors or 786 * to ignore them; note that we *ignore* if the parent is not an 787 * element, but here we report an error. 788 */ 789 if (targetElem->children != NULL) { 790 /* 791 * NOTE: Ah! This seems to be intended to support streamed 792 * result generation!. 793 */ 794 xsltTransformError(ctxt, NULL, inst, 795 "xsl:attribute: Cannot add attributes to an " 796 "element if children have been already added " 797 "to the element.\n"); 798 return; 799 } 800 801 /* 802 * Process the name 803 * ---------------- 804 */ 805 806 #ifdef WITH_DEBUGGER 807 if (ctxt->debugStatus != XSLT_DEBUG_NONE) 808 xslHandleDebugger(inst, contextNode, NULL, ctxt); 809 #endif 810 811 if (comp->name == NULL) { 812 /* TODO: fix attr acquisition wrt to the XSLT namespace */ 813 prop = xsltEvalAttrValueTemplate(ctxt, inst, 814 (const xmlChar *) "name", XSLT_NAMESPACE); 815 if (prop == NULL) { 816 xsltTransformError(ctxt, NULL, inst, 817 "xsl:attribute: The attribute 'name' is missing.\n"); 818 goto error; 819 } 820 if (xmlValidateQName(prop, 0)) { 821 xsltTransformError(ctxt, NULL, inst, 822 "xsl:attribute: The effective name '%s' is not a " 823 "valid QName.\n", prop); 824 /* we fall through to catch any further errors, if possible */ 825 } 826 827 /* 828 * Reject a name of "xmlns". 829 */ 830 if (xmlStrEqual(prop, BAD_CAST "xmlns")) { 831 xsltTransformError(ctxt, NULL, inst, 832 "xsl:attribute: The effective name 'xmlns' is not allowed.\n"); 833 xmlFree(prop); 834 goto error; 835 } 836 837 name = xsltSplitQName(ctxt->dict, prop, &prefix); 838 xmlFree(prop); 839 } else { 840 /* 841 * The "name" value was static. 842 */ 843 #ifdef XSLT_REFACTORED 844 prefix = comp->nsPrefix; 845 name = comp->name; 846 #else 847 name = xsltSplitQName(ctxt->dict, comp->name, &prefix); 848 #endif 849 } 850 851 /* 852 * Process namespace semantics 853 * --------------------------- 854 * 855 * Evaluate the namespace name. 856 */ 857 if (comp->has_ns) { 858 /* 859 * The "namespace" attribute was existent. 860 */ 861 if (comp->ns != NULL) { 862 /* 863 * No AVT; just plain text for the namespace name. 864 */ 865 if (comp->ns[0] != 0) 866 nsName = comp->ns; 867 } else { 868 xmlChar *tmpNsName; 869 /* 870 * Eval the AVT. 871 */ 872 /* TODO: check attr acquisition wrt to the XSLT namespace */ 873 tmpNsName = xsltEvalAttrValueTemplate(ctxt, inst, 874 (const xmlChar *) "namespace", XSLT_NAMESPACE); 875 /* 876 * This fixes bug #302020: The AVT might also evaluate to the 877 * empty string; this means that the empty string also indicates 878 * "no namespace". 879 * SPEC XSLT 1.0: 880 * "If the string is empty, then the expanded-name of the 881 * attribute has a null namespace URI." 882 */ 883 if ((tmpNsName != NULL) && (tmpNsName[0] != 0)) 884 nsName = xmlDictLookup(ctxt->dict, BAD_CAST tmpNsName, -1); 885 xmlFree(tmpNsName); 886 } 887 888 if (xmlStrEqual(nsName, BAD_CAST "http://www.w3.org/2000/xmlns/")) { 889 xsltTransformError(ctxt, NULL, inst, 890 "xsl:attribute: Namespace http://www.w3.org/2000/xmlns/ " 891 "forbidden.\n"); 892 goto error; 893 } 894 if (xmlStrEqual(nsName, XML_XML_NAMESPACE)) { 895 prefix = BAD_CAST "xml"; 896 } else if (xmlStrEqual(prefix, BAD_CAST "xml")) { 897 prefix = NULL; 898 } 899 } else if (prefix != NULL) { 900 /* 901 * SPEC XSLT 1.0: 902 * "If the namespace attribute is not present, then the QName is 903 * expanded into an expanded-name using the namespace declarations 904 * in effect for the xsl:attribute element, *not* including any 905 * default namespace declaration." 906 */ 907 ns = xmlSearchNs(inst->doc, inst, prefix); 908 if (ns == NULL) { 909 /* 910 * Note that this is treated as an error now (checked with 911 * Saxon, Xalan-J and MSXML). 912 */ 913 xsltTransformError(ctxt, NULL, inst, 914 "xsl:attribute: The QName '%s:%s' has no " 915 "namespace binding in scope in the stylesheet; " 916 "this is an error, since the namespace was not " 917 "specified by the instruction itself.\n", prefix, name); 918 } else 919 nsName = ns->href; 920 } 921 922 /* 923 * Find/create a matching ns-decl in the result tree. 924 */ 925 ns = NULL; 926 927 #if 0 928 if (0) { 929 /* 930 * OPTIMIZE TODO: How do we know if we are adding to a 931 * fragment or to the result tree? 932 * 933 * If we are adding to a result tree fragment (i.e., not to the 934 * actual result tree), we'll don't bother searching for the 935 * ns-decl, but just store it in the dummy-doc of the result 936 * tree fragment. 937 */ 938 if (nsName != NULL) { 939 /* 940 * TODO: Get the doc of @targetElem. 941 */ 942 ns = xsltTreeAcquireStoredNs(some doc, nsName, prefix); 943 } 944 } 945 #endif 946 947 if (nsName != NULL) { 948 /* 949 * Something about ns-prefixes: 950 * SPEC XSLT 1.0: 951 * "XSLT processors may make use of the prefix of the QName specified 952 * in the name attribute when selecting the prefix used for outputting 953 * the created attribute as XML; however, they are not required to do 954 * so and, if the prefix is xmlns, they must not do so" 955 */ 956 /* 957 * xsl:attribute can produce a scenario where the prefix is NULL, 958 * so generate a prefix. 959 */ 960 if ((prefix == NULL) || xmlStrEqual(prefix, BAD_CAST "xmlns")) { 961 xmlChar *pref = xmlStrdup(BAD_CAST "ns_1"); 962 963 ns = xsltGetSpecialNamespace(ctxt, inst, nsName, pref, targetElem); 964 965 xmlFree(pref); 966 } else { 967 ns = xsltGetSpecialNamespace(ctxt, inst, nsName, prefix, 968 targetElem); 969 } 970 if (ns == NULL) { 971 xsltTransformError(ctxt, NULL, inst, 972 "Namespace fixup error: Failed to acquire an in-scope " 973 "namespace binding for the generated attribute '{%s}%s'.\n", 974 nsName, name); 975 goto error; 976 } 977 } 978 /* 979 * Construction of the value 980 * ------------------------- 981 */ 982 if (inst->children == NULL) { 983 /* 984 * No content. 985 * TODO: Do we need to put the empty string in ? 986 */ 987 attr = xmlSetNsProp(ctxt->insert, ns, name, (const xmlChar *) ""); 988 } else if ((inst->children->next == NULL) && 989 ((inst->children->type == XML_TEXT_NODE) || 990 (inst->children->type == XML_CDATA_SECTION_NODE))) 991 { 992 xmlNodePtr copyTxt; 993 994 /* 995 * xmlSetNsProp() will take care of duplicates. 996 */ 997 attr = xmlSetNsProp(ctxt->insert, ns, name, NULL); 998 if (attr == NULL) /* TODO: report error ? */ 999 goto error; 1000 /* 1001 * This was taken over from xsltCopyText() (transform.c). 1002 */ 1003 if (ctxt->internalized && 1004 (ctxt->insert->doc != NULL) && 1005 (ctxt->insert->doc->dict == ctxt->dict)) 1006 { 1007 copyTxt = xmlNewText(NULL); 1008 if (copyTxt == NULL) /* TODO: report error */ 1009 goto error; 1010 /* 1011 * This is a safe scenario where we don't need to lookup 1012 * the dict. 1013 */ 1014 copyTxt->content = inst->children->content; 1015 /* 1016 * Copy "disable-output-escaping" information. 1017 * TODO: Does this have any effect for attribute values 1018 * anyway? 1019 */ 1020 if (inst->children->name == xmlStringTextNoenc) 1021 copyTxt->name = xmlStringTextNoenc; 1022 } else { 1023 /* 1024 * Copy the value. 1025 */ 1026 copyTxt = xmlNewText(inst->children->content); 1027 if (copyTxt == NULL) /* TODO: report error */ 1028 goto error; 1029 } 1030 attr->children = attr->last = copyTxt; 1031 copyTxt->parent = (xmlNodePtr) attr; 1032 copyTxt->doc = attr->doc; 1033 /* 1034 * Copy "disable-output-escaping" information. 1035 * TODO: Does this have any effect for attribute values 1036 * anyway? 1037 */ 1038 if (inst->children->name == xmlStringTextNoenc) 1039 copyTxt->name = xmlStringTextNoenc; 1040 1041 /* 1042 * since we create the attribute without content IDness must be 1043 * asserted as a second step 1044 */ 1045 if ((copyTxt->content != NULL) && 1046 (xmlIsID(attr->doc, attr->parent, attr))) 1047 xmlAddID(NULL, attr->doc, copyTxt->content, attr); 1048 } else { 1049 /* 1050 * The sequence constructor might be complex, so instantiate it. 1051 */ 1052 value = xsltEvalTemplateString(ctxt, contextNode, inst); 1053 if (value != NULL) { 1054 attr = xmlSetNsProp(ctxt->insert, ns, name, value); 1055 xmlFree(value); 1056 } else { 1057 /* 1058 * TODO: Do we have to add the empty string to the attr? 1059 * TODO: Does a value of NULL indicate an 1060 * error in xsltEvalTemplateString() ? 1061 */ 1062 attr = xmlSetNsProp(ctxt->insert, ns, name, 1063 (const xmlChar *) ""); 1064 } 1065 } 1066 1067 error: 1068 return; 1069 } 1070 1071 /** 1072 * xsltApplyAttributeSet: 1073 * @ctxt: the XSLT stylesheet 1074 * @node: the node in the source tree. 1075 * @inst: the attribute node "xsl:use-attribute-sets" 1076 * @attrSets: the list of QNames of the attribute-sets to be applied 1077 * 1078 * Apply the xsl:use-attribute-sets. 1079 * If @attrSets is NULL, then @inst will be used to exctract this 1080 * value. 1081 * If both, @attrSets and @inst, are NULL, then this will do nothing. 1082 */ 1083 void 1084 xsltApplyAttributeSet(xsltTransformContextPtr ctxt, xmlNodePtr node, 1085 xmlNodePtr inst, 1086 const xmlChar *attrSets) 1087 { 1088 const xmlChar *ncname = NULL; 1089 const xmlChar *prefix = NULL; 1090 const xmlChar *curstr, *endstr; 1091 xsltAttrSetPtr set; 1092 xsltStylesheetPtr style; 1093 1094 if (attrSets == NULL) { 1095 if (inst == NULL) 1096 return; 1097 else { 1098 /* 1099 * Extract the value from @inst. 1100 */ 1101 if (inst->type == XML_ATTRIBUTE_NODE) { 1102 if ( ((xmlAttrPtr) inst)->children != NULL) 1103 attrSets = ((xmlAttrPtr) inst)->children->content; 1104 1105 } 1106 if (attrSets == NULL) { 1107 /* 1108 * TODO: Return an error? 1109 */ 1110 return; 1111 } 1112 } 1113 } 1114 /* 1115 * Parse/apply the list of QNames. 1116 */ 1117 curstr = attrSets; 1118 while (*curstr != 0) { 1119 while (IS_BLANK(*curstr)) 1120 curstr++; 1121 if (*curstr == 0) 1122 break; 1123 endstr = curstr; 1124 while ((*endstr != 0) && (!IS_BLANK(*endstr))) 1125 endstr++; 1126 curstr = xmlDictLookup(ctxt->dict, curstr, endstr - curstr); 1127 if (curstr) { 1128 xmlNsPtr ns; 1129 const xmlChar *nsUri = NULL; 1130 1131 #ifdef WITH_XSLT_DEBUG_ATTRIBUTES 1132 xsltGenericDebug(xsltGenericDebugContext, 1133 "apply attribute set %s\n", curstr); 1134 #endif 1135 1136 if (xmlValidateQName(curstr, 0)) { 1137 xsltTransformError(ctxt, NULL, inst, 1138 "The name '%s' in use-attribute-sets is not a valid " 1139 "QName.\n", curstr); 1140 return; 1141 } 1142 1143 ncname = xsltSplitQName(ctxt->dict, curstr, &prefix); 1144 if (prefix != NULL) { 1145 ns = xmlSearchNs(inst->doc, inst, prefix); 1146 if (ns == NULL) { 1147 xsltTransformError(ctxt, NULL, inst, 1148 "use-attribute-set : No namespace found for QName " 1149 "'%s:%s'\n", prefix, ncname); 1150 return; 1151 } 1152 nsUri = ns->href; 1153 } 1154 1155 style = ctxt->style; 1156 1157 #ifdef WITH_DEBUGGER 1158 if ((style != NULL) && 1159 (style->attributeSets != NULL) && 1160 (ctxt->debugStatus != XSLT_DEBUG_NONE)) 1161 { 1162 set = xmlHashLookup2(style->attributeSets, ncname, nsUri); 1163 if ((set != NULL) && (set->attrs != NULL) && 1164 (set->attrs->attr != NULL)) 1165 xslHandleDebugger(set->attrs->attr->parent, node, NULL, 1166 ctxt); 1167 } 1168 #endif 1169 /* 1170 * Lookup the referenced attribute-set. All attribute sets were 1171 * moved to the top stylesheet so there's no need to iterate 1172 * imported stylesheets 1173 */ 1174 set = xmlHashLookup2(style->attributeSets, ncname, nsUri); 1175 if (set != NULL) { 1176 xsltAttrElemPtr cur = set->attrs; 1177 while (cur != NULL) { 1178 if (cur->attr != NULL) { 1179 xsltAttribute(ctxt, node, cur->attr, 1180 cur->attr->psvi); 1181 } 1182 cur = cur->next; 1183 } 1184 } 1185 } 1186 curstr = endstr; 1187 } 1188 } 1189 1190 /** 1191 * xsltFreeAttributeSetsHashes: 1192 * @style: an XSLT stylesheet 1193 * 1194 * Free up the memory used by attribute sets 1195 */ 1196 void 1197 xsltFreeAttributeSetsHashes(xsltStylesheetPtr style) { 1198 if (style->attributeSets != NULL) 1199 xmlHashFree((xmlHashTablePtr) style->attributeSets, 1200 (xmlHashDeallocator) xsltFreeAttrSet); 1201 style->attributeSets = NULL; 1202 } 1203