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