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