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#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 85typedef struct _xsltAttrElem xsltAttrElem; 86typedef xsltAttrElem *xsltAttrElemPtr; 87struct _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 */ 108static xsltAttrElemPtr 109xsltNewAttrElem(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 */ 129static void 130xsltFreeAttrElem(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 */ 140static void 141xsltFreeAttrElemList(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 */ 165static xsltAttrElemPtr 166xsltAddAttrElemList(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 */ 198static xsltAttrElemPtr 199xsltMergeAttrElemList(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 288void 289xsltParseStylesheetAttributeSet(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) || (cur->type != XML_ELEMENT_NODE)) 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 400next_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 */ 471static xsltAttrElemPtr 472xsltGetSAS(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 */ 490static void 491xsltResolveSASCallback(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 */ 545static void 546xsltMergeSASCallback(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 */ 587void 588xsltResolveStylesheetAttributeSet(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 */ 639static void 640xsltAttributeInternal(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 (inst->type != XML_ELEMENT_NODE) ) 661 return; 662 663 /* 664 * A comp->has_name == 0 indicates that we need to skip this instruction, 665 * since it was evaluated to be invalid already during compilation. 666 */ 667 if (!comp->has_name) 668 return; 669 /* 670 * BIG NOTE: This previously used xsltGetSpecialNamespace() and 671 * xsltGetNamespace(), but since both are not appropriate, we 672 * will process namespace lookup here to avoid adding yet another 673 * ns-lookup function to namespaces.c. 674 */ 675 /* 676 * SPEC XSLT 1.0: Error cases: 677 * - Creating nodes other than text nodes during the instantiation of 678 * the content of the xsl:attribute element; implementations may 679 * either signal the error or ignore the offending nodes." 680 */ 681 682 if (comp == NULL) { 683 xsltTransformError(ctxt, NULL, inst, 684 "Internal error in xsltAttributeInternal(): " 685 "The XSLT 'attribute' instruction was not compiled.\n"); 686 return; 687 } 688 /* 689 * TODO: Shouldn't ctxt->insert == NULL be treated as an internal error? 690 * So report an internal error? 691 */ 692 if (ctxt->insert == NULL) 693 return; 694 /* 695 * SPEC XSLT 1.0: 696 * "Adding an attribute to a node that is not an element; 697 * implementations may either signal the error or ignore the attribute." 698 * 699 * TODO: I think we should signal such errors in the future, and maybe 700 * provide an option to ignore such errors. 701 */ 702 targetElem = ctxt->insert; 703 if (targetElem->type != XML_ELEMENT_NODE) 704 return; 705 706 /* 707 * SPEC XSLT 1.0: 708 * "Adding an attribute to an element after children have been added 709 * to it; implementations may either signal the error or ignore the 710 * attribute." 711 * 712 * TODO: We should decide whether not to report such errors or 713 * to ignore them; note that we *ignore* if the parent is not an 714 * element, but here we report an error. 715 */ 716 if (targetElem->children != NULL) { 717 /* 718 * NOTE: Ah! This seems to be intended to support streamed 719 * result generation!. 720 */ 721 xsltTransformError(ctxt, NULL, inst, 722 "xsl:attribute: Cannot add attributes to an " 723 "element if children have been already added " 724 "to the element.\n"); 725 return; 726 } 727 728 /* 729 * Process the name 730 * ---------------- 731 */ 732 733#ifdef WITH_DEBUGGER 734 if (ctxt->debugStatus != XSLT_DEBUG_NONE) 735 xslHandleDebugger(inst, contextNode, NULL, ctxt); 736#endif 737 738 if (comp->name == NULL) { 739 /* TODO: fix attr acquisition wrt to the XSLT namespace */ 740 prop = xsltEvalAttrValueTemplate(ctxt, inst, 741 (const xmlChar *) "name", XSLT_NAMESPACE); 742 if (prop == NULL) { 743 xsltTransformError(ctxt, NULL, inst, 744 "xsl:attribute: The attribute 'name' is missing.\n"); 745 goto error; 746 } 747 if (xmlValidateQName(prop, 0)) { 748 xsltTransformError(ctxt, NULL, inst, 749 "xsl:attribute: The effective name '%s' is not a " 750 "valid QName.\n", prop); 751 /* we fall through to catch any further errors, if possible */ 752 } 753 754 /* 755 * Reject a name of "xmlns". 756 */ 757 if (xmlStrEqual(prop, BAD_CAST "xmlns")) { 758 xsltTransformError(ctxt, NULL, inst, 759 "xsl:attribute: The effective name 'xmlns' is not allowed.\n"); 760 xmlFree(prop); 761 goto error; 762 } 763 764 name = xsltSplitQName(ctxt->dict, prop, &prefix); 765 xmlFree(prop); 766 } else { 767 /* 768 * The "name" value was static. 769 */ 770#ifdef XSLT_REFACTORED 771 prefix = comp->nsPrefix; 772 name = comp->name; 773#else 774 name = xsltSplitQName(ctxt->dict, comp->name, &prefix); 775#endif 776 } 777 778 /* 779 * Process namespace semantics 780 * --------------------------- 781 * 782 * Evaluate the namespace name. 783 */ 784 if (comp->has_ns) { 785 /* 786 * The "namespace" attribute was existent. 787 */ 788 if (comp->ns != NULL) { 789 /* 790 * No AVT; just plain text for the namespace name. 791 */ 792 if (comp->ns[0] != 0) 793 nsName = comp->ns; 794 } else { 795 xmlChar *tmpNsName; 796 /* 797 * Eval the AVT. 798 */ 799 /* TODO: check attr acquisition wrt to the XSLT namespace */ 800 tmpNsName = xsltEvalAttrValueTemplate(ctxt, inst, 801 (const xmlChar *) "namespace", XSLT_NAMESPACE); 802 /* 803 * This fixes bug #302020: The AVT might also evaluate to the 804 * empty string; this means that the empty string also indicates 805 * "no namespace". 806 * SPEC XSLT 1.0: 807 * "If the string is empty, then the expanded-name of the 808 * attribute has a null namespace URI." 809 */ 810 if ((tmpNsName != NULL) && (tmpNsName[0] != 0)) 811 nsName = xmlDictLookup(ctxt->dict, BAD_CAST tmpNsName, -1); 812 xmlFree(tmpNsName); 813 } 814 815 if (xmlStrEqual(nsName, BAD_CAST "http://www.w3.org/2000/xmlns/")) { 816 xsltTransformError(ctxt, NULL, inst, 817 "xsl:attribute: Namespace http://www.w3.org/2000/xmlns/ " 818 "forbidden.\n"); 819 goto error; 820 } 821 if (xmlStrEqual(nsName, XML_XML_NAMESPACE)) { 822 prefix = BAD_CAST "xml"; 823 } else if (xmlStrEqual(prefix, BAD_CAST "xml")) { 824 prefix = NULL; 825 } 826 } else if (prefix != NULL) { 827 /* 828 * SPEC XSLT 1.0: 829 * "If the namespace attribute is not present, then the QName is 830 * expanded into an expanded-name using the namespace declarations 831 * in effect for the xsl:attribute element, *not* including any 832 * default namespace declaration." 833 */ 834 ns = xmlSearchNs(inst->doc, inst, prefix); 835 if (ns == NULL) { 836 /* 837 * Note that this is treated as an error now (checked with 838 * Saxon, Xalan-J and MSXML). 839 */ 840 xsltTransformError(ctxt, NULL, inst, 841 "xsl:attribute: The QName '%s:%s' has no " 842 "namespace binding in scope in the stylesheet; " 843 "this is an error, since the namespace was not " 844 "specified by the instruction itself.\n", prefix, name); 845 } else 846 nsName = ns->href; 847 } 848 849 if (fromAttributeSet) { 850 /* 851 * This tries to ensure that xsl:attribute(s) coming 852 * from an xsl:attribute-set won't override attribute of 853 * literal result elements or of explicit xsl:attribute(s). 854 * URGENT TODO: This might be buggy, since it will miss to 855 * overwrite two equal attributes both from attribute sets. 856 */ 857 attr = xmlHasNsProp(targetElem, name, nsName); 858 if (attr != NULL) 859 return; 860 } 861 862 /* 863 * Find/create a matching ns-decl in the result tree. 864 */ 865 ns = NULL; 866 867#if 0 868 if (0) { 869 /* 870 * OPTIMIZE TODO: How do we know if we are adding to a 871 * fragment or to the result tree? 872 * 873 * If we are adding to a result tree fragment (i.e., not to the 874 * actual result tree), we'll don't bother searching for the 875 * ns-decl, but just store it in the dummy-doc of the result 876 * tree fragment. 877 */ 878 if (nsName != NULL) { 879 /* 880 * TODO: Get the doc of @targetElem. 881 */ 882 ns = xsltTreeAcquireStoredNs(some doc, nsName, prefix); 883 } 884 } 885#endif 886 887 if (nsName != NULL) { 888 /* 889 * Something about ns-prefixes: 890 * SPEC XSLT 1.0: 891 * "XSLT processors may make use of the prefix of the QName specified 892 * in the name attribute when selecting the prefix used for outputting 893 * the created attribute as XML; however, they are not required to do 894 * so and, if the prefix is xmlns, they must not do so" 895 */ 896 /* 897 * xsl:attribute can produce a scenario where the prefix is NULL, 898 * so generate a prefix. 899 */ 900 if ((prefix == NULL) || xmlStrEqual(prefix, BAD_CAST "xmlns")) { 901 xmlChar *pref = xmlStrdup(BAD_CAST "ns_1"); 902 903 ns = xsltGetSpecialNamespace(ctxt, inst, nsName, pref, 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 1007error: 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 */ 1020void 1021xsltAttribute(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 */ 1038void 1039xsltApplyAttributeSet(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 */ 1133void 1134xsltFreeAttributeSetsHashes(xsltStylesheetPtr style) { 1135 if (style->attributeSets != NULL) 1136 xmlHashFree((xmlHashTablePtr) style->attributeSets, 1137 (xmlHashDeallocator) xsltFreeAttrElemList); 1138 style->attributeSets = NULL; 1139} 1140