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 |