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 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(xsltGenericDebugContext,
89 "xsltEvalXPathPredicate: returns %d\n", ret));
90 #endif
91 } else {
92 #ifdef WITH_XSLT_DEBUG_TEMPLATES
93 XSLT_TRACE(ctxt,XSLT_TRACE_TEMPLATES,xsltGenericDebug(xsltGenericDebugContext,
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 *
xsltEvalXPathStringNs(xsltTransformContextPtr ctxt,xmlXPathCompExprPtr comp,int nsNr,xmlNsPtr * nsList)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(xsltGenericDebugContext,
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 *
xsltEvalXPathString(xsltTransformContextPtr ctxt,xmlXPathCompExprPtr comp)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 *
xsltEvalTemplateString(xsltTransformContextPtr ctxt,xmlNodePtr contextNode,xmlNodePtr inst)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 *
xsltAttrTemplateValueProcessNode(xsltTransformContextPtr ctxt,const xmlChar * str,xmlNodePtr inst)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 goto exit;
312 }
313 str++;
314 expr = xmlStrndup(str, cur - str);
315 if (expr == NULL)
316 goto exit;
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 = xmlXPathCtxtCompile(ctxt->xpathCtxt, 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 exit:
365 if (nsList != NULL)
366 xmlFree(nsList);
367
368 return(ret);
369 }
370
371 /**
372 * xsltAttrTemplateValueProcess:
373 * @ctxt: the XSLT transformation context
374 * @str: the attribute template node value
375 *
376 * Process the given node and return the new string value.
377 *
378 * Returns the computed string value or NULL, must be deallocated by the
379 * caller.
380 */
381 xmlChar *
xsltAttrTemplateValueProcess(xsltTransformContextPtr ctxt,const xmlChar * str)382 xsltAttrTemplateValueProcess(xsltTransformContextPtr ctxt, const xmlChar *str) {
383 return(xsltAttrTemplateValueProcessNode(ctxt, str, NULL));
384 }
385
386 /**
387 * xsltEvalAttrValueTemplate:
388 * @ctxt: the XSLT transformation context
389 * @inst: the instruction (or LRE) in the stylesheet holding the
390 * attribute with an AVT
391 * @name: the attribute QName
392 * @ns: the attribute namespace URI
393 *
394 * Evaluate a attribute value template, i.e. the attribute value can
395 * contain expressions contained in curly braces ({}) and those are
396 * substituted by they computed value.
397 *
398 * Returns the computed string value or NULL, must be deallocated by the
399 * caller.
400 */
401 xmlChar *
xsltEvalAttrValueTemplate(xsltTransformContextPtr ctxt,xmlNodePtr inst,const xmlChar * name,const xmlChar * ns)402 xsltEvalAttrValueTemplate(xsltTransformContextPtr ctxt, xmlNodePtr inst,
403 const xmlChar *name, const xmlChar *ns)
404 {
405 xmlChar *ret;
406 xmlChar *expr;
407
408 if ((ctxt == NULL) || (inst == NULL) || (name == NULL) ||
409 (inst->type != XML_ELEMENT_NODE))
410 return(NULL);
411
412 expr = xsltGetNsProp(inst, name, ns);
413 if (expr == NULL)
414 return(NULL);
415
416 /*
417 * TODO: though now {} is detected ahead, it would still be good to
418 * optimize both functions to keep the splitted value if the
419 * attribute content and the XPath precompiled expressions around
420 */
421
422 ret = xsltAttrTemplateValueProcessNode(ctxt, expr, inst);
423 #ifdef WITH_XSLT_DEBUG_TEMPLATES
424 XSLT_TRACE(ctxt,XSLT_TRACE_TEMPLATES,xsltGenericDebug(xsltGenericDebugContext,
425 "xsltEvalAttrValueTemplate: %s returns %s\n", expr, ret));
426 #endif
427 if (expr != NULL)
428 xmlFree(expr);
429 return(ret);
430 }
431
432 /**
433 * xsltEvalStaticAttrValueTemplate:
434 * @style: the XSLT stylesheet
435 * @inst: the instruction (or LRE) in the stylesheet holding the
436 * attribute with an AVT
437 * @name: the attribute Name
438 * @ns: the attribute namespace URI
439 * @found: indicator whether the attribute is present
440 *
441 * Check if an attribute value template has a static value, i.e. the
442 * attribute value does not contain expressions contained in curly braces ({})
443 *
444 * Returns the static string value or NULL, must be deallocated by the
445 * caller.
446 */
447 const xmlChar *
xsltEvalStaticAttrValueTemplate(xsltStylesheetPtr style,xmlNodePtr inst,const xmlChar * name,const xmlChar * ns,int * found)448 xsltEvalStaticAttrValueTemplate(xsltStylesheetPtr style, xmlNodePtr inst,
449 const xmlChar *name, const xmlChar *ns, int *found) {
450 const xmlChar *ret;
451 xmlChar *expr;
452
453 if ((style == NULL) || (inst == NULL) || (name == NULL) ||
454 (inst->type != XML_ELEMENT_NODE))
455 return(NULL);
456
457 expr = xsltGetNsProp(inst, name, ns);
458 if (expr == NULL) {
459 *found = 0;
460 return(NULL);
461 }
462 *found = 1;
463
464 ret = xmlStrchr(expr, '{');
465 if (ret != NULL) {
466 xmlFree(expr);
467 return(NULL);
468 }
469 ret = xmlDictLookup(style->dict, expr, -1);
470 xmlFree(expr);
471 return(ret);
472 }
473
474 /**
475 * xsltAttrTemplateProcess:
476 * @ctxt: the XSLT transformation context
477 * @target: the element where the attribute will be grafted
478 * @attr: the attribute node of a literal result element
479 *
480 * Process one attribute of a Literal Result Element (in the stylesheet).
481 * Evaluates Attribute Value Templates and copies the attribute over to
482 * the result element.
483 * This does *not* process attribute sets (xsl:use-attribute-set).
484 *
485 *
486 * Returns the generated attribute node.
487 */
488 xmlAttrPtr
xsltAttrTemplateProcess(xsltTransformContextPtr ctxt,xmlNodePtr target,xmlAttrPtr attr)489 xsltAttrTemplateProcess(xsltTransformContextPtr ctxt, xmlNodePtr target,
490 xmlAttrPtr attr)
491 {
492 const xmlChar *value;
493 xmlAttrPtr ret;
494
495 if ((ctxt == NULL) || (attr == NULL) || (target == NULL) ||
496 (target->type != XML_ELEMENT_NODE))
497 return(NULL);
498
499 if (attr->type != XML_ATTRIBUTE_NODE)
500 return(NULL);
501
502 /*
503 * Skip all XSLT attributes.
504 */
505 #ifdef XSLT_REFACTORED
506 if (attr->psvi == xsltXSLTAttrMarker)
507 return(NULL);
508 #else
509 if ((attr->ns != NULL) && xmlStrEqual(attr->ns->href, XSLT_NAMESPACE))
510 return(NULL);
511 #endif
512 /*
513 * Get the value.
514 */
515 if (attr->children != NULL) {
516 if ((attr->children->type != XML_TEXT_NODE) ||
517 (attr->children->next != NULL))
518 {
519 xsltTransformError(ctxt, NULL, attr->parent,
520 "Internal error: The children of an attribute node of a "
521 "literal result element are not in the expected form.\n");
522 return(NULL);
523 }
524 value = attr->children->content;
525 if (value == NULL)
526 value = xmlDictLookup(ctxt->dict, BAD_CAST "", 0);
527 } else
528 value = xmlDictLookup(ctxt->dict, BAD_CAST "", 0);
529 /*
530 * Overwrite duplicates.
531 */
532 ret = target->properties;
533 while (ret != NULL) {
534 if (((attr->ns != NULL) == (ret->ns != NULL)) &&
535 xmlStrEqual(ret->name, attr->name) &&
536 ((attr->ns == NULL) || xmlStrEqual(ret->ns->href, attr->ns->href)))
537 {
538 break;
539 }
540 ret = ret->next;
541 }
542 if (ret != NULL) {
543 /* free the existing value */
544 xmlFreeNodeList(ret->children);
545 ret->children = ret->last = NULL;
546 /*
547 * Adjust ns-prefix if needed.
548 */
549 if ((ret->ns != NULL) &&
550 (! xmlStrEqual(ret->ns->prefix, attr->ns->prefix)))
551 {
552 ret->ns = xsltGetNamespace(ctxt, attr->parent, attr->ns, target);
553 }
554 } else {
555 /* create a new attribute */
556 if (attr->ns != NULL)
557 ret = xmlNewNsProp(target,
558 xsltGetNamespace(ctxt, attr->parent, attr->ns, target),
559 attr->name, NULL);
560 else
561 ret = xmlNewNsProp(target, NULL, attr->name, NULL);
562 }
563 /*
564 * Set the value.
565 */
566 if (ret != NULL) {
567 xmlNodePtr text;
568
569 text = xmlNewText(NULL);
570 if (text != NULL) {
571 ret->last = ret->children = text;
572 text->parent = (xmlNodePtr) ret;
573 text->doc = ret->doc;
574
575 if (attr->psvi != NULL) {
576 /*
577 * Evaluate the Attribute Value Template.
578 */
579 xmlChar *val;
580 val = xsltEvalAVT(ctxt, attr->psvi, attr->parent);
581 if (val == NULL) {
582 /*
583 * TODO: Damn, we need an easy mechanism to report
584 * qualified names!
585 */
586 if (attr->ns) {
587 xsltTransformError(ctxt, NULL, attr->parent,
588 "Internal error: Failed to evaluate the AVT "
589 "of attribute '{%s}%s'.\n",
590 attr->ns->href, attr->name);
591 } else {
592 xsltTransformError(ctxt, NULL, attr->parent,
593 "Internal error: Failed to evaluate the AVT "
594 "of attribute '%s'.\n",
595 attr->name);
596 }
597 text->content = xmlStrdup(BAD_CAST "");
598 } else {
599 text->content = val;
600 }
601 } else if ((ctxt->internalized) && (target != NULL) &&
602 (target->doc != NULL) &&
603 (target->doc->dict == ctxt->dict) &&
604 xmlDictOwns(ctxt->dict, value)) {
605 text->content = (xmlChar *) value;
606 } else {
607 text->content = xmlStrdup(value);
608 }
609 }
610 } else {
611 if (attr->ns) {
612 xsltTransformError(ctxt, NULL, attr->parent,
613 "Internal error: Failed to create attribute '{%s}%s'.\n",
614 attr->ns->href, attr->name);
615 } else {
616 xsltTransformError(ctxt, NULL, attr->parent,
617 "Internal error: Failed to create attribute '%s'.\n",
618 attr->name);
619 }
620 }
621 return(ret);
622 }
623
624
625 /**
626 * xsltAttrListTemplateProcess:
627 * @ctxt: the XSLT transformation context
628 * @target: the element where the attributes will be grafted
629 * @attrs: the first attribute
630 *
631 * Processes all attributes of a Literal Result Element.
632 * Attribute references are applied via xsl:use-attribute-set
633 * attributes.
634 * Copies all non XSLT-attributes over to the @target element
635 * and evaluates Attribute Value Templates.
636 *
637 * Called by xsltApplySequenceConstructor() (transform.c).
638 *
639 * Returns a new list of attribute nodes, or NULL in case of error.
640 * (Don't assign the result to @target->properties; if
641 * the result is NULL, you'll get memory leaks, since the
642 * attributes will be disattached.)
643 */
644 xmlAttrPtr
xsltAttrListTemplateProcess(xsltTransformContextPtr ctxt,xmlNodePtr target,xmlAttrPtr attrs)645 xsltAttrListTemplateProcess(xsltTransformContextPtr ctxt,
646 xmlNodePtr target, xmlAttrPtr attrs)
647 {
648 xmlAttrPtr attr, copy, last = NULL;
649 xmlNodePtr oldInsert, text;
650 xmlNsPtr origNs = NULL, copyNs = NULL;
651 const xmlChar *value;
652 xmlChar *valueAVT;
653 int hasAttr = 0;
654
655 if ((ctxt == NULL) || (target == NULL) || (attrs == NULL) ||
656 (target->type != XML_ELEMENT_NODE))
657 return(NULL);
658
659 oldInsert = ctxt->insert;
660 ctxt->insert = target;
661
662 /*
663 * Apply attribute-sets.
664 */
665 attr = attrs;
666 do {
667 #ifdef XSLT_REFACTORED
668 if ((attr->psvi == xsltXSLTAttrMarker) &&
669 xmlStrEqual(attr->name, (const xmlChar *)"use-attribute-sets"))
670 {
671 xsltApplyAttributeSet(ctxt, ctxt->node, (xmlNodePtr) attr, NULL);
672 }
673 #else
674 if ((attr->ns != NULL) &&
675 xmlStrEqual(attr->name, (const xmlChar *)"use-attribute-sets") &&
676 xmlStrEqual(attr->ns->href, XSLT_NAMESPACE))
677 {
678 xsltApplyAttributeSet(ctxt, ctxt->node, (xmlNodePtr) attr, NULL);
679 }
680 #endif
681 attr = attr->next;
682 } while (attr != NULL);
683
684 if (target->properties != NULL) {
685 hasAttr = 1;
686 }
687
688 /*
689 * Instantiate LRE-attributes.
690 */
691 attr = attrs;
692 do {
693 /*
694 * Skip XSLT attributes.
695 */
696 #ifdef XSLT_REFACTORED
697 if (attr->psvi == xsltXSLTAttrMarker) {
698 goto next_attribute;
699 }
700 #else
701 if ((attr->ns != NULL) &&
702 xmlStrEqual(attr->ns->href, XSLT_NAMESPACE))
703 {
704 goto next_attribute;
705 }
706 #endif
707 /*
708 * Get the value.
709 */
710 if (attr->children != NULL) {
711 if ((attr->children->type != XML_TEXT_NODE) ||
712 (attr->children->next != NULL))
713 {
714 xsltTransformError(ctxt, NULL, attr->parent,
715 "Internal error: The children of an attribute node of a "
716 "literal result element are not in the expected form.\n");
717 goto error;
718 }
719 value = attr->children->content;
720 if (value == NULL)
721 value = xmlDictLookup(ctxt->dict, BAD_CAST "", 0);
722 } else
723 value = xmlDictLookup(ctxt->dict, BAD_CAST "", 0);
724
725 /*
726 * Get the namespace. Avoid lookups of same namespaces.
727 */
728 if (attr->ns != origNs) {
729 origNs = attr->ns;
730 if (attr->ns != NULL) {
731 #ifdef XSLT_REFACTORED
732 copyNs = xsltGetSpecialNamespace(ctxt, attr->parent,
733 attr->ns->href, attr->ns->prefix, target);
734 #else
735 copyNs = xsltGetNamespace(ctxt, attr->parent,
736 attr->ns, target);
737 #endif
738 if (copyNs == NULL)
739 goto error;
740 } else
741 copyNs = NULL;
742 }
743 /*
744 * Create a new attribute.
745 */
746 if (hasAttr) {
747 copy = xmlSetNsProp(target, copyNs, attr->name, NULL);
748 } else {
749 /*
750 * Avoid checking for duplicate attributes if there aren't
751 * any attribute sets.
752 */
753 copy = xmlNewDocProp(target->doc, attr->name, NULL);
754
755 if (copy != NULL) {
756 copy->ns = copyNs;
757
758 /*
759 * Attach it to the target element.
760 */
761 copy->parent = target;
762 if (last == NULL) {
763 target->properties = copy;
764 last = copy;
765 } else {
766 last->next = copy;
767 copy->prev = last;
768 last = copy;
769 }
770 }
771 }
772 if (copy == NULL) {
773 if (attr->ns) {
774 xsltTransformError(ctxt, NULL, attr->parent,
775 "Internal error: Failed to create attribute '{%s}%s'.\n",
776 attr->ns->href, attr->name);
777 } else {
778 xsltTransformError(ctxt, NULL, attr->parent,
779 "Internal error: Failed to create attribute '%s'.\n",
780 attr->name);
781 }
782 goto error;
783 }
784
785 /*
786 * Set the value.
787 */
788 text = xmlNewText(NULL);
789 if (text != NULL) {
790 copy->last = copy->children = text;
791 text->parent = (xmlNodePtr) copy;
792 text->doc = copy->doc;
793
794 if (attr->psvi != NULL) {
795 /*
796 * Evaluate the Attribute Value Template.
797 */
798 valueAVT = xsltEvalAVT(ctxt, attr->psvi, attr->parent);
799 if (valueAVT == NULL) {
800 /*
801 * TODO: Damn, we need an easy mechanism to report
802 * qualified names!
803 */
804 if (attr->ns) {
805 xsltTransformError(ctxt, NULL, attr->parent,
806 "Internal error: Failed to evaluate the AVT "
807 "of attribute '{%s}%s'.\n",
808 attr->ns->href, attr->name);
809 } else {
810 xsltTransformError(ctxt, NULL, attr->parent,
811 "Internal error: Failed to evaluate the AVT "
812 "of attribute '%s'.\n",
813 attr->name);
814 }
815 text->content = xmlStrdup(BAD_CAST "");
816 goto error;
817 } else {
818 text->content = valueAVT;
819 }
820 } else if ((ctxt->internalized) &&
821 (target->doc != NULL) &&
822 (target->doc->dict == ctxt->dict) &&
823 xmlDictOwns(ctxt->dict, value))
824 {
825 text->content = (xmlChar *) value;
826 } else {
827 text->content = xmlStrdup(value);
828 }
829 if ((copy != NULL) && (text != NULL) &&
830 (xmlIsID(copy->doc, copy->parent, copy)))
831 xmlAddID(NULL, copy->doc, text->content, copy);
832 }
833
834 next_attribute:
835 attr = attr->next;
836 } while (attr != NULL);
837
838 ctxt->insert = oldInsert;
839 return(target->properties);
840
841 error:
842 ctxt->insert = oldInsert;
843 return(NULL);
844 }
845
846
847 /**
848 * xsltTemplateProcess:
849 * @ctxt: the XSLT transformation context
850 * @node: the attribute template node
851 *
852 * Obsolete. Don't use it.
853 *
854 * Returns NULL.
855 */
856 xmlNodePtr *
xsltTemplateProcess(xsltTransformContextPtr ctxt ATTRIBUTE_UNUSED,xmlNodePtr node)857 xsltTemplateProcess(xsltTransformContextPtr ctxt ATTRIBUTE_UNUSED, xmlNodePtr node) {
858 if (node == NULL)
859 return(NULL);
860
861 return(0);
862 }
863
864
865