/** * xsltGetNamespace: * @ctxt: a transformation context * @cur: the input node * @ns: the namespace * @out: the output node (or its parent) * * Find a matching (prefix and ns-name) ns-declaration * for the requested @ns->prefix and @ns->href in the result tree. * If none is found then a new ns-declaration will be * added to @resultElem. If, in this case, the given prefix is * already in use, then a ns-declaration with a modified ns-prefix * be we created. * * Called by: * - xsltCopyPropList() (*not* anymore) * - xsltShallowCopyElement() * - xsltCopyTreeInternal() (*not* anymore) * - xsltApplySequenceConstructor() (*not* in the refactored code), * - xsltElement() (*not* anymore) * * Returns a namespace declaration or NULL in case of * namespace fixup failures or API or internal errors. */ xmlNsPtr xsltGetNamespace(xsltTransformContextPtr ctxt, xmlNodePtr cur, xmlNsPtr ns, xmlNodePtr out) { if (ns == NULL) return(NULL); #ifdef XSLT_REFACTORED /* * Namespace exclusion and ns-aliasing is performed at * compilation-time in the refactored code. * Additionally, aliasing is not intended for non Literal * Result Elements. */ return(xsltGetSpecialNamespace(ctxt, cur, ns->href, ns->prefix, out)); #else { xsltStylesheetPtr style; const xmlChar *URI = NULL; /* the replacement URI */ if ((ctxt == NULL) || (cur == NULL) || (out == NULL)) return(NULL); style = ctxt->style; while (style != NULL) { if (style->nsAliases != NULL) URI = (const xmlChar *) xmlHashLookup(style->nsAliases, ns->href); if (URI != NULL) break; style = xsltNextImport(style); } if (URI == UNDEFINED_DEFAULT_NS) { return(xsltGetSpecialNamespace(ctxt, cur, NULL, NULL, out)); #if 0 /* * TODO: Removed, since wrong. If there was no default * namespace in the stylesheet then this must resolve to * the NULL namespace. */ xmlNsPtr dflt; dflt = xmlSearchNs(cur->doc, cur, NULL); if (dflt != NULL) URI = dflt->href; else return NULL; #endif } else if (URI == NULL) URI = ns->href; return(xsltGetSpecialNamespace(ctxt, cur, URI, ns->prefix, out)); } #endif }
/** * xsltAttrListTemplateProcess: * @ctxt: the XSLT transformation context * @target: the element where the attributes will be grafted * @attrs: the first attribute * * Processes all attributes of a Literal Result Element. * Attribute references are applied via xsl:use-attribute-set * attributes. * Copies all non XSLT-attributes over to the @target element * and evaluates Attribute Value Templates. * * Called by xsltApplySequenceConstructor() (transform.c). * * Returns a new list of attribute nodes, or NULL in case of error. * (Don't assign the result to @target->properties; if * the result is NULL, you'll get memory leaks, since the * attributes will be disattached.) */ xmlAttrPtr xsltAttrListTemplateProcess(xsltTransformContextPtr ctxt, xmlNodePtr target, xmlAttrPtr attrs) { xmlAttrPtr attr, copy, last; xmlNodePtr oldInsert, text; xmlNsPtr origNs = NULL, copyNs = NULL; const xmlChar *value; xmlChar *valueAVT; if ((ctxt == NULL) || (target == NULL) || (attrs == NULL)) return(NULL); oldInsert = ctxt->insert; ctxt->insert = target; /* * Instantiate LRE-attributes. */ if (target->properties) { last = target->properties; while (last->next != NULL) last = last->next; } else { last = NULL; } attr = attrs; do { /* * Skip XSLT attributes. */ #ifdef XSLT_REFACTORED if (attr->psvi == xsltXSLTAttrMarker) { goto next_attribute; } #else if ((attr->ns != NULL) && xmlStrEqual(attr->ns->href, XSLT_NAMESPACE)) { goto next_attribute; } #endif /* * Get the value. */ if (attr->children != NULL) { if ((attr->children->type != XML_TEXT_NODE) || (attr->children->next != NULL)) { xsltTransformError(ctxt, NULL, attr->parent, "Internal error: The children of an attribute node of a " "literal result element are not in the expected form.\n"); goto error; } value = attr->children->content; if (value == NULL) value = xmlDictLookup(ctxt->dict, BAD_CAST "", 0); } else value = xmlDictLookup(ctxt->dict, BAD_CAST "", 0); /* * Create a new attribute. */ copy = xmlNewDocProp(target->doc, attr->name, NULL); if (copy == NULL) { if (attr->ns) { xsltTransformError(ctxt, NULL, attr->parent, "Internal error: Failed to create attribute '{%s}%s'.\n", attr->ns->href, attr->name); } else { xsltTransformError(ctxt, NULL, attr->parent, "Internal error: Failed to create attribute '%s'.\n", attr->name); } goto error; } /* * Attach it to the target element. */ copy->parent = target; if (last == NULL) { target->properties = copy; last = copy; } else { last->next = copy; copy->prev = last; last = copy; } /* * Set the namespace. Avoid lookups of same namespaces. */ if (attr->ns != origNs) { origNs = attr->ns; if (attr->ns != NULL) { #ifdef XSLT_REFACTORED copyNs = xsltGetSpecialNamespace(ctxt, attr->parent, attr->ns->href, attr->ns->prefix, target); #else copyNs = xsltGetNamespace(ctxt, attr->parent, attr->ns, target); #endif if (copyNs == NULL) goto error; } else copyNs = NULL; } copy->ns = copyNs; /* * Set the value. */ text = xmlNewText(NULL); if (text != NULL) { copy->last = copy->children = text; text->parent = (xmlNodePtr) copy; text->doc = copy->doc; if (attr->psvi != NULL) { /* * Evaluate the Attribute Value Template. */ valueAVT = xsltEvalAVT(ctxt, attr->psvi, attr->parent); if (valueAVT == NULL) { /* * TODO: Damn, we need an easy mechanism to report * qualified names! */ if (attr->ns) { xsltTransformError(ctxt, NULL, attr->parent, "Internal error: Failed to evaluate the AVT " "of attribute '{%s}%s'.\n", attr->ns->href, attr->name); } else { xsltTransformError(ctxt, NULL, attr->parent, "Internal error: Failed to evaluate the AVT " "of attribute '%s'.\n", attr->name); } text->content = xmlStrdup(BAD_CAST ""); goto error; } else { text->content = valueAVT; } } else if ((ctxt->internalized) && (target->doc != NULL) && (target->doc->dict == ctxt->dict) && xmlDictOwns(ctxt->dict, value)) { text->content = (xmlChar *) value; } else { text->content = xmlStrdup(value); } if ((copy != NULL) && (text != NULL) && (xmlIsID(copy->doc, copy->parent, copy))) xmlAddID(NULL, copy->doc, text->content, copy); } next_attribute: attr = attr->next; } while (attr != NULL); /* * Apply attribute-sets. * The creation of such attributes will not overwrite any existing * attribute. */ attr = attrs; do { #ifdef XSLT_REFACTORED if ((attr->psvi == xsltXSLTAttrMarker) && xmlStrEqual(attr->name, (const xmlChar *)"use-attribute-sets")) { xsltApplyAttributeSet(ctxt, ctxt->node, (xmlNodePtr) attr, NULL); } #else if ((attr->ns != NULL) && xmlStrEqual(attr->name, (const xmlChar *)"use-attribute-sets") && xmlStrEqual(attr->ns->href, XSLT_NAMESPACE)) { xsltApplyAttributeSet(ctxt, ctxt->node, (xmlNodePtr) attr, NULL); } #endif attr = attr->next; } while (attr != NULL); ctxt->insert = oldInsert; return(target->properties); error: ctxt->insert = oldInsert; return(NULL); }
/** * xsltAttribute: * @ctxt: a XSLT process context * @node: the current node in the source tree * @inst: the xsl:attribute element * @comp: precomputed information * * Process the xslt attribute node on the source node */ void xsltAttribute(xsltTransformContextPtr ctxt, xmlNodePtr contextNode, xmlNodePtr inst, xsltStylePreCompPtr castedComp) { #ifdef XSLT_REFACTORED xsltStyleItemAttributePtr comp = (xsltStyleItemAttributePtr) castedComp; #else xsltStylePreCompPtr comp = castedComp; #endif xmlNodePtr targetElem; xmlChar *prop = NULL; const xmlChar *name = NULL, *prefix = NULL, *nsName = NULL; xmlChar *value = NULL; xmlNsPtr ns = NULL; xmlAttrPtr attr; if ((ctxt == NULL) || (contextNode == NULL) || (inst == NULL) || (inst->type != XML_ELEMENT_NODE) ) return; /* * A comp->has_name == 0 indicates that we need to skip this instruction, * since it was evaluated to be invalid already during compilation. */ if (!comp->has_name) return; /* * BIG NOTE: This previously used xsltGetSpecialNamespace() and * 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) { xsltTransformError(ctxt, NULL, inst, "Internal error in xsltAttribute(): " "The XSLT 'attribute' instruction was not 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; /* * 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: Cannot add attributes to an " "element if children have been already added " "to the element.\n"); return; } /* * Process the name * ---------------- */ #ifdef WITH_DEBUGGER if (ctxt->debugStatus != XSLT_DEBUG_NONE) xslHandleDebugger(inst, contextNode, NULL, ctxt); #endif if (comp->name == NULL) { /* 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: The attribute 'name' is missing.\n"); goto error; } if (xmlValidateQName(prop, 0)) { xsltTransformError(ctxt, NULL, inst, "xsl:attribute: The effective name '%s' is not a " "valid QName.\n", prop); /* we fall through to catch any further errors, if possible */ } /* * Reject a name of "xmlns". */ if (xmlStrEqual(prop, BAD_CAST "xmlns")) { xsltTransformError(ctxt, NULL, inst, "xsl:attribute: The effective name 'xmlns' is not allowed.\n"); xmlFree(prop); goto error; } name = xsltSplitQName(ctxt->dict, prop, &prefix); xmlFree(prop); } else { /* * The "name" value was static. */ #ifdef XSLT_REFACTORED prefix = comp->nsPrefix; name = comp->name; #else name = xsltSplitQName(ctxt->dict, comp->name, &prefix); #endif } /* * Process namespace semantics * --------------------------- * * Evaluate the namespace name. */ if (comp->has_ns) { /* * The "namespace" attribute was existent. */ if (comp->ns != NULL) { /* * No AVT; just plain text for the namespace name. */ if (comp->ns[0] != 0) nsName = comp->ns; } else { 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); } if (xmlStrEqual(nsName, BAD_CAST "http://www.w3.org/2000/xmlns/")) { xsltTransformError(ctxt, NULL, inst, "xsl:attribute: Namespace http://www.w3.org/2000/xmlns/ " "forbidden.\n"); goto error; } if (xmlStrEqual(nsName, XML_XML_NAMESPACE)) { prefix = BAD_CAST "xml"; } else if (xmlStrEqual(prefix, BAD_CAST "xml")) { prefix = NULL; } } 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 QName '%s:%s' has no " "namespace binding in scope in the stylesheet; " "this is an error, since the namespace was not " "specified by the instruction itself.\n", prefix, name); } else nsName = ns->href; } /* * Find/create a matching ns-decl in the result tree. */ ns = NULL; #if 0 if (0) { /* * OPTIMIZE TODO: How do we know if we are adding to a * fragment or to the result tree? * * If we are adding to a result tree fragment (i.e., not to the * actual result tree), we'll don't bother searching for the * ns-decl, but just store it in the dummy-doc of the result * tree fragment. */ if (nsName != NULL) { /* * TODO: Get the doc of @targetElem. */ ns = xsltTreeAcquireStoredNs(some doc, nsName, prefix); } } #endif if (nsName != NULL) { /* * 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" */ /* * xsl:attribute can produce a scenario where the prefix is NULL, * so generate a prefix. */ if ((prefix == NULL) || xmlStrEqual(prefix, BAD_CAST "xmlns")) { xmlChar *pref = xmlStrdup(BAD_CAST "ns_1"); ns = xsltGetSpecialNamespace(ctxt, inst, nsName, pref, targetElem); xmlFree(pref); } else { ns = xsltGetSpecialNamespace(ctxt, inst, nsName, prefix, targetElem); } if (ns == NULL) { xsltTransformError(ctxt, NULL, inst, "Namespace fixup error: Failed to acquire an in-scope " "namespace binding for the generated attribute '{%s}%s'.\n", nsName, name); goto error; } } /* * 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) && ((inst->children->type == XML_TEXT_NODE) || (inst->children->type == XML_CDATA_SECTION_NODE))) { xmlNodePtr copyTxt; /* * xmlSetNsProp() will take care of duplicates. */ attr = xmlSetNsProp(ctxt->insert, ns, name, NULL); if (attr == NULL) /* TODO: report error ? */ goto error; /* * This was taken over from xsltCopyText() (transform.c). */ if (ctxt->internalized && (ctxt->insert->doc != NULL) && (ctxt->insert->doc->dict == ctxt->dict)) { copyTxt = xmlNewText(NULL); if (copyTxt == NULL) /* TODO: report error */ goto error; /* * This is a safe scenario where we don't need to lookup * the dict. */ copyTxt->content = inst->children->content; /* * Copy "disable-output-escaping" information. * TODO: Does this have any effect for attribute values * anyway? */ if (inst->children->name == xmlStringTextNoenc) copyTxt->name = xmlStringTextNoenc; } else { /* * Copy the value. */ copyTxt = xmlNewText(inst->children->content); if (copyTxt == NULL) /* TODO: report error */ goto error; } attr->children = attr->last = copyTxt; copyTxt->parent = (xmlNodePtr) attr; copyTxt->doc = attr->doc; /* * Copy "disable-output-escaping" information. * TODO: Does this have any effect for attribute values * anyway? */ if (inst->children->name == xmlStringTextNoenc) copyTxt->name = xmlStringTextNoenc; /* * since we create the attribute without content IDness must be * asserted as a second step */ if ((copyTxt->content != NULL) && (xmlIsID(attr->doc, attr->parent, attr))) xmlAddID(NULL, attr->doc, copyTxt->content, attr); } else { /* * The sequence constructor might be complex, so instantiate it. */ value = xsltEvalTemplateString(ctxt, contextNode, inst); if (value != NULL) { attr = xmlSetNsProp(ctxt->insert, ns, name, value); xmlFree(value); } else { /* * TODO: Do we have to add the empty string to the attr? * TODO: Does a value of NULL indicate an * error in xsltEvalTemplateString() ? */ attr = xmlSetNsProp(ctxt->insert, ns, name, (const xmlChar *) ""); } } error: return; }