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