xslt: Import upstream release 1.1.38.
[wine.git] / libs / xslt / libxslt / attributes.c
blob4cc49d0d1a556c9fa8f8775d23f24fd4c15ffa03
1 /*
2 * attributes.c: Implementation of the XSLT attributes handling
4 * Reference:
5 * http://www.w3.org/TR/1999/REC-xslt-19991116
7 * See Copyright for the status of this software.
9 * daniel@veillard.com
12 #define IN_LIBXSLT
13 #include "libxslt.h"
15 #include <string.h>
17 #include <libxml/xmlmemory.h>
18 #include <libxml/tree.h>
19 #include <libxml/hash.h>
20 #include <libxml/xmlerror.h>
21 #include <libxml/uri.h>
22 #include <libxml/parserInternals.h>
23 #include "xslt.h"
24 #include "xsltInternals.h"
25 #include "xsltutils.h"
26 #include "attributes.h"
27 #include "namespaces.h"
28 #include "templates.h"
29 #include "imports.h"
30 #include "transform.h"
31 #include "preproc.h"
33 #define WITH_XSLT_DEBUG_ATTRIBUTES
34 #ifdef WITH_XSLT_DEBUG
35 #define WITH_XSLT_DEBUG_ATTRIBUTES
36 #endif
39 * Useful macros
41 #ifdef IS_BLANK
42 #undef IS_BLANK
43 #endif
45 #define IS_BLANK(c) (((c) == 0x20) || ((c) == 0x09) || ((c) == 0xA) || \
46 ((c) == 0x0D))
48 #define IS_BLANK_NODE(n) \
49 (((n)->type == XML_TEXT_NODE) && (xsltIsBlank((n)->content)))
51 #define ATTRSET_UNRESOLVED 0
52 #define ATTRSET_RESOLVING 1
53 #define ATTRSET_RESOLVED 2
57 * The in-memory structure corresponding to an XSLT Attribute in
58 * an attribute set
62 typedef struct _xsltAttrElem xsltAttrElem;
63 typedef xsltAttrElem *xsltAttrElemPtr;
64 struct _xsltAttrElem {
65 struct _xsltAttrElem *next;/* chained list */
66 xmlNodePtr attr; /* the xsl:attribute definition */
69 typedef struct _xsltUseAttrSet xsltUseAttrSet;
70 typedef xsltUseAttrSet *xsltUseAttrSetPtr;
71 struct _xsltUseAttrSet {
72 struct _xsltUseAttrSet *next; /* chained list */
73 const xmlChar *ncname;
74 const xmlChar *ns;
77 typedef struct _xsltAttrSet xsltAttrSet;
78 typedef xsltAttrSet *xsltAttrSetPtr;
79 struct _xsltAttrSet {
80 int state;
81 xsltAttrElemPtr attrs; /* list head */
82 xsltUseAttrSetPtr useAttrSets; /* list head */
85 typedef struct _xsltAttrSetContext xsltAttrSetContext;
86 typedef xsltAttrSetContext *xsltAttrSetContextPtr;
87 struct _xsltAttrSetContext {
88 xsltStylesheetPtr topStyle;
89 xsltStylesheetPtr style;
90 int error;
93 static void
94 xsltResolveAttrSet(xsltAttrSetPtr set, xsltStylesheetPtr topStyle,
95 xsltStylesheetPtr style, const xmlChar *name,
96 const xmlChar *ns, int depth);
98 /************************************************************************
99 * *
100 * XSLT Attribute handling *
102 ************************************************************************/
105 * xsltNewAttrElem:
106 * @attr: the new xsl:attribute node
108 * Create a new XSLT AttrElem
110 * Returns the newly allocated xsltAttrElemPtr or NULL in case of error
112 static xsltAttrElemPtr
113 xsltNewAttrElem(xmlNodePtr attr) {
114 xsltAttrElemPtr cur;
116 cur = (xsltAttrElemPtr) xmlMalloc(sizeof(xsltAttrElem));
117 if (cur == NULL) {
118 xsltGenericError(xsltGenericErrorContext,
119 "xsltNewAttrElem : malloc failed\n");
120 return(NULL);
122 memset(cur, 0, sizeof(xsltAttrElem));
123 cur->attr = attr;
124 return(cur);
128 * xsltFreeAttrElem:
129 * @attr: an XSLT AttrElem
131 * Free up the memory allocated by @attr
133 static void
134 xsltFreeAttrElem(xsltAttrElemPtr attr) {
135 xmlFree(attr);
139 * xsltFreeAttrElemList:
140 * @list: an XSLT AttrElem list
142 * Free up the memory allocated by @list
144 static void
145 xsltFreeAttrElemList(xsltAttrElemPtr list) {
146 xsltAttrElemPtr next;
148 while (list != NULL) {
149 next = list->next;
150 xsltFreeAttrElem(list);
151 list = next;
156 * xsltAddAttrElemList:
157 * @list: an XSLT AttrElem list
158 * @attr: the new xsl:attribute node
160 * Add the new attribute to the list.
162 * Returns the new list pointer
164 static xsltAttrElemPtr
165 xsltAddAttrElemList(xsltAttrElemPtr list, xmlNodePtr attr) {
166 xsltAttrElemPtr next, cur;
168 if (attr == NULL)
169 return(list);
170 if (list == NULL)
171 return(xsltNewAttrElem(attr));
172 cur = list;
173 while (cur != NULL) {
174 next = cur->next;
175 if (next == NULL) {
176 cur->next = xsltNewAttrElem(attr);
177 return(list);
179 cur = next;
181 return(list);
185 * xsltNewUseAttrSet:
186 * @ncname: local name
187 * @ns: namespace URI
189 * Create a new XSLT UseAttrSet
191 * Returns the newly allocated xsltUseAttrSetPtr or NULL in case of error.
193 static xsltUseAttrSetPtr
194 xsltNewUseAttrSet(const xmlChar *ncname, const xmlChar *ns) {
195 xsltUseAttrSetPtr cur;
197 cur = (xsltUseAttrSetPtr) xmlMalloc(sizeof(xsltUseAttrSet));
198 if (cur == NULL) {
199 xsltGenericError(xsltGenericErrorContext,
200 "xsltNewUseAttrSet : malloc failed\n");
201 return(NULL);
203 memset(cur, 0, sizeof(xsltUseAttrSet));
204 cur->ncname = ncname;
205 cur->ns = ns;
206 return(cur);
210 * xsltFreeUseAttrSet:
211 * @use: an XSLT UseAttrSet
213 * Free up the memory allocated by @use
215 static void
216 xsltFreeUseAttrSet(xsltUseAttrSetPtr use) {
217 xmlFree(use);
221 * xsltFreeUseAttrSetList:
222 * @list: an XSLT UseAttrSet list
224 * Free up the memory allocated by @list
226 static void
227 xsltFreeUseAttrSetList(xsltUseAttrSetPtr list) {
228 xsltUseAttrSetPtr next;
230 while (list != NULL) {
231 next = list->next;
232 xsltFreeUseAttrSet(list);
233 list = next;
238 * xsltAddUseAttrSetList:
239 * @list: a xsltUseAttrSet list
240 * @ncname: local name
241 * @ns: namespace URI
243 * Add the use-attribute-set name to the list.
245 * Returns the new list pointer.
247 static xsltUseAttrSetPtr
248 xsltAddUseAttrSetList(xsltUseAttrSetPtr list, const xmlChar *ncname,
249 const xmlChar *ns) {
250 xsltUseAttrSetPtr next, cur;
252 if (ncname == NULL)
253 return(list);
254 if (list == NULL)
255 return(xsltNewUseAttrSet(ncname, ns));
256 cur = list;
257 while (cur != NULL) {
258 if ((cur->ncname == ncname) && (cur->ns == ns))
259 return(list);
260 next = cur->next;
261 if (next == NULL) {
262 cur->next = xsltNewUseAttrSet(ncname, ns);
263 return(list);
265 cur = next;
267 return(list);
271 * xsltNewAttrSet:
273 * Create a new attribute set.
275 * Returns the newly allocated xsltAttrSetPtr or NULL in case of error.
277 static xsltAttrSetPtr
278 xsltNewAttrSet(void) {
279 xsltAttrSetPtr cur;
281 cur = (xsltAttrSetPtr) xmlMalloc(sizeof(xsltAttrSet));
282 if (cur == NULL) {
283 xsltGenericError(xsltGenericErrorContext,
284 "xsltNewAttrSet : malloc failed\n");
285 return(NULL);
287 memset(cur, 0, sizeof(xsltAttrSet));
288 return(cur);
292 * xsltFreeAttrSet:
293 * @set: an attribute set
295 * Free memory allocated by @set
297 static void
298 xsltFreeAttrSet(xsltAttrSetPtr set) {
299 if (set == NULL)
300 return;
302 xsltFreeAttrElemList(set->attrs);
303 xsltFreeUseAttrSetList(set->useAttrSets);
304 xmlFree(set);
308 * xsltMergeAttrSets:
309 * @set: an attribute set
310 * @other: another attribute set
312 * Add all the attributes from @other to @set,
313 * but drop redefinition of existing values.
315 static void
316 xsltMergeAttrSets(xsltAttrSetPtr set, xsltAttrSetPtr other) {
317 xsltAttrElemPtr cur;
318 xsltAttrElemPtr old = other->attrs;
319 int add;
321 while (old != NULL) {
323 * Check that the attribute is not yet in the list
325 cur = set->attrs;
326 add = 1;
327 while (cur != NULL) {
328 xsltStylePreCompPtr curComp = cur->attr->psvi;
329 xsltStylePreCompPtr oldComp = old->attr->psvi;
331 if ((curComp->name == oldComp->name) &&
332 (curComp->ns == oldComp->ns)) {
333 add = 0;
334 break;
336 if (cur->next == NULL)
337 break;
338 cur = cur->next;
341 if (add == 1) {
342 if (cur == NULL) {
343 set->attrs = xsltNewAttrElem(old->attr);
344 } else if (add) {
345 cur->next = xsltNewAttrElem(old->attr);
349 old = old->next;
353 /************************************************************************
355 * Module interfaces *
357 ************************************************************************/
360 * xsltParseStylesheetAttributeSet:
361 * @style: the XSLT stylesheet
362 * @cur: the "attribute-set" element
364 * parse an XSLT stylesheet attribute-set element
367 void
368 xsltParseStylesheetAttributeSet(xsltStylesheetPtr style, xmlNodePtr cur) {
369 const xmlChar *ncname;
370 const xmlChar *prefix;
371 const xmlChar *nsUri = NULL;
372 xmlChar *value;
373 xmlNodePtr child;
374 xsltAttrSetPtr set;
376 if ((cur == NULL) || (style == NULL) || (cur->type != XML_ELEMENT_NODE))
377 return;
379 value = xmlGetNsProp(cur, (const xmlChar *)"name", NULL);
380 if ((value == NULL) || (*value == 0)) {
381 xsltGenericError(xsltGenericErrorContext,
382 "xsl:attribute-set : name is missing\n");
383 if (value)
384 xmlFree(value);
385 return;
388 if (xmlValidateQName(value, 0)) {
389 xsltTransformError(NULL, style, cur,
390 "xsl:attribute-set : The name '%s' is not a valid QName.\n",
391 value);
392 style->errors++;
393 xmlFree(value);
394 return;
397 ncname = xsltSplitQName(style->dict, value, &prefix);
398 xmlFree(value);
399 value = NULL;
400 if (prefix != NULL) {
401 xmlNsPtr ns = xmlSearchNs(style->doc, cur, prefix);
402 if (ns == NULL) {
403 xsltTransformError(NULL, style, cur,
404 "xsl:attribute-set : No namespace found for QName '%s:%s'\n",
405 prefix, ncname);
406 style->errors++;
407 return;
409 nsUri = ns->href;
412 if (style->attributeSets == NULL) {
413 #ifdef WITH_XSLT_DEBUG_ATTRIBUTES
414 xsltGenericDebug(xsltGenericDebugContext,
415 "creating attribute set table\n");
416 #endif
417 style->attributeSets = xmlHashCreate(10);
419 if (style->attributeSets == NULL)
420 return;
422 set = xmlHashLookup2(style->attributeSets, ncname, nsUri);
423 if (set == NULL) {
424 set = xsltNewAttrSet();
425 if ((set == NULL) ||
426 (xmlHashAddEntry2(style->attributeSets, ncname, nsUri, set) < 0)) {
427 xsltGenericError(xsltGenericErrorContext, "memory error\n");
428 xsltFreeAttrSet(set);
429 return;
434 * Parse the content. Only xsl:attribute elements are allowed.
436 child = cur->children;
437 while (child != NULL) {
439 * Report invalid nodes.
441 if ((child->type != XML_ELEMENT_NODE) ||
442 (child->ns == NULL) ||
443 (! IS_XSLT_ELEM(child)))
445 if (child->type == XML_ELEMENT_NODE)
446 xsltTransformError(NULL, style, child,
447 "xsl:attribute-set : unexpected child %s\n",
448 child->name);
449 else
450 xsltTransformError(NULL, style, child,
451 "xsl:attribute-set : child of unexpected type\n");
452 } else if (!IS_XSLT_NAME(child, "attribute")) {
453 xsltTransformError(NULL, style, child,
454 "xsl:attribute-set : unexpected child xsl:%s\n",
455 child->name);
456 } else {
457 #ifdef WITH_XSLT_DEBUG_ATTRIBUTES
458 xsltGenericDebug(xsltGenericDebugContext,
459 "add attribute to list %s\n", ncname);
460 #endif
461 xsltStylePreCompute(style, child);
462 if (child->children != NULL) {
463 #ifdef XSLT_REFACTORED
464 xsltParseSequenceConstructor(XSLT_CCTXT(style),
465 child->children);
466 #else
467 xsltParseTemplateContent(style, child);
468 #endif
470 if (child->psvi == NULL) {
471 xsltTransformError(NULL, style, child,
472 "xsl:attribute-set : internal error, attribute %s not "
473 "compiled\n", child->name);
475 else {
476 set->attrs = xsltAddAttrElemList(set->attrs, child);
480 child = child->next;
484 * Process attribute "use-attribute-sets".
486 value = xmlGetNsProp(cur, BAD_CAST "use-attribute-sets", NULL);
487 if (value != NULL) {
488 const xmlChar *curval, *endval;
489 curval = value;
490 while (*curval != 0) {
491 while (IS_BLANK(*curval)) curval++;
492 if (*curval == 0)
493 break;
494 endval = curval;
495 while ((*endval != 0) && (!IS_BLANK(*endval))) endval++;
496 curval = xmlDictLookup(style->dict, curval, endval - curval);
497 if (curval) {
498 const xmlChar *ncname2 = NULL;
499 const xmlChar *prefix2 = NULL;
500 const xmlChar *nsUri2 = NULL;
502 #ifdef WITH_XSLT_DEBUG_ATTRIBUTES
503 xsltGenericDebug(xsltGenericDebugContext,
504 "xsl:attribute-set : %s adds use %s\n", ncname, curval);
505 #endif
507 if (xmlValidateQName(curval, 0)) {
508 xsltTransformError(NULL, style, cur,
509 "xsl:attribute-set : The name '%s' in "
510 "use-attribute-sets is not a valid QName.\n", curval);
511 style->errors++;
512 xmlFree(value);
513 return;
516 ncname2 = xsltSplitQName(style->dict, curval, &prefix2);
517 if (prefix2 != NULL) {
518 xmlNsPtr ns2 = xmlSearchNs(style->doc, cur, prefix2);
519 if (ns2 == NULL) {
520 xsltTransformError(NULL, style, cur,
521 "xsl:attribute-set : No namespace found for QName "
522 "'%s:%s' in use-attribute-sets\n",
523 prefix2, ncname2);
524 style->errors++;
525 xmlFree(value);
526 return;
528 nsUri2 = ns2->href;
530 set->useAttrSets = xsltAddUseAttrSetList(set->useAttrSets,
531 ncname2, nsUri2);
533 curval = endval;
535 xmlFree(value);
536 value = NULL;
539 #ifdef WITH_XSLT_DEBUG_ATTRIBUTES
540 xsltGenericDebug(xsltGenericDebugContext,
541 "updated attribute list %s\n", ncname);
542 #endif
546 * xsltResolveUseAttrSets:
547 * @set: the attribute set
548 * @asctx: the context for attribute set resolution
549 * @depth: recursion depth
551 * Process "use-attribute-sets".
553 static void
554 xsltResolveUseAttrSets(xsltAttrSetPtr set, xsltStylesheetPtr topStyle,
555 int depth) {
556 xsltStylesheetPtr cur;
557 xsltAttrSetPtr other;
558 xsltUseAttrSetPtr use = set->useAttrSets;
559 xsltUseAttrSetPtr next;
561 while (use != NULL) {
563 * Iterate top stylesheet and all imports.
565 cur = topStyle;
566 while (cur != NULL) {
567 if (cur->attributeSets) {
568 other = xmlHashLookup2(cur->attributeSets, use->ncname,
569 use->ns);
570 if (other != NULL) {
571 xsltResolveAttrSet(other, topStyle, cur, use->ncname,
572 use->ns, depth + 1);
573 xsltMergeAttrSets(set, other);
574 break;
577 cur = xsltNextImport(cur);
580 next = use->next;
581 /* Free useAttrSets early. */
582 xsltFreeUseAttrSet(use);
583 use = next;
586 set->useAttrSets = NULL;
590 * xsltResolveAttrSet:
591 * @set: the attribute set
592 * @asctx: the context for attribute set resolution
593 * @name: the local name of the attirbute set
594 * @ns: the namespace of the attribute set
595 * @depth: recursion depth
597 * resolve the references in an attribute set.
599 static void
600 xsltResolveAttrSet(xsltAttrSetPtr set, xsltStylesheetPtr topStyle,
601 xsltStylesheetPtr style, const xmlChar *name,
602 const xmlChar *ns, int depth) {
603 xsltStylesheetPtr cur;
604 xsltAttrSetPtr other;
606 if (set->state == ATTRSET_RESOLVED)
607 return;
608 if (set->state == ATTRSET_RESOLVING) {
609 xsltTransformError(NULL, topStyle, NULL,
610 "xsl:attribute-set : use-attribute-sets recursion detected"
611 " on %s\n", name);
612 topStyle->errors++;
613 set->state = ATTRSET_RESOLVED;
614 return;
616 if (depth > 100) {
617 xsltTransformError(NULL, topStyle, NULL,
618 "xsl:attribute-set : use-attribute-sets maximum recursion "
619 "depth exceeded on %s\n", name);
620 topStyle->errors++;
621 return;
624 set->state = ATTRSET_RESOLVING;
626 xsltResolveUseAttrSets(set, topStyle, depth);
628 /* Merge imported sets. */
629 cur = xsltNextImport(style);
630 while (cur != NULL) {
631 if (cur->attributeSets != NULL) {
632 other = xmlHashLookup2(cur->attributeSets, name, ns);
634 if (other != NULL) {
635 #ifdef WITH_XSLT_DEBUG_ATTRIBUTES
636 xsltGenericDebug(xsltGenericDebugContext,
637 "xsl:attribute-set : merging import for %s\n", name);
638 #endif
639 xsltResolveUseAttrSets(other, topStyle, depth);
640 xsltMergeAttrSets(set, other);
641 xmlHashRemoveEntry2(cur->attributeSets, name, ns, NULL);
642 xsltFreeAttrSet(other);
646 cur = xsltNextImport(cur);
649 set->state = ATTRSET_RESOLVED;
653 * xsltResolveSASCallback:
654 * @set: the attribute set
655 * @asctx: the context for attribute set resolution
656 * @name: the local name of the attirbute set
657 * @ns: the namespace of the attribute set
659 * resolve the references in an attribute set.
661 static void
662 xsltResolveSASCallback(void *payload, void *data,
663 const xmlChar *name, const xmlChar *ns,
664 ATTRIBUTE_UNUSED const xmlChar *ignored) {
665 xsltAttrSetPtr set = (xsltAttrSetPtr) payload;
666 xsltAttrSetContextPtr asctx = (xsltAttrSetContextPtr) data;
667 xsltStylesheetPtr topStyle = asctx->topStyle;
668 xsltStylesheetPtr style = asctx->style;
670 if (asctx->error) {
671 if (style != topStyle)
672 xsltFreeAttrSet(set);
673 return;
676 xsltResolveAttrSet(set, topStyle, style, name, ns, 1);
678 /* Move attribute sets to top stylesheet. */
679 if (style != topStyle) {
681 * This imported stylesheet won't be visited anymore. Don't bother
682 * removing the hash entry.
684 if (xmlHashAddEntry2(topStyle->attributeSets, name, ns, set) < 0) {
685 xsltGenericError(xsltGenericErrorContext,
686 "xsl:attribute-set : internal error, can't move imported "
687 " attribute set %s\n", name);
688 asctx->error = 1;
689 xsltFreeAttrSet(set);
695 * xsltResolveStylesheetAttributeSet:
696 * @style: the XSLT stylesheet
698 * resolve the references between attribute sets.
700 void
701 xsltResolveStylesheetAttributeSet(xsltStylesheetPtr style) {
702 xsltStylesheetPtr cur;
703 xsltAttrSetContext asctx;
705 #ifdef WITH_XSLT_DEBUG_ATTRIBUTES
706 xsltGenericDebug(xsltGenericDebugContext,
707 "Resolving attribute sets references\n");
708 #endif
709 asctx.topStyle = style;
710 asctx.error = 0;
711 cur = style;
712 while (cur != NULL) {
713 if (cur->attributeSets != NULL) {
714 if (style->attributeSets == NULL) {
715 #ifdef WITH_XSLT_DEBUG_ATTRIBUTES
716 xsltGenericDebug(xsltGenericDebugContext,
717 "creating attribute set table\n");
718 #endif
719 style->attributeSets = xmlHashCreate(10);
721 asctx.style = cur;
722 xmlHashScanFull(cur->attributeSets, xsltResolveSASCallback,
723 &asctx);
725 if (cur != style) {
727 * the attribute lists have either been migrated to style
728 * or freed directly in xsltResolveSASCallback()
730 xmlHashFree(cur->attributeSets, NULL);
731 cur->attributeSets = NULL;
734 cur = xsltNextImport(cur);
739 * xsltAttribute:
740 * @ctxt: a XSLT process context
741 * @contextNode: the current node in the source tree
742 * @inst: the xsl:attribute element
743 * @castedComp: precomputed information
745 * Process the xslt attribute node on the source node
747 void
748 xsltAttribute(xsltTransformContextPtr ctxt,
749 xmlNodePtr contextNode,
750 xmlNodePtr inst,
751 xsltElemPreCompPtr castedComp)
753 #ifdef XSLT_REFACTORED
754 xsltStyleItemAttributePtr comp =
755 (xsltStyleItemAttributePtr) castedComp;
756 #else
757 xsltStylePreCompPtr comp = (xsltStylePreCompPtr) castedComp;
758 #endif
759 xmlNodePtr targetElem;
760 xmlChar *prop = NULL;
761 const xmlChar *name = NULL, *prefix = NULL, *nsName = NULL;
762 xmlChar *value = NULL;
763 xmlNsPtr ns = NULL;
764 xmlAttrPtr attr;
766 if ((ctxt == NULL) || (contextNode == NULL) || (inst == NULL) ||
767 (inst->type != XML_ELEMENT_NODE) )
768 return;
771 * A comp->has_name == 0 indicates that we need to skip this instruction,
772 * since it was evaluated to be invalid already during compilation.
774 if (!comp->has_name)
775 return;
777 * BIG NOTE: This previously used xsltGetSpecialNamespace() and
778 * xsltGetNamespace(), but since both are not appropriate, we
779 * will process namespace lookup here to avoid adding yet another
780 * ns-lookup function to namespaces.c.
783 * SPEC XSLT 1.0: Error cases:
784 * - Creating nodes other than text nodes during the instantiation of
785 * the content of the xsl:attribute element; implementations may
786 * either signal the error or ignore the offending nodes."
789 if (comp == NULL) {
790 xsltTransformError(ctxt, NULL, inst,
791 "Internal error in xsltAttribute(): "
792 "The XSLT 'attribute' instruction was not compiled.\n");
793 return;
796 * TODO: Shouldn't ctxt->insert == NULL be treated as an internal error?
797 * So report an internal error?
799 if (ctxt->insert == NULL)
800 return;
802 * SPEC XSLT 1.0:
803 * "Adding an attribute to a node that is not an element;
804 * implementations may either signal the error or ignore the attribute."
806 * TODO: I think we should signal such errors in the future, and maybe
807 * provide an option to ignore such errors.
809 targetElem = ctxt->insert;
810 if (targetElem->type != XML_ELEMENT_NODE)
811 return;
814 * SPEC XSLT 1.0:
815 * "Adding an attribute to an element after children have been added
816 * to it; implementations may either signal the error or ignore the
817 * attribute."
819 * TODO: We should decide whether not to report such errors or
820 * to ignore them; note that we *ignore* if the parent is not an
821 * element, but here we report an error.
823 if (targetElem->children != NULL) {
825 * NOTE: Ah! This seems to be intended to support streamed
826 * result generation!.
828 xsltTransformError(ctxt, NULL, inst,
829 "xsl:attribute: Cannot add attributes to an "
830 "element if children have been already added "
831 "to the element.\n");
832 return;
836 * Process the name
837 * ----------------
840 #ifdef WITH_DEBUGGER
841 if (ctxt->debugStatus != XSLT_DEBUG_NONE)
842 xslHandleDebugger(inst, contextNode, NULL, ctxt);
843 #endif
845 if (comp->name == NULL) {
846 /* TODO: fix attr acquisition wrt to the XSLT namespace */
847 prop = xsltEvalAttrValueTemplate(ctxt, inst,
848 (const xmlChar *) "name", XSLT_NAMESPACE);
849 if (prop == NULL) {
850 xsltTransformError(ctxt, NULL, inst,
851 "xsl:attribute: The attribute 'name' is missing.\n");
852 goto error;
854 if (xmlValidateQName(prop, 0)) {
855 xsltTransformError(ctxt, NULL, inst,
856 "xsl:attribute: The effective name '%s' is not a "
857 "valid QName.\n", prop);
858 /* we fall through to catch any further errors, if possible */
862 * Reject a name of "xmlns".
864 if (xmlStrEqual(prop, BAD_CAST "xmlns")) {
865 xsltTransformError(ctxt, NULL, inst,
866 "xsl:attribute: The effective name 'xmlns' is not allowed.\n");
867 xmlFree(prop);
868 goto error;
871 name = xsltSplitQName(ctxt->dict, prop, &prefix);
872 xmlFree(prop);
873 } else {
875 * The "name" value was static.
877 #ifdef XSLT_REFACTORED
878 prefix = comp->nsPrefix;
879 name = comp->name;
880 #else
881 name = xsltSplitQName(ctxt->dict, comp->name, &prefix);
882 #endif
886 * Process namespace semantics
887 * ---------------------------
889 * Evaluate the namespace name.
891 if (comp->has_ns) {
893 * The "namespace" attribute was existent.
895 if (comp->ns != NULL) {
897 * No AVT; just plain text for the namespace name.
899 if (comp->ns[0] != 0)
900 nsName = comp->ns;
901 } else {
902 xmlChar *tmpNsName;
904 * Eval the AVT.
906 /* TODO: check attr acquisition wrt to the XSLT namespace */
907 tmpNsName = xsltEvalAttrValueTemplate(ctxt, inst,
908 (const xmlChar *) "namespace", XSLT_NAMESPACE);
910 * This fixes bug #302020: The AVT might also evaluate to the
911 * empty string; this means that the empty string also indicates
912 * "no namespace".
913 * SPEC XSLT 1.0:
914 * "If the string is empty, then the expanded-name of the
915 * attribute has a null namespace URI."
917 if ((tmpNsName != NULL) && (tmpNsName[0] != 0))
918 nsName = xmlDictLookup(ctxt->dict, BAD_CAST tmpNsName, -1);
919 xmlFree(tmpNsName);
922 if (xmlStrEqual(nsName, BAD_CAST "http://www.w3.org/2000/xmlns/")) {
923 xsltTransformError(ctxt, NULL, inst,
924 "xsl:attribute: Namespace http://www.w3.org/2000/xmlns/ "
925 "forbidden.\n");
926 goto error;
928 if (xmlStrEqual(nsName, XML_XML_NAMESPACE)) {
929 prefix = BAD_CAST "xml";
930 } else if (xmlStrEqual(prefix, BAD_CAST "xml")) {
931 prefix = NULL;
933 } else if (prefix != NULL) {
935 * SPEC XSLT 1.0:
936 * "If the namespace attribute is not present, then the QName is
937 * expanded into an expanded-name using the namespace declarations
938 * in effect for the xsl:attribute element, *not* including any
939 * default namespace declaration."
941 ns = xmlSearchNs(inst->doc, inst, prefix);
942 if (ns == NULL) {
944 * Note that this is treated as an error now (checked with
945 * Saxon, Xalan-J and MSXML).
947 xsltTransformError(ctxt, NULL, inst,
948 "xsl:attribute: The QName '%s:%s' has no "
949 "namespace binding in scope in the stylesheet; "
950 "this is an error, since the namespace was not "
951 "specified by the instruction itself.\n", prefix, name);
952 } else
953 nsName = ns->href;
957 * Find/create a matching ns-decl in the result tree.
959 ns = NULL;
961 #if 0
962 if (0) {
964 * OPTIMIZE TODO: How do we know if we are adding to a
965 * fragment or to the result tree?
967 * If we are adding to a result tree fragment (i.e., not to the
968 * actual result tree), we'll don't bother searching for the
969 * ns-decl, but just store it in the dummy-doc of the result
970 * tree fragment.
972 if (nsName != NULL) {
974 * TODO: Get the doc of @targetElem.
976 ns = xsltTreeAcquireStoredNs(some doc, nsName, prefix);
979 #endif
981 if (nsName != NULL) {
983 * Something about ns-prefixes:
984 * SPEC XSLT 1.0:
985 * "XSLT processors may make use of the prefix of the QName specified
986 * in the name attribute when selecting the prefix used for outputting
987 * the created attribute as XML; however, they are not required to do
988 * so and, if the prefix is xmlns, they must not do so"
991 * xsl:attribute can produce a scenario where the prefix is NULL,
992 * so generate a prefix.
994 if ((prefix == NULL) || xmlStrEqual(prefix, BAD_CAST "xmlns")) {
995 xmlChar *pref = xmlStrdup(BAD_CAST "ns_1");
997 ns = xsltGetSpecialNamespace(ctxt, inst, nsName, pref, targetElem);
999 xmlFree(pref);
1000 } else {
1001 ns = xsltGetSpecialNamespace(ctxt, inst, nsName, prefix,
1002 targetElem);
1004 if (ns == NULL) {
1005 xsltTransformError(ctxt, NULL, inst,
1006 "Namespace fixup error: Failed to acquire an in-scope "
1007 "namespace binding for the generated attribute '{%s}%s'.\n",
1008 nsName, name);
1009 goto error;
1013 * Construction of the value
1014 * -------------------------
1016 if (inst->children == NULL) {
1018 * No content.
1019 * TODO: Do we need to put the empty string in ?
1021 attr = xmlSetNsProp(ctxt->insert, ns, name, (const xmlChar *) "");
1022 } else if ((inst->children->next == NULL) &&
1023 ((inst->children->type == XML_TEXT_NODE) ||
1024 (inst->children->type == XML_CDATA_SECTION_NODE)))
1026 xmlNodePtr copyTxt;
1029 * xmlSetNsProp() will take care of duplicates.
1031 attr = xmlSetNsProp(ctxt->insert, ns, name, NULL);
1032 if (attr == NULL) /* TODO: report error ? */
1033 goto error;
1035 * This was taken over from xsltCopyText() (transform.c).
1037 if (ctxt->internalized &&
1038 (ctxt->insert->doc != NULL) &&
1039 (ctxt->insert->doc->dict == ctxt->dict))
1041 copyTxt = xmlNewText(NULL);
1042 if (copyTxt == NULL) /* TODO: report error */
1043 goto error;
1045 * This is a safe scenario where we don't need to lookup
1046 * the dict.
1048 copyTxt->content = inst->children->content;
1050 * Copy "disable-output-escaping" information.
1051 * TODO: Does this have any effect for attribute values
1052 * anyway?
1054 if (inst->children->name == xmlStringTextNoenc)
1055 copyTxt->name = xmlStringTextNoenc;
1056 } else {
1058 * Copy the value.
1060 copyTxt = xmlNewText(inst->children->content);
1061 if (copyTxt == NULL) /* TODO: report error */
1062 goto error;
1064 attr->children = attr->last = copyTxt;
1065 copyTxt->parent = (xmlNodePtr) attr;
1066 copyTxt->doc = attr->doc;
1068 * Copy "disable-output-escaping" information.
1069 * TODO: Does this have any effect for attribute values
1070 * anyway?
1072 if (inst->children->name == xmlStringTextNoenc)
1073 copyTxt->name = xmlStringTextNoenc;
1076 * since we create the attribute without content IDness must be
1077 * asserted as a second step
1079 if ((copyTxt->content != NULL) &&
1080 (xmlIsID(attr->doc, attr->parent, attr)))
1081 xmlAddID(NULL, attr->doc, copyTxt->content, attr);
1082 } else {
1084 * The sequence constructor might be complex, so instantiate it.
1086 value = xsltEvalTemplateString(ctxt, contextNode, inst);
1087 if (value != NULL) {
1088 attr = xmlSetNsProp(ctxt->insert, ns, name, value);
1089 xmlFree(value);
1090 } else {
1092 * TODO: Do we have to add the empty string to the attr?
1093 * TODO: Does a value of NULL indicate an
1094 * error in xsltEvalTemplateString() ?
1096 attr = xmlSetNsProp(ctxt->insert, ns, name,
1097 (const xmlChar *) "");
1101 error:
1102 return;
1106 * xsltApplyAttributeSet:
1107 * @ctxt: the XSLT stylesheet
1108 * @node: the node in the source tree.
1109 * @inst: the attribute node "xsl:use-attribute-sets"
1110 * @attrSets: the list of QNames of the attribute-sets to be applied
1112 * Apply the xsl:use-attribute-sets.
1113 * If @attrSets is NULL, then @inst will be used to exctract this
1114 * value.
1115 * If both, @attrSets and @inst, are NULL, then this will do nothing.
1117 void
1118 xsltApplyAttributeSet(xsltTransformContextPtr ctxt, xmlNodePtr node,
1119 xmlNodePtr inst,
1120 const xmlChar *attrSets)
1122 const xmlChar *ncname = NULL;
1123 const xmlChar *prefix = NULL;
1124 const xmlChar *curstr, *endstr;
1125 xsltAttrSetPtr set;
1126 xsltStylesheetPtr style;
1128 if (attrSets == NULL) {
1129 if (inst == NULL)
1130 return;
1131 else {
1133 * Extract the value from @inst.
1135 if (inst->type == XML_ATTRIBUTE_NODE) {
1136 if ( ((xmlAttrPtr) inst)->children != NULL)
1137 attrSets = ((xmlAttrPtr) inst)->children->content;
1140 if (attrSets == NULL) {
1142 * TODO: Return an error?
1144 return;
1149 * Parse/apply the list of QNames.
1151 curstr = attrSets;
1152 while (*curstr != 0) {
1153 while (IS_BLANK(*curstr))
1154 curstr++;
1155 if (*curstr == 0)
1156 break;
1157 endstr = curstr;
1158 while ((*endstr != 0) && (!IS_BLANK(*endstr)))
1159 endstr++;
1160 curstr = xmlDictLookup(ctxt->dict, curstr, endstr - curstr);
1161 if (curstr) {
1162 xmlNsPtr ns;
1163 const xmlChar *nsUri = NULL;
1165 #ifdef WITH_XSLT_DEBUG_ATTRIBUTES
1166 xsltGenericDebug(xsltGenericDebugContext,
1167 "apply attribute set %s\n", curstr);
1168 #endif
1170 if (xmlValidateQName(curstr, 0)) {
1171 xsltTransformError(ctxt, NULL, inst,
1172 "The name '%s' in use-attribute-sets is not a valid "
1173 "QName.\n", curstr);
1174 return;
1177 ncname = xsltSplitQName(ctxt->dict, curstr, &prefix);
1178 if (prefix != NULL) {
1179 ns = xmlSearchNs(inst->doc, inst, prefix);
1180 if (ns == NULL) {
1181 xsltTransformError(ctxt, NULL, inst,
1182 "use-attribute-set : No namespace found for QName "
1183 "'%s:%s'\n", prefix, ncname);
1184 return;
1186 nsUri = ns->href;
1189 style = ctxt->style;
1191 #ifdef WITH_DEBUGGER
1192 if ((style != NULL) &&
1193 (style->attributeSets != NULL) &&
1194 (ctxt->debugStatus != XSLT_DEBUG_NONE))
1196 set = xmlHashLookup2(style->attributeSets, ncname, nsUri);
1197 if ((set != NULL) && (set->attrs != NULL) &&
1198 (set->attrs->attr != NULL))
1199 xslHandleDebugger(set->attrs->attr->parent, node, NULL,
1200 ctxt);
1202 #endif
1204 * Lookup the referenced attribute-set. All attribute sets were
1205 * moved to the top stylesheet so there's no need to iterate
1206 * imported stylesheets
1208 set = xmlHashLookup2(style->attributeSets, ncname, nsUri);
1209 if (set != NULL) {
1210 xsltAttrElemPtr cur = set->attrs;
1211 while (cur != NULL) {
1212 if (cur->attr != NULL) {
1213 xsltAttribute(ctxt, node, cur->attr,
1214 cur->attr->psvi);
1216 cur = cur->next;
1220 curstr = endstr;
1224 static void
1225 xsltFreeAttributeSetsEntry(void *payload,
1226 const xmlChar *name ATTRIBUTE_UNUSED) {
1227 xsltFreeAttrSet((xsltAttrSetPtr) payload);
1231 * xsltFreeAttributeSetsHashes:
1232 * @style: an XSLT stylesheet
1234 * Free up the memory used by attribute sets
1236 void
1237 xsltFreeAttributeSetsHashes(xsltStylesheetPtr style) {
1238 if (style->attributeSets != NULL)
1239 xmlHashFree((xmlHashTablePtr) style->attributeSets,
1240 xsltFreeAttributeSetsEntry);
1241 style->attributeSets = NULL;