From 76ccb3003459ef0bac2fb6b5ac5c818ebb2e8674 Mon Sep 17 00:00:00 2001 From: "Kasimier T. Buchcik" Date: Fri, 12 May 2006 21:31:53 +0000 Subject: [PATCH] Next step of refactoring. For more details see bug #341588. I applied the * libxslt/documents.c libxslt/namespaces.c libxslt/preproc.c libxslt/transform.c libxslt/xslt.c libxslt/xsltInternals.h libxslt/xsltutils.c: Next step of refactoring. For more details see bug #341588. I applied the suggestion of Jerome Pesenti to the refactored (still IDFEDed out with XSLT_REFACTORED) code: The XPath compilation context (accessible via the compilation context) is now reused for compilation of expressions at compilation-time; this should reduce compilation time to 50% for avarage stylesheets. --- ChangeLog | 13 + libxslt/documents.c | 10 +- libxslt/namespaces.c | 373 ++++++++++++---- libxslt/preproc.c | 28 +- libxslt/transform.c | 624 ++++++++++++++++++++++++++- libxslt/xslt.c | 1095 +++++++++++++++++++++++++++++++++-------------- libxslt/xsltInternals.h | 171 ++++++-- libxslt/xsltutils.c | 48 ++- 8 files changed, 1907 insertions(+), 455 deletions(-) diff --git a/ChangeLog b/ChangeLog index fb74489..ea37ba9 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,16 @@ +Fri May 12 23:23:06 CEST 2006 Kasimier Buchcik + + * libxslt/documents.c libxslt/namespaces.c + libxslt/preproc.c libxslt/transform.c + libxslt/xslt.c libxslt/xsltInternals.h libxslt/xsltutils.c: + Next step of refactoring. For more details see bug #341588. + I applied the suggestion of Jerome Pesenti to the refactored + (still IDFEDed out with XSLT_REFACTORED) code: The XPath + compilation context (accessible via the compilation context) + is now reused for compilation of expressions at + compilation-time; this should reduce compilation time to 50% + for avarage stylesheets. + Thu May 11 22:12:22 CEST 2006 Kasimier Buchcik * libxslt/extensions.c: Changed a comment to indicate that a diff --git a/libxslt/documents.c b/libxslt/documents.c index 0963e1c..f762185 100644 --- a/libxslt/documents.c +++ b/libxslt/documents.c @@ -201,33 +201,31 @@ xsltNewStyleDocument(xsltStylesheetPtr style, xmlDocPtr doc) { void xsltFreeStyleDocuments(xsltStylesheetPtr style) { xsltDocumentPtr doc, cur; -#ifdef XSLT_REFACTORED +#ifdef XSLT_REFACTORED_XSLT_NSCOMP xsltNsMapPtr nsMap; #endif if (style == NULL) return; -#ifdef XSLT_REFACTORED +#ifdef XSLT_REFACTORED_XSLT_NSCOMP if (XSLT_HAS_INTERNAL_NSMAP(style)) nsMap = XSLT_GET_INTERNAL_NSMAP(style); else nsMap = NULL; -#endif +#endif cur = style->docList; while (cur != NULL) { doc = cur; cur = cur->next; - -#ifdef XSLT_REFACTORED +#ifdef XSLT_REFACTORED_XSLT_NSCOMP /* * Restore all changed namespace URIs of ns-decls. */ if (nsMap) xsltRestoreDocumentNamespaces(nsMap, doc->doc); #endif - xsltFreeDocumentKeys(doc); if (!doc->main) xmlFreeDoc(doc->doc); diff --git a/libxslt/namespaces.c b/libxslt/namespaces.c index 063741b..b9bedc2 100644 --- a/libxslt/namespaces.c +++ b/libxslt/namespaces.c @@ -52,6 +52,32 @@ * * ************************************************************************/ +#ifdef XSLT_REFACTORED +static xsltNsAliasPtr +xsltNewNsAlias(xsltCompilerCtxtPtr cctxt) +{ + xsltNsAliasPtr ret; + + if (cctxt == NULL) + return(NULL); + + ret = (xsltNsAliasPtr) xmlMalloc(sizeof(xsltNsAlias)); + if (ret == NULL) { + xsltTransformError(NULL, cctxt->style, NULL, + "Internal error in xsltNewNsAlias(): Memory allocation failed.\n"); + cctxt->style->errors++; + return(NULL); + } + memset(ret, 0, sizeof(xsltNsAlias)); + /* + * TODO: Store the item at current stylesheet-level. + */ + ret->next = cctxt->nsAliases; + cctxt->nsAliases = ret; + + return(ret); +} +#endif /* XSLT_REFACTORED */ /** * xsltNamespaceAlias: * @style: the XSLT stylesheet @@ -61,46 +87,163 @@ * them as well as the corresponding namespace. */ void -xsltNamespaceAlias(xsltStylesheetPtr style, xmlNodePtr node) { - xmlChar *sprefix; - xmlNsPtr sNs; - const xmlChar *shref; - xmlChar *rprefix; - xmlNsPtr rNs; - const xmlChar *rhref; - - sprefix = xmlGetNsProp(node, (const xmlChar *)"stylesheet-prefix", NULL); - if (sprefix == NULL) { +xsltNamespaceAlias(xsltStylesheetPtr style, xmlNodePtr node) +{ + xmlChar *resultPrefix = NULL; + xmlChar *stylePrefix = NULL; + xmlNsPtr literalNs = NULL; + xmlNsPtr targetNs = NULL; + +#ifdef XSLT_REFACTORED + xsltNsAliasPtr alias; + + if ((style == NULL) || (node == NULL)) + return; + + /* + * SPEC XSLT 1.0: + * "If a namespace URI is declared to be an alias for multiple + * different namespace URIs, then the declaration with the highest + * import precedence is used. It is an error if there is more than + * one such declaration. An XSLT processor may signal the error; + * if it does not signal the error, it must recover by choosing, + * from amongst the declarations with the highest import precedence, + * the one that occurs last in the stylesheet." + * + * SPEC TODO: Check for the errors mentioned above. + */ + /* + * NOTE that the XSLT 2.0 also *does* use the NULL namespace if + * "#default" is used and there's no default namespace is scope. + * I.e., this is *not* an error. + * Most XSLT 1.0 implementations work this way. + * The XSLT 1.0 spec has nothing to say on the subject. + */ + /* + * Attribute "stylesheet-prefix". + */ + stylePrefix = xmlGetNsProp(node, (const xmlChar *)"stylesheet-prefix", NULL); + if (stylePrefix == NULL) { + xsltTransformError(NULL, style, node, + "The attribute 'stylesheet-prefix' is missing.\n"); + return; + } + if (xmlStrEqual(stylePrefix, (const xmlChar *)"#default")) + literalNs = xmlSearchNs(node->doc, node, NULL); + else { + literalNs = xmlSearchNs(node->doc, node, stylePrefix); + if (literalNs == NULL) { + xsltTransformError(NULL, style, node, + "Attribute 'stylesheet-prefix': There's no namespace " + "declaration in scope for the prefix '%s'.\n", + stylePrefix); + goto error; + } + } + /* + * Attribute "result-prefix". + */ + resultPrefix = xmlGetNsProp(node, (const xmlChar *)"result-prefix", NULL); + if (resultPrefix == NULL) { + xsltTransformError(NULL, style, node, + "The attribute 'result-prefix' is missing.\n"); + goto error; + } + if (xmlStrEqual(resultPrefix, (const xmlChar *)"#default")) + targetNs = xmlSearchNs(node->doc, node, NULL); + else { + targetNs = xmlSearchNs(node->doc, node, resultPrefix); + + if (targetNs == NULL) { + xsltTransformError(NULL, style, node, + "Attribute 'result-prefix': There's no namespace " + "declaration in scope for the prefix '%s'.\n", + stylePrefix); + goto error; + } + } + /* + * + * Same alias for multiple different target namespace URIs: + * TODO: The one with the highest import precedence is used. + * Example: + * + * + * + * + * Same target namespace URI for multiple different aliases: + * All alias-definitions will be used. + * Example: + * + * + * + * Cases using #default: + * + * TODO: Has this an effect at all? + * + * + * From namespace to no namespace. + * + * + * From no namespace to namespace. + */ + + + /* + * 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 */ + const xmlChar *literalNsName; + const xmlChar *targetNsName; + + + if ((style == NULL) || (node == NULL)) + return; + + stylePrefix = xmlGetNsProp(node, (const xmlChar *)"stylesheet-prefix", NULL); + if (stylePrefix == NULL) { xsltTransformError(NULL, style, node, "namespace-alias: stylesheet-prefix attribute missing\n"); return; } - rprefix = xmlGetNsProp(node, (const xmlChar *)"result-prefix", NULL); - if (rprefix == NULL) { + resultPrefix = xmlGetNsProp(node, (const xmlChar *)"result-prefix", NULL); + if (resultPrefix == NULL) { xsltTransformError(NULL, style, node, "namespace-alias: result-prefix attribute missing\n"); goto error; } - if (xmlStrEqual(sprefix, (const xmlChar *)"#default")) { - /* - * Do we have a default namespace previously declared? - */ - sNs = xmlSearchNs(node->doc, node, NULL); - if (sNs == NULL) - shref = NULL; /* No - set NULL */ - else - shref = sNs->href; /* Yes - set for nsAlias table */ + if (xmlStrEqual(stylePrefix, (const xmlChar *)"#default")) { + literalNs = xmlSearchNs(node->doc, node, NULL); + if (literalNs == NULL) { + literalNsName = NULL; + } else + literalNsName = literalNs->href; /* Yes - set for nsAlias table */ } else { - sNs = xmlSearchNs(node->doc, node, sprefix); + literalNs = xmlSearchNs(node->doc, node, stylePrefix); - if ((sNs == NULL) || (sNs->href == NULL)) { + if ((literalNs == NULL) || (literalNs->href == NULL)) { xsltTransformError(NULL, style, node, "namespace-alias: prefix %s not bound to any namespace\n", - sprefix); + stylePrefix); goto error; } else - shref = sNs->href; + literalNsName = literalNs->href; } /* @@ -108,30 +251,43 @@ xsltNamespaceAlias(xsltStylesheetPtr style, xmlNodePtr node) { * been explicitly declared the special value UNDEFINED_DEFAULT_NS is * put into the nsAliases table */ - if (xmlStrEqual(rprefix, (const xmlChar *)"#default")) { - rNs = xmlSearchNs(node->doc, node, NULL); - if (rNs == NULL) - rhref = UNDEFINED_DEFAULT_NS; - else - rhref = rNs->href; + if (xmlStrEqual(resultPrefix, (const xmlChar *)"#default")) { + targetNs = xmlSearchNs(node->doc, node, NULL); + if (targetNs == NULL) { + targetNsName = UNDEFINED_DEFAULT_NS; + } else + targetNsName = targetNs->href; } else { - rNs = xmlSearchNs(node->doc, node, rprefix); + targetNs = xmlSearchNs(node->doc, node, resultPrefix); - if ((rNs == NULL) || (rNs->href == NULL)) { + if ((targetNs == NULL) || (targetNs->href == NULL)) { xsltTransformError(NULL, style, node, "namespace-alias: prefix %s not bound to any namespace\n", - rprefix); + resultPrefix); goto error; } else - rhref = rNs->href; + targetNsName = targetNs->href; } /* - * Special case if #default is used for stylesheet and no default has - * been explicitly declared. We use style->defaultAlias for this - */ - if (shref == NULL) { - if (rNs != NULL) - style->defaultAlias = rNs->href; + * Special case: if #default is used for + * the stylesheet-prefix (literal namespace) and there's no default + * namespace in scope, we'll use style->defaultAlias for this. + */ + if (literalNsName == NULL) { + if (targetNs != NULL) { + /* + * BUG TODO: Is it not sufficient to have only 1 field for + * this, since subsequently alias declarations will + * overwrite this. + * Example: + * + * + * The mapping for "foo" won't be visible anymore. + */ + style->defaultAlias = targetNs->href; + } } else { if (style->nsAliases == NULL) style->nsAliases = xmlHashCreate(10); @@ -140,15 +296,16 @@ xsltNamespaceAlias(xsltStylesheetPtr style, xmlNodePtr node) { "namespace-alias: cannot create hash table\n"); goto error; } - xmlHashAddEntry((xmlHashTablePtr) style->nsAliases, - shref, (void *) rhref); + xmlHashAddEntry((xmlHashTablePtr) style->nsAliases, + literalNsName, (void *) targetNsName); } +#endif /* else of XSLT_REFACTORED */ error: - if (sprefix != NULL) - xmlFree(sprefix); - if (rprefix != NULL) - xmlFree(rprefix); + if (stylePrefix != NULL) + xmlFree(stylePrefix); + if (resultPrefix != NULL) + xmlFree(resultPrefix); } /** @@ -255,27 +412,39 @@ xsltSearchPlainNsByHref(xmlDocPtr doc, xmlNodePtr node, const xmlChar * href) * 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) * * Returns the namespace node to use or NULL */ xmlNsPtr xsltGetPlainNamespace(xsltTransformContextPtr ctxt, xmlNodePtr cur, - xmlNsPtr ns, xmlNodePtr out) { - xsltStylesheetPtr style; + xmlNsPtr ns, xmlNodePtr out) { xmlNsPtr ret; const xmlChar *URI = NULL; /* the replacement URI */ if ((ctxt == NULL) || (cur == NULL) || (out == NULL) || (ns == NULL)) return(NULL); - style = ctxt->style; - while (style != NULL) { - if (style->nsAliases != NULL) - URI = (const xmlChar *) xmlHashLookup(style->nsAliases, ns->href); - if (URI != NULL) - break; - - style = xsltNextImport(style); +#ifdef XSLT_REFACTORED + /* + * Namespace exclusion and ns-aliasing is performed at + * compilation-time in the refactored code. + */ + 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; + + style = xsltNextImport(style); + } } if (URI == UNDEFINED_DEFAULT_NS) { @@ -289,6 +458,7 @@ xsltGetPlainNamespace(xsltTransformContextPtr ctxt, xmlNodePtr cur, if (URI == NULL) URI = ns->href; +#endif if ((out->parent != NULL) && (out->parent->type == XML_ELEMENT_NODE) && @@ -351,8 +521,9 @@ xsltGetSpecialNamespace(xsltTransformContextPtr ctxt, xmlNodePtr cur, (out->parent->type == XML_ELEMENT_NODE) && (out->parent->ns != NULL) && (xmlStrEqual(out->parent->ns->href, URI))) + { ret = out->parent->ns; - else + } else ret = xmlSearchNsByHref(out->doc, out, URI); if ((ret == NULL) || (ret->prefix == NULL)) { @@ -381,6 +552,9 @@ 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 the right namespace value for this prefix, if needed create * and add a new namespace decalaration on the node * Handle namespace aliases @@ -389,27 +563,38 @@ xsltGetSpecialNamespace(xsltTransformContextPtr ctxt, xmlNodePtr cur, */ xmlNsPtr xsltGetNamespace(xsltTransformContextPtr ctxt, xmlNodePtr cur, xmlNsPtr ns, - xmlNodePtr out) { - xsltStylesheetPtr style; + xmlNodePtr out) { xmlNsPtr ret; const xmlChar *URI = NULL; /* the replacement URI */ if ((ctxt == NULL) || (cur == NULL) || (out == NULL) || (ns == NULL)) return(NULL); - - style = ctxt->style; - while (style != NULL) { - if (style->nsAliases != NULL) - URI = (const xmlChar *) + +#ifdef XSLT_REFACTORED + /* + * Namespace exclusion and ns-aliasing is performed at + * compilation-time in the refactored code. + */ + 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; - - style = xsltNextImport(style); + if (URI != NULL) + break; + + style = xsltNextImport(style); + } } if (URI == UNDEFINED_DEFAULT_NS) { xmlNsPtr dflt; + /* + */ dflt = xmlSearchNs(cur->doc, cur, NULL); if (dflt != NULL) URI = dflt->href; @@ -417,7 +602,7 @@ xsltGetNamespace(xsltTransformContextPtr ctxt, xmlNodePtr cur, xmlNsPtr ns, return NULL; } else if (URI == NULL) URI = ns->href; - +#endif /* * If the parent is an XML_ELEMENT_NODE, and has the "equivalent" * namespace as ns (either both default, or both with a prefix @@ -468,7 +653,14 @@ xsltGetNamespace(xsltTransformContextPtr ctxt, xmlNodePtr cur, xmlNsPtr ns, * * Do a copy of an namespace list. If @node is non-NULL the * new namespaces are added automatically. This handles namespaces - * aliases + * aliases. + * This function is intended only for *internal* use at + * transformation-time. Use it *only* for copying ns-decls of + * literal result elements. + * + * Called by: + * xsltCopyTree() (transform.c) + * xsltCopyNode() (transform.c) * * Returns: a new xmlNsPtr, or NULL in case of error. */ @@ -476,8 +668,7 @@ xmlNsPtr xsltCopyNamespaceList(xsltTransformContextPtr ctxt, xmlNodePtr node, xmlNsPtr cur) { xmlNsPtr ret = NULL, tmp; - xmlNsPtr p = NULL,q; - const xmlChar *URI; + xmlNsPtr p = NULL,q; if (cur == NULL) return(NULL); @@ -495,12 +686,13 @@ xsltCopyNamespaceList(xsltTransformContextPtr ctxt, xmlNodePtr node, break; /* - * Avoid duplicating namespace declrations on the tree + * Avoid duplicating namespace declarations in the tree if + * a matching declaration is in scope. */ if (node != NULL) { if ((node->ns != NULL) && - (xmlStrEqual(node->ns->href, cur->href)) && - (xmlStrEqual(node->ns->prefix, cur->prefix))) { + (xmlStrEqual(node->ns->prefix, cur->prefix)) && + (xmlStrEqual(node->ns->href, cur->href))) { cur = cur->next; continue; } @@ -510,8 +702,21 @@ xsltCopyNamespaceList(xsltTransformContextPtr ctxt, xmlNodePtr node, continue; } } - +#ifdef XSLT_REFACTORED + /* + * Namespace exclusion and ns-aliasing is performed at + * compilation-time in the refactored code. + */ + q = xmlNewNs(node, cur->href, cur->prefix); + if (p == NULL) { + ret = p = q; + } else { + p->next = q; + p = q; + } +#else if (!xmlStrEqual(cur->href, XSLT_NAMESPACE)) { + const xmlChar *URI; /* TODO apply cascading */ URI = (const xmlChar *) xmlHashLookup(ctxt->style->nsAliases, cur->href); @@ -529,6 +734,7 @@ xsltCopyNamespaceList(xsltTransformContextPtr ctxt, xmlNodePtr node, p = q; } } +#endif cur = cur->next; } return(ret); @@ -550,8 +756,7 @@ xmlNsPtr xsltCopyNamespace(xsltTransformContextPtr ctxt, xmlNodePtr node, xmlNsPtr cur) { xmlNsPtr ret = NULL; - const xmlChar *URI; - + if (cur == NULL) return(NULL); if (cur->type != XML_NAMESPACE_DECL) @@ -563,7 +768,16 @@ xsltCopyNamespace(xsltTransformContextPtr ctxt, xmlNodePtr node, 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) @@ -574,6 +788,7 @@ xsltCopyNamespace(xsltTransformContextPtr ctxt, xmlNodePtr node, ret = xmlNewNs(node, cur->href, cur->prefix); } } +#endif return(ret); } diff --git a/libxslt/preproc.c b/libxslt/preproc.c index 3e2d70c..6e8bf03 100644 --- a/libxslt/preproc.c +++ b/libxslt/preproc.c @@ -370,6 +370,20 @@ xsltNewStylePreComp(xsltStylesheetPtr style, xsltStyleType type) { return(cur); } +#ifdef XSLT_REFACTORED +static void +xsltLREEffectiveNsNodesFree(xsltEffectiveNsPtr first) +{ + xsltEffectiveNsPtr tmp; + + while (first != NULL) { + tmp = first; + first = first->next; + xmlFree(tmp); + } +} +#endif + /** * xsltFreeStylePreComp: * @comp: an XSLT Style precomputed block @@ -385,6 +399,13 @@ xsltFreeStylePreComp(xsltStylePreCompPtr comp) { * URGENT TODO: Implement destructors. */ switch (comp->type) { + case XSLT_FUNC_LITERAL_RESULT_ELEMENT: { + xsltStyleItemLRElementInfoPtr item = + (xsltStyleItemLRElementInfoPtr) comp; + if (item->effectiveNs) + xsltLREEffectiveNsNodesFree(item->effectiveNs); + } + break; case XSLT_FUNC_COPY: break; case XSLT_FUNC_SORT: { @@ -473,7 +494,12 @@ xsltFreeStylePreComp(xsltStylePreCompPtr comp) { xmlXPathFreeCompExpr(item->comp); } break; - case XSLT_FUNC_OTHERWISE: + case XSLT_FUNC_OTHERWISE: + case XSLT_FUNC_FALLBACK: + case XSLT_FUNC_MESSAGE: + case XSLT_FUNC_INCLUDE: + case XSLT_FUNC_ATTRSET: + break; default: /* TODO: Raise error. */ diff --git a/libxslt/transform.c b/libxslt/transform.c index ae66aa0..0c94128 100644 --- a/libxslt/transform.c +++ b/libxslt/transform.c @@ -851,6 +851,11 @@ xsltCopyPropList(xsltTransformContextPtr ctxt, xmlNodePtr target, * * 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. + * Called from: + * xsltApplyOneTemplateInt() + * xsltCopy() * * Returns a pointer to the new node, or NULL in case of error */ @@ -933,6 +938,11 @@ 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() + * + * TODO: What is the exact difference between this function + * and xsltCopyNamespaceList() in "namespaces.c"? * * Returns: a new xmlNsPtr, or NULL in case of error. */ @@ -1064,6 +1074,12 @@ xsltCopyTree(xsltTransformContextPtr ctxt, xmlNodePtr node, xmlNsPtr *nsList, *cur, ns; /* * must add in any new namespaces in scope for the node + * REVISIT: + * Question: Do we really have to add every namespace in scope? + * Answer: I think yes, since if we are only adding the + * ns-decls which are declared on this element and actually + * referenced by this element/attribute, then we would miss + * ns-decls for QName in element/attribute content. */ nsList = xmlGetNsList(node->doc, node); if (nsList != NULL) { @@ -1127,8 +1143,9 @@ xsltApplyFallbacks(xsltTransformContextPtr ctxt, xmlNodePtr node, xmlNodePtr child; int ret = 0; - if ((ctxt == NULL) || (node == NULL) || (inst == NULL)) - return(ret); + if ((ctxt == NULL) || (node == NULL) || (inst == NULL) || + (inst->children == NULL)) + return(0); child = inst->children; while (child != NULL) { @@ -1465,6 +1482,186 @@ xsltProcessOneNode(xsltTransformContextPtr ctxt, xmlNodePtr node, } } +/** +* xsltTransLREAcquireInScopeNs: +* @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. +*/ +xmlNsPtr +xsltTransLREAcquireInScopeNs(xsltTransformContextPtr ctxt, + xmlNodePtr cur, xmlNsPtr literalNs, + xmlNodePtr resultElem) +{ + xmlNsPtr ns; + int prefixOccupied = 0; + + if ((ctxt == NULL) || (cur == NULL) || + (resultElem == NULL) || (literalNs == 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. + */ +#if 0 + ns = xmlSearchNsByHref(resultElem->doc, resultElem, + literalNs->href); + if (ns != NULL) + return(ns); +#endif + /* + * 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); +} /** * xsltApplyOneTemplate: @@ -1513,6 +1710,9 @@ xsltApplyOneTemplateInt(xsltTransformContextPtr ctxt, xmlNodePtr node, xmlNodePtr oldInst = NULL; int oldBase; xmlDocPtr tmpRVT = NULL; +#ifdef XSLT_REFACTORED + xsltStylePreCompPtr info; +#endif int level = 0; @@ -1615,7 +1815,7 @@ xsltApplyOneTemplateInt(xsltTransformContextPtr ctxt, xmlNodePtr node, if (insert == NULL) { #ifdef WITH_XSLT_DEBUG_PROCESS XSLT_TRACE(ctxt,XSLT_TRACE_APPLY_TEMPLATE,xsltGenericDebug(xsltGenericDebugContext, - "xsltApplyOneTemplate: insert == NULL !\n")); + "xsltApplyOneTemplateInt: insert == NULL !\n")); #endif goto error; } @@ -1625,10 +1825,403 @@ xsltApplyOneTemplateInt(xsltTransformContextPtr ctxt, xmlNodePtr node, #endif #ifdef XSLT_REFACTORED - if (IS_XSLT_ELEM_FAST(cur)) { -#else - if (IS_XSLT_ELEM(cur)) { + if (cur->type == XML_ELEMENT_NODE) { + info = (xsltStylePreCompPtr) cur->psvi; + /* + * We expect a compiled representation on: + * 1) XSLT instructions of this XSLT version (1.0) + * (with a few exceptions) + * 2) Literal result elements + * 3) Extension instructions + * 4) XSLT instructions of future XSLT versions + * (forwards-compatible mode). + */ + if (info == NULL) { + /* + * Handle the rare cases where we don't expect a compiled + * representation on an XSLT element. + */ + if (IS_XSLT_ELEM_FAST(cur) && IS_XSLT_NAME(cur, "message")) { + xsltMessage(ctxt, node, cur); + goto skip_children; + } + /* + * Something really went wrong: + */ + xsltTransformError(ctxt, NULL, cur, + "Internal error in xsltApplyOneTemplateInt(): " + "The element '%s' in the stylesheet has no compiled " + "representation.\n", + cur->name); + goto skip_children; + } + + if (info->type == XSLT_FUNC_LITERAL_RESULT_ELEMENT) { + xsltStyleItemLRElementInfoPtr lrInfo = + (xsltStyleItemLRElementInfoPtr) info; + /* + * Literal result elements + * -------------------------------------------------------- + */ +#ifdef WITH_XSLT_DEBUG_PROCESS + XSLT_TRACE(ctxt, XSLT_TRACE_APPLY_TEMPLATE, + xsltGenericDebug(xsltGenericDebugContext, + "xsltApplyOneTemplateInt: copy literal result " + "element '%s'\n", cur->name)); #endif + /* + * Copy the raw element-node. + * OLD: if ((copy = xsltCopyNode(ctxt, cur, insert)) == NULL) + * goto error; + */ + copy = xmlDocCopyNode(cur, insert->doc, 0); + if (copy == NULL) { + xsltTransformError(ctxt, NULL, cur, + "Internal error in xsltApplyOneTemplateInt(): " + "Failed to copy literal result element '%s'.\n", + cur->name); + goto error; + } else { + /* + * Add the element-node to the result tree. + */ + copy->doc = ctxt->output; + xmlAddChild(insert, copy); + /* + * Create effective namespaces declarations. + * OLD: xsltCopyNamespaceList(ctxt, copy, cur->nsDef); + */ + if (lrInfo->effectiveNs != NULL) { + xsltEffectiveNsPtr effNs = lrInfo->effectiveNs; + xmlNsPtr ns, lastns = NULL; + + while (effNs != NULL) { + /* + * Avoid generating redundant namespace + * declarations; thus lookup if there is already + * such a ns-decl in the result. + */ + ns = xmlSearchNs(copy->doc, copy, + effNs->prefix); + if ((ns != NULL) && + (xmlStrEqual(ns->href, effNs->nsName))) + { + effNs = effNs->next; + continue; + } + ns = xmlNewNs(copy, effNs->nsName, + effNs->prefix); + if (ns == NULL) { + xsltTransformError(ctxt, NULL, cur, + "Internal error in xsltApplyOneTemplateInt(): " + "Failed to copy a namespace declaration.\n"); + goto error; + } + + if (lastns == NULL) + copy->nsDef = ns; + else + lastns->next =ns; + lastns = ns; + + effNs = effNs->next; + } + + } + /* + * NOTE that we don't need to apply ns-alising: this was + * already done at compile-time. + */ + 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 = xsltTransLREAcquireInScopeNs(ctxt, + cur, cur->ns, copy); + } else if ((insert->type == XML_ELEMENT_NODE) && + (insert->ns != NULL)) + { + xmlNsPtr defaultNs; + + /* + * Undeclare the default namespace if needed. + * REVISIT TODO: This might result in massive + * generation of ns-decls if nodes in a default + * namespaces are mixed with nodes in no namespace. + * + */ + defaultNs = xmlSearchNs(insert->doc, insert, NULL); + if ((defaultNs != NULL) && (defaultNs->href != NULL)) + xmlNewNs(copy, BAD_CAST "", NULL); + } + } + /* + * SPEC XSLT 2.0 "Each attribute of the literal result + * element, other than an attribute in the XSLT namespace, + * is processed to produce an attribute for the element in + * the result tree." + * TODO: Refactor this, since it still uses ns-aliasing. + */ + 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 + * -------------------------------------------------------- + */ + if (info->type == XSLT_FUNC_UNKOWN_FORWARDS_COMPAT) { + /* + * We hit an unknown XSLT element. + * Try to apply one of the fallback cases. + */ + ctxt->insert = insert; + if (!xsltApplyFallbacks(ctxt, node, cur)) { + xsltTransformError(ctxt, NULL, cur, + "The is no fallback behaviour defined for " + "the unknown XSLT element '%s'.\n", + cur->name); + } + ctxt->insert = oldInsert; + goto skip_children; + } + /* + * Execute the XSLT instruction. + */ + if (info->func != NULL) { + ctxt->insert = insert; + info->func(ctxt, node, cur, (xsltElemPreCompPtr) info); + ctxt->insert = oldInsert; + goto skip_children; + } + /* + * Some XSLT instructions need custom execution. + */ + if (info->type == XSLT_FUNC_VARIABLE) { + if (level != 0) { + /* + * Build a new subframe and skip all the nodes + * at that level. + */ + ctxt->insert = insert; + xsltApplyOneTemplateInt(ctxt, node, cur, NULL, NULL, 0); + while (cur->next != NULL) + cur = cur->next; + ctxt->insert = oldInsert; + } else { + xsltParseStylesheetVariable(ctxt, cur); + } + } else if (info->type == XSLT_FUNC_PARAM) { + xsltParseStylesheetParam(ctxt, cur); + } else if (info->type == XSLT_FUNC_MESSAGE) { + /* + * TODO: Won't be hit, since we don't compile xsl:message. + */ + xsltMessage(ctxt, node, cur); + } else { + xsltGenericError(xsltGenericErrorContext, + "Internal error in xsltApplyOneTemplateInt(): " + "Don't know how to process the XSLT element " + "'%s'.\n", cur->name); + } + goto skip_children; + + } else { + xsltTransformFunction func; + /* + * Extension intructions (elements) + * -------------------------------------------------------- + */ + if (cur->psvi == xsltExtMarker) { + /* + * The xsltExtMarker was set during the compilation + * of extension instructions if there was no registered + * handler for this specific extension function at + * compile-time. + * Libxslt will now lookup if a handler is + * registered in the context of this transformation. + */ + func = (xsltTransformFunction) + xsltExtElementLookup(ctxt, cur->name, cur->ns->href); + } else + func = ((xsltElemPreCompPtr) cur->psvi)->func; + + if (func == NULL) { + /* + * No handler available. + * Try to execute fallback behaviour via xsl:fallback. + */ +#ifdef WITH_XSLT_DEBUG_PROCESS + XSLT_TRACE(ctxt, XSLT_TRACE_APPLY_TEMPLATE, + xsltGenericDebug(xsltGenericDebugContext, + "xsltApplyOneTemplate: unknown extension %s\n", + cur->name)); +#endif + ctxt->insert = insert; + if (!xsltApplyFallbacks(ctxt, node, cur)) { + xsltTransformError(ctxt, NULL, cur, + "Unknown extension instruction '{%s}%s'.\n", + cur->ns->href, cur->name); + } + ctxt->insert = oldInsert; + } else { + /* + * Execute the handler-callback. + */ +#ifdef WITH_XSLT_DEBUG_PROCESS + XSLT_TRACE(ctxt,XSLT_TRACE_APPLY_TEMPLATE,xsltGenericDebug(xsltGenericDebugContext, + "xsltApplyOneTemplate: extension construct %s\n", + cur->name)); +#endif + ctxt->insert = insert; + func(ctxt, node, cur, cur->psvi); + ctxt->insert = oldInsert; + } + goto skip_children; + } + + } else if (XSLT_IS_TEXT_NODE(cur)) { + /* + * Text + * ------------------------------------------------------------ + */ +#ifdef WITH_XSLT_DEBUG_PROCESS + if (cur->name == xmlStringTextNoenc) { + XSLT_TRACE(ctxt, XSLT_TRACE_APPLY_TEMPLATE, + xsltGenericDebug(xsltGenericDebugContext, + "xsltApplyOneTemplateInt: copy unescaped text '%s'\n", + cur->content)); + } else { + XSLT_TRACE(ctxt, XSLT_TRACE_APPLY_TEMPLATE, + xsltGenericDebug(xsltGenericDebugContext, + "xsltApplyOneTemplateInt: copy text '%s'\n", + cur->content)); + } +#endif + if (xsltCopyText(ctxt, insert, cur, ctxt->internalized) == NULL) + goto error; + } + +#else /* XSLT_REFACTORED */ + + if (IS_XSLT_ELEM(cur)) { /* * This is an XSLT node */ @@ -1685,6 +2278,7 @@ xsltApplyOneTemplateInt(xsltTransformContextPtr ctxt, xmlNodePtr node, goto skip_children; } else if ((cur->type == XML_TEXT_NODE) || (cur->type == XML_CDATA_SECTION_NODE)) { + /* * This text comes from the stylesheet * For stylesheets, the set of whitespace-preserving @@ -1778,7 +2372,7 @@ xsltApplyOneTemplateInt(xsltTransformContextPtr ctxt, xmlNodePtr node, /* * Add extra namespaces inherited from the current template * if we are in the first level children and this is a - * "real" template. + * "real" template. */ if ((templ != NULL) && (oldInsert == insert) && (ctxt->templ != NULL) && (ctxt->templ->inheritedNs != NULL)) { @@ -1799,7 +2393,7 @@ xsltApplyOneTemplateInt(xsltTransformContextPtr ctxt, xmlNodePtr node, style = xsltNextImport(style); } - + if (URI == UNDEFINED_DEFAULT_NS) { xmlNsPtr dflt; dflt = xmlSearchNs(cur->doc, cur, NULL); @@ -1829,9 +2423,10 @@ xsltApplyOneTemplateInt(xsltTransformContextPtr ctxt, xmlNodePtr node, } } } +#endif /* else of XSLT_REFACTORED */ /* - * Skip to next node, in document order. + * Descend into content in document order. */ if (cur->children != NULL) { if (cur->children->type != XML_ENTITY_DECL) { @@ -1842,10 +2437,13 @@ xsltApplyOneTemplateInt(xsltTransformContextPtr ctxt, xmlNodePtr node, continue; } } - skip_children: + +skip_children: /* - If xslt:message just processed, might have terminate='yes' - If so, break out of while to clean up processing. + * If xslt:message was just processed, we might have hit a + * terminate='yes'; if so, then break the loop and clean up. + * TODO: Do we need to check this also before trying to descend + * into the content? */ if (ctxt->state == XSLT_STATE_STOPPED) break; @@ -2466,7 +3064,7 @@ xsltSort(xsltTransformContextPtr ctxt, * @inst: the xslt copy node * @comp: precomputed information * - * Process the xslt copy node on the source node + * Execute the xsl:copy instruction on the source node. */ void xsltCopy(xsltTransformContextPtr ctxt, xmlNodePtr node, diff --git a/libxslt/xslt.c b/libxslt/xslt.c index 8d1dd03..ce31817 100644 --- a/libxslt/xslt.c +++ b/libxslt/xslt.c @@ -27,6 +27,7 @@ #include #include #include +#include #include "xslt.h" #include "xsltInternals.h" #include "pattern.h" @@ -55,15 +56,6 @@ const int xsltLibxmlVersion = LIBXML_VERSION; const xmlChar *xsltConstNamespaceNameXSLT= (const xmlChar *) XSLT_NAMESPACE; -static const xmlChar *xsltLibxsltDataElementNameMeta = - (const xmlChar *) "meta"; - -static const xmlChar *xsltLibxsltDataElementNamespaceMeta = - (const xmlChar *) XSLT_DEFAULT_URL; - -static const xmlChar *xsltUserDataElemMarker = - (const xmlChar *) "User Data Element"; - /* * xsltLiteralResultMarker: * Marker for Literal result elements, in order to avoid multiple attempts @@ -426,60 +418,21 @@ xsltFreeTemplateList(xsltTemplatePtr template) { } #ifdef XSLT_REFACTORED -/** - * xsltCompilerCreate: - * - * Create an XSLT compiler context. - * - * Returns the allocated xsltCompilerCtxtPtr or NULL in case of error. - */ -static xsltCompilerCtxtPtr -xsltCompilerCreate(xsltStylesheetPtr style) { - xsltCompilerCtxtPtr ret; - - ret = (xsltCompilerCtxtPtr) xmlMalloc(sizeof(xsltCompilerCtxt)); - if (ret == NULL) { - xsltTransformError(NULL, style, NULL, - "xsltCompilerCreate: allocation of compiler " - "context failed.\n"); - return(NULL); - } - memset(ret, 0, sizeof(xsltCompilerCtxt)); - - ret->errSeverity = XSLT_ERROR_SEVERITY_ERROR; - - style->compCtxt = (void *) ret; - ret->style = style; - ret->tmpList = xsltPointerListCreate(20); - if (ret->tmpList == NULL) { - xmlFree(ret); - return(NULL); - } - ret->dict = style->dict; - return(ret); -} static void -xsltCompilerCtxtFree(xsltCompilerCtxtPtr cctxt) -{ - if (cctxt == NULL) - return; - /* - * Free node-infos. - */ - if (cctxt->inodeList != NULL) { - xsltCompilerNodeInfoPtr tmp, cur = cctxt->inodeList; - while (cur != NULL) { - tmp = cur; - cur = cur->next; - xmlFree(tmp); - } - } - if (cctxt->tmpList != NULL) - xsltPointerListFree(cctxt->tmpList); - xmlFree(cctxt); +xsltFreeNsAliasList(xsltNsAliasPtr item) +{ + xsltNsAliasPtr tmp; + + while (item) { + tmp = item; + item = item->next; + xmlFree(tmp); + } + return; } +#ifdef XSLT_REFACTORED_XSLT_NSCOMP static void xsltFreeNamespaceMap(xsltNsMapPtr item) { @@ -525,6 +478,80 @@ xsltNewNamespaceMapItem(xsltCompilerCtxtPtr cctxt, return(ret); } +#endif /* XSLT_REFACTORED_XSLT_NSCOMP */ + +/** + * xsltCompilerCtxtFree: + * + * Free an XSLT compiler context. + */ +static void +xsltCompilationCtxtFree(xsltCompilerCtxtPtr cctxt) +{ + if (cctxt == NULL) + return; + /* + * Free node-infos. + */ + if (cctxt->inodeList != NULL) { + xsltCompilerNodeInfoPtr tmp, cur = cctxt->inodeList; + while (cur != NULL) { + tmp = cur; + cur = cur->next; + xmlFree(tmp); + } + } + if (cctxt->tmpList != NULL) + xsltPointerListFree(cctxt->tmpList); + if (cctxt->xpathCtxt != NULL) + xmlXPathFreeContext(cctxt->xpathCtxt); + if (cctxt->nsAliases != NULL) + xsltFreeNsAliasList(cctxt->nsAliases); + + xmlFree(cctxt); +} + +/** + * xsltCompilerCreate: + * + * Creates an XSLT compiler context. + * + * Returns the pointer to the created xsltCompilerCtxt or + * NULL in case of an internal error. + */ +static xsltCompilerCtxtPtr +xsltCompilationCtxtCreate(xsltStylesheetPtr style) { + xsltCompilerCtxtPtr ret; + + ret = (xsltCompilerCtxtPtr) xmlMalloc(sizeof(xsltCompilerCtxt)); + if (ret == NULL) { + xsltTransformError(NULL, style, NULL, + "xsltCompilerCreate: allocation of compiler " + "context failed.\n"); + return(NULL); + } + memset(ret, 0, sizeof(xsltCompilerCtxt)); + + ret->errSeverity = XSLT_ERROR_SEVERITY_ERROR; + ret->tmpList = xsltPointerListCreate(20); + if (ret->tmpList == NULL) { + goto internal_err; + } + /* + * Create the XPath compilation context in order + * to speed up precompilation of XPath expressions. + */ + ret->xpathCtxt = xmlXPathNewContext(NULL); + if (ret->xpathCtxt == NULL) + goto internal_err; + + return(ret); + +internal_err: + xsltCompilationCtxtFree(ret); + return(NULL); +} + static void xsltFreePrincipalStylesheetData(xsltPrincipalStylesheetDataPtr data) @@ -534,7 +561,7 @@ xsltFreePrincipalStylesheetData(xsltPrincipalStylesheetDataPtr data) if (data->inScopeNamespaces != NULL) { int i; - xsltNsListPtr nsi; + xsltNsListContainerPtr nsi; xsltPointerListPtr list = (xsltPointerListPtr) data->inScopeNamespaces; @@ -542,7 +569,7 @@ xsltFreePrincipalStylesheetData(xsltPrincipalStylesheetDataPtr data) /* * REVISIT TODO: Free info of in-scope namespaces. */ - nsi = (xsltNsListPtr) list->items[i]; + nsi = (xsltNsListContainerPtr) list->items[i]; if (nsi->list != NULL) xmlFree(nsi->list); xmlFree(nsi); @@ -574,7 +601,9 @@ xsltFreePrincipalStylesheetData(xsltPrincipalStylesheetDataPtr data) xsltPointerListFree(list); data->extElemNamespaces = NULL; } +#ifdef XSLT_REFACTORED_XSLT_NSCOMP xsltFreeNamespaceMap(data->nsMap); +#endif xmlFree(data); } @@ -600,12 +629,12 @@ xsltNewPrincipalStylesheetData(void) goto internal_err; /* * Global list of excluded result ns-decls. - */ + */ ret->exclResultNamespaces = xsltPointerListCreate(-1); if (ret->exclResultNamespaces == NULL) goto internal_err; /* - * Global list of extension element namespace names. + * Global list of extension instruction namespace names. */ ret->extElemNamespaces = xsltPointerListCreate(-1); if (ret->extElemNamespaces == NULL) @@ -767,7 +796,8 @@ xsltFreeStylesheetList(xsltStylesheetPtr style) { */ static int xsltCleanupStylesheetTree(xmlDocPtr doc, xmlNodePtr rootElem) -{ +{ +#if 0 /* TODO: Currently disabled, since probably not needed. */ xmlNodePtr cur; if ((doc == NULL) || (rootElem == NULL) || @@ -808,6 +838,7 @@ leave_node: goto leave_node; } } +#endif /* #if 0 */ return(0); } @@ -830,6 +861,7 @@ xsltFreeStylesheet(xsltStylesheetPtr style) if ((style->principal == style) && (style->doc)) xsltCleanupStylesheetTree(style->doc, xmlDocGetRootElement(style->doc)); +#ifdef XSLT_REFACTORED_XSLT_NSCOMP /* * Restore changed ns-decls before freeing the document. */ @@ -839,6 +871,7 @@ xsltFreeStylesheet(xsltStylesheetPtr style) xsltRestoreDocumentNamespaces(XSLT_GET_INTERNAL_NSMAP(style), style->doc); } +#endif /* XSLT_REFACTORED_XSLT_NSCOMP */ #else /* * Start with a cleanup of the main stylesheet's doc. @@ -846,7 +879,7 @@ xsltFreeStylesheet(xsltStylesheetPtr style) if ((style->parent == NULL) && (style->doc)) xsltCleanupStylesheetTree(style->doc, xmlDocGetRootElement(style->doc)); -#endif +#endif /* XSLT_REFACTORED */ xsltFreeKeys(style); xsltFreeExts(style); @@ -1434,13 +1467,13 @@ xsltParseStylesheetPreserveSpace(xsltStylesheetPtr style, xmlNodePtr cur) { * @template: the "extension-element-prefixes" prefix * * parse an XSLT stylesheet's "extension-element-prefix" attribute value - * and register the namespaces of extension elements. + * and register the namespaces of extension instruction. * SPEC "A namespace is designated as an extension namespace by using * an extension-element-prefixes attribute on: * 1) an xsl:stylesheet element - * 2) TODO: an xsl:extension-element-prefixes attribute on a + * 2) an xsl:extension-element-prefixes attribute on a * literal result element - * 3) TODO: an extension element." + * 3) an extension instruction." */ static void xsltParseStylesheetExtPrefix(xsltStylesheetPtr style, xmlNodePtr cur, @@ -1456,7 +1489,7 @@ xsltParseStylesheetExtPrefix(xsltStylesheetPtr style, xmlNodePtr cur, prefixes = xmlGetNsProp(cur, (const xmlChar *)"extension-element-prefixes", NULL); } else { - /* For literal result elements and extension elements. */ + /* For literal result elements and extension instructions. */ prefixes = xmlGetNsProp(cur, (const xmlChar *)"extension-element-prefixes", XSLT_NAMESPACE); } @@ -1637,6 +1670,423 @@ xsltParseStylesheetExcludePrefix(xsltStylesheetPtr style, xmlNodePtr cur, #ifdef XSLT_REFACTORED /* +* xsltTreeEnsureXMLDecl: +* @doc: the doc +* +* BIG NOTE: +* This was copy&pasted from Libxml2's xmlTreeEnsureXMLDecl() in "tree.c". +* Ensures that there is an XML namespace declaration on the doc. +* +* Returns the XML ns-struct or NULL on API and internal errors. +*/ +static xmlNsPtr +xsltTreeEnsureXMLDecl(xmlDocPtr doc) +{ + if (doc == NULL) + return (NULL); + if (doc->oldNs != NULL) + return (doc->oldNs); + { + xmlNsPtr ns; + ns = (xmlNsPtr) xmlMalloc(sizeof(xmlNs)); + if (ns == NULL) { + xmlGenericError(xmlGenericErrorContext, + "xsltTreeEnsureXMLDecl: Failed to allocate " + "the XML namespace.\n"); + return (NULL); + } + memset(ns, 0, sizeof(xmlNs)); + ns->type = XML_LOCAL_NAMESPACE; + ns->href = xmlStrdup(XML_XML_NAMESPACE); + ns->prefix = xmlStrdup((const xmlChar *)"xml"); + doc->oldNs = ns; + return (ns); + } +} + +/* +* xsltTreeAcquireStoredNs: +* @doc: the doc +* @nsName: the namespace name +* @prefix: the prefix +* +* BIG NOTE: +* This was copy&pasted from Libxml2's xmlDOMWrapStoreNs() in "tree.c". +* Creates or reuses an xmlNs struct on doc->oldNs with +* the given prefix and namespace name. +* +* Returns the aquired ns struct or NULL in case of an API +* or internal error. +*/ +static xmlNsPtr +xsltTreeAcquireStoredNs(xmlDocPtr doc, + const xmlChar *nsName, + const xmlChar *prefix) +{ + xmlNsPtr ns; + + if (doc == NULL) + return (NULL); + ns = xsltTreeEnsureXMLDecl(doc); + if (ns == NULL) + return (NULL); + if (ns->next != NULL) { + /* Reuse. */ + ns = ns->next; + while (ns != NULL) { + if ((ns->prefix == NULL) != (prefix == NULL)) { + /* NOP */ + } else if (prefix == NULL) { + if (xmlStrEqual(ns->href, nsName)) + return (ns); + } else { + if ((ns->prefix[0] == prefix[0]) && + xmlStrEqual(ns->prefix, prefix) && + xmlStrEqual(ns->href, nsName)) + return (ns); + + } + if (ns->next == NULL) + break; + ns = ns->next; + } + } + /* Create. */ + ns->next = xmlNewNs(NULL, nsName, prefix); + return (ns->next); +} + +/** + * xsltLREBuildEffectiveNs: + * + * Apply ns-aliasing on the namespace of the given @elem and + * its attributes. + */ +static int +xsltLREBuildEffectiveNs(xsltCompilerCtxtPtr cctxt, + xmlNodePtr elem) +{ + xmlNsPtr ns; + xsltNsAliasPtr alias; + + if ((cctxt == NULL) || (elem == NULL)) + return(-1); + if ((cctxt->nsAliases == NULL) || (! cctxt->hasNsAliases)) + return(0); + + alias = cctxt->nsAliases; + while (alias != NULL) { + if ( /* If both namespaces are NULL... */ + ( (elem->ns == NULL) && + ((alias->literalNs == NULL) || + (alias->literalNs->href == NULL)) ) || + /* ... or both namespace are equal */ + ( (elem->ns != NULL) && + (alias->literalNs != NULL) && + xmlStrEqual(elem->ns->href, alias->literalNs->href) ) ) + { + if ((alias->targetNs != NULL) && + (alias->targetNs->href != NULL)) + { + /* + * Convert namespace. + */ + if (elem->doc == alias->docOfTargetNs) { + /* + * This is the nice case: same docs. + * This will eventually assign a ns-decl which + * is shadowed, but this has no negative effect on + * the generation of the result tree. + */ + elem->ns = alias->targetNs; + } else { + /* + * This target xmlNs originates from a different + * stylesheet tree. Try to locate it in the + * in-scope namespaces. + * OPTIMIZE TODO: Use the compiler-node-info inScopeNs. + */ + ns = xmlSearchNs(elem->doc, elem, + alias->targetNs->prefix); + /* + * If no matching ns-decl found, then assign a + * ns-decl stored in xmlDoc. + */ + if ((ns == NULL) || + (! xmlStrEqual(ns->href, alias->targetNs->href))) + { + /* + * BIG NOTE: The use of xsltTreeAcquireStoredNs() + * is not very efficient, but currently I don't + * see an other way of *safely* changing a node's + * namespace, since the xmlNs struct in + * alias->targetNs might come from an other + * stylesheet tree. So we need to anchor it in the + * current document, without adding it to the tree, + * which would otherwise change the in-scope-ns + * semantic of the tree. + */ + ns = xsltTreeAcquireStoredNs(elem->doc, + alias->targetNs->href, + alias->targetNs->prefix); + + if (ns == NULL) { + xsltTransformError(NULL, cctxt->style, elem, + "Internal error in " + "xsltLREBuildEffectiveNs(): " + "failed to acquire a stored " + "ns-declaration.\n"); + cctxt->style->errors++; + return(-1); + + } + } + elem->ns = ns; + } + } else { + /* + * Move into or leave in the NULL namespace. + */ + elem->ns = NULL; + } + break; + } + alias = alias->next; + } + /* + * Same with attributes of literal result elements. + */ + if (elem->properties != NULL) { + xmlAttrPtr attr = elem->properties; + + while (attr != NULL) { + if (attr->ns == NULL) { + attr = attr->next; + continue; + } + alias = cctxt->nsAliases; + while (alias != NULL) { + if ( /* If both namespaces are NULL... */ + ( (elem->ns == NULL) && + ((alias->literalNs == NULL) || + (alias->literalNs->href == NULL)) ) || + /* ... or both namespace are equal */ + ( (elem->ns != NULL) && + (alias->literalNs != NULL) && + xmlStrEqual(elem->ns->href, alias->literalNs->href) ) ) + { + if ((alias->targetNs != NULL) && + (alias->targetNs->href != NULL)) + { + if (elem->doc == alias->docOfTargetNs) { + elem->ns = alias->targetNs; + } else { + ns = xmlSearchNs(elem->doc, elem, + alias->targetNs->prefix); + if ((ns == NULL) || + (! xmlStrEqual(ns->href, alias->targetNs->href))) + { + ns = xsltTreeAcquireStoredNs(elem->doc, + alias->targetNs->href, + alias->targetNs->prefix); + + if (ns == NULL) { + xsltTransformError(NULL, cctxt->style, elem, + "Internal error in " + "xsltLREBuildEffectiveNs(): " + "failed to acquire a stored " + "ns-declaration.\n"); + cctxt->style->errors++; + return(-1); + + } + } + elem->ns = ns; + } + } else { + /* + * Move into or leave in the NULL namespace. + */ + elem->ns = NULL; + } + break; + } + alias = alias->next; + } + + attr = attr->next; + } + } + return(0); +} + +/** + * xsltLREBuildEffectiveNsNodes: + * + * Computes the effective namespaces nodes for a literal result + * element. + * @effectiveNs is the set of effective ns-nodes + * on the literal result element, which will be added to the result + * element if not already existing in the result tree. + * This means that excluded namespaces (via exclude-result-prefixes, + * extension-element-prefixes and the XSLT namespace) not added + * to the set. + * Namespace-aliasing was applied on the @effectiveNs. + */ +static int +xsltLREBuildEffectiveNsNodes(xsltCompilerCtxtPtr cctxt, + xsltStyleItemLRElementInfoPtr item, + xmlNodePtr elem) +{ + xmlNsPtr ns; + xsltEffectiveNsPtr effNs, lastEffNs = NULL; + int i; + xsltPointerListPtr extElemNs = cctxt->inode->extElemNs; + xsltPointerListPtr exclResultNs = cctxt->inode->exclResultNs; + + if ((cctxt == NULL) || (cctxt->inode == NULL) || (elem == NULL) || + (item == NULL) || (item->effectiveNs != NULL)) + return(-1); + + if (elem->nsDef == NULL) + return(0); + + extElemNs = cctxt->inode->extElemNs; + exclResultNs = cctxt->inode->exclResultNs; + + for (ns = elem->nsDef; ns != NULL; ns = ns->next) { + /* + * Skip namespaces designated as excluded namespaces + * ------------------------------------------------- + * + * XSLT-20 TODO: In XSLT 2.0 we need to keep namespaces + * which are target namespaces of namespace-aliases + * regardless if designated as excluded. + * + * Exclude the XSLT namespace. + */ + if (xmlStrEqual(ns->href, XSLT_NAMESPACE)) + goto skip_ns; + + /* + * Exclude excluded result namespaces. + */ + if (exclResultNs) { + for (i = 0; i < exclResultNs->number; i++) + if (xmlStrEqual(ns->href, BAD_CAST exclResultNs->items[i])) + goto skip_ns; + } + /* + * Exclude extension-element namespaces. + */ + if (extElemNs) { + for (i = 0; i < extElemNs->number; i++) + if (xmlStrEqual(ns->href, BAD_CAST extElemNs->items[i])) + goto skip_ns; + } + /* + * Apply namespace aliasing + * ------------------------ + * + * NOTE: The ns-aliasing machanism is non-cascading. + * (checked with Saxon, Xalan and MSXML .NET). + * URGENT TODO: is style->nsAliases the effective list of + * ns-aliases, or do we need to lookup the whole + * import-tree? + * TODO: Get rid of import-tree lookup. + */ + if (cctxt->hasNsAliases) { + xsltNsAliasPtr alias = cctxt->nsAliases; + do { + /* + * TODO: What to do with xmlns="" ? + */ + if ((alias->literalNs != NULL) && + (alias->literalNs->href == ns->href)) + { + /* + * Recognized as an namespace alias; convert it to + * the target namespace. + */ + ns = alias->literalNs; + break; + } + alias = alias->next; + } while (alias != NULL); + } + /* + * Add the effective namespace declaration. + */ + effNs = (xsltEffectiveNsPtr) xmlMalloc(sizeof(xsltEffectiveNs)); + if (effNs == NULL) { + xsltTransformError(NULL, cctxt->style, elem, + "Internal error in xsltLREBuildEffectiveNs(): " + "failed to allocate memory.\n"); + cctxt->style->errors++; + return(-1); + } + effNs->next = NULL; + effNs->prefix = ns->prefix; + effNs->nsName = ns->href; + + if (lastEffNs == NULL) + item->effectiveNs = effNs; + else + lastEffNs->next = effNs; + lastEffNs = effNs; + +skip_ns: + {} + } + return(0); +} + + +/** + * xsltLREInfoCreate: + * + * Creates a new info for a literal result element. + */ +static int +xsltLREInfoCreate(xsltCompilerCtxtPtr cctxt, + xmlNodePtr elem) +{ + xsltStyleItemLRElementInfoPtr item; + + if ((cctxt == NULL) || (cctxt->inode == NULL)) + return(-1); + + item = (xsltStyleItemLRElementInfoPtr) + xmlMalloc(sizeof(xsltStyleItemLRElementInfo)); + if (item == NULL) { + xsltTransformError(NULL, cctxt->style, NULL, + "Internal error in xsltLREInfoCreate(): " + "memory allocation failed.\n"); + cctxt->style->errors++; + return(-1); + } + memset(item, 0, sizeof(xsltStyleItemLRElementInfo)); + item->type = XSLT_FUNC_LITERAL_RESULT_ELEMENT; + /* + * Store it in the stylesheet. + */ + item->next = cctxt->style->preComps; + cctxt->style->preComps = (xsltElemPreCompPtr) item; + /* + * @inScopeNs are used for execution of XPath expressions + * in AVTs. + */ + item->inScopeNs = cctxt->inode->inScopeNs; + + if (elem && (elem->nsDef != NULL)) + xsltLREBuildEffectiveNsNodes(cctxt, item, elem); + + cctxt->inode->litResElemInfo = item; + cctxt->inode->nsChanged = 0; + return(0); +} + +/* * xsltCompilerNodePush: * * @cctxt: the compilation context @@ -1673,19 +2123,24 @@ xsltCompilerNodePush(xsltCompilerCtxtPtr cctxt, xmlNodePtr node) inode->prev = cctxt->inodeLast; } cctxt->inodeLast = inode; - cctxt->maxNodeInfos++; - } - /* - * REVISIT TODO: Keep the reset always complete. - */ + cctxt->maxNodeInfos++; + if (cctxt->inode == NULL) { + cctxt->inode = inode; + /* + * Create an initial literal result element info for + * the root of the stylesheet. + */ + xsltLREInfoCreate(cctxt, NULL); + } + } cctxt->depth++; - + cctxt->inode = inode; /* + * REVISIT TODO: Keep the reset always complete. * NOTE: Be carefull with the @node, since it might be * a doc-node. */ inode->node = node; - inode->depth = cctxt->depth; inode->templ = NULL; inode->category = XSLT_ELEMENT_CATEGORY_XSLT; @@ -1695,25 +2150,33 @@ xsltCompilerNodePush(xsltCompilerCtxtPtr cctxt, xmlNodePtr node) inode->extContentHandled = 0; inode->isRoot = 0; - if (inode->prev != NULL) { + if (inode->prev != NULL) { /* * Inherit the following information: + * --------------------------------- + * + * In-scope namespaces */ inode->inScopeNs = inode->prev->inScopeNs; /* - * in-scope namespaces + * Info for literal result elements + */ + inode->litResElemInfo = inode->prev->litResElemInfo; + inode->nsChanged = inode->prev->nsChanged; + /* + * Excluded result namespaces */ inode->exclResultNs = inode->prev->exclResultNs; /* - * extension-element namespaces + * Extension instruction namespaces */ inode->extElemNs = inode->prev->extElemNs; /* - * whitespace perservation + * Whitespace preservation */ inode->preserveWhitespace = inode->prev->preserveWhitespace; /* - * forwards-compatible mode + * Forwards-compatible mode */ inode->forwardsCompat = inode->prev->forwardsCompat; } else { @@ -1723,7 +2186,7 @@ xsltCompilerNodePush(xsltCompilerCtxtPtr cctxt, xmlNodePtr node) inode->preserveWhitespace = 0; inode->forwardsCompat = 0; } - cctxt->inode = inode; + return(inode); } @@ -1816,10 +2279,10 @@ mismatch: * * Returns the ns-info or NULL if there are no namespaces in scope. */ -static xsltNsListPtr +static xsltNsListContainerPtr xsltCompilerBuildInScopeNsList(xsltCompilerCtxtPtr cctxt, xmlNodePtr node) { - xsltNsListPtr nsi = NULL; + xsltNsListContainerPtr nsi = NULL; xmlNsPtr *list = NULL; /* * Create a new ns-list for this position in the node-tree. @@ -1834,13 +2297,13 @@ xsltCompilerBuildInScopeNsList(xsltCompilerCtxtPtr cctxt, xmlNodePtr node) /* * Create the info-structure. */ - nsi = (xsltNsListPtr) xmlMalloc(sizeof(xsltNsList)); + nsi = (xsltNsListContainerPtr) xmlMalloc(sizeof(xsltNsListContainer)); if (nsi == NULL) { xsltTransformError(NULL, cctxt->style, NULL, "xsltCompilerBuildInScopeNsList: malloc failed.\n"); goto internal_err; } - memset(nsi, 0, sizeof(xsltNsList)); + memset(nsi, 0, sizeof(xsltNsListContainer)); nsi->list = list; /* * Eval the number of ns-decls; this is used to speed up @@ -1860,7 +2323,12 @@ xsltCompilerBuildInScopeNsList(xsltCompilerCtxtPtr cctxt, xmlNodePtr node) xsltTransformError(NULL, cctxt->style, NULL, "xsltCompilerBuildInScopeNsList: failed to add ns-info.\n"); goto internal_err; - } + } + /* + * Notify of change in status wrt namespaces. + */ + if (cctxt->inode != NULL) + cctxt->inode->nsChanged = 1; return(nsi); @@ -2038,7 +2506,7 @@ xsltParseExclResultPrefixes(xsltCompilerCtxtPtr cctxt, xmlNodePtr node, if (list == NULL) goto exit; /* - * Store the list in the stylesheet. + * Store the list in the stylesheet/compiler context. */ if (xsltPointerListAddSize( cctxt->psData->exclResultNamespaces, list, 5) == -1) @@ -2047,6 +2515,11 @@ xsltParseExclResultPrefixes(xsltCompilerCtxtPtr cctxt, xmlNodePtr node, list = NULL; goto exit; } + /* + * Notify of change in status wrt namespaces. + */ + if (cctxt->inode != NULL) + cctxt->inode->nsChanged = 1; exit: if (value != NULL) @@ -2116,6 +2589,11 @@ xsltParseExtElemPrefixes(xsltCompilerCtxtPtr cctxt, xmlNodePtr node, list = NULL; goto exit; } + /* + * Notify of change in status wrt namespaces. + */ + if (cctxt->inode != NULL) + cctxt->inode->nsChanged = 1; exit: if (value != NULL) @@ -2195,7 +2673,9 @@ xsltParsePreprocessStylesheetTree(xsltCompilerCtxtPtr cctxt, xmlNodePtr node) xmlChar *value; const xmlChar *name, *nsNameXSLT = NULL; int strictWhitespace, inXSLText = 0; +#ifdef XSLT_REFACTORED_XSLT_NSCOMP xsltNsMapPtr nsMapItem; +#endif if ((cctxt == NULL) || (cctxt->style == NULL) || (node == NULL) || (node->type != XML_ELEMENT_NODE)) @@ -2203,7 +2683,7 @@ xsltParsePreprocessStylesheetTree(xsltCompilerCtxtPtr cctxt, xmlNodePtr node) doc = node->doc; if (doc == NULL) - return(-1); + goto internal_err; style = cctxt->style; if ((style->dict != NULL) && (doc->dict == style->dict)) @@ -2264,6 +2744,7 @@ xsltParsePreprocessStylesheetTree(xsltCompilerCtxtPtr cctxt, xmlNodePtr node) * TODO: I'd love to use a string pointer comparison here :-/ */ if (IS_XSLT_ELEM(cur)) { +#ifdef XSLT_REFACTORED_XSLT_NSCOMP if (cur->ns->href != nsNameXSLT) { nsMapItem = xsltNewNamespaceMapItem(cctxt, doc, cur, cur->ns); @@ -2271,6 +2752,7 @@ xsltParsePreprocessStylesheetTree(xsltCompilerCtxtPtr cctxt, xmlNodePtr node) goto internal_err; cur->ns->href = nsNameXSLT; } +#endif if (cur->name == NULL) goto process_attributes; @@ -3005,10 +3487,10 @@ xsltParseAnyXSLTElem(xsltCompilerCtxtPtr cctxt, xmlNodePtr elem) elem->psvi = NULL; - if (! (IS_IN_XSLT_NS(elem))) + if (! (IS_XSLT_ELEM_FAST(elem))) return(-1); /* - * Detection of handled content of extension elements. + * Detection of handled content of extension instructions. */ if (cctxt->inode->category == XSLT_ELEMENT_CATEGORY_EXTENSION) { cctxt->inode->extContentHandled = 1; @@ -3131,18 +3613,15 @@ apply_templates: xmlNodePtr child = elem->children; do { if (child->type == XML_ELEMENT_NODE) { - if (IS_IN_XSLT_NS(child)) { - xsltStyleType type; - - type = xsltGetXSLTElementTypeByNode(cctxt, child); - if ((type == XSLT_FUNC_WITHPARAM) || - (type == XSLT_FUNC_SORT)) - { - /* cctxt->inode->type = type; */ + if (IS_XSLT_ELEM_FAST(child)) { + if (xmlStrEqual(child->name, BAD_CAST "with-param")) { + cctxt->inode->curChildType = XSLT_FUNC_WITHPARAM; xsltParseAnyXSLTElem(cctxt, child); - } else { + } else if (xmlStrEqual(child->name, BAD_CAST "sort")) { + cctxt->inode->curChildType = XSLT_FUNC_SORT; + xsltParseAnyXSLTElem(cctxt, child); + } else xsltParseContentError(cctxt->style, child); - } } else xsltParseContentError(cctxt->style, child); } @@ -3157,7 +3636,7 @@ call_template: xmlNodePtr child = elem->children; do { if (child->type == XML_ELEMENT_NODE) { - if (IS_IN_XSLT_NS(child)) { + if (IS_XSLT_ELEM_FAST(child)) { xsltStyleType type; type = xsltGetXSLTElementTypeByNode(cctxt, child); @@ -3193,9 +3672,22 @@ text: empty_content: if (elem->children != NULL) { - xsltTransformError(NULL, cctxt->style, elem, - "This XSLT element must have no content.\n"); - cctxt->style->errors++; + xmlNodePtr child = elem->children; + /* + * Relaxed behaviour: we will allow whitespace-only text-nodes. + */ + do { + if (((child->type != XML_TEXT_NODE) && + (child->type != XML_CDATA_SECTION_NODE)) || + (! IS_BLANK_NODE(child))) + { + xsltTransformError(NULL, cctxt->style, elem, + "This XSLT element must have no content.\n"); + cctxt->style->errors++; + break; + } + child = child->next; + } while (child != NULL); } goto exit; @@ -3212,7 +3704,7 @@ choose: int nbWhen = 0, nbOtherwise = 0, err = 0; do { if (child->type == XML_ELEMENT_NODE) { - if (IS_IN_XSLT_NS(child)) { + if (IS_XSLT_ELEM_FAST(child)) { xsltStyleType type; type = xsltGetXSLTElementTypeByNode(cctxt, child); @@ -3278,7 +3770,7 @@ for_each: */ do { if ((child->type == XML_ELEMENT_NODE) && - IS_IN_XSLT_NS(child)) + IS_XSLT_ELEM_FAST(child)) { if (xsltGetXSLTElementTypeByNode(cctxt, child) == XSLT_FUNC_SORT) @@ -3312,21 +3804,72 @@ internal_err: return(-1); } +static xsltStyleItemUknownPtr +xsltForwardsCompatUnkownItemCreate(xsltCompilerCtxtPtr cctxt) +{ + xsltStyleItemUknownPtr item; + + item = (xsltStyleItemUknownPtr) xmlMalloc(sizeof(xsltStyleItemUknown)); + if (item == NULL) { + xsltTransformError(NULL, cctxt->style, NULL, + "Internal error in xsltForwardsCompatUnkownItemCreate(): " + "Failed to allocate memory.\n"); + cctxt->style->errors++; + return(NULL); + } + memset(item, 0, sizeof(xsltStyleItemUknown)); + item->type = XSLT_FUNC_UNKOWN_FORWARDS_COMPAT; + /* + * Store it in the stylesheet. + */ + item->next = cctxt->style->preComps; + cctxt->style->preComps = (xsltElemPreCompPtr) item; + return(item); +} + static int -xsltParseForwardsCompatUnexpectedXSLTElem(xsltCompilerCtxtPtr cctxt, - xmlNodePtr node) +xsltParseUnknownXSLTElem(xsltCompilerCtxtPtr cctxt, + xmlNodePtr node) { if ((cctxt == NULL) || (node == NULL)) return(-1); /* - * Detection of handled content of extension elements. + * Detection of handled content of extension instructions. */ if (cctxt->inode->category == XSLT_ELEMENT_CATEGORY_EXTENSION) { cctxt->inode->extContentHandled = 1; + } + if (cctxt->inode->forwardsCompat == 0) { + /* + * We are not in forwards-compatible mode, so raise an error. + */ + xsltTransformError(NULL, cctxt->style, node, + "Unknown XSLT element '%s'.\n", node->name); + cctxt->style->errors++; + return(0); } - - node->psvi = NULL; + /* + * Forwards-compatible mode. + * ------------------------ + * + * Parse/compile xsl:fallback elements. + * + * QUESTION: Do we have to raise an error if there's no xsl:fallback? + * ANSWER: No, since in the stylesheet the fallback behaviour might + * also be provided by using the XSLT function "element-available". + */ + if (cctxt->unknownItem == NULL) { + /* + * Create a singleton for all unknown XSLT instructions. + */ + cctxt->unknownItem = xsltForwardsCompatUnkownItemCreate(cctxt); + if (cctxt->unknownItem == NULL) { + node->psvi = NULL; + return(-1); + } + } + node->psvi = cctxt->unknownItem; if (node->children == NULL) return(0); else { @@ -3338,13 +3881,13 @@ xsltParseForwardsCompatUnexpectedXSLTElem(xsltCompilerCtxtPtr cctxt, */ if (node->nsDef != NULL) cctxt->inode->inScopeNs = - xsltCompilerBuildInScopeNsList(cctxt, node); + xsltCompilerBuildInScopeNsList(cctxt, node); /* * Parse all xsl:fallback children. */ do { if ((child->type == XML_ELEMENT_NODE) && - IS_IN_XSLT_NS(child) && + IS_XSLT_ELEM_FAST(child) && IS_XSLT_NAME(child, "fallback")) { cctxt->inode->curChildType = XSLT_FUNC_FALLBACK; @@ -3352,6 +3895,7 @@ xsltParseForwardsCompatUnexpectedXSLTElem(xsltCompilerCtxtPtr cctxt, } child = child->next; } while (child != NULL); + xsltCompilerNodePop(cctxt, node); } return(0); @@ -3378,7 +3922,7 @@ xsltParseSequenceConstructor(xsltCompilerCtxtPtr cctxt, xmlNodePtr cur) return; } /* - * Detection of handled content of extension elements. + * Detection of handled content of extension instructions. */ if (cctxt->inode->category == XSLT_ELEMENT_CATEGORY_EXTENSION) { cctxt->inode->extContentHandled = 1; @@ -3406,7 +3950,7 @@ xsltParseSequenceConstructor(xsltCompilerCtxtPtr cctxt, xmlNodePtr cur) * xsl:processing-instruction, xsl:comment, xsl:element * xsl:attribute. * Additional allowed content: - * 1) extension elements + * 1) extension instructions * 2) literal result elements * 3) PCDATA * @@ -3426,8 +3970,8 @@ xsltParseSequenceConstructor(xsltCompilerCtxtPtr cctxt, xmlNodePtr cur) if (cur->psvi == xsltXSLTTextMarker) { /* - * Process xsl:text elements. - * ========================== + * xsl:text elements + * -------------------------------------------------------- */ xmlNodePtr tmp; @@ -3507,15 +4051,16 @@ xsltParseSequenceConstructor(xsltCompilerCtxtPtr cctxt, xmlNodePtr cur) attr = attr->next; } while (attr != NULL); } - } else if (IS_IN_XSLT_NS(cur)) { + } else if (IS_XSLT_ELEM_FAST(cur)) { /* * TODO: Using the XSLT-marker is still not stable yet. */ /* if (cur->psvi == xsltXSLTElemMarker) { */ /* - * This is element in the XSLT namespace. - * ===================================== + * XSLT instructions + * -------------------------------------------------------- */ + cur->psvi = NULL; type = xsltGetXSLTElementTypeByNode(cctxt, cur); switch (type) { case XSLT_FUNC_APPLYIMPORTS: @@ -3536,8 +4081,7 @@ xsltParseSequenceConstructor(xsltCompilerCtxtPtr cctxt, xmlNodePtr cur) case XSLT_FUNC_PI: case XSLT_FUNC_TEXT: case XSLT_FUNC_VALUEOF: - case XSLT_FUNC_VARIABLE: - /* cctxt->inode->type = type; */ + case XSLT_FUNC_VARIABLE: /* * Parse the XSLT element. */ @@ -3545,33 +4089,25 @@ xsltParseSequenceConstructor(xsltCompilerCtxtPtr cctxt, xmlNodePtr cur) xsltParseAnyXSLTElem(cctxt, cur); break; default: - cur->psvi = NULL; - /* - * Ignore unknown elements in the - * XSLT namespace if in forwards-compatible - * mode. - */ - if (cctxt->inode->forwardsCompat) { - /* - * This will parse xsl:fallback elements. - */ - xsltParseForwardsCompatUnexpectedXSLTElem( - cctxt, cur); - } else { - xsltParseContentError(cctxt->style, cur); - } + xsltParseUnknownXSLTElem(cctxt, cur); cur = cur->next; continue; } } else { /* - * This is a non-XSLT element. - * ========================== + * Non-XSLT elements + * ----------------- + */ + xsltCompilerNodePush(cctxt, cur); + /* + * Update the in-scope namespaces if needed. */ - xsltCompilerNodePush(cctxt, cur); + if (cur->nsDef != NULL) + cctxt->inode->inScopeNs = + xsltCompilerBuildInScopeNsList(cctxt, cur); /* * The current element is either a literal result element - * or an extension element. + * or an extension instruction. * * Process attr "xsl:extension-element-prefixes". * FUTURE TODO: IIRC in XSLT 2.0 this attribute must be @@ -3601,15 +4137,15 @@ xsltParseSequenceConstructor(xsltCompilerCtxtPtr cctxt, xmlNodePtr cur) xsltParseExtElemPrefixes(cctxt, cur, cctxt->inode->extElemNs, 0); /* - * Eval if we have an extension element here. + * Eval if we have an extension instruction here. */ if ((cur->ns != NULL) && (cctxt->inode->extElemNs != NULL) && (xsltCheckExtPrefix(cctxt->style, cur->ns->href) == 1)) { /* - * This is an extension element. - * ============================ + * Extension instructions + * ---------------------------------------------------- * Mark the node information. */ cctxt->inode->category = XSLT_ELEMENT_CATEGORY_EXTENSION; @@ -3620,8 +4156,8 @@ xsltParseSequenceConstructor(xsltCompilerCtxtPtr cctxt, xmlNodePtr cur) * TODO: Temporary sanity check. */ xsltTransformError(NULL, cctxt->style, cur, - "Internal error: (xsltParseNonXSLTElement) " - " Occupied psvi field.\n"); + "Internal error in xsltParseSequenceConstructor(): " + "Occupied PSVI field.\n"); cctxt->style->errors++; cur = cur->next; continue; @@ -3643,7 +4179,7 @@ xsltParseSequenceConstructor(xsltCompilerCtxtPtr cctxt, xmlNodePtr cur) /* * BIG NOTE: Now the ugly part. In previous versions * of Libxslt (until 1.1.16), all the content of an - * extension element was processed and compiled without + * extension instruction was processed and compiled without * the need of the extension-author to explicitely call * such a processing;.We now need to mimic this old * behaviour in order to avoid breaking old code @@ -3673,15 +4209,16 @@ xsltParseSequenceConstructor(xsltCompilerCtxtPtr cctxt, xmlNodePtr cur) } } else { /* - * This is a literal result element. - * ================================ + * Literal result element + * ---------------------------------------------------- * Allowed XSLT attributes: * xsl:extension-element-prefixes CDATA #IMPLIED * xsl:exclude-result-prefixes CDATA #IMPLIED * TODO: xsl:use-attribute-sets %qnames; #IMPLIED * xsl:version NMTOKEN #IMPLIED */ - cctxt->inode->category = XSLT_ELEMENT_CATEGORY_LR; + cur->psvi = NULL; + cctxt->inode->category = XSLT_ELEMENT_CATEGORY_LR; if (cur->properties != NULL) { /* * Attribute "xsl:exclude-result-prefixes". @@ -3695,18 +4232,19 @@ xsltParseSequenceConstructor(xsltCompilerCtxtPtr cctxt, xmlNodePtr cur) xsltParseAttrXSLTVersion(cctxt, cur, 0); } /* - * TODO: Create the literal result item. - * TODO: Check what this *defaultAlias* mechanism does. Do we - * need to create the compiled item after such namespace - * conversion? + * Create/reuse info for the literal result element. + */ + if (cctxt->inode->nsChanged) + xsltLREInfoCreate(cctxt, cur); + cur->psvi = cctxt->inode->litResElemInfo; + /* + * Apply ns-aliasing on the element and on its attributes. + */ + if (cctxt->hasNsAliases) + xsltLREBuildEffectiveNs(cctxt, cur); + /* + * Compile attribute value templates (AVT). */ - if ((cur->ns == NULL) && (cctxt->style->defaultAlias != NULL) && - (cur->type == XML_ELEMENT_NODE)) - { - - cur->ns = xmlSearchNsByHref(cur->doc, cur, - cctxt->style->defaultAlias); - } if (cur->properties) { xmlAttrPtr attr = cur->properties; @@ -3762,7 +4300,7 @@ xsltParseTemplateContent(xsltStylesheetPtr style, xmlNodePtr templ) { return; /* - * Detection of handled content of extension elements. + * Detection of handled content of extension instructions. */ if (XSLT_CCTXT(style)->inode->category == XSLT_ELEMENT_CATEGORY_EXTENSION) { XSLT_CCTXT(style)->inode->extContentHandled = 1; @@ -3773,11 +4311,12 @@ xsltParseTemplateContent(xsltStylesheetPtr style, xmlNodePtr templ) { /* * Process xsl:param elements, which can only occur as the * immediate children of xsl:template (well, and of any - * user-defined extension element if needed). + * user-defined extension instruction if needed). */ do { if ((child->type == XML_ELEMENT_NODE) && - IS_IN_XSLT_NS(child) && IS_XSLT_NAME(child, "param")) + IS_XSLT_ELEM_FAST(child) && + IS_XSLT_NAME(child, "param")) { XSLT_CCTXT(style)->inode->curChildType = XSLT_FUNC_PARAM; xsltParseAnyXSLTElem(XSLT_CCTXT(style), child); @@ -4136,9 +4675,9 @@ xsltParseXSLTTemplate(xsltCompilerCtxtPtr cctxt, xmlNodePtr templNode) { if (cctxt->inode->inScopeNs != NULL) { int i, j; xmlNsPtr ns; - xsltPointerListPtr extElemList = cctxt->inode->extElemNs; - xsltPointerListPtr exclResultList = cctxt->inode->exclResultNs; - xsltNsListPtr inScopeNs = cctxt->inode->inScopeNs; + 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]; @@ -4150,18 +4689,17 @@ xsltParseXSLTTemplate(xsltCompilerCtxtPtr cctxt, xmlNodePtr templNode) { /* * Exclude excluded result namespaces. */ - if (exclResultList) { - for (j = 0; j < exclResultList->number; j++) - if (xmlStrEqual(ns->href, - BAD_CAST exclResultList->items[j])) - goto skip_ns; + 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 (extElemList) { - for (j = 0; j < extElemList->number; j++) - if (xmlStrEqual(ns->href, BAD_CAST extElemList->items[j])) + if (extElemNs) { + for (j = 0; j < extElemNs->number; j++) + if (xmlStrEqual(ns->href, BAD_CAST extElemNs->items[j])) goto skip_ns; } /* @@ -4283,8 +4821,24 @@ skip_ns: curTempl = curTempl->next; } } - if (templNode->children != NULL) - xsltParseTemplateContent(cctxt->style, templNode); + if (templNode->children != NULL) { + xsltParseTemplateContent(cctxt->style, templNode); + /* + * MAYBE TODO: Custom behaviour: In order to stay compatible with + * Xalan and MSXML(.NET), we could allow whitespace + * to appear before an xml:param element; this whitespace + * will additionally become part of the "template". + * NOTE that this is totally deviates from the spec, but + * is the de facto behaviour of Xalan and MSXML(.NET). + * Personally I wouldn't allow this, since if we have: + * + * + * + * + * ... the whitespace between every xsl:param would be + * added to the result tree. + */ + } templ->elem = templNode; templ->content = templNode->children; @@ -4564,6 +5118,7 @@ exit: return(ret); } +#if 0 static int xsltParseRemoveWhitespace(xmlNodePtr node) { @@ -4592,103 +5147,7 @@ xsltParseRemoveWhitespace(xmlNodePtr node) } return(0); } - -static int -xsltParseMetaInfo(xsltCompilerCtxtPtr cctxt, xmlNodePtr node) -{ -#define XSLT_MODULE_SCOPE_GLOBAL 0 -#define XSLT_MODULE_SCOPE_SSLEVEL 1 - xmlNodePtr cur; - xmlChar *value = NULL, *value2 = NULL; - int moduleScope; - - if ((cctxt == NULL) || (node == NULL) || (node->children == NULL)) - return(-1); - - /* - * Remove whitespace in case we had a xml:space. - */ - xsltParseRemoveWhitespace(node); - - cur = node->children; - while (cur != NULL) { - if (value != NULL) { - xmlFree(value); - value = NULL; - } - if (value2 != NULL) { - xmlFree(value2); - value2 = NULL; - } - /* - * Process the module-registration specific elements. - */ - if ((cur->type == XML_ELEMENT_NODE) && - (cur->ns == NULL)) - { - if (xmlStrEqual(cur->name, "initialize-module")) { - /* - * TODO: Validation of attributes. - */ - value = xmlGetNsProp(cur, "namespace", NULL); - if (value != NULL) { - moduleScope = XSLT_MODULE_SCOPE_GLOBAL; - value2 = xmlGetNsProp(cur, "scope", NULL); - if (value != NULL) { - if (xmlStrEqual(value2, "global")) - moduleScope = XSLT_MODULE_SCOPE_GLOBAL; - else if (xmlStrEqual(value2, "stylesheet-level")) - moduleScope = XSLT_MODULE_SCOPE_SSLEVEL; - else { - xsltTransformError(NULL, cctxt->style, cur, - "Attribute 'scope': Invalid value. " - "Only the values 'global' and " - "'stylesheet-level' are expected.\n"); - } - } - /* - * Call the module initialization. - */ - if (moduleScope == XSLT_MODULE_SCOPE_GLOBAL) { - /* - * Initialize the modle using global user-data - * storage. This means that this module - * will be initialized only *once* (regardless - * of the number of stylesheet-modules) and the - * user-data will be only stored in the main - * stylesheet. - */ - xsltStyleGetExtData(cctxt->style, BAD_CAST value); - } else { - /* - * Initialize the modle using per-stylesheet - * user-data storage. This means that this module - * will be initialized (and user-data is stored) - * in the stylesheet at the current stylesheet-level. - */ - xsltStyleStylesheetLevelGetExtData(cctxt->style, - BAD_CAST value); - } - goto next_item; - } - } - } - xsltParseContentError(cctxt->style, cur); - -next_item: - cur = cur->next; - } - if (value != NULL) { - xmlFree(value); - value = NULL; - } - if (value2 != NULL) { - xmlFree(value2); - value2 = NULL; - } - - return(0); -} +#endif static int xsltParseXSLTStylesheetElemCore(xsltCompilerCtxtPtr cctxt, xmlNodePtr node) @@ -4709,7 +5168,7 @@ xsltParseXSLTStylesheetElemCore(xsltCompilerCtxtPtr cctxt, xmlNodePtr node) * with the same stylesheet level have been processed. * Now we can safely parse the rest of the declarations. */ - if (IS_IN_XSLT_NS(node) && IS_XSLT_NAME(node, "include")) + if (IS_XSLT_ELEM_FAST(node) && IS_XSLT_NAME(node, "include")) { xsltDocumentPtr include; /* @@ -4740,9 +5199,14 @@ xsltParseXSLTStylesheetElemCore(xsltCompilerCtxtPtr cctxt, xmlNodePtr node) return(0); /* * Push the xsl:stylesheet/xsl:transform element. - */ + */ xsltCompilerNodePush(cctxt, node); cctxt->inode->isRoot = 1; + cctxt->inode->nsChanged = 0; + /* + * Start with the dummy info for literal result elements. + */ + cctxt->inode->litResElemInfo = cctxt->inodeList->litResElemInfo; /* * In every case, we need to have @@ -4775,11 +5239,11 @@ xsltParseXSLTStylesheetElemCore(xsltCompilerCtxtPtr cctxt, xmlNodePtr node) #ifdef XSLT_REFACTORED_MANDATORY_VERSION if (isXsltElem) xsltTransformError(NULL, cctxt->style, node, - "The attribute 'version' is missing.\n"); + "The attribute 'version' is missing.\n"); cctxt->style->errors++; #else /* OLD behaviour. */ -xsltTransformError(NULL, cctxt->style, node, + xsltTransformError(NULL, cctxt->style, node, "xsl:version is missing: document may not be a stylesheet\n"); cctxt->style->warnings++; #endif @@ -4800,7 +5264,11 @@ xsltTransformError(NULL, cctxt->style, node, */ cctxt->inode->exclResultNs = xsltParseExclResultPrefixes(cctxt, node, NULL, 1); - + /* + * Create/reuse info for the literal result element. + */ + if (cctxt->inode->nsChanged) + xsltLREInfoCreate(cctxt, NULL); /* * Processed top-level elements: * ---------------------------- @@ -4868,22 +5336,7 @@ xsltTransformError(NULL, cctxt->style, node, { xsltParseTopLevelXSLTElem(cctxt, cur, XSLT_FUNC_VARIABLE); cur = cur->next; - } - /* - * REVISIT TODO: Process Libxslt's module handling user-defined - * data element. - */ - cur = start; - while ((cur != NULL) && - xsltParseFindTopLevelElem(cctxt, cur, - xsltLibxsltDataElementNameMeta, - xsltLibxsltDataElementNamespaceMeta, 0, &cur) == 1) - { - if (xsltParseMetaInfo(cctxt, cur) == -1) - goto internal_err; - cur->psvi = (void *) xsltUserDataElemMarker; - cur = cur->next; - } + } /* * Process all the rest of top-level elements. */ @@ -4900,15 +5353,10 @@ xsltTransformError(NULL, cctxt->style, node, cur = cur->next; continue; } - if (cur->psvi == xsltUserDataElemMarker) { - cur->psvi = NULL; - cur = cur->next; - continue; - } /* * Process all XSLT elements. */ - if (IS_IN_XSLT_NS(cur)) { + if (IS_XSLT_ELEM_FAST(cur)) { /* * xsl:import is only allowed at the beginning. */ @@ -4966,7 +5414,7 @@ xsltTransformError(NULL, cctxt->style, node, xsltParseTopLevelXSLTElem(cctxt, cur, XSLT_FUNC_ATTRSET); } else if (IS_XSLT_NAME(cur, "namespace-alias")) { - xsltNamespaceAlias(style, cur); + /* NOP; done already */ } else { if (cctxt->inode->forwardsCompat) { /* @@ -5022,10 +5470,6 @@ exit: "parsed %d templates\n", templates); #endif return(0); - -internal_err: - xsltCompilerNodePop(cctxt, node); - return(-1); } /** @@ -5053,7 +5497,7 @@ internal_err: static int xsltParseXSLTStylesheetElem(xsltCompilerCtxtPtr cctxt, xmlNodePtr node) { - xmlNodePtr cur; + xmlNodePtr cur, start; if ((cctxt == NULL) || (node == NULL)) return(-1); @@ -5083,9 +5527,11 @@ xsltParseXSLTStylesheetElem(xsltCompilerCtxtPtr cctxt, xmlNodePtr node) } if (cur == NULL) goto exit; + start = cur; /* * Pre-process all xsl:include elements. */ + cur = start; while ((cur != NULL) && xsltParseFindTopLevelElem(cctxt, cur, BAD_CAST "include", XSLT_NAMESPACE, 0, &cur) == 1) @@ -5093,6 +5539,17 @@ xsltParseXSLTStylesheetElem(xsltCompilerCtxtPtr cctxt, xmlNodePtr node) xsltParseTopLevelXSLTElem(cctxt, cur, XSLT_FUNC_INCLUDE); cur = cur->next; } + /* + * Pre-process all xsl:namespace-alias elements. + */ + cur = start; + while ((cur != NULL) && + xsltParseFindTopLevelElem(cctxt, cur, + BAD_CAST "namespace-alias", XSLT_NAMESPACE, 0, &cur) == 1) + { + xsltNamespaceAlias(cctxt->style, cur); + cur = cur->next; + } if (cctxt->isInclude) { /* @@ -5341,6 +5798,7 @@ xsltParseSimplifiedStylesheetTree(xsltCompilerCtxtPtr cctxt, return(0); } +#ifdef XSLT_REFACTORED_XSLT_NSCOMP int xsltRestoreDocumentNamespaces(xsltNsMapPtr ns, xmlDocPtr doc) { @@ -5360,6 +5818,7 @@ xsltRestoreDocumentNamespaces(xsltNsMapPtr ns, xmlDocPtr doc) } return(0); } +#endif /* XSLT_REFACTORED_XSLT_NSCOMP */ /** * xsltParseStylesheetProcess: @@ -5595,16 +6054,20 @@ xsltParseStylesheetImportedDoc(xmlDocPtr doc, } retStyle->principalData = principalData; /* - * Create the compilation context (only once; for the - * principal stylesheet). + * Create the compilation context + * ------------------------------ + * (only once; for the principal stylesheet). * This is currently the only function where the * compilation context is created. - */ - if (xsltCompilerCreate(retStyle) == NULL) { + */ + cctxt = xsltCompilationCtxtCreate(retStyle); + if (cctxt == NULL) { xsltFreeStylesheet(retStyle); return(NULL); - } - cctxt = XSLT_CCTXT(retStyle); + } + retStyle->compCtxt = (void *) cctxt; + cctxt->style = retStyle; + cctxt->dict = retStyle->dict; cctxt->psData = principalData; /* * Push initial dummy node info. @@ -5648,11 +6111,13 @@ xsltParseStylesheetImportedDoc(xmlDocPtr doc, */ if (retStyle != NULL) { if (retStyle->errors != 0) { +#ifdef XSLT_REFACTORED_XSLT_NSCOMP /* * Restore all changes made to namespace URIs of ns-decls. */ if (cctxt->psData->nsMap) xsltRestoreDocumentNamespaces(cctxt->psData->nsMap, doc); +#endif /* * Detach the doc from the stylesheet; otherwise the doc * will be freed in xsltFreeStylesheet(). @@ -5718,7 +6183,7 @@ xsltParseStylesheetDoc(xmlDocPtr doc) { if (ret->compCtxt != NULL) { /* TEST TODO: REMOVE test output*/ /* printf("MAX NODE INFO: %d\n", XSLT_CCTXT(ret)->maxNodeInfos); */ - xsltCompilerCtxtFree(XSLT_CCTXT(ret)); + xsltCompilationCtxtFree(XSLT_CCTXT(ret)); ret->compCtxt = NULL; } #endif diff --git a/libxslt/xsltInternals.h b/libxslt/xsltInternals.h index 7fb8940..a43c69b 100644 --- a/libxslt/xsltInternals.h +++ b/libxslt/xsltInternals.h @@ -26,29 +26,46 @@ extern "C" { #endif +#define XSLT_IS_TEXT_NODE(n) (((n) != NULL) && \ + (((n)->type == XML_TEXT_NODE) || \ + ((n)->type == XML_CDATA_SECTION_NODE))) + /** * XSLT_REFACTORED: * - * Internal define to enable the refactored parts of Libxslt - * mostly related to pre-computation. + * Internal define to enable the refactored parts of Libxslt. */ /* #define XSLT_REFACTORED */ #ifdef XSLT_REFACTORED -extern const xmlChar *xsltConstNamespaceNameXSLT; +/* TODO: REMOVE: #define XSLT_REFACTORED_EXCLRESNS */ + +/* TODO: REMOVE: #define XSLT_REFACTORED_NSALIAS */ /** - * IS_XSLT_ELEM_FAST: + * XSLT_REFACTORED_XSLT_NSCOMP * - * Checks that the element pertains to XSLT namespace. + * Internal define to enable the pointer-comparison of + * namespaces of XSLT elements. */ -#define IS_XSLT_ELEM_FAST(e) \ - (((e) != NULL) && ((e)->type == XML_ELEMENT_NODE) && \ - ((e)->ns != NULL) && ((e)->ns->href == xsltConstNamespaceNameXSLT)) +#define XSLT_REFACTORED_XSLT_NSCOMP -#define IS_IN_XSLT_NS(e) \ - (((e)->ns != NULL) && ((e)->ns->href == xsltConstNamespaceNameXSLT)) +/** + * XSLT_REFACTORED_XPATHCOMP + * + * Internal define to enable the optimization of the + * compilation of XPath expressions. + */ +#define XSLT_REFACTORED_XPATHCOMP + +#ifdef XSLT_REFACTORED_XSLT_NSCOMP + +extern const xmlChar *xsltConstNamespaceNameXSLT; + +#define IS_XSLT_ELEM_FAST(n) \ + (((n) != NULL) && ((n)->ns != NULL) && \ + ((n)->ns->href == xsltConstNamespaceNameXSLT)) #define XSLT_HAS_INTERNAL_NSMAP(s) \ (((s) != NULL) && ((s)->principal) && \ @@ -57,6 +74,16 @@ extern const xmlChar *xsltConstNamespaceNameXSLT; #define XSLT_GET_INTERNAL_NSMAP(s) ((s)->principal->principalData->nsMap) +#else /* XSLT_REFACTORED_XSLT_NSCOMP */ + +#define IS_XSLT_ELEM_FAST(n) \ + (((n) != NULL) && ((n)->ns != NULL) && \ + (xmlStrEqual((n)->ns->href, XSLT_NAMESPACE))) + + +#endif /* XSLT_REFACTORED_XSLT_NSCOMP */ + + /** * XSLT_REFACTORED_MANDATORY_VERSION: * @@ -288,7 +315,9 @@ typedef enum { XSLT_FUNC_FALLBACK, XSLT_FUNC_MESSAGE, XSLT_FUNC_INCLUDE, - XSLT_FUNC_ATTRSET + XSLT_FUNC_ATTRSET, + XSLT_FUNC_LITERAL_RESULT_ELEMENT, + XSLT_FUNC_UNKOWN_FORWARDS_COMPAT, #endif } xsltStyleType; @@ -354,9 +383,9 @@ XSLTPUBFUN int XSLTCALL * * ************************************************************************/ -typedef struct _xsltNsList xsltNsList; -typedef xsltNsList *xsltNsListPtr; -struct _xsltNsList { +typedef struct _xsltNsListContainer xsltNsListContainer; +typedef xsltNsListContainer *xsltNsListContainerPtr; +struct _xsltNsListContainer { xmlNsPtr *list; int number; }; @@ -392,7 +421,7 @@ struct _xsltNsList { * * The in-scope namespaces. */ -#define XSLT_ITEM_NSINSCOPE_FIELDS xsltNsListPtr inScopeNs; +#define XSLT_ITEM_NSINSCOPE_FIELDS xsltNsListContainerPtr inScopeNs; /** * XSLT_ITEM_COMMON_FIELDS: @@ -425,7 +454,7 @@ struct _xsltStylePreComp { xmlNodePtr inst; /* the node in the stylesheet's tree corresponding to this item. */ /* Currently no navigational fields. */ - xsltNsListPtr inScopeNs; + xsltNsListContainerPtr inScopeNs; }; /** @@ -924,6 +953,19 @@ struct _xsltStyleItemInclude { /************************************************************************ * * + * XSLT elements in forwards-compatible mode * + * * + ************************************************************************/ + +typedef struct _xsltStyleItemUknown xsltStyleItemUknown; +typedef xsltStyleItemUknown *xsltStyleItemUknownPtr; +struct _xsltStyleItemUknown { + XSLT_ITEM_COMMON_FIELDS +}; + + +/************************************************************************ + * * * Extension elements * * * ************************************************************************/ @@ -961,15 +1003,50 @@ struct _xsltStyleItemExtElement { * * ************************************************************************/ +typedef struct _xsltEffectiveNs xsltEffectiveNs; +typedef xsltEffectiveNs *xsltEffectiveNsPtr; +struct _xsltEffectiveNs { + xsltEffectiveNsPtr next; /* next item in the list */ + const xmlChar *prefix; + const xmlChar *nsName; +}; + /* - * Literal result elements. - */ -typedef struct _xsltStyleItemLRElement xsltStyleItemLRElement; -typedef xsltStyleItemLRElement *xsltStyleItemLRElementPtr; -struct _xsltStyleItemLRElement { + * Info for literal result elements. + * This will be set on the elem->psvi field and will be + * shared by literal result elements, which have the same + * excluded result namespaces; i.e., this *won't* be created uniquely + * for every literal result element. + */ +typedef struct _xsltStyleItemLRElementInfo xsltStyleItemLRElementInfo; +typedef xsltStyleItemLRElementInfo *xsltStyleItemLRElementInfoPtr; +struct _xsltStyleItemLRElementInfo { XSLT_ITEM_COMMON_FIELDS - xsltPointerListPtr exclResultNs; + /* + * @effectiveNs is the set of effective ns-nodes + * on the literal result element, which will be added to the result + * element if not already existing in the result tree. + * This means that excluded namespaces (via exclude-result-prefixes, + * extension-element-prefixes and the XSLT namespace) not added + * to the set. + * Namespace-aliasing was applied on the @effectiveNs. + */ + xsltEffectiveNsPtr effectiveNs; +}; + +#ifdef XSLT_REFACTORED + +typedef struct _xsltNsAlias xsltNsAlias; +typedef xsltNsAlias *xsltNsAliasPtr; +struct _xsltNsAlias { + xsltNsAliasPtr next; /* next in the list */ + xmlNsPtr literalNs; + xmlNsPtr targetNs; + xmlDocPtr docOfTargetNs; }; +#endif + +#ifdef XSLT_REFACTORED_XSLT_NSCOMP typedef struct _xsltNsMap xsltNsMap; typedef xsltNsMap *xsltNsMapPtr; @@ -981,6 +1058,7 @@ struct _xsltNsMap { const xmlChar *origNsName; /* the original XML namespace name */ const xmlChar *newNsName; /* the mapped XML namespace name */ }; +#endif /************************************************************************ * * @@ -991,6 +1069,13 @@ struct _xsltNsMap { typedef struct _xsltPrincipalStylesheetData xsltPrincipalStylesheetData; typedef xsltPrincipalStylesheetData *xsltPrincipalStylesheetDataPtr; +typedef struct _xsltNsList xsltNsList; +typedef xsltNsList *xsltNsListPtr; +struct _xsltNsList { + xsltNsListPtr next; /* next in the list */ + xmlNsPtr ns; +}; + #define XSLT_ELEMENT_CATEGORY_XSLT 0 #define XSLT_ELEMENT_CATEGORY_EXTENSION 1 #define XSLT_ELEMENT_CATEGORY_LR 2 @@ -1012,11 +1097,22 @@ struct _xsltCompilerNodeInfo { extension element */ xsltStyleType type; xsltElemPreCompPtr item; /* The compiled information */ - xsltNsListPtr inScopeNs; /* The in-scope namespaces for the current - position in the node-tree */ - xsltPointerListPtr exclResultNs; /* The current excluded - result namespaces */ + /* The current in-scope namespaces */ + xsltNsListContainerPtr inScopeNs; + /* The current excluded result namespaces */ + xsltPointerListPtr exclResultNs; + /* The current extension instruction namespaces */ xsltPointerListPtr extElemNs; + /* The current info for literal result elements. */ + xsltStyleItemLRElementInfoPtr litResElemInfo; + /* + * Set to 1 if in-scope namespaces changed, + * or excluded result namespaces changed, + * or extension element namespaces changed. + * This will trigger creation of new infos + * for literal result elements. + */ + int nsChanged; int preserveWhitespace; int stripWhitespace; int isRoot; /* whether this is the stylesheet's root node */ @@ -1024,7 +1120,7 @@ struct _xsltCompilerNodeInfo { /* whether the content of an extension element was processed */ int extContentHandled; /* the type of the current child */ - xsltStyleType curChildType; + xsltStyleType curChildType; }; #define XSLT_CCTXT(style) ((xsltCompilerCtxtPtr) style->compCtxt) @@ -1068,7 +1164,13 @@ struct _xsltCompilerCtxt { * mechanisms like whitespace-stripping in the stylesheet. */ int strict; - xsltPrincipalStylesheetDataPtr psData; + xsltPrincipalStylesheetDataPtr psData; +#ifdef XSLT_REFACTORED_XPATHCOMP + xmlXPathContextPtr xpathCtxt; +#endif + xsltStyleItemUknownPtr unknownItem; + int hasNsAliases; /* Indicator if there was an xsl:namespace-alias. */ + xsltNsAliasPtr nsAliases; }; #else /* XSLT_REFACTORED */ @@ -1164,19 +1266,21 @@ struct _xsltPrincipalStylesheetData { /* * Global list of in-scope namespaces. */ - void *inScopeNamespaces; + xsltPointerListPtr inScopeNamespaces; /* * Global list of information for [xsl:]excluded-result-prefixes. */ - void *exclResultNamespaces; + xsltPointerListPtr exclResultNamespaces; /* * Global list of information for [xsl:]extension-element-prefixes. */ - void *extElemNamespaces; + xsltPointerListPtr extElemNamespaces; +#ifdef XSLT_REFACTORED_XSLT_NSCOMP /* * Namespace name map to get rid of string comparison of namespace names. */ xsltNsMapPtr nsMap; +#endif }; @@ -1227,6 +1331,7 @@ struct _xsltStylesheet { /* * Namespace aliases. + * NOTE: Not used in the refactored code. */ xmlHashTablePtr nsAliases; /* the namespace alias hash tables */ @@ -1237,6 +1342,7 @@ struct _xsltStylesheet { /* * Namespaces. + * TODO: Eliminate this. */ xmlHashTablePtr nsHash; /* the set of namespaces in use: ATTENTION: This is used for @@ -1309,6 +1415,7 @@ struct _xsltStylesheet { void *attVTs; /* * if namespace-alias has an alias for the default stylesheet prefix + * NOTE: Not used in the refactored code. */ const xmlChar *defaultAlias; /* @@ -1600,10 +1707,12 @@ XSLTPUBFUN void XSLTCALL XSLTPUBFUN int XSLTCALL xsltParseAnyXSLTElem (xsltCompilerCtxtPtr cctxt, xmlNodePtr elem); +#ifdef XSLT_REFACTORED_XSLT_NSCOMP XSLTPUBFUN int XSLTCALL xsltRestoreDocumentNamespaces( xsltNsMapPtr ns, xmlDocPtr doc); +#endif #endif /* XSLT_REFACTORED */ #ifdef __cplusplus diff --git a/libxslt/xsltutils.c b/libxslt/xsltutils.c index 723962f..d9b094e 100644 --- a/libxslt/xsltutils.c +++ b/libxslt/xsltutils.c @@ -2063,22 +2063,50 @@ xsltGetProfileInformation(xsltTransformContextPtr ctxt) */ xmlXPathCompExprPtr xsltXPathCompile(xsltStylesheetPtr style, const xmlChar *str) { - xmlXPathContextPtr xctxt; + xmlXPathContextPtr xpathCtxt; xmlXPathCompExprPtr ret; - if (style != NULL) - xctxt = xmlXPathNewContext(style->doc); - else - xctxt = xmlXPathNewContext(NULL); - if (style != NULL) - xctxt->dict = style->dict; - ret = xmlXPathCtxtCompile(xctxt, str); - xmlXPathFreeContext(xctxt); + if (style != NULL) { +#ifdef XSLT_REFACTORED_XPATHCOMP + if (XSLT_CCTXT(style)) { + /* + * Proposed by Jerome Pesenti + * -------------------------- + * For better efficiency we'll reuse the compilation + * context's XPath context. For the common stylesheet using + * XPath expressions this will reduce compilation time to + * about 50%. + * + * See http://mail.gnome.org/archives/xslt/2006-April/msg00037.html + */ + xpathCtxt = XSLT_CCTXT(style)->xpathCtxt; + xpathCtxt->doc = style->doc; + } else + xpathCtxt = xmlXPathNewContext(style->doc); +#else + xpathCtxt = xmlXPathNewContext(style->doc); +#endif + xpathCtxt->dict = style->dict; + } else { + xpathCtxt = xmlXPathNewContext(NULL); + } + /* + * Compile the expression. + */ + ret = xmlXPathCtxtCompile(xpathCtxt, str); + +#ifdef XSLT_REFACTORED_XPATHCOMP + if ((style == NULL) || (! XSLT_CCTXT(style))) { + xmlXPathFreeContext(xpathCtxt); + } +#else + xmlXPathFreeContext(xpathCtxt); +#endif /* * TODO: there is a lot of optimizations which should be possible * like variable slot precomputations, function precomputations, etc. */ - + return(ret); } -- 2.7.4