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