| OLD | NEW |
| 1 /* | 1 /* |
| 2 * templates.c: Implementation of the template processing | 2 * templates.c: Implementation of the template processing |
| 3 * | 3 * |
| 4 * Reference: | 4 * Reference: |
| 5 * http://www.w3.org/TR/1999/REC-xslt-19991116 | 5 * http://www.w3.org/TR/1999/REC-xslt-19991116 |
| 6 * | 6 * |
| 7 * See Copyright for the status of this software. | 7 * See Copyright for the status of this software. |
| 8 * | 8 * |
| 9 * daniel@veillard.com | 9 * daniel@veillard.com |
| 10 */ | 10 */ |
| (...skipping 22 matching lines...) Expand all Loading... |
| 33 | 33 |
| 34 #ifdef WITH_XSLT_DEBUG | 34 #ifdef WITH_XSLT_DEBUG |
| 35 #define WITH_XSLT_DEBUG_TEMPLATES | 35 #define WITH_XSLT_DEBUG_TEMPLATES |
| 36 #endif | 36 #endif |
| 37 | 37 |
| 38 /************************************************************************ | 38 /************************************************************************ |
| 39 * * | 39 * * |
| 40 * Module interfaces * | 40 * Module interfaces * |
| 41 * * | 41 * * |
| 42 ************************************************************************/ | 42 ************************************************************************/ |
| 43 | 43 |
| 44 /** | 44 /** |
| 45 * xsltEvalXPathPredicate: | 45 * xsltEvalXPathPredicate: |
| 46 * @ctxt: the XSLT transformation context | 46 * @ctxt: the XSLT transformation context |
| 47 * @comp: the XPath compiled expression | 47 * @comp: the XPath compiled expression |
| 48 * @nsList: the namespaces in scope | 48 * @nsList: the namespaces in scope |
| 49 * @nsNr: the number of namespaces in scope | 49 * @nsNr: the number of namespaces in scope |
| 50 * | 50 * |
| 51 * Process the expression using XPath and evaluate the result as | 51 * Process the expression using XPath and evaluate the result as |
| 52 * an XPath predicate | 52 * an XPath predicate |
| 53 * | 53 * |
| (...skipping 138 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 192 * free the result. | 192 * free the result. |
| 193 */ | 193 */ |
| 194 xmlChar * | 194 xmlChar * |
| 195 xsltEvalTemplateString(xsltTransformContextPtr ctxt, | 195 xsltEvalTemplateString(xsltTransformContextPtr ctxt, |
| 196 xmlNodePtr contextNode, | 196 xmlNodePtr contextNode, |
| 197 xmlNodePtr inst) | 197 xmlNodePtr inst) |
| 198 { | 198 { |
| 199 xmlNodePtr oldInsert, insert = NULL; | 199 xmlNodePtr oldInsert, insert = NULL; |
| 200 xmlChar *ret; | 200 xmlChar *ret; |
| 201 | 201 |
| 202 if ((ctxt == NULL) || (contextNode == NULL) || (inst == NULL)) | 202 if ((ctxt == NULL) || (contextNode == NULL) || (inst == NULL) || |
| 203 (inst->type != XML_ELEMENT_NODE)) |
| 203 return(NULL); | 204 return(NULL); |
| 204 | 205 |
| 205 if (inst->children == NULL) | 206 if (inst->children == NULL) |
| 206 return(NULL); | 207 return(NULL); |
| 207 | 208 |
| 208 /* | 209 /* |
| 209 * This creates a temporary element-node to add the resulting | 210 * This creates a temporary element-node to add the resulting |
| 210 * text content to. | 211 * text content to. |
| 211 * OPTIMIZE TODO: Keep such an element-node in the transformation | 212 * OPTIMIZE TODO: Keep such an element-node in the transformation |
| 212 * context to avoid creating it every time. | 213 * context to avoid creating it every time. |
| (...skipping 60 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 273 if (*(cur+1) == '{') { /* escaped '{' */ | 274 if (*(cur+1) == '{') { /* escaped '{' */ |
| 274 cur++; | 275 cur++; |
| 275 ret = xmlStrncat(ret, str, cur - str); | 276 ret = xmlStrncat(ret, str, cur - str); |
| 276 cur++; | 277 cur++; |
| 277 str = cur; | 278 str = cur; |
| 278 continue; | 279 continue; |
| 279 } | 280 } |
| 280 ret = xmlStrncat(ret, str, cur - str); | 281 ret = xmlStrncat(ret, str, cur - str); |
| 281 str = cur; | 282 str = cur; |
| 282 cur++; | 283 cur++; |
| 283 » while ((*cur != 0) && (*cur != '}')) 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 } |
| 284 if (*cur == 0) { | 295 if (*cur == 0) { |
| 285 xsltTransformError(ctxt, NULL, inst, | 296 xsltTransformError(ctxt, NULL, inst, |
| 286 "xsltAttrTemplateValueProcessNode: unmatched '{'\n"); | 297 "xsltAttrTemplateValueProcessNode: unmatched '{'\n"); |
| 287 ret = xmlStrncat(ret, str, cur - str); | 298 ret = xmlStrncat(ret, str, cur - str); |
| 288 return(ret); | 299 return(ret); |
| 289 } | 300 } |
| 290 str++; | 301 str++; |
| 291 expr = xmlStrndup(str, cur - str); | 302 expr = xmlStrndup(str, cur - str); |
| 292 if (expr == NULL) | 303 if (expr == NULL) |
| 293 return(ret); | 304 return(ret); |
| (...skipping 80 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 374 * Returns the computed string value or NULL, must be deallocated by the | 385 * Returns the computed string value or NULL, must be deallocated by the |
| 375 * caller. | 386 * caller. |
| 376 */ | 387 */ |
| 377 xmlChar * | 388 xmlChar * |
| 378 xsltEvalAttrValueTemplate(xsltTransformContextPtr ctxt, xmlNodePtr inst, | 389 xsltEvalAttrValueTemplate(xsltTransformContextPtr ctxt, xmlNodePtr inst, |
| 379 const xmlChar *name, const xmlChar *ns) | 390 const xmlChar *name, const xmlChar *ns) |
| 380 { | 391 { |
| 381 xmlChar *ret; | 392 xmlChar *ret; |
| 382 xmlChar *expr; | 393 xmlChar *expr; |
| 383 | 394 |
| 384 if ((ctxt == NULL) || (inst == NULL) || (name == NULL)) | 395 if ((ctxt == NULL) || (inst == NULL) || (name == NULL) || |
| 396 (inst->type != XML_ELEMENT_NODE)) |
| 385 return(NULL); | 397 return(NULL); |
| 386 | 398 |
| 387 expr = xsltGetNsProp(inst, name, ns); | 399 expr = xsltGetNsProp(inst, name, ns); |
| 388 if (expr == NULL) | 400 if (expr == NULL) |
| 389 return(NULL); | 401 return(NULL); |
| 390 | 402 |
| 391 /* | 403 /* |
| 392 * TODO: though now {} is detected ahead, it would still be good to | 404 * TODO: though now {} is detected ahead, it would still be good to |
| 393 * optimize both functions to keep the splitted value if the | 405 * optimize both functions to keep the splitted value if the |
| 394 * attribute content and the XPath precompiled expressions around | 406 * attribute content and the XPath precompiled expressions around |
| (...skipping 23 matching lines...) Expand all Loading... |
| 418 * | 430 * |
| 419 * Returns the static string value or NULL, must be deallocated by the | 431 * Returns the static string value or NULL, must be deallocated by the |
| 420 * caller. | 432 * caller. |
| 421 */ | 433 */ |
| 422 const xmlChar * | 434 const xmlChar * |
| 423 xsltEvalStaticAttrValueTemplate(xsltStylesheetPtr style, xmlNodePtr inst, | 435 xsltEvalStaticAttrValueTemplate(xsltStylesheetPtr style, xmlNodePtr inst, |
| 424 const xmlChar *name, const xmlChar *ns, int *found) { | 436 const xmlChar *name, const xmlChar *ns, int *found) { |
| 425 const xmlChar *ret; | 437 const xmlChar *ret; |
| 426 xmlChar *expr; | 438 xmlChar *expr; |
| 427 | 439 |
| 428 if ((style == NULL) || (inst == NULL) || (name == NULL)) | 440 if ((style == NULL) || (inst == NULL) || (name == NULL) || |
| 441 (inst->type != XML_ELEMENT_NODE)) |
| 429 return(NULL); | 442 return(NULL); |
| 430 | 443 |
| 431 expr = xsltGetNsProp(inst, name, ns); | 444 expr = xsltGetNsProp(inst, name, ns); |
| 432 if (expr == NULL) { | 445 if (expr == NULL) { |
| 433 *found = 0; | 446 *found = 0; |
| 434 return(NULL); | 447 return(NULL); |
| 435 } | 448 } |
| 436 *found = 1; | 449 *found = 1; |
| 437 | 450 |
| 438 ret = xmlStrchr(expr, '{'); | 451 ret = xmlStrchr(expr, '{'); |
| 439 if (ret != NULL) { | 452 if (ret != NULL) { |
| 440 xmlFree(expr); | 453 xmlFree(expr); |
| 441 return(NULL); | 454 return(NULL); |
| 442 } | 455 } |
| 443 ret = xmlDictLookup(style->dict, expr, -1); | 456 ret = xmlDictLookup(style->dict, expr, -1); |
| 444 xmlFree(expr); | 457 xmlFree(expr); |
| 445 return(ret); | 458 return(ret); |
| 446 } | 459 } |
| 447 | 460 |
| 448 /** | 461 /** |
| 449 * xsltAttrTemplateProcess: | 462 * xsltAttrTemplateProcess: |
| 450 * @ctxt: the XSLT transformation context | 463 * @ctxt: the XSLT transformation context |
| 451 * @target: the element where the attribute will be grafted | 464 * @target: the element where the attribute will be grafted |
| 452 * @attr: the attribute node of a literal result element | 465 * @attr: the attribute node of a literal result element |
| 453 * | 466 * |
| 454 * Process one attribute of a Literal Result Element (in the stylesheet). | 467 * Process one attribute of a Literal Result Element (in the stylesheet). |
| 455 * Evaluates Attribute Value Templates and copies the attribute over to | 468 * Evaluates Attribute Value Templates and copies the attribute over to |
| 456 * the result element. | 469 * the result element. |
| 457 * This does *not* process attribute sets (xsl:use-attribute-set). | 470 * This does *not* process attribute sets (xsl:use-attribute-set). |
| 458 * | 471 * |
| 459 * | 472 * |
| 460 * Returns the generated attribute node. | 473 * Returns the generated attribute node. |
| 461 */ | 474 */ |
| 462 xmlAttrPtr | 475 xmlAttrPtr |
| 463 xsltAttrTemplateProcess(xsltTransformContextPtr ctxt, xmlNodePtr target, | 476 xsltAttrTemplateProcess(xsltTransformContextPtr ctxt, xmlNodePtr target, |
| 464 xmlAttrPtr attr) | 477 xmlAttrPtr attr) |
| 465 { | 478 { |
| 466 const xmlChar *value; | 479 const xmlChar *value; |
| 467 xmlAttrPtr ret; | 480 xmlAttrPtr ret; |
| 468 | 481 |
| 469 if ((ctxt == NULL) || (attr == NULL) || (target == NULL)) | 482 if ((ctxt == NULL) || (attr == NULL) || (target == NULL) || |
| 483 (target->type != XML_ELEMENT_NODE)) |
| 470 return(NULL); | 484 return(NULL); |
| 471 | 485 |
| 472 if (attr->type != XML_ATTRIBUTE_NODE) | 486 if (attr->type != XML_ATTRIBUTE_NODE) |
| 473 return(NULL); | 487 return(NULL); |
| 474 | 488 |
| 475 /* | 489 /* |
| 476 * Skip all XSLT attributes. | 490 * Skip all XSLT attributes. |
| 477 */ | 491 */ |
| 478 #ifdef XSLT_REFACTORED | 492 #ifdef XSLT_REFACTORED |
| 479 if (attr->psvi == xsltXSLTAttrMarker) | 493 if (attr->psvi == xsltXSLTAttrMarker) |
| 480 return(NULL); | 494 return(NULL); |
| 481 #else | 495 #else |
| 482 if ((attr->ns != NULL) && xmlStrEqual(attr->ns->href, XSLT_NAMESPACE)) | 496 if ((attr->ns != NULL) && xmlStrEqual(attr->ns->href, XSLT_NAMESPACE)) |
| 483 return(NULL); | 497 return(NULL); |
| 484 #endif | 498 #endif |
| 485 /* | 499 /* |
| 486 * Get the value. | 500 * Get the value. |
| 487 */ | 501 */ |
| 488 if (attr->children != NULL) { | 502 if (attr->children != NULL) { |
| (...skipping 16 matching lines...) Expand all Loading... |
| 505 ret = target->properties; | 519 ret = target->properties; |
| 506 while (ret != NULL) { | 520 while (ret != NULL) { |
| 507 if (((attr->ns != NULL) == (ret->ns != NULL)) && | 521 if (((attr->ns != NULL) == (ret->ns != NULL)) && |
| 508 xmlStrEqual(ret->name, attr->name) && | 522 xmlStrEqual(ret->name, attr->name) && |
| 509 ((attr->ns == NULL) || xmlStrEqual(ret->ns->href, attr->ns->href))) | 523 ((attr->ns == NULL) || xmlStrEqual(ret->ns->href, attr->ns->href))) |
| 510 { | 524 { |
| 511 break; | 525 break; |
| 512 } | 526 } |
| 513 ret = ret->next; | 527 ret = ret->next; |
| 514 } | 528 } |
| 515 if (ret != NULL) {» | 529 if (ret != NULL) { |
| 516 /* free the existing value */ | 530 /* free the existing value */ |
| 517 xmlFreeNodeList(ret->children); | 531 xmlFreeNodeList(ret->children); |
| 518 ret->children = ret->last = NULL; | 532 ret->children = ret->last = NULL; |
| 519 /* | 533 /* |
| 520 * Adjust ns-prefix if needed. | 534 * Adjust ns-prefix if needed. |
| 521 */ | 535 */ |
| 522 if ((ret->ns != NULL) && | 536 if ((ret->ns != NULL) && |
| 523 (! xmlStrEqual(ret->ns->prefix, attr->ns->prefix))) | 537 (! xmlStrEqual(ret->ns->prefix, attr->ns->prefix))) |
| 524 { | 538 { |
| 525 ret->ns = xsltGetNamespace(ctxt, attr->parent, attr->ns, target); | 539 ret->ns = xsltGetNamespace(ctxt, attr->parent, attr->ns, target); |
| 526 } | 540 } |
| 527 } else { | 541 } else { |
| 528 /* create a new attribute */ | 542 /* create a new attribute */ |
| 529 if (attr->ns != NULL) | 543 if (attr->ns != NULL) |
| 530 ret = xmlNewNsProp(target, | 544 ret = xmlNewNsProp(target, |
| 531 xsltGetNamespace(ctxt, attr->parent, attr->ns, target), | 545 xsltGetNamespace(ctxt, attr->parent, attr->ns, target), |
| 532 attr->name, NULL); | 546 attr->name, NULL); |
| 533 else | 547 else |
| 534 » ret = xmlNewNsProp(target, NULL, attr->name, NULL);» | 548 » ret = xmlNewNsProp(target, NULL, attr->name, NULL); |
| 535 } | 549 } |
| 536 /* | 550 /* |
| 537 * Set the value. | 551 * Set the value. |
| 538 */ | 552 */ |
| 539 if (ret != NULL) { | 553 if (ret != NULL) { |
| 540 xmlNodePtr text; | 554 xmlNodePtr text; |
| 541 | 555 |
| 542 text = xmlNewText(NULL); | 556 text = xmlNewText(NULL); |
| 543 if (text != NULL) { | 557 if (text != NULL) { |
| 544 ret->last = ret->children = text; | 558 ret->last = ret->children = text; |
| (...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 576 (target->doc->dict == ctxt->dict) && | 590 (target->doc->dict == ctxt->dict) && |
| 577 xmlDictOwns(ctxt->dict, value)) { | 591 xmlDictOwns(ctxt->dict, value)) { |
| 578 text->content = (xmlChar *) value; | 592 text->content = (xmlChar *) value; |
| 579 } else { | 593 } else { |
| 580 text->content = xmlStrdup(value); | 594 text->content = xmlStrdup(value); |
| 581 } | 595 } |
| 582 } | 596 } |
| 583 } else { | 597 } else { |
| 584 if (attr->ns) { | 598 if (attr->ns) { |
| 585 xsltTransformError(ctxt, NULL, attr->parent, | 599 xsltTransformError(ctxt, NULL, attr->parent, |
| 586 » » "Internal error: Failed to create attribute '{%s}%s'.\n", | 600 » » "Internal error: Failed to create attribute '{%s}%s'.\n", |
| 587 attr->ns->href, attr->name); | 601 attr->ns->href, attr->name); |
| 588 } else { | 602 } else { |
| 589 xsltTransformError(ctxt, NULL, attr->parent, | 603 xsltTransformError(ctxt, NULL, attr->parent, |
| 590 » » "Internal error: Failed to create attribute '%s'.\n", | 604 » » "Internal error: Failed to create attribute '%s'.\n", |
| 591 attr->name); | 605 attr->name); |
| 592 } | 606 } |
| 593 } | 607 } |
| 594 return(ret); | 608 return(ret); |
| 595 } | 609 } |
| 596 | 610 |
| 597 | 611 |
| 598 /** | 612 /** |
| 599 * xsltAttrListTemplateProcess: | 613 * xsltAttrListTemplateProcess: |
| 600 * @ctxt: the XSLT transformation context | 614 * @ctxt: the XSLT transformation context |
| 601 * @target: the element where the attributes will be grafted | 615 * @target: the element where the attributes will be grafted |
| 602 * @attrs: the first attribute | 616 * @attrs: the first attribute |
| 603 * | 617 * |
| 604 * Processes all attributes of a Literal Result Element. | 618 * Processes all attributes of a Literal Result Element. |
| 605 * Attribute references are applied via xsl:use-attribute-set | 619 * Attribute references are applied via xsl:use-attribute-set |
| 606 * attributes. | 620 * attributes. |
| 607 * Copies all non XSLT-attributes over to the @target element | 621 * Copies all non XSLT-attributes over to the @target element |
| 608 * and evaluates Attribute Value Templates. | 622 * and evaluates Attribute Value Templates. |
| 609 * | 623 * |
| 610 * Called by xsltApplySequenceConstructor() (transform.c). | 624 * Called by xsltApplySequenceConstructor() (transform.c). |
| 611 * | 625 * |
| 612 * Returns a new list of attribute nodes, or NULL in case of error. | 626 * Returns a new list of attribute nodes, or NULL in case of error. |
| 613 * (Don't assign the result to @target->properties; if | 627 * (Don't assign the result to @target->properties; if |
| 614 * the result is NULL, you'll get memory leaks, since the | 628 * the result is NULL, you'll get memory leaks, since the |
| 615 * attributes will be disattached.) | 629 * attributes will be disattached.) |
| 616 */ | 630 */ |
| 617 xmlAttrPtr | 631 xmlAttrPtr |
| 618 xsltAttrListTemplateProcess(xsltTransformContextPtr ctxt, | 632 xsltAttrListTemplateProcess(xsltTransformContextPtr ctxt, |
| 619 xmlNodePtr target, xmlAttrPtr attrs) | 633 xmlNodePtr target, xmlAttrPtr attrs) |
| 620 { | 634 { |
| 621 xmlAttrPtr attr, copy, last; | 635 xmlAttrPtr attr, copy, last; |
| 622 xmlNodePtr oldInsert, text; | 636 xmlNodePtr oldInsert, text; |
| 623 xmlNsPtr origNs = NULL, copyNs = NULL; | 637 xmlNsPtr origNs = NULL, copyNs = NULL; |
| 624 const xmlChar *value; | 638 const xmlChar *value; |
| 625 xmlChar *valueAVT; | 639 xmlChar *valueAVT; |
| 626 | 640 |
| 627 if ((ctxt == NULL) || (target == NULL) || (attrs == NULL)) | 641 if ((ctxt == NULL) || (target == NULL) || (attrs == NULL) || |
| 642 (target->type != XML_ELEMENT_NODE)) |
| 628 return(NULL); | 643 return(NULL); |
| 629 | 644 |
| 630 oldInsert = ctxt->insert; | 645 oldInsert = ctxt->insert; |
| 631 ctxt->insert = target; | 646 ctxt->insert = target; |
| 632 | 647 |
| 633 /* | 648 /* |
| 634 * Instantiate LRE-attributes. | 649 * Instantiate LRE-attributes. |
| 635 */ | 650 */ |
| 636 if (target->properties) { | 651 if (target->properties) { |
| 637 last = target->properties; | 652 last = target->properties; |
| 638 while (last->next != NULL) | 653 while (last->next != NULL) |
| 639 last = last->next; | 654 last = last->next; |
| 640 } else { | 655 } else { |
| 641 last = NULL; | 656 last = NULL; |
| (...skipping 172 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 814 * @node: the attribute template node | 829 * @node: the attribute template node |
| 815 * | 830 * |
| 816 * Obsolete. Don't use it. | 831 * Obsolete. Don't use it. |
| 817 * | 832 * |
| 818 * Returns NULL. | 833 * Returns NULL. |
| 819 */ | 834 */ |
| 820 xmlNodePtr * | 835 xmlNodePtr * |
| 821 xsltTemplateProcess(xsltTransformContextPtr ctxt ATTRIBUTE_UNUSED, xmlNodePtr no
de) { | 836 xsltTemplateProcess(xsltTransformContextPtr ctxt ATTRIBUTE_UNUSED, xmlNodePtr no
de) { |
| 822 if (node == NULL) | 837 if (node == NULL) |
| 823 return(NULL); | 838 return(NULL); |
| 824 | 839 |
| 825 return(0); | 840 return(0); |
| 826 } | 841 } |
| 827 | 842 |
| 828 | 843 |
| OLD | NEW |