1
0
mirror of https://gitlab.gnome.org/GNOME/libxslt synced 2025-11-08 11:02:18 +03:00
Files
libxslt/libxslt/attributes.c
Daniel Veillard f73f508250 fixed bug #59757 on inheritance of attributes from multiple
* libxslt/attributes.c libxslt/transform.c: fixed bug #59757
  on inheritance of attributes from multiple attributes-sets
Daniel
2001-09-10 17:43:00 +00:00

589 lines
14 KiB
C

/*
* 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
*/
#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 "xslt.h"
#include "xsltInternals.h"
#include "xsltutils.h"
#include "attributes.h"
#include "namespaces.h"
#include "templates.h"
#include "imports.h"
#include "transform.h"
#ifdef WITH_XSLT_DEBUG
#define WITH_XSLT_DEBUG_ATTRIBUTES
#endif
/*
* TODO: merge attribute sets from different import precedence.
* all this should be precomputed just before the transformation
* starts or at first hit with a cache in the context.
* The simple way for now would be to not allow redefinition of
* attributes once generated in the output tree, possibly costlier.
*/
/*
* Useful macros
*/
#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)))
/*
* 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 */
};
/************************************************************************
* *
* 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) {
memset(attr, -1, sizeof(xsltAttrElem));
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 (cur->attr == attr)
return(cur);
if (cur->next == NULL) {
cur->next = xsltNewAttrElem(attr);
}
cur = next;
}
return(list);
}
/**
* xsltMergeAttrElemList:
* @list: an XSLT AttrElem list
* @old: another XSLT AttrElem list
*
* Add all the attributes from list @old to list @list,
* but drop redefinition of existing values.
*
* Returns the new list pointer
*/
static xsltAttrElemPtr
xsltMergeAttrElemList(xsltAttrElemPtr list, xsltAttrElemPtr old) {
xsltAttrElemPtr cur;
int add;
while (old != NULL) {
/*
* Check that the attribute is not yet in the list
*/
cur = list;
add = 1;
while (cur != NULL) {
if (cur->attr == old->attr) {
xsltGenericError(xsltGenericErrorContext,
"xsl:attribute-set : use-attribute-sets recursion detected\n");
return(list);
}
if (xmlStrEqual(cur->attr->name, old->attr->name)) {
if (cur->attr->ns == old->attr->ns) {
add = 0;
break;
}
if ((cur->attr->ns != NULL) && (old->attr->ns != NULL) &&
(xmlStrEqual(cur->attr->ns->href, old->attr->ns->href))) {
add = 0;
break;
}
}
if (cur->next == NULL)
break;
cur = cur->next;
}
if (cur == NULL) {
list = xsltNewAttrElem(old->attr);
} else if (add) {
cur->next = xsltNewAttrElem(old->attr);
}
old = old->next;
}
return(list);
}
/************************************************************************
* *
* Module interfaces *
* *
************************************************************************/
/**
* xsltParseStylesheetAttributeSet:
* @style: the XSLT stylesheet
* @template: the "preserve-space" element
*
* parse an XSLT stylesheet preserve-space element and record
* elements needing preserving
*/
void
xsltParseStylesheetAttributeSet(xsltStylesheetPtr style, xmlNodePtr cur) {
xmlChar *prop = NULL;
xmlChar *ncname = NULL;
xmlChar *prefix = NULL;
xmlChar *attributes;
xmlChar *attribute, *end;
xmlNodePtr list;
xsltAttrElemPtr values;
if ((cur == NULL) || (style == NULL))
return;
prop = xsltGetNsProp(cur, (const xmlChar *)"name", XSLT_NAMESPACE);
if (prop == NULL) {
xsltGenericError(xsltGenericErrorContext,
"xsl:attribute-set : name is missing\n");
goto error;
}
ncname = xmlSplitQName2(prop, &prefix);
if (ncname == NULL) {
ncname = prop;
prop = NULL;
prefix = NULL;
}
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)
goto error;
values = xmlHashLookup2(style->attributeSets, ncname, prefix);
/*
* check the children list
*/
list = cur->children;
while (list != NULL) {
if (IS_XSLT_ELEM(list)) {
if (!IS_XSLT_NAME(list, "attribute")) {
xsltGenericError(xsltGenericErrorContext,
"xsl:attribute-set : unexpected child xsl:%s\n",
list->name);
} else {
#ifdef WITH_XSLT_DEBUG_ATTRIBUTES
xsltGenericDebug(xsltGenericDebugContext,
"add attribute to list %s\n", ncname);
#endif
values = xsltAddAttrElemList(values, list);
}
} else {
xsltGenericError(xsltGenericErrorContext,
"xsl:attribute-set : unexpected child %s\n", list->name);
}
list = list->next;
}
/*
* Check a possible use-attribute-sets definition
*/
/* TODO check recursion */
attributes = xsltGetNsProp(cur, (const xmlChar *)"use-attribute-sets",
XSLT_NAMESPACE);
if (attributes == NULL) {
goto done;
}
attribute = attributes;
while (*attribute != 0) {
while (IS_BLANK(*attribute)) attribute++;
if (*attribute == 0)
break;
end = attribute;
while ((*end != 0) && (!IS_BLANK(*end))) end++;
attribute = xmlStrndup(attribute, end - attribute);
if (attribute) {
xmlChar *ncname2 = NULL;
xmlChar *prefix2 = NULL;
xsltAttrElemPtr values2;
#ifdef WITH_XSLT_DEBUG_ATTRIBUTES
xsltGenericDebug(xsltGenericDebugContext,
"xsl:attribute-set : %s adds use %s\n", ncname, attribute);
#endif
ncname2 = xmlSplitQName2(attribute, &prefix2);
if (ncname2 == NULL) {
ncname2 = attribute;
attribute = NULL;
prefix = NULL;
}
values2 = xmlHashLookup2(style->attributeSets, ncname2, prefix2);
values = xsltMergeAttrElemList(values, values2);
if (attribute != NULL)
xmlFree(attribute);
if (ncname2 != NULL)
xmlFree(ncname2);
if (prefix2 != NULL)
xmlFree(prefix2);
}
attribute = end;
}
xmlFree(attributes);
done:
/*
* Update the value
*/
xmlHashUpdateEntry2(style->attributeSets, ncname, prefix, values, NULL);
#ifdef WITH_XSLT_DEBUG_ATTRIBUTES
xsltGenericDebug(xsltGenericDebugContext,
"updated attribute list %s\n", ncname);
#endif
error:
if (prop != NULL)
xmlFree(prop);
if (ncname != NULL)
xmlFree(ncname);
if (prefix != NULL)
xmlFree(prefix);
}
/**
* xsltAttributeInternal:
* @ctxt: a XSLT process context
* @node: the node in the source tree.
* @inst: the xslt attribute node
* @comp: precomputed information
* @fromset: the attribute comes from an attribute-set
*
* Process the xslt attribute node on the source node
*/
static void
xsltAttributeInternal(xsltTransformContextPtr ctxt, xmlNodePtr node,
xmlNodePtr inst, xsltStylePreCompPtr comp, int fromset) {
xmlChar *prop = NULL;
xmlChar *ncname = NULL, *name, *namespace;
xmlChar *prefix = NULL;
xmlChar *value = NULL;
xmlNsPtr ns = NULL;
xmlAttrPtr attr;
const xmlChar *URL = NULL;
if (ctxt->insert == NULL)
return;
if (comp == NULL) {
xsltPrintErrorContext(ctxt, NULL, inst);
xsltGenericError(xsltGenericErrorContext,
"xsl:attribute : compilation failed\n");
return;
}
if ((ctxt == NULL) || (node == NULL) || (inst == NULL) || (comp == NULL))
return;
if (!comp->has_name) {
return;
}
if (ctxt->insert->children != NULL) {
xsltPrintErrorContext(ctxt, NULL, inst);
xsltGenericError(xsltGenericErrorContext,
"xsl:attribute : node already has children\n");
return;
}
if (comp->name == NULL) {
prop = xsltEvalAttrValueTemplate(ctxt, inst, (const xmlChar *)"name",
XSLT_NAMESPACE);
if (prop == NULL) {
xsltPrintErrorContext(ctxt, NULL, inst);
xsltGenericError(xsltGenericErrorContext,
"xsl:attribute : name is missing\n");
goto error;
}
name = prop;
} else {
name = comp->name;
}
ncname = xmlSplitQName2(name, &prefix);
if (ncname == NULL) {
prefix = NULL;
} else {
name = ncname;
}
if (!xmlStrncasecmp(prefix, (xmlChar *)"xml", 3)) {
#ifdef WITH_XSLT_DEBUG_PARSING
xsltGenericDebug(xsltGenericDebugContext,
"xsltAttribute: xml prefix forbidden\n");
#endif
goto error;
}
if ((comp->ns == NULL) && (comp->has_ns)) {
namespace = xsltEvalAttrValueTemplate(ctxt, inst,
(const xmlChar *)"namespace", XSLT_NAMESPACE);
if (namespace != NULL) {
ns = xsltGetSpecialNamespace(ctxt, inst, namespace, prefix,
ctxt->insert);
xmlFree(namespace);
} else {
if (prefix != NULL) {
ns = xmlSearchNs(inst->doc, inst, prefix);
if (ns == NULL) {
xsltPrintErrorContext(ctxt, NULL, inst);
xsltGenericError(xsltGenericErrorContext,
"xsl:attribute : no namespace bound to prefix %s\n", prefix);
} else {
ns = xsltGetNamespace(ctxt, inst, ns, ctxt->insert);
}
}
}
} else if (comp->ns != NULL) {
ns = xsltGetSpecialNamespace(ctxt, inst, comp->ns, prefix,
ctxt->insert);
}
if ((fromset) && (ns != NULL))
URL = ns->href;
if ((fromset == 0) || (!xmlHasNsProp(ctxt->insert, name, URL))) {
value = xsltEvalTemplateString(ctxt, node, inst);
if (value == NULL) {
if (ns) {
attr = xmlSetNsProp(ctxt->insert, ns, name,
(const xmlChar *)"");
} else {
attr = xmlSetProp(ctxt->insert, name, (const xmlChar *)"");
}
} else {
if (ns) {
attr = xmlSetNsProp(ctxt->insert, ns, name, value);
} else {
attr = xmlSetProp(ctxt->insert, name, value);
}
}
}
error:
if (prop != NULL)
xmlFree(prop);
if (ncname != NULL)
xmlFree(ncname);
if (prefix != NULL)
xmlFree(prefix);
if (value != NULL)
xmlFree(value);
}
/**
* xsltAttribute:
* @ctxt: a XSLT process context
* @node: the node in the source tree.
* @inst: the xslt attribute node
* @comp: precomputed information
*
* Process the xslt attribute node on the source node
*/
void
xsltAttribute(xsltTransformContextPtr ctxt, xmlNodePtr node,
xmlNodePtr inst, xsltStylePreCompPtr comp) {
xsltAttributeInternal(ctxt, node, inst, comp, 0);
}
/**
* xsltApplyAttributeSet:
* @ctxt: the XSLT stylesheet
* @node: the node in the source tree.
* @inst: the xslt attribute node
* @attributes: the set list.
*
* Apply the xsl:use-attribute-sets
*/
void
xsltApplyAttributeSet(xsltTransformContextPtr ctxt, xmlNodePtr node,
xmlNodePtr inst ATTRIBUTE_UNUSED, xmlChar *attributes) {
xmlChar *ncname = NULL;
xmlChar *prefix = NULL;
xmlChar *attribute, *end;
xsltAttrElemPtr values;
xsltStylesheetPtr style;
if (attributes == NULL) {
return;
}
attribute = attributes;
while (*attribute != 0) {
while (IS_BLANK(*attribute)) attribute++;
if (*attribute == 0)
break;
end = attribute;
while ((*end != 0) && (!IS_BLANK(*end))) end++;
attribute = xmlStrndup(attribute, end - attribute);
if (attribute) {
#ifdef WITH_XSLT_DEBUG_ATTRIBUTES
xsltGenericDebug(xsltGenericDebugContext,
"apply attribute set %s\n", attribute);
#endif
ncname = xmlSplitQName2(attribute, &prefix);
if (ncname == NULL) {
ncname = attribute;
attribute = NULL;
prefix = NULL;
}
style = ctxt->style;
while (style != NULL) {
values = xmlHashLookup2(style->attributeSets, ncname, prefix);
while (values != NULL) {
if (values->attr != NULL) {
xsltAttributeInternal(ctxt, node, values->attr,
values->attr->_private, 1);
}
values = values->next;
}
style = xsltNextImport(style);
}
if (attribute != NULL)
xmlFree(attribute);
if (ncname != NULL)
xmlFree(ncname);
if (prefix != NULL)
xmlFree(prefix);
}
attribute = end;
}
}
/**
* 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,
(xmlHashDeallocator) xsltFreeAttrElemList);
style->attributeSets = NULL;
}