1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249 |
- /*
- * attributes.c: Implementation of the XSLT attributes handling
- *
- * Reference:
- * http://www.w3.org/TR/1999/REC-xslt-19991116
- *
- * See Copyright for the status of this software.
- *
- * daniel@veillard.com
- */
- #define IN_LIBXSLT
- #include "libxslt.h"
- #include <string.h>
- #ifdef HAVE_SYS_TYPES_H
- #include <sys/types.h>
- #endif
- #ifdef HAVE_MATH_H
- #include <math.h>
- #endif
- #ifdef HAVE_FLOAT_H
- #include <float.h>
- #endif
- #ifdef HAVE_IEEEFP_H
- #include <ieeefp.h>
- #endif
- #ifdef HAVE_NAN_H
- #include <nan.h>
- #endif
- #ifdef HAVE_CTYPE_H
- #include <ctype.h>
- #endif
- #include <libxml/xmlmemory.h>
- #include <libxml/tree.h>
- #include <libxml/hash.h>
- #include <libxml/xmlerror.h>
- #include <libxml/uri.h>
- #include <libxml/parserInternals.h>
- #include "xslt.h"
- #include "xsltInternals.h"
- #include "xsltutils.h"
- #include "attributes.h"
- #include "namespaces.h"
- #include "templates.h"
- #include "imports.h"
- #include "transform.h"
- #include "preproc.h"
- #define WITH_XSLT_DEBUG_ATTRIBUTES
- #ifdef WITH_XSLT_DEBUG
- #define WITH_XSLT_DEBUG_ATTRIBUTES
- #endif
- /*
- * Useful macros
- */
- #ifdef IS_BLANK
- #undef IS_BLANK
- #endif
- #define IS_BLANK(c) (((c) == 0x20) || ((c) == 0x09) || ((c) == 0xA) || \
- ((c) == 0x0D))
- #define IS_BLANK_NODE(n) \
- (((n)->type == XML_TEXT_NODE) && (xsltIsBlank((n)->content)))
- #define ATTRSET_UNRESOLVED 0
- #define ATTRSET_RESOLVING 1
- #define ATTRSET_RESOLVED 2
- /*
- * The in-memory structure corresponding to an XSLT Attribute in
- * an attribute set
- */
- typedef struct _xsltAttrElem xsltAttrElem;
- typedef xsltAttrElem *xsltAttrElemPtr;
- struct _xsltAttrElem {
- struct _xsltAttrElem *next;/* chained list */
- xmlNodePtr attr; /* the xsl:attribute definition */
- };
- typedef struct _xsltUseAttrSet xsltUseAttrSet;
- typedef xsltUseAttrSet *xsltUseAttrSetPtr;
- struct _xsltUseAttrSet {
- struct _xsltUseAttrSet *next; /* chained list */
- const xmlChar *ncname;
- const xmlChar *ns;
- };
- typedef struct _xsltAttrSet xsltAttrSet;
- typedef xsltAttrSet *xsltAttrSetPtr;
- struct _xsltAttrSet {
- int state;
- xsltAttrElemPtr attrs; /* list head */
- xsltUseAttrSetPtr useAttrSets; /* list head */
- };
- typedef struct _xsltAttrSetContext xsltAttrSetContext;
- typedef xsltAttrSetContext *xsltAttrSetContextPtr;
- struct _xsltAttrSetContext {
- xsltStylesheetPtr topStyle;
- xsltStylesheetPtr style;
- };
- static void
- xsltResolveAttrSet(xsltAttrSetPtr set, xsltStylesheetPtr topStyle,
- xsltStylesheetPtr style, const xmlChar *name,
- const xmlChar *ns, int depth);
- /************************************************************************
- * *
- * XSLT Attribute handling *
- * *
- ************************************************************************/
- /**
- * xsltNewAttrElem:
- * @attr: the new xsl:attribute node
- *
- * Create a new XSLT AttrElem
- *
- * Returns the newly allocated xsltAttrElemPtr or NULL in case of error
- */
- static xsltAttrElemPtr
- xsltNewAttrElem(xmlNodePtr attr) {
- xsltAttrElemPtr cur;
- cur = (xsltAttrElemPtr) xmlMalloc(sizeof(xsltAttrElem));
- if (cur == NULL) {
- xsltGenericError(xsltGenericErrorContext,
- "xsltNewAttrElem : malloc failed\n");
- return(NULL);
- }
- memset(cur, 0, sizeof(xsltAttrElem));
- cur->attr = attr;
- return(cur);
- }
- /**
- * xsltFreeAttrElem:
- * @attr: an XSLT AttrElem
- *
- * Free up the memory allocated by @attr
- */
- static void
- xsltFreeAttrElem(xsltAttrElemPtr attr) {
- xmlFree(attr);
- }
- /**
- * xsltFreeAttrElemList:
- * @list: an XSLT AttrElem list
- *
- * Free up the memory allocated by @list
- */
- static void
- xsltFreeAttrElemList(xsltAttrElemPtr list) {
- xsltAttrElemPtr next;
- while (list != NULL) {
- next = list->next;
- xsltFreeAttrElem(list);
- list = next;
- }
- }
- /**
- * xsltAddAttrElemList:
- * @list: an XSLT AttrElem list
- * @attr: the new xsl:attribute node
- *
- * Add the new attribute to the list.
- *
- * Returns the new list pointer
- */
- static xsltAttrElemPtr
- xsltAddAttrElemList(xsltAttrElemPtr list, xmlNodePtr attr) {
- xsltAttrElemPtr next, cur;
- if (attr == NULL)
- return(list);
- if (list == NULL)
- return(xsltNewAttrElem(attr));
- cur = list;
- while (cur != NULL) {
- next = cur->next;
- if (next == NULL) {
- cur->next = xsltNewAttrElem(attr);
- return(list);
- }
- cur = next;
- }
- return(list);
- }
- /**
- * xsltNewUseAttrSet:
- * @ncname: local name
- * @ns: namespace URI
- *
- * Create a new XSLT UseAttrSet
- *
- * Returns the newly allocated xsltUseAttrSetPtr or NULL in case of error.
- */
- static xsltUseAttrSetPtr
- xsltNewUseAttrSet(const xmlChar *ncname, const xmlChar *ns) {
- xsltUseAttrSetPtr cur;
- cur = (xsltUseAttrSetPtr) xmlMalloc(sizeof(xsltUseAttrSet));
- if (cur == NULL) {
- xsltGenericError(xsltGenericErrorContext,
- "xsltNewUseAttrSet : malloc failed\n");
- return(NULL);
- }
- memset(cur, 0, sizeof(xsltUseAttrSet));
- cur->ncname = ncname;
- cur->ns = ns;
- return(cur);
- }
- /**
- * xsltFreeUseAttrSet:
- * @use: an XSLT UseAttrSet
- *
- * Free up the memory allocated by @use
- */
- static void
- xsltFreeUseAttrSet(xsltUseAttrSetPtr use) {
- xmlFree(use);
- }
- /**
- * xsltFreeUseAttrSetList:
- * @list: an XSLT UseAttrSet list
- *
- * Free up the memory allocated by @list
- */
- static void
- xsltFreeUseAttrSetList(xsltUseAttrSetPtr list) {
- xsltUseAttrSetPtr next;
- while (list != NULL) {
- next = list->next;
- xsltFreeUseAttrSet(list);
- list = next;
- }
- }
- /**
- * xsltAddUseAttrSetList:
- * @list: a xsltUseAttrSet list
- * @ncname: local name
- * @ns: namespace URI
- *
- * Add the use-attribute-set name to the list.
- *
- * Returns the new list pointer.
- */
- static xsltUseAttrSetPtr
- xsltAddUseAttrSetList(xsltUseAttrSetPtr list, const xmlChar *ncname,
- const xmlChar *ns) {
- xsltUseAttrSetPtr next, cur;
- if (ncname == NULL)
- return(list);
- if (list == NULL)
- return(xsltNewUseAttrSet(ncname, ns));
- cur = list;
- while (cur != NULL) {
- if ((cur->ncname == ncname) && (cur->ns == ns))
- return(list);
- next = cur->next;
- if (next == NULL) {
- cur->next = xsltNewUseAttrSet(ncname, ns);
- return(list);
- }
- cur = next;
- }
- return(list);
- }
- /**
- * xsltNewAttrSet:
- *
- * Create a new attribute set.
- *
- * Returns the newly allocated xsltAttrSetPtr or NULL in case of error.
- */
- static xsltAttrSetPtr
- xsltNewAttrSet() {
- xsltAttrSetPtr cur;
- cur = (xsltAttrSetPtr) xmlMalloc(sizeof(xsltAttrSet));
- if (cur == NULL) {
- xsltGenericError(xsltGenericErrorContext,
- "xsltNewAttrSet : malloc failed\n");
- return(NULL);
- }
- memset(cur, 0, sizeof(xsltAttrSet));
- return(cur);
- }
- /**
- * xsltFreeAttrSet:
- * @set: an attribute set
- *
- * Free memory allocated by @set
- */
- static void
- xsltFreeAttrSet(xsltAttrSetPtr set) {
- if (set == NULL)
- return;
- xsltFreeAttrElemList(set->attrs);
- xsltFreeUseAttrSetList(set->useAttrSets);
- xmlFree(set);
- }
- /**
- * xsltMergeAttrSets:
- * @set: an attribute set
- * @other: another attribute set
- *
- * Add all the attributes from @other to @set,
- * but drop redefinition of existing values.
- */
- static void
- xsltMergeAttrSets(xsltAttrSetPtr set, xsltAttrSetPtr other) {
- xsltAttrElemPtr cur;
- xsltAttrElemPtr old = other->attrs;
- int add;
- while (old != NULL) {
- /*
- * Check that the attribute is not yet in the list
- */
- cur = set->attrs;
- add = 1;
- while (cur != NULL) {
- xsltStylePreCompPtr curComp = cur->attr->psvi;
- xsltStylePreCompPtr oldComp = old->attr->psvi;
- if ((curComp->name == oldComp->name) &&
- (curComp->ns == oldComp->ns)) {
- add = 0;
- break;
- }
- if (cur->next == NULL)
- break;
- cur = cur->next;
- }
- if (add == 1) {
- if (cur == NULL) {
- set->attrs = xsltNewAttrElem(old->attr);
- } else if (add) {
- cur->next = xsltNewAttrElem(old->attr);
- }
- }
- old = old->next;
- }
- }
- /************************************************************************
- * *
- * Module interfaces *
- * *
- ************************************************************************/
- /**
- * xsltParseStylesheetAttributeSet:
- * @style: the XSLT stylesheet
- * @cur: the "attribute-set" element
- *
- * parse an XSLT stylesheet attribute-set element
- */
- void
- xsltParseStylesheetAttributeSet(xsltStylesheetPtr style, xmlNodePtr cur) {
- const xmlChar *ncname;
- const xmlChar *prefix;
- const xmlChar *nsUri = NULL;
- xmlChar *value;
- xmlNodePtr child;
- xsltAttrSetPtr set;
- if ((cur == NULL) || (style == NULL) || (cur->type != XML_ELEMENT_NODE))
- return;
- value = xmlGetNsProp(cur, (const xmlChar *)"name", NULL);
- if ((value == NULL) || (*value == 0)) {
- xsltGenericError(xsltGenericErrorContext,
- "xsl:attribute-set : name is missing\n");
- if (value)
- xmlFree(value);
- return;
- }
- if (xmlValidateQName(value, 0)) {
- xsltTransformError(NULL, style, cur,
- "xsl:attribute-set : The name '%s' is not a valid QName.\n",
- value);
- style->errors++;
- xmlFree(value);
- return;
- }
- ncname = xsltSplitQName(style->dict, value, &prefix);
- xmlFree(value);
- value = NULL;
- if (prefix != NULL) {
- xmlNsPtr ns = xmlSearchNs(style->doc, cur, prefix);
- if (ns == NULL) {
- xsltTransformError(NULL, style, cur,
- "xsl:attribute-set : No namespace found for QName '%s:%s'\n",
- prefix, ncname);
- style->errors++;
- return;
- }
- nsUri = ns->href;
- }
- if (style->attributeSets == NULL) {
- #ifdef WITH_XSLT_DEBUG_ATTRIBUTES
- xsltGenericDebug(xsltGenericDebugContext,
- "creating attribute set table\n");
- #endif
- style->attributeSets = xmlHashCreate(10);
- }
- if (style->attributeSets == NULL)
- return;
- set = xmlHashLookup2(style->attributeSets, ncname, nsUri);
- if (set == NULL) {
- set = xsltNewAttrSet();
- if (set == NULL)
- return;
- xmlHashAddEntry2(style->attributeSets, ncname, nsUri, set);
- }
- /*
- * Parse the content. Only xsl:attribute elements are allowed.
- */
- child = cur->children;
- while (child != NULL) {
- /*
- * Report invalid nodes.
- */
- if ((child->type != XML_ELEMENT_NODE) ||
- (child->ns == NULL) ||
- (! IS_XSLT_ELEM(child)))
- {
- if (child->type == XML_ELEMENT_NODE)
- xsltTransformError(NULL, style, child,
- "xsl:attribute-set : unexpected child %s\n",
- child->name);
- else
- xsltTransformError(NULL, style, child,
- "xsl:attribute-set : child of unexpected type\n");
- } else if (!IS_XSLT_NAME(child, "attribute")) {
- xsltTransformError(NULL, style, child,
- "xsl:attribute-set : unexpected child xsl:%s\n",
- child->name);
- } else {
- #ifdef WITH_XSLT_DEBUG_ATTRIBUTES
- xsltGenericDebug(xsltGenericDebugContext,
- "add attribute to list %s\n", ncname);
- #endif
- xsltStylePreCompute(style, child);
- if (child->children != NULL) {
- #ifdef XSLT_REFACTORED
- xsltParseSequenceConstructor(XSLT_CCTXT(style),
- child->children);
- #else
- xsltParseTemplateContent(style, child);
- #endif
- }
- if (child->psvi == NULL) {
- xsltTransformError(NULL, style, child,
- "xsl:attribute-set : internal error, attribute %s not "
- "compiled\n", child->name);
- }
- else {
- set->attrs = xsltAddAttrElemList(set->attrs, child);
- }
- }
- child = child->next;
- }
- /*
- * Process attribute "use-attribute-sets".
- */
- value = xmlGetNsProp(cur, BAD_CAST "use-attribute-sets", NULL);
- if (value != NULL) {
- const xmlChar *curval, *endval;
- curval = value;
- while (*curval != 0) {
- while (IS_BLANK(*curval)) curval++;
- if (*curval == 0)
- break;
- endval = curval;
- while ((*endval != 0) && (!IS_BLANK(*endval))) endval++;
- curval = xmlDictLookup(style->dict, curval, endval - curval);
- if (curval) {
- const xmlChar *ncname2 = NULL;
- const xmlChar *prefix2 = NULL;
- const xmlChar *nsUri2 = NULL;
- #ifdef WITH_XSLT_DEBUG_ATTRIBUTES
- xsltGenericDebug(xsltGenericDebugContext,
- "xsl:attribute-set : %s adds use %s\n", ncname, curval);
- #endif
- if (xmlValidateQName(curval, 0)) {
- xsltTransformError(NULL, style, cur,
- "xsl:attribute-set : The name '%s' in "
- "use-attribute-sets is not a valid QName.\n", curval);
- style->errors++;
- xmlFree(value);
- return;
- }
- ncname2 = xsltSplitQName(style->dict, curval, &prefix2);
- if (prefix2 != NULL) {
- xmlNsPtr ns2 = xmlSearchNs(style->doc, cur, prefix2);
- if (ns2 == NULL) {
- xsltTransformError(NULL, style, cur,
- "xsl:attribute-set : No namespace found for QName "
- "'%s:%s' in use-attribute-sets\n",
- prefix2, ncname2);
- style->errors++;
- xmlFree(value);
- return;
- }
- nsUri2 = ns2->href;
- }
- set->useAttrSets = xsltAddUseAttrSetList(set->useAttrSets,
- ncname2, nsUri2);
- }
- curval = endval;
- }
- xmlFree(value);
- value = NULL;
- }
- #ifdef WITH_XSLT_DEBUG_ATTRIBUTES
- xsltGenericDebug(xsltGenericDebugContext,
- "updated attribute list %s\n", ncname);
- #endif
- }
- /**
- * xsltResolveUseAttrSets:
- * @set: the attribute set
- * @asctx: the context for attribute set resolution
- * @depth: recursion depth
- *
- * Process "use-attribute-sets".
- */
- static void
- xsltResolveUseAttrSets(xsltAttrSetPtr set, xsltStylesheetPtr topStyle,
- int depth) {
- xsltStylesheetPtr cur;
- xsltAttrSetPtr other;
- xsltUseAttrSetPtr use = set->useAttrSets;
- xsltUseAttrSetPtr next;
- while (use != NULL) {
- /*
- * Iterate top stylesheet and all imports.
- */
- cur = topStyle;
- while (cur != NULL) {
- if (cur->attributeSets) {
- other = xmlHashLookup2(cur->attributeSets, use->ncname,
- use->ns);
- if (other != NULL) {
- xsltResolveAttrSet(other, topStyle, cur, use->ncname,
- use->ns, depth + 1);
- xsltMergeAttrSets(set, other);
- break;
- }
- }
- cur = xsltNextImport(cur);
- }
- next = use->next;
- /* Free useAttrSets early. */
- xsltFreeUseAttrSet(use);
- use = next;
- }
- set->useAttrSets = NULL;
- }
- /**
- * xsltResolveAttrSet:
- * @set: the attribute set
- * @asctx: the context for attribute set resolution
- * @name: the local name of the attirbute set
- * @ns: the namespace of the attribute set
- * @depth: recursion depth
- *
- * resolve the references in an attribute set.
- */
- static void
- xsltResolveAttrSet(xsltAttrSetPtr set, xsltStylesheetPtr topStyle,
- xsltStylesheetPtr style, const xmlChar *name,
- const xmlChar *ns, int depth) {
- xsltStylesheetPtr cur;
- xsltAttrSetPtr other;
- if (set->state == ATTRSET_RESOLVED)
- return;
- if (set->state == ATTRSET_RESOLVING) {
- xsltTransformError(NULL, topStyle, NULL,
- "xsl:attribute-set : use-attribute-sets recursion detected"
- " on %s\n", name);
- topStyle->errors++;
- set->state = ATTRSET_RESOLVED;
- return;
- }
- if (depth > 100) {
- xsltTransformError(NULL, topStyle, NULL,
- "xsl:attribute-set : use-attribute-sets maximum recursion "
- "depth exceeded on %s\n", name);
- topStyle->errors++;
- return;
- }
- set->state = ATTRSET_RESOLVING;
- xsltResolveUseAttrSets(set, topStyle, depth);
- /* Merge imported sets. */
- cur = xsltNextImport(style);
- while (cur != NULL) {
- if (cur->attributeSets != NULL) {
- other = xmlHashLookup2(cur->attributeSets, name, ns);
- if (other != NULL) {
- #ifdef WITH_XSLT_DEBUG_ATTRIBUTES
- xsltGenericDebug(xsltGenericDebugContext,
- "xsl:attribute-set : merging import for %s\n", name);
- #endif
- xsltResolveUseAttrSets(other, topStyle, depth);
- xsltMergeAttrSets(set, other);
- xmlHashRemoveEntry2(cur->attributeSets, name, ns, NULL);
- xsltFreeAttrSet(other);
- }
- }
- cur = xsltNextImport(cur);
- }
- set->state = ATTRSET_RESOLVED;
- }
- /**
- * xsltResolveSASCallback:
- * @set: the attribute set
- * @asctx: the context for attribute set resolution
- * @name: the local name of the attirbute set
- * @ns: the namespace of the attribute set
- *
- * resolve the references in an attribute set.
- */
- static void
- xsltResolveSASCallback(void *payload, void *data,
- const xmlChar *name, const xmlChar *ns,
- ATTRIBUTE_UNUSED const xmlChar *ignored) {
- xsltAttrSetPtr set = (xsltAttrSetPtr) payload;
- xsltAttrSetContextPtr asctx = (xsltAttrSetContextPtr) data;
- xsltStylesheetPtr topStyle = asctx->topStyle;
- xsltStylesheetPtr style = asctx->style;
- xsltResolveAttrSet(set, topStyle, style, name, ns, 1);
- /* Move attribute sets to top stylesheet. */
- if (style != topStyle) {
- /*
- * This imported stylesheet won't be visited anymore. Don't bother
- * removing the hash entry.
- */
- if (xmlHashAddEntry2(topStyle->attributeSets, name, ns, set) < 0) {
- xsltGenericError(xsltGenericErrorContext,
- "xsl:attribute-set : internal error, can't move imported "
- " attribute set %s\n", name);
- }
- }
- }
- /**
- * xsltResolveStylesheetAttributeSet:
- * @style: the XSLT stylesheet
- *
- * resolve the references between attribute sets.
- */
- void
- xsltResolveStylesheetAttributeSet(xsltStylesheetPtr style) {
- xsltStylesheetPtr cur;
- xsltAttrSetContext asctx;
- #ifdef WITH_XSLT_DEBUG_ATTRIBUTES
- xsltGenericDebug(xsltGenericDebugContext,
- "Resolving attribute sets references\n");
- #endif
- asctx.topStyle = style;
- cur = style;
- while (cur != NULL) {
- if (cur->attributeSets != NULL) {
- if (style->attributeSets == NULL) {
- #ifdef WITH_XSLT_DEBUG_ATTRIBUTES
- xsltGenericDebug(xsltGenericDebugContext,
- "creating attribute set table\n");
- #endif
- style->attributeSets = xmlHashCreate(10);
- }
- asctx.style = cur;
- xmlHashScanFull(cur->attributeSets, xsltResolveSASCallback,
- &asctx);
- if (cur != style) {
- /*
- * the attribute lists have either been migrated to style
- * or freed directly in xsltResolveSASCallback()
- */
- xmlHashFree(cur->attributeSets, NULL);
- cur->attributeSets = NULL;
- }
- }
- cur = xsltNextImport(cur);
- }
- }
- /**
- * xsltAttribute:
- * @ctxt: a XSLT process context
- * @contextNode: the current node in the source tree
- * @inst: the xsl:attribute element
- * @castedComp: precomputed information
- *
- * Process the xslt attribute node on the source node
- */
- void
- xsltAttribute(xsltTransformContextPtr ctxt,
- xmlNodePtr contextNode,
- xmlNodePtr inst,
- xsltElemPreCompPtr castedComp)
- {
- #ifdef XSLT_REFACTORED
- xsltStyleItemAttributePtr comp =
- (xsltStyleItemAttributePtr) castedComp;
- #else
- xsltStylePreCompPtr comp = (xsltStylePreCompPtr) castedComp;
- #endif
- xmlNodePtr targetElem;
- xmlChar *prop = NULL;
- const xmlChar *name = NULL, *prefix = NULL, *nsName = NULL;
- xmlChar *value = NULL;
- xmlNsPtr ns = NULL;
- xmlAttrPtr attr;
- if ((ctxt == NULL) || (contextNode == NULL) || (inst == NULL) ||
- (inst->type != XML_ELEMENT_NODE) )
- return;
- /*
- * A comp->has_name == 0 indicates that we need to skip this instruction,
- * since it was evaluated to be invalid already during compilation.
- */
- if (!comp->has_name)
- return;
- /*
- * BIG NOTE: This previously used xsltGetSpecialNamespace() and
- * xsltGetNamespace(), but since both are not appropriate, we
- * will process namespace lookup here to avoid adding yet another
- * ns-lookup function to namespaces.c.
- */
- /*
- * SPEC XSLT 1.0: Error cases:
- * - Creating nodes other than text nodes during the instantiation of
- * the content of the xsl:attribute element; implementations may
- * either signal the error or ignore the offending nodes."
- */
- if (comp == NULL) {
- xsltTransformError(ctxt, NULL, inst,
- "Internal error in xsltAttribute(): "
- "The XSLT 'attribute' instruction was not compiled.\n");
- return;
- }
- /*
- * TODO: Shouldn't ctxt->insert == NULL be treated as an internal error?
- * So report an internal error?
- */
- if (ctxt->insert == NULL)
- return;
- /*
- * SPEC XSLT 1.0:
- * "Adding an attribute to a node that is not an element;
- * implementations may either signal the error or ignore the attribute."
- *
- * TODO: I think we should signal such errors in the future, and maybe
- * provide an option to ignore such errors.
- */
- targetElem = ctxt->insert;
- if (targetElem->type != XML_ELEMENT_NODE)
- return;
- /*
- * SPEC XSLT 1.0:
- * "Adding an attribute to an element after children have been added
- * to it; implementations may either signal the error or ignore the
- * attribute."
- *
- * TODO: We should decide whether not to report such errors or
- * to ignore them; note that we *ignore* if the parent is not an
- * element, but here we report an error.
- */
- if (targetElem->children != NULL) {
- /*
- * NOTE: Ah! This seems to be intended to support streamed
- * result generation!.
- */
- xsltTransformError(ctxt, NULL, inst,
- "xsl:attribute: Cannot add attributes to an "
- "element if children have been already added "
- "to the element.\n");
- return;
- }
- /*
- * Process the name
- * ----------------
- */
- #ifdef WITH_DEBUGGER
- if (ctxt->debugStatus != XSLT_DEBUG_NONE)
- xslHandleDebugger(inst, contextNode, NULL, ctxt);
- #endif
- if (comp->name == NULL) {
- /* TODO: fix attr acquisition wrt to the XSLT namespace */
- prop = xsltEvalAttrValueTemplate(ctxt, inst,
- (const xmlChar *) "name", XSLT_NAMESPACE);
- if (prop == NULL) {
- xsltTransformError(ctxt, NULL, inst,
- "xsl:attribute: The attribute 'name' is missing.\n");
- goto error;
- }
- if (xmlValidateQName(prop, 0)) {
- xsltTransformError(ctxt, NULL, inst,
- "xsl:attribute: The effective name '%s' is not a "
- "valid QName.\n", prop);
- /* we fall through to catch any further errors, if possible */
- }
- /*
- * Reject a name of "xmlns".
- */
- if (xmlStrEqual(prop, BAD_CAST "xmlns")) {
- xsltTransformError(ctxt, NULL, inst,
- "xsl:attribute: The effective name 'xmlns' is not allowed.\n");
- xmlFree(prop);
- goto error;
- }
- name = xsltSplitQName(ctxt->dict, prop, &prefix);
- xmlFree(prop);
- } else {
- /*
- * The "name" value was static.
- */
- #ifdef XSLT_REFACTORED
- prefix = comp->nsPrefix;
- name = comp->name;
- #else
- name = xsltSplitQName(ctxt->dict, comp->name, &prefix);
- #endif
- }
- /*
- * Process namespace semantics
- * ---------------------------
- *
- * Evaluate the namespace name.
- */
- if (comp->has_ns) {
- /*
- * The "namespace" attribute was existent.
- */
- if (comp->ns != NULL) {
- /*
- * No AVT; just plain text for the namespace name.
- */
- if (comp->ns[0] != 0)
- nsName = comp->ns;
- } else {
- xmlChar *tmpNsName;
- /*
- * Eval the AVT.
- */
- /* TODO: check attr acquisition wrt to the XSLT namespace */
- tmpNsName = xsltEvalAttrValueTemplate(ctxt, inst,
- (const xmlChar *) "namespace", XSLT_NAMESPACE);
- /*
- * This fixes bug #302020: The AVT might also evaluate to the
- * empty string; this means that the empty string also indicates
- * "no namespace".
- * SPEC XSLT 1.0:
- * "If the string is empty, then the expanded-name of the
- * attribute has a null namespace URI."
- */
- if ((tmpNsName != NULL) && (tmpNsName[0] != 0))
- nsName = xmlDictLookup(ctxt->dict, BAD_CAST tmpNsName, -1);
- xmlFree(tmpNsName);
- }
- if (xmlStrEqual(nsName, BAD_CAST "http://www.w3.org/2000/xmlns/")) {
- xsltTransformError(ctxt, NULL, inst,
- "xsl:attribute: Namespace http://www.w3.org/2000/xmlns/ "
- "forbidden.\n");
- goto error;
- }
- if (xmlStrEqual(nsName, XML_XML_NAMESPACE)) {
- prefix = BAD_CAST "xml";
- } else if (xmlStrEqual(prefix, BAD_CAST "xml")) {
- prefix = NULL;
- }
- } else if (prefix != NULL) {
- /*
- * SPEC XSLT 1.0:
- * "If the namespace attribute is not present, then the QName is
- * expanded into an expanded-name using the namespace declarations
- * in effect for the xsl:attribute element, *not* including any
- * default namespace declaration."
- */
- ns = xmlSearchNs(inst->doc, inst, prefix);
- if (ns == NULL) {
- /*
- * Note that this is treated as an error now (checked with
- * Saxon, Xalan-J and MSXML).
- */
- xsltTransformError(ctxt, NULL, inst,
- "xsl:attribute: The QName '%s:%s' has no "
- "namespace binding in scope in the stylesheet; "
- "this is an error, since the namespace was not "
- "specified by the instruction itself.\n", prefix, name);
- } else
- nsName = ns->href;
- }
- /*
- * Find/create a matching ns-decl in the result tree.
- */
- ns = NULL;
- #if 0
- if (0) {
- /*
- * OPTIMIZE TODO: How do we know if we are adding to a
- * fragment or to the result tree?
- *
- * If we are adding to a result tree fragment (i.e., not to the
- * actual result tree), we'll don't bother searching for the
- * ns-decl, but just store it in the dummy-doc of the result
- * tree fragment.
- */
- if (nsName != NULL) {
- /*
- * TODO: Get the doc of @targetElem.
- */
- ns = xsltTreeAcquireStoredNs(some doc, nsName, prefix);
- }
- }
- #endif
- if (nsName != NULL) {
- /*
- * Something about ns-prefixes:
- * SPEC XSLT 1.0:
- * "XSLT processors may make use of the prefix of the QName specified
- * in the name attribute when selecting the prefix used for outputting
- * the created attribute as XML; however, they are not required to do
- * so and, if the prefix is xmlns, they must not do so"
- */
- /*
- * xsl:attribute can produce a scenario where the prefix is NULL,
- * so generate a prefix.
- */
- if ((prefix == NULL) || xmlStrEqual(prefix, BAD_CAST "xmlns")) {
- xmlChar *pref = xmlStrdup(BAD_CAST "ns_1");
- ns = xsltGetSpecialNamespace(ctxt, inst, nsName, pref, targetElem);
- xmlFree(pref);
- } else {
- ns = xsltGetSpecialNamespace(ctxt, inst, nsName, prefix,
- targetElem);
- }
- if (ns == NULL) {
- xsltTransformError(ctxt, NULL, inst,
- "Namespace fixup error: Failed to acquire an in-scope "
- "namespace binding for the generated attribute '{%s}%s'.\n",
- nsName, name);
- goto error;
- }
- }
- /*
- * Construction of the value
- * -------------------------
- */
- if (inst->children == NULL) {
- /*
- * No content.
- * TODO: Do we need to put the empty string in ?
- */
- attr = xmlSetNsProp(ctxt->insert, ns, name, (const xmlChar *) "");
- } else if ((inst->children->next == NULL) &&
- ((inst->children->type == XML_TEXT_NODE) ||
- (inst->children->type == XML_CDATA_SECTION_NODE)))
- {
- xmlNodePtr copyTxt;
- /*
- * xmlSetNsProp() will take care of duplicates.
- */
- attr = xmlSetNsProp(ctxt->insert, ns, name, NULL);
- if (attr == NULL) /* TODO: report error ? */
- goto error;
- /*
- * This was taken over from xsltCopyText() (transform.c).
- */
- if (ctxt->internalized &&
- (ctxt->insert->doc != NULL) &&
- (ctxt->insert->doc->dict == ctxt->dict))
- {
- copyTxt = xmlNewText(NULL);
- if (copyTxt == NULL) /* TODO: report error */
- goto error;
- /*
- * This is a safe scenario where we don't need to lookup
- * the dict.
- */
- copyTxt->content = inst->children->content;
- /*
- * Copy "disable-output-escaping" information.
- * TODO: Does this have any effect for attribute values
- * anyway?
- */
- if (inst->children->name == xmlStringTextNoenc)
- copyTxt->name = xmlStringTextNoenc;
- } else {
- /*
- * Copy the value.
- */
- copyTxt = xmlNewText(inst->children->content);
- if (copyTxt == NULL) /* TODO: report error */
- goto error;
- }
- attr->children = attr->last = copyTxt;
- copyTxt->parent = (xmlNodePtr) attr;
- copyTxt->doc = attr->doc;
- /*
- * Copy "disable-output-escaping" information.
- * TODO: Does this have any effect for attribute values
- * anyway?
- */
- if (inst->children->name == xmlStringTextNoenc)
- copyTxt->name = xmlStringTextNoenc;
- /*
- * since we create the attribute without content IDness must be
- * asserted as a second step
- */
- if ((copyTxt->content != NULL) &&
- (xmlIsID(attr->doc, attr->parent, attr)))
- xmlAddID(NULL, attr->doc, copyTxt->content, attr);
- } else {
- /*
- * The sequence constructor might be complex, so instantiate it.
- */
- value = xsltEvalTemplateString(ctxt, contextNode, inst);
- if (value != NULL) {
- attr = xmlSetNsProp(ctxt->insert, ns, name, value);
- xmlFree(value);
- } else {
- /*
- * TODO: Do we have to add the empty string to the attr?
- * TODO: Does a value of NULL indicate an
- * error in xsltEvalTemplateString() ?
- */
- attr = xmlSetNsProp(ctxt->insert, ns, name,
- (const xmlChar *) "");
- }
- }
- error:
- return;
- }
- /**
- * xsltApplyAttributeSet:
- * @ctxt: the XSLT stylesheet
- * @node: the node in the source tree.
- * @inst: the attribute node "xsl:use-attribute-sets"
- * @attrSets: the list of QNames of the attribute-sets to be applied
- *
- * Apply the xsl:use-attribute-sets.
- * If @attrSets is NULL, then @inst will be used to exctract this
- * value.
- * If both, @attrSets and @inst, are NULL, then this will do nothing.
- */
- void
- xsltApplyAttributeSet(xsltTransformContextPtr ctxt, xmlNodePtr node,
- xmlNodePtr inst,
- const xmlChar *attrSets)
- {
- const xmlChar *ncname = NULL;
- const xmlChar *prefix = NULL;
- const xmlChar *curstr, *endstr;
- xsltAttrSetPtr set;
- xsltStylesheetPtr style;
- if (attrSets == NULL) {
- if (inst == NULL)
- return;
- else {
- /*
- * Extract the value from @inst.
- */
- if (inst->type == XML_ATTRIBUTE_NODE) {
- if ( ((xmlAttrPtr) inst)->children != NULL)
- attrSets = ((xmlAttrPtr) inst)->children->content;
- }
- if (attrSets == NULL) {
- /*
- * TODO: Return an error?
- */
- return;
- }
- }
- }
- /*
- * Parse/apply the list of QNames.
- */
- curstr = attrSets;
- while (*curstr != 0) {
- while (IS_BLANK(*curstr))
- curstr++;
- if (*curstr == 0)
- break;
- endstr = curstr;
- while ((*endstr != 0) && (!IS_BLANK(*endstr)))
- endstr++;
- curstr = xmlDictLookup(ctxt->dict, curstr, endstr - curstr);
- if (curstr) {
- xmlNsPtr ns;
- const xmlChar *nsUri = NULL;
- #ifdef WITH_XSLT_DEBUG_ATTRIBUTES
- xsltGenericDebug(xsltGenericDebugContext,
- "apply attribute set %s\n", curstr);
- #endif
- if (xmlValidateQName(curstr, 0)) {
- xsltTransformError(ctxt, NULL, inst,
- "The name '%s' in use-attribute-sets is not a valid "
- "QName.\n", curstr);
- return;
- }
- ncname = xsltSplitQName(ctxt->dict, curstr, &prefix);
- if (prefix != NULL) {
- ns = xmlSearchNs(inst->doc, inst, prefix);
- if (ns == NULL) {
- xsltTransformError(ctxt, NULL, inst,
- "use-attribute-set : No namespace found for QName "
- "'%s:%s'\n", prefix, ncname);
- return;
- }
- nsUri = ns->href;
- }
- style = ctxt->style;
- #ifdef WITH_DEBUGGER
- if ((style != NULL) &&
- (style->attributeSets != NULL) &&
- (ctxt->debugStatus != XSLT_DEBUG_NONE))
- {
- set = xmlHashLookup2(style->attributeSets, ncname, nsUri);
- if ((set != NULL) && (set->attrs != NULL) &&
- (set->attrs->attr != NULL))
- xslHandleDebugger(set->attrs->attr->parent, node, NULL,
- ctxt);
- }
- #endif
- /*
- * Lookup the referenced attribute-set. All attribute sets were
- * moved to the top stylesheet so there's no need to iterate
- * imported stylesheets
- */
- set = xmlHashLookup2(style->attributeSets, ncname, nsUri);
- if (set != NULL) {
- xsltAttrElemPtr cur = set->attrs;
- while (cur != NULL) {
- if (cur->attr != NULL) {
- xsltAttribute(ctxt, node, cur->attr,
- cur->attr->psvi);
- }
- cur = cur->next;
- }
- }
- }
- curstr = endstr;
- }
- }
- static void
- xsltFreeAttributeSetsEntry(void *payload,
- const xmlChar *name ATTRIBUTE_UNUSED) {
- xsltFreeAttrSet((xsltAttrSetPtr) payload);
- }
- /**
- * xsltFreeAttributeSetsHashes:
- * @style: an XSLT stylesheet
- *
- * Free up the memory used by attribute sets
- */
- void
- xsltFreeAttributeSetsHashes(xsltStylesheetPtr style) {
- if (style->attributeSets != NULL)
- xmlHashFree((xmlHashTablePtr) style->attributeSets,
- xsltFreeAttributeSetsEntry);
- style->attributeSets = NULL;
- }
|