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 (at) 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 */ 56 int 57 xsltEvalXPathPredicate(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 */ 116 xmlChar * 117 xsltEvalXPathStringNs(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 */ 176 xmlChar * 177 xsltEvalXPathString(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 */ 194 xmlChar * 195 xsltEvalTemplateString(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 return(NULL); 204 205 if (inst->children == NULL) 206 return(NULL); 207 208 /* 209 * This creates a temporary element-node to add the resulting 210 * text content to. 211 * OPTIMIZE TODO: Keep such an element-node in the transformation 212 * context to avoid creating it every time. 213 */ 214 insert = xmlNewDocNode(ctxt->output, NULL, 215 (const xmlChar *)"fake", NULL); 216 if (insert == NULL) { 217 xsltTransformError(ctxt, NULL, contextNode, 218 "Failed to create temporary node\n"); 219 return(NULL); 220 } 221 oldInsert = ctxt->insert; 222 ctxt->insert = insert; 223 /* 224 * OPTIMIZE TODO: if inst->children consists only of text-nodes. 225 */ 226 xsltApplyOneTemplate(ctxt, contextNode, inst->children, NULL, NULL); 227 228 ctxt->insert = oldInsert; 229 230 ret = xmlNodeGetContent(insert); 231 if (insert != NULL) 232 xmlFreeNode(insert); 233 return(ret); 234 } 235 236 /** 237 * xsltAttrTemplateValueProcessNode: 238 * @ctxt: the XSLT transformation context 239 * @str: the attribute template node value 240 * @inst: the instruction (or LRE) in the stylesheet holding the 241 * attribute with an AVT 242 * 243 * Process the given string, allowing to pass a namespace mapping 244 * context and return the new string value. 245 * 246 * Called by: 247 * - xsltAttrTemplateValueProcess() (templates.c) 248 * - xsltEvalAttrValueTemplate() (templates.c) 249 * 250 * QUESTION: Why is this function public? It is not used outside 251 * of templates.c. 252 * 253 * Returns the computed string value or NULL, must be deallocated by the 254 * caller. 255 */ 256 xmlChar * 257 xsltAttrTemplateValueProcessNode(xsltTransformContextPtr ctxt, 258 const xmlChar *str, xmlNodePtr inst) 259 { 260 xmlChar *ret = NULL; 261 const xmlChar *cur; 262 xmlChar *expr, *val; 263 xmlNsPtr *nsList = NULL; 264 int nsNr = 0; 265 266 if (str == NULL) return(NULL); 267 if (*str == 0) 268 return(xmlStrndup((xmlChar *)"", 0)); 269 270 cur = str; 271 while (*cur != 0) { 272 if (*cur == '{') { 273 if (*(cur+1) == '{') { /* escaped '{' */ 274 cur++; 275 ret = xmlStrncat(ret, str, cur - str); 276 cur++; 277 str = cur; 278 continue; 279 } 280 ret = xmlStrncat(ret, str, cur - str); 281 str = cur; 282 cur++; 283 while ((*cur != 0) && (*cur != '}')) cur++; 284 if (*cur == 0) { 285 xsltTransformError(ctxt, NULL, inst, 286 "xsltAttrTemplateValueProcessNode: unmatched '{'\n"); 287 ret = xmlStrncat(ret, str, cur - str); 288 return(ret); 289 } 290 str++; 291 expr = xmlStrndup(str, cur - str); 292 if (expr == NULL) 293 return(ret); 294 else if (*expr == '{') { 295 ret = xmlStrcat(ret, expr); 296 xmlFree(expr); 297 } else { 298 xmlXPathCompExprPtr comp; 299 /* 300 * TODO: keep precompiled form around 301 */ 302 if ((nsList == NULL) && (inst != NULL)) { 303 int i = 0; 304 305 nsList = xmlGetNsList(inst->doc, inst); 306 if (nsList != NULL) { 307 while (nsList[i] != NULL) 308 i++; 309 nsNr = i; 310 } 311 } 312 comp = xmlXPathCompile(expr); 313 val = xsltEvalXPathStringNs(ctxt, comp, nsNr, nsList); 314 xmlXPathFreeCompExpr(comp); 315 xmlFree(expr); 316 if (val != NULL) { 317 ret = xmlStrcat(ret, val); 318 xmlFree(val); 319 } 320 } 321 cur++; 322 str = cur; 323 } else if (*cur == '}') { 324 cur++; 325 if (*cur == '}') { /* escaped '}' */ 326 ret = xmlStrncat(ret, str, cur - str); 327 cur++; 328 str = cur; 329 continue; 330 } else { 331 xsltTransformError(ctxt, NULL, inst, 332 "xsltAttrTemplateValueProcessNode: unmatched '}'\n"); 333 } 334 } else 335 cur++; 336 } 337 if (cur != str) { 338 ret = xmlStrncat(ret, str, cur - str); 339 } 340 341 if (nsList != NULL) 342 xmlFree(nsList); 343 344 return(ret); 345 } 346 347 /** 348 * xsltAttrTemplateValueProcess: 349 * @ctxt: the XSLT transformation context 350 * @str: the attribute template node value 351 * 352 * Process the given node and return the new string value. 353 * 354 * Returns the computed string value or NULL, must be deallocated by the 355 * caller. 356 */ 357 xmlChar * 358 xsltAttrTemplateValueProcess(xsltTransformContextPtr ctxt, const xmlChar *str) { 359 return(xsltAttrTemplateValueProcessNode(ctxt, str, NULL)); 360 } 361 362 /** 363 * xsltEvalAttrValueTemplate: 364 * @ctxt: the XSLT transformation context 365 * @inst: the instruction (or LRE) in the stylesheet holding the 366 * attribute with an AVT 367 * @name: the attribute QName 368 * @ns: the attribute namespace URI 369 * 370 * Evaluate a attribute value template, i.e. the attribute value can 371 * contain expressions contained in curly braces ({}) and those are 372 * substituted by they computed value. 373 * 374 * Returns the computed string value or NULL, must be deallocated by the 375 * caller. 376 */ 377 xmlChar * 378 xsltEvalAttrValueTemplate(xsltTransformContextPtr ctxt, xmlNodePtr inst, 379 const xmlChar *name, const xmlChar *ns) 380 { 381 xmlChar *ret; 382 xmlChar *expr; 383 384 if ((ctxt == NULL) || (inst == NULL) || (name == NULL)) 385 return(NULL); 386 387 expr = xsltGetNsProp(inst, name, ns); 388 if (expr == NULL) 389 return(NULL); 390 391 /* 392 * TODO: though now {} is detected ahead, it would still be good to 393 * optimize both functions to keep the splitted value if the 394 * attribute content and the XPath precompiled expressions around 395 */ 396 397 ret = xsltAttrTemplateValueProcessNode(ctxt, expr, inst); 398 #ifdef WITH_XSLT_DEBUG_TEMPLATES 399 XSLT_TRACE(ctxt,XSLT_TRACE_TEMPLATES,xsltGenericDebug(xsltGenericDebugContext, 400 "xsltEvalAttrValueTemplate: %s returns %s\n", expr, ret)); 401 #endif 402 if (expr != NULL) 403 xmlFree(expr); 404 return(ret); 405 } 406 407 /** 408 * xsltEvalStaticAttrValueTemplate: 409 * @style: the XSLT stylesheet 410 * @inst: the instruction (or LRE) in the stylesheet holding the 411 * attribute with an AVT 412 * @name: the attribute Name 413 * @ns: the attribute namespace URI 414 * @found: indicator whether the attribute is present 415 * 416 * Check if an attribute value template has a static value, i.e. the 417 * attribute value does not contain expressions contained in curly braces ({}) 418 * 419 * Returns the static string value or NULL, must be deallocated by the 420 * caller. 421 */ 422 const xmlChar * 423 xsltEvalStaticAttrValueTemplate(xsltStylesheetPtr style, xmlNodePtr inst, 424 const xmlChar *name, const xmlChar *ns, int *found) { 425 const xmlChar *ret; 426 xmlChar *expr; 427 428 if ((style == NULL) || (inst == NULL) || (name == NULL)) 429 return(NULL); 430 431 expr = xsltGetNsProp(inst, name, ns); 432 if (expr == NULL) { 433 *found = 0; 434 return(NULL); 435 } 436 *found = 1; 437 438 ret = xmlStrchr(expr, '{'); 439 if (ret != NULL) { 440 xmlFree(expr); 441 return(NULL); 442 } 443 ret = xmlDictLookup(style->dict, expr, -1); 444 xmlFree(expr); 445 return(ret); 446 } 447 448 /** 449 * xsltAttrTemplateProcess: 450 * @ctxt: the XSLT transformation context 451 * @target: the element where the attribute will be grafted 452 * @attr: the attribute node of a literal result element 453 * 454 * Process one attribute of a Literal Result Element (in the stylesheet). 455 * Evaluates Attribute Value Templates and copies the attribute over to 456 * the result element. 457 * This does *not* process attribute sets (xsl:use-attribute-set). 458 * 459 * 460 * Returns the generated attribute node. 461 */ 462 xmlAttrPtr 463 xsltAttrTemplateProcess(xsltTransformContextPtr ctxt, xmlNodePtr target, 464 xmlAttrPtr attr) 465 { 466 const xmlChar *value; 467 xmlAttrPtr ret; 468 469 if ((ctxt == NULL) || (attr == NULL) || (target == NULL)) 470 return(NULL); 471 472 if (attr->type != XML_ATTRIBUTE_NODE) 473 return(NULL); 474 475 /* 476 * Skip all XSLT attributes. 477 */ 478 #ifdef XSLT_REFACTORED 479 if (attr->psvi == xsltXSLTAttrMarker) 480 return(NULL); 481 #else 482 if ((attr->ns != NULL) && xmlStrEqual(attr->ns->href, XSLT_NAMESPACE)) 483 return(NULL); 484 #endif 485 /* 486 * Get the value. 487 */ 488 if (attr->children != NULL) { 489 if ((attr->children->type != XML_TEXT_NODE) || 490 (attr->children->next != NULL)) 491 { 492 xsltTransformError(ctxt, NULL, attr->parent, 493 "Internal error: The children of an attribute node of a " 494 "literal result element are not in the expected form.\n"); 495 return(NULL); 496 } 497 value = attr->children->content; 498 if (value == NULL) 499 value = xmlDictLookup(ctxt->dict, BAD_CAST "", 0); 500 } else 501 value = xmlDictLookup(ctxt->dict, BAD_CAST "", 0); 502 /* 503 * Overwrite duplicates. 504 */ 505 ret = target->properties; 506 while (ret != NULL) { 507 if (((attr->ns != NULL) == (ret->ns != NULL)) && 508 xmlStrEqual(ret->name, attr->name) && 509 ((attr->ns == NULL) || xmlStrEqual(ret->ns->href, attr->ns->href))) 510 { 511 break; 512 } 513 ret = ret->next; 514 } 515 if (ret != NULL) { 516 /* free the existing value */ 517 xmlFreeNodeList(ret->children); 518 ret->children = ret->last = NULL; 519 /* 520 * Adjust ns-prefix if needed. 521 */ 522 if ((ret->ns != NULL) && 523 (! xmlStrEqual(ret->ns->prefix, attr->ns->prefix))) 524 { 525 ret->ns = xsltGetNamespace(ctxt, attr->parent, attr->ns, target); 526 } 527 } else { 528 /* create a new attribute */ 529 if (attr->ns != NULL) 530 ret = xmlNewNsProp(target, 531 xsltGetNamespace(ctxt, attr->parent, attr->ns, target), 532 attr->name, NULL); 533 else 534 ret = xmlNewNsProp(target, NULL, attr->name, NULL); 535 } 536 /* 537 * Set the value. 538 */ 539 if (ret != NULL) { 540 xmlNodePtr text; 541 542 text = xmlNewText(NULL); 543 if (text != NULL) { 544 ret->last = ret->children = text; 545 text->parent = (xmlNodePtr) ret; 546 text->doc = ret->doc; 547 548 if (attr->psvi != NULL) { 549 /* 550 * Evaluate the Attribute Value Template. 551 */ 552 xmlChar *val; 553 val = xsltEvalAVT(ctxt, attr->psvi, attr->parent); 554 if (val == NULL) { 555 /* 556 * TODO: Damn, we need an easy mechanism to report 557 * qualified names! 558 */ 559 if (attr->ns) { 560 xsltTransformError(ctxt, NULL, attr->parent, 561 "Internal error: Failed to evaluate the AVT " 562 "of attribute '{%s}%s'.\n", 563 attr->ns->href, attr->name); 564 } else { 565 xsltTransformError(ctxt, NULL, attr->parent, 566 "Internal error: Failed to evaluate the AVT " 567 "of attribute '%s'.\n", 568 attr->name); 569 } 570 text->content = xmlStrdup(BAD_CAST ""); 571 } else { 572 text->content = val; 573 } 574 } else if ((ctxt->internalized) && (target != NULL) && 575 (target->doc != NULL) && 576 (target->doc->dict == ctxt->dict) && 577 xmlDictOwns(ctxt->dict, value)) { 578 text->content = (xmlChar *) value; 579 } else { 580 text->content = xmlStrdup(value); 581 } 582 } 583 } else { 584 if (attr->ns) { 585 xsltTransformError(ctxt, NULL, attr->parent, 586 "Internal error: Failed to create attribute '{%s}%s'.\n", 587 attr->ns->href, attr->name); 588 } else { 589 xsltTransformError(ctxt, NULL, attr->parent, 590 "Internal error: Failed to create attribute '%s'.\n", 591 attr->name); 592 } 593 } 594 return(ret); 595 } 596 597 598 /** 599 * xsltAttrListTemplateProcess: 600 * @ctxt: the XSLT transformation context 601 * @target: the element where the attributes will be grafted 602 * @attrs: the first attribute 603 * 604 * Processes all attributes of a Literal Result Element. 605 * Attribute references are applied via xsl:use-attribute-set 606 * attributes. 607 * Copies all non XSLT-attributes over to the @target element 608 * and evaluates Attribute Value Templates. 609 * 610 * Called by xsltApplySequenceConstructor() (transform.c). 611 * 612 * Returns a new list of attribute nodes, or NULL in case of error. 613 * (Don't assign the result to @target->properties; if 614 * the result is NULL, you'll get memory leaks, since the 615 * attributes will be disattached.) 616 */ 617 xmlAttrPtr 618 xsltAttrListTemplateProcess(xsltTransformContextPtr ctxt, 619 xmlNodePtr target, xmlAttrPtr attrs) 620 { 621 xmlAttrPtr attr, copy, last; 622 xmlNodePtr oldInsert, text; 623 xmlNsPtr origNs = NULL, copyNs = NULL; 624 const xmlChar *value; 625 xmlChar *valueAVT; 626 627 if ((ctxt == NULL) || (target == NULL) || (attrs == NULL)) 628 return(NULL); 629 630 oldInsert = ctxt->insert; 631 ctxt->insert = target; 632 633 /* 634 * Instantiate LRE-attributes. 635 */ 636 if (target->properties) { 637 last = target->properties; 638 while (last->next != NULL) 639 last = last->next; 640 } else { 641 last = NULL; 642 } 643 attr = attrs; 644 do { 645 /* 646 * Skip XSLT attributes. 647 */ 648 #ifdef XSLT_REFACTORED 649 if (attr->psvi == xsltXSLTAttrMarker) { 650 goto next_attribute; 651 } 652 #else 653 if ((attr->ns != NULL) && 654 xmlStrEqual(attr->ns->href, XSLT_NAMESPACE)) 655 { 656 goto next_attribute; 657 } 658 #endif 659 /* 660 * Get the value. 661 */ 662 if (attr->children != NULL) { 663 if ((attr->children->type != XML_TEXT_NODE) || 664 (attr->children->next != NULL)) 665 { 666 xsltTransformError(ctxt, NULL, attr->parent, 667 "Internal error: The children of an attribute node of a " 668 "literal result element are not in the expected form.\n"); 669 goto error; 670 } 671 value = attr->children->content; 672 if (value == NULL) 673 value = xmlDictLookup(ctxt->dict, BAD_CAST "", 0); 674 } else 675 value = xmlDictLookup(ctxt->dict, BAD_CAST "", 0); 676 677 /* 678 * Create a new attribute. 679 */ 680 copy = xmlNewDocProp(target->doc, attr->name, NULL); 681 if (copy == NULL) { 682 if (attr->ns) { 683 xsltTransformError(ctxt, NULL, attr->parent, 684 "Internal error: Failed to create attribute '{%s}%s'.\n", 685 attr->ns->href, attr->name); 686 } else { 687 xsltTransformError(ctxt, NULL, attr->parent, 688 "Internal error: Failed to create attribute '%s'.\n", 689 attr->name); 690 } 691 goto error; 692 } 693 /* 694 * Attach it to the target element. 695 */ 696 copy->parent = target; 697 if (last == NULL) { 698 target->properties = copy; 699 last = copy; 700 } else { 701 last->next = copy; 702 copy->prev = last; 703 last = copy; 704 } 705 /* 706 * Set the namespace. Avoid lookups of same namespaces. 707 */ 708 if (attr->ns != origNs) { 709 origNs = attr->ns; 710 if (attr->ns != NULL) { 711 #ifdef XSLT_REFACTORED 712 copyNs = xsltGetSpecialNamespace(ctxt, attr->parent, 713 attr->ns->href, attr->ns->prefix, target); 714 #else 715 copyNs = xsltGetNamespace(ctxt, attr->parent, 716 attr->ns, target); 717 #endif 718 if (copyNs == NULL) 719 goto error; 720 } else 721 copyNs = NULL; 722 } 723 copy->ns = copyNs; 724 725 /* 726 * Set the value. 727 */ 728 text = xmlNewText(NULL); 729 if (text != NULL) { 730 copy->last = copy->children = text; 731 text->parent = (xmlNodePtr) copy; 732 text->doc = copy->doc; 733 734 if (attr->psvi != NULL) { 735 /* 736 * Evaluate the Attribute Value Template. 737 */ 738 valueAVT = xsltEvalAVT(ctxt, attr->psvi, attr->parent); 739 if (valueAVT == NULL) { 740 /* 741 * TODO: Damn, we need an easy mechanism to report 742 * qualified names! 743 */ 744 if (attr->ns) { 745 xsltTransformError(ctxt, NULL, attr->parent, 746 "Internal error: Failed to evaluate the AVT " 747 "of attribute '{%s}%s'.\n", 748 attr->ns->href, attr->name); 749 } else { 750 xsltTransformError(ctxt, NULL, attr->parent, 751 "Internal error: Failed to evaluate the AVT " 752 "of attribute '%s'.\n", 753 attr->name); 754 } 755 text->content = xmlStrdup(BAD_CAST ""); 756 goto error; 757 } else { 758 text->content = valueAVT; 759 } 760 } else if ((ctxt->internalized) && 761 (target->doc != NULL) && 762 (target->doc->dict == ctxt->dict) && 763 xmlDictOwns(ctxt->dict, value)) 764 { 765 text->content = (xmlChar *) value; 766 } else { 767 text->content = xmlStrdup(value); 768 } 769 if ((copy != NULL) && (text != NULL) && 770 (xmlIsID(copy->doc, copy->parent, copy))) 771 xmlAddID(NULL, copy->doc, text->content, copy); 772 } 773 774 next_attribute: 775 attr = attr->next; 776 } while (attr != NULL); 777 778 /* 779 * Apply attribute-sets. 780 * The creation of such attributes will not overwrite any existing 781 * attribute. 782 */ 783 attr = attrs; 784 do { 785 #ifdef XSLT_REFACTORED 786 if ((attr->psvi == xsltXSLTAttrMarker) && 787 xmlStrEqual(attr->name, (const xmlChar *)"use-attribute-sets")) 788 { 789 xsltApplyAttributeSet(ctxt, ctxt->node, (xmlNodePtr) attr, NULL); 790 } 791 #else 792 if ((attr->ns != NULL) && 793 xmlStrEqual(attr->name, (const xmlChar *)"use-attribute-sets") && 794 xmlStrEqual(attr->ns->href, XSLT_NAMESPACE)) 795 { 796 xsltApplyAttributeSet(ctxt, ctxt->node, (xmlNodePtr) attr, NULL); 797 } 798 #endif 799 attr = attr->next; 800 } while (attr != NULL); 801 802 ctxt->insert = oldInsert; 803 return(target->properties); 804 805 error: 806 ctxt->insert = oldInsert; 807 return(NULL); 808 } 809 810 811 /** 812 * xsltTemplateProcess: 813 * @ctxt: the XSLT transformation context 814 * @node: the attribute template node 815 * 816 * Obsolete. Don't use it. 817 * 818 * Returns NULL. 819 */ 820 xmlNodePtr * 821 xsltTemplateProcess(xsltTransformContextPtr ctxt ATTRIBUTE_UNUSED, xmlNodePtr node) { 822 if (node == NULL) 823 return(NULL); 824 825 return(0); 826 } 827 828 829