+Thu Jun 1 13:58:19 CEST 2006 Kasimier Buchcik <libxml2-cvs@cazic.net>
+
+ * libxslt/attributes.c libxslt/variables.c
+ libxslt/transform.c libxslt/xslt.c:
+ Next step in the refactored code: enhanced xsl:attribute;
+ enhanced xsltCopyProp.
+ Added the creation of an XPath cache in
+ xsltNewTransformContext().
+
Wed May 31 22:32:44 CEST 2006 Kasimier Buchcik <libxml2-cvs@cazic.net>
* libxslt/transform.c: Fixed a difference in processing of
* @node: the node in the source tree.
* @inst: the xsl:attribute element
* @comp: precomputed information
- * @fromset: the attribute comes from an attribute-set
+ * @fromAttributeSet: the attribute comes from an attribute-set
*
* Process the xslt attribute node on the source node
*/
xsltAttributeInternal(xsltTransformContextPtr ctxt, xmlNodePtr node,
xmlNodePtr inst,
xsltStylePreCompPtr castedComp,
- int fromset) {
+ int fromAttributeSet) {
#ifdef XSLT_REFACTORED
xsltStyleItemAttributePtr comp =
(xsltStyleItemAttributePtr) castedComp;
+ /*
+ * TODO: Change the fields of the compiled struct:
+ * 1) @name (char)
+ * 2) @nameType (String, AVT)
+ * 3) @nsName (char)
+ * 4) nsNameType (None, String, AVT)
+ */
#else
xsltStylePreCompPtr comp = castedComp;
#endif
- xmlChar *prop = NULL;
- xmlChar *namespace;
- const xmlChar *name = NULL;
- const xmlChar *prefix = NULL;
+ xmlNodePtr targetElem;
+ xmlChar *prop = NULL;
+ const xmlChar *name = NULL, *prefix = NULL, *nsName = NULL;
xmlChar *value = NULL;
xmlNsPtr ns = NULL;
- xmlAttrPtr attr;
- const xmlChar *URL = NULL;
+ xmlAttrPtr attr;
+ if ((ctxt == NULL) || (node == NULL) || (inst == NULL))
+ return;
+ /*
+ * BIG NOTE: This previously used xsltGetSpecialNamespace() and
+ * xsltGetNamespace(), but since both are not appropriate, we
+ * will process namespace lookup here to avoid adding yet another
+ * ns-lookup function to namespaces.c.
+ */
+ /*
+ * SPEC XSLT 1.0: Error cases:
+ * - Creating nodes other than text nodes during the instantiation of
+ * the content of the xsl:attribute element; implementations may
+ * either signal the error or ignore the offending nodes."
+ */
- if ((comp == NULL) || (ctxt == NULL)) {
+ if (comp == NULL) {
xsltTransformError(ctxt, NULL, inst,
- "xsl:attribute : compilation failed\n");
+ "Internal error in xsltAttributeInternal(): "
+ "The instruction was no compiled.\n");
return;
- }
+ }
+ /*
+ * TODO: Shouldn't ctxt->insert == NULL be treated as an internal error?
+ * So report an internal error?
+ */
if (ctxt->insert == NULL)
- return;
-
- if ((node == NULL) || (inst == NULL) || (comp == NULL))
- return;
- if (!comp->has_name) {
- return;
- }
- if (ctxt->insert->children != NULL) {
+ return;
+ /*
+ * SPEC XSLT 1.0:
+ * "Adding an attribute to a node that is not an element;
+ * implementations may either signal the error or ignore the attribute."
+ *
+ * TODO: I think we should signal such errors in the future, and maybe
+ * provide an option to ignore such errors.
+ */
+ targetElem = ctxt->insert;
+ if (targetElem->type != XML_ELEMENT_NODE)
+ return;
+
+ /*
+ * SPEC XSLT 1.0:
+ * "Adding an attribute to an element after children have been added
+ * to it; implementations may either signal the error or ignore the
+ * attribute."
+ *
+ * TODO: We should decide whether not to report such errors or
+ * to ignore them; note that we *ignore* if the parent is not an
+ * element, but here we report an error.
+ */
+ if (targetElem->children != NULL) {
+ /*
+ * NOTE: Ah! This seems to be intended to support streamed
+ * result generation!.
+ */
xsltTransformError(ctxt, NULL, inst,
- "xsl:attribute : node already has children\n");
+ "xsl:attribute: Cannot add attributes to an "
+ "element if children have been already added "
+ "to the element.\n");
return;
}
+
+ /*
+ * Process the name
+ * ----------------
+ */
+ if (!comp->has_name) /* TODO: raise error */
+ return;
+
#ifdef WITH_DEBUGGER
- if (ctxt->debugStatus != XSLT_DEBUG_NONE) {
+ if (ctxt->debugStatus != XSLT_DEBUG_NONE)
xslHandleDebugger(inst, node, NULL, ctxt);
- }
#endif
if (comp->name == NULL) {
- prop =
- xsltEvalAttrValueTemplate(ctxt, inst, (const xmlChar *) "name",
- XSLT_NAMESPACE);
+ /* TODO: fix attr acquisition wrt to the XSLT namespace */
+ prop = xsltEvalAttrValueTemplate(ctxt, inst,
+ (const xmlChar *) "name", XSLT_NAMESPACE);
if (prop == NULL) {
xsltTransformError(ctxt, NULL, inst,
- "xsl:attribute : name is missing\n");
+ "xsl:attribute: The attribute 'name' is missing.\n");
goto error;
}
if (xmlValidateQName(prop, 0)) {
xsltTransformError(ctxt, NULL, inst,
- "xsl:attribute : invalid QName\n");
+ "xsl:attribute: The effective name '%s' is not a "
+ "valid QName.\n", prop);
/* we fall through to catch any further errors, if possible */
}
name = xsltSplitQName(ctxt->dict, prop, &prefix);
if (!xmlStrncasecmp(prefix, (xmlChar *) "xmlns", 5)) {
#ifdef WITH_XSLT_DEBUG_PARSING
xsltGenericDebug(xsltGenericDebugContext,
- "xsltAttribute: xmlns prefix forbidden\n");
+ "xsltAttribute: xmlns prefix forbidden\n");
#endif
+ /*
+ * SPEC XSLT 1.0:
+ * "It is an error if the string that results from instantiating
+ * the attribute value template is not a QName or is the string
+ * xmlns. An XSLT processor may signal the error; if it does not
+ * signal the error, it must recover by not adding the attribute
+ * to the result tree."
+ * TODO: Decide which way to go here.
+ */
goto error;
}
- if ((comp->ns == NULL) && (comp->has_ns)) {
- namespace = xsltEvalAttrValueTemplate(ctxt, inst,
- (const xmlChar *)
- "namespace", XSLT_NAMESPACE);
+ /*
+ * Process namespace semantics
+ * ---------------------------
+ *
+ * Evaluate the namespace name.
+ */
+ if (comp->has_ns) {
+ if (comp->ns != NULL) {
+ if (comp->ns[0] != 0)
+ nsName = comp->ns;
+ } else {
+ xmlChar *tmpNsName;
+ /*
+ * Eval the AVT.
+ */
+ /* TODO: check attr acquisition wrt to the XSLT namespace */
+ tmpNsName = xsltEvalAttrValueTemplate(ctxt, inst,
+ (const xmlChar *) "namespace", XSLT_NAMESPACE);
+ /*
+ * This fixes bug #302020: The AVT might also evaluate to the
+ * empty string; this means that the empty string also indicates
+ * "no namespace".
+ * SPEC XSLT 1.0:
+ * "If the string is empty, then the expanded-name of the
+ * attribute has a null namespace URI."
+ */
+ if ((tmpNsName != NULL) && (tmpNsName[0] != 0))
+ nsName = xmlDictLookup(ctxt->dict, BAD_CAST tmpNsName, -1);
+ xmlFree(tmpNsName);
+ };
+ } else if (prefix != NULL) {
+ /*
+ * SPEC XSLT 1.0:
+ * "If the namespace attribute is not present, then the QName is
+ * expanded into an expanded-name using the namespace declarations
+ * in effect for the xsl:attribute element, *not* including any
+ * default namespace declaration."
+ */
+ ns = xmlSearchNs(inst->doc, inst, prefix);
+ if (ns == NULL) {
+ /*
+ * Note that this is treated as an error now (checked with
+ * Saxon, Xalan-J and MSXML).
+ */
+ xsltTransformError(ctxt, NULL, inst,
+ "xsl:attribute: The effective prefix '%s', has no "
+ "namespace binding in scope in the stylesheet; "
+ "this is an error, since the namespace was not "
+ "specified by the instruction itself.\n", prefix);
+ } else
+ nsName = ns->href;
+ }
+
+ if (fromAttributeSet) {
/*
- * This fixes bug #302020: check also for the empty string.
+ * I think this tries to ensure that xsl:attribute(s) coming
+ * from an xsl:attribute-set won't override attribute of
+ * literal result elements or of explicit xsl:attribute(s).
*/
- if ((namespace != NULL) && (*namespace != 0)) {
- ns = xsltGetSpecialNamespace(ctxt, inst, namespace, prefix,
- ctxt->insert);
- xmlFree(namespace);
- } else {
- if (prefix != NULL) {
- ns = xmlSearchNs(inst->doc, inst, prefix);
- if (ns == NULL) {
- xsltTransformError(ctxt, NULL, inst,
- "xsl:attribute : no namespace bound to prefix %s\n",
- prefix);
- } else {
- ns = xsltGetNamespace(ctxt, inst, ns, ctxt->insert);
- }
- }
- }
- } else if (comp->ns != NULL) {
- ns = xsltGetSpecialNamespace(ctxt, inst, comp->ns, prefix,
- ctxt->insert);
- } else if (prefix != NULL) {
- xmlNsPtr tmp;
- tmp = xmlSearchNs(inst->doc, inst, prefix);
- if (tmp != NULL) {
- ns = xsltGetNamespace(ctxt, inst, tmp, ctxt->insert);
+ attr = xmlHasNsProp(targetElem, name, nsName);
+ if (attr != NULL)
+ return;
+ }
+
+ /*
+ * Something about ns-prefixes:
+ * SPEC XSLT 1.0:
+ * "XSLT processors may make use of the prefix of the QName specified
+ * in the name attribute when selecting the prefix used for outputting
+ * the created attribute as XML; however, they are not required to do
+ * so and, if the prefix is xmlns, they must not do so"
+ */
+ /*
+ * Find/create a matching ns-decl in the result tree.
+ */
+ ns = NULL;
+#if 0
+ if (0) {
+ /*
+ * OPTIMIZE TODO: How do we know if we are adding to a
+ * fragment or not?
+ *
+ * If we are adding to a result tree fragment (i.e., not to the
+ * actual result tree), we'll don't bother searching for the
+ * ns-decl, but just store it in the dummy-doc of the result
+ * tree fragment.
+ */
+ if (nsName != NULL) {
+ ns = xsltTreeAcquireStoredNs(ctxt->document->doc, nsName,
+ prefix);
}
}
+#endif
+ if (nsName != NULL) {
+ /*
+ * The owner-element might be in the same namespace.
+ */
+ if ((targetElem->ns != NULL) &&
+ (targetElem->ns->prefix != NULL) &&
+ xmlStrEqual(targetElem->ns->href, nsName))
+ {
+ ns = targetElem->ns;
+ goto namespace_finished;
+ }
+ if (prefix != NULL) {
+ /*
+ * Search by ns-prefix.
+ */
+ ns = xmlSearchNs(targetElem->doc, targetElem, prefix);
+ if ((ns != NULL) && (xmlStrEqual(ns->href, nsName))) {
+ goto namespace_finished;
+ }
+ }
+ /*
+ * Fallback to a search by ns-name.
+ */
+ ns = xmlSearchNsByHref(targetElem->doc, targetElem, nsName);
+ if ((ns != NULL) && (ns->prefix != NULL)) {
+ goto namespace_finished;
+ }
+ /*
+ * OK, we need to declare the namespace on the target element.
+ */
+ if (prefix) {
+ if (targetElem->nsDef != NULL) {
+ ns = targetElem->nsDef;
+ do {
+ if ((ns->prefix) && xmlStrEqual(ns->prefix, prefix)) {
+ /*
+ * The prefix is aready occupied.
+ */
+ break;
+ }
+ ns = ns->next;
+ } while (ns != NULL);
+ if (ns == NULL) {
+ ns = xmlNewNs(targetElem, nsName, prefix);
+ goto namespace_finished;
+ }
+ }
+ }
+ /*
+ * Generate a new prefix.
+ */
+ {
+ const xmlChar *basepref = prefix;
+ xmlChar pref[30];
+ int counter = 0;
+
+ if (prefix != NULL)
+ basepref = prefix;
+ else
+ basepref = xmlStrdup(BAD_CAST "ns");
+
+ do {
+ snprintf((char *) pref, 30, "%s_%d",
+ basepref, counter++);
+ ns = xmlSearchNs(targetElem->doc, targetElem, BAD_CAST pref);
+ if (counter > 1000) {
+ xsltTransformError(ctxt, NULL, inst,
+ "Namespace fixup error: Failed to compute a "
+ "new unique ns-prefix for the generated attribute "
+ "{%s}%s'.\n", nsName, name);
+ ns = NULL;
+ break;
+ }
+ } while (ns != NULL);
+ if (basepref != prefix)
+ xmlFree((xmlChar *)basepref);
+ ns = xmlNewNs(targetElem, nsName, BAD_CAST pref);
+ }
- if ((fromset) && (ns != NULL))
- URL = ns->href;
+namespace_finished:
- if (fromset) {
- if (URL != NULL)
- attr = xmlHasNsProp(ctxt->insert, name, URL);
- else
- attr = xmlHasProp(ctxt->insert, name);
- if (attr != NULL)
- return;
+ if (ns == NULL) {
+ xsltTransformError(ctxt, NULL, inst,
+ "Namespace fixup error: Failed to acquire an in-scope "
+ "namespace binding of the generated attribute '{%s}%s'.\n",
+ nsName, name);
+ /*
+ * TODO: Should we just stop here?
+ */
+ }
}
-
+ /*
+ * Construction of the value
+ * -------------------------
+ */
if (inst->children == NULL) {
/*
* No content.
+ * TODO: Do we need to put the empty string in ?
*/
attr = xmlSetNsProp(ctxt->insert, ns, name, (const xmlChar *) "");
} else if ((inst->children->next == NULL) &&
* Optimization: if the content is just 1 text node, then
* just need to copy it over, or just assign it to the result
* if the string is shared.
- * NOTE that xmlSetNsProp() will query if this is an ID attribute :-/
*/
attr = xmlSetNsProp(ctxt->insert, ns, name, NULL);
- if (attr == NULL) /* TODO: report error */
- goto internal_err;
+ if (attr == NULL) /* TODO: report error ? */
+ goto error;
/*
* This was taken over from xsltCopyText() (transform.c).
*/
{
copyTxt = xmlNewText(NULL);
if (copyTxt == NULL) /* TODO: report error */
- goto internal_err;
+ goto error;
/*
* This is a safe scenario where we don't need to lookup
* the dict.
copyTxt->content = origTxt->content;
/*
* Copy "disable-output-escaping" information.
+ * TODO: Does this have any effect for attribute values
+ * anyway?
*/
if (origTxt->name == xmlStringTextNoenc)
- copyTxt->name = xmlStringTextNoenc;
+ copyTxt->name = xmlStringTextNoenc;
} else {
/*
* Copy the value.
*/
copyTxt = xmlNewText(origTxt->content);
if (copyTxt == NULL) /* TODO: report error */
- goto internal_err;
+ goto error;
+ /*
+ * Copy "disable-output-escaping" information.
+ * TODO: Does this have any effect for attribute values
+ * anyway?
+ */
if (origTxt->name == xmlStringTextNoenc)
- copyTxt->name = xmlStringTextNoenc;
+ copyTxt->name = xmlStringTextNoenc;
}
- if (copyTxt != NULL) {
+ if (copyTxt != NULL) {
copyTxt->doc = attr->doc;
- xmlAddChild((xmlNodePtr) attr, copyTxt);
+ xmlAddChild((xmlNodePtr) attr, copyTxt);
}
} else {
/*
* TODO: Do we have to add the empty string to the attr?
*/
attr = xmlSetNsProp(ctxt->insert, ns, name,
- (const xmlChar *) "");
-
+ (const xmlChar *) "");
}
}
error:
-internal_err:
return;
}
if (cur->templTab == NULL) {
xsltTransformError(NULL, NULL, (xmlNodePtr) doc,
"xsltNewTransformContext: out of memory\n");
- xmlFree(cur);
- return(NULL);
+ goto internal_err;
}
cur->templNr = 0;
cur->templMax = 5;
if (cur->varsTab == NULL) {
xmlGenericError(xmlGenericErrorContext,
"xsltNewTransformContext: out of memory\n");
- xmlFree(cur->templTab);
- xmlFree(cur);
- return(NULL);
+ goto internal_err;
}
cur->varsNr = 0;
cur->varsMax = 5;
if (cur->xpathCtxt == NULL) {
xsltTransformError(NULL, NULL, (xmlNodePtr) doc,
"xsltNewTransformContext : xmlXPathNewContext failed\n");
- xmlFree(cur->templTab);
- xmlFree(cur->varsTab);
- xmlFree(cur);
- return(NULL);
+ goto internal_err;
}
cur->xpathCtxt->proximityPosition = 0;
cur->xpathCtxt->contextSize = 0;
-
+ /*
+ * Create an XPath cache.
+ */
+ if (xmlXPathContextSetCache(cur->xpathCtxt, 1, -1, 0) == -1)
+ goto internal_err;
/*
* Initialize the extras array
*/
if (cur->extras == NULL) {
xmlGenericError(xmlGenericErrorContext,
"xsltNewTransformContext: out of memory\n");
- xmlFree(cur->xpathCtxt);
- xmlFree(cur->varsTab);
- xmlFree(cur->templTab);
- xmlFree(cur);
- return(NULL);
+ goto internal_err;
}
cur->extrasNr = style->extrasNr;
for (i = 0;i < cur->extrasMax;i++) {
if (docu == NULL) {
xsltTransformError(cur, NULL, (xmlNodePtr)doc,
"xsltNewTransformContext : xsltNewDocument failed\n");
- xmlFree(cur->templTab);
- xmlFree(cur->varsTab);
- xmlFree(cur);
- return(NULL);
+ goto internal_err;
}
docu->main = 1;
cur->document = docu;
cur->xinclude = xsltGetXIncludeDefault();
return(cur);
+
+internal_err:
+ if (cur != NULL)
+ xsltFreeTransformContext(cur);
+ return(NULL);
}
/**
if (copy != NULL) {
if (target != NULL) {
copy->doc = target->doc;
+ /*
+ * MAYBE TODO: Maybe we should reset the ctxt->lasttext here
+ * to ensure that the optimized text-merging mechanism
+ * won't interfere with normal node-merging in any case.
+ */
xmlAddChild(target, copy);
}
} else {
/**
* xsltCopyProp:
* @ctxt: a XSLT process context
- * @target: the element where the attribute will be grafted
- * @attr: the attribute
+ * @targetElem: the element where the attribute will be grafted
+ * @attr: the attribute to be copied
*
* Do a copy of an attribute
*
* Returns: a new xmlAttrPtr, or NULL in case of error.
*/
static xmlAttrPtr
-xsltCopyProp(xsltTransformContextPtr ctxt, xmlNodePtr target,
- xmlAttrPtr attr) {
- xmlAttrPtr ret = NULL;
- xmlNsPtr ns;
- xmlChar *val;
+xsltCopyProp(xsltTransformContextPtr ctxt, xmlNodePtr targetElem,
+ xmlAttrPtr attr)
+{
+ xmlAttrPtr attrCopy;
+ xmlChar *value;
+#ifdef XSLT_REFACTORED
+ xmlNodePtr txtNode;
+#endif
if (attr == NULL)
return(NULL);
- if (target->type != XML_ELEMENT_NODE)
+
+ if (targetElem->type != XML_ELEMENT_NODE) {
+ /*
+ * TODO: Hmm, it would be better to have the node at hand of the
+ * instruction which lead to this here.
+ */
+ xsltTransformError(ctxt, NULL, NULL,
+ "Result tree construction error: cannot set an attribute node "
+ "on a non-element node.\n");
return(NULL);
+ }
+#ifdef XSLT_REFACTORED
+ /*
+ * Create the attribute node.
+ */
if (attr->ns != NULL) {
- ns = xsltGetPlainNamespace(ctxt, attr->parent, attr->ns, target);
+ xmlNsPtr ns = NULL;
+ const xmlChar *prefix = attr->ns->prefix;
+
+ /*
+ * Process namespace semantics
+ *
+ * RESTRUCTURE TODO: This is the same code as in
+ * xsltAttributeInternal() (attributes.c), but I currently
+ * don't want to add yet another ns-lookup function.
+ */
+ if ((targetElem->ns != NULL) &&
+ (targetElem->ns->prefix != NULL) &&
+ xmlStrEqual(targetElem->ns->href, attr->ns->href))
+ {
+ ns = targetElem->ns;
+ goto namespace_finished;
+ }
+ if (prefix != NULL) {
+ /*
+ * Search by ns-prefix.
+ */
+ ns = xmlSearchNs(targetElem->doc, targetElem, prefix);
+ if ((ns != NULL) &&
+ (xmlStrEqual(ns->href, attr->ns->href)))
+ {
+ goto namespace_finished;
+ }
+ }
+ /*
+ * Fallback to a search by ns-name.
+ */
+ ns = xmlSearchNsByHref(targetElem->doc, targetElem, attr->ns->href);
+ if ((ns != NULL) && (ns->prefix != NULL)) {
+ goto namespace_finished;
+ }
+ /*
+ * OK, we need to declare the namespace on the target element.
+ */
+ if (prefix) {
+ if (targetElem->nsDef != NULL) {
+ ns = targetElem->nsDef;
+ do {
+ if ((ns->prefix) && xmlStrEqual(ns->prefix, prefix)) {
+ /*
+ * The prefix aready occupied.
+ */
+ break;
+ }
+ ns = ns->next;
+ } while (ns != NULL);
+ if (ns == NULL) {
+ ns = xmlNewNs(targetElem, attr->ns->href, prefix);
+ goto namespace_finished;
+ }
+ }
+ }
+ /*
+ * Generate a new prefix.
+ */
+ {
+ const xmlChar *basepref = prefix;
+ xmlChar pref[30];
+ int counter = 0;
+
+ if (prefix != NULL)
+ basepref = prefix;
+ else
+ basepref = xmlStrdup(BAD_CAST "ns");
+
+ do {
+ snprintf((char *) pref, 30, "%s_%d",
+ basepref, counter++);
+ ns = xmlSearchNs(targetElem->doc,
+ (xmlNodePtr) attr, BAD_CAST pref);
+ if (counter > 1000) {
+ xsltTransformError(ctxt, NULL, (xmlNodePtr) attr,
+ "Namespace fixup error: Failed to compute a "
+ "new unique ns-prefix for the copied attribute "
+ "{%s}%s'.\n", attr->ns->href, attr->name);
+ ns = NULL;
+ break;
+ }
+ } while (ns != NULL);
+ if (basepref != prefix)
+ xmlFree((xmlChar *)basepref);
+ ns = xmlNewNs(targetElem, attr->ns->href, BAD_CAST pref);
+ }
+
+namespace_finished:
+
+ if (ns == NULL) {
+ xsltTransformError(ctxt, NULL, (xmlNodePtr) attr,
+ "Namespace fixup error: Failed to acquire an in-scope "
+ "namespace binding of the copied attribute '{%s}%s'.\n",
+ attr->ns->href, attr->name);
+ /*
+ * TODO: Should we just stop here?
+ */
+ }
+ attrCopy = xmlSetNsProp(targetElem, ns, attr->name, NULL);
} else {
- ns = NULL;
+ attrCopy = xmlSetNsProp(targetElem, NULL, attr->name, NULL);
}
- val = xmlNodeListGetString(attr->doc, attr->children, 1);
- ret = xmlSetNsProp(target, ns, attr->name, val);
- if (val != NULL)
- xmlFree(val);
- return(ret);
+ if (attrCopy == NULL)
+ return(NULL);
+ /*
+ * NOTE: This was optimized according to bug #342695.
+ * TODO: Can this further be optimized, if source and target
+ * share the same dict and attr->children is just 1 text node
+ * which is in the dict? How probable is such a case?
+ */
+ value = xmlNodeListGetString(attr->doc, attr->children, 1);
+ if (value != NULL) {
+ txtNode = xmlNewDocText(targetElem->doc, NULL);
+ if (txtNode == NULL)
+ return(NULL);
+ if ((targetElem->doc != NULL) &&
+ (targetElem->doc->dict != NULL))
+ {
+ txtNode->content =
+ (xmlChar *) xmlDictLookup(targetElem->doc->dict,
+ BAD_CAST value, -1);
+ xmlFree(value);
+ } else
+ txtNode->content = value;
+ attrCopy->children = txtNode;
+ }
+ /*
+ * URGENT TODO: Do we need to create an empty text node if the value
+ * is the empty string?
+ */
+
+#else /* not XSLT_REFACTORED */
+
+ value = xmlNodeListGetString(attr->doc, attr->children, 1);
+ if (attr->ns != NULL) {
+ xmlNsPtr ns;
+ ns = xsltGetPlainNamespace(ctxt, attr->parent, attr->ns, targetElem);
+ attrCopy = xmlSetNsProp(targetElem, ns, attr->name, value);
+ } else {
+ attrCopy = xmlSetNsProp(targetElem, NULL, attr->name, value);
+ }
+ if (value != NULL)
+ xmlFree(value);
+
+#endif /* not XSLT_REFACTORED */
+
+ return(attrCopy);
}
/**
* Make a copy of the full tree under the element node @node
* and insert it as last child of @insert
* For literal result element, some of the namespaces may not be copied
- * over according to section 7.1 .
+ * over according to section 7.1.
+ * TODO: Why is this a public function?
*
* Returns a pointer to the new tree, or NULL in case of error
*/
(node->type == XML_ATTRIBUTE_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.
+ * Must add any new namespaces in scope for the node.
+ * TODO: Since we try to reuse existing in-scope ns-decls by
+ * using xmlSearchNsByHref(), this will eventually change
+ * the prefix of an original ns-binding; thus it might
+ * break QNames in element/attribute content.
*/
nsList = xmlGetNsList(node->doc, node);
if (nsList != NULL) {
}
xmlFree(nsList);
}
- if (node->ns != NULL)
- copy->ns = xsltGetNamespace(ctxt, node, node->ns, copy);
- else if ((insert->type == XML_ELEMENT_NODE) && (insert->ns != NULL)) {
+ if (node->ns != NULL) {
+ /*
+ * This will map copy->ns to one of the newly created
+ * in-scope ns-decls.
+ */
+ copy->ns = xsltGetNamespace(ctxt, node, node->ns, copy);
+ } else if ((insert->type == XML_ELEMENT_NODE) &&
+ (insert->ns != NULL))
+ {
xmlNsPtr defaultNs;
defaultNs = xmlSearchNs(insert->doc, insert, NULL);
/**
* xsltTransLREAcquireResultInScopeNs:
-* @ctxt: the transformation context
-* @cur: the literal result element
-* @ns: the namespace
-* @out: the output node (or its parent)
+* @ctxt: the transformation context
+* @cur: the literal result element (in the stylesheet)
+* @literalNs: the namespace (in the stylsheet)
+* @resultElem: the generated result element
*
*
* Find a matching (prefix and ns-name) ns-declaration
*/
static xmlNsPtr
xsltTransLREAcquireResultInScopeNs(xsltTransformContextPtr ctxt,
- xmlNodePtr cur, xmlNsPtr literalNs,
+ xmlNodePtr cur,
+ xmlNsPtr literalNs,
xmlNodePtr resultElem)
{
xmlNsPtr ns;
xsltStylePreCompPtr comp = castedComp;
#endif
xmlNodePtr copy, oldInsert;
-
+
oldInsert = ctxt->insert;
if (ctxt->insert != NULL) {
switch (node->type) {
/**
* xsltCopyOf:
- * @ctxt: a XSLT process context
- * @node: the node in the source tree.
- * @inst: the xslt copy-of node
- * @comp: precomputed information
+ * @ctxt: an XSLT transformation context
+ * @node: the current node in the source tree
+ * @inst: the element node of the XSLT copy-of instruction
+ * @comp: precomputed information of the XSLT copy-of instruction
*
- * Process the xslt copy-of node on the source node
+ * Process the XSLT copy-of instruction.
*/
void
xsltCopyOf(xsltTransformContextPtr ctxt, xmlNodePtr node,
return;
}
+ /*
+ * SPEC XSLT 1.0:
+ * "The xsl:copy-of element can be used to insert a result tree
+ * fragment into the result tree, without first converting it to
+ * a string as xsl:value-of does (see [7.6.1 Generating Text with
+ * xsl:value-of]). The required select attribute contains an
+ * expression. When the result of evaluating the expression is a
+ * result tree fragment, the complete fragment is copied into the
+ * result tree. When the result is a node-set, all the nodes in the
+ * set are copied in document order into the result tree; copying
+ * an element node copies the attribute nodes, namespace nodes and
+ * children of the element node as well as the element node itself;
+ * a root node is copied by copying its children. When the result
+ * is neither a node-set nor a result tree fragment, the result is
+ * converted to a string and then inserted into the result tree,
+ * as with xsl:value-of.
+ */
+
#ifdef WITH_XSLT_DEBUG_PROCESS
XSLT_TRACE(ctxt,XSLT_TRACE_COPY_OF,xsltGenericDebug(xsltGenericDebugContext,
"xsltCopyOf: select %s\n", comp->select));
#endif
+ /*
+ * Set up the XPath evaluation context.
+ */
oldProximityPosition = ctxt->xpathCtxt->proximityPosition;
oldContextSize = ctxt->xpathCtxt->contextSize;
oldNsNr = ctxt->xpathCtxt->nsNr;
ctxt->xpathCtxt->namespaces = comp->nsList;
ctxt->xpathCtxt->nsNr = comp->nsNr;
#endif
+ /*
+ * Evaluate the "select" expression.
+ */
res = xmlXPathCompiledEval(comp->comp, ctxt->xpathCtxt);
+ /*
+ * Revert the XPath evaluation context to previous state.
+ */
ctxt->xpathCtxt->proximityPosition = oldProximityPosition;
ctxt->xpathCtxt->contextSize = oldContextSize;
ctxt->xpathCtxt->nsNr = oldNsNr;
ctxt->xpathCtxt->namespaces = oldNamespaces;
+
if (res != NULL) {
if (res->type == XPATH_NODESET) {
+ /*
+ * Node-set
+ * --------
+ */
#ifdef WITH_XSLT_DEBUG_PROCESS
XSLT_TRACE(ctxt,XSLT_TRACE_COPY_OF,xsltGenericDebug(xsltGenericDebugContext,
"xsltCopyOf: result is a node set\n"));
#endif
list = res->nodesetval;
if (list != NULL) {
- /* the list is already sorted in document order by XPath */
- /* append everything in this order under ctxt->insert */
+ xmlNodePtr cur;
+ /*
+ * The list is already sorted in document order by XPath.
+ * Append everything in this order under ctxt->insert.
+ */
for (i = 0;i < list->nodeNr;i++) {
- if (list->nodeTab[i] == NULL)
+ cur = list->nodeTab[i];
+ if (cur == NULL)
continue;
- if ((list->nodeTab[i]->type == XML_DOCUMENT_NODE) ||
- (list->nodeTab[i]->type == XML_HTML_DOCUMENT_NODE)) {
- xsltCopyTreeList(ctxt, list->nodeTab[i]->children,
- ctxt->insert, 0);
- } else if (list->nodeTab[i]->type == XML_ATTRIBUTE_NODE) {
- xsltCopyProp(ctxt, ctxt->insert,
- (xmlAttrPtr) list->nodeTab[i]);
+ if ((cur->type == XML_DOCUMENT_NODE) ||
+ (cur->type == XML_HTML_DOCUMENT_NODE))
+ {
+ xsltCopyTreeList(ctxt, cur->children, ctxt->insert, 0);
+ } else if (cur->type == XML_ATTRIBUTE_NODE) {
+ xsltCopyProp(ctxt, ctxt->insert, (xmlAttrPtr) cur);
} else {
- xsltCopyTree(ctxt, list->nodeTab[i], ctxt->insert, 0);
+ xsltCopyTree(ctxt, cur, ctxt->insert, 0);
}
}
}
} else if (res->type == XPATH_XSLT_TREE) {
+ /*
+ * Result tree fragment
+ * --------------------
+ */
#ifdef WITH_XSLT_DEBUG_PROCESS
XSLT_TRACE(ctxt,XSLT_TRACE_COPY_OF,xsltGenericDebug(xsltGenericDebugContext,
"xsltCopyOf: result is a result tree fragment\n"));
#endif
+ /*
+ * TODO: Is list->nodeTab[0] is an xmlDocPtr?
+ */
list = res->nodesetval;
if ((list != NULL) && (list->nodeTab != NULL) &&
(list->nodeTab[0] != NULL) &&
- (IS_XSLT_REAL_NODE(list->nodeTab[0]))) {
+ (IS_XSLT_REAL_NODE(list->nodeTab[0])))
+ {
xsltCopyTreeList(ctxt, list->nodeTab[0]->children,
ctxt->insert, 0);
}
} else {
- /* convert to a string */
+ /* Convert to a string. */
res = xmlXPathConvertString(res);
if ((res != NULL) && (res->type == XPATH_STRING)) {
#ifdef WITH_XSLT_DEBUG_PROCESS
XSLT_TRACE(ctxt,XSLT_TRACE_COPY_OF,xsltGenericDebug(xsltGenericDebugContext,
"xsltCopyOf: result %s\n", res->stringval));
#endif
- /* append content as text node */
+ /* Append content as text node. */
xsltCopyTextString(ctxt, ctxt->insert, res->stringval, 0);
}
}
}
if (tmp == root) {
ctxt->type = XSLT_OUTPUT_HTML;
+ /*
+ * REVISIT TODO: XML_HTML_DOCUMENT_NODE is set after the
+ * transformation on the doc, but functions like
+ */
res->type = XML_HTML_DOCUMENT_NODE;
if (((doctypePublic != NULL) || (doctypeSystem != NULL))) {
res->intSubset = xmlCreateIntSubset(res, doctype,
/************************************************************************
* *
- * Result Value Tree interfaces *
+ * Result Value Tree (Result Tree Fragment) interfaces *
* *
************************************************************************/
/**
* xsltCreateRVT:
* @ctxt: an XSLT transformation context
*
- * Create a result value tree
+ * Create a Result Value Tree
+ * (the XSLT 1.0 term for this is "Result Tree Fragment")
*
* Returns the result value tree or NULL in case of error
*/
xmlDocPtr container;
/*
- * TODO: Couldn't we use an XML_DOCUMENT_FRAG_NODE for this?
- */
+ * Question: Why is this function public?
+ * Answer: It is called by the EXSLT module.
+ */
if (ctxt == NULL) return(NULL);
container = xmlNewDoc(NULL);
/**
* xsltRegisterTmpRVT:
* @ctxt: an XSLT transformation context
- * @RVT: a result value tree
+ * @RVT: a result value tree (Result Tree Fragment)
*
- * Register the result value tree for destruction at the end of the context
+ * Register the result value tree (XSLT 1.0 term: Result Tree Fragment)
+ * for destruction at the end of the context
*
* Returns 0 in case of success and -1 in case of error.
*/
/**
* xsltRegisterPersistRVT:
* @ctxt: an XSLT transformation context
- * @RVT: a result value tree
+ * @RVT: a result value tree (Result Tree Fragment)
*
- * Register the result value tree for destruction at the end of the processing
+ * Register the result value tree (XSLT 1.0 term: Result Tree Fragment)
+ * for destruction at the end of the processing
*
* Returns 0 in case of success and -1 in case of error.
*/
* xsltFreeRVTs:
* @ctxt: an XSLT transformation context
*
- * Free all the registered result value tree of the transformation
+ * Free all the registered result value tree (Result Tree Fragment)
+ * of the transformation
*/
void
xsltFreeRVTs(xsltTransformContextPtr ctxt)
#else
precomp = elem->comp;
#endif
+
+ /*
+ * OPTIMIZE TODO: We should consider if instantiating global vars/params
+ * on a on-demand basis would be better. The vars/params don't
+ * need to be evaluated if never called; and in the case of
+ * global params, if values for such params are provided by the
+ * user.
+ */
if (elem->select != NULL) {
xmlXPathCompExprPtr comp = NULL;
if (doc == NULL)
return (NULL);
- ns = xsltTreeEnsureXMLDecl(doc);
+ if (doc->oldNs != NULL)
+ ns = doc->oldNs;
+ else
+ ns = xsltTreeEnsureXMLDecl(doc);
if (ns == NULL)
return (NULL);
if (ns->next != NULL) {