1/* 2 * templates.c: Implementation of the template processing 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#include <libxml/xmlmemory.h> 18#include <libxml/globals.h> 19#include <libxml/xmlerror.h> 20#include <libxml/tree.h> 21#include <libxml/xpathInternals.h> 22#include <libxml/parserInternals.h> 23#include "xslt.h" 24#include "xsltInternals.h" 25#include "xsltutils.h" 26#include "variables.h" 27#include "functions.h" 28#include "templates.h" 29#include "transform.h" 30#include "namespaces.h" 31#include "attributes.h" 32 33#ifdef WITH_XSLT_DEBUG 34#define WITH_XSLT_DEBUG_TEMPLATES 35#endif 36 37/************************************************************************ 38 * * 39 * Module interfaces * 40 * * 41 ************************************************************************/ 42 43/** 44 * xsltEvalXPathPredicate: 45 * @ctxt: the XSLT transformation context 46 * @comp: the XPath compiled expression 47 * @nsList: the namespaces in scope 48 * @nsNr: the number of namespaces in scope 49 * 50 * Process the expression using XPath and evaluate the result as 51 * an XPath predicate 52 * 53 * Returns 1 is the predicate was true, 0 otherwise 54 */ 55int 56xsltEvalXPathPredicate(xsltTransformContextPtr ctxt, xmlXPathCompExprPtr comp, 57 xmlNsPtr *nsList, int nsNr) { 58 int ret; 59 xmlXPathObjectPtr res; 60 int oldNsNr; 61 xmlNsPtr *oldNamespaces; 62 xmlNodePtr oldInst; 63 int oldProximityPosition, oldContextSize; 64 65 oldContextSize = ctxt->xpathCtxt->contextSize; 66 oldProximityPosition = ctxt->xpathCtxt->proximityPosition; 67 oldNsNr = ctxt->xpathCtxt->nsNr; 68 oldNamespaces = ctxt->xpathCtxt->namespaces; 69 oldInst = ctxt->inst; 70 71 ctxt->xpathCtxt->node = ctxt->node; 72 ctxt->xpathCtxt->namespaces = nsList; 73 ctxt->xpathCtxt->nsNr = nsNr; 74 75 res = xmlXPathCompiledEval(comp, ctxt->xpathCtxt); 76 77 if (res != NULL) { 78 ret = xmlXPathEvalPredicate(ctxt->xpathCtxt, res); 79 xmlXPathFreeObject(res); 80#ifdef WITH_XSLT_DEBUG_TEMPLATES 81 XSLT_TRACE(ctxt,XSLT_TRACE_TEMPLATES,xsltGenericDebug(xsltGenericDebugContext, 82 "xsltEvalXPathPredicate: returns %d\n", ret)); 83#endif 84 } else { 85#ifdef WITH_XSLT_DEBUG_TEMPLATES 86 XSLT_TRACE(ctxt,XSLT_TRACE_TEMPLATES,xsltGenericDebug(xsltGenericDebugContext, 87 "xsltEvalXPathPredicate: failed\n")); 88#endif 89 ctxt->state = XSLT_STATE_STOPPED; 90 ret = 0; 91 } 92 ctxt->xpathCtxt->nsNr = oldNsNr; 93 94 ctxt->xpathCtxt->namespaces = oldNamespaces; 95 ctxt->inst = oldInst; 96 ctxt->xpathCtxt->contextSize = oldContextSize; 97 ctxt->xpathCtxt->proximityPosition = oldProximityPosition; 98 99 return(ret); 100} 101 102/** 103 * xsltEvalXPathStringNs: 104 * @ctxt: the XSLT transformation context 105 * @comp: the compiled XPath expression 106 * @nsNr: the number of namespaces in the list 107 * @nsList: the list of in-scope namespaces to use 108 * 109 * Process the expression using XPath, allowing to pass a namespace mapping 110 * context and get a string 111 * 112 * Returns the computed string value or NULL, must be deallocated by the 113 * caller. 114 */ 115xmlChar * 116xsltEvalXPathStringNs(xsltTransformContextPtr ctxt, xmlXPathCompExprPtr comp, 117 int nsNr, xmlNsPtr *nsList) { 118 xmlChar *ret = NULL; 119 xmlXPathObjectPtr res; 120 xmlNodePtr oldInst; 121 xmlNodePtr oldNode; 122 int oldPos, oldSize; 123 int oldNsNr; 124 xmlNsPtr *oldNamespaces; 125 126 oldInst = ctxt->inst; 127 oldNode = ctxt->node; 128 oldPos = ctxt->xpathCtxt->proximityPosition; 129 oldSize = ctxt->xpathCtxt->contextSize; 130 oldNsNr = ctxt->xpathCtxt->nsNr; 131 oldNamespaces = ctxt->xpathCtxt->namespaces; 132 133 ctxt->xpathCtxt->node = ctxt->node; 134 /* TODO: do we need to propagate the namespaces here ? */ 135 ctxt->xpathCtxt->namespaces = nsList; 136 ctxt->xpathCtxt->nsNr = nsNr; 137 res = xmlXPathCompiledEval(comp, ctxt->xpathCtxt); 138 if (res != NULL) { 139 if (res->type != XPATH_STRING) 140 res = xmlXPathConvertString(res); 141 if (res->type == XPATH_STRING) { 142 ret = res->stringval; 143 res->stringval = NULL; 144 } else { 145 xsltTransformError(ctxt, NULL, NULL, 146 "xpath : string() function didn't return a String\n"); 147 } 148 xmlXPathFreeObject(res); 149 } else { 150 ctxt->state = XSLT_STATE_STOPPED; 151 } 152#ifdef WITH_XSLT_DEBUG_TEMPLATES 153 XSLT_TRACE(ctxt,XSLT_TRACE_TEMPLATES,xsltGenericDebug(xsltGenericDebugContext, 154 "xsltEvalXPathString: returns %s\n", ret)); 155#endif 156 ctxt->inst = oldInst; 157 ctxt->node = oldNode; 158 ctxt->xpathCtxt->contextSize = oldSize; 159 ctxt->xpathCtxt->proximityPosition = oldPos; 160 ctxt->xpathCtxt->nsNr = oldNsNr; 161 ctxt->xpathCtxt->namespaces = oldNamespaces; 162 return(ret); 163} 164 165/** 166 * xsltEvalXPathString: 167 * @ctxt: the XSLT transformation context 168 * @comp: the compiled XPath expression 169 * 170 * Process the expression using XPath and get a string 171 * 172 * Returns the computed string value or NULL, must be deallocated by the 173 * caller. 174 */ 175xmlChar * 176xsltEvalXPathString(xsltTransformContextPtr ctxt, xmlXPathCompExprPtr comp) { 177 return(xsltEvalXPathStringNs(ctxt, comp, 0, NULL)); 178} 179 180/** 181 * xsltEvalTemplateString: 182 * @ctxt: the XSLT transformation context 183 * @contextNode: the current node in the source tree 184 * @inst: the XSLT instruction (xsl:comment, xsl:processing-instruction) 185 * 186 * Processes the sequence constructor of the given instruction on 187 * @contextNode and converts the resulting tree to a string. 188 * This is needed by e.g. xsl:comment and xsl:processing-instruction. 189 * 190 * Returns the computed string value or NULL; it's up to the caller to 191 * free the result. 192 */ 193xmlChar * 194xsltEvalTemplateString(xsltTransformContextPtr ctxt, 195 xmlNodePtr contextNode, 196 xmlNodePtr inst) 197{ 198 xmlNodePtr oldInsert, insert = NULL; 199 xmlChar *ret; 200 201 if ((ctxt == NULL) || (contextNode == NULL) || (inst == NULL)) 202 return(NULL); 203 204 if (inst->children == NULL) 205 return(NULL); 206 207 /* 208 * This creates a temporary element-node to add the resulting 209 * text content to. 210 * OPTIMIZE TODO: Keep such an element-node in the transformation 211 * context to avoid creating it every time. 212 */ 213 insert = xmlNewDocNode(ctxt->output, NULL, 214 (const xmlChar *)"fake", NULL); 215 if (insert == NULL) { 216 xsltTransformError(ctxt, NULL, contextNode, 217 "Failed to create temporary node\n"); 218 return(NULL); 219 } 220 oldInsert = ctxt->insert; 221 ctxt->insert = insert; 222 /* 223 * OPTIMIZE TODO: if inst->children consists only of text-nodes. 224 */ 225 xsltApplyOneTemplate(ctxt, contextNode, inst->children, NULL, NULL); 226 227 ctxt->insert = oldInsert; 228 229 ret = xmlNodeGetContent(insert); 230 if (insert != NULL) 231 xmlFreeNode(insert); 232 return(ret); 233} 234 235/** 236 * xsltAttrTemplateValueProcessNode: 237 * @ctxt: the XSLT transformation context 238 * @str: the attribute template node value 239 * @inst: the instruction (or LRE) in the stylesheet holding the 240 * attribute with an AVT 241 * 242 * Process the given string, allowing to pass a namespace mapping 243 * context and return the new string value. 244 * 245 * Called by: 246 * - xsltAttrTemplateValueProcess() (templates.c) 247 * - xsltEvalAttrValueTemplate() (templates.c) 248 * 249 * QUESTION: Why is this function public? It is not used outside 250 * of templates.c. 251 * 252 * Returns the computed string value or NULL, must be deallocated by the 253 * caller. 254 */ 255xmlChar * 256xsltAttrTemplateValueProcessNode(xsltTransformContextPtr ctxt, 257 const xmlChar *str, xmlNodePtr inst) 258{ 259 xmlChar *ret = NULL; 260 const xmlChar *cur; 261 xmlChar *expr, *val; 262 xmlNsPtr *nsList = NULL; 263 int nsNr = 0; 264 265 if (str == NULL) return(NULL); 266 if (*str == 0) 267 return(xmlStrndup((xmlChar *)"", 0)); 268 269 cur = str; 270 while (*cur != 0) { 271 if (*cur == '{') { 272 if (*(cur+1) == '{') { /* escaped '{' */ 273 cur++; 274 ret = xmlStrncat(ret, str, cur - str); 275 cur++; 276 str = cur; 277 continue; 278 } 279 ret = xmlStrncat(ret, str, cur - str); 280 str = cur; 281 cur++; 282 while ((*cur != 0) && (*cur != '}')) cur++; 283 if (*cur == 0) { 284 xsltTransformError(ctxt, NULL, inst, 285 "xsltAttrTemplateValueProcessNode: unmatched '{'\n"); 286 ret = xmlStrncat(ret, str, cur - str); 287 return(ret); 288 } 289 str++; 290 expr = xmlStrndup(str, cur - str); 291 if (expr == NULL) 292 return(ret); 293 else if (*expr == '{') { 294 ret = xmlStrcat(ret, expr); 295 xmlFree(expr); 296 } else { 297 xmlXPathCompExprPtr comp; 298 /* 299 * TODO: keep precompiled form around 300 */ 301 if ((nsList == NULL) && (inst != NULL)) { 302 int i = 0; 303 304 nsList = xmlGetNsList(inst->doc, inst); 305 if (nsList != NULL) { 306 while (nsList[i] != NULL) 307 i++; 308 nsNr = i; 309 } 310 } 311 comp = xmlXPathCompile(expr); 312 val = xsltEvalXPathStringNs(ctxt, comp, nsNr, nsList); 313 xmlXPathFreeCompExpr(comp); 314 xmlFree(expr); 315 if (val != NULL) { 316 ret = xmlStrcat(ret, val); 317 xmlFree(val); 318 } 319 } 320 cur++; 321 str = cur; 322 } else if (*cur == '}') { 323 cur++; 324 if (*cur == '}') { /* escaped '}' */ 325 ret = xmlStrncat(ret, str, cur - str); 326 cur++; 327 str = cur; 328 continue; 329 } else { 330 xsltTransformError(ctxt, NULL, inst, 331 "xsltAttrTemplateValueProcessNode: unmatched '}'\n"); 332 } 333 } else 334 cur++; 335 } 336 if (cur != str) { 337 ret = xmlStrncat(ret, str, cur - str); 338 } 339 340 if (nsList != NULL) 341 xmlFree(nsList); 342 343 return(ret); 344} 345 346/** 347 * xsltAttrTemplateValueProcess: 348 * @ctxt: the XSLT transformation context 349 * @str: the attribute template node value 350 * 351 * Process the given node and return the new string value. 352 * 353 * Returns the computed string value or NULL, must be deallocated by the 354 * caller. 355 */ 356xmlChar * 357xsltAttrTemplateValueProcess(xsltTransformContextPtr ctxt, const xmlChar *str) { 358 return(xsltAttrTemplateValueProcessNode(ctxt, str, NULL)); 359} 360 361/** 362 * xsltEvalAttrValueTemplate: 363 * @ctxt: the XSLT transformation context 364 * @inst: the instruction (or LRE) in the stylesheet holding the 365 * attribute with an AVT 366 * @name: the attribute QName 367 * @ns: the attribute namespace URI 368 * 369 * Evaluate a attribute value template, i.e. the attribute value can 370 * contain expressions contained in curly braces ({}) and those are 371 * substituted by they computed value. 372 * 373 * Returns the computed string value or NULL, must be deallocated by the 374 * caller. 375 */ 376xmlChar * 377xsltEvalAttrValueTemplate(xsltTransformContextPtr ctxt, xmlNodePtr inst, 378 const xmlChar *name, const xmlChar *ns) 379{ 380 xmlChar *ret; 381 xmlChar *expr; 382 383 if ((ctxt == NULL) || (inst == NULL) || (name == NULL)) 384 return(NULL); 385 386 expr = xsltGetNsProp(inst, name, ns); 387 if (expr == NULL) 388 return(NULL); 389 390 /* 391 * TODO: though now {} is detected ahead, it would still be good to 392 * optimize both functions to keep the splitted value if the 393 * attribute content and the XPath precompiled expressions around 394 */ 395 396 ret = xsltAttrTemplateValueProcessNode(ctxt, expr, inst); 397#ifdef WITH_XSLT_DEBUG_TEMPLATES 398 XSLT_TRACE(ctxt,XSLT_TRACE_TEMPLATES,xsltGenericDebug(xsltGenericDebugContext, 399 "xsltEvalAttrValueTemplate: %s returns %s\n", expr, ret)); 400#endif 401 if (expr != NULL) 402 xmlFree(expr); 403 return(ret); 404} 405 406/** 407 * xsltEvalStaticAttrValueTemplate: 408 * @style: the XSLT stylesheet 409 * @inst: the instruction (or LRE) in the stylesheet holding the 410 * attribute with an AVT 411 * @name: the attribute Name 412 * @ns: the attribute namespace URI 413 * @found: indicator whether the attribute is present 414 * 415 * Check if an attribute value template has a static value, i.e. the 416 * attribute value does not contain expressions contained in curly braces ({}) 417 * 418 * Returns the static string value or NULL, must be deallocated by the 419 * caller. 420 */ 421const xmlChar * 422xsltEvalStaticAttrValueTemplate(xsltStylesheetPtr style, xmlNodePtr inst, 423 const xmlChar *name, const xmlChar *ns, int *found) { 424 const xmlChar *ret; 425 xmlChar *expr; 426 427 if ((style == NULL) || (inst == NULL) || (name == NULL)) 428 return(NULL); 429 430 expr = xsltGetNsProp(inst, name, ns); 431 if (expr == NULL) { 432 *found = 0; 433 return(NULL); 434 } 435 *found = 1; 436 437 ret = xmlStrchr(expr, '{'); 438 if (ret != NULL) { 439 xmlFree(expr); 440 return(NULL); 441 } 442 ret = xmlDictLookup(style->dict, expr, -1); 443 xmlFree(expr); 444 return(ret); 445} 446 447/** 448 * xsltAttrTemplateProcess: 449 * @ctxt: the XSLT transformation context 450 * @target: the element where the attribute will be grafted 451 * @attr: the attribute node of a literal result element 452 * 453 * Process one attribute of a Literal Result Element (in the stylesheet). 454 * Evaluates Attribute Value Templates and copies the attribute over to 455 * the result element. 456 * This does *not* process attribute sets (xsl:use-attribute-set). 457 * 458 * 459 * Returns the generated attribute node. 460 */ 461xmlAttrPtr 462xsltAttrTemplateProcess(xsltTransformContextPtr ctxt, xmlNodePtr target, 463 xmlAttrPtr attr) 464{ 465 const xmlChar *value; 466 xmlAttrPtr ret; 467 468 if ((ctxt == NULL) || (attr == NULL) || (target == NULL)) 469 return(NULL); 470 471 if (attr->type != XML_ATTRIBUTE_NODE) 472 return(NULL); 473 474 /* 475 * Skip all XSLT attributes. 476 */ 477#ifdef XSLT_REFACTORED 478 if (attr->psvi == xsltXSLTAttrMarker) 479 return(NULL); 480#else 481 if ((attr->ns != NULL) && xmlStrEqual(attr->ns->href, XSLT_NAMESPACE)) 482 return(NULL); 483#endif 484 /* 485 * Get the value. 486 */ 487 if (attr->children != NULL) { 488 if ((attr->children->type != XML_TEXT_NODE) || 489 (attr->children->next != NULL)) 490 { 491 xsltTransformError(ctxt, NULL, attr->parent, 492 "Internal error: The children of an attribute node of a " 493 "literal result element are not in the expected form.\n"); 494 return(NULL); 495 } 496 value = attr->children->content; 497 if (value == NULL) 498 value = xmlDictLookup(ctxt->dict, BAD_CAST "", 0); 499 } else 500 value = xmlDictLookup(ctxt->dict, BAD_CAST "", 0); 501 /* 502 * Overwrite duplicates. 503 */ 504 ret = target->properties; 505 while (ret != NULL) { 506 if (((attr->ns != NULL) == (ret->ns != NULL)) && 507 xmlStrEqual(ret->name, attr->name) && 508 ((attr->ns == NULL) || xmlStrEqual(ret->ns->href, attr->ns->href))) 509 { 510 break; 511 } 512 ret = ret->next; 513 } 514 if (ret != NULL) { 515 /* free the existing value */ 516 xmlFreeNodeList(ret->children); 517 ret->children = ret->last = NULL; 518 /* 519 * Adjust ns-prefix if needed. 520 */ 521 if ((ret->ns != NULL) && 522 (! xmlStrEqual(ret->ns->prefix, attr->ns->prefix))) 523 { 524 ret->ns = xsltGetNamespace(ctxt, attr->parent, attr->ns, target); 525 } 526 } else { 527 /* create a new attribute */ 528 if (attr->ns != NULL) 529 ret = xmlNewNsProp(target, 530 xsltGetNamespace(ctxt, attr->parent, attr->ns, target), 531 attr->name, NULL); 532 else 533 ret = xmlNewNsProp(target, NULL, attr->name, NULL); 534 } 535 /* 536 * Set the value. 537 */ 538 if (ret != NULL) { 539 xmlNodePtr text; 540 541 text = xmlNewText(NULL); 542 if (text != NULL) { 543 ret->last = ret->children = text; 544 text->parent = (xmlNodePtr) ret; 545 text->doc = ret->doc; 546 547 if (attr->psvi != NULL) { 548 /* 549 * Evaluate the Attribute Value Template. 550 */ 551 xmlChar *val; 552 val = xsltEvalAVT(ctxt, attr->psvi, attr->parent); 553 if (val == NULL) { 554 /* 555 * TODO: Damn, we need an easy mechanism to report 556 * qualified names! 557 */ 558 if (attr->ns) { 559 xsltTransformError(ctxt, NULL, attr->parent, 560 "Internal error: Failed to evaluate the AVT " 561 "of attribute '{%s}%s'.\n", 562 attr->ns->href, attr->name); 563 } else { 564 xsltTransformError(ctxt, NULL, attr->parent, 565 "Internal error: Failed to evaluate the AVT " 566 "of attribute '%s'.\n", 567 attr->name); 568 } 569 text->content = xmlStrdup(BAD_CAST ""); 570 } else { 571 text->content = val; 572 } 573 } else if ((ctxt->internalized) && (target != NULL) && 574 (target->doc != NULL) && 575 (target->doc->dict == ctxt->dict)) { 576 text->content = (xmlChar *) value; 577 } else { 578 text->content = xmlStrdup(value); 579 } 580 } 581 } else { 582 if (attr->ns) { 583 xsltTransformError(ctxt, NULL, attr->parent, 584 "Internal error: Failed to create attribute '{%s}%s'.\n", 585 attr->ns->href, attr->name); 586 } else { 587 xsltTransformError(ctxt, NULL, attr->parent, 588 "Internal error: Failed to create attribute '%s'.\n", 589 attr->name); 590 } 591 } 592 return(ret); 593} 594 595 596/** 597 * xsltAttrListTemplateProcess: 598 * @ctxt: the XSLT transformation context 599 * @target: the element where the attributes will be grafted 600 * @attrs: the first attribute 601 * 602 * Processes all attributes of a Literal Result Element. 603 * Attribute references are applied via xsl:use-attribute-set 604 * attributes. 605 * Copies all non XSLT-attributes over to the @target element 606 * and evaluates Attribute Value Templates. 607 * 608 * Called by xsltApplySequenceConstructor() (transform.c). 609 * 610 * Returns a new list of attribute nodes, or NULL in case of error. 611 * (Don't assign the result to @target->properties; if 612 * the result is NULL, you'll get memory leaks, since the 613 * attributes will be disattached.) 614 */ 615xmlAttrPtr 616xsltAttrListTemplateProcess(xsltTransformContextPtr ctxt, 617 xmlNodePtr target, xmlAttrPtr attrs) 618{ 619 xmlAttrPtr attr, copy, last; 620 xmlNodePtr oldInsert, text; 621 xmlNsPtr origNs = NULL, copyNs = NULL; 622 const xmlChar *value; 623 xmlChar *valueAVT; 624 625 if ((ctxt == NULL) || (target == NULL) || (attrs == NULL)) 626 return(NULL); 627 628 oldInsert = ctxt->insert; 629 ctxt->insert = target; 630 631 /* 632 * Instantiate LRE-attributes. 633 */ 634 if (target->properties) { 635 last = target->properties; 636 while (last->next != NULL) 637 last = last->next; 638 } else { 639 last = NULL; 640 } 641 attr = attrs; 642 do { 643 /* 644 * Skip XSLT attributes. 645 */ 646#ifdef XSLT_REFACTORED 647 if (attr->psvi == xsltXSLTAttrMarker) { 648 goto next_attribute; 649 } 650#else 651 if ((attr->ns != NULL) && 652 xmlStrEqual(attr->ns->href, XSLT_NAMESPACE)) 653 { 654 goto next_attribute; 655 } 656#endif 657 /* 658 * Get the value. 659 */ 660 if (attr->children != NULL) { 661 if ((attr->children->type != XML_TEXT_NODE) || 662 (attr->children->next != NULL)) 663 { 664 xsltTransformError(ctxt, NULL, attr->parent, 665 "Internal error: The children of an attribute node of a " 666 "literal result element are not in the expected form.\n"); 667 goto error; 668 } 669 value = attr->children->content; 670 if (value == NULL) 671 value = xmlDictLookup(ctxt->dict, BAD_CAST "", 0); 672 } else 673 value = xmlDictLookup(ctxt->dict, BAD_CAST "", 0); 674 675 /* 676 * Create a new attribute. 677 */ 678 copy = xmlNewDocProp(target->doc, attr->name, NULL); 679 if (copy == NULL) { 680 if (attr->ns) { 681 xsltTransformError(ctxt, NULL, attr->parent, 682 "Internal error: Failed to create attribute '{%s}%s'.\n", 683 attr->ns->href, attr->name); 684 } else { 685 xsltTransformError(ctxt, NULL, attr->parent, 686 "Internal error: Failed to create attribute '%s'.\n", 687 attr->name); 688 } 689 goto error; 690 } 691 /* 692 * Attach it to the target element. 693 */ 694 copy->parent = target; 695 if (last == NULL) { 696 target->properties = copy; 697 last = copy; 698 } else { 699 last->next = copy; 700 copy->prev = last; 701 last = copy; 702 } 703 /* 704 * Set the namespace. Avoid lookups of same namespaces. 705 */ 706 if (attr->ns != origNs) { 707 origNs = attr->ns; 708 if (attr->ns != NULL) { 709#ifdef XSLT_REFACTORED 710 copyNs = xsltGetSpecialNamespace(ctxt, attr->parent, 711 attr->ns->href, attr->ns->prefix, target); 712#else 713 copyNs = xsltGetNamespace(ctxt, attr->parent, 714 attr->ns, target); 715#endif 716 if (copyNs == NULL) 717 goto error; 718 } else 719 copyNs = NULL; 720 } 721 copy->ns = copyNs; 722 723 /* 724 * Set the value. 725 */ 726 text = xmlNewText(NULL); 727 if (text != NULL) { 728 copy->last = copy->children = text; 729 text->parent = (xmlNodePtr) copy; 730 text->doc = copy->doc; 731 732 if (attr->psvi != NULL) { 733 /* 734 * Evaluate the Attribute Value Template. 735 */ 736 valueAVT = xsltEvalAVT(ctxt, attr->psvi, attr->parent); 737 if (valueAVT == NULL) { 738 /* 739 * TODO: Damn, we need an easy mechanism to report 740 * qualified names! 741 */ 742 if (attr->ns) { 743 xsltTransformError(ctxt, NULL, attr->parent, 744 "Internal error: Failed to evaluate the AVT " 745 "of attribute '{%s}%s'.\n", 746 attr->ns->href, attr->name); 747 } else { 748 xsltTransformError(ctxt, NULL, attr->parent, 749 "Internal error: Failed to evaluate the AVT " 750 "of attribute '%s'.\n", 751 attr->name); 752 } 753 text->content = xmlStrdup(BAD_CAST ""); 754 goto error; 755 } else { 756 text->content = valueAVT; 757 } 758 } else if ((ctxt->internalized) && 759 (target->doc != NULL) && 760 (target->doc->dict == ctxt->dict)) 761 { 762 text->content = (xmlChar *) value; 763 } else { 764 text->content = xmlStrdup(value); 765 } 766 if ((copy != NULL) && (text != NULL) && 767 (xmlIsID(copy->doc, copy->parent, copy))) 768 xmlAddID(NULL, copy->doc, text->content, copy); 769 } 770 771next_attribute: 772 attr = attr->next; 773 } while (attr != NULL); 774 775 /* 776 * Apply attribute-sets. 777 * The creation of such attributes will not overwrite any existing 778 * attribute. 779 */ 780 attr = attrs; 781 do { 782#ifdef XSLT_REFACTORED 783 if ((attr->psvi == xsltXSLTAttrMarker) && 784 xmlStrEqual(attr->name, (const xmlChar *)"use-attribute-sets")) 785 { 786 xsltApplyAttributeSet(ctxt, ctxt->node, (xmlNodePtr) attr, NULL); 787 } 788#else 789 if ((attr->ns != NULL) && 790 xmlStrEqual(attr->name, (const xmlChar *)"use-attribute-sets") && 791 xmlStrEqual(attr->ns->href, XSLT_NAMESPACE)) 792 { 793 xsltApplyAttributeSet(ctxt, ctxt->node, (xmlNodePtr) attr, NULL); 794 } 795#endif 796 attr = attr->next; 797 } while (attr != NULL); 798 799 ctxt->insert = oldInsert; 800 return(target->properties); 801 802error: 803 ctxt->insert = oldInsert; 804 return(NULL); 805} 806 807 808/** 809 * xsltTemplateProcess: 810 * @ctxt: the XSLT transformation context 811 * @node: the attribute template node 812 * 813 * Obsolete. Don't use it. 814 * 815 * Returns NULL. 816 */ 817xmlNodePtr * 818xsltTemplateProcess(xsltTransformContextPtr ctxt ATTRIBUTE_UNUSED, xmlNodePtr node) { 819 if (node == NULL) 820 return(NULL); 821 822 return(0); 823} 824 825 826