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