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