From c09974f82f86d2eca4d3788c089eb41a7ad8bb65 Mon Sep 17 00:00:00 2001 From: "Kasimier T. Buchcik" Date: Mon, 19 Jun 2006 17:45:33 +0000 Subject: [PATCH] Merged all the namespace lookup/create/disable functions into * libxslt/attributes.c libxslt/attrvt.c libxslt/namespaces.c libxslt/namespaces.h libxslt/preproc.c libxslt/templates.c libxslt/transform.c libxslt/variables.c libxslt/xslt.c libxslt/xsltInternals.h libxslt/xsltutils.c: Merged all the namespace lookup/create/disable functions into xsltGetSpecialNamespace(). Changed xsltGetNamespace() and xsltGetPlainNamespace() to call xsltGetSpecialNamespace(), but kept the ns-aliasing mechanism; the ns-aliasing needs to be removed when we move to the refactored code, which applies ns-alias only at compilaton time. Refactored xsltElementComp() (preproc.c); enhanced error reports. Fixed: if the "namespace" attribute was not given, then this performed incorrectly only a lookup for a default namespace; i.e., without taking any prefix on the "name" attribute into account. Refactored xsltElement() (transform.c); enhanced error reports. Refactored xsltAttributeComp() (preproc.c). Added namespace lookup as in xsltElementComp(). Enhanced error reports. Refactored xsltAttribute() (transform.c); enhanced error reports. xsltCopyTreeInternal(): eliminated the need to call xmlGetNsList() for every element in the tree; this needs to be done only for the top-most elements. For subsequent elements reconcile only the ns-declarations. Disallowed setting of ns-declarations if children have been already added to an element. Removed ns-aliasing code where necessary. xsltCopyProp(): disallowed setting of attribute nodes if children have been already added to an element. xsltCopy(): removed the incorrect skipping of attributes in the XSLT namespace. Removed the incorrect ns-aliasing for attributes. Changed to use the introduced function xsltShallowCopyAttr(). xsltShallowCopyAttr(): Centralized all attribute-copy related code in this function. It will now be called by xsltCopyTreeInternal(), xsltCopyOf() and xsltCopy(). xsltCopyAttrListNoOverwrite(): Renamed. Refactored. Optimized to use xsltGetSpecialNamespace() and xmlNewDocProp(). Further substitution of various scattered namespace-lookup related code for the use of xsltGetSpecialNamespace(). xsltAttrTemplateProcess(): Refactored. Removed the incorrect processing of attribute-sets. Attribute sets need to be applied before adding any normal attribute of the literal result element; this is now done in xsltAttrListTemplateProcess(). Fixed to ensure that the ns-prefix of the overwriting attribute is used. xsltAttrListTemplateProcess(): Refactored. Moved semantics from xsltAttrTemplateProcess() over to this function in order to optimize processing of multiple attributes. This does not call xsltAttrTemplateProcess() anymore. Fixed: do not exclude the XSLT namespace after ns-aliasing have beed applied. The IFDEFed-out refactored code fixes the following issues: - #313711: namespace collision with namespace-alias (reported by by Oleg Paraschenko) - #338214: Incorrect scope for exclude-result-prefixes - #341392: Excluding namespace declarations of literal result elements. - #341325: Namespace aliasing and resulting namespace prefixes Already enabled fixes: - #344183: xsl:copy misses to copy attributes in the XSLT namespace - #341463: Namespace-alias using #default for result-prefix with no default namespace in scope - #313890: namespace collision with xsl:element and xsl:attribute, reported by Oleg Paraschenko - #344176: xsl:copy misses to set an element's namespace-URI in some cases - #305739: the "name" QName of xsl:element is incorrectly always resolved to the default namespace --- ChangeLog | 68 ++ libxslt/attributes.c | 352 +++++------ libxslt/attrvt.c | 32 +- libxslt/namespaces.c | 711 ++++++++++----------- libxslt/namespaces.h | 4 +- libxslt/preproc.c | 182 +++++- libxslt/templates.c | 410 +++++++++--- libxslt/transform.c | 1600 +++++++++++++++++++++-------------------------- libxslt/variables.c | 4 +- libxslt/xslt.c | 307 +++++---- libxslt/xsltInternals.h | 33 +- libxslt/xsltutils.c | 2 +- 12 files changed, 2005 insertions(+), 1700 deletions(-) diff --git a/ChangeLog b/ChangeLog index fe9ca19..2690b16 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,71 @@ +Mon Jun 19 13:33:50 CEST 2006 Kasimier Buchcik + + * libxslt/attributes.c libxslt/attrvt.c libxslt/namespaces.c + libxslt/namespaces.h libxslt/preproc.c libxslt/templates.c + libxslt/transform.c libxslt/variables.c libxslt/xslt.c + libxslt/xsltInternals.h libxslt/xsltutils.c: + Merged all the namespace lookup/create/disable functions + into xsltGetSpecialNamespace(). Changed xsltGetNamespace() + and xsltGetPlainNamespace() to call xsltGetSpecialNamespace(), + but kept the ns-aliasing mechanism; the ns-aliasing needs + to be removed when we move to the refactored code, which + applies ns-alias only at compilaton time. + Refactored xsltElementComp() (preproc.c); enhanced error reports. + Fixed: if the "namespace" attribute was not given, then this + performed incorrectly only a lookup for a default namespace; + i.e., without taking any prefix on the "name" attribute into + account. + Refactored xsltElement() (transform.c); enhanced error reports. + Refactored xsltAttributeComp() (preproc.c). Added namespace + lookup as in xsltElementComp(). Enhanced error reports. + Refactored xsltAttribute() (transform.c); enhanced error reports. + xsltCopyTreeInternal(): eliminated the need to call xmlGetNsList() + for every element in the tree; this needs to be done only for + the top-most elements. For subsequent elements reconcile only + the ns-declarations. Disallowed setting of ns-declarations if + children have been already added to an element. + Removed ns-aliasing code where necessary. + xsltCopyProp(): disallowed setting of attribute nodes if + children have been already added to an element. + xsltCopy(): removed the incorrect skipping of attributes in the + XSLT namespace. Removed the incorrect ns-aliasing for attributes. + Changed to use the introduced function xsltShallowCopyAttr(). + xsltShallowCopyAttr(): Centralized all attribute-copy related + code in this function. It will now be called by + xsltCopyTreeInternal(), xsltCopyOf() and xsltCopy(). + xsltCopyAttrListNoOverwrite(): Renamed. Refactored. Optimized to + use xsltGetSpecialNamespace() and xmlNewDocProp(). + Further substitution of various scattered namespace-lookup + related code for the use of xsltGetSpecialNamespace(). + xsltAttrTemplateProcess(): Refactored. Removed the incorrect + processing of attribute-sets. Attribute sets need to be applied + before adding any normal attribute of the literal result element; + this is now done in xsltAttrListTemplateProcess(). Fixed to + ensure that the ns-prefix of the overwriting attribute is used. + xsltAttrListTemplateProcess(): Refactored. Moved semantics from + xsltAttrTemplateProcess() over to this function in order to + optimize processing of multiple attributes. This does not call + xsltAttrTemplateProcess() anymore. + Fixed: do not exclude the XSLT namespace after ns-aliasing have + beed applied. + The IFDEFed-out refactored code fixes the following issues: + - #313711: namespace collision with namespace-alias (reported + by by Oleg Paraschenko) + - #338214: Incorrect scope for exclude-result-prefixes + - #341392: Excluding namespace declarations of literal result + elements. + - #341325: Namespace aliasing and resulting namespace prefixes + Already enabled fixes: + - #344183: xsl:copy misses to copy attributes in the XSLT namespace + - #341463: Namespace-alias using #default for result-prefix with no + default namespace in scope + - #313890: namespace collision with xsl:element and xsl:attribute, + reported by Oleg Paraschenko + - #344176: xsl:copy misses to set an element's namespace-URI in + some cases + - #305739: the "name" QName of xsl:element is incorrectly always + resolved to the default namespace + Mon Jun 12 16:34:15 CEST 2006 Daniel Veillard * doc/xsltproc.1 doc/xsltproc.xml: more info about --output diff --git a/libxslt/attributes.c b/libxslt/attributes.c index 2f5d660..46e365c 100644 --- a/libxslt/attributes.c +++ b/libxslt/attributes.c @@ -629,7 +629,7 @@ xsltResolveStylesheetAttributeSet(xsltStylesheetPtr style) { /** * xsltAttributeInternal: * @ctxt: a XSLT process context - * @node: the node in the source tree. + * @node: the current node in the source tree * @inst: the xsl:attribute element * @comp: precomputed information * @fromAttributeSet: the attribute comes from an attribute-set @@ -637,20 +637,15 @@ xsltResolveStylesheetAttributeSet(xsltStylesheetPtr style) { * Process the xslt attribute node on the source node */ static void -xsltAttributeInternal(xsltTransformContextPtr ctxt, xmlNodePtr node, +xsltAttributeInternal(xsltTransformContextPtr ctxt, + xmlNodePtr currentNode, xmlNodePtr inst, xsltStylePreCompPtr castedComp, - int fromAttributeSet) { + int fromAttributeSet) +{ #ifdef XSLT_REFACTORED xsltStyleItemAttributePtr comp = - (xsltStyleItemAttributePtr) castedComp; - /* - * TODO: Change the fields of the compiled struct: - * 1) @name (char) - * 2) @nameType (String, AVT) - * 3) @nsName (char) - * 4) nsNameType (None, String, AVT) - */ + (xsltStyleItemAttributePtr) castedComp; #else xsltStylePreCompPtr comp = castedComp; #endif @@ -661,7 +656,14 @@ xsltAttributeInternal(xsltTransformContextPtr ctxt, xmlNodePtr node, xmlNsPtr ns = NULL; xmlAttrPtr attr; - if ((ctxt == NULL) || (node == NULL) || (inst == NULL)) + if ((ctxt == NULL) || (currentNode == NULL) || (inst == NULL)) + 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 @@ -725,13 +727,11 @@ xsltAttributeInternal(xsltTransformContextPtr ctxt, xmlNodePtr node, /* * Process the name * ---------------- - */ - if (!comp->has_name) /* TODO: raise error */ - return; + */ #ifdef WITH_DEBUGGER if (ctxt->debugStatus != XSLT_DEBUG_NONE) - xslHandleDebugger(inst, node, NULL, ctxt); + xslHandleDebugger(inst, currentNode, NULL, ctxt); #endif if (comp->name == NULL) { @@ -751,34 +751,55 @@ xsltAttributeInternal(xsltTransformContextPtr ctxt, xmlNodePtr node, } name = xsltSplitQName(ctxt->dict, prop, &prefix); xmlFree(prop); - } else { - name = xsltSplitQName(ctxt->dict, comp->name, &prefix); - } - if (!xmlStrncasecmp(prefix, (xmlChar *) "xmlns", 5)) { + /* + * Reject a prefix of "xmlns". + */ + if ((prefix != NULL) && + (!xmlStrncasecmp(prefix, (xmlChar *) "xmlns", 5))) + { #ifdef WITH_XSLT_DEBUG_PARSING - xsltGenericDebug(xsltGenericDebugContext, - "xsltAttribute: xmlns prefix forbidden\n"); + xsltGenericDebug(xsltGenericDebugContext, + "xsltAttribute: xmlns prefix forbidden\n"); #endif + /* + * SPEC XSLT 1.0: + * "It is an error if the string that results from instantiating + * the attribute value template is not a QName or is the string + * xmlns. An XSLT processor may signal the error; if it does not + * signal the error, it must recover by not adding the attribute + * to the result tree." + * TODO: Decide which way to go here. + */ + goto error; + } + + } else { /* - * SPEC XSLT 1.0: - * "It is an error if the string that results from instantiating - * the attribute value template is not a QName or is the string - * xmlns. An XSLT processor may signal the error; if it does not - * signal the error, it must recover by not adding the attribute - * to the result tree." - * TODO: Decide which way to go here. + * The "name" value was static. */ - goto error; +#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) { + 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 { @@ -816,19 +837,21 @@ xsltAttributeInternal(xsltTransformContextPtr ctxt, xmlNodePtr node, * Saxon, Xalan-J and MSXML). */ xsltTransformError(ctxt, NULL, inst, - "xsl:attribute: The effective prefix '%s', has no " + "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); + "specified by the instruction itself.\n", prefix, name); } else nsName = ns->href; } if (fromAttributeSet) { /* - * I think this tries to ensure that xsl:attribute(s) coming + * This tries to ensure that xsl:attribute(s) coming * from an xsl:attribute-set won't override attribute of * literal result elements or of explicit xsl:attribute(s). + * URGENT TODO: This might be buggy, since it will miss to + * overwrite two equal attributes both from attribute sets. */ attr = xmlHasNsProp(targetElem, name, nsName); if (attr != NULL) @@ -836,22 +859,15 @@ xsltAttributeInternal(xsltTransformContextPtr ctxt, xmlNodePtr node, } /* - * 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" - */ - /* * 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 not? + * 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 @@ -864,95 +880,37 @@ xsltAttributeInternal(xsltTransformContextPtr ctxt, xmlNodePtr node, } } #endif - if (nsName != NULL) { - /* - * The owner-element might be in the same namespace. - */ - if ((targetElem->ns != NULL) && - (targetElem->ns->prefix != NULL) && - xmlStrEqual(targetElem->ns->href, nsName)) - { - ns = targetElem->ns; - goto namespace_finished; - } - if (prefix != NULL) { - /* - * Search by ns-prefix. - */ - ns = xmlSearchNs(targetElem->doc, targetElem, prefix); - if ((ns != NULL) && (xmlStrEqual(ns->href, nsName))) { - goto namespace_finished; - } - } - /* - * Fallback to a search by ns-name. - */ - ns = xmlSearchNsByHref(targetElem->doc, targetElem, nsName); - if ((ns != NULL) && (ns->prefix != NULL)) { - goto namespace_finished; - } + + if (nsName != NULL) { /* - * OK, we need to declare the namespace on the target element. + * 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" */ - if (prefix) { - if (targetElem->nsDef != NULL) { - ns = targetElem->nsDef; - do { - if ((ns->prefix) && xmlStrEqual(ns->prefix, prefix)) { - /* - * The prefix is aready occupied. - */ - break; - } - ns = ns->next; - } while (ns != NULL); - if (ns == NULL) { - ns = xmlNewNs(targetElem, nsName, prefix); - goto namespace_finished; - } - } - } /* - * Generate a new prefix. + * xsl:attribute can produce a scenario where the prefix is NULL, + * so generate a prefix. */ - { - const xmlChar *basepref = prefix; - xmlChar pref[30]; - int counter = 1; - - if (prefix != NULL) - basepref = prefix; - else - basepref = xmlStrdup(BAD_CAST "ns"); - - do { - snprintf((char *) pref, 30, "%s%d", - basepref, counter++); - ns = xmlSearchNs(targetElem->doc, targetElem, BAD_CAST pref); - if (counter > 1000) { - xsltTransformError(ctxt, NULL, inst, - "Namespace fixup error: Failed to compute a " - "new unique ns-prefix for the generated attribute " - "{%s}%s'.\n", nsName, name); - ns = NULL; - break; - } - } while (ns != NULL); - if (basepref != prefix) - xmlFree((xmlChar *)basepref); - ns = xmlNewNs(targetElem, nsName, BAD_CAST pref); - } + if (prefix == NULL) { + xmlChar *pref = xmlStrdup(BAD_CAST "ns_1"); -namespace_finished: + ns = xsltGetSpecialNamespace(ctxt, inst, nsName, BAD_CAST 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 of the generated attribute '{%s}%s'.\n", + "namespace binding for the generated attribute '{%s}%s'.\n", nsName, name); - /* - * TODO: Should we just stop here? - */ + goto error; } } /* @@ -969,11 +927,10 @@ namespace_finished: ((inst->children->type == XML_TEXT_NODE) || (inst->children->type == XML_CDATA_SECTION_NODE))) { - xmlNodePtr origTxt = inst->children, copyTxt; + xmlNodePtr copyTxt; + /* - * Optimization: if the content is just 1 text node, then - * just need to copy it over, or just assign it to the result - * if the string is shared. + * xmlSetNsProp() will take care of duplicates. */ attr = xmlSetNsProp(ctxt->insert, ns, name, NULL); if (attr == NULL) /* TODO: report error ? */ @@ -992,44 +949,46 @@ namespace_finished: * This is a safe scenario where we don't need to lookup * the dict. */ - copyTxt->content = origTxt->content; + copyTxt->content = inst->children->content; /* * Copy "disable-output-escaping" information. * TODO: Does this have any effect for attribute values * anyway? */ - if (origTxt->name == xmlStringTextNoenc) + if (inst->children->name == xmlStringTextNoenc) copyTxt->name = xmlStringTextNoenc; } else { /* * Copy the value. */ - copyTxt = xmlNewText(origTxt->content); + copyTxt = xmlNewText(inst->children->content); if (copyTxt == NULL) /* TODO: report error */ - goto error; - /* - * Copy "disable-output-escaping" information. - * TODO: Does this have any effect for attribute values - * anyway? - */ - if (origTxt->name == xmlStringTextNoenc) - copyTxt->name = xmlStringTextNoenc; - } - if (copyTxt != NULL) { - copyTxt->doc = attr->doc; - xmlAddChild((xmlNodePtr) attr, copyTxt); + 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; + } else { /* * The sequence constructor might be complex, so instantiate it. */ - value = xsltEvalTemplateString(ctxt, node, inst); + value = xsltEvalTemplateString(ctxt, currentNode, 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 *) ""); @@ -1059,69 +1018,100 @@ xsltAttribute(xsltTransformContextPtr ctxt, xmlNodePtr node, * xsltApplyAttributeSet: * @ctxt: the XSLT stylesheet * @node: the node in the source tree. - * @inst: the xslt attribute node - * @attributes: the set list. + * @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 + * 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 ATTRIBUTE_UNUSED, - const xmlChar * attributes) + xmlNodePtr inst, + const xmlChar *attrSets) { const xmlChar *ncname = NULL; - const xmlChar *prefix = NULL; - const xmlChar *attrib, *endattr; - xsltAttrElemPtr values; - xsltStylesheetPtr style; + const xmlChar *prefix = NULL; + const xmlChar *curstr, *endstr; + xsltAttrElemPtr attrs; + xsltStylesheetPtr style; - if (attributes == NULL) { - return; + 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; + } + } } - - attrib = attributes; - while (*attrib != 0) { - while (IS_BLANK(*attrib)) - attrib++; - if (*attrib == 0) + /* + * Parse/apply the list of QNames. + */ + curstr = attrSets; + while (*curstr != 0) { + while (IS_BLANK(*curstr)) + curstr++; + if (*curstr == 0) break; - endattr = attrib; - while ((*endattr != 0) && (!IS_BLANK(*endattr))) - endattr++; - attrib = xmlDictLookup(ctxt->dict, attrib, endattr - attrib); - if (attrib) { -#ifdef WITH_XSLT_DEBUG_ATTRIBUTES + endstr = curstr; + while ((*endstr != 0) && (!IS_BLANK(*endstr))) + endstr++; + curstr = xmlDictLookup(ctxt->dict, curstr, endstr - curstr); + if (curstr) { + /* + * TODO: Validate the QName. + */ + +#ifdef WITH_XSLT_DEBUG_curstrUTES xsltGenericDebug(xsltGenericDebugContext, - "apply attribute set %s\n", attrib); + "apply curstrute set %s\n", curstr); #endif - ncname = xsltSplitQName(ctxt->dict, attrib, &prefix); + ncname = xsltSplitQName(ctxt->dict, curstr, &prefix); style = ctxt->style; + #ifdef WITH_DEBUGGER - if ((style != NULL) && (style->attributeSets != NULL) && - (ctxt->debugStatus != XSLT_DEBUG_NONE)) { - values = + if ((style != NULL) && + (style->attributeSets != NULL) && + (ctxt->debugStatus != XSLT_DEBUG_NONE)) + { + attrs = xmlHashLookup2(style->attributeSets, ncname, prefix); - if ((values != NULL) && (values->attr != NULL)) - xslHandleDebugger(values->attr->parent, node, NULL, - ctxt); + if ((attrs != NULL) && (attrs->attr != NULL)) + xslHandleDebugger(attrs->attr->parent, node, NULL, + ctxt); } #endif + /* + * Lookup the referenced curstrute-set. + */ while (style != NULL) { - values = + attrs = xmlHashLookup2(style->attributeSets, ncname, prefix); - while (values != NULL) { - if (values->attr != NULL) { - xsltAttributeInternal(ctxt, node, values->attr, - values->attr->psvi, 1); + while (attrs != NULL) { + if (attrs->attr != NULL) { + xsltAttributeInternal(ctxt, node, attrs->attr, + attrs->attr->psvi, 1); } - values = values->next; + attrs = attrs->next; } style = xsltNextImport(style); } } - attrib = endattr; + curstr = endstr; } } diff --git a/libxslt/attrvt.c b/libxslt/attrvt.c index cafcf74..b63b77b 100644 --- a/libxslt/attrvt.c +++ b/libxslt/attrvt.c @@ -181,7 +181,8 @@ xsltCompileAttr(xsltStylesheetPtr style, xmlAttrPtr attr) { if ((attr->children->type != XML_TEXT_NODE) || (attr->children->next != NULL)) { xsltTransformError(NULL, style, attr->parent, - "Attribute %s content is not a text node\n", attr->name); + "Attribute '%s': The content is expected to be a single text " + "node when compiling an AVT.\n", attr->name); style->errors++; return; } @@ -200,8 +201,12 @@ xsltCompileAttr(xsltStylesheetPtr style, xmlAttrPtr attr) { #endif return; } + /* + * Create a new AVT object. + */ avt = xsltNewAttrVT(style); - if (avt == NULL) return; + if (avt == NULL) + return; attr->psvi = avt; avt->nsList = xmlGetNsList(attr->doc, attr->parent); @@ -223,7 +228,7 @@ xsltCompileAttr(xsltStylesheetPtr style, xmlAttrPtr attr) { } if (*(cur+1) == '}') { /* skip empty AVT */ ret = xmlStrncat(ret, str, cur - str); - cur+=2; + cur += 2; str = cur; continue; } @@ -232,7 +237,7 @@ xsltCompileAttr(xsltStylesheetPtr style, xmlAttrPtr attr) { str = cur; if (avt->nb_seg == 0) avt->strstart = 1; - if ((avt=xsltSetAttrVTsegment(avt, (void *) ret)) == NULL) + if ((avt = xsltSetAttrVTsegment(avt, (void *) ret)) == NULL) goto error; ret = NULL; lastavt = 0; @@ -242,13 +247,17 @@ xsltCompileAttr(xsltStylesheetPtr style, xmlAttrPtr attr) { while ((*cur != 0) && (*cur != '}')) cur++; if (*cur == 0) { xsltTransformError(NULL, style, attr->parent, - "Attribute template %s: unmatched '{'\n", attr->name); + "Attribute '%s': The AVT has an unmatched '{'.\n", + attr->name); style->errors++; goto error; } str++; expr = xmlStrndup(str, cur - str); if (expr == NULL) { + /* + * TODO: What needs to be done here? + */ XSLT_TODO goto error; } else { @@ -257,18 +266,18 @@ xsltCompileAttr(xsltStylesheetPtr style, xmlAttrPtr attr) { comp = xsltXPathCompile(style, expr); if (comp == NULL) { xsltTransformError(NULL, style, attr->parent, - "Attribute template %s: failed to compile %s\n", - attr->name, expr); + "Attribute '%s': Failed to compile the expression " + "'%s' in the AVT.\n", attr->name, expr); style->errors++; goto error; } if (avt->nb_seg == 0) avt->strstart = 0; if (lastavt == 1) { - if ((avt=xsltSetAttrVTsegment(avt, NULL)) == NULL) + if ((avt = xsltSetAttrVTsegment(avt, NULL)) == NULL) goto error; } - if ((avt=xsltSetAttrVTsegment(avt, (void *) comp))==NULL) + if ((avt = xsltSetAttrVTsegment(avt, (void *) comp)) == NULL) goto error; lastavt = 1; xmlFree(expr); @@ -285,7 +294,8 @@ xsltCompileAttr(xsltStylesheetPtr style, xmlAttrPtr attr) { continue; } else { xsltTransformError(NULL, style, attr->parent, - "Attribute template %s: unmatched '}'\n", attr->name); + "Attribute '%s': The AVT has an unmatched '}'.\n", + attr->name); goto error; } } else @@ -296,7 +306,7 @@ xsltCompileAttr(xsltStylesheetPtr style, xmlAttrPtr attr) { str = cur; if (avt->nb_seg == 0) avt->strstart = 1; - if ((avt=xsltSetAttrVTsegment(avt, (void *) ret)) == NULL) + if ((avt = xsltSetAttrVTsegment(avt, (void *) ret)) == NULL) goto error; ret = NULL; } diff --git a/libxslt/namespaces.c b/libxslt/namespaces.c index 8f783bb..0cb302d 100644 --- a/libxslt/namespaces.c +++ b/libxslt/namespaces.c @@ -196,15 +196,15 @@ xsltNamespaceAlias(xsltStylesheetPtr style, xmlNodePtr node) */ - /* - * Store the ns-node in the alias-object. - */ - alias = xsltNewNsAlias(XSLT_CCTXT(style)); - if (alias == NULL) - return; - alias->literalNs = literalNs; - alias->targetNs = targetNs; - XSLT_CCTXT(style)->hasNsAliases = 1; + /* + * Store the ns-node in the alias-object. + */ + alias = xsltNewNsAlias(XSLT_CCTXT(style)); + if (alias == NULL) + return; + alias->literalNs = literalNs; + alias->targetNs = targetNs; + XSLT_CCTXT(style)->hasNsAliases = 1; #else /* XSLT_REFACTORED */ @@ -309,264 +309,294 @@ error: } /** - * xsltNsInScope: - * @doc: the document - * @node: the current node - * @ancestor: the ancestor carrying the namespace - * @prefix: the namespace prefix - * - * Copy of xmlNsInScope which is not public ... - * - * Returns 1 if true, 0 if false and -1 in case of error. - */ -static int -xsltNsInScope(xmlDocPtr doc ATTRIBUTE_UNUSED, xmlNodePtr node, - xmlNodePtr ancestor, const xmlChar * prefix) -{ - xmlNsPtr tst; - - while ((node != NULL) && (node != ancestor)) { - if ((node->type == XML_ENTITY_REF_NODE) || - (node->type == XML_ENTITY_NODE) || - (node->type == XML_ENTITY_DECL)) - return (-1); - if (node->type == XML_ELEMENT_NODE) { - tst = node->nsDef; - while (tst != NULL) { - if ((tst->prefix == NULL) - && (prefix == NULL)) - return (0); - if ((tst->prefix != NULL) - && (prefix != NULL) - && (xmlStrEqual(tst->prefix, prefix))) - return (0); - tst = tst->next; - } - } - node = node->parent; - } - if (node != ancestor) - return (-1); - return (1); -} - -/** - * xsltSearchPlainNsByHref: - * @doc: the document - * @node: the current node - * @href: the namespace value - * - * Search a Ns aliasing a given URI and without a NULL prefix. - * Recurse on the parents until it finds - * the defined namespace or return NULL otherwise. - * Returns the namespace pointer or NULL. - */ -static xmlNsPtr -xsltSearchPlainNsByHref(xmlDocPtr doc, xmlNodePtr node, const xmlChar * href) -{ - xmlNsPtr cur; - xmlNodePtr orig = node; - - if ((node == NULL) || (href == NULL)) - return (NULL); - - while (node != NULL) { - if ((node->type == XML_ENTITY_REF_NODE) || - (node->type == XML_ENTITY_NODE) || - (node->type == XML_ENTITY_DECL)) - return (NULL); - if (node->type == XML_ELEMENT_NODE) { - cur = node->nsDef; - while (cur != NULL) { - if ((cur->href != NULL) && (cur->prefix != NULL) && - (href != NULL) && (xmlStrEqual(cur->href, href))) { - if (xsltNsInScope(doc, orig, node, cur->href) == 1) - return (cur); - } - cur = cur->next; - } - if (orig != node) { - cur = node->ns; - if (cur != NULL) { - if ((cur->href != NULL) && (cur->prefix != NULL) && - (href != NULL) && (xmlStrEqual(cur->href, href))) { - if (xsltNsInScope(doc, orig, node, cur->href) == 1) - return (cur); - } - } - } - } - node = node->parent; - } - return (NULL); -} - -/** - * xsltGetPlainNamespace: - * @ctxt: a transformation context - * @cur: the input node - * @ns: the namespace - * @out: the output node (or its parent) + * xsltGetSpecialNamespace: + * @ctxt: the transformation context + * @invocNode: the invoking node; e.g. a literal result element/attr; + * only used for error reports + * @nsName: the namespace name (or NULL) + * @nsPrefix: the suggested namespace prefix (or NULL) + * @target: the result element on which to anchor a namespace * - * Find the right namespace value for this prefix, if needed create - * and add a new namespace decalaration on the node - * Handle namespace aliases and make sure the prefix is not NULL, this - * is needed for attributes. - * Called from: - * xsltAttrTemplateProcess() (templates.c) - * xsltCopyProp() (transform.c) + * Find a matching (prefix and ns-name) ns-declaration + * for the requested @nsName and @nsPrefix in the result tree. + * If none is found then a new ns-declaration will be + * added to @resultElem. If, in this case, the given prefix is + * already in use, then a ns-declaration with a modified ns-prefix + * be we created. Note that this function's priority is to + * preserve ns-prefixes; it will only change a prefix if there's + * a namespace clash. + * If both @nsName and @nsPrefix are NULL, then this will try to + * "undeclare" a default namespace by declaring an xmlns="". * - * Returns the namespace node to use or NULL + * Returns a namespace declaration or NULL. */ xmlNsPtr -xsltGetPlainNamespace(xsltTransformContextPtr ctxt, xmlNodePtr cur, - xmlNsPtr ns, xmlNodePtr out) { - xmlNsPtr ret; - const xmlChar *URI = NULL; /* the replacement URI */ +xsltGetSpecialNamespace(xsltTransformContextPtr ctxt, xmlNodePtr invocNode, + const xmlChar *nsName, const xmlChar *nsPrefix, + xmlNodePtr target) +{ + xmlNsPtr ns; + int prefixOccupied = 0; - if ((ctxt == NULL) || (cur == NULL) || (out == NULL) || (ns == NULL)) + if ((ctxt == NULL) || (target == NULL) || + (target->type != XML_ELEMENT_NODE)) return(NULL); -#ifdef XSLT_REFACTORED /* - * Namespace exclusion and ns-aliasing is performed at - * compilation-time in the refactored code. + * NOTE: Namespace exclusion and ns-aliasing is performed at + * compilation-time in the refactored code; so this need not be done + * here (it was in the old code). + * NOTE: @invocNode was named @cur in the old code and was documented to + * be an input node; since it was only used to anchor an error report + * somewhere, we can safely change this to @invocNode, which now + * will be the XSLT instruction (also a literal result element/attribute), + * which was responsible for this call. */ - URI = ns->href; -#else - { - xsltStylesheetPtr style; - style = ctxt->style; - while (style != NULL) { - if (style->nsAliases != NULL) - URI = (const xmlChar *) xmlHashLookup(style->nsAliases, ns->href); - if (URI != NULL) - break; + /* + * OPTIMIZE TODO: This all could be optimized by keeping track of + * the ns-decls currently in-scope via a specialized context. + */ + if ((nsPrefix == NULL) && ((nsName == NULL) || (nsName[0] == 0))) { + /* + * NOTE: the "undeclaration" of the default namespace was + * part of the logic of the old xsltGetSpecialNamespace() code, + * so we'll keep that mechanism. + * Related to the old code: bug #302020: + */ + /* + * OPTIMIZE TODO: This all could be optimized by keeping track of + * the ns-decls currently in-scope via a specialized context. + */ + /* + * Search on the result element itself. + */ + if (target->nsDef != NULL) { + ns = target->nsDef; + do { + if (ns->prefix == NULL) { + if ((ns->href != NULL) && (ns->href[0] != 0)) { + /* + * Raise a namespace normalization error. + */ + xsltTransformError(ctxt, NULL, invocNode, + "Namespace normalization error: Cannot undeclare " + "the default namespace, since the default namespace " + "'%s' is already declared on the result element " + "'%s'.\n", ns->href, target->name); + return(NULL); + } else { + /* + * The default namespace was undeclared on the + * result element. + */ + return(NULL); + } + break; + } + ns = ns->next; + } while (ns != NULL); + } + if ((target->parent != NULL) && + (target->parent->type == XML_ELEMENT_NODE)) + { + /* + * The parent element is in no namespace, so assume + * that there is no default namespace in scope. + */ + if (target->parent->ns == NULL) + return(NULL); - style = xsltNextImport(style); + ns = xmlSearchNs(target->doc, target->parent, + NULL); + /* + * Fine if there's no default ns is scope, or if the + * default ns was undeclared. + */ + if ((ns == NULL) || (ns->href == NULL) || (ns->href[0] == 0)) + return(NULL); + + /* + * Undeclare the default namespace. + */ + xmlNewNs(target, BAD_CAST "", NULL); + /* TODO: Check result */ + return(NULL); } + return(NULL); } - - if (URI == UNDEFINED_DEFAULT_NS) { - xmlNsPtr dflt; - dflt = xmlSearchNs(cur->doc, cur, NULL); - if (dflt == NULL) - return NULL; - else - URI = dflt->href; + /* + * Handle the XML namespace. + * QUESTION: Is this faster than using xmlStrEqual() anyway? + */ + if ((nsPrefix != NULL) && + (nsPrefix[0] == 'x') && (nsPrefix[1] == 'm') && + (nsPrefix[2] == 'l') && (nsPrefix[3] == 0)) + { + return(xmlSearchNs(target->doc, target, nsPrefix)); } - - if (URI == NULL) - URI = ns->href; -#endif - - if ((out->parent != NULL) && - (out->parent->type == XML_ELEMENT_NODE) && - (out->parent->ns != NULL) && - (out->parent->ns->prefix != 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(ret->href, URI)) || - (ret->prefix == NULL)) { - ret = xsltSearchPlainNsByHref(out->doc, out, URI); + /* + * First: search on the result element itself. + */ + if (target->nsDef != NULL) { + ns = target->nsDef; + do { + if ((ns->prefix == NULL) == (nsPrefix == NULL)) { + if (ns->prefix == nsPrefix) { + if (xmlStrEqual(ns->href, nsName)) + return(ns); + prefixOccupied = 1; + break; + } else if (xmlStrEqual(ns->prefix, nsPrefix)) { + if (xmlStrEqual(ns->href, nsName)) + return(ns); + prefixOccupied = 1; + break; + } } - } else { - ret = xsltSearchPlainNsByHref(out->doc, out, URI); - } - } - - if (ret == NULL) { - if (out->type == XML_ELEMENT_NODE) - ret = xmlNewNs(out, URI, ns->prefix); + ns = ns->next; + } while (ns != NULL); } - return(ret); -} - -/** - * 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)) { + if (prefixOccupied) { /* - * This tries to "undeclare" a default namespace. - * This fixes a part of bug #302020: - * 1) Added a check whether the queried ns-decl - * is already an "undeclaration" of the default - * namespace. - * 2) This fires an error if the default namespace - * couldn't be "undeclared". + * If the ns-prefix is occupied by an other ns-decl on the + * result element, then this means: + * 1) The desired prefix is shadowed + * 2) There's no way around changing the prefix + * + * Try a desperate search for an in-scope ns-decl + * with a matching ns-name before we use the last option, + * which is to recreate the ns-decl with a modified prefix. */ - ret = xmlSearchNs(out->doc, out, NULL); - if ((ret == NULL) || - (ret->href == NULL) || (ret->href[0] == 0)) - return(ret); - - if (ret != NULL) { - xmlNsPtr newns; - - newns = xmlNewNs(out, URI, prefix); - if (newns == NULL) { - xsltTransformError(ctxt, NULL, cur, - "Namespace fixup error: Failed to undeclare " - "the default namespace '%s'.\n", - ret->href); + ns = xmlSearchNsByHref(target->doc, target, nsName); + if (ns != NULL) + return(ns); + + /* + * Fallback to changing the prefix. + */ + } else if ((target->parent != NULL) && + (target->parent->type == XML_ELEMENT_NODE)) + { + /* + * Try to find a matching ns-decl in the ancestor-axis. + * + * Check the common case: The parent element of the current + * result element is in the same namespace (with an equal ns-prefix). + */ + if ((target->parent->ns != NULL) && + ((target->parent->ns->prefix != NULL) == (nsPrefix != NULL))) + { + ns = target->parent->ns; + + if (nsPrefix == NULL) { + if (xmlStrEqual(ns->href, nsName)) + return(ns); + } else if (xmlStrEqual(ns->prefix, nsPrefix) && + xmlStrEqual(ns->href, nsName)) + { + return(ns); + } + } + /* + * Lookup the remaining in-scope namespaces. + */ + ns = xmlSearchNs(target->doc, target->parent, nsPrefix); + if (ns != NULL) { + if (xmlStrEqual(ns->href, nsName)) + return(ns); + /* + * Now check for a nasty case: We need to ensure that the new + * ns-decl won't shadow a prefix in-use by an existing attribute. + * + * + * + * val-b + * + * + */ + if (target->properties) { + xmlAttrPtr attr = target->properties; + do { + if ((attr->ns) && + xmlStrEqual(attr->ns->prefix, nsPrefix)) + { + /* + * Bad, this prefix is already in use. + * Since we'll change the prefix anyway, try + * a search for a matching ns-decl based on the + * namespace name. + */ + ns = xmlSearchNsByHref(target->doc, target, nsName); + if (ns != NULL) + return(ns); + goto declare_new_prefix; + } + attr = attr->next; + } while (attr != NULL); } + } else { /* - * TODO: Why does this try to return an xmlns="" at all? + * Either no matching ns-prefix was found or the namespace is + * shadowed. + * Create a new ns-decl on the current result element. + * + * Hmm, we could also try to reuse an in-scope + * namespace with a matching ns-name but a different + * ns-prefix. + * What has higher priority? + * 1) If keeping the prefix: create a new ns-decl. + * 2) If reusal: first lookup ns-names; then fallback + * to creation of a new ns-decl. + * REVISIT: this currently uses case 1) although + * the old way was use xmlSearchNsByHref() and to let change + * the prefix. */ - return(newns); +#if 0 + ns = xmlSearchNsByHref(target->doc, target, nsName); + if (ns != NULL) + return(ns); +#endif } - return(NULL); + /* + * Create the ns-decl on the current result element. + */ + ns = xmlNewNs(target, nsName, nsPrefix); + /* TODO: check errors */ + return(ns); + } else { + /* + * This is either the root of the tree or something weird is going on. + */ + ns = xmlNewNs(target, nsName, nsPrefix); + /* TODO: Check result */ + return(ns); } - if ((out->parent != NULL) && - (out->parent->type == XML_ELEMENT_NODE) && - (out->parent->ns != NULL) && - (xmlStrEqual(out->parent->ns->href, URI))) +declare_new_prefix: + /* + * Fallback: we need to generate a new prefix and declare the namespace + * on the result element. + */ { - 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]; - } else if ((ret != NULL) && (ret->prefix == NULL)) { - /* found ns but no prefix - search for the prefix */ - ret = xmlSearchNs(out->doc, out, prefix); - if (ret != NULL) - return(ret); /* found it */ - } - if (out->type == XML_ELEMENT_NODE) - ret = xmlNewNs(out, URI, prefix); + xmlChar pref[30]; + int counter = 1; + + do { + snprintf((char *) pref, 30, "%s_%d", nsPrefix, counter++); + ns = xmlSearchNs(target->doc, target, BAD_CAST pref); + if (counter > 1000) { + xsltTransformError(ctxt, NULL, invocNode, + "Internal error in xsltAcquireResultInScopeNs(): " + "Failed to compute a unique ns-prefix for the " + "generated element"); + return(NULL); + } + } while (ns != NULL); + ns = xmlNewNs(target, nsName, BAD_CAST pref); + /* TODO: Check result */ + return(ns); } - return(ret); + return(NULL); } /** @@ -576,33 +606,47 @@ xsltGetSpecialNamespace(xsltTransformContextPtr ctxt, xmlNodePtr cur, * @ns: the namespace * @out: the output node (or its parent) * - * REFACTORED NOTE: Won't be used anymore in the refactored code - * for literal result elements/attributes. + * Find a matching (prefix and ns-name) ns-declaration + * for the requested @ns->prefix and @ns->href in the result tree. + * If none is found then a new ns-declaration will be + * added to @resultElem. If, in this case, the given prefix is + * already in use, then a ns-declaration with a modified ns-prefix + * be we created. * - * Find the right namespace value for this prefix, if needed create - * and add a new namespace decalaration on the node - * Handle namespace aliases + * Called by: + * - xsltCopyPropList() (*not* anymore) + * - xsltShallowCopyElement() + * - xsltCopyTreeInternal() (*not* anymore) + * - xsltApplyOneTemplateInt() (*not* in the refactored code), + * - xsltElement() (*not* anymore) * - * Returns the namespace node to use or NULL + * Returns a namespace declaration or NULL in case of + * namespace fixup failures or API or internal errors. */ xmlNsPtr xsltGetNamespace(xsltTransformContextPtr ctxt, xmlNodePtr cur, xmlNsPtr ns, - xmlNodePtr out) { - xmlNsPtr ret; - const xmlChar *URI = NULL; /* the replacement URI */ - - if ((ctxt == NULL) || (cur == NULL) || (out == NULL) || (ns == NULL)) - return(NULL); + xmlNodePtr out) +{ + if (ns == NULL) + return(NULL); + #ifdef XSLT_REFACTORED /* * Namespace exclusion and ns-aliasing is performed at - * compilation-time in the refactored code. + * compilation-time in the refactored code. + * Additionally, aliasing is not intended for non Literal + * Result Elements. */ - URI = ns->href; + return(xsltGetSpecialNamespace(ctxt, cur, ns->href, ns->prefix, out)); #else { xsltStylesheetPtr style; + const xmlChar *URI = NULL; /* the replacement URI */ + + if ((ctxt == NULL) || (cur == NULL) || (out == NULL)) + return(NULL); + style = ctxt->style; while (style != NULL) { if (style->nsAliases != NULL) @@ -613,60 +657,50 @@ xsltGetNamespace(xsltTransformContextPtr ctxt, xmlNodePtr cur, xmlNsPtr ns, style = xsltNextImport(style); } - } - - if (URI == UNDEFINED_DEFAULT_NS) { - xmlNsPtr dflt; - /* - */ - dflt = xmlSearchNs(cur->doc, cur, NULL); - if (dflt != NULL) - URI = dflt->href; - else - return NULL; - } else if (URI == NULL) - URI = ns->href; + + + if (URI == UNDEFINED_DEFAULT_NS) { + return(xsltGetSpecialNamespace(ctxt, cur, NULL, NULL, out)); +#if 0 + /* + * TODO: Removed, since wrong. If there was no default + * namespace in the stylesheet then this must resolve to + * the NULL namespace. + */ + xmlNsPtr dflt; + dflt = xmlSearchNs(cur->doc, cur, NULL); + if (dflt != NULL) + URI = dflt->href; + else + return NULL; #endif - /* - * If the parent is an XML_ELEMENT_NODE, and has the "equivalent" - * namespace as ns (either both default, or both with a prefix - * with the same href) then return the parent's ns record - */ - if ((out->parent != NULL) && - (out->parent->type == XML_ELEMENT_NODE) && - (out->parent->ns != NULL) && - (((out->parent->ns->prefix == NULL) && (ns->prefix == NULL)) || - ((out->parent->ns->prefix != NULL) && (ns->prefix != NULL))) && - (xmlStrEqual(out->parent->ns->href, URI))) - ret = out->parent->ns; - else { - /* - * do a standard namespace search for ns in the output doc - */ - ret = xmlSearchNs(out->doc, out, ns->prefix); - if ((ret != NULL) && (!xmlStrEqual(ret->href, URI))) - ret = NULL; + } else if (URI == NULL) + URI = ns->href; - /* - * if the search fails and it's not for the default prefix - * do a search by href - */ - if ((ret == NULL) && (ns->prefix != NULL)) - ret = xmlSearchNsByHref(out->doc, out, URI); + return(xsltGetSpecialNamespace(ctxt, cur, URI, ns->prefix, out)); } +#endif +} - /* - * For an element node, if we don't find it, or it's the default - * and this element already defines a default (bug 165560), we need to - * create it. - */ - if (out->type == XML_ELEMENT_NODE) { - if ((ret == NULL) || ((ret->prefix == NULL) && (out->ns == NULL) && - (out->nsDef != NULL) && (!xmlStrEqual(URI, out->nsDef->href)))) { - ret = xmlNewNs(out, URI, ns->prefix); - } - } - return(ret); +/** + * xsltGetPlainNamespace: + * @ctxt: a transformation context + * @cur: the input node + * @ns: the namespace + * @out: the result element + * + * Obsolete. + * *Not* called by any Libxslt/Libexslt function. + * Exaclty the same as xsltGetNamespace(). + * + * Returns a namespace declaration or NULL in case of + * namespace fixup failures or API or internal errors. + */ +xmlNsPtr +xsltGetPlainNamespace(xsltTransformContextPtr ctxt, xmlNodePtr cur, + xmlNsPtr ns, xmlNodePtr out) +{ + return(xsltGetNamespace(ctxt, cur, ns, out)); } /** @@ -679,12 +713,14 @@ xsltGetNamespace(xsltTransformContextPtr ctxt, xmlNodePtr cur, xmlNsPtr ns, * new namespaces are added automatically. This handles namespaces * aliases. * This function is intended only for *internal* use at - * transformation-time. Use it *only* for copying ns-decls of - * literal result elements. + * transformation-time for copying ns-declarations of Literal + * Result Elements. * * Called by: - * xsltCopyTree() (transform.c) - * xsltCopyNode() (transform.c) + * xsltCopyTreeInternal() (transform.c) + * xsltShallowCopyElem() (transform.c) + * + * REVISIT: This function won't be used in the refactored code. * * Returns: a new xmlNsPtr, or NULL in case of error. */ @@ -739,6 +775,9 @@ xsltCopyNamespaceList(xsltTransformContextPtr ctxt, xmlNodePtr node, p = q; } #else + /* + * TODO: Remove this if the refactored code gets enabled. + */ if (!xmlStrEqual(cur->href, XSLT_NAMESPACE)) { const xmlChar *URI; /* TODO apply cascading */ @@ -767,53 +806,27 @@ xsltCopyNamespaceList(xsltTransformContextPtr ctxt, xmlNodePtr node, /** * xsltCopyNamespace: * @ctxt: a transformation context - * @node: the target node - * @cur: the namespace node + * @elem: the target element node + * @ns: 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 + * Copies a namespace node (declaration). If @elem is not NULL, + * then the new namespace will be declared on @elem. * - * Returns: a new xmlNsPtr, or NULL in case of error. + * Returns: a new xmlNsPtr, or NULL in case of an error. */ xmlNsPtr -xsltCopyNamespace(xsltTransformContextPtr ctxt, xmlNodePtr node, - xmlNsPtr cur) { - xmlNsPtr ret = NULL; - - if (cur == NULL) +xsltCopyNamespace(xsltTransformContextPtr ctxt, xmlNodePtr elem, + xmlNsPtr ns) +{ + if ((ns == NULL) || (ns->type != XML_NAMESPACE_DECL)) 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; - -#ifdef XSLT_REFACTORED - /* - * Namespace exclusion and ns-aliasing is performed at - * compilation-time in the refactored code. - */ - ret = xmlNewNs(node, cur->href, cur->prefix); -#else - if (!xmlStrEqual(cur->href, XSLT_NAMESPACE)) { - const xmlChar *URI; - - URI = (const xmlChar *) xmlHashLookup(ctxt->style->nsAliases, - cur->href); - if (URI == UNDEFINED_DEFAULT_NS) - return(NULL); - if (URI != NULL) { - ret = xmlNewNs(node, URI, cur->prefix); - } else { - ret = xmlNewNs(node, cur->href, cur->prefix); - } - } -#endif - return(ret); + if ((elem != NULL) && (elem->type != XML_ELEMENT_NODE)) + return(xmlNewNs(NULL, ns->href, ns->prefix)); + else + return(xmlNewNs(elem, ns->href, ns->prefix)); } diff --git a/libxslt/namespaces.h b/libxslt/namespaces.h index acc3aa0..4c6f8ab 100644 --- a/libxslt/namespaces.h +++ b/libxslt/namespaces.h @@ -45,8 +45,8 @@ XSLTPUBFUN xmlNsPtr XSLTCALL xmlNodePtr out); XSLTPUBFUN xmlNsPtr XSLTCALL xsltCopyNamespace (xsltTransformContextPtr ctxt, - xmlNodePtr node, - xmlNsPtr cur); + xmlNodePtr elem, + xmlNsPtr ns); XSLTPUBFUN xmlNsPtr XSLTCALL xsltCopyNamespaceList (xsltTransformContextPtr ctxt, xmlNodePtr node, diff --git a/libxslt/preproc.c b/libxslt/preproc.c index c6c020b..344de8c 100644 --- a/libxslt/preproc.c +++ b/libxslt/preproc.c @@ -878,31 +878,85 @@ xsltElementComp(xsltStylesheetPtr style, xmlNodePtr inst) { inst->psvi = comp; comp->inst = inst; + /* + * Attribute "name". + */ + /* + * TODO: Precompile the AVT. See bug #344894. + */ comp->name = xsltEvalStaticAttrValueTemplate(style, inst, - (const xmlChar *)"name", - NULL, &comp->has_name); - if (comp->name != NULL) { - if (xmlValidateQName(comp->name, 0)) { - xsltTransformError(NULL, style, inst, - "xsl:element : invalid name\n"); - if (style != NULL) style->errors++; - } + (const xmlChar *)"name", NULL, &comp->has_name); + if (! comp->has_name) { + xsltTransformError(NULL, style, inst, + "xsl:element: The attribute 'name' is missing.\n"); + style->errors++; + goto error; } + /* + * Attribute "namespace". + */ + /* + * TODO: Precompile the AVT. See bug #344894. + */ comp->ns = xsltEvalStaticAttrValueTemplate(style, inst, - (const xmlChar *)"namespace", - NULL, &comp->has_ns); - if (comp->has_ns == 0) { - xmlNsPtr defaultNs; - - defaultNs = xmlSearchNs(inst->doc, inst, NULL); - if (defaultNs != NULL) { - comp->ns = xmlDictLookup(style->dict, defaultNs->href, -1); - comp->has_ns = 1; + (const xmlChar *)"namespace", NULL, &comp->has_ns); + + if (comp->name != NULL) { + if (xmlValidateQName(comp->name, 0)) { + xsltTransformError(NULL, style, inst, + "xsl:element: The value '%s' of the attribute 'name' is " + "not a valid QName.\n", comp->name); + style->errors++; + } else { + const xmlChar *prefix = NULL, *name; + + name = xsltSplitQName(style->dict, comp->name, &prefix); + if (comp->has_ns == 0) { + xmlNsPtr ns; + + /* + * 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:element element, including any default + * namespace declaration. + */ + ns = xmlSearchNs(inst->doc, inst, prefix); + if (ns != NULL) { + comp->ns = xmlDictLookup(style->dict, ns->href, -1); + comp->has_ns = 1; +#ifdef XSLT_REFACTORED + comp->nsPrefix = prefix; + comp->name = name; +#endif + } else if (prefix != NULL) { + xsltTransformError(NULL, style, inst, + "xsl:element: The prefixed QName '%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", comp->name); + style->errors++; + } + } + if ((prefix != NULL) && + (!xmlStrncasecmp(prefix, (xmlChar *)"xml", 3))) + { + /* + * Mark is to be skipped. + */ + comp->has_name = 0; + } } - } + } + /* + * Attribute "use-attribute-sets", + */ comp->use = xsltEvalStaticAttrValueTemplate(style, inst, (const xmlChar *)"use-attribute-sets", NULL, &comp->has_use); + +error: + return; } /** @@ -931,7 +985,8 @@ xsltAttributeComp(xsltStylesheetPtr style, xmlNodePtr inst) { return; #ifdef XSLT_REFACTORED - comp = (xsltStyleItemAttributePtr) xsltNewStylePreComp(style, XSLT_FUNC_ATTRIBUTE); + comp = (xsltStyleItemAttributePtr) xsltNewStylePreComp(style, + XSLT_FUNC_ATTRIBUTE); #else comp = xsltNewStylePreComp(style, XSLT_FUNC_ATTRIBUTE); #endif @@ -942,22 +997,93 @@ xsltAttributeComp(xsltStylesheetPtr style, xmlNodePtr inst) { comp->inst = inst; /* - * TODO: more computation can be done there, especially namespace lookup - */ + * Attribute "name". + */ + /* + * TODO: Precompile the AVT. See bug #344894. + */ comp->name = xsltEvalStaticAttrValueTemplate(style, inst, (const xmlChar *)"name", NULL, &comp->has_name); + if (! comp->has_name) { + xsltTransformError(NULL, style, inst, + "xsl:attribute: The attribute 'name' is missing.\n"); + style->errors++; + return; + } + /* + * Attribute "namespace". + */ + /* + * TODO: Precompile the AVT. See bug #344894. + */ + comp->ns = xsltEvalStaticAttrValueTemplate(style, inst, + (const xmlChar *)"namespace", + NULL, &comp->has_ns); + if (comp->name != NULL) { if (xmlValidateQName(comp->name, 0)) { xsltTransformError(NULL, style, inst, - "xsl:attribute : invalid QName\n"); - if (style != NULL) style->errors++; - } + "xsl:attribute: The value '%s' of the attribute 'name' is " + "not a valid QName.\n", comp->name); + style->errors++; + } else { + const xmlChar *prefix = NULL, *name; + + name = xsltSplitQName(style->dict, comp->name, &prefix); + if (prefix != NULL) { + if (comp->has_ns == 0) { + xmlNsPtr ns; + + /* + * 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:element + * element, including any default namespace declaration. + */ + ns = xmlSearchNs(inst->doc, inst, prefix); + if (ns != NULL) { + comp->ns = xmlDictLookup(style->dict, ns->href, -1); + comp->has_ns = 1; +#ifdef XSLT_REFACTORED + comp->nsPrefix = prefix; + comp->name = name; +#endif + } else { + xsltTransformError(NULL, style, inst, + "xsl:attribute: The prefixed QName '%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", comp->name); + style->errors++; + } + } + if (!xmlStrncasecmp(prefix, (xmlChar *) "xmlns", 5)) { + /* + * SPEC XSLT 1.0: + * "It is an error if the string that results from + * instantiating the attribute value template is not a + * QName or is the string xmlns. An XSLT processor may + * signal the error; if it does not signal the error, + * it must recover by not adding the attribute to the + * result tree." + * + * Reject a prefix of "xmlns". Mark to be skipped. + */ + comp->has_name = 0; + +#ifdef WITH_XSLT_DEBUG_PARSING + xsltGenericDebug(xsltGenericDebugContext, + "xsltAttribute: xmlns prefix forbidden\n"); +#endif + return; + } + + } + } } - comp->ns = xsltEvalStaticAttrValueTemplate(style, inst, - (const xmlChar *)"namespace", - NULL, &comp->has_ns); - } /** diff --git a/libxslt/templates.c b/libxslt/templates.c index d6dc728..c20e287 100644 --- a/libxslt/templates.c +++ b/libxslt/templates.c @@ -180,10 +180,10 @@ xsltEvalXPathString(xsltTransformContextPtr ctxt, xmlXPathCompExprPtr comp) { /** * xsltEvalTemplateString: * @ctxt: the XSLT transformation context - * @node: the stylesheet node + * @currentNode: the current node in the source tree * @parent: the content parent * - * Evaluate a template string value, i.e. the parent list is interpreter + * Evaluate a template string value, i.e. the parent list is interpreted * as template content and the resulting tree string value is returned * This is needed for example by xsl:comment and xsl:processing-instruction * @@ -191,12 +191,14 @@ xsltEvalXPathString(xsltTransformContextPtr ctxt, xmlXPathCompExprPtr comp) { * caller. */ xmlChar * -xsltEvalTemplateString(xsltTransformContextPtr ctxt, xmlNodePtr node, - xmlNodePtr parent) { +xsltEvalTemplateString(xsltTransformContextPtr ctxt, + xmlNodePtr currentNode, + xmlNodePtr parent) +{ xmlNodePtr oldInsert, insert = NULL; xmlChar *ret; - if ((ctxt == NULL) || (node == NULL) || (parent == NULL)) + if ((ctxt == NULL) || (currentNode == NULL) || (parent == NULL)) return(NULL); if (parent->children == NULL) @@ -211,14 +213,14 @@ xsltEvalTemplateString(xsltTransformContextPtr ctxt, xmlNodePtr node, insert = xmlNewDocNode(ctxt->output, NULL, (const xmlChar *)"fake", NULL); if (insert == NULL) { - xsltTransformError(ctxt, NULL, node, + xsltTransformError(ctxt, NULL, currentNode, "Failed to create temporary node\n"); return(NULL); } oldInsert = ctxt->insert; ctxt->insert = insert; /* OPTIMIZE TODO: if parent->children consists only of text-nodes. */ - xsltApplyOneTemplate(ctxt, node, parent->children, NULL, NULL); + xsltApplyOneTemplate(ctxt, currentNode, parent->children, NULL, NULL); ctxt->insert = oldInsert; @@ -232,17 +234,26 @@ xsltEvalTemplateString(xsltTransformContextPtr ctxt, xmlNodePtr node, * xsltAttrTemplateValueProcessNode: * @ctxt: the XSLT transformation context * @str: the attribute template node value - * @node: the node hosting the attribute + * @inst: the instruction (or LRE) in the stylesheet holding the + * attribute with an AVT * * Process the given string, allowing to pass a namespace mapping * context and return the new string value. * + * Called by: + * - xsltAttrTemplateValueProcess() (templates.c) + * - xsltEvalAttrValueTemplate() (templates.c) + * + * QUESTION: Why is this function public? It is not used outside + * of templates.c. + * * Returns the computed string value or NULL, must be deallocated by the * caller. */ xmlChar * xsltAttrTemplateValueProcessNode(xsltTransformContextPtr ctxt, - const xmlChar *str, xmlNodePtr node) { + const xmlChar *str, xmlNodePtr inst) +{ xmlChar *ret = NULL; const xmlChar *cur; xmlChar *expr, *val; @@ -268,7 +279,7 @@ xsltAttrTemplateValueProcessNode(xsltTransformContextPtr ctxt, cur++; while ((*cur != 0) && (*cur != '}')) cur++; if (*cur == 0) { - xsltTransformError(ctxt, NULL, NULL, + xsltTransformError(ctxt, NULL, inst, "xsltAttrTemplateValueProcessNode: unmatched '{'\n"); ret = xmlStrncat(ret, str, cur - str); return(ret); @@ -285,10 +296,10 @@ xsltAttrTemplateValueProcessNode(xsltTransformContextPtr ctxt, /* * TODO: keep precompiled form around */ - if ((nsList == NULL) && (node != NULL)) { + if ((nsList == NULL) && (inst != NULL)) { int i = 0; - nsList = xmlGetNsList(node->doc, node); + nsList = xmlGetNsList(inst->doc, inst); if (nsList != NULL) { while (nsList[i] != NULL) i++; @@ -314,7 +325,7 @@ xsltAttrTemplateValueProcessNode(xsltTransformContextPtr ctxt, str = cur; continue; } else { - xsltTransformError(ctxt, NULL, NULL, + xsltTransformError(ctxt, NULL, inst, "xsltAttrTemplateValueProcessNode: unmatched '}'\n"); } } else @@ -348,7 +359,8 @@ xsltAttrTemplateValueProcess(xsltTransformContextPtr ctxt, const xmlChar *str) { /** * xsltEvalAttrValueTemplate: * @ctxt: the XSLT transformation context - * @node: the stylesheet node + * @inst: the instruction (or LRE) in the stylesheet holding the + * attribute with an AVT * @name: the attribute QName * @ns: the attribute namespace URI * @@ -360,15 +372,16 @@ xsltAttrTemplateValueProcess(xsltTransformContextPtr ctxt, const xmlChar *str) { * caller. */ xmlChar * -xsltEvalAttrValueTemplate(xsltTransformContextPtr ctxt, xmlNodePtr node, - const xmlChar *name, const xmlChar *ns) { +xsltEvalAttrValueTemplate(xsltTransformContextPtr ctxt, xmlNodePtr inst, + const xmlChar *name, const xmlChar *ns) +{ xmlChar *ret; xmlChar *expr; - if ((ctxt == NULL) || (node == NULL) || (name == NULL)) + if ((ctxt == NULL) || (inst == NULL) || (name == NULL)) return(NULL); - expr = xsltGetNsProp(node, name, ns); + expr = xsltGetNsProp(inst, name, ns); if (expr == NULL) return(NULL); @@ -378,7 +391,7 @@ xsltEvalAttrValueTemplate(xsltTransformContextPtr ctxt, xmlNodePtr node, * attribute content and the XPath precompiled expressions around */ - ret = xsltAttrTemplateValueProcessNode(ctxt, expr, node); + ret = xsltAttrTemplateValueProcessNode(ctxt, expr, inst); #ifdef WITH_XSLT_DEBUG_TEMPLATES XSLT_TRACE(ctxt,XSLT_TRACE_TEMPLATES,xsltGenericDebug(xsltGenericDebugContext, "xsltEvalAttrValueTemplate: %s returns %s\n", expr, ret)); @@ -391,7 +404,8 @@ xsltEvalAttrValueTemplate(xsltTransformContextPtr ctxt, xmlNodePtr node, /** * xsltEvalStaticAttrValueTemplate: * @style: the XSLT stylesheet - * @node: the stylesheet node + * @inst: the instruction (or LRE) in the stylesheet holding the + * attribute with an AVT * @name: the attribute Name * @ns: the attribute namespace URI * @found: indicator whether the attribute is present @@ -403,15 +417,15 @@ xsltEvalAttrValueTemplate(xsltTransformContextPtr ctxt, xmlNodePtr node, * caller. */ const xmlChar * -xsltEvalStaticAttrValueTemplate(xsltStylesheetPtr style, xmlNodePtr node, +xsltEvalStaticAttrValueTemplate(xsltStylesheetPtr style, xmlNodePtr inst, const xmlChar *name, const xmlChar *ns, int *found) { const xmlChar *ret; xmlChar *expr; - if ((style == NULL) || (node == NULL) || (name == NULL)) + if ((style == NULL) || (inst == NULL) || (name == NULL)) return(NULL); - expr = xsltGetNsProp(node, name, ns); + expr = xsltGetNsProp(inst, name, ns); if (expr == NULL) { *found = 0; return(NULL); @@ -431,68 +445,94 @@ xsltEvalStaticAttrValueTemplate(xsltStylesheetPtr style, xmlNodePtr node, /** * xsltAttrTemplateProcess: * @ctxt: the XSLT transformation context - * @target: the result node - * @cur: the attribute template node + * @target: the element where the attribute will be grafted + * @attr: the attribute node of a literal result element * - * Process the given attribute and return the new processed copy. + * Process one attribute of a Literal Result Element (in the stylesheet). + * Evaluates Attribute Value Templates and copies the attribute over to + * the result element. + * This does *not* process attribute sets (xsl:use-attribute-set). + * * - * Returns the attribute replacement. + * Returns the generated attribute node. */ xmlAttrPtr xsltAttrTemplateProcess(xsltTransformContextPtr ctxt, xmlNodePtr target, - xmlAttrPtr cur) { + xmlAttrPtr attr) +{ const xmlChar *value; - xmlNsPtr ns; xmlAttrPtr ret; - if ((ctxt == NULL) || (cur == NULL) || (target == NULL)) + + if ((ctxt == NULL) || (attr == NULL) || (target == NULL)) return(NULL); - if (cur->type != XML_ATTRIBUTE_NODE) + if (attr->type != XML_ATTRIBUTE_NODE) return(NULL); - if ((cur->children == NULL) || (cur->children->type != XML_TEXT_NODE) || - (cur->children->next != NULL)) { - xsltTransformError(ctxt, NULL, cur->parent, - "attribute %s content problem\n", cur->name); - return(NULL); - } - value = cur->children->content; - if (value == NULL) - value = xmlDictLookup(ctxt->dict, BAD_CAST "", 0); - if ((cur->ns != NULL) && - (xmlStrEqual(cur->ns->href, XSLT_NAMESPACE))) { - if (xmlStrEqual(cur->name, (const xmlChar *)"use-attribute-sets")) { - xsltApplyAttributeSet(ctxt, ctxt->node, NULL, value); - } + /* + * Skip all XSLT attributes. + */ +#ifdef XSLT_REFACTORED + if (attr->psvi == xsltXSLTAttrMarker) return(NULL); - } - +#else + if ((attr->ns != NULL) && xmlStrEqual(attr->ns->href, XSLT_NAMESPACE)) + return(NULL); +#endif + /* + * Get the value. + */ + if (attr->children != NULL) { + if ((attr->children->type != XML_TEXT_NODE) || + (attr->children->next != NULL)) + { + xsltTransformError(ctxt, NULL, attr->parent, + "Internal error: The children of an attribute node of a " + "literal result element are not in the expected form.\n"); + return(NULL); + } + value = attr->children->content; + if (value == NULL) + value = xmlDictLookup(ctxt->dict, BAD_CAST "", 0); + } else + value = xmlDictLookup(ctxt->dict, BAD_CAST "", 0); + /* + * Overwrite duplicates. + */ ret = target->properties; while (ret != NULL) { - if (xmlStrEqual(ret->name, cur->name)) { - if (cur->ns == NULL) { - if (ret->ns == NULL) - break; - } else { - if ((ret->ns != NULL) && - (xmlStrEqual(ret->ns->href, cur->ns->href))) - break; - } + if (((attr->ns != NULL) == (ret->ns != NULL)) && + xmlStrEqual(ret->name, attr->name) && + ((attr->ns == NULL) || xmlStrEqual(ret->ns->href, attr->ns->href))) + { + break; } ret = ret->next; } - if (ret != NULL) { + if (ret != NULL) { /* free the existing value */ xmlFreeNodeList(ret->children); ret->children = ret->last = NULL; + /* + * Adjust ns-prefix if needed. + */ + if ((ret->ns != NULL) && + (! xmlStrEqual(ret->ns->prefix, attr->ns->prefix))) + { + ret->ns = xsltGetNamespace(ctxt, attr->parent, attr->ns, target); + } } else { /* create a new attribute */ - if (cur->ns != NULL) - ns = xsltGetPlainNamespace(ctxt, cur->parent, cur->ns, target); + if (attr->ns != NULL) + ret = xmlNewNsProp(target, + xsltGetNamespace(ctxt, attr->parent, attr->ns, target), + attr->name, NULL); else - ns = NULL; - ret = xmlNewNsProp(target, ns, cur->name, NULL); + ret = xmlNewNsProp(target, NULL, attr->name, NULL); } + /* + * Set the value. + */ if (ret != NULL) { xmlNodePtr text; @@ -501,11 +541,30 @@ xsltAttrTemplateProcess(xsltTransformContextPtr ctxt, xmlNodePtr target, ret->last = ret->children = text; text->parent = (xmlNodePtr) ret; text->doc = ret->doc; - if (cur->psvi != NULL) { + + if (attr->psvi != NULL) { + /* + * Evaluate the Attribute Value Template. + */ xmlChar *val; - val = xsltEvalAVT(ctxt, cur->psvi, cur->parent); + val = xsltEvalAVT(ctxt, attr->psvi, attr->parent); if (val == NULL) { - text->content = xmlStrdup(BAD_CAST "runtime error"); + /* + * TODO: Damn, we need an easy mechanism to report + * qualified names! + */ + if (attr->ns) { + xsltTransformError(ctxt, NULL, attr->parent, + "Internal error: Failed to evaluate the AVT " + "of attribute '{%s}%s'.\n", + attr->ns->href, attr->name); + } else { + xsltTransformError(ctxt, NULL, attr->parent, + "Internal error: Failed to evaluate the AVT " + "of attribute '%s'.\n", + attr->name); + } + text->content = xmlStrdup(BAD_CAST ""); } else { text->content = val; } @@ -518,8 +577,15 @@ xsltAttrTemplateProcess(xsltTransformContextPtr ctxt, xmlNodePtr target, } } } else { - xsltTransformError(ctxt, NULL, cur->parent, - "Failed to create attribute %s\n", cur->name); + if (attr->ns) { + xsltTransformError(ctxt, NULL, attr->parent, + "Internal error: Failed to create attribute '{%s}%s'.\n", + attr->ns->href, attr->name); + } else { + xsltTransformError(ctxt, NULL, attr->parent, + "Internal error: Failed to create attribute '%s'.\n", + attr->name); + } } return(ret); } @@ -531,32 +597,206 @@ xsltAttrTemplateProcess(xsltTransformContextPtr ctxt, xmlNodePtr target, * @target: the element where the attributes will be grafted * @cur: the first attribute * - * Do a copy of an attribute list with attribute template processing + * Processes all attributes of a Literal Result Element. + * Attribute references are applied via xsl:use-attribute-set + * attributes. + * Copies all non XSLT-attributes over to the @target element + * and evaluates Attribute Value Templates. * - * Returns: a new xmlAttrPtr, or NULL in case of error. + * Called by xsltApplyOneTemplateInt() (transform.c). + * + * Returns a new list of attribute nodes, or NULL in case of error. + * (Don't assign the result to @target->properties; if + * the result is NULL, you'll get memory leaks, since the + * attributes will be disattached.) */ xmlAttrPtr xsltAttrListTemplateProcess(xsltTransformContextPtr ctxt, - xmlNodePtr target, xmlAttrPtr cur) { - xmlAttrPtr ret = NULL; - xmlAttrPtr q; - xmlNodePtr oldInsert; + xmlNodePtr target, xmlAttrPtr attrs) +{ + xmlAttrPtr attr, copy, last; + xmlNodePtr oldInsert, text; + xmlNsPtr origNs = NULL, copyNs = NULL; + const xmlChar *value; + xmlChar *valueAVT; + + if ((ctxt == NULL) || (target == NULL) || (attrs == NULL)) + return(NULL); oldInsert = ctxt->insert; - ctxt->insert = target; - while (cur != NULL) { - q = xsltAttrTemplateProcess(ctxt, target, cur); - if (q != NULL) { - q->parent = target; - q->doc = target->doc; - if (ret == NULL) { - ret = q; + ctxt->insert = target; + + /* + * Instantiate LRE-attributes. + */ + if (target->properties) { + last = target->properties; + while (last->next != NULL) + last = last->next; + } else { + last = NULL; + } + attr = attrs; + do { + /* + * Skip XSLT attributes. + */ +#ifdef XSLT_REFACTORED + if (attr->psvi == xsltXSLTAttrMarker) { + goto next_attribute; + } +#else + if ((attr->ns != NULL) && + xmlStrEqual(attr->ns->href, XSLT_NAMESPACE)) + { + goto next_attribute; + } +#endif + /* + * Get the value. + */ + if (attr->children != NULL) { + if ((attr->children->type != XML_TEXT_NODE) || + (attr->children->next != NULL)) + { + xsltTransformError(ctxt, NULL, attr->parent, + "Internal error: The children of an attribute node of a " + "literal result element are not in the expected form.\n"); + goto error; } + value = attr->children->content; + if (value == NULL) + value = xmlDictLookup(ctxt->dict, BAD_CAST "", 0); + } else + value = xmlDictLookup(ctxt->dict, BAD_CAST "", 0); + + /* + * Create a new attribute. + */ + copy = xmlNewDocProp(target->doc, attr->name, NULL); + if (copy == NULL) { + if (attr->ns) { + xsltTransformError(ctxt, NULL, attr->parent, + "Internal error: Failed to create attribute '{%s}%s'.\n", + attr->ns->href, attr->name); + } else { + xsltTransformError(ctxt, NULL, attr->parent, + "Internal error: Failed to create attribute '%s'.\n", + attr->name); + } + goto error; } - cur = cur->next; - } + /* + * Attach it to the target element. + */ + copy->parent = target; + if (last == NULL) { + target->properties = copy; + last = copy; + } else { + last->next = copy; + copy->prev = last; + last = copy; + } + /* + * Set the namespace. Avoid lookups of same namespaces. + */ + if (attr->ns != origNs) { + origNs = attr->ns; + if (attr->ns != NULL) { +#ifdef XSLT_REFACTORED + copyNs = xsltGetSpecialNamespace(ctxt, attr->parent, + attr->ns->href, attr->ns->prefix, target); +#else + copyNs = xsltGetNamespace(ctxt, attr->parent, + attr->ns, target); +#endif + if (copyNs == NULL) + goto error; + } else + copyNs = NULL; + } + copy->ns = copyNs; + + /* + * Set the value. + */ + text = xmlNewText(NULL); + if (text != NULL) { + copy->last = copy->children = text; + text->parent = (xmlNodePtr) copy; + text->doc = copy->doc; + + if (attr->psvi != NULL) { + /* + * Evaluate the Attribute Value Template. + */ + valueAVT = xsltEvalAVT(ctxt, attr->psvi, attr->parent); + if (valueAVT == NULL) { + /* + * TODO: Damn, we need an easy mechanism to report + * qualified names! + */ + if (attr->ns) { + xsltTransformError(ctxt, NULL, attr->parent, + "Internal error: Failed to evaluate the AVT " + "of attribute '{%s}%s'.\n", + attr->ns->href, attr->name); + } else { + xsltTransformError(ctxt, NULL, attr->parent, + "Internal error: Failed to evaluate the AVT " + "of attribute '%s'.\n", + attr->name); + } + text->content = xmlStrdup(BAD_CAST ""); + goto error; + } else { + text->content = valueAVT; + } + } else if ((ctxt->internalized) && + (target->doc != NULL) && + (target->doc->dict == ctxt->dict)) + { + text->content = (xmlChar *) value; + } else { + text->content = xmlStrdup(value); + } + } + +next_attribute: + attr = attr->next; + } while (attr != NULL); + + /* + * Apply attribute-sets. + * The creation of such attributes will not overwrite any existing + * attribute. + */ + attr = attrs; + do { +#ifdef XSLT_REFACTORED + if ((attr->psvi == xsltXSLTAttrMarker) && + xmlStrEqual(attr->name, (const xmlChar *)"use-attribute-sets")) + { + xsltApplyAttributeSet(ctxt, ctxt->node, (xmlNodePtr) attr, NULL); + } +#else + if ((attr->ns != NULL) && + xmlStrEqual(attr->name, (const xmlChar *)"use-attribute-sets") && + xmlStrEqual(attr->ns->href, XSLT_NAMESPACE)) + { + xsltApplyAttributeSet(ctxt, ctxt->node, (xmlNodePtr) attr, NULL); + } +#endif + attr = attr->next; + } while (attr != NULL); + ctxt->insert = oldInsert; - return(ret); + return(target->properties); + +error: + ctxt->insert = oldInsert; + return(NULL); } @@ -565,9 +805,7 @@ xsltAttrListTemplateProcess(xsltTransformContextPtr ctxt, * @ctxt: the XSLT transformation context * @node: the attribute template node * - * Process the given node and return the new string value. - * - * Returns the computed tree replacement + * Obsolete. Does always return NULL. Don't use it. */ xmlNodePtr * xsltTemplateProcess(xsltTransformContextPtr ctxt ATTRIBUTE_UNUSED, xmlNodePtr node) { diff --git a/libxslt/transform.c b/libxslt/transform.c index ed4d4db..76b5af8 100644 --- a/libxslt/transform.c +++ b/libxslt/transform.c @@ -82,6 +82,20 @@ int xsltMaxDepth = 5000; #define IS_BLANK_NODE(n) \ (((n)->type == XML_TEXT_NODE) && (xsltIsBlank((n)->content))) + +/* +* Forward declarations +*/ + +static xmlNsPtr +xsltCopyNamespaceListInternal(xmlNodePtr node, xmlNsPtr cur); + +static xmlNodePtr +xsltCopyTreeInternal(xsltTransformContextPtr ctxt, + xmlNodePtr invocNode, + xmlNodePtr node, + xmlNodePtr insert, int isLRE, int topElemVisited); + /** * templPush: * @ctxt: the transformation context @@ -615,15 +629,18 @@ xsltCopyTextString(xsltTransformContextPtr ctxt, xmlNodePtr target, len = xmlStrlen(string); if ((ctxt->type == XSLT_OUTPUT_XML) && (ctxt->style->cdataSection != NULL) && - (target != NULL) && (target->type == XML_ELEMENT_NODE) && + (target != NULL) && + (target->type == XML_ELEMENT_NODE) && (((target->ns == NULL) && (xmlHashLookup2(ctxt->style->cdataSection, target->name, NULL) != NULL)) || ((target->ns != NULL) && (xmlHashLookup2(ctxt->style->cdataSection, - target->name, target->ns->href) != NULL)))) { + target->name, target->ns->href) != NULL)))) + { if ((target != NULL) && (target->last != NULL) && - (target->last->type == XML_CDATA_SECTION_NODE)) { + (target->last->type == XML_CDATA_SECTION_NODE)) + { return(xsltAddTextString(ctxt, target->last, string, len)); } copy = xmlNewCDataBlock(ctxt->output, string, len); @@ -711,6 +728,7 @@ xsltCopyText(xsltTransformContextPtr ctxt, xmlNodePtr target, /* * TODO: Since this doesn't merge adjacent CDATA-section nodes, * we'll get: . + * TODO: Reported in #321505. */ copy = xmlNewCDataBlock(ctxt->output, cur->content, xmlStrlen(cur->content)); @@ -789,136 +807,51 @@ xsltCopyText(xsltTransformContextPtr ctxt, xmlNodePtr target, } /** - * xsltCopyProp: + * xsltShallowCopyAttr: * @ctxt: a XSLT process context - * @targetElem: the element where the attribute will be grafted + * @invocNode: responsible node in the stylesheet; used for error reports + * @target: the element where the attribute will be grafted * @attr: the attribute to be copied * - * Do a copy of an attribute + * Do a copy of an attribute. + * Called by: + * - xsltCopyTreeInternal() + * - xsltCopyOf() + * - xsltCopy() * * Returns: a new xmlAttrPtr, or NULL in case of error. */ static xmlAttrPtr -xsltCopyProp(xsltTransformContextPtr ctxt, xmlNodePtr targetElem, - xmlAttrPtr attr) +xsltShallowCopyAttr(xsltTransformContextPtr ctxt, xmlNodePtr invocNode, + xmlNodePtr target, xmlAttrPtr attr) { - xmlAttrPtr attrCopy; + xmlAttrPtr copy; xmlChar *value; -#ifdef XSLT_REFACTORED - xmlNodePtr txtNode; -#endif if (attr == NULL) return(NULL); - if (targetElem->type != XML_ELEMENT_NODE) { - /* - * TODO: Hmm, it would be better to have the node at hand of the - * instruction which lead to this here. - */ - xsltTransformError(ctxt, NULL, NULL, - "Result tree construction error: cannot set an attribute node " - "on a non-element node.\n"); + if (target->type != XML_ELEMENT_NODE) { + xsltTransformError(ctxt, NULL, invocNode, + "Cannot add an attribute node to a non-element node.\n"); return(NULL); - } + } + + if (target->children != NULL) { + xsltTransformError(ctxt, NULL, invocNode, + "Attribute nodes must be added before " + "any child nodes to an element.\n"); + return(NULL); + } -#ifdef XSLT_REFACTORED - /* - * Create the attribute node. - */ + value = xmlNodeListGetString(attr->doc, attr->children, 1); if (attr->ns != NULL) { - xmlNsPtr ns = NULL; - const xmlChar *prefix = attr->ns->prefix; - - /* - * Process namespace semantics - * - * RESTRUCTURE TODO: This is the same code as in - * xsltAttributeInternal() (attributes.c), but I currently - * don't want to add yet another ns-lookup function. - */ - if ((targetElem->ns != NULL) && - (targetElem->ns->prefix != NULL) && - xmlStrEqual(targetElem->ns->href, attr->ns->href)) - { - ns = targetElem->ns; - goto namespace_finished; - } - if (prefix != NULL) { - /* - * Search by ns-prefix. - */ - ns = xmlSearchNs(targetElem->doc, targetElem, prefix); - if ((ns != NULL) && - (xmlStrEqual(ns->href, attr->ns->href))) - { - goto namespace_finished; - } - } - /* - * Fallback to a search by ns-name. - */ - ns = xmlSearchNsByHref(targetElem->doc, targetElem, attr->ns->href); - if ((ns != NULL) && (ns->prefix != NULL)) { - goto namespace_finished; - } - /* - * OK, we need to declare the namespace on the target element. - */ - if (prefix) { - if (targetElem->nsDef != NULL) { - ns = targetElem->nsDef; - do { - if ((ns->prefix) && xmlStrEqual(ns->prefix, prefix)) { - /* - * The prefix aready occupied. - */ - break; - } - ns = ns->next; - } while (ns != NULL); - if (ns == NULL) { - ns = xmlNewNs(targetElem, attr->ns->href, prefix); - goto namespace_finished; - } - } - } - /* - * Generate a new prefix. - */ - { - const xmlChar *basepref = prefix; - xmlChar pref[30]; - int counter = 0; - - if (prefix != NULL) - basepref = prefix; - else - basepref = xmlStrdup(BAD_CAST "ns"); - - do { - snprintf((char *) pref, 30, "%s_%d", - basepref, counter++); - ns = xmlSearchNs(targetElem->doc, - (xmlNodePtr) attr, BAD_CAST pref); - if (counter > 1000) { - xsltTransformError(ctxt, NULL, (xmlNodePtr) attr, - "Namespace fixup error: Failed to compute a " - "new unique ns-prefix for the copied attribute " - "{%s}%s'.\n", attr->ns->href, attr->name); - ns = NULL; - break; - } - } while (ns != NULL); - if (basepref != prefix) - xmlFree((xmlChar *)basepref); - ns = xmlNewNs(targetElem, attr->ns->href, BAD_CAST pref); - } - -namespace_finished: + xmlNsPtr ns; + ns = xsltGetSpecialNamespace(ctxt, invocNode, + attr->ns->href, attr->ns->prefix, target); if (ns == NULL) { - xsltTransformError(ctxt, NULL, (xmlNodePtr) attr, + xsltTransformError(ctxt, NULL, invocNode, "Namespace fixup error: Failed to acquire an in-scope " "namespace binding of the copied attribute '{%s}%s'.\n", attr->ns->href, attr->name); @@ -926,115 +859,155 @@ namespace_finished: * TODO: Should we just stop here? */ } - attrCopy = xmlSetNsProp(targetElem, ns, attr->name, NULL); + /* + * Note that xmlSetNsProp() will take care of duplicates + * and assigns the new namespace even to a duplicate. + */ + copy = xmlSetNsProp(target, ns, attr->name, value); } else { - attrCopy = xmlSetNsProp(targetElem, NULL, attr->name, NULL); + copy = xmlSetNsProp(target, NULL, attr->name, value); } - if (attrCopy == NULL) - return(NULL); + if (value != NULL) + xmlFree(value); + + if (copy == NULL) + return(NULL); + +#if 0 /* * NOTE: This was optimized according to bug #342695. * TODO: Can this further be optimized, if source and target * share the same dict and attr->children is just 1 text node * which is in the dict? How probable is such a case? */ + /* + * TODO: Do we need to create an empty text node if the value + * is the empty string? + */ value = xmlNodeListGetString(attr->doc, attr->children, 1); if (value != NULL) { - txtNode = xmlNewDocText(targetElem->doc, NULL); + txtNode = xmlNewDocText(target->doc, NULL); if (txtNode == NULL) return(NULL); - if ((targetElem->doc != NULL) && - (targetElem->doc->dict != NULL)) + if ((target->doc != NULL) && + (target->doc->dict != NULL)) { txtNode->content = - (xmlChar *) xmlDictLookup(targetElem->doc->dict, + (xmlChar *) xmlDictLookup(target->doc->dict, BAD_CAST value, -1); - xmlFree(value); + xmlFree(value); } else txtNode->content = value; - attrCopy->children = txtNode; - } - /* - * URGENT TODO: Do we need to create an empty text node if the value - * is the empty string? - */ - -#else /* not XSLT_REFACTORED */ - - value = xmlNodeListGetString(attr->doc, attr->children, 1); - if (attr->ns != NULL) { - xmlNsPtr ns; - ns = xsltGetPlainNamespace(ctxt, attr->parent, attr->ns, targetElem); - attrCopy = xmlSetNsProp(targetElem, ns, attr->name, value); - } else { - attrCopy = xmlSetNsProp(targetElem, NULL, attr->name, value); + copy->children = txtNode; } - if (value != NULL) - xmlFree(value); - -#endif /* not XSLT_REFACTORED */ +#endif - return(attrCopy); + return(copy); } /** - * xsltCopyPropList: + * xsltCopyAttrListNoOverwrite: * @ctxt: a XSLT process context - * @target: the element where the properties will be grafted - * @cur: the first property + * @invocNode: responsible node in the stylesheet; used for error reports + * @target: the element where the new attributes will be grafted + * @attr: the first attribute in the list to be copied * - * Do a copy of a properties list. + * Copies a list of attribute nodes, starting with @attr, over to the + * @target element node. * - * Returns: a new xmlAttrPtr, or NULL in case of error. + * Called by: + * - xsltCopyTreeInternal() + * + * Returns 0 on success and -1 on errors and internal errors. */ -static xmlAttrPtr -xsltCopyPropList(xsltTransformContextPtr ctxt, xmlNodePtr target, - xmlAttrPtr cur) { - xmlAttrPtr ret = NULL; - xmlAttrPtr p = NULL,q; - xmlNsPtr ns; +static int +xsltCopyAttrListNoOverwrite(xsltTransformContextPtr ctxt, + xmlNodePtr invocNode, + xmlNodePtr target, xmlAttrPtr attr) +{ + xmlAttrPtr last = NULL, copy; + xmlNsPtr origNs = NULL, copyNs = NULL; + xmlChar *value = NULL; - while (cur != NULL) { - if (cur->ns != NULL) { - ns = xsltGetNamespace(ctxt, cur->parent, cur->ns, target); + /* + * Don't use xmlCopyProp() here, since it will try to + * reconciliate namespaces. + */ + while (attr != NULL) { + /* + * Find a namespace node in the tree of @target. + * Avoid searching for the same ns. + */ + if (attr->ns != origNs) { + origNs = attr->ns; + if (attr->ns != NULL) { + copyNs = xsltGetSpecialNamespace(ctxt, invocNode, + attr->ns->href, attr->ns->prefix, target); + if (copyNs == NULL) + return(-1); + } else + copyNs = NULL; + } + if (attr->children) + value = xmlNodeListGetString(attr->doc, attr->children, 1); + /* + * REVISIT: I think xmlNewDocProp() is the only attr function + * which does not eval if the attr is of type ID. This is good, + * since we don't need this. + */ + copy = xmlNewDocProp(target->doc, attr->name, BAD_CAST value); + if (copy == NULL) + return(-1); + copy->parent = target; + copy->ns = copyNs; + + if (last == NULL) { + target->properties = copy; + last = copy; } else { - ns = NULL; + last->next = copy; + copy->prev = last; + last = copy; } - q = xmlCopyProp(target, cur); - if (q != NULL) { - q->ns = ns; - if (p == NULL) { - ret = p = q; - } else { - p->next = q; - q->prev = p; - p = q; - } + /* + * OPTIMIZE TODO: How to avoid this intermediate string? + */ + if (value != NULL) { + xmlFree(value); + value = NULL; } - cur = cur->next; - } - return(ret); + attr = attr->next; + } + return(0); } /** - * xsltCopyNode: - * @ctxt: a XSLT process context - * @node: the element node in the source tree. - * @insert: the parent in the result tree. + * xsltShallowCopyElem: + * @ctxt: the XSLT process context + * @node: the element node in the source tree + * or the Literal Result Element + * @insert: the parent in the result tree + * @isLRE: if @node is a Literal Result Element * * Make a copy of the element node @node - * and insert it as last child of @insert - * Intended *only* for copying literal result elements and - * text-nodes. + * and insert it as last child of @insert. + * + * URGENT TODO: The problem with this one (for the non-refactored code) + * is that it is used for both, Literal Result Elements *and* + * copying input nodes. + * + * BIG NOTE: This is only called for XML_ELEMENT_NODEs. + * * Called from: - * xsltApplyOneTemplateInt() - * xsltCopy() + * xsltApplyOneTemplateInt() (for Literal Result Elements - which is a problem) + * xsltCopy() (for shallow-copying elements via xsl:copy) * * Returns a pointer to the new node, or NULL in case of error */ static xmlNodePtr -xsltCopyNode(xsltTransformContextPtr ctxt, xmlNodePtr node, - xmlNodePtr insert) { +xsltShallowCopyElem(xsltTransformContextPtr ctxt, xmlNodePtr node, + xmlNodePtr insert, int isLRE) +{ xmlNodePtr copy; if ((node->type == XML_DTD_NODE) || (insert == NULL)) @@ -1042,33 +1015,59 @@ xsltCopyNode(xsltTransformContextPtr ctxt, xmlNodePtr node, if ((node->type == XML_TEXT_NODE) || (node->type == XML_CDATA_SECTION_NODE)) return(xsltCopyText(ctxt, insert, node, 0)); + copy = xmlDocCopyNode(node, insert->doc, 0); if (copy != NULL) { copy->doc = ctxt->output; xmlAddChild(insert, copy); + if (node->type == XML_ELEMENT_NODE) { /* * Add namespaces as they are needed */ - if (node->nsDef != NULL) - xsltCopyNamespaceList(ctxt, copy, node->nsDef); - } - if ((node->type == XML_ELEMENT_NODE) || - (node->type == XML_ATTRIBUTE_NODE)) { + if (node->nsDef != NULL) { + /* + * TODO: Remove the LRE case in the refactored code + * gets enabled. + */ + if (isLRE) + xsltCopyNamespaceList(ctxt, copy, node->nsDef); + else + xsltCopyNamespaceListInternal(copy, node->nsDef); + } + + /* + * URGENT TODO: The problem with this is that it does not + * copy over all namespace nodes in scope. + * The damn thing about this is, that we would need to + * use the xmlGetNsList(), for every single node; this is + * also done in xsltCopyTreeInternal(), but only for the top node. + */ if (node->ns != NULL) { - copy->ns = xsltGetNamespace(ctxt, node, node->ns, copy); + if (isLRE) { + /* + * REVISIT TODO: Since the non-refactored code still does + * ns-aliasing, we need to call xsltGetNamespace() here. + * Remove this when ready. + */ + copy->ns = xsltGetNamespace(ctxt, node, node->ns, copy); + } else { + copy->ns = xsltGetSpecialNamespace(ctxt, + node, node->ns->href, node->ns->prefix, copy); + + } } else if ((insert->type == XML_ELEMENT_NODE) && - (insert->ns != NULL)) { - xmlNsPtr defaultNs; - - defaultNs = xmlSearchNs(insert->doc, insert, NULL); - if (defaultNs != NULL) - xmlNewNs(copy, BAD_CAST "", NULL); + (insert->ns != NULL)) + { + /* + * "Undeclare" the default namespace. + */ + xsltGetSpecialNamespace(ctxt, node, NULL, NULL, copy); } } } else { xsltTransformError(ctxt, NULL, node, - "xsltCopyNode: copy %s failed\n", node->name); + "xsltShallowCopyElem: copy %s failed\n", node->name); } return(copy); } @@ -1076,24 +1075,31 @@ xsltCopyNode(xsltTransformContextPtr ctxt, xmlNodePtr node, /** * xsltCopyTreeList: * @ctxt: a XSLT process context + * @invocNode: responsible node in the stylesheet; used for error reports * @list: the list of element nodes in the source tree. * @insert: the parent in the result tree. * @literal: is this a literal result element list * * Make a copy of the full list of tree @list * and insert it as last children of @insert - * For literal result element, some of the namespaces may not be copied - * over according to section 7.1 . + * + * NOTE: Not to be used for Literal Result Elements. + * + * Used by: + * - xsltCopyOf() * * Returns a pointer to the new list, or NULL in case of error */ static xmlNodePtr -xsltCopyTreeList(xsltTransformContextPtr ctxt, xmlNodePtr list, - xmlNodePtr insert, int literal) { +xsltCopyTreeList(xsltTransformContextPtr ctxt, xmlNodePtr invocNode, + xmlNodePtr list, + xmlNodePtr insert, int isLRE, int topElemVisited) +{ xmlNodePtr copy, ret = NULL; while (list != NULL) { - copy = xsltCopyTree(ctxt, list, insert, literal); + copy = xsltCopyTreeInternal(ctxt, invocNode, + list, insert, isLRE, topElemVisited); if (copy != NULL) { if (ret == NULL) { ret = copy; @@ -1112,73 +1118,183 @@ xsltCopyTreeList(xsltTransformContextPtr ctxt, xmlNodePtr list, * Do a copy of a namespace list. If @node is non-NULL the * new namespaces are added automatically. * Called by: - * xsltCopyTree() + * xsltCopyTreeInternal() * - * TODO: What is the exact difference between this function + * QUESTION: What is the exact difference between this function * and xsltCopyNamespaceList() in "namespaces.c"? + * ANSWER: xsltCopyNamespaceList() tries to apply ns-aliases. * * Returns: a new xmlNsPtr, or NULL in case of error. */ static xmlNsPtr -xsltCopyNamespaceListInternal(xmlNodePtr node, xmlNsPtr cur) { +xsltCopyNamespaceListInternal(xmlNodePtr elem, xmlNsPtr ns) { xmlNsPtr ret = NULL; - xmlNsPtr p = NULL,q; + xmlNsPtr p = NULL, q, luNs; - if (cur == NULL) - return(NULL); - if (cur->type != XML_NAMESPACE_DECL) + if (ns == NULL) return(NULL); - /* * One can add namespaces only on element nodes */ - if ((node != NULL) && (node->type != XML_ELEMENT_NODE)) - node = NULL; + if ((elem != NULL) && (elem->type != XML_ELEMENT_NODE)) + elem = NULL; - while (cur != NULL) { - if (cur->type != XML_NAMESPACE_DECL) + do { + if (ns->type != XML_NAMESPACE_DECL) break; - /* - * Avoid duplicating namespace declarations on the tree + * Avoid duplicating namespace declarations 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; - } - - q = xmlNewNs(node, cur->href, cur->prefix); + if (elem != NULL) { + if ((elem->ns != NULL) && + xmlStrEqual(elem->ns->prefix, ns->prefix) && + xmlStrEqual(elem->ns->href, ns->href)) + { + ns = ns->next; + continue; + } + luNs = xmlSearchNs(elem->doc, elem, ns->prefix); + if ((luNs != NULL) && (xmlStrEqual(luNs->href, ns->href))) + { + ns = ns->next; + continue; + } + } + q = xmlNewNs(elem, ns->href, ns->prefix); if (p == NULL) { ret = p = q; } else if (q != NULL) { p->next = q; p = q; } - cur = cur->next; - } + ns = ns->next; + } while (ns != NULL); return(ret); } /** - * xsltCopyTree: - * @ctxt: a XSLT process context - * @node: the element node in the source tree. - * @insert: the parent in the result tree. - * @literal: is this a literal result element list + * xsltShallowCopyNsNode: + * @ctxt: the XSLT transformation context + * @invocNode: responsible node in the stylesheet; used for error reports + * @insert: the target element node in the result tree + * @ns: the namespace node + * + * This is used for copying ns-nodes with xsl:copy-of and xsl:copy. + * + * Returns a new/existing ns-node, or NULL. + */ +static int +xsltShallowCopyNsNode(xsltTransformContextPtr ctxt, + xmlNodePtr invocNode, + xmlNodePtr insert, + xmlNsPtr ns) +{ + xmlNsPtr tmpns; + + if ((insert == NULL) || (insert->type != XML_ELEMENT_NODE)) + return(-1); + + if (insert->children != NULL) { + xsltTransformError(ctxt, NULL, invocNode, + "Namespace nodes must be added before " + "any child nodes are added to an element.\n"); + return(1); + } + /* + * + * BIG NOTE: Xalan-J simply overwrites any ns-decls with + * an equal prefix. We definitively won't do that. + * + * MSXML 4.0 and the .NET ignores ns-decls for which an + * equal prefix is already in use. + * + * Saxon raises an error like: + * "net.sf.saxon.xpath.DynamicError: Cannot create two namespace + * nodes with the same name". + * + * NOTE: We'll currently follow MSXML here. + * REVISIT TODO: Check if it's better to follow Saxon here. + */ + if (ns->prefix == NULL) { + /* + * If we are adding ns-nodes to an element using e.g. + * , then we need + * to ensure that we don't incorrectly declare a default + * namespace on an element in no namespace, which otherwise + * would move the element incorrectly into a namespace, if + * the node tree is serialized. + */ + if (insert->ns == NULL) + goto occupied; + } else if ((ns->prefix[0] == 'x') && + xmlStrEqual(ns->prefix, BAD_CAST "xml")) + { + return(0); + } + + if (insert->nsDef != NULL) { + tmpns = insert->nsDef; + do { + if ((tmpns->prefix == NULL) == (ns->prefix == NULL)) { + if ((tmpns->prefix == ns->prefix) || + xmlStrEqual(tmpns->prefix, ns->prefix)) + { + /* + * Same prefix. + */ + if (xmlStrEqual(tmpns->href, ns->href)) + return(0); + goto occupied; + } + } + tmpns = tmpns->next; + } while (tmpns != NULL); + } + tmpns = xmlSearchNs(insert->doc, insert, ns->prefix); + if ((tmpns != NULL) && xmlStrEqual(tmpns->href, ns->href)) + return(0); + /* + * Declare a new namespace. + * TODO: The problem (wrt efficiency) with this xmlNewNs() is + * that it will again search the already declared namespaces + * for a duplicate :-/ + */ + xmlNewNs(insert, ns->href, ns->prefix); + return(0); + +occupied: + /* + * TODO: We could as well raise an error here (like Saxon does), + * or at least generate a warning. + */ + return(0); +} + +/** + * xsltCopyTreeInternal: + * @ctxt: the XSLT transformation context + * @invocNode: responsible node in the stylesheet; used for error reports + * @node: the element node in the source tree + * @insert: the parent in the result tree + * @isLRE: indicates if @node is a Literal Result Element + * @topElemVisited: indicates if a top-most element was already processed * * Make a copy of the full tree under the element node @node * and insert it as last child of @insert - * For literal result element, some of the namespaces may not be copied - * over according to section 7.1. - * TODO: Why is this a public function? + * + * NOTE: Not to be used for Literal Result Elements. + * + * Used by: + * - xsltCopyOf() * * Returns a pointer to the new tree, or NULL in case of error */ -xmlNodePtr -xsltCopyTree(xsltTransformContextPtr ctxt, xmlNodePtr node, - xmlNodePtr insert, int literal) { +static xmlNodePtr +xsltCopyTreeInternal(xsltTransformContextPtr ctxt, + xmlNodePtr invocNode, + xmlNodePtr node, + xmlNodePtr insert, int isLRE, int topElemVisited) +{ xmlNodePtr copy; if (node == NULL) @@ -1203,12 +1319,10 @@ xsltCopyTree(xsltTransformContextPtr ctxt, xmlNodePtr node, return(xsltCopyTextString(ctxt, insert, node->content, 0)); case XML_ATTRIBUTE_NODE: return((xmlNodePtr) - xsltCopyProp(ctxt, insert, (xmlAttrPtr) node)); + xsltShallowCopyAttr(ctxt, invocNode, insert, (xmlAttrPtr) node)); case XML_NAMESPACE_DECL: - if (insert->type != XML_ELEMENT_NODE) - return(NULL); - return((xmlNodePtr) - xsltCopyNamespaceList(ctxt, insert, (xmlNsPtr) node)); + return((xmlNodePtr) xsltShallowCopyNsNode(ctxt, invocNode, + insert, (xmlNsPtr) node)); case XML_DOCUMENT_TYPE_NODE: case XML_DOCUMENT_FRAG_NODE: @@ -1223,7 +1337,8 @@ xsltCopyTree(xsltTransformContextPtr ctxt, xmlNodePtr node, } if (XSLT_IS_RES_TREE_FRAG(node)) { if (node->children != NULL) - copy = xsltCopyTreeList(ctxt, node->children, insert, 0); + copy = xsltCopyTreeList(ctxt, invocNode, + node->children, insert, 0, 0); else copy = NULL; return(copy); @@ -1237,66 +1352,157 @@ xsltCopyTree(xsltTransformContextPtr ctxt, xmlNodePtr node, */ if (insert->last != copy) return(insert->last); - copy->next = NULL; - /* - * Add namespaces as they are needed - */ - if ((node->type == XML_ELEMENT_NODE) || - (node->type == XML_ATTRIBUTE_NODE)) { - xmlNsPtr *nsList, *cur, ns; + + if (node->type == XML_ELEMENT_NODE) { /* - * Must add any new namespaces in scope for the node. - * TODO: Since we try to reuse existing in-scope ns-decls by - * using xmlSearchNsByHref(), this will eventually change - * the prefix of an original ns-binding; thus it might - * break QNames in element/attribute content. - */ - nsList = xmlGetNsList(node->doc, node); - if (nsList != NULL) { - cur = nsList; - while (*cur != NULL) { - ns = xmlSearchNsByHref(insert->doc, insert, (*cur)->href); - if (ns == NULL) - xmlNewNs(copy, (*cur)->href, (*cur)->prefix); - cur++; + * Copy in-scope namespace nodes. + * + * REVISIT: Since we try to reuse existing in-scope ns-decls by + * using xmlSearchNsByHref(), this will eventually change + * the prefix of an original ns-binding; thus it might + * break QNames in element/attribute content. + * OPTIMIZE TODO: If we had a xmlNsPtr * on the transformation + * context, plus a ns-lookup function, which writes directly + * to a given list, then we wouldn't need to create/free the + * nsList every time. + */ + if ((topElemVisited == 0) && + (node->parent != NULL) && + (node->parent->type != XML_DOCUMENT_NODE) && + (node->parent->type != XML_HTML_DOCUMENT_NODE)) + { + xmlNsPtr *nsList, *curns, ns; + + /* + * If this is a top-most element in a tree to be + * copied, then we need to ensure that all in-scope + * namespaces are copied over. For nodes deeper in the + * tree, it is sufficient to reconcile only the ns-decls + * (node->nsDef entries). + */ + + nsList = xmlGetNsList(node->doc, node); + if (nsList != NULL) { + curns = nsList; + do { + /* + * Search by prefix first in order to break as less + * QNames in element/attribute content as possible. + */ + ns = xmlSearchNs(insert->doc, insert, + (*curns)->prefix); + + if ((ns == NULL) || + (! xmlStrEqual(ns->href, (*curns)->href))) + { + ns = NULL; + /* + * Search by namespace name. + * REVISIT TODO: Currently disabled. + */ +#if 0 + ns = xmlSearchNsByHref(insert->doc, + insert, (*curns)->href); +#endif + } + if (ns == NULL) { + /* + * Declare a new namespace on the copied element. + */ + ns = xmlNewNs(copy, (*curns)->href, + (*curns)->prefix); + /* TODO: Handle errors */ + } + if (node->ns == *curns) { + /* + * If this was the original's namespace then set + * the generated counterpart on the copy. + */ + copy->ns = ns; + } + curns++; + } while (*curns != NULL); + xmlFree(nsList); } - xmlFree(nsList); - } - if (node->ns != NULL) { + } else if (node->nsDef != NULL) { /* - * This will map copy->ns to one of the newly created - * in-scope ns-decls. + * Copy over all namespace declaration attributes. */ - copy->ns = xsltGetNamespace(ctxt, node, node->ns, copy); + if (node->nsDef != NULL) { + if (isLRE) + xsltCopyNamespaceList(ctxt, copy, node->nsDef); + else + xsltCopyNamespaceListInternal(copy, node->nsDef); + } + } + /* + * Set the namespace. + */ + if (node->ns != NULL) { + if (copy->ns == NULL) { + /* + * This will map copy->ns to one of the newly created + * in-scope ns-decls, OR create a new ns-decl on @copy. + */ + copy->ns = xsltGetSpecialNamespace(ctxt, invocNode, + node->ns->href, node->ns->prefix, copy); + } } else if ((insert->type == XML_ELEMENT_NODE) && (insert->ns != NULL)) { - xmlNsPtr defaultNs; - - defaultNs = xmlSearchNs(insert->doc, insert, NULL); - if (defaultNs != NULL) - xmlNewNs(copy, BAD_CAST "", NULL); + /* + * "Undeclare" the default namespace on @copy with xmlns="". + */ + xsltGetSpecialNamespace(ctxt, invocNode, NULL, NULL, copy); } + /* + * Copy attribute nodes. + */ + if (node->properties != NULL) { + xsltCopyAttrListNoOverwrite(ctxt, invocNode, + copy, node->properties); + } + if (topElemVisited == 0) + topElemVisited = 1; } - if (node->nsDef != NULL) { - if (literal) - xsltCopyNamespaceList(ctxt, copy, node->nsDef); - else - xsltCopyNamespaceListInternal(copy, node->nsDef); + /* + * Copy the subtree. + */ + if (node->children != NULL) { + xsltCopyTreeList(ctxt, invocNode, + node->children, copy, isLRE, topElemVisited); } - if (node->properties != NULL) - copy->properties = xsltCopyPropList(ctxt, copy, - node->properties); - if (node->children != NULL) - xsltCopyTreeList(ctxt, node->children, copy, literal); } else { - xsltTransformError(ctxt, NULL, node, - "xsltCopyTree: copy %s failed\n", node->name); + xsltTransformError(ctxt, NULL, invocNode, + "xsltCopyTreeInternal: Copying of '%s' failed.\n", node->name); } return(copy); } +/** + * xsltCopyTree: + * @ctxt: the XSLT transformation context + * @node: the element node in the source tree + * @insert: the parent in the result tree + * @literal: indicates if @node is a Literal Result Element + * + * Make a copy of the full tree under the element node @node + * and insert it as last child of @insert + * For literal result element, some of the namespaces may not be copied + * over according to section 7.1. + * TODO: Why is this a public function? + * + * Returns a pointer to the new tree, or NULL in case of error + */ +xmlNodePtr +xsltCopyTree(xsltTransformContextPtr ctxt, xmlNodePtr node, + xmlNodePtr insert, int literal) +{ + return(xsltCopyTreeInternal(ctxt, node, node, insert, literal, 0)); + +} + /************************************************************************ * * * Error/fallback processing * @@ -1659,277 +1865,6 @@ xsltProcessOneNode(xsltTransformContextPtr ctxt, xmlNodePtr node, } } -#ifdef XSLT_REFACTORED -/** -* xsltTransLREUndeclareDefaultNs: -* @ctxt: the transformation context -* @cur: the literal result element -* @ns: the namespace -* @out: the output node (or its parent) -* -* -* Find a matching (prefix and ns-name) ns-declaration -* for the given @ns in the result tree. -* If none is found then a new ns-declaration will be -* added to @out. If, in this case, the given prefix is already -* in use, then a ns-declaration with a modified ns-prefix -* be we created. -* -* Returns the acquired ns-declaration -* or NULL in case of an API or internal error. -*/ -static int -xsltTransLREUndeclareResultDefaultNs(xsltTransformContextPtr ctxt, - xmlNodePtr cur, - xmlNodePtr resultElem) -{ - xmlNsPtr ns; - /* - * OPTIMIZE TODO: This all could be optimized by keeping track of - * the ns-decls currently in-scope via a specialized context. - */ - /* - * Search on the result element itself. - */ - if (resultElem->nsDef != NULL) { - ns = resultElem->nsDef; - do { - if (ns->prefix == NULL) { - if ((ns->href != NULL) && (ns->href[0] != 0)) { - /* - * Raise a namespace normalization error. - */ - xsltTransformError(ctxt, NULL, cur, - "Namespace normalization error: Cannot undeclare " - "the default namespace, since the default namespace " - "'%s' is already declared on the result element.\n", - ns->href); - return(1); - } else { - /* - * The default namespace was undeclared on the - * result element. - */ - return(0); - } - break; - } - ns = ns->next; - } while (ns != NULL); - } - - if ((resultElem->parent != NULL) && - (resultElem->parent->type == XML_ELEMENT_NODE)) - { - /* - * The parent element is in no namespace, so assume - * that there is no default namespace in scope. - */ - if (resultElem->parent->ns == NULL) - return(0); - - ns = xmlSearchNs(resultElem->doc, resultElem->parent, - NULL); - /* - * Fine if there's no default ns is scope, or if the - * default ns was undeclared. - */ - if ((ns == NULL) || (ns->href == NULL) || (ns->href[0] == 0)) - return(0); - - /* - * Undeclare the default namespace. - */ - ns = xmlNewNs(resultElem, BAD_CAST "", NULL); - /* TODO: Check result */ - return(0); - } - return(0); -} - -/** -* xsltTransLREAcquireResultInScopeNs: -* @ctxt: the transformation context -* @cur: the literal result element (in the stylesheet) -* @literalNs: the namespace (in the stylsheet) -* @resultElem: the generated result element -* -* -* Find a matching (prefix and ns-name) ns-declaration -* for the given @ns in the result tree. -* If none is found then a new ns-declaration will be -* added to @out. If, in this case, the given prefix is already -* in use, then a ns-declaration with a modified ns-prefix -* be we created. -* -* Returns the acquired ns-declaration -* or NULL in case of an API or internal error. -*/ -static xmlNsPtr -xsltTransLREAcquireResultInScopeNs(xsltTransformContextPtr ctxt, - xmlNodePtr cur, - xmlNsPtr literalNs, - xmlNodePtr resultElem) -{ - xmlNsPtr ns; - int prefixOccupied = 0; - - if ((ctxt == NULL) || (cur == NULL) || (resultElem == NULL)) - return(NULL); - - /* - * OPTIMIZE TODO: This all could be optimized by keeping track of - * the ns-decls currently in-scope via a specialized context. - */ - /* - * NOTE: Namespace exclusion and ns-aliasing is performed at - * compilation-time in the refactored code; so this need not be done - * here. - */ - /* - * First: search on the result element itself. - */ - if (resultElem->nsDef != NULL) { - ns = resultElem->nsDef; - do { - if ((ns->prefix == NULL) == (literalNs->prefix == NULL)) { - if (literalNs->prefix == NULL) { - if (xmlStrEqual(ns->href, literalNs->href)) - return(ns); - prefixOccupied = 1; - break; - } else if ((ns->prefix[0] == literalNs->prefix[0]) && - xmlStrEqual(ns->prefix, literalNs->prefix)) - { - if (xmlStrEqual(ns->href, literalNs->href)) - return(ns); - prefixOccupied = 1; - break; - } - } - ns = ns->next; - } while (ns != NULL); - } - if (prefixOccupied) { - /* - * If the ns-prefix is occupied by an other ns-decl on the - * result element, then this means: - * 1) The desired prefix is shadowed - * 2) There's no way around changing the prefix - * - * Try a desperate search for an in-scope ns-decl - * with a matching ns-name before we use the last option, - * which is to recreate the ns-decl with a modified prefix. - */ - ns = xmlSearchNsByHref(resultElem->doc, resultElem, literalNs->href); - if (ns != NULL) - return(ns); - - /* - * Fallback to changing the prefix. - */ - } else if ((resultElem->parent != NULL) && - (resultElem->parent->type == XML_ELEMENT_NODE)) { - /* - * Try to find a matching ns-decl in the ancestor-axis. - * - * Check the common case: The parent element of the current - * result element is in the same namespace (with an equal ns-prefix). - */ - if ((resultElem->parent->ns != NULL) && - ((resultElem->parent->ns->prefix == NULL) == - (literalNs->prefix == NULL))) - { - ns = resultElem->parent->ns; - - if (literalNs->prefix == NULL) { - if (xmlStrEqual(ns->href, literalNs->href)) - return(ns); - } else if ((ns->prefix[0] == literalNs->prefix[0]) && - xmlStrEqual(ns->prefix, literalNs->prefix) && - xmlStrEqual(ns->href, literalNs->href)) - { - return(ns); - } - } - /* - * Lookup the remaining in-scope namespaces. - */ - ns = xmlSearchNs(resultElem->doc, resultElem->parent, - literalNs->prefix); - if ((ns != NULL) && xmlStrEqual(ns->href, literalNs->href)) - return(ns); - ns = NULL; - /* - * Either no matching ns-prefix was found or the namespace is - * shadowed. - * Create a new ns-decl on the current result element. - * - * SPEC TODO: Hmm, we could also try to reuse an in-scope - * namespace with a matching ns-name but a different - * ns-prefix. - * What has higher precedence? - * 1) If keeping the prefix: create a new ns-decl. - * 2) If reusal: first lookup ns-names; then fallback - * to creation of a new ns-decl. - * REVISIT TODO: this currently uses case 2) since this - * is the way it used to be before refactoring. - */ - ns = xmlSearchNsByHref(resultElem->doc, resultElem, - literalNs->href); - if (ns != NULL) - return(ns); - /* - * Create the ns-decl on the current result element. - */ - ns = xmlNewNs(resultElem, literalNs->href, literalNs->prefix); - /* TODO: check errors */ - return(ns); - } else if ((resultElem->parent == NULL) || - (resultElem->parent->type != XML_ELEMENT_NODE)) - { - /* - * This is the root of the tree. - */ - ns = xmlNewNs(resultElem, literalNs->href, literalNs->prefix); - /* TODO: Check result */ - return(ns); - } - /* - * Fallback: we need to generate a new prefix and declare the namespace - * on the result element. - */ - { - xmlChar prefix[30]; - int counter = 0; - - /* - * Comment copied from xslGetNamespace(): - * "For an element node, if we don't find it, or it's the default - * and this element already defines a default (bug 165560), we - * need to create it." - */ - do { - snprintf((char *) prefix, 30, "%s_%d", - literalNs->prefix, counter++); - ns = xmlSearchNs(resultElem->doc, resultElem, BAD_CAST prefix); - if (counter > 1000) { - xsltTransformError(ctxt, NULL, cur, - "Internal error in xsltTransLREAcquireInScopeNs(): " - "Failed to compute a unique ns-prefix for the " - "result element"); - return(NULL); - } - } while (ns != NULL); - ns = xmlNewNs(resultElem, literalNs->href, BAD_CAST prefix); - /* TODO: Check result */ - return(ns); - } - return(NULL); -} - -#endif /* XSLT_REFACTORED */ - /** * xsltApplyOneTemplate: * @ctxt: a XSLT process context @@ -2138,7 +2073,8 @@ xsltApplyOneTemplateInt(xsltTransformContextPtr ctxt, xmlNodePtr node, #endif /* * Copy the raw element-node. - * OLD: if ((copy = xsltCopyNode(ctxt, cur, insert)) == NULL) + * OLD: if ((copy = xsltShallowCopyElem(ctxt, cur, insert)) + * == NULL) * goto error; */ copy = xmlDocCopyNode(cur, insert->doc, 0); @@ -2200,43 +2136,34 @@ xsltApplyOneTemplateInt(xsltTransformContextPtr ctxt, xmlNodePtr node, if (cur->ns != NULL) { /* * If there's no such ns-decl in the result tree, - * then xsltGetNamespace() will create a ns-decl - * on the copied node. - */ - /* - * REVISIT TODO: Changed to use - * xsltTransLREAcquireInScopeNs() instead of - * xsltGetNamespace(). - * OLD: copy->ns = xsltGetNamespace(ctxt, cur, - * cur->ns, copy); - */ - copy->ns = xsltTransLREAcquireResultInScopeNs(ctxt, - cur, cur->ns, copy); + * then xsltGetSpecialNamespace() will + * create a ns-decl on the copied node. + */ + copy->ns = xsltGetSpecialNamespace(ctxt, cur, + cur->ns->href, cur->ns->prefix, copy); } else { /* * Undeclare the default namespace if needed. - * This can be skipped, if: - * 1) If the result element has no ns-decls, in which - * case the result element abviously does not - * declare a default namespace. - * 2) AND there's either no parent, or the parent - * is in no namespace; this means there's no - * default namespace is scope to care about. + * This can be skipped, if the result element has + * no ns-decls, in which case the result element + * obviously does not declare a default namespace; + * AND there's either no parent, or the parent + * element is in no namespace; this means there's no + * default namespace is scope to care about. * - * REVISIT TODO: This might result in massive + * REVISIT: This might result in massive * generation of ns-decls if nodes in a default * namespaces are mixed with nodes in no namespace. * */ if (copy->nsDef || - ((insert != NULL) && (insert->ns != NULL))) - xsltTransLREUndeclareResultDefaultNs(ctxt, - cur, copy); -#if 0 - defaultNs = xmlSearchNs(insert->doc, insert, NULL); - if ((defaultNs != NULL) && (defaultNs->href != NULL)) - xmlNewNs(copy, BAD_CAST "", NULL); -#endif + ((insert != NULL) && + (insert->type == XML_ELEMENT_NODE) && + (insert->ns != NULL))) + { + xsltGetSpecialNamespace(ctxt, cur, + NULL, NULL, copy); + } } } /* @@ -2245,118 +2172,11 @@ xsltApplyOneTemplateInt(xsltTransformContextPtr ctxt, xmlNodePtr node, * is processed to produce an attribute for the element in * the result tree." * TODO: Refactor this, since it still uses ns-aliasing. + * NOTE: See bug #341325. */ if (cur->properties != NULL) { xsltAttrListTemplateProcess(ctxt, copy, cur->properties); } - /* - * OLD-COMMENT: "Add extra namespaces inherited from the - * current template if we are in the first level children - * and this is a "real" template. - * - * SPEC XSLT 2.0: - * "The following namespaces are designated as excluded - * namespaces: - * - The XSLT namespace URI - * (http://www.w3.org/1999/XSL/Transform) - * - A namespace URI declared as an extension namespace - * - A namespace URI designated by using an - * [xsl:]exclude-result-prefixes - * - * TODO: - * XSLT 1.0 - * 1) Supply all in-scope namespaces - * 2) Skip excluded namespaces (see above) - * 3) Apply namespace aliasing - * - * XSLT 2.0 (will generate - * redundant namespaces in some cases): - * 1) Supply all in-scope namespaces - * 2) Skip excluded namespaces if *not* target-namespace - * of an namespace alias - * 3) Apply namespace aliasing - * - * NOTE: See bug #341325. - */ -#if 0 - if ((templ != NULL) && (oldInsert == insert) && - (ctxt->templ != NULL) && - (ctxt->templ->inheritedNs != NULL)) { - int i; - xmlNsPtr ns, ret; - - for (i = 0; i < ctxt->templ->inheritedNsNr; i++) { - const xmlChar *URI = NULL; - xsltStylesheetPtr style; - - ns = ctxt->templ->inheritedNs[i]; - /* - * Apply namespace aliasing. - * - * TODO: Compute the effective value of namespace - * aliases at compilation-time in order to avoid - * the lookup in the import-tree here. - */ - 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 == UNDEFINED_DEFAULT_NS) { - xmlNsPtr defaultNs; - - defaultNs = xmlSearchNs(cur->doc, cur, NULL); - if (defaultNs == NULL) { - /* - * TODO: Should not happen; i.e., it is - * an error at compilation-time if there's - * no default namespace in scope if - * "#default" is used. - */ - continue; - } else - URI = defaultNs->href; - } - - if (URI == NULL) { - /* - * There was no matching namespace-alias, so - * just create a matching ns-decl if not - * already in scope. - */ - ret = xmlSearchNs(copy->doc, copy, ns->prefix); - if ((ret == NULL) || - (!xmlStrEqual(ret->href, ns->href))) - xmlNewNs(copy, ns->href, ns->prefix); - } else if (!xmlStrEqual(URI, XSLT_NAMESPACE)) { - ret = xmlSearchNs(copy->doc, copy, ns->prefix); - if ((ret == NULL) || - (!xmlStrEqual(ret->href, URI))) { - /* - * Here we create a namespace - * declaration with the literal namespace - * prefix and with the target namespace name. - * TODO: We should consider to fix this and - * use the *target* namespace prefix, not the - * literal one (see bug #341325). - */ - xmlNewNs(copy, URI, ns->prefix); - } - } - } - if (copy->ns != NULL) { - /* - * Fix the node namespace if needed - */ - copy->ns = xsltGetNamespace(ctxt, copy, copy->ns, copy); - } - } -#endif } else if (IS_XSLT_ELEM_FAST(cur)) { /* * XSLT instructions @@ -2512,8 +2332,8 @@ xsltApplyOneTemplateInt(xsltTransformContextPtr ctxt, xmlNodePtr node, ctxt->insert = insert; if (!xsltApplyFallbacks(ctxt, node, cur)) { xsltGenericError(xsltGenericErrorContext, - "xsltApplyOneTemplate: %s was not compiled\n", - cur->name); + "xsltApplyOneTemplate: %s was not compiled\n", + cur->name); } ctxt->insert = oldInsert; } @@ -2547,8 +2367,8 @@ xsltApplyOneTemplateInt(xsltTransformContextPtr ctxt, xmlNodePtr node, xsltMessage(ctxt, node, cur); } else { xsltGenericError(xsltGenericErrorContext, - "xsltApplyOneTemplate: problem with xsl:%s\n", - cur->name); + "xsltApplyOneTemplate: problem with xsl:%s\n", + cur->name); } goto skip_children; } else if ((cur->type == XML_TEXT_NODE) || @@ -2635,15 +2455,8 @@ xsltApplyOneTemplateInt(xsltTransformContextPtr ctxt, xmlNodePtr node, "xsltApplyOneTemplate: copy node %s\n", cur->name)); #endif - if ((copy = xsltCopyNode(ctxt, cur, insert)) == NULL) - goto error; - /* - * all the attributes are directly inherited - */ - if (cur->properties != NULL) { - xsltAttrListTemplateProcess(ctxt, copy, - cur->properties); - } + if ((copy = xsltShallowCopyElem(ctxt, cur, insert, 1)) == NULL) + goto error; /* * Add extra namespaces inherited from the current template * if we are in the first level children and this is a @@ -2657,36 +2470,36 @@ xsltApplyOneTemplateInt(xsltTransformContextPtr ctxt, xmlNodePtr node, for (i = 0; i < ctxt->templ->inheritedNsNr; i++) { const xmlChar *URI = NULL; xsltStylesheetPtr style; - ns = ctxt->templ->inheritedNs[i]; + ns = ctxt->templ->inheritedNs[i]; + + /* Note that the XSLT namespace was already excluded + * in xsltGetInheritedNsList(). + */ +#if 0 + if (xmlStrEqual(ns->href, XSLT_NAMESPACE)) + continue; +#endif 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 == UNDEFINED_DEFAULT_NS) { - xmlNsPtr dflt; - dflt = xmlSearchNs(cur->doc, cur, NULL); - if (dflt == NULL) - continue; - else - URI = dflt->href; + if (style->nsAliases != NULL) + URI = (const xmlChar *) + xmlHashLookup(style->nsAliases, ns->href); + if (URI != NULL) + break; + + style = xsltNextImport(style); } - - if (URI == NULL) { - ret = xmlSearchNs(copy->doc, copy, ns->prefix); - if ((ret == NULL) || - (!xmlStrEqual(ret->href, ns->href))) - xmlNewNs(copy, ns->href, ns->prefix); - } else if (!xmlStrEqual(URI, XSLT_NAMESPACE)) { - ret = xmlSearchNs(copy->doc, copy, ns->prefix); - if ((ret == NULL) || - (!xmlStrEqual(ret->href, URI))) + if (URI == UNDEFINED_DEFAULT_NS) + continue; + if (URI == NULL) + URI = ns->href; + /* + * TODO: The following will still be buggy for the + * non-refactored code. + */ + ret = xmlSearchNs(copy->doc, copy, ns->prefix); + if ((ret == NULL) || (!xmlStrEqual(ret->href, URI))) + { xmlNewNs(copy, URI, ns->prefix); } } @@ -2694,9 +2507,15 @@ xsltApplyOneTemplateInt(xsltTransformContextPtr ctxt, xmlNodePtr node, /* * Fix the node namespace if needed */ - copy->ns = xsltGetNamespace(ctxt, copy, copy->ns, copy); + copy->ns = xsltGetNamespace(ctxt, cur, copy->ns, copy); } } + /* + * all the attributes are directly inherited + */ + if (cur->properties != NULL) { + xsltAttrListTemplateProcess(ctxt, copy, cur->properties); + } } #endif /* else of XSLT_REFACTORED */ @@ -3334,16 +3153,17 @@ xsltSort(xsltTransformContextPtr ctxt, /** * xsltCopy: - * @ctxt: a XSLT process context - * @node: the node in the source tree. - * @inst: the xslt copy node - * @comp: precomputed information + * @ctxt: an XSLT process context + * @node: the node in the source tree + * @inst: the element node of the XSLT-copy instruction + * @comp: computed information of the XSLT-copy instruction * - * Execute the xsl:copy instruction on the source node. + * Execute the XSLT-copy instruction on the source node. */ void xsltCopy(xsltTransformContextPtr ctxt, xmlNodePtr node, - xmlNodePtr inst, xsltStylePreCompPtr castedComp) { + xmlNodePtr inst, xsltStylePreCompPtr castedComp) +{ #ifdef XSLT_REFACTORED xsltStyleItemCopyPtr comp = (xsltStyleItemCopyPtr) castedComp; #else @@ -3377,8 +3197,8 @@ xsltCopy(xsltTransformContextPtr ctxt, xmlNodePtr node, break; case XML_ELEMENT_NODE: /* - * NOTE: The "fake" is a doc-node, not an element node. - * OLD: + * REVISIT NOTE: The "fake" is a doc-node, not an element node. + * REMOVED: * if (xmlStrEqual(node->name, BAD_CAST " fake node libxslt")) * return; */ @@ -3387,7 +3207,7 @@ xsltCopy(xsltTransformContextPtr ctxt, xmlNodePtr node, XSLT_TRACE(ctxt,XSLT_TRACE_COPY,xsltGenericDebug(xsltGenericDebugContext, "xsltCopy: node %s\n", node->name)); #endif - copy = xsltCopyNode(ctxt, node, ctxt->insert); + copy = xsltShallowCopyElem(ctxt, node, ctxt->insert, 0); ctxt->insert = copy; if (comp->use != NULL) { xsltApplyAttributeSet(ctxt, node, inst, comp->use); @@ -3398,62 +3218,13 @@ xsltCopy(xsltTransformContextPtr ctxt, xmlNodePtr node, XSLT_TRACE(ctxt,XSLT_TRACE_COPY,xsltGenericDebug(xsltGenericDebugContext, "xsltCopy: attribute %s\n", node->name)); #endif - if (ctxt->insert->type == XML_ELEMENT_NODE) { - xmlAttrPtr attr = (xmlAttrPtr) node, ret = NULL, cur; - - if (attr->ns != NULL) { - if (!xmlStrEqual(attr->ns->href, XSLT_NAMESPACE)) { - ret = xmlCopyProp(ctxt->insert, attr); - ret->ns = xsltGetNamespace(ctxt, node, attr->ns, - ctxt->insert); - } - } else - ret = xmlCopyProp(ctxt->insert, attr); - - if (ret != NULL) { - cur = ctxt->insert->properties; - if (cur != NULL) { - /* - * Avoid duplicates and insert at the end - * of the attribute list - */ - while (cur->next != NULL) { - if ((xmlStrEqual(cur->name, ret->name)) && - (((cur->ns == NULL) && (ret->ns == NULL)) || - ((cur->ns != NULL) && (ret->ns != NULL) && - (xmlStrEqual(cur->ns->href, - ret->ns->href))))) { - xmlFreeProp(ret); - return; - } - cur = cur->next; - } - if ((xmlStrEqual(cur->name, ret->name)) && - (((cur->ns == NULL) && (ret->ns == NULL)) || - ((cur->ns != NULL) && (ret->ns != NULL) && - (xmlStrEqual(cur->ns->href, - ret->ns->href))))) { - xmlNodePtr tmp; - - /* - * Attribute already exists, - * update it with the new value - */ - tmp = cur->children; - cur->children = ret->children; - ret->children = tmp; - tmp = cur->last; - cur->last = ret->last; - ret->last = tmp; - xmlFreeProp(ret); - return; - } - cur->next = ret; - ret->prev = cur; - } else - ctxt->insert->properties = ret; - } - } + /* + * REVISIT: We could also raise an error if the parent is not + * an element node. + * OPTIMIZE TODO: Can we set the value/children of the + * attribute without an intermediate copy of the string value? + */ + xsltShallowCopyAttr(ctxt, inst, ctxt->insert, (xmlAttrPtr) node); break; } case XML_PI_NODE: @@ -3477,8 +3248,8 @@ xsltCopy(xsltTransformContextPtr ctxt, xmlNodePtr node, #ifdef WITH_XSLT_DEBUG_PROCESS XSLT_TRACE(ctxt,XSLT_TRACE_COPY,xsltGenericDebug(xsltGenericDebugContext, "xsltCopy: namespace declaration\n")); -#endif - xsltCopyNamespace(ctxt, ctxt->insert, (xmlNsPtr)node); +#endif + xsltShallowCopyNsNode(ctxt, inst, ctxt->insert, (xmlNsPtr)node); break; default: break; @@ -3553,20 +3324,20 @@ xsltElement(xsltTransformContextPtr ctxt, xmlNodePtr node, #else xsltStylePreCompPtr comp = castedComp; #endif - xmlChar *prop = NULL, *attributes = NULL, *namespace; - const xmlChar *name; - const xmlChar *prefix; - xmlNsPtr ns = NULL, oldns = NULL; + xmlChar *prop = NULL; + const xmlChar *name, *prefix = NULL, *nsName = NULL; xmlNodePtr copy; xmlNodePtr oldInsert; - int generateDefault = 0; - if (ctxt->insert == NULL) return; - if (!comp->has_name) { - 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; /* * stack and saves @@ -3574,24 +3345,42 @@ xsltElement(xsltTransformContextPtr ctxt, xmlNodePtr node, oldInsert = ctxt->insert; if (comp->name == NULL) { - prop = xsltEvalAttrValueTemplate(ctxt, inst, - (const xmlChar *)"name", NULL); - if (prop == NULL) { - xsltTransformError(ctxt, NULL, inst, - "xsl:element : name is missing\n"); - return; - } + /* 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:element: The attribute 'name' is missing.\n"); + goto error; + } if (xmlValidateQName(prop, 0)) { xsltTransformError(ctxt, NULL, inst, - "xsl:element : invalid name\n"); - /* we fall through to catch any other errors if possible */ + "xsl:element: The effective name '%s' is not a " + "valid QName.\n", prop); + /* we fall through to catch any further errors, if possible */ } name = xsltSplitQName(ctxt->dict, prop, &prefix); xmlFree(prop); + if ((prefix != NULL) && + (!xmlStrncasecmp(prefix, (xmlChar *)"xml", 3))) + { + /* + * TODO: Should we really disallow an "xml" prefix? + */ + goto error; + } } else { + /* + * The "name" value was static. + */ +#ifdef XSLT_REFACTORED + prefix = comp->nsPrefix; + name = comp->name; +#else name = xsltSplitQName(ctxt->dict, comp->name, &prefix); +#endif } - + /* * Create the new element */ @@ -3605,79 +3394,105 @@ xsltElement(xsltTransformContextPtr ctxt, xmlNodePtr node, "xsl:element : creation of %s failed\n", name); return; } - xmlAddChild(ctxt->insert, copy); - ctxt->insert = copy; + xmlAddChild(ctxt->insert, copy); - if ((comp->ns == NULL) && (comp->has_ns)) { - namespace = xsltEvalAttrValueTemplate(ctxt, inst, - (const xmlChar *)"namespace", NULL); - if (namespace != NULL) { - ns = xsltGetSpecialNamespace(ctxt, inst, namespace, prefix, - ctxt->insert); - xmlFree(namespace); - } - } else if ((comp->ns != NULL) && (prefix == NULL) && (comp->has_ns)) { - generateDefault = 1; - } else if (comp->ns != NULL) { - ns = xsltGetSpecialNamespace(ctxt, inst, comp->ns, prefix, - ctxt->insert); - } - if ((ns == NULL) && (prefix != NULL)) { - if (!xmlStrncasecmp(prefix, (xmlChar *)"xml", 3)) { -#ifdef WITH_XSLT_DEBUG_PARSING - xsltGenericDebug(xsltGenericDebugContext, - "xsltElement: xml prefix forbidden\n"); -#endif - return; - } - oldns = xmlSearchNs(inst->doc, inst, prefix); - if (oldns == NULL) { - xsltTransformError(ctxt, NULL, inst, - "xsl:element : no namespace bound to prefix %s\n", prefix); + /* + * Namespace + * --------- + */ + if (comp->has_ns) { + if (comp->ns != NULL) { + /* + * No AVT; just plain text for the namespace name. + */ + if (comp->ns[0] != 0) + nsName = comp->ns; } else { - ns = xsltGetNamespace(ctxt, inst, oldns, ctxt->insert); - } + xmlChar *tmpNsName; + /* + * Eval the AVT. + */ + /* TODO: check attr acquisition wrt to the XSLT namespace */ + tmpNsName = xsltEvalAttrValueTemplate(ctxt, inst, + (const xmlChar *) "namespace", XSLT_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); + }; + } else { + xmlNsPtr ns; + /* + * 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:element element, including any default + * namespace declaration. + */ + ns = xmlSearchNs(inst->doc, inst, prefix); + if (ns == NULL) { + /* + * TODO: Check this in the compilation layer in case it's a + * static value. + */ + if (prefix != NULL) { + xsltTransformError(ctxt, NULL, inst, + "xsl:element: 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. + */ + if (nsName != NULL) { + copy->ns = xsltGetSpecialNamespace(ctxt, inst, nsName, prefix, copy); + } else if ((copy->parent != NULL) && + (copy->parent->type == XML_ELEMENT_NODE) && + (copy->parent->ns != NULL)) + { + /* + * "Undeclare" the default namespace. + */ + xsltGetSpecialNamespace(ctxt, inst, NULL, NULL, copy); } - if (generateDefault == 1) { - xmlNsPtr defaultNs = NULL; - - if ((oldInsert != NULL) && (oldInsert->type == XML_ELEMENT_NODE)) - defaultNs = xmlSearchNs(oldInsert->doc, oldInsert, NULL); - if ((defaultNs == NULL) || (!xmlStrEqual(defaultNs->href, comp->ns))) { - ns = xmlNewNs(ctxt->insert, comp->ns, NULL); - ctxt->insert->ns = ns; - } else { - ctxt->insert->ns = defaultNs; - } - } else if ((ns == NULL) && (oldns != NULL)) { - /* very specific case xsltGetNamespace failed */ - ns = xmlNewNs(ctxt->insert, oldns->href, oldns->prefix); - ctxt->insert->ns = ns; - } else - ctxt->insert->ns = ns; - + ctxt->insert = copy; if (comp->has_use) { if (comp->use != NULL) { xsltApplyAttributeSet(ctxt, node, inst, comp->use); } else { + xmlChar *attrSets = NULL; /* * BUG TODO: use-attribute-sets is not a value template. * use-attribute-sets = qnames */ - attributes = xsltEvalAttrValueTemplate(ctxt, inst, - (const xmlChar *)"use-attribute-sets", NULL); - if (attributes != NULL) { - xsltApplyAttributeSet(ctxt, node, inst, attributes); - xmlFree(attributes); + attrSets = xsltEvalAttrValueTemplate(ctxt, inst, + (const xmlChar *)"use-attribute-sets", NULL); + if (attrSets != NULL) { + xsltApplyAttributeSet(ctxt, node, inst, attrSets); + xmlFree(attrSets); } } } - - xsltApplyOneTemplateInt(ctxt, ctxt->node, inst->children, NULL, NULL, 0); + /* + * Instantiate the sequence constructor. + */ + if (inst->children != NULL) + xsltApplyOneTemplateInt(ctxt, ctxt->node, inst->children, + NULL, NULL, 0); +error: ctxt->insert = oldInsert; + return; } @@ -3857,7 +3672,7 @@ xsltCopyOf(xsltTransformContextPtr ctxt, xmlNodePtr node, #ifdef XSLT_REFACTORED if (comp->inScopeNs != NULL) { ctxt->xpathCtxt->namespaces = comp->inScopeNs->list; - ctxt->xpathCtxt->nsNr = comp->inScopeNs->number; + ctxt->xpathCtxt->nsNr = comp->inScopeNs->xpathNumber; } else { ctxt->xpathCtxt->namespaces = NULL; ctxt->xpathCtxt->nsNr = 0; @@ -3902,17 +3717,20 @@ xsltCopyOf(xsltTransformContextPtr ctxt, xmlNodePtr node, if ((cur->type == XML_DOCUMENT_NODE) || (cur->type == XML_HTML_DOCUMENT_NODE)) { - xsltCopyTreeList(ctxt, cur->children, ctxt->insert, 0); + xsltCopyTreeList(ctxt, inst, + cur->children, ctxt->insert, 0, 0); } else if (cur->type == XML_ATTRIBUTE_NODE) { - xsltCopyProp(ctxt, ctxt->insert, (xmlAttrPtr) cur); + xsltShallowCopyAttr(ctxt, inst, + ctxt->insert, (xmlAttrPtr) cur); } else { - xsltCopyTree(ctxt, cur, ctxt->insert, 0); + xsltCopyTreeInternal(ctxt, inst, + cur, ctxt->insert, 0, 0); } } } } else if (res->type == XPATH_XSLT_TREE) { /* - * Result tree fragment + * Result tree fragment (e.g. via ) * -------------------- */ #ifdef WITH_XSLT_DEBUG_PROCESS @@ -3927,8 +3745,8 @@ xsltCopyOf(xsltTransformContextPtr ctxt, xmlNodePtr node, (list->nodeTab[0] != NULL) && (IS_XSLT_REAL_NODE(list->nodeTab[0]))) { - xsltCopyTreeList(ctxt, list->nodeTab[0]->children, - ctxt->insert, 0); + xsltCopyTreeList(ctxt, inst, + list->nodeTab[0]->children, ctxt->insert, 0, 0); } } else { /* Convert to a string. */ @@ -3995,7 +3813,7 @@ xsltValueOf(xsltTransformContextPtr ctxt, xmlNodePtr node, #ifdef XSLT_REFACTORED if (comp->inScopeNs != NULL) { ctxt->xpathCtxt->namespaces = comp->inScopeNs->list; - ctxt->xpathCtxt->nsNr = comp->inScopeNs->number; + ctxt->xpathCtxt->nsNr = comp->inScopeNs->xpathNumber; } else { ctxt->xpathCtxt->namespaces = NULL; ctxt->xpathCtxt->nsNr = 0; @@ -4279,7 +4097,7 @@ xsltApplyTemplates(xsltTransformContextPtr ctxt, xmlNodePtr node, #ifdef XSLT_REFACTORED if (comp->inScopeNs != NULL) { ctxt->xpathCtxt->namespaces = comp->inScopeNs->list; - ctxt->xpathCtxt->nsNr = comp->inScopeNs->number; + ctxt->xpathCtxt->nsNr = comp->inScopeNs->xpathNumber; } else { ctxt->xpathCtxt->namespaces = NULL; ctxt->xpathCtxt->nsNr = 0; @@ -4618,7 +4436,7 @@ xsltChoose(xsltTransformContextPtr ctxt, xmlNodePtr node, #ifdef XSLT_REFACTORED if (wcomp->inScopeNs != NULL) { ctxt->xpathCtxt->namespaces = wcomp->inScopeNs->list; - ctxt->xpathCtxt->nsNr = wcomp->inScopeNs->number; + ctxt->xpathCtxt->nsNr = wcomp->inScopeNs->xpathNumber; } else { ctxt->xpathCtxt->namespaces = NULL; ctxt->xpathCtxt->nsNr = 0; @@ -4745,7 +4563,7 @@ xsltIf(xsltTransformContextPtr ctxt, xmlNodePtr node, #ifdef XSLT_REFACTORED if (comp->inScopeNs != NULL) { ctxt->xpathCtxt->namespaces = comp->inScopeNs->list; - ctxt->xpathCtxt->nsNr = comp->inScopeNs->number; + ctxt->xpathCtxt->nsNr = comp->inScopeNs->xpathNumber; } else { ctxt->xpathCtxt->namespaces = NULL; ctxt->xpathCtxt->nsNr = 0; @@ -4754,6 +4572,10 @@ xsltIf(xsltTransformContextPtr ctxt, xmlNodePtr node, ctxt->xpathCtxt->namespaces = comp->nsList; ctxt->xpathCtxt->nsNr = comp->nsNr; #endif + /* + * OPTIMIZE TODO: Use a specialized function, which returns only + * true/false. + */ res = xmlXPathCompiledEval(comp->comp, ctxt->xpathCtxt); ctxt->xpathCtxt->contextSize = oldContextSize; ctxt->xpathCtxt->proximityPosition = oldProximityPosition; @@ -4840,7 +4662,7 @@ xsltForEach(xsltTransformContextPtr ctxt, xmlNodePtr node, #ifdef XSLT_REFACTORED if (comp->inScopeNs != NULL) { ctxt->xpathCtxt->namespaces = comp->inScopeNs->list; - ctxt->xpathCtxt->nsNr = comp->inScopeNs->number; + ctxt->xpathCtxt->nsNr = comp->inScopeNs->xpathNumber; } else { ctxt->xpathCtxt->namespaces = NULL; ctxt->xpathCtxt->nsNr = 0; diff --git a/libxslt/variables.c b/libxslt/variables.c index 93635b4..124f02a 100644 --- a/libxslt/variables.c +++ b/libxslt/variables.c @@ -481,7 +481,7 @@ xsltEvalVariable(xsltTransformContextPtr ctxt, xsltStackElemPtr elem, #ifdef XSLT_REFACTORED if (precomp->inScopeNs != NULL) { ctxt->xpathCtxt->namespaces = precomp->inScopeNs->list; - ctxt->xpathCtxt->nsNr = precomp->inScopeNs->number; + ctxt->xpathCtxt->nsNr = precomp->inScopeNs->xpathNumber; } else { ctxt->xpathCtxt->namespaces = NULL; ctxt->xpathCtxt->nsNr = 0; @@ -650,7 +650,7 @@ xsltEvalGlobalVariable(xsltStackElemPtr elem, xsltTransformContextPtr ctxt) #ifdef XSLT_REFACTORED if (precomp->inScopeNs != NULL) { ctxt->xpathCtxt->namespaces = precomp->inScopeNs->list; - ctxt->xpathCtxt->nsNr = precomp->inScopeNs->number; + ctxt->xpathCtxt->nsNr = precomp->inScopeNs->xpathNumber; } else { ctxt->xpathCtxt->namespaces = NULL; ctxt->xpathCtxt->nsNr = 0; diff --git a/libxslt/xslt.c b/libxslt/xslt.c index a643d0a..96141c6 100644 --- a/libxslt/xslt.c +++ b/libxslt/xslt.c @@ -75,6 +75,12 @@ const xmlChar *xsltLiteralResultMarker = */ const xmlChar *xsltXSLTTextMarker = (const xmlChar *) "XSLT Text Element"; +/* +* xsltXSLTAttrMarker: +* Marker for XSLT attribute on Literal Result Elements. +*/ +const xmlChar *xsltXSLTAttrMarker = (const xmlChar *) "LRE XSLT Attr"; + #endif /* @@ -1151,6 +1157,9 @@ xsltParseStylesheetOutput(xsltStylesheetPtr style, xmlNodePtr cur) xmlFree(style->methodURI); style->methodURI = NULL; + /* + * TODO: Don't use xsltGetQNameURI(). + */ URI = xsltGetQNameURI(cur, &prop); if (prop == NULL) { if (style != NULL) style->errors++; @@ -1260,6 +1269,9 @@ xsltParseStylesheetOutput(xsltStylesheetPtr style, xmlNodePtr cur) } else { const xmlChar *URI; + /* + * TODO: Don't use xsltGetQNameURI(). + */ URI = xsltGetQNameURI(cur, &element); if (element == NULL) { /* @@ -1470,6 +1482,9 @@ xsltParseStylesheetPreserveSpace(xsltStylesheetPtr style, xmlNodePtr cur) { } else { const xmlChar *URI; + /* + * TODO: Don't use xsltGetQNameURI(). + */ URI = xsltGetQNameURI(cur, &element); xmlHashAddEntry2(style->stripSpaces, element, URI, @@ -1606,6 +1621,9 @@ xsltParseStylesheetStripSpace(xsltStylesheetPtr style, xmlNodePtr cur) { } else { const xmlChar *URI; + /* + * TODO: Don't use xsltGetQNameURI(). + */ URI = xsltGetQNameURI(cur, &element); xmlHashAddEntry2(style->stripSpaces, element, URI, @@ -1994,7 +2012,7 @@ xsltLREBuildEffectiveNsNodes(xsltCompilerCtxtPtr cctxt, extElemNs = cctxt->inode->extElemNs; exclResultNs = cctxt->inode->exclResultNs; - for (i = 0; i < item->inScopeNs->number; i++) { + for (i = 0; i < item->inScopeNs->totalNumber; i++) { ns = item->inScopeNs->list[i]; /* * Skip namespaces designated as excluded namespaces @@ -2064,7 +2082,14 @@ xsltLREBuildEffectiveNsNodes(xsltCompilerCtxtPtr cctxt, * Recognized as an namespace alias; convert it to * the target namespace. */ - ns = alias->literalNs; + if (alias->targetNs != NULL) + ns = alias->literalNs; + else { + /* + * The target is the NULL namespace. + */ + goto skip_ns; + } break; } alias = alias->next; @@ -2354,7 +2379,8 @@ static xsltNsListContainerPtr xsltCompilerBuildInScopeNsList(xsltCompilerCtxtPtr cctxt, xmlNodePtr node) { xsltNsListContainerPtr nsi = NULL; - xmlNsPtr *list = NULL; + xmlNsPtr *list = NULL, ns; + int i, maxns = 5; /* * Create a new ns-list for this position in the node-tree. * xmlGetNsList() will return NULL, if there are no ns-decls in the @@ -2362,26 +2388,75 @@ xsltCompilerBuildInScopeNsList(xsltCompilerCtxtPtr cctxt, xmlNodePtr node) * to the resulting list; the XPath module handles the XML namespace * internally. */ - list = xmlGetNsList(node->doc, node); - if (list == NULL) + while (node != NULL) { + if (node->type == XML_ELEMENT_NODE) { + ns = node->nsDef; + while (ns != NULL) { + if (nsi == NULL) { + nsi = (xsltNsListContainerPtr) + xmlMalloc(sizeof(xsltNsListContainer)); + if (nsi == NULL) { + xsltTransformError(NULL, cctxt->style, NULL, + "xsltCompilerBuildInScopeNsList: " + "malloc failed!\n"); + goto internal_err; + } + memset(nsi, 0, sizeof(xsltNsListContainer)); + nsi->list = + (xmlNsPtr *) xmlMalloc(maxns * sizeof(xmlNsPtr)); + if (nsi->list == NULL) { + xsltTransformError(NULL, cctxt->style, NULL, + "xsltCompilerBuildInScopeNsList: " + "malloc failed!\n"); + goto internal_err; + } + nsi->list[0] = NULL; + } + /* + * Skip shadowed namespace bindings. + */ + for (i = 0; i < nsi->totalNumber; i++) { + if ((ns->prefix == nsi->list[i]->prefix) || + (xmlStrEqual(ns->prefix, nsi->list[i]->prefix))) + break; + } + if (i >= nsi->totalNumber) { + if (nsi->totalNumber >= maxns) { + maxns *= 2; + nsi->list = + (xmlNsPtr *) xmlRealloc(nsi->list, + maxns * sizeof(xmlNsPtr)); + if (nsi->list == NULL) { + xsltTransformError(NULL, cctxt->style, NULL, + "xsltCompilerBuildInScopeNsList: " + "realloc failed!\n"); + goto internal_err; + } + } + nsi->list[nsi->totalNumber++] = ns; + nsi->list[nsi->totalNumber] = NULL; + } + + ns = ns->next; + } + } + node = node->parent; + } + if (nsi == NULL) return(NULL); /* - * Create the info-structure. + * Move the default namespace to last position. */ - nsi = (xsltNsListContainerPtr) xmlMalloc(sizeof(xsltNsListContainer)); - if (nsi == NULL) { - xsltTransformError(NULL, cctxt->style, NULL, - "xsltCompilerBuildInScopeNsList: malloc failed.\n"); - goto internal_err; + nsi->xpathNumber = nsi->totalNumber; + for (i = 0; i < nsi->totalNumber; i++) { + if (nsi->list[i]->prefix == NULL) { + ns = nsi->list[i]; + nsi->list[i] = nsi->list[nsi->totalNumber-1]; + nsi->list[nsi->totalNumber-1] = ns; + nsi->xpathNumber--; + break; + } } - memset(nsi, 0, sizeof(xsltNsListContainer)); - nsi->list = list; - /* - * Eval the number of ns-decls; this is used to speed up - * XPath-context initialization. - */ - while (list[nsi->number] != NULL) - nsi->number++; /* * Store the ns-list in the stylesheet. */ @@ -2551,19 +2626,36 @@ xsltParseExclResultPrefixes(xsltCompilerCtxtPtr cctxt, xmlNodePtr node, int instrCategory) { xsltPointerListPtr list = NULL; - xmlChar *value = NULL; + xmlChar *value; + xmlAttrPtr attr; if ((cctxt == NULL) || (node == NULL)) return(NULL); - + if (instrCategory == XSLT_ELEMENT_CATEGORY_XSLT) - value = xmlGetNsProp(node, BAD_CAST "exclude-result-prefixes", NULL); + attr = xmlHasNsProp(node, BAD_CAST "exclude-result-prefixes", NULL); else - value = xmlGetNsProp(node, BAD_CAST "exclude-result-prefixes", + attr = xmlHasNsProp(node, BAD_CAST "exclude-result-prefixes", XSLT_NAMESPACE); + if (attr == NULL) + return(def); - if (value == NULL) + if (attr && (instrCategory == XSLT_ELEMENT_CATEGORY_LRE)) { + /* + * Mark the XSLT attr. + */ + attr->psvi = (void *) xsltXSLTAttrMarker; + } + + if ((attr->children != NULL) && + (attr->children->content != NULL)) + value = attr->children->content; + else { + xsltTransformError(NULL, cctxt->style, node, + "Attribute 'exclude-result-prefixes': Invalid value.\n"); + cctxt->style->errors++; return(def); + } if (xsltParseNsPrefixList(cctxt, cctxt->tmpList, node, BAD_CAST value) != 0) @@ -2592,9 +2684,7 @@ xsltParseExclResultPrefixes(xsltCompilerCtxtPtr cctxt, xmlNodePtr node, if (cctxt->inode != NULL) cctxt->inode->nsChanged = 1; -exit: - if (value != NULL) - xmlFree(value); +exit: if (list != NULL) return(list); else @@ -2632,6 +2722,13 @@ xsltParseExtElemPrefixes(xsltCompilerCtxtPtr cctxt, xmlNodePtr node, if (attr == NULL) return(def); + if (attr && (instrCategory == XSLT_ELEMENT_CATEGORY_LRE)) { + /* + * Mark the XSLT attr. + */ + attr->psvi = (void *) xsltXSLTAttrMarker; + } + if ((attr->children != NULL) && (attr->children->content != NULL)) value = attr->children->content; @@ -2677,21 +2774,13 @@ xsltParseExtElemPrefixes(xsltCompilerCtxtPtr cctxt, xmlNodePtr node, if (cctxt->inode != NULL) cctxt->inode->nsChanged = 1; -exit: - if (attr && (instrCategory == XSLT_ELEMENT_CATEGORY_LRE)) { - /* - * Remove the XSLT attribute from the literal result element. - */ - xmlUnlinkNode((xmlNodePtr) attr); - xmlFreeProp(attr); - } +exit: if (list != NULL) return(list); else return(def); } - /* * xsltParseAttrXSLTVersion: * @@ -2722,6 +2811,8 @@ xsltParseAttrXSLTVersion(xsltCompilerCtxtPtr cctxt, xmlNodePtr node, if (attr == NULL) return(0); + attr->psvi = (void *) xsltXSLTAttrMarker; + if ((attr->children != NULL) && (attr->children->content != NULL)) value = attr->children->content; @@ -2758,35 +2849,13 @@ xsltParseAttrXSLTVersion(xsltCompilerCtxtPtr cctxt, xmlNodePtr node, if (attr && (instrCategory == XSLT_ELEMENT_CATEGORY_LRE)) { /* - * Remove the XSLT attribute from the literal result element. + * Set a marker on XSLT attributes. */ - xmlUnlinkNode((xmlNodePtr) attr); - xmlFreeProp(attr); + attr->psvi = (void *) xsltXSLTAttrMarker; } return(1); } -#if 0 -static int -xsltParseRemoveXSLTAttrs(xsltCompilerCtxtPtr cctxt, xmlNodePtr node) -{ - if (node->properties == NULL) - return(0); - else { - xmlAttrPtr tmpattr, attr = node->properties; - do { - if (IS_XSLT_ATTR_FAST(attr)) { - tmpattr = attr; - attr = attr->next; - xmlUnlinkNode((xmlNodePtr) tmpattr); - xmlFreeProp(tmpattr); - } else - attr = attr->next; - } while (attr != NULL); - } -} -#endif - static int xsltParsePreprocessStylesheetTree(xsltCompilerCtxtPtr cctxt, xmlNodePtr node) { @@ -4358,6 +4427,7 @@ xsltParseSequenceConstructor(xsltCompilerCtxtPtr cctxt, xmlNodePtr cur) cur->psvi = NULL; cctxt->inode->category = XSLT_ELEMENT_CATEGORY_LRE; if (cur->properties != NULL) { + xmlAttrPtr attr = cur->properties; /* * Attribute "xsl:exclude-result-prefixes". */ @@ -4371,24 +4441,35 @@ xsltParseSequenceConstructor(xsltCompilerCtxtPtr cctxt, xmlNodePtr cur) xsltParseAttrXSLTVersion(cctxt, cur, XSLT_ELEMENT_CATEGORY_LRE); /* - * Report invalid XSLT attributes. + * Report invalid XSLT attributes. + * For XSLT 1.0 only xsl:use-attribute-sets is allowed + * next to xsl:version, xsl:exclude-result-prefixes and + * xsl:extension-element-prefixes. + * + * Mark all XSLT attributes, in order to skip such + * attributes when instantiating the LRE. */ - if (cur->properties) { - xmlAttrPtr attr = cur->properties; - - do { - if (IS_XSLT_ATTR_FAST(attr) && - (! xmlStrEqual(attr->name, - BAD_CAST "use-attribute-sets"))) + do { + if ((attr->psvi != xsltXSLTAttrMarker) && + IS_XSLT_ATTR_FAST(attr)) + { + if (! xmlStrEqual(attr->name, + BAD_CAST "use-attribute-sets")) { - xsltTransformError(NULL, cctxt->style, cur, + xsltTransformError(NULL, cctxt->style, + cur, "Unknown XSLT attribute '%s'.\n", - attr->name); - cctxt->style->errors++; + attr->name); + cctxt->style->errors++; + } else { + /* + * XSLT attr marker. + */ + attr->psvi = (void *) xsltXSLTAttrMarker; } - attr = attr->next; - } while (attr != NULL); - } + } + attr = attr->next; + } while (attr != NULL); } /* * Create/reuse info for the literal result element. @@ -4732,6 +4813,9 @@ xsltParseStylesheetKey(xsltStylesheetPtr style, xmlNodePtr key) { if (prop != NULL) { const xmlChar *URI; + /* + * TODO: Don't use xsltGetQNameURI(). + */ URI = xsltGetQNameURI(key, &prop); if (prop == NULL) { if (style != NULL) style->errors++; @@ -4826,69 +4910,7 @@ xsltParseXSLTTemplate(xsltCompilerCtxtPtr cctxt, xmlNodePtr templNode) { templ->next = cctxt->style->templates; cctxt->style->templates = templ; - templ->style = cctxt->style; - - - /* - * Get inherited namespaces. - */ - if (cctxt->inode->inScopeNs != NULL) { - int i, j; - xmlNsPtr ns; - xsltPointerListPtr extElemNs = cctxt->inode->extElemNs; - xsltPointerListPtr exclResultNs = cctxt->inode->exclResultNs; - xsltNsListContainerPtr inScopeNs = cctxt->inode->inScopeNs; - - for (i = 0; i < inScopeNs->number; i++) { - ns = inScopeNs->list[i]; - /* - * Exclude the XSLT namespace. - */ - if (xmlStrEqual(ns->href, XSLT_NAMESPACE)) - goto skip_ns; - /* - * Exclude excluded result namespaces. - */ - if (exclResultNs) { - for (j = 0; j < exclResultNs->number; j++) - if (xmlStrEqual(ns->href, BAD_CAST exclResultNs->items[j])) - goto skip_ns; - } - /* - * Exclude extension-element namespaces. - */ - if (extElemNs) { - for (j = 0; j < extElemNs->number; j++) - if (xmlStrEqual(ns->href, BAD_CAST extElemNs->items[j])) - goto skip_ns; - } - /* - * Add the xmlNs item. - */ - if (templ->inheritedNs == NULL) { - templ->inheritedNs = (xmlNsPtr *) xmlMalloc( - inScopeNs->number * sizeof(xmlNsPtr)); - if (templ->inheritedNs == NULL) { - xmlGenericError(xmlGenericErrorContext, - "xsltGetInheritedNsList : out of memory!\n"); - cctxt->style->errors++; - goto error; - } - memset(templ->inheritedNs, 0, - inScopeNs->number * sizeof(xmlNsPtr)); - } - templ->inheritedNs[templ->inheritedNsNr++] = ns; -skip_ns: - {} - } -#ifdef WITH_XSLT_DEBUG_PARSING - if (templ->inheritedNsNr != 0) { - xsltGenericDebug(xsltGenericDebugContext, - "xsl:template has %d inherited namespaces\n", - templ->inheritedNsNr); - } -#endif - } + templ->style = cctxt->style; /* * Attribute "mode". @@ -4900,6 +4922,8 @@ skip_ns: /* * TODO: We need a standardized function for extraction * of namespace names and local names from QNames. + * Don't use xsltGetQNameURI() as it cannot channeƶ + * reports through the context. */ modeURI = xsltGetQNameURI(templNode, &prop); if (prop == NULL) { @@ -4949,6 +4973,9 @@ skip_ns: const xmlChar *nameURI; xsltTemplatePtr curTempl; + /* + * TODO: Don't use xsltGetQNameURI(). + */ nameURI = xsltGetQNameURI(templNode, &prop); if (prop == NULL) { cctxt->style->errors++; @@ -5056,6 +5083,9 @@ xsltParseStylesheetTemplate(xsltStylesheetPtr style, xmlNodePtr template) { if (prop != NULL) { const xmlChar *URI; + /* + * TODO: Don't use xsltGetQNameURI(). + */ URI = xsltGetQNameURI(template, &prop); if (prop == NULL) { if (style != NULL) style->errors++; @@ -5092,6 +5122,9 @@ xsltParseStylesheetTemplate(xsltStylesheetPtr style, xmlNodePtr template) { const xmlChar *URI; xsltTemplatePtr cur; + /* + * TODO: Don't use xsltGetQNameURI(). + */ URI = xsltGetQNameURI(template, &prop); if (prop == NULL) { if (style != NULL) style->errors++; diff --git a/libxslt/xsltInternals.h b/libxslt/xsltInternals.h index e76f170..89c068b 100644 --- a/libxslt/xsltInternals.h +++ b/libxslt/xsltInternals.h @@ -70,6 +70,9 @@ extern const xmlChar *xsltDocFragFake; /* ==================================================================== */ #ifdef XSLT_REFACTORED + +extern const xmlChar *xsltXSLTAttrMarker; + /* TODO: REMOVE: #define XSLT_REFACTORED_EXCLRESNS */ /* TODO: REMOVE: #define XSLT_REFACTORED_NSALIAS */ @@ -80,7 +83,7 @@ extern const xmlChar *xsltDocFragFake; * Internal define to enable the pointer-comparison of * namespaces of XSLT elements. */ -#define XSLT_REFACTORED_XSLT_NSCOMP +/* #define XSLT_REFACTORED_XSLT_NSCOMP */ /** * XSLT_REFACTORED_XPATHCOMP @@ -466,7 +469,8 @@ typedef struct _xsltNsListContainer xsltNsListContainer; typedef xsltNsListContainer *xsltNsListContainerPtr; struct _xsltNsListContainer { xmlNsPtr *list; - int number; + int totalNumber; + int xpathNumber; }; /** @@ -590,13 +594,13 @@ typedef xsltStyleItemElement *xsltStyleItemElementPtr; struct _xsltStyleItemElement { XSLT_ITEM_COMMON_FIELDS - const xmlChar *use; /* copy, element */ - int has_use; /* copy, element */ - const xmlChar *name; /* element, attribute, pi */ - int has_name; /* element, attribute, pi */ - const xmlChar *ns; /* element */ - int has_ns; /* element */ - + const xmlChar *use; + int has_use; + const xmlChar *name; + int has_name; + const xmlChar *ns; + const xmlChar *nsPrefix; + int has_ns; }; /** @@ -614,10 +618,11 @@ typedef xsltStyleItemAttribute *xsltStyleItemAttributePtr; struct _xsltStyleItemAttribute { XSLT_ITEM_COMMON_FIELDS - const xmlChar *name; /* element, attribute, pi */ - int has_name; /* element, attribute, pi */ - const xmlChar *ns; /* element attribute */ - int has_ns; /* element attribute */ + const xmlChar *name; + int has_name; + const xmlChar *ns; + const xmlChar *nsPrefix; + int has_ns; }; /** @@ -689,7 +694,7 @@ typedef struct _xsltStyleItemApplyTemplates xsltStyleItemApplyTemplates; typedef xsltStyleItemApplyTemplates *xsltStyleItemApplyTemplatesPtr; struct _xsltStyleItemApplyTemplates { - XSLT_ITEM_COMMON_FIELDS + XSLT_ITEM_COMMON_FIELDS const xmlChar *mode; /* apply-templates */ const xmlChar *modeURI; /* apply-templates */ diff --git a/libxslt/xsltutils.c b/libxslt/xsltutils.c index d9b094e..ed3aeb2 100644 --- a/libxslt/xsltutils.c +++ b/libxslt/xsltutils.c @@ -995,7 +995,7 @@ xsltComputeSortResult(xsltTransformContextPtr ctxt, xmlNodePtr sort) { #ifdef XSLT_REFACTORED if (comp->inScopeNs != NULL) { ctxt->xpathCtxt->namespaces = comp->inScopeNs->list; - ctxt->xpathCtxt->nsNr = comp->inScopeNs->number; + ctxt->xpathCtxt->nsNr = comp->inScopeNs->xpathNumber; } else { ctxt->xpathCtxt->namespaces = NULL; ctxt->xpathCtxt->nsNr = 0; -- 2.7.4