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