2 * attributes.c: Implementation of the XSLT attributes handling
5 * http://www.w3.org/TR/1999/REC-xslt-19991116
7 * See Copyright for the status of this software.
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>
24 #include "xsltInternals.h"
25 #include "xsltutils.h"
26 #include "attributes.h"
27 #include "namespaces.h"
28 #include "templates.h"
30 #include "transform.h"
33 #define WITH_XSLT_DEBUG_ATTRIBUTES
34 #ifdef WITH_XSLT_DEBUG
35 #define WITH_XSLT_DEBUG_ATTRIBUTES
45 #define IS_BLANK(c) (((c) == 0x20) || ((c) == 0x09) || ((c) == 0xA) || \
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
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
;
77 typedef struct _xsltAttrSet xsltAttrSet
;
78 typedef xsltAttrSet
*xsltAttrSetPtr
;
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
;
94 xsltResolveAttrSet(xsltAttrSetPtr set
, xsltStylesheetPtr topStyle
,
95 xsltStylesheetPtr style
, const xmlChar
*name
,
96 const xmlChar
*ns
, int depth
);
98 /************************************************************************
100 * XSLT Attribute handling *
102 ************************************************************************/
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
) {
116 cur
= (xsltAttrElemPtr
) xmlMalloc(sizeof(xsltAttrElem
));
118 xsltGenericError(xsltGenericErrorContext
,
119 "xsltNewAttrElem : malloc failed\n");
122 memset(cur
, 0, sizeof(xsltAttrElem
));
129 * @attr: an XSLT AttrElem
131 * Free up the memory allocated by @attr
134 xsltFreeAttrElem(xsltAttrElemPtr attr
) {
139 * xsltFreeAttrElemList:
140 * @list: an XSLT AttrElem list
142 * Free up the memory allocated by @list
145 xsltFreeAttrElemList(xsltAttrElemPtr list
) {
146 xsltAttrElemPtr next
;
148 while (list
!= NULL
) {
150 xsltFreeAttrElem(list
);
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
;
171 return(xsltNewAttrElem(attr
));
173 while (cur
!= NULL
) {
176 cur
->next
= xsltNewAttrElem(attr
);
186 * @ncname: local name
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
));
199 xsltGenericError(xsltGenericErrorContext
,
200 "xsltNewUseAttrSet : malloc failed\n");
203 memset(cur
, 0, sizeof(xsltUseAttrSet
));
204 cur
->ncname
= ncname
;
210 * xsltFreeUseAttrSet:
211 * @use: an XSLT UseAttrSet
213 * Free up the memory allocated by @use
216 xsltFreeUseAttrSet(xsltUseAttrSetPtr use
) {
221 * xsltFreeUseAttrSetList:
222 * @list: an XSLT UseAttrSet list
224 * Free up the memory allocated by @list
227 xsltFreeUseAttrSetList(xsltUseAttrSetPtr list
) {
228 xsltUseAttrSetPtr next
;
230 while (list
!= NULL
) {
232 xsltFreeUseAttrSet(list
);
238 * xsltAddUseAttrSetList:
239 * @list: a xsltUseAttrSet list
240 * @ncname: local name
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
,
250 xsltUseAttrSetPtr next
, cur
;
255 return(xsltNewUseAttrSet(ncname
, ns
));
257 while (cur
!= NULL
) {
258 if ((cur
->ncname
== ncname
) && (cur
->ns
== ns
))
262 cur
->next
= xsltNewUseAttrSet(ncname
, ns
);
273 * Create a new attribute set.
275 * Returns the newly allocated xsltAttrSetPtr or NULL in case of error.
277 static xsltAttrSetPtr
278 xsltNewAttrSet(void) {
281 cur
= (xsltAttrSetPtr
) xmlMalloc(sizeof(xsltAttrSet
));
283 xsltGenericError(xsltGenericErrorContext
,
284 "xsltNewAttrSet : malloc failed\n");
287 memset(cur
, 0, sizeof(xsltAttrSet
));
293 * @set: an attribute set
295 * Free memory allocated by @set
298 xsltFreeAttrSet(xsltAttrSetPtr set
) {
302 xsltFreeAttrElemList(set
->attrs
);
303 xsltFreeUseAttrSetList(set
->useAttrSets
);
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.
316 xsltMergeAttrSets(xsltAttrSetPtr set
, xsltAttrSetPtr other
) {
318 xsltAttrElemPtr old
= other
->attrs
;
321 while (old
!= NULL
) {
323 * Check that the attribute is not yet in the list
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
)) {
336 if (cur
->next
== NULL
)
343 set
->attrs
= xsltNewAttrElem(old
->attr
);
345 cur
->next
= xsltNewAttrElem(old
->attr
);
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
368 xsltParseStylesheetAttributeSet(xsltStylesheetPtr style
, xmlNodePtr cur
) {
369 const xmlChar
*ncname
;
370 const xmlChar
*prefix
;
371 const xmlChar
*nsUri
= NULL
;
376 if ((cur
== NULL
) || (style
== NULL
) || (cur
->type
!= XML_ELEMENT_NODE
))
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");
388 if (xmlValidateQName(value
, 0)) {
389 xsltTransformError(NULL
, style
, cur
,
390 "xsl:attribute-set : The name '%s' is not a valid QName.\n",
397 ncname
= xsltSplitQName(style
->dict
, value
, &prefix
);
400 if (prefix
!= NULL
) {
401 xmlNsPtr ns
= xmlSearchNs(style
->doc
, cur
, prefix
);
403 xsltTransformError(NULL
, style
, cur
,
404 "xsl:attribute-set : No namespace found for QName '%s:%s'\n",
412 if (style
->attributeSets
== NULL
) {
413 #ifdef WITH_XSLT_DEBUG_ATTRIBUTES
414 xsltGenericDebug(xsltGenericDebugContext
,
415 "creating attribute set table\n");
417 style
->attributeSets
= xmlHashCreate(10);
419 if (style
->attributeSets
== NULL
)
422 set
= xmlHashLookup2(style
->attributeSets
, ncname
, nsUri
);
424 set
= xsltNewAttrSet();
426 (xmlHashAddEntry2(style
->attributeSets
, ncname
, nsUri
, set
) < 0)) {
427 xsltGenericError(xsltGenericErrorContext
, "memory error\n");
428 xsltFreeAttrSet(set
);
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",
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",
457 #ifdef WITH_XSLT_DEBUG_ATTRIBUTES
458 xsltGenericDebug(xsltGenericDebugContext
,
459 "add attribute to list %s\n", ncname
);
461 xsltStylePreCompute(style
, child
);
462 if (child
->children
!= NULL
) {
463 #ifdef XSLT_REFACTORED
464 xsltParseSequenceConstructor(XSLT_CCTXT(style
),
467 xsltParseTemplateContent(style
, child
);
470 if (child
->psvi
== NULL
) {
471 xsltTransformError(NULL
, style
, child
,
472 "xsl:attribute-set : internal error, attribute %s not "
473 "compiled\n", child
->name
);
476 set
->attrs
= xsltAddAttrElemList(set
->attrs
, child
);
484 * Process attribute "use-attribute-sets".
486 value
= xmlGetNsProp(cur
, BAD_CAST
"use-attribute-sets", NULL
);
488 const xmlChar
*curval
, *endval
;
490 while (*curval
!= 0) {
491 while (IS_BLANK(*curval
)) curval
++;
495 while ((*endval
!= 0) && (!IS_BLANK(*endval
))) endval
++;
496 curval
= xmlDictLookup(style
->dict
, curval
, endval
- 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
);
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
);
516 ncname2
= xsltSplitQName(style
->dict
, curval
, &prefix2
);
517 if (prefix2
!= NULL
) {
518 xmlNsPtr ns2
= xmlSearchNs(style
->doc
, cur
, prefix2
);
520 xsltTransformError(NULL
, style
, cur
,
521 "xsl:attribute-set : No namespace found for QName "
522 "'%s:%s' in use-attribute-sets\n",
530 set
->useAttrSets
= xsltAddUseAttrSetList(set
->useAttrSets
,
539 #ifdef WITH_XSLT_DEBUG_ATTRIBUTES
540 xsltGenericDebug(xsltGenericDebugContext
,
541 "updated attribute list %s\n", ncname
);
546 * xsltResolveUseAttrSets:
547 * @set: the attribute set
548 * @asctx: the context for attribute set resolution
549 * @depth: recursion depth
551 * Process "use-attribute-sets".
554 xsltResolveUseAttrSets(xsltAttrSetPtr set
, xsltStylesheetPtr topStyle
,
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.
566 while (cur
!= NULL
) {
567 if (cur
->attributeSets
) {
568 other
= xmlHashLookup2(cur
->attributeSets
, use
->ncname
,
571 xsltResolveAttrSet(other
, topStyle
, cur
, use
->ncname
,
573 xsltMergeAttrSets(set
, other
);
577 cur
= xsltNextImport(cur
);
581 /* Free useAttrSets early. */
582 xsltFreeUseAttrSet(use
);
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.
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
)
608 if (set
->state
== ATTRSET_RESOLVING
) {
609 xsltTransformError(NULL
, topStyle
, NULL
,
610 "xsl:attribute-set : use-attribute-sets recursion detected"
613 set
->state
= ATTRSET_RESOLVED
;
617 xsltTransformError(NULL
, topStyle
, NULL
,
618 "xsl:attribute-set : use-attribute-sets maximum recursion "
619 "depth exceeded on %s\n", name
);
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
);
635 #ifdef WITH_XSLT_DEBUG_ATTRIBUTES
636 xsltGenericDebug(xsltGenericDebugContext
,
637 "xsl:attribute-set : merging import for %s\n", name
);
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.
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
;
671 if (style
!= topStyle
)
672 xsltFreeAttrSet(set
);
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
);
689 xsltFreeAttrSet(set
);
695 * xsltResolveStylesheetAttributeSet:
696 * @style: the XSLT stylesheet
698 * resolve the references between attribute sets.
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");
709 asctx
.topStyle
= 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");
719 style
->attributeSets
= xmlHashCreate(10);
722 xmlHashScanFull(cur
->attributeSets
, xsltResolveSASCallback
,
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
);
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
748 xsltAttribute(xsltTransformContextPtr ctxt
,
749 xmlNodePtr contextNode
,
751 xsltElemPreCompPtr castedComp
)
753 #ifdef XSLT_REFACTORED
754 xsltStyleItemAttributePtr comp
=
755 (xsltStyleItemAttributePtr
) castedComp
;
757 xsltStylePreCompPtr comp
= (xsltStylePreCompPtr
) castedComp
;
759 xmlNodePtr targetElem
;
760 xmlChar
*prop
= NULL
;
761 const xmlChar
*name
= NULL
, *prefix
= NULL
, *nsName
= NULL
;
762 xmlChar
*value
= NULL
;
766 if ((ctxt
== NULL
) || (contextNode
== NULL
) || (inst
== NULL
) ||
767 (inst
->type
!= XML_ELEMENT_NODE
) )
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.
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."
790 xsltTransformError(ctxt
, NULL
, inst
,
791 "Internal error in xsltAttribute(): "
792 "The XSLT 'attribute' instruction was not compiled.\n");
796 * TODO: Shouldn't ctxt->insert == NULL be treated as an internal error?
797 * So report an internal error?
799 if (ctxt
->insert
== NULL
)
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
)
815 * "Adding an attribute to an element after children have been added
816 * to it; implementations may either signal the error or ignore the
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");
841 if (ctxt
->debugStatus
!= XSLT_DEBUG_NONE
)
842 xslHandleDebugger(inst
, contextNode
, NULL
, ctxt
);
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
);
850 xsltTransformError(ctxt
, NULL
, inst
,
851 "xsl:attribute: The attribute 'name' is missing.\n");
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");
871 name
= xsltSplitQName(ctxt
->dict
, prop
, &prefix
);
875 * The "name" value was static.
877 #ifdef XSLT_REFACTORED
878 prefix
= comp
->nsPrefix
;
881 name
= xsltSplitQName(ctxt
->dict
, comp
->name
, &prefix
);
886 * Process namespace semantics
887 * ---------------------------
889 * Evaluate the namespace name.
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)
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
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);
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/ "
928 if (xmlStrEqual(nsName
, XML_XML_NAMESPACE
)) {
929 prefix
= BAD_CAST
"xml";
930 } else if (xmlStrEqual(prefix
, BAD_CAST
"xml")) {
933 } else if (prefix
!= NULL
) {
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
);
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
);
957 * Find/create a matching ns-decl in the result tree.
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
972 if (nsName
!= NULL
) {
974 * TODO: Get the doc of @targetElem.
976 ns
= xsltTreeAcquireStoredNs(some doc
, nsName
, prefix
);
981 if (nsName
!= NULL
) {
983 * Something about ns-prefixes:
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
);
1001 ns
= xsltGetSpecialNamespace(ctxt
, inst
, nsName
, prefix
,
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",
1013 * Construction of the value
1014 * -------------------------
1016 if (inst
->children
== NULL
) {
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
)))
1029 * xmlSetNsProp() will take care of duplicates.
1031 attr
= xmlSetNsProp(ctxt
->insert
, ns
, name
, NULL
);
1032 if (attr
== NULL
) /* TODO: report 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 */
1045 * This is a safe scenario where we don't need to lookup
1048 copyTxt
->content
= inst
->children
->content
;
1050 * Copy "disable-output-escaping" information.
1051 * TODO: Does this have any effect for attribute values
1054 if (inst
->children
->name
== xmlStringTextNoenc
)
1055 copyTxt
->name
= xmlStringTextNoenc
;
1060 copyTxt
= xmlNewText(inst
->children
->content
);
1061 if (copyTxt
== NULL
) /* TODO: report 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
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
);
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
);
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
*) "");
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
1115 * If both, @attrSets and @inst, are NULL, then this will do nothing.
1118 xsltApplyAttributeSet(xsltTransformContextPtr ctxt
, xmlNodePtr node
,
1120 const xmlChar
*attrSets
)
1122 const xmlChar
*ncname
= NULL
;
1123 const xmlChar
*prefix
= NULL
;
1124 const xmlChar
*curstr
, *endstr
;
1126 xsltStylesheetPtr style
;
1128 if (attrSets
== NULL
) {
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?
1149 * Parse/apply the list of QNames.
1152 while (*curstr
!= 0) {
1153 while (IS_BLANK(*curstr
))
1158 while ((*endstr
!= 0) && (!IS_BLANK(*endstr
)))
1160 curstr
= xmlDictLookup(ctxt
->dict
, curstr
, endstr
- curstr
);
1163 const xmlChar
*nsUri
= NULL
;
1165 #ifdef WITH_XSLT_DEBUG_ATTRIBUTES
1166 xsltGenericDebug(xsltGenericDebugContext
,
1167 "apply attribute set %s\n", curstr
);
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
);
1177 ncname
= xsltSplitQName(ctxt
->dict
, curstr
, &prefix
);
1178 if (prefix
!= NULL
) {
1179 ns
= xmlSearchNs(inst
->doc
, inst
, prefix
);
1181 xsltTransformError(ctxt
, NULL
, inst
,
1182 "use-attribute-set : No namespace found for QName "
1183 "'%s:%s'\n", prefix
, ncname
);
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
,
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
);
1210 xsltAttrElemPtr cur
= set
->attrs
;
1211 while (cur
!= NULL
) {
1212 if (cur
->attr
!= NULL
) {
1213 xsltAttribute(ctxt
, node
, cur
->attr
,
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
1237 xsltFreeAttributeSetsHashes(xsltStylesheetPtr style
) {
1238 if (style
->attributeSets
!= NULL
)
1239 xmlHashFree((xmlHashTablePtr
) style
->attributeSets
,
1240 xsltFreeAttributeSetsEntry
);
1241 style
->attributeSets
= NULL
;