/* * namespaces.c: Implementation of the XSLT namespaces 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 #ifdef HAVE_SYS_TYPES_H #include #endif #ifdef HAVE_MATH_H #include #endif #ifdef HAVE_FLOAT_H #include #endif #ifdef HAVE_IEEEFP_H #include #endif #ifdef HAVE_NAN_H #include #endif #ifdef HAVE_CTYPE_H #include #endif #include #include #include #include #include #include "xslt.h" #include "xsltInternals.h" #include "xsltutils.h" #include "namespaces.h" #include "imports.h" /************************************************************************ * * * Module interfaces * * * ************************************************************************/ /** * xsltNamespaceAlias: * @style: the XSLT stylesheet * @node: the xsl:namespace-alias node * * Read the stylesheet-prefix and result-prefix attributes, register * them as well as the corresponding namespace. */ void xsltNamespaceAlias(xsltStylesheetPtr style, xmlNodePtr node) { xmlChar *sprefix; xmlNsPtr sNs; xmlChar *rprefix; xmlNsPtr rNs; sprefix = xsltGetNsProp(node, (const xmlChar *)"stylesheet-prefix", XSLT_NAMESPACE); if (sprefix == NULL) { xsltTransformError(NULL, style, node, "namespace-alias: stylesheet-prefix attribute missing\n"); return; } rprefix = xsltGetNsProp(node, (const xmlChar *)"result-prefix", XSLT_NAMESPACE); if (rprefix == NULL) { xsltTransformError(NULL, style, node, "namespace-alias: result-prefix attribute missing\n"); goto error; } if (xmlStrEqual(sprefix, (const xmlChar *)"#default")) { sNs = xmlSearchNs(node->doc, node, NULL); } else { sNs = xmlSearchNs(node->doc, node, sprefix); } if ((sNs == NULL) || (sNs->href == NULL)) { xsltTransformError(NULL, style, node, "namespace-alias: prefix %s not bound to any namespace\n", sprefix); goto error; } if (xmlStrEqual(rprefix, (const xmlChar *)"#default")) { rNs = xmlSearchNs(node->doc, node, NULL); } else { rNs = xmlSearchNs(node->doc, node, rprefix); } if ((rNs == NULL) || (rNs->href == NULL)) { xsltTransformError(NULL, style, node, "namespace-alias: prefix %s not bound to any namespace\n", rprefix); goto error; } if (style->nsAliases == NULL) style->nsAliases = xmlHashCreate(10); if (style->nsAliases == NULL) { xsltTransformError(NULL, style, node, "namespace-alias: cannot create hash table\n"); goto error; } xmlHashAddEntry((xmlHashTablePtr) style->nsAliases, sNs->href, (void *) rNs->href); error: if (sprefix != NULL) xmlFree(sprefix); if (rprefix != NULL) xmlFree(rprefix); } /** * xsltGetSpecialNamespace: * @ctxt: a transformation context * @cur: the input node * @URI: the namespace URI * @prefix: the suggested prefix * @out: the output node (or its parent) * * Find the right namespace value for this URI, if needed create * and add a new namespace decalaration on the node * * Returns the namespace node to use or NULL */ xmlNsPtr xsltGetSpecialNamespace(xsltTransformContextPtr ctxt, xmlNodePtr cur, const xmlChar *URI, const xmlChar *prefix, xmlNodePtr out) { xmlNsPtr ret; static int prefixno = 1; char nprefix[10]; if ((ctxt == NULL) || (cur == NULL) || (out == NULL) || (URI == NULL)) return(NULL); if ((prefix == NULL) && (URI[0] == 0)) { ret = xmlSearchNs(out->doc, out, NULL); if (ret != NULL) { ret = xmlNewNs(out, URI, prefix); return(ret); } return(NULL); } if ((out->parent != NULL) && (out->parent->type == XML_ELEMENT_NODE) && (out->parent->ns != NULL) && (xmlStrEqual(out->parent->ns->href, URI))) ret = out->parent->ns; else ret = xmlSearchNsByHref(out->doc, out, URI); if ((ret == NULL) || (ret->prefix == NULL)) { if (prefix == NULL) { do { sprintf(nprefix, "ns%d", prefixno++); ret = xmlSearchNs(out->doc, out, (xmlChar *)nprefix); } while (ret != NULL); prefix = (const xmlChar *) &nprefix[0]; } if (out->type == XML_ELEMENT_NODE) ret = xmlNewNs(out, URI, prefix); } return(ret); } /** * xsltGetNamespace: * @ctxt: a transformation context * @cur: the input node * @ns: the namespace * @out: the output node (or its parent) * * Find the right namespace value for this prefix, if needed create * and add a new namespace decalaration on the node * Handle namespace aliases * * Returns the namespace node to use or NULL */ xmlNsPtr xsltGetNamespace(xsltTransformContextPtr ctxt, xmlNodePtr cur, xmlNsPtr ns, xmlNodePtr out) { xsltStylesheetPtr style; xmlNsPtr ret; const xmlChar *URI = NULL; /* the replacement URI */ if ((ctxt == NULL) || (cur == NULL) || (out == NULL) || (ns == NULL)) return(NULL); style = ctxt->style; while (style != NULL) { if (style->nsAliases != NULL) URI = (const xmlChar *) xmlHashLookup(style->nsAliases, ns->href); if (URI != NULL) break; style = xsltNextImport(style); } if (URI == NULL) URI = ns->href; if ((out->parent != NULL) && (out->parent->type == XML_ELEMENT_NODE) && (out->parent->ns != NULL) && (xmlStrEqual(out->parent->ns->href, URI))) ret = out->parent->ns; else { if (ns->prefix != NULL) { ret = xmlSearchNs(out->doc, out, ns->prefix); if ((ret == NULL) || (!xmlStrEqual(ns->href, URI))) { ret = xmlSearchNsByHref(out->doc, out, URI); } } else { ret = xmlSearchNsByHref(out->doc, out, URI); } } if (ret == NULL) { if (out->type == XML_ELEMENT_NODE) ret = xmlNewNs(out, URI, ns->prefix); } return(ret); } /** * xsltCopyNamespaceList: * @ctxt: a transformation context * @node: the target node * @cur: the first namespace * * Do a copy of an namespace list. If @node is non-NULL the * new namespaces are added automatically. This handles namespaces * aliases * * Returns: a new xmlNsPtr, or NULL in case of error. */ xmlNsPtr xsltCopyNamespaceList(xsltTransformContextPtr ctxt, xmlNodePtr node, xmlNsPtr cur) { xmlNsPtr ret = NULL, tmp; xmlNsPtr p = NULL,q; const xmlChar *URI; if (cur == NULL) return(NULL); if (cur->type != XML_NAMESPACE_DECL) return(NULL); /* * One can add namespaces only on element nodes */ if ((node != NULL) && (node->type != XML_ELEMENT_NODE)) node = NULL; while (cur != NULL) { if (cur->type != XML_NAMESPACE_DECL) break; /* * Avoid duplicating namespace declrations on the tree */ if ((node != NULL) && (node->ns != NULL) && (xmlStrEqual(node->ns->href, cur->href)) && (xmlStrEqual(node->ns->prefix, cur->prefix))) { cur = cur->next; continue; } tmp = xmlSearchNs(node->doc, node, cur->prefix); if ((tmp != NULL) && (xmlStrEqual(tmp->href, cur->href))) { cur = cur->next; continue; } if (!xmlStrEqual(cur->href, XSLT_NAMESPACE)) { /* TODO apply cascading */ URI = (const xmlChar *) xmlHashLookup(ctxt->style->nsAliases, cur->href); if (URI != NULL) { q = xmlNewNs(node, URI, cur->prefix); } else { q = xmlNewNs(node, cur->href, cur->prefix); } if (p == NULL) { ret = p = q; } else { p->next = q; p = q; } } cur = cur->next; } return(ret); } /** * xsltCopyNamespace: * @ctxt: a transformation context * @node: the target node * @cur: the namespace node * * Do a copy of an namespace node. If @node is non-NULL the * new namespaces are added automatically. This handles namespaces * aliases * * Returns: a new xmlNsPtr, or NULL in case of error. */ xmlNsPtr xsltCopyNamespace(xsltTransformContextPtr ctxt, xmlNodePtr node, xmlNsPtr cur) { xmlNsPtr ret = NULL; const xmlChar *URI; if (cur == NULL) return(NULL); if (cur->type != XML_NAMESPACE_DECL) return(NULL); /* * One can add namespaces only on element nodes */ if ((node != NULL) && (node->type != XML_ELEMENT_NODE)) node = NULL; if (!xmlStrEqual(cur->href, XSLT_NAMESPACE)) { URI = (const xmlChar *) xmlHashLookup(ctxt->style->nsAliases, cur->href); if (URI != NULL) { ret = xmlNewNs(node, URI, cur->prefix); } else { ret = xmlNewNs(node, cur->href, cur->prefix); } } return(ret); } /** * xsltFreeNamespaceAliasHashes: * @style: an XSLT stylesheet * * Free up the memory used by namespaces aliases */ void xsltFreeNamespaceAliasHashes(xsltStylesheetPtr style) { if (style->nsAliases != NULL) xmlHashFree((xmlHashTablePtr) style->nsAliases, NULL); style->nsAliases = NULL; }