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) {
506 ret = xmlHashAddEntry2(style->attributeSets, name, ns, values);
509 * Add failed, this attribute set can be removed.
511 #ifdef WITH_XSLT_DEBUG_ATTRIBUTES
512 xsltGenericDebug(xsltGenericDebugContext,
513 "attribute sets %s present already in top stylesheet\n",
516 xsltFreeAttrElem(values);
517 #ifdef WITH_XSLT_DEBUG_ATTRIBUTES
519 xsltGenericDebug(xsltGenericDebugContext,
520 "attribute sets %s moved to top stylesheet\n",
527 * xsltResolveStylesheetAttributeSet:
528 * @style: the XSLT stylesheet
530 * resolve the references between attribute sets.
533 xsltResolveStylesheetAttributeSet(xsltStylesheetPtr style) {
534 xsltStylesheetPtr cur;
536 #ifdef WITH_XSLT_DEBUG_ATTRIBUTES
537 xsltGenericDebug(xsltGenericDebugContext,
538 "Resolving attribute sets references\n");
541 * First aggregate all the attribute sets definitions from the imports
543 cur = xsltNextImport(style);
544 while (cur != NULL) {
545 if (cur->attributeSets != NULL) {
546 if (style->attributeSets == NULL) {
547 #ifdef WITH_XSLT_DEBUG_ATTRIBUTES
548 xsltGenericDebug(xsltGenericDebugContext,
549 "creating attribute set table\n");
551 style->attributeSets = xmlHashCreate(10);
553 xmlHashScanFull(cur->attributeSets,
554 (xmlHashScannerFull) xsltMergeSASCallback, style);
556 * the attribute lists have either been migrated to style
557 * or freed directly in xsltMergeSASCallback()
559 xmlHashFree(cur->attributeSets, NULL);
560 cur->attributeSets = NULL;
562 cur = xsltNextImport(cur);
566 * Then resolve all the references and computes the resulting sets
568 if (style->attributeSets != NULL) {
569 xmlHashScanFull(style->attributeSets,
570 (xmlHashScannerFull) xsltResolveSASCallback, style);
575 * xsltAttributeInternal:
576 * @ctxt: a XSLT process context
577 * @node: the node in the source tree.
578 * @inst: the xslt attribute node
579 * @comp: precomputed information
580 * @fromset: the attribute comes from an attribute-set
582 * Process the xslt attribute node on the source node
585 xsltAttributeInternal(xsltTransformContextPtr ctxt, xmlNodePtr node,
586 xmlNodePtr inst, xsltStylePreCompPtr comp,
589 xmlChar *prop = NULL;
590 xmlChar *ncname = NULL, *name, *namespace;
591 xmlChar *prefix = NULL;
592 xmlChar *value = NULL;
595 const xmlChar *URL = NULL;
598 if (ctxt->insert == NULL)
601 xsltTransformError(ctxt, NULL, inst,
602 "xsl:attribute : compilation failed\n");
606 if ((ctxt == NULL) || (node == NULL) || (inst == NULL)
609 if (!comp->has_name) {
612 if (ctxt->insert->children != NULL) {
613 xsltTransformError(ctxt, NULL, inst,
614 "xsl:attribute : node already has children\n");
618 if (xslDebugStatus != XSLT_DEBUG_NONE) {
619 xslHandleDebugger(inst, node, NULL, ctxt);
623 if (comp->name == NULL) {
625 xsltEvalAttrValueTemplate(ctxt, inst, (const xmlChar *) "name",
628 xsltTransformError(ctxt, NULL, inst,
629 "xsl:attribute : name is missing\n");
637 ncname = xmlSplitQName2(name, &prefix);
638 if (ncname == NULL) {
643 if (!xmlStrncasecmp(prefix, (xmlChar *) "xml", 3)) {
644 #ifdef WITH_XSLT_DEBUG_PARSING
645 xsltGenericDebug(xsltGenericDebugContext,
646 "xsltAttribute: xml prefix forbidden\n");
650 if ((comp->ns == NULL) && (comp->has_ns)) {
651 namespace = xsltEvalAttrValueTemplate(ctxt, inst,
653 "namespace", XSLT_NAMESPACE);
654 if (namespace != NULL) {
655 ns = xsltGetSpecialNamespace(ctxt, inst, namespace, prefix,
659 if (prefix != NULL) {
660 ns = xmlSearchNs(inst->doc, inst, prefix);
662 xsltTransformError(ctxt, NULL, inst,
663 "xsl:attribute : no namespace bound to prefix %s\n",
666 ns = xsltGetNamespace(ctxt, inst, ns, ctxt->insert);
670 } else if (comp->ns != NULL) {
671 ns = xsltGetSpecialNamespace(ctxt, inst, comp->ns, prefix,
673 } else if (prefix != NULL) {
675 tmp = xmlSearchNs(inst->doc, inst, prefix);
677 ns = xsltGetNamespace(ctxt, inst, tmp, ctxt->insert);
681 if ((fromset) && (ns != NULL))
686 attr = xmlHasNsProp(ctxt->insert, name, URL);
688 attr = xmlHasProp(ctxt->insert, name);
692 value = xsltEvalTemplateString(ctxt, node, inst);
695 attr = xmlSetNsProp(ctxt->insert, ns, name,
696 (const xmlChar *) "");
699 xmlSetProp(ctxt->insert, name, (const xmlChar *) "");
703 attr = xmlSetNsProp(ctxt->insert, ns, name, value);
705 attr = xmlSetProp(ctxt->insert, name, value);
722 * @ctxt: a XSLT process context
723 * @node: the node in the source tree.
724 * @inst: the xslt attribute node
725 * @comp: precomputed information
727 * Process the xslt attribute node on the source node
730 xsltAttribute(xsltTransformContextPtr ctxt, xmlNodePtr node,
731 xmlNodePtr inst, xsltStylePreCompPtr comp) {
732 xsltAttributeInternal(ctxt, node, inst, comp, 0);
736 * xsltApplyAttributeSet:
737 * @ctxt: the XSLT stylesheet
738 * @node: the node in the source tree.
739 * @inst: the xslt attribute node
740 * @attributes: the set list.
742 * Apply the xsl:use-attribute-sets
746 xsltApplyAttributeSet(xsltTransformContextPtr ctxt, xmlNodePtr node,
747 xmlNodePtr inst ATTRIBUTE_UNUSED,
748 xmlChar * attributes)
750 xmlChar *ncname = NULL;
751 xmlChar *prefix = NULL;
752 xmlChar *attrib, *endattr;
753 xsltAttrElemPtr values;
754 xsltStylesheetPtr style;
756 if (attributes == NULL) {
761 while (*attrib != 0) {
762 while (IS_BLANK(*attrib))
767 while ((*endattr != 0) && (!IS_BLANK(*endattr)))
769 attrib = xmlStrndup(attrib, endattr - attrib);
771 #ifdef WITH_XSLT_DEBUG_ATTRIBUTES
772 xsltGenericDebug(xsltGenericDebugContext,
773 "apply attribute set %s\n", attrib);
775 ncname = xmlSplitQName2(attrib, &prefix);
776 if (ncname == NULL) {
784 if ((style != NULL) && (style->attributeSets != NULL) &&
785 (xslDebugStatus != XSLT_DEBUG_NONE)) {
787 xmlHashLookup2(style->attributeSets, ncname, prefix);
788 if ((values != NULL) && (values->attr != NULL))
789 xslHandleDebugger(values->attr->parent, node, NULL,
793 while (style != NULL) {
795 xmlHashLookup2(style->attributeSets, ncname, prefix);
796 while (values != NULL) {
797 if (values->attr != NULL) {
798 xsltAttributeInternal(ctxt, node, values->attr,
799 values->attr->_private, 1);
801 values = values->next;
803 style = xsltNextImport(style);
817 * xsltFreeAttributeSetsHashes:
818 * @style: an XSLT stylesheet
820 * Free up the memory used by attribute sets
823 xsltFreeAttributeSetsHashes(xsltStylesheetPtr style) {
824 if (style->attributeSets != NULL)
825 xmlHashFree((xmlHashTablePtr) style->attributeSets,
826 (xmlHashDeallocator) xsltFreeAttrElemList);
827 style->attributeSets = NULL;