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(void *payload, void *data, 640 const xmlChar *name, const xmlChar *ns, 641 ATTRIBUTE_UNUSED const xmlChar *ignored) { 642 xsltAttrSetPtr set = (xsltAttrSetPtr) payload; 643 xsltAttrSetContextPtr asctx = (xsltAttrSetContextPtr) data; 644 xsltStylesheetPtr topStyle = asctx->topStyle; 645 xsltStylesheetPtr style = asctx->style; 646 647 xsltResolveAttrSet(set, topStyle, style, name, ns, 1); 648 649 /* Move attribute sets to top stylesheet. */ 650 if (style != topStyle) { 651 /* 652 * This imported stylesheet won't be visited anymore. Don't bother 653 * removing the hash entry. 654 */ 655 if (xmlHashAddEntry2(topStyle->attributeSets, name, ns, set) < 0) { 656 xsltGenericError(xsltGenericErrorContext, 657 "xsl:attribute-set : internal error, can't move imported " 658 " attribute set %s\n", name); 659 } 660 } 661 } 662 663 /** 664 * xsltResolveStylesheetAttributeSet: 665 * @style: the XSLT stylesheet 666 * 667 * resolve the references between attribute sets. 668 */ 669 void 670 xsltResolveStylesheetAttributeSet(xsltStylesheetPtr style) { 671 xsltStylesheetPtr cur; 672 xsltAttrSetContext asctx; 673 674 #ifdef WITH_XSLT_DEBUG_ATTRIBUTES 675 xsltGenericDebug(xsltGenericDebugContext, 676 "Resolving attribute sets references\n"); 677 #endif 678 asctx.topStyle = style; 679 cur = style; 680 while (cur != NULL) { 681 if (cur->attributeSets != NULL) { 682 if (style->attributeSets == NULL) { 683 #ifdef WITH_XSLT_DEBUG_ATTRIBUTES 684 xsltGenericDebug(xsltGenericDebugContext, 685 "creating attribute set table\n"); 686 #endif 687 style->attributeSets = xmlHashCreate(10); 688 } 689 asctx.style = cur; 690 xmlHashScanFull(cur->attributeSets, xsltResolveSASCallback, 691 &asctx); 692 693 if (cur != style) { 694 /* 695 * the attribute lists have either been migrated to style 696 * or freed directly in xsltResolveSASCallback() 697 */ 698 xmlHashFree(cur->attributeSets, NULL); 699 cur->attributeSets = NULL; 700 } 701 } 702 cur = xsltNextImport(cur); 703 } 704 } 705 706 /** 707 * xsltAttribute: 708 * @ctxt: a XSLT process context 709 * @contextNode: the current node in the source tree 710 * @inst: the xsl:attribute element 711 * @castedComp: precomputed information 712 * 713 * Process the xslt attribute node on the source node 714 */ 715 void 716 xsltAttribute(xsltTransformContextPtr ctxt, 717 xmlNodePtr contextNode, 718 xmlNodePtr inst, 719 xsltElemPreCompPtr castedComp) 720 { 721 #ifdef XSLT_REFACTORED 722 xsltStyleItemAttributePtr comp = 723 (xsltStyleItemAttributePtr) castedComp; 724 #else 725 xsltStylePreCompPtr comp = (xsltStylePreCompPtr) castedComp; 726 #endif 727 xmlNodePtr targetElem; 728 xmlChar *prop = NULL; 729 const xmlChar *name = NULL, *prefix = NULL, *nsName = NULL; 730 xmlChar *value = NULL; 731 xmlNsPtr ns = NULL; 732 xmlAttrPtr attr; 733 734 if ((ctxt == NULL) || (contextNode == NULL) || (inst == NULL) || 735 (inst->type != XML_ELEMENT_NODE) ) 736 return; 737 738 /* 739 * A comp->has_name == 0 indicates that we need to skip this instruction, 740 * since it was evaluated to be invalid already during compilation. 741 */ 742 if (!comp->has_name) 743 return; 744 /* 745 * BIG NOTE: This previously used xsltGetSpecialNamespace() and 746 * xsltGetNamespace(), but since both are not appropriate, we 747 * will process namespace lookup here to avoid adding yet another 748 * ns-lookup function to namespaces.c. 749 */ 750 /* 751 * SPEC XSLT 1.0: Error cases: 752 * - Creating nodes other than text nodes during the instantiation of 753 * the content of the xsl:attribute element; implementations may 754 * either signal the error or ignore the offending nodes." 755 */ 756 757 if (comp == NULL) { 758 xsltTransformError(ctxt, NULL, inst, 759 "Internal error in xsltAttribute(): " 760 "The XSLT 'attribute' instruction was not compiled.\n"); 761 return; 762 } 763 /* 764 * TODO: Shouldn't ctxt->insert == NULL be treated as an internal error? 765 * So report an internal error? 766 */ 767 if (ctxt->insert == NULL) 768 return; 769 /* 770 * SPEC XSLT 1.0: 771 * "Adding an attribute to a node that is not an element; 772 * implementations may either signal the error or ignore the attribute." 773 * 774 * TODO: I think we should signal such errors in the future, and maybe 775 * provide an option to ignore such errors. 776 */ 777 targetElem = ctxt->insert; 778 if (targetElem->type != XML_ELEMENT_NODE) 779 return; 780 781 /* 782 * SPEC XSLT 1.0: 783 * "Adding an attribute to an element after children have been added 784 * to it; implementations may either signal the error or ignore the 785 * attribute." 786 * 787 * TODO: We should decide whether not to report such errors or 788 * to ignore them; note that we *ignore* if the parent is not an 789 * element, but here we report an error. 790 */ 791 if (targetElem->children != NULL) { 792 /* 793 * NOTE: Ah! This seems to be intended to support streamed 794 * result generation!. 795 */ 796 xsltTransformError(ctxt, NULL, inst, 797 "xsl:attribute: Cannot add attributes to an " 798 "element if children have been already added " 799 "to the element.\n"); 800 return; 801 } 802 803 /* 804 * Process the name 805 * ---------------- 806 */ 807 808 #ifdef WITH_DEBUGGER 809 if (ctxt->debugStatus != XSLT_DEBUG_NONE) 810 xslHandleDebugger(inst, contextNode, NULL, ctxt); 811 #endif 812 813 if (comp->name == NULL) { 814 /* TODO: fix attr acquisition wrt to the XSLT namespace */ 815 prop = xsltEvalAttrValueTemplate(ctxt, inst, 816 (const xmlChar *) "name", XSLT_NAMESPACE); 817 if (prop == NULL) { 818 xsltTransformError(ctxt, NULL, inst, 819 "xsl:attribute: The attribute 'name' is missing.\n"); 820 goto error; 821 } 822 if (xmlValidateQName(prop, 0)) { 823 xsltTransformError(ctxt, NULL, inst, 824 "xsl:attribute: The effective name '%s' is not a " 825 "valid QName.\n", prop); 826 /* we fall through to catch any further errors, if possible */ 827 } 828 829 /* 830 * Reject a name of "xmlns". 831 */ 832 if (xmlStrEqual(prop, BAD_CAST "xmlns")) { 833 xsltTransformError(ctxt, NULL, inst, 834 "xsl:attribute: The effective name 'xmlns' is not allowed.\n"); 835 xmlFree(prop); 836 goto error; 837 } 838 839 name = xsltSplitQName(ctxt->dict, prop, &prefix); 840 xmlFree(prop); 841 } else { 842 /* 843 * The "name" value was static. 844 */ 845 #ifdef XSLT_REFACTORED 846 prefix = comp->nsPrefix; 847 name = comp->name; 848 #else 849 name = xsltSplitQName(ctxt->dict, comp->name, &prefix); 850 #endif 851 } 852 853 /* 854 * Process namespace semantics 855 * --------------------------- 856 * 857 * Evaluate the namespace name. 858 */ 859 if (comp->has_ns) { 860 /* 861 * The "namespace" attribute was existent. 862 */ 863 if (comp->ns != NULL) { 864 /* 865 * No AVT; just plain text for the namespace name. 866 */ 867 if (comp->ns[0] != 0) 868 nsName = comp->ns; 869 } else { 870 xmlChar *tmpNsName; 871 /* 872 * Eval the AVT. 873 */ 874 /* TODO: check attr acquisition wrt to the XSLT namespace */ 875 tmpNsName = xsltEvalAttrValueTemplate(ctxt, inst, 876 (const xmlChar *) "namespace", XSLT_NAMESPACE); 877 /* 878 * This fixes bug #302020: The AVT might also evaluate to the 879 * empty string; this means that the empty string also indicates 880 * "no namespace". 881 * SPEC XSLT 1.0: 882 * "If the string is empty, then the expanded-name of the 883 * attribute has a null namespace URI." 884 */ 885 if ((tmpNsName != NULL) && (tmpNsName[0] != 0)) 886 nsName = xmlDictLookup(ctxt->dict, BAD_CAST tmpNsName, -1); 887 xmlFree(tmpNsName); 888 } 889 890 if (xmlStrEqual(nsName, BAD_CAST "http://www.w3.org/2000/xmlns/")) { 891 xsltTransformError(ctxt, NULL, inst, 892 "xsl:attribute: Namespace http://www.w3.org/2000/xmlns/ " 893 "forbidden.\n"); 894 goto error; 895 } 896 if (xmlStrEqual(nsName, XML_XML_NAMESPACE)) { 897 prefix = BAD_CAST "xml"; 898 } else if (xmlStrEqual(prefix, BAD_CAST "xml")) { 899 prefix = NULL; 900 } 901 } else if (prefix != NULL) { 902 /* 903 * SPEC XSLT 1.0: 904 * "If the namespace attribute is not present, then the QName is 905 * expanded into an expanded-name using the namespace declarations 906 * in effect for the xsl:attribute element, *not* including any 907 * default namespace declaration." 908 */ 909 ns = xmlSearchNs(inst->doc, inst, prefix); 910 if (ns == NULL) { 911 /* 912 * Note that this is treated as an error now (checked with 913 * Saxon, Xalan-J and MSXML). 914 */ 915 xsltTransformError(ctxt, NULL, inst, 916 "xsl:attribute: The QName '%s:%s' has no " 917 "namespace binding in scope in the stylesheet; " 918 "this is an error, since the namespace was not " 919 "specified by the instruction itself.\n", prefix, name); 920 } else 921 nsName = ns->href; 922 } 923 924 /* 925 * Find/create a matching ns-decl in the result tree. 926 */ 927 ns = NULL; 928 929 #if 0 930 if (0) { 931 /* 932 * OPTIMIZE TODO: How do we know if we are adding to a 933 * fragment or to the result tree? 934 * 935 * If we are adding to a result tree fragment (i.e., not to the 936 * actual result tree), we'll don't bother searching for the 937 * ns-decl, but just store it in the dummy-doc of the result 938 * tree fragment. 939 */ 940 if (nsName != NULL) { 941 /* 942 * TODO: Get the doc of @targetElem. 943 */ 944 ns = xsltTreeAcquireStoredNs(some doc, nsName, prefix); 945 } 946 } 947 #endif 948 949 if (nsName != NULL) { 950 /* 951 * Something about ns-prefixes: 952 * SPEC XSLT 1.0: 953 * "XSLT processors may make use of the prefix of the QName specified 954 * in the name attribute when selecting the prefix used for outputting 955 * the created attribute as XML; however, they are not required to do 956 * so and, if the prefix is xmlns, they must not do so" 957 */ 958 /* 959 * xsl:attribute can produce a scenario where the prefix is NULL, 960 * so generate a prefix. 961 */ 962 if ((prefix == NULL) || xmlStrEqual(prefix, BAD_CAST "xmlns")) { 963 xmlChar *pref = xmlStrdup(BAD_CAST "ns_1"); 964 965 ns = xsltGetSpecialNamespace(ctxt, inst, nsName, pref, targetElem); 966 967 xmlFree(pref); 968 } else { 969 ns = xsltGetSpecialNamespace(ctxt, inst, nsName, prefix, 970 targetElem); 971 } 972 if (ns == NULL) { 973 xsltTransformError(ctxt, NULL, inst, 974 "Namespace fixup error: Failed to acquire an in-scope " 975 "namespace binding for the generated attribute '{%s}%s'.\n", 976 nsName, name); 977 goto error; 978 } 979 } 980 /* 981 * Construction of the value 982 * ------------------------- 983 */ 984 if (inst->children == NULL) { 985 /* 986 * No content. 987 * TODO: Do we need to put the empty string in ? 988 */ 989 attr = xmlSetNsProp(ctxt->insert, ns, name, (const xmlChar *) ""); 990 } else if ((inst->children->next == NULL) && 991 ((inst->children->type == XML_TEXT_NODE) || 992 (inst->children->type == XML_CDATA_SECTION_NODE))) 993 { 994 xmlNodePtr copyTxt; 995 996 /* 997 * xmlSetNsProp() will take care of duplicates. 998 */ 999 attr = xmlSetNsProp(ctxt->insert, ns, name, NULL); 1000 if (attr == NULL) /* TODO: report error ? */ 1001 goto error; 1002 /* 1003 * This was taken over from xsltCopyText() (transform.c). 1004 */ 1005 if (ctxt->internalized && 1006 (ctxt->insert->doc != NULL) && 1007 (ctxt->insert->doc->dict == ctxt->dict)) 1008 { 1009 copyTxt = xmlNewText(NULL); 1010 if (copyTxt == NULL) /* TODO: report error */ 1011 goto error; 1012 /* 1013 * This is a safe scenario where we don't need to lookup 1014 * the dict. 1015 */ 1016 copyTxt->content = inst->children->content; 1017 /* 1018 * Copy "disable-output-escaping" information. 1019 * TODO: Does this have any effect for attribute values 1020 * anyway? 1021 */ 1022 if (inst->children->name == xmlStringTextNoenc) 1023 copyTxt->name = xmlStringTextNoenc; 1024 } else { 1025 /* 1026 * Copy the value. 1027 */ 1028 copyTxt = xmlNewText(inst->children->content); 1029 if (copyTxt == NULL) /* TODO: report error */ 1030 goto error; 1031 } 1032 attr->children = attr->last = copyTxt; 1033 copyTxt->parent = (xmlNodePtr) attr; 1034 copyTxt->doc = attr->doc; 1035 /* 1036 * Copy "disable-output-escaping" information. 1037 * TODO: Does this have any effect for attribute values 1038 * anyway? 1039 */ 1040 if (inst->children->name == xmlStringTextNoenc) 1041 copyTxt->name = xmlStringTextNoenc; 1042 1043 /* 1044 * since we create the attribute without content IDness must be 1045 * asserted as a second step 1046 */ 1047 if ((copyTxt->content != NULL) && 1048 (xmlIsID(attr->doc, attr->parent, attr))) 1049 xmlAddID(NULL, attr->doc, copyTxt->content, attr); 1050 } else { 1051 /* 1052 * The sequence constructor might be complex, so instantiate it. 1053 */ 1054 value = xsltEvalTemplateString(ctxt, contextNode, inst); 1055 if (value != NULL) { 1056 attr = xmlSetNsProp(ctxt->insert, ns, name, value); 1057 xmlFree(value); 1058 } else { 1059 /* 1060 * TODO: Do we have to add the empty string to the attr? 1061 * TODO: Does a value of NULL indicate an 1062 * error in xsltEvalTemplateString() ? 1063 */ 1064 attr = xmlSetNsProp(ctxt->insert, ns, name, 1065 (const xmlChar *) ""); 1066 } 1067 } 1068 1069 error: 1070 return; 1071 } 1072 1073 /** 1074 * xsltApplyAttributeSet: 1075 * @ctxt: the XSLT stylesheet 1076 * @node: the node in the source tree. 1077 * @inst: the attribute node "xsl:use-attribute-sets" 1078 * @attrSets: the list of QNames of the attribute-sets to be applied 1079 * 1080 * Apply the xsl:use-attribute-sets. 1081 * If @attrSets is NULL, then @inst will be used to exctract this 1082 * value. 1083 * If both, @attrSets and @inst, are NULL, then this will do nothing. 1084 */ 1085 void 1086 xsltApplyAttributeSet(xsltTransformContextPtr ctxt, xmlNodePtr node, 1087 xmlNodePtr inst, 1088 const xmlChar *attrSets) 1089 { 1090 const xmlChar *ncname = NULL; 1091 const xmlChar *prefix = NULL; 1092 const xmlChar *curstr, *endstr; 1093 xsltAttrSetPtr set; 1094 xsltStylesheetPtr style; 1095 1096 if (attrSets == NULL) { 1097 if (inst == NULL) 1098 return; 1099 else { 1100 /* 1101 * Extract the value from @inst. 1102 */ 1103 if (inst->type == XML_ATTRIBUTE_NODE) { 1104 if ( ((xmlAttrPtr) inst)->children != NULL) 1105 attrSets = ((xmlAttrPtr) inst)->children->content; 1106 1107 } 1108 if (attrSets == NULL) { 1109 /* 1110 * TODO: Return an error? 1111 */ 1112 return; 1113 } 1114 } 1115 } 1116 /* 1117 * Parse/apply the list of QNames. 1118 */ 1119 curstr = attrSets; 1120 while (*curstr != 0) { 1121 while (IS_BLANK(*curstr)) 1122 curstr++; 1123 if (*curstr == 0) 1124 break; 1125 endstr = curstr; 1126 while ((*endstr != 0) && (!IS_BLANK(*endstr))) 1127 endstr++; 1128 curstr = xmlDictLookup(ctxt->dict, curstr, endstr - curstr); 1129 if (curstr) { 1130 xmlNsPtr ns; 1131 const xmlChar *nsUri = NULL; 1132 1133 #ifdef WITH_XSLT_DEBUG_ATTRIBUTES 1134 xsltGenericDebug(xsltGenericDebugContext, 1135 "apply attribute set %s\n", curstr); 1136 #endif 1137 1138 if (xmlValidateQName(curstr, 0)) { 1139 xsltTransformError(ctxt, NULL, inst, 1140 "The name '%s' in use-attribute-sets is not a valid " 1141 "QName.\n", curstr); 1142 return; 1143 } 1144 1145 ncname = xsltSplitQName(ctxt->dict, curstr, &prefix); 1146 if (prefix != NULL) { 1147 ns = xmlSearchNs(inst->doc, inst, prefix); 1148 if (ns == NULL) { 1149 xsltTransformError(ctxt, NULL, inst, 1150 "use-attribute-set : No namespace found for QName " 1151 "'%s:%s'\n", prefix, ncname); 1152 return; 1153 } 1154 nsUri = ns->href; 1155 } 1156 1157 style = ctxt->style; 1158 1159 #ifdef WITH_DEBUGGER 1160 if ((style != NULL) && 1161 (style->attributeSets != NULL) && 1162 (ctxt->debugStatus != XSLT_DEBUG_NONE)) 1163 { 1164 set = xmlHashLookup2(style->attributeSets, ncname, nsUri); 1165 if ((set != NULL) && (set->attrs != NULL) && 1166 (set->attrs->attr != NULL)) 1167 xslHandleDebugger(set->attrs->attr->parent, node, NULL, 1168 ctxt); 1169 } 1170 #endif 1171 /* 1172 * Lookup the referenced attribute-set. All attribute sets were 1173 * moved to the top stylesheet so there's no need to iterate 1174 * imported stylesheets 1175 */ 1176 set = xmlHashLookup2(style->attributeSets, ncname, nsUri); 1177 if (set != NULL) { 1178 xsltAttrElemPtr cur = set->attrs; 1179 while (cur != NULL) { 1180 if (cur->attr != NULL) { 1181 xsltAttribute(ctxt, node, cur->attr, 1182 cur->attr->psvi); 1183 } 1184 cur = cur->next; 1185 } 1186 } 1187 } 1188 curstr = endstr; 1189 } 1190 } 1191 1192 static void 1193 xsltFreeAttributeSetsEntry(void *payload, 1194 const xmlChar *name ATTRIBUTE_UNUSED) { 1195 xsltFreeAttrSet((xsltAttrSetPtr) payload); 1196 } 1197 1198 /** 1199 * xsltFreeAttributeSetsHashes: 1200 * @style: an XSLT stylesheet 1201 * 1202 * Free up the memory used by attribute sets 1203 */ 1204 void 1205 xsltFreeAttributeSetsHashes(xsltStylesheetPtr style) { 1206 if (style->attributeSets != NULL) 1207 xmlHashFree((xmlHashTablePtr) style->attributeSets, 1208 xsltFreeAttributeSetsEntry); 1209 style->attributeSets = NULL; 1210 } 1211