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 #ifdef HAVE_SYS_TYPES_H
18 #include <sys/types.h>
36 #include <libxml/xmlmemory.h>
37 #include <libxml/tree.h>
38 #include <libxml/hash.h>
39 #include <libxml/xmlerror.h>
40 #include <libxml/uri.h>
42 #include "xsltInternals.h"
43 #include "xsltutils.h"
44 #include "attributes.h"
45 #include "namespaces.h"
46 #include "templates.h"
48 #include "transform.h"
50 #define WITH_XSLT_DEBUG_ATTRIBUTES
51 #ifdef WITH_XSLT_DEBUG
52 #define WITH_XSLT_DEBUG_ATTRIBUTES
56 * TODO: merge attribute sets from different import precedence.
57 * all this should be precomputed just before the transformation
58 * starts or at first hit with a cache in the context.
59 * The simple way for now would be to not allow redefinition of
60 * attributes once generated in the output tree, possibly costlier.
67 #define IS_BLANK(c) (((c) == 0x20) || ((c) == 0x09) || ((c) == 0xA) || \
70 #define IS_BLANK_NODE(n) \
71 (((n)->type == XML_TEXT_NODE) && (xsltIsBlank((n)->content)))
75 * The in-memory structure corresponding to an XSLT Attribute in
80 typedef struct _xsltAttrElem xsltAttrElem;
81 typedef xsltAttrElem *xsltAttrElemPtr;
82 struct _xsltAttrElem {
83 struct _xsltAttrElem *next;/* chained list */
84 xmlNodePtr attr; /* the xsl:attribute definition */
85 const xmlChar *set; /* or the attribute set */
86 const xmlChar *ns; /* and its namespace */
89 /************************************************************************
91 * XSLT Attribute handling *
93 ************************************************************************/
97 * @attr: the new xsl:attribute node
99 * Create a new XSLT AttrElem
101 * Returns the newly allocated xsltAttrElemPtr or NULL in case of error
103 static xsltAttrElemPtr
104 xsltNewAttrElem(xmlNodePtr attr) {
107 cur = (xsltAttrElemPtr) xmlMalloc(sizeof(xsltAttrElem));
109 xsltGenericError(xsltGenericErrorContext,
110 "xsltNewAttrElem : malloc failed\n");
113 memset(cur, 0, sizeof(xsltAttrElem));
120 * @attr: an XSLT AttrElem
122 * Free up the memory allocated by @attr
125 xsltFreeAttrElem(xsltAttrElemPtr attr) {
126 if (attr->set != NULL)
127 xmlFree((char *)attr->set);
128 if (attr->ns != NULL)
129 xmlFree((char *)attr->ns);
134 * xsltFreeAttrElemList:
135 * @list: an XSLT AttrElem list
137 * Free up the memory allocated by @list
140 xsltFreeAttrElemList(xsltAttrElemPtr list) {
141 xsltAttrElemPtr next;
143 while (list != NULL) {
145 xsltFreeAttrElem(list);
151 * xsltAddAttrElemList:
152 * @list: an XSLT AttrElem list
153 * @attr: the new xsl:attribute node
155 * Add the new attribute to the list.
157 * Returns the new list pointer
159 static xsltAttrElemPtr
160 xsltAddAttrElemList(xsltAttrElemPtr list, xmlNodePtr attr) {
161 xsltAttrElemPtr next, cur;
166 return(xsltNewAttrElem(attr));
168 while (cur != NULL) {
170 if (cur->attr == attr)
172 if (cur->next == NULL) {
173 cur->next = xsltNewAttrElem(attr);
182 * xsltMergeAttrElemList:
183 * @list: an XSLT AttrElem list
184 * @old: another XSLT AttrElem list
186 * Add all the attributes from list @old to list @list,
187 * but drop redefinition of existing values.
189 * Returns the new list pointer
191 static xsltAttrElemPtr
192 xsltMergeAttrElemList(xsltAttrElemPtr list, xsltAttrElemPtr old) {
196 while (old != NULL) {
197 if ((old->attr == NULL) && (old->set == NULL)) {
202 * Check that the attribute is not yet in the list
206 while (cur != NULL) {
207 if ((cur->attr == NULL) && (cur->set == NULL)) {
208 if (cur->next == NULL)
213 if ((cur->set != NULL) && (cur->set == old->set)) {
217 if (cur->set != NULL) {
218 if (cur->next == NULL)
223 if (old->set != NULL) {
224 if (cur->next == NULL)
229 if (cur->attr == old->attr) {
230 xsltGenericError(xsltGenericErrorContext,
231 "xsl:attribute-set : use-attribute-sets recursion detected\n");
234 if (cur->next == NULL)
241 list = xsltNewAttrElem(old->attr);
242 if (old->set != NULL) {
243 list->set = xmlStrdup(old->set);
245 list->ns = xmlStrdup(old->ns);
248 cur->next = xsltNewAttrElem(old->attr);
249 if (old->set != NULL) {
250 cur->next->set = xmlStrdup(old->set);
252 cur->next->ns = xmlStrdup(old->ns);
262 /************************************************************************
264 * Module interfaces *
266 ************************************************************************/
269 * xsltParseStylesheetAttributeSet:
270 * @style: the XSLT stylesheet
271 * @cur: the "attribute-set" element
273 * parse an XSLT stylesheet attribute-set element
277 xsltParseStylesheetAttributeSet(xsltStylesheetPtr style, xmlNodePtr cur) {
278 xmlChar *prop = NULL;
279 xmlChar *ncname = NULL;
280 xmlChar *prefix = NULL;
282 xmlChar *attrib, *endattr;
284 xsltAttrElemPtr values;
286 if ((cur == NULL) || (style == NULL))
289 prop = xsltGetNsProp(cur, (const xmlChar *)"name", XSLT_NAMESPACE);
291 xsltGenericError(xsltGenericErrorContext,
292 "xsl:attribute-set : name is missing\n");
296 ncname = xmlSplitQName2(prop, &prefix);
297 if (ncname == NULL) {
303 if (style->attributeSets == NULL) {
304 #ifdef WITH_XSLT_DEBUG_ATTRIBUTES
305 xsltGenericDebug(xsltGenericDebugContext,
306 "creating attribute set table\n");
308 style->attributeSets = xmlHashCreate(10);
310 if (style->attributeSets == NULL)
313 values = xmlHashLookup2(style->attributeSets, ncname, prefix);
316 * check the children list
318 list = cur->children;
319 while (list != NULL) {
320 if (IS_XSLT_ELEM(list)) {
321 if (!IS_XSLT_NAME(list, "attribute")) {
322 xsltGenericError(xsltGenericErrorContext,
323 "xsl:attribute-set : unexpected child xsl:%s\n",
326 #ifdef WITH_XSLT_DEBUG_ATTRIBUTES
327 xsltGenericDebug(xsltGenericDebugContext,
328 "add attribute to list %s\n", ncname);
330 values = xsltAddAttrElemList(values, list);
333 xsltGenericError(xsltGenericErrorContext,
334 "xsl:attribute-set : unexpected child %s\n", list->name);
340 * Check a possible use-attribute-sets definition
342 /* TODO check recursion */
344 attributes = xsltGetNsProp(cur, (const xmlChar *)"use-attribute-sets",
346 if (attributes == NULL) {
351 while (*attrib != 0) {
352 while (IS_BLANK(*attrib)) attrib++;
356 while ((*endattr != 0) && (!IS_BLANK(*endattr))) endattr++;
357 attrib = xmlStrndup(attrib, endattr - attrib);
359 xmlChar *ncname2 = NULL;
360 xmlChar *prefix2 = NULL;
361 xsltAttrElemPtr values2;
362 #ifdef WITH_XSLT_DEBUG_ATTRIBUTES
363 xsltGenericDebug(xsltGenericDebugContext,
364 "xsl:attribute-set : %s adds use %s\n", ncname, attrib);
366 ncname2 = xmlSplitQName2(attrib, &prefix2);
367 if (ncname2 == NULL) {
372 values2 = xsltNewAttrElem(NULL);
373 if (values2 != NULL) {
374 values2->set = ncname2;
375 values2->ns = prefix2;
376 values = xsltMergeAttrElemList(values, values2);
377 xsltFreeAttrElem(values2);
397 values = xsltNewAttrElem(NULL);
398 xmlHashUpdateEntry2(style->attributeSets, ncname, prefix, values, NULL);
399 #ifdef WITH_XSLT_DEBUG_ATTRIBUTES
400 xsltGenericDebug(xsltGenericDebugContext,
401 "updated attribute list %s\n", ncname);
415 * @style: the XSLT stylesheet
416 * @name: the attribute list name
417 * @ns: the attribute list namespace
419 * lookup an attribute set based on the style cascade
421 * Returns the attribute set or NULL
423 static xsltAttrElemPtr
424 xsltGetSAS(xsltStylesheetPtr style, const xmlChar *name, const xmlChar *ns) {
425 xsltAttrElemPtr values;
427 while (style != NULL) {
428 values = xmlHashLookup2(style->attributeSets, name, ns);
431 style = xsltNextImport(style);
437 * xsltResolveSASCallback,:
438 * @style: the XSLT stylesheet
440 * resolve the references in an attribute set.
443 xsltResolveSASCallback(xsltAttrElemPtr values, xsltStylesheetPtr style,
444 const xmlChar *name, const xmlChar *ns,
445 ATTRIBUTE_UNUSED const xmlChar *ignored) {
447 xsltAttrElemPtr refs;
450 while (tmp != NULL) {
451 if (tmp->set != NULL) {
453 * Check against cycles !
455 if ((xmlStrEqual(name, tmp->set)) && (xmlStrEqual(ns, tmp->ns))) {
456 xsltGenericError(xsltGenericErrorContext,
457 "xsl:attribute-set : use-attribute-sets recursion detected on %s\n",
460 #ifdef WITH_XSLT_DEBUG_ATTRIBUTES
461 xsltGenericDebug(xsltGenericDebugContext,
462 "Importing attribute list %s\n", tmp->set);
465 refs = xsltGetSAS(style, tmp->set, tmp->ns);
467 xsltGenericError(xsltGenericErrorContext,
468 "xsl:attribute-set : use-attribute-sets %s reference missing %s\n",
472 * recurse first for cleanup
474 xsltResolveSASCallback(refs, style, name, ns, NULL);
478 xsltMergeAttrElemList(values, refs);
480 * Then suppress the reference
482 xmlFree((char *)tmp->set);
484 if (tmp->ns != NULL) {
485 xmlFree((char *)tmp->ns);
495 * xsltMergeSASCallback,:
496 * @style: the XSLT stylesheet
498 * Merge an attribute set from an imported stylesheet.
501 xsltMergeSASCallback(xsltAttrElemPtr values, xsltStylesheetPtr style,
502 const xmlChar *name, const xmlChar *ns,
503 ATTRIBUTE_UNUSED const xmlChar *ignored) {
505 xsltAttrElemPtr topSet;
507 ret = xmlHashAddEntry2(style->attributeSets, name, ns, values);
510 * Add failed, this attribute set can be removed.
512 #ifdef WITH_XSLT_DEBUG_ATTRIBUTES
513 xsltGenericDebug(xsltGenericDebugContext,
514 "attribute set %s present already in top stylesheet"
515 " - merging\n", name);
517 topSet = xmlHashLookup2(style->attributeSets, name, ns);
519 xsltGenericError(xsltGenericErrorContext,
520 "xsl:attribute-set : logic error merging from imports for"
521 " attribute-set %s\n", name);
523 topSet = xsltMergeAttrElemList(topSet, values);
524 xmlHashUpdateEntry2(style->attributeSets, name, ns, topSet, NULL);
526 xsltFreeAttrElemList(values);
527 #ifdef WITH_XSLT_DEBUG_ATTRIBUTES
529 xsltGenericDebug(xsltGenericDebugContext,
530 "attribute set %s moved to top stylesheet\n",
537 * xsltResolveStylesheetAttributeSet:
538 * @style: the XSLT stylesheet
540 * resolve the references between attribute sets.
543 xsltResolveStylesheetAttributeSet(xsltStylesheetPtr style) {
544 xsltStylesheetPtr cur;
546 #ifdef WITH_XSLT_DEBUG_ATTRIBUTES
547 xsltGenericDebug(xsltGenericDebugContext,
548 "Resolving attribute sets references\n");
551 * First aggregate all the attribute sets definitions from the imports
553 cur = xsltNextImport(style);
554 while (cur != NULL) {
555 if (cur->attributeSets != NULL) {
556 if (style->attributeSets == NULL) {
557 #ifdef WITH_XSLT_DEBUG_ATTRIBUTES
558 xsltGenericDebug(xsltGenericDebugContext,
559 "creating attribute set table\n");
561 style->attributeSets = xmlHashCreate(10);
563 xmlHashScanFull(cur->attributeSets,
564 (xmlHashScannerFull) xsltMergeSASCallback, style);
566 * the attribute lists have either been migrated to style
567 * or freed directly in xsltMergeSASCallback()
569 xmlHashFree(cur->attributeSets, NULL);
570 cur->attributeSets = NULL;
572 cur = xsltNextImport(cur);
576 * Then resolve all the references and computes the resulting sets
578 if (style->attributeSets != NULL) {
579 xmlHashScanFull(style->attributeSets,
580 (xmlHashScannerFull) xsltResolveSASCallback, style);
585 * xsltAttributeInternal:
586 * @ctxt: a XSLT process context
587 * @node: the node in the source tree.
588 * @inst: the xslt attribute node
589 * @comp: precomputed information
590 * @fromset: the attribute comes from an attribute-set
592 * Process the xslt attribute node on the source node
595 xsltAttributeInternal(xsltTransformContextPtr ctxt, xmlNodePtr node,
596 xmlNodePtr inst, xsltStylePreCompPtr comp,
599 xmlChar *prop = NULL;
600 xmlChar *ncname = NULL, *name, *namespace;
601 xmlChar *prefix = NULL;
602 xmlChar *value = NULL;
605 const xmlChar *URL = NULL;
608 if (ctxt->insert == NULL)
611 xsltTransformError(ctxt, NULL, inst,
612 "xsl:attribute : compilation failed\n");
616 if ((ctxt == NULL) || (node == NULL) || (inst == NULL)
619 if (!comp->has_name) {
622 if (ctxt->insert->children != NULL) {
623 xsltTransformError(ctxt, NULL, inst,
624 "xsl:attribute : node already has children\n");
628 if (xslDebugStatus != XSLT_DEBUG_NONE) {
629 xslHandleDebugger(inst, node, NULL, ctxt);
633 if (comp->name == NULL) {
635 xsltEvalAttrValueTemplate(ctxt, inst, (const xmlChar *) "name",
638 xsltTransformError(ctxt, NULL, inst,
639 "xsl:attribute : name is missing\n");
647 ncname = xmlSplitQName2(name, &prefix);
648 if (ncname == NULL) {
653 if (!xmlStrncasecmp(prefix, (xmlChar *) "xml", 3)) {
654 #ifdef WITH_XSLT_DEBUG_PARSING
655 xsltGenericDebug(xsltGenericDebugContext,
656 "xsltAttribute: xml prefix forbidden\n");
660 if ((comp->ns == NULL) && (comp->has_ns)) {
661 namespace = xsltEvalAttrValueTemplate(ctxt, inst,
663 "namespace", XSLT_NAMESPACE);
664 if (namespace != NULL) {
665 ns = xsltGetSpecialNamespace(ctxt, inst, namespace, prefix,
669 if (prefix != NULL) {
670 ns = xmlSearchNs(inst->doc, inst, prefix);
672 xsltTransformError(ctxt, NULL, inst,
673 "xsl:attribute : no namespace bound to prefix %s\n",
676 ns = xsltGetNamespace(ctxt, inst, ns, ctxt->insert);
680 } else if (comp->ns != NULL) {
681 ns = xsltGetSpecialNamespace(ctxt, inst, comp->ns, prefix,
683 } else if (prefix != NULL) {
685 tmp = xmlSearchNs(inst->doc, inst, prefix);
687 ns = xsltGetNamespace(ctxt, inst, tmp, ctxt->insert);
691 if ((fromset) && (ns != NULL))
696 attr = xmlHasNsProp(ctxt->insert, name, URL);
698 attr = xmlHasProp(ctxt->insert, name);
702 value = xsltEvalTemplateString(ctxt, node, inst);
705 attr = xmlSetNsProp(ctxt->insert, ns, name,
706 (const xmlChar *) "");
709 xmlSetProp(ctxt->insert, name, (const xmlChar *) "");
713 attr = xmlSetNsProp(ctxt->insert, ns, name, value);
715 attr = xmlSetProp(ctxt->insert, name, value);
732 * @ctxt: a XSLT process context
733 * @node: the node in the source tree.
734 * @inst: the xslt attribute node
735 * @comp: precomputed information
737 * Process the xslt attribute node on the source node
740 xsltAttribute(xsltTransformContextPtr ctxt, xmlNodePtr node,
741 xmlNodePtr inst, xsltStylePreCompPtr comp) {
742 xsltAttributeInternal(ctxt, node, inst, comp, 0);
746 * xsltApplyAttributeSet:
747 * @ctxt: the XSLT stylesheet
748 * @node: the node in the source tree.
749 * @inst: the xslt attribute node
750 * @attributes: the set list.
752 * Apply the xsl:use-attribute-sets
756 xsltApplyAttributeSet(xsltTransformContextPtr ctxt, xmlNodePtr node,
757 xmlNodePtr inst ATTRIBUTE_UNUSED,
758 xmlChar * attributes)
760 xmlChar *ncname = NULL;
761 xmlChar *prefix = NULL;
762 xmlChar *attrib, *endattr;
763 xsltAttrElemPtr values;
764 xsltStylesheetPtr style;
766 if (attributes == NULL) {
771 while (*attrib != 0) {
772 while (IS_BLANK(*attrib))
777 while ((*endattr != 0) && (!IS_BLANK(*endattr)))
779 attrib = xmlStrndup(attrib, endattr - attrib);
781 #ifdef WITH_XSLT_DEBUG_ATTRIBUTES
782 xsltGenericDebug(xsltGenericDebugContext,
783 "apply attribute set %s\n", attrib);
785 ncname = xmlSplitQName2(attrib, &prefix);
786 if (ncname == NULL) {
794 if ((style != NULL) && (style->attributeSets != NULL) &&
795 (xslDebugStatus != XSLT_DEBUG_NONE)) {
797 xmlHashLookup2(style->attributeSets, ncname, prefix);
798 if ((values != NULL) && (values->attr != NULL))
799 xslHandleDebugger(values->attr->parent, node, NULL,
803 while (style != NULL) {
805 xmlHashLookup2(style->attributeSets, ncname, prefix);
806 while (values != NULL) {
807 if (values->attr != NULL) {
808 xsltAttributeInternal(ctxt, node, values->attr,
809 values->attr->_private, 1);
811 values = values->next;
813 style = xsltNextImport(style);
827 * xsltFreeAttributeSetsHashes:
828 * @style: an XSLT stylesheet
830 * Free up the memory used by attribute sets
833 xsltFreeAttributeSetsHashes(xsltStylesheetPtr style) {
834 if (style->attributeSets != NULL)
835 xmlHashFree((xmlHashTablePtr) style->attributeSets,
836 (xmlHashDeallocator) xsltFreeAttrElemList);
837 style->attributeSets = NULL;