blob: a4ca41df380ee2686b852b14713bd82958728c64 [file] [log] [blame]
/*
* transform.c: Implementation of the XSL Transformation 1.0 engine
* transform part, i.e. applying a Stylesheet to a document
*
* References:
* http://www.w3.org/TR/1999/REC-xslt-19991116
*
* Michael Kay "XSLT Programmer's Reference" pp 637-643
* Writing Multiple Output Files
*
* XSLT-1.1 Working Draft
* http://www.w3.org/TR/xslt11#multiple-output
*
* See Copyright for the status of this software.
*
* daniel@veillard.com
*/
#define IN_LIBXSLT
#include "libxslt.h"
#include <string.h>
#include <libxml/xmlmemory.h>
#include <libxml/parser.h>
#include <libxml/tree.h>
#include <libxml/valid.h>
#include <libxml/hash.h>
#include <libxml/encoding.h>
#include <libxml/xmlerror.h>
#include <libxml/xpath.h>
#include <libxml/parserInternals.h>
#include <libxml/xpathInternals.h>
#include <libxml/HTMLtree.h>
#include <libxml/debugXML.h>
#include <libxml/uri.h>
#include "xslt.h"
#include "xsltInternals.h"
#include "xsltutils.h"
#include "pattern.h"
#include "transform.h"
#include "variables.h"
#include "numbersInternals.h"
#include "namespaces.h"
#include "attributes.h"
#include "templates.h"
#include "imports.h"
#include "keys.h"
#include "documents.h"
#include "extensions.h"
#include "extra.h"
#include "preproc.h"
#include "security.h"
#ifdef WITH_XSLT_DEBUG
#define WITH_XSLT_DEBUG_EXTRA
#define WITH_XSLT_DEBUG_PROCESS
#endif
#define XSLT_GENERATE_HTML_DOCTYPE
#ifdef XSLT_GENERATE_HTML_DOCTYPE
static int xsltGetHTMLIDs(const xmlChar *version, const xmlChar **publicID,
const xmlChar **systemID);
#endif
int xsltMaxDepth = 3000;
/*
* Useful macros
*/
#ifndef FALSE
# define FALSE (0 == 1)
# define TRUE (!FALSE)
#endif
#define IS_BLANK_NODE(n) \
(((n)->type == XML_TEXT_NODE) && (xsltIsBlank((n)->content)))
/*
* Forward declarations
*/
static xmlNsPtr
xsltCopyNamespaceListInternal(xmlNodePtr node, xmlNsPtr cur);
static xmlNodePtr
xsltCopyTreeInternal(xsltTransformContextPtr ctxt,
xmlNodePtr invocNode,
xmlNodePtr node,
xmlNodePtr insert, int isLRE, int topElemVisited);
static void
xsltApplySequenceConstructor(xsltTransformContextPtr ctxt,
xmlNodePtr contextNode, xmlNodePtr list,
xsltTemplatePtr templ);
static void
xsltApplyXSLTTemplate(xsltTransformContextPtr ctxt,
xmlNodePtr contextNode,
xmlNodePtr list,
xsltTemplatePtr templ,
xsltStackElemPtr withParams);
/**
* templPush:
* @ctxt: the transformation context
* @value: the template to push on the stack
*
* Push a template on the stack
*
* Returns the new index in the stack or 0 in case of error
*/
static int
templPush(xsltTransformContextPtr ctxt, xsltTemplatePtr value)
{
if (ctxt->templMax == 0) {
ctxt->templMax = 4;
ctxt->templTab =
(xsltTemplatePtr *) xmlMalloc(ctxt->templMax *
sizeof(ctxt->templTab[0]));
if (ctxt->templTab == NULL) {
xmlGenericError(xmlGenericErrorContext, "malloc failed !\n");
return (0);
}
}
if (ctxt->templNr >= ctxt->templMax) {
ctxt->templMax *= 2;
ctxt->templTab =
(xsltTemplatePtr *) xmlRealloc(ctxt->templTab,
ctxt->templMax *
sizeof(ctxt->templTab[0]));
if (ctxt->templTab == NULL) {
xmlGenericError(xmlGenericErrorContext, "realloc failed !\n");
return (0);
}
}
ctxt->templTab[ctxt->templNr] = value;
ctxt->templ = value;
return (ctxt->templNr++);
}
/**
* templPop:
* @ctxt: the transformation context
*
* Pop a template value from the stack
*
* Returns the stored template value
*/
static xsltTemplatePtr
templPop(xsltTransformContextPtr ctxt)
{
xsltTemplatePtr ret;
if (ctxt->templNr <= 0)
return (0);
ctxt->templNr--;
if (ctxt->templNr > 0)
ctxt->templ = ctxt->templTab[ctxt->templNr - 1];
else
ctxt->templ = (xsltTemplatePtr) 0;
ret = ctxt->templTab[ctxt->templNr];
ctxt->templTab[ctxt->templNr] = 0;
return (ret);
}
/**
* xsltLocalVariablePop:
* @ctxt: the transformation context
* @limitNr: number of variables which should remain
* @level: the depth in the xsl:template's tree
*
* Pops all variable values at the given @depth from the stack.
*
* Returns the stored variable value
* **NOTE:**
* This is an internal routine and should not be called by users!
*/
void
xsltLocalVariablePop(xsltTransformContextPtr ctxt, int limitNr, int level)
{
xsltStackElemPtr variable;
if (ctxt->varsNr <= 0)
return;
do {
if (ctxt->varsNr <= limitNr)
break;
variable = ctxt->varsTab[ctxt->varsNr - 1];
if (variable->level <= level)
break;
if (variable->level >= 0)
xsltFreeStackElemList(variable);
ctxt->varsNr--;
} while (ctxt->varsNr != 0);
if (ctxt->varsNr > 0)
ctxt->vars = ctxt->varsTab[ctxt->varsNr - 1];
else
ctxt->vars = NULL;
}
/**
* xsltTemplateParamsCleanup:
*
* Removes xsl:param and xsl:with-param items from the
* variable-stack. Only xsl:with-param items are not freed.
*/
static void
xsltTemplateParamsCleanup(xsltTransformContextPtr ctxt)
{
xsltStackElemPtr param;
for (; ctxt->varsNr > ctxt->varsBase; ctxt->varsNr--) {
param = ctxt->varsTab[ctxt->varsNr -1];
/*
* Free xsl:param items.
* xsl:with-param items will have a level of -1 or -2.
*/
if (param->level >= 0) {
xsltFreeStackElemList(param);
}
}
if (ctxt->varsNr > 0)
ctxt->vars = ctxt->varsTab[ctxt->varsNr - 1];
else
ctxt->vars = NULL;
}
/**
* profPush:
* @ctxt: the transformation context
* @value: the profiling value to push on the stack
*
* Push a profiling value on the stack
*
* Returns the new index in the stack or 0 in case of error
*/
static int
profPush(xsltTransformContextPtr ctxt, long value)
{
if (ctxt->profMax == 0) {
ctxt->profMax = 4;
ctxt->profTab =
(long *) xmlMalloc(ctxt->profMax * sizeof(ctxt->profTab[0]));
if (ctxt->profTab == NULL) {
xmlGenericError(xmlGenericErrorContext, "malloc failed !\n");
return (0);
}
}
if (ctxt->profNr >= ctxt->profMax) {
ctxt->profMax *= 2;
ctxt->profTab =
(long *) xmlRealloc(ctxt->profTab,
ctxt->profMax * sizeof(ctxt->profTab[0]));
if (ctxt->profTab == NULL) {
xmlGenericError(xmlGenericErrorContext, "realloc failed !\n");
return (0);
}
}
ctxt->profTab[ctxt->profNr] = value;
ctxt->prof = value;
return (ctxt->profNr++);
}
/**
* profPop:
* @ctxt: the transformation context
*
* Pop a profiling value from the stack
*
* Returns the stored profiling value
*/
static long
profPop(xsltTransformContextPtr ctxt)
{
long ret;
if (ctxt->profNr <= 0)
return (0);
ctxt->profNr--;
if (ctxt->profNr > 0)
ctxt->prof = ctxt->profTab[ctxt->profNr - 1];
else
ctxt->prof = (long) 0;
ret = ctxt->profTab[ctxt->profNr];
ctxt->profTab[ctxt->profNr] = 0;
return (ret);
}
/************************************************************************
* *
* XInclude default settings *
* *
************************************************************************/
static int xsltDoXIncludeDefault = 0;
/**
* xsltSetXIncludeDefault:
* @xinclude: whether to do XInclude processing
*
* Set whether XInclude should be processed on document being loaded by default
*/
void
xsltSetXIncludeDefault(int xinclude) {
xsltDoXIncludeDefault = (xinclude != 0);
}
/**
* xsltGetXIncludeDefault:
*
* Provides the default state for XInclude processing
*
* Returns 0 if there is no processing 1 otherwise
*/
int
xsltGetXIncludeDefault(void) {
return(xsltDoXIncludeDefault);
}
unsigned long xsltDefaultTrace = (unsigned long) XSLT_TRACE_ALL;
/**
* xsltDebugSetDefaultTrace:
* @val: tracing level mask
*
* Set the default debug tracing level mask
*/
void xsltDebugSetDefaultTrace(xsltDebugTraceCodes val) {
xsltDefaultTrace = val;
}
/**
* xsltDebugGetDefaultTrace:
*
* Get the current default debug tracing level mask
*
* Returns the current default debug tracing level mask
*/
xsltDebugTraceCodes xsltDebugGetDefaultTrace() {
return xsltDefaultTrace;
}
/************************************************************************
* *
* Handling of Transformation Contexts *
* *
************************************************************************/
static xsltTransformCachePtr
xsltTransformCacheCreate(void)
{
xsltTransformCachePtr ret;
ret = (xsltTransformCachePtr) xmlMalloc(sizeof(xsltTransformCache));
if (ret == NULL) {
xsltTransformError(NULL, NULL, NULL,
"xsltTransformCacheCreate : malloc failed\n");
return(NULL);
}
memset(ret, 0, sizeof(xsltTransformCache));
return(ret);
}
static void
xsltTransformCacheFree(xsltTransformCachePtr cache)
{
if (cache == NULL)
return;
/*
* Free tree fragments.
*/
if (cache->RVT) {
xmlDocPtr tmp, cur = cache->RVT;
while (cur) {
tmp = cur;
cur = (xmlDocPtr) cur->next;
if (tmp->_private != NULL) {
/*
* Tree the document info.
*/
xsltFreeDocumentKeys((xsltDocumentPtr) tmp->_private);
xmlFree(tmp->_private);
}
xmlFreeDoc(tmp);
}
}
/*
* Free vars/params.
*/
if (cache->stackItems) {
xsltStackElemPtr tmp, cur = cache->stackItems;
while (cur) {
tmp = cur;
cur = cur->next;
/*
* REVISIT TODO: Should be call a destruction-function
* instead?
*/
xmlFree(tmp);
}
}
xmlFree(cache);
}
/**
* xsltNewTransformContext:
* @style: a parsed XSLT stylesheet
* @doc: the input document
*
* Create a new XSLT TransformContext
*
* Returns the newly allocated xsltTransformContextPtr or NULL in case of error
*/
xsltTransformContextPtr
xsltNewTransformContext(xsltStylesheetPtr style, xmlDocPtr doc) {
xsltTransformContextPtr cur;
xsltDocumentPtr docu;
int i;
xsltInitGlobals();
cur = (xsltTransformContextPtr) xmlMalloc(sizeof(xsltTransformContext));
if (cur == NULL) {
xsltTransformError(NULL, NULL, (xmlNodePtr)doc,
"xsltNewTransformContext : malloc failed\n");
return(NULL);
}
memset(cur, 0, sizeof(xsltTransformContext));
cur->cache = xsltTransformCacheCreate();
if (cur->cache == NULL)
goto internal_err;
/*
* setup of the dictionary must be done early as some of the
* processing later like key handling may need it.
*/
cur->dict = xmlDictCreateSub(style->dict);
cur->internalized = ((style->internalized) && (cur->dict != NULL));
#ifdef WITH_XSLT_DEBUG
xsltGenericDebug(xsltGenericDebugContext,
"Creating sub-dictionary from stylesheet for transformation\n");
#endif
/*
* initialize the template stack
*/
cur->templTab = (xsltTemplatePtr *)
xmlMalloc(10 * sizeof(xsltTemplatePtr));
if (cur->templTab == NULL) {
xsltTransformError(NULL, NULL, (xmlNodePtr) doc,
"xsltNewTransformContext: out of memory\n");
goto internal_err;
}
cur->templNr = 0;
cur->templMax = 5;
cur->templ = NULL;
/*
* initialize the variables stack
*/
cur->varsTab = (xsltStackElemPtr *)
xmlMalloc(10 * sizeof(xsltStackElemPtr));
if (cur->varsTab == NULL) {
xmlGenericError(xmlGenericErrorContext,
"xsltNewTransformContext: out of memory\n");
goto internal_err;
}
cur->varsNr = 0;
cur->varsMax = 10;
cur->vars = NULL;
cur->varsBase = 0;
/*
* the profiling stack is not initialized by default
*/
cur->profTab = NULL;
cur->profNr = 0;
cur->profMax = 0;
cur->prof = 0;
cur->style = style;
xmlXPathInit();
cur->xpathCtxt = xmlXPathNewContext(doc);
if (cur->xpathCtxt == NULL) {
xsltTransformError(NULL, NULL, (xmlNodePtr) doc,
"xsltNewTransformContext : xmlXPathNewContext failed\n");
goto internal_err;
}
/*
* Create an XPath cache.
*/
if (xmlXPathContextSetCache(cur->xpathCtxt, 1, -1, 0) == -1)
goto internal_err;
/*
* Initialize the extras array
*/
if (style->extrasNr != 0) {
cur->extrasMax = style->extrasNr + 20;
cur->extras = (xsltRuntimeExtraPtr)
xmlMalloc(cur->extrasMax * sizeof(xsltRuntimeExtra));
if (cur->extras == NULL) {
xmlGenericError(xmlGenericErrorContext,
"xsltNewTransformContext: out of memory\n");
goto internal_err;
}
cur->extrasNr = style->extrasNr;
for (i = 0;i < cur->extrasMax;i++) {
cur->extras[i].info = NULL;
cur->extras[i].deallocate = NULL;
cur->extras[i].val.ptr = NULL;
}
} else {
cur->extras = NULL;
cur->extrasNr = 0;
cur->extrasMax = 0;
}
XSLT_REGISTER_VARIABLE_LOOKUP(cur);
XSLT_REGISTER_FUNCTION_LOOKUP(cur);
cur->xpathCtxt->nsHash = style->nsHash;
/*
* Initialize the registered external modules
*/
xsltInitCtxtExts(cur);
/*
* Setup document element ordering for later efficiencies
* (bug 133289)
*/
if (xslDebugStatus == XSLT_DEBUG_NONE)
xmlXPathOrderDocElems(doc);
/*
* Must set parserOptions before calling xsltNewDocument
* (bug 164530)
*/
cur->parserOptions = XSLT_PARSE_OPTIONS;
docu = xsltNewDocument(cur, doc);
if (docu == NULL) {
xsltTransformError(cur, NULL, (xmlNodePtr)doc,
"xsltNewTransformContext : xsltNewDocument failed\n");
goto internal_err;
}
docu->main = 1;
cur->document = docu;
cur->inst = NULL;
cur->outputFile = NULL;
cur->sec = xsltGetDefaultSecurityPrefs();
cur->debugStatus = xslDebugStatus;
cur->traceCode = (unsigned long*) &xsltDefaultTrace;
cur->xinclude = xsltGetXIncludeDefault();
cur->keyInitLevel = 0;
return(cur);
internal_err:
if (cur != NULL)
xsltFreeTransformContext(cur);
return(NULL);
}
/**
* xsltFreeTransformContext:
* @ctxt: an XSLT parser context
*
* Free up the memory allocated by @ctxt
*/
void
xsltFreeTransformContext(xsltTransformContextPtr ctxt) {
if (ctxt == NULL)
return;
/*
* Shutdown the extension modules associated to the stylesheet
* used if needed.
*/
xsltShutdownCtxtExts(ctxt);
if (ctxt->xpathCtxt != NULL) {
ctxt->xpathCtxt->nsHash = NULL;
xmlXPathFreeContext(ctxt->xpathCtxt);
}
if (ctxt->templTab != NULL)
xmlFree(ctxt->templTab);
if (ctxt->varsTab != NULL)
xmlFree(ctxt->varsTab);
if (ctxt->profTab != NULL)
xmlFree(ctxt->profTab);
if ((ctxt->extrasNr > 0) && (ctxt->extras != NULL)) {
int i;
for (i = 0;i < ctxt->extrasNr;i++) {
if ((ctxt->extras[i].deallocate != NULL) &&
(ctxt->extras[i].info != NULL))
ctxt->extras[i].deallocate(ctxt->extras[i].info);
}
xmlFree(ctxt->extras);
}
xsltFreeGlobalVariables(ctxt);
xsltFreeDocuments(ctxt);
xsltFreeCtxtExts(ctxt);
xsltFreeRVTs(ctxt);
xsltTransformCacheFree(ctxt->cache);
xmlDictFree(ctxt->dict);
#ifdef WITH_XSLT_DEBUG
xsltGenericDebug(xsltGenericDebugContext,
"freeing transformation dictionary\n");
#endif
memset(ctxt, -1, sizeof(xsltTransformContext));
xmlFree(ctxt);
}
/************************************************************************
* *
* Copy of Nodes in an XSLT fashion *
* *
************************************************************************/
xmlNodePtr xsltCopyTree(xsltTransformContextPtr ctxt,
xmlNodePtr node, xmlNodePtr insert, int literal);
/**
* xsltAddChild:
* @parent: the parent node
* @cur: the child node
*
* Wrapper version of xmlAddChild with a more consistent behaviour on
* error. One expect the use to be child = xsltAddChild(parent, child);
* and the routine will take care of not leaking on errors or node merge
*
* Returns the child is successfully attached or NULL if merged or freed
*/
static xmlNodePtr
xsltAddChild(xmlNodePtr parent, xmlNodePtr cur) {
xmlNodePtr ret;
if ((cur == NULL) || (parent == NULL))
return(NULL);
if (parent == NULL) {
xmlFreeNode(cur);
return(NULL);
}
ret = xmlAddChild(parent, cur);
return(ret);
}
/**
* xsltAddTextString:
* @ctxt: a XSLT process context
* @target: the text node where the text will be attached
* @string: the text string
* @len: the string length in byte
*
* Extend the current text node with the new string, it handles coalescing
*
* Returns: the text node
*/
static xmlNodePtr
xsltAddTextString(xsltTransformContextPtr ctxt, xmlNodePtr target,
const xmlChar *string, int len) {
/*
* optimization
*/
if ((len <= 0) || (string == NULL) || (target == NULL))
return(target);
if (ctxt->lasttext == target->content) {
if (ctxt->lasttuse + len >= ctxt->lasttsize) {
xmlChar *newbuf;
int size;
size = ctxt->lasttsize + len + 100;
size *= 2;
newbuf = (xmlChar *) xmlRealloc(target->content,size);
if (newbuf == NULL) {
xsltTransformError(ctxt, NULL, target,
"xsltCopyText: text allocation failed\n");
return(NULL);
}
ctxt->lasttsize = size;
ctxt->lasttext = newbuf;
target->content = newbuf;
}
memcpy(&(target->content[ctxt->lasttuse]), string, len);
ctxt->lasttuse += len;
target->content[ctxt->lasttuse] = 0;
} else {
xmlNodeAddContent(target, string);
ctxt->lasttext = target->content;
len = xmlStrlen(target->content);
ctxt->lasttsize = len;
ctxt->lasttuse = len;
}
return(target);
}
/**
* xsltCopyTextString:
* @ctxt: a XSLT process context
* @target: the element where the text will be attached
* @string: the text string
* @noescape: should disable-escaping be activated for this text node.
*
* Adds @string to a newly created or an existent text node child of
* @target.
*
* Returns: the text node, where the text content of @cur is copied to.
* NULL in case of API or internal errors.
*/
xmlNodePtr
xsltCopyTextString(xsltTransformContextPtr ctxt, xmlNodePtr target,
const xmlChar *string, int noescape)
{
xmlNodePtr copy;
int len;
if (string == NULL)
return(NULL);
#ifdef WITH_XSLT_DEBUG_PROCESS
XSLT_TRACE(ctxt,XSLT_TRACE_COPY_TEXT,xsltGenericDebug(xsltGenericDebugContext,
"xsltCopyTextString: copy text %s\n",
string));
#endif
/*
* Play save and reset the merging mechanism for every new
* target node.
*/
if ((target == NULL) || (target->children == NULL)) {
ctxt->lasttext = NULL;
}
/* handle coalescing of text nodes here */
len = xmlStrlen(string);
if ((ctxt->type == XSLT_OUTPUT_XML) &&
(ctxt->style->cdataSection != NULL) &&
(target != NULL) &&
(target->type == XML_ELEMENT_NODE) &&
(((target->ns == NULL) &&
(xmlHashLookup2(ctxt->style->cdataSection,
target->name, NULL) != NULL)) ||
((target->ns != NULL) &&
(xmlHashLookup2(ctxt->style->cdataSection,
target->name, target->ns->href) != NULL))))
{
/*
* Process "cdata-section-elements".
*/
if ((target->last != NULL) &&
(target->last->type == XML_CDATA_SECTION_NODE))
{
return(xsltAddTextString(ctxt, target->last, string, len));
}
copy = xmlNewCDataBlock(ctxt->output, string, len);
} else if (noescape) {
/*
* Process "disable-output-escaping".
*/
if ((target != NULL) && (target->last != NULL) &&
(target->last->type == XML_TEXT_NODE) &&
(target->last->name == xmlStringTextNoenc))
{
return(xsltAddTextString(ctxt, target->last, string, len));
}
copy = xmlNewTextLen(string, len);
if (copy != NULL)
copy->name = xmlStringTextNoenc;
} else {
/*
* Default processing.
*/
if ((target != NULL) && (target->last != NULL) &&
(target->last->type == XML_TEXT_NODE) &&
(target->last->name == xmlStringText)) {
return(xsltAddTextString(ctxt, target->last, string, len));
}
copy = xmlNewTextLen(string, len);
}
if (copy != NULL) {
if (target != NULL)
copy = xsltAddChild(target, copy);
ctxt->lasttext = copy->content;
ctxt->lasttsize = len;
ctxt->lasttuse = len;
} else {
xsltTransformError(ctxt, NULL, target,
"xsltCopyTextString: text copy failed\n");
ctxt->lasttext = NULL;
}
return(copy);
}
/**
* xsltCopyText:
* @ctxt: a XSLT process context
* @target: the element where the text will be attached
* @cur: the text or CDATA node
* @interned: the string is in the target doc dictionary
*
* Copy the text content of @cur and append it to @target's children.
*
* Returns: the text node, where the text content of @cur is copied to.
* NULL in case of API or internal errors.
*/
static xmlNodePtr
xsltCopyText(xsltTransformContextPtr ctxt, xmlNodePtr target,
xmlNodePtr cur, int interned)
{
xmlNodePtr copy;
if ((cur->type != XML_TEXT_NODE) &&
(cur->type != XML_CDATA_SECTION_NODE))
return(NULL);
if (cur->content == NULL)
return(NULL);
#ifdef WITH_XSLT_DEBUG_PROCESS
if (cur->type == XML_CDATA_SECTION_NODE) {
XSLT_TRACE(ctxt,XSLT_TRACE_COPY_TEXT,xsltGenericDebug(xsltGenericDebugContext,
"xsltCopyText: copy CDATA text %s\n",
cur->content));
} else if (cur->name == xmlStringTextNoenc) {
XSLT_TRACE(ctxt,XSLT_TRACE_COPY_TEXT,xsltGenericDebug(xsltGenericDebugContext,
"xsltCopyText: copy unescaped text %s\n",
cur->content));
} else {
XSLT_TRACE(ctxt,XSLT_TRACE_COPY_TEXT,xsltGenericDebug(xsltGenericDebugContext,
"xsltCopyText: copy text %s\n",
cur->content));
}
#endif
/*
* Play save and reset the merging mechanism for every new
* target node.
*/
if ((target == NULL) || (target->children == NULL)) {
ctxt->lasttext = NULL;
}
if ((ctxt->style->cdataSection != NULL) &&
(ctxt->type == XSLT_OUTPUT_XML) &&
(target != NULL) &&
(target->type == XML_ELEMENT_NODE) &&
(((target->ns == NULL) &&
(xmlHashLookup2(ctxt->style->cdataSection,
target->name, NULL) != NULL)) ||
((target->ns != NULL) &&
(xmlHashLookup2(ctxt->style->cdataSection,
target->name, target->ns->href) != NULL))))
{
/*
* Process "cdata-section-elements".
*/
/*
* OPTIMIZE TODO: xsltCopyText() is also used for attribute content.
*/
/*
* TODO: Since this doesn't merge adjacent CDATA-section nodes,
* we'll get: <![CDATA[x]]><!CDATA[y]]>.
* TODO: Reported in #321505.
*/
if ((target->last != NULL) &&
(target->last->type == XML_CDATA_SECTION_NODE))
{
/*
* Append to existing CDATA-section node.
*/
copy = xsltAddTextString(ctxt, target->last, cur->content,
xmlStrlen(cur->content));
goto exit;
} else {
unsigned int len;
len = xmlStrlen(cur->content);
copy = xmlNewCDataBlock(ctxt->output, cur->content, len);
if (copy == NULL)
goto exit;
ctxt->lasttext = copy->content;
ctxt->lasttsize = len;
ctxt->lasttuse = len;
}
} else if ((target != NULL) &&
(target->last != NULL) &&
/* both escaped or both non-escaped text-nodes */
(((target->last->type == XML_TEXT_NODE) &&
(target->last->name == cur->name)) ||
/* non-escaped text nodes and CDATA-section nodes */
(((target->last->type == XML_CDATA_SECTION_NODE) &&
(cur->name == xmlStringTextNoenc)))))
{
/*
* we are appending to an existing text node
*/
copy = xsltAddTextString(ctxt, target->last, cur->content,
xmlStrlen(cur->content));
goto exit;
} else if ((interned) && (target != NULL) &&
(target->doc != NULL) &&
(target->doc->dict == ctxt->dict))
{
/*
* TODO: DO we want to use this also for "text" output?
*/
copy = xmlNewTextLen(NULL, 0);
if (copy == NULL)
goto exit;
if (cur->name == xmlStringTextNoenc)
copy->name = xmlStringTextNoenc;
/*
* Must confirm that content is in dict (bug 302821)
* TODO: This check should be not needed for text coming
* from the stylesheets
*/
if (xmlDictOwns(ctxt->dict, cur->content))
copy->content = cur->content;
else {
if ((copy->content = xmlStrdup(cur->content)) == NULL)
return NULL;
}
} else {
/*
* normal processing. keep counters to extend the text node
* in xsltAddTextString if needed.
*/
unsigned int len;
len = xmlStrlen(cur->content);
copy = xmlNewTextLen(cur->content, len);
if (copy == NULL)
goto exit;
if (cur->name == xmlStringTextNoenc)
copy->name = xmlStringTextNoenc;
ctxt->lasttext = copy->content;
ctxt->lasttsize = len;
ctxt->lasttuse = len;
}
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.
*/
copy = xsltAddChild(target, copy);
}
} else {
xsltTransformError(ctxt, NULL, target,
"xsltCopyText: text copy failed\n");
}
exit:
if ((copy == NULL) || (copy->content == NULL)) {
xsltTransformError(ctxt, NULL, target,
"Internal error in xsltCopyText(): "
"Failed to copy the string.\n");
ctxt->state = XSLT_STATE_STOPPED;
}
return(copy);
}
/**
* xsltShallowCopyAttr:
* @ctxt: a XSLT process context
* @invocNode: responsible node in the stylesheet; used for error reports
* @target: the element where the attribute will be grafted
* @attr: the attribute to be copied
*
* Do a copy of an attribute.
* Called by:
* - xsltCopyTreeInternal()
* - xsltCopyOf()
* - xsltCopy()
*
* Returns: a new xmlAttrPtr, or NULL in case of error.
*/
static xmlAttrPtr
xsltShallowCopyAttr(xsltTransformContextPtr ctxt, xmlNodePtr invocNode,
xmlNodePtr target, xmlAttrPtr attr)
{
xmlAttrPtr copy;
xmlChar *value;
if (attr == NULL)
return(NULL);
if (target->type != XML_ELEMENT_NODE) {
xsltTransformError(ctxt, NULL, invocNode,
"Cannot add an attribute node to a non-element node.\n");
return(NULL);
}
if (target->children != NULL) {
xsltTransformError(ctxt, NULL, invocNode,
"Attribute nodes must be added before "
"any child nodes to an element.\n");
return(NULL);
}
value = xmlNodeListGetString(attr->doc, attr->children, 1);
if (attr->ns != NULL) {
xmlNsPtr ns;
ns = xsltGetSpecialNamespace(ctxt, invocNode,
attr->ns->href, attr->ns->prefix, target);
if (ns == NULL) {
xsltTransformError(ctxt, NULL, invocNode,
"Namespace fixup error: Failed to acquire an in-scope "
"namespace binding of the copied attribute '{%s}%s'.\n",
attr->ns->href, attr->name);
/*
* TODO: Should we just stop here?
*/
}
/*
* Note that xmlSetNsProp() will take care of duplicates
* and assigns the new namespace even to a duplicate.
*/
copy = xmlSetNsProp(target, ns, attr->name, value);
} else {
copy = xmlSetNsProp(target, NULL, attr->name, value);
}
if (value != NULL)
xmlFree(value);
if (copy == NULL)
return(NULL);
#if 0
/*
* NOTE: This was optimized according to bug #342695.
* TODO: Can this further be optimized, if source and target
* share the same dict and attr->children is just 1 text node
* which is in the dict? How probable is such a case?
*/
/*
* TODO: Do we need to create an empty text node if the value
* is the empty string?
*/
value = xmlNodeListGetString(attr->doc, attr->children, 1);
if (value != NULL) {
txtNode = xmlNewDocText(target->doc, NULL);
if (txtNode == NULL)
return(NULL);
if ((target->doc != NULL) &&
(target->doc->dict != NULL))
{
txtNode->content =
(xmlChar *) xmlDictLookup(target->doc->dict,
BAD_CAST value, -1);
xmlFree(value);
} else
txtNode->content = value;
copy->children = txtNode;
}
#endif
return(copy);
}
/**
* xsltCopyAttrListNoOverwrite:
* @ctxt: a XSLT process context
* @invocNode: responsible node in the stylesheet; used for error reports
* @target: the element where the new attributes will be grafted
* @attr: the first attribute in the list to be copied
*
* Copies a list of attribute nodes, starting with @attr, over to the
* @target element node.
*
* Called by:
* - xsltCopyTreeInternal()
*
* Returns 0 on success and -1 on errors and internal errors.
*/
static int
xsltCopyAttrListNoOverwrite(xsltTransformContextPtr ctxt,
xmlNodePtr invocNode,
xmlNodePtr target, xmlAttrPtr attr)
{
xmlAttrPtr copy;
xmlNsPtr origNs = NULL, copyNs = NULL;
xmlChar *value;
/*
* Don't use xmlCopyProp() here, since it will try to
* reconciliate namespaces.
*/
while (attr != NULL) {
/*
* Find a namespace node in the tree of @target.
* Avoid searching for the same ns.
*/
if (attr->ns != origNs) {
origNs = attr->ns;
if (attr->ns != NULL) {
copyNs = xsltGetSpecialNamespace(ctxt, invocNode,
attr->ns->href, attr->ns->prefix, target);
if (copyNs == NULL)
return(-1);
} else
copyNs = NULL;
}
/*
* If attribute has a value, we need to copy it (watching out
* for possible entities)
*/
if ((attr->children) && (attr->children->type == XML_TEXT_NODE) &&
(attr->children->next == NULL)) {
copy = xmlNewNsProp(target, copyNs, attr->name,
attr->children->content);
} else if (attr->children != NULL) {
value = xmlNodeListGetString(attr->doc, attr->children, 1);
copy = xmlNewNsProp(target, copyNs, attr->name, BAD_CAST value);
xmlFree(value);
} else {
copy = xmlNewNsProp(target, copyNs, attr->name, NULL);
}
if (copy == NULL)
return(-1);
attr = attr->next;
}
return(0);
}
/**
* xsltShallowCopyElem:
* @ctxt: the XSLT process context
* @node: the element node in the source tree
* or the Literal Result Element
* @insert: the parent in the result tree
* @isLRE: if @node is a Literal Result Element
*
* Make a copy of the element node @node
* and insert it as last child of @insert.
*
* URGENT TODO: The problem with this one (for the non-refactored code)
* is that it is used for both, Literal Result Elements *and*
* copying input nodes.
*
* BIG NOTE: This is only called for XML_ELEMENT_NODEs.
*
* Called from:
* xsltApplySequenceConstructor()
* (for Literal Result Elements - which is a problem)
* xsltCopy() (for shallow-copying elements via xsl:copy)
*
* Returns a pointer to the new node, or NULL in case of error
*/
static xmlNodePtr
xsltShallowCopyElem(xsltTransformContextPtr ctxt, xmlNodePtr node,
xmlNodePtr insert, int isLRE)
{
xmlNodePtr copy;
if ((node->type == XML_DTD_NODE) || (insert == NULL))
return(NULL);
if ((node->type == XML_TEXT_NODE) ||
(node->type == XML_CDATA_SECTION_NODE))
return(xsltCopyText(ctxt, insert, node, 0));
copy = xmlDocCopyNode(node, insert->doc, 0);
if (copy != NULL) {
copy->doc = ctxt->output;
copy = xsltAddChild(insert, copy);
if (node->type == XML_ELEMENT_NODE) {
/*
* Add namespaces as they are needed
*/
if (node->nsDef != NULL) {
/*
* TODO: Remove the LRE case in the refactored code
* gets enabled.
*/
if (isLRE)
xsltCopyNamespaceList(ctxt, copy, node->nsDef);
else
xsltCopyNamespaceListInternal(copy, node->nsDef);
}
/*
* URGENT TODO: The problem with this is that it does not
* copy over all namespace nodes in scope.
* The damn thing about this is, that we would need to
* use the xmlGetNsList(), for every single node; this is
* also done in xsltCopyTreeInternal(), but only for the top node.
*/
if (node->ns != NULL) {
if (isLRE) {
/*
* REVISIT TODO: Since the non-refactored code still does
* ns-aliasing, we need to call xsltGetNamespace() here.
* Remove this when ready.
*/
copy->ns = xsltGetNamespace(ctxt, node, node->ns, copy);
} else {
copy->ns = xsltGetSpecialNamespace(ctxt,
node, node->ns->href, node->ns->prefix, copy);
}
} else if ((insert->type == XML_ELEMENT_NODE) &&
(insert->ns != NULL))
{
/*
* "Undeclare" the default namespace.
*/
xsltGetSpecialNamespace(ctxt, node, NULL, NULL, copy);
}
}
} else {
xsltTransformError(ctxt, NULL, node,
"xsltShallowCopyElem: copy %s failed\n", node->name);
}
return(copy);
}
/**
* xsltCopyTreeList:
* @ctxt: a XSLT process context
* @invocNode: responsible node in the stylesheet; used for error reports
* @list: the list of element nodes in the source tree.
* @insert: the parent in the result tree.
* @isLRE: is this a literal result element list
* @topElemVisited: indicates if a top-most element was already processed
*
* Make a copy of the full list of tree @list
* and insert it as last children of @insert
*
* NOTE: Not to be used for Literal Result Elements.
*
* Used by:
* - xsltCopyOf()
*
* Returns a pointer to the new list, or NULL in case of error
*/
static xmlNodePtr
xsltCopyTreeList(xsltTransformContextPtr ctxt, xmlNodePtr invocNode,
xmlNodePtr list,
xmlNodePtr insert, int isLRE, int topElemVisited)
{
xmlNodePtr copy, ret = NULL;
while (list != NULL) {
copy = xsltCopyTreeInternal(ctxt, invocNode,
list, insert, isLRE, topElemVisited);
if (copy != NULL) {
if (ret == NULL) {
ret = copy;
}
}
list = list->next;
}
return(ret);
}
/**
* xsltCopyNamespaceListInternal:
* @node: the target node
* @cur: the first namespace
*
* Do a copy of a namespace list. If @node is non-NULL the
* new namespaces are added automatically.
* Called by:
* xsltCopyTreeInternal()
*
* QUESTION: What is the exact difference between this function
* and xsltCopyNamespaceList() in "namespaces.c"?
* ANSWER: xsltCopyNamespaceList() tries to apply ns-aliases.
*
* Returns: a new xmlNsPtr, or NULL in case of error.
*/
static xmlNsPtr
xsltCopyNamespaceListInternal(xmlNodePtr elem, xmlNsPtr ns) {
xmlNsPtr ret = NULL;
xmlNsPtr p = NULL, q, luNs;
if (ns == NULL)
return(NULL);
/*
* One can add namespaces only on element nodes
*/
if ((elem != NULL) && (elem->type != XML_ELEMENT_NODE))
elem = NULL;
do {
if (ns->type != XML_NAMESPACE_DECL)
break;
/*
* Avoid duplicating namespace declarations on the tree.
*/
if (elem != NULL) {
if ((elem->ns != NULL) &&
xmlStrEqual(elem->ns->prefix, ns->prefix) &&
xmlStrEqual(elem->ns->href, ns->href))
{
ns = ns->next;
continue;
}
luNs = xmlSearchNs(elem->doc, elem, ns->prefix);
if ((luNs != NULL) && (xmlStrEqual(luNs->href, ns->href)))
{
ns = ns->next;
continue;
}
}
q = xmlNewNs(elem, ns->href, ns->prefix);
if (p == NULL) {
ret = p = q;
} else if (q != NULL) {
p->next = q;
p = q;
}
ns = ns->next;
} while (ns != NULL);
return(ret);
}
/**
* xsltShallowCopyNsNode:
* @ctxt: the XSLT transformation context
* @invocNode: responsible node in the stylesheet; used for error reports
* @insert: the target element node in the result tree
* @ns: the namespace node
*
* This is used for copying ns-nodes with xsl:copy-of and xsl:copy.
*
* Returns a new/existing ns-node, or NULL.
*/
static xmlNsPtr
xsltShallowCopyNsNode(xsltTransformContextPtr ctxt,
xmlNodePtr invocNode,
xmlNodePtr insert,
xmlNsPtr ns)
{
/*
* TODO: Contrary to header comments, this is declared as int.
* be modified to return a node pointer, or NULL if any error
*/
xmlNsPtr tmpns;
if ((insert == NULL) || (insert->type != XML_ELEMENT_NODE))
return(NULL);
if (insert->children != NULL) {
xsltTransformError(ctxt, NULL, invocNode,
"Namespace nodes must be added before "
"any child nodes are added to an element.\n");
return(NULL);
}
/*
* BIG NOTE: Xalan-J simply overwrites any ns-decls with
* an equal prefix. We definitively won't do that.
*
* MSXML 4.0 and the .NET ignores ns-decls for which an
* equal prefix is already in use.
*
* Saxon raises an error like:
* "net.sf.saxon.xpath.DynamicError: Cannot create two namespace
* nodes with the same name".
*
* NOTE: We'll currently follow MSXML here.
* REVISIT TODO: Check if it's better to follow Saxon here.
*/
if (ns->prefix == NULL) {
/*
* If we are adding ns-nodes to an element using e.g.
* <xsl:copy-of select="/foo/namespace::*">, then we need
* to ensure that we don't incorrectly declare a default
* namespace on an element in no namespace, which otherwise
* would move the element incorrectly into a namespace, if
* the node tree is serialized.
*/
if (insert->ns == NULL)
goto occupied;
} else if ((ns->prefix[0] == 'x') &&
xmlStrEqual(ns->prefix, BAD_CAST "xml"))
{
/*
* The XML namespace is built in.
*/
return(NULL);
}
if (insert->nsDef != NULL) {
tmpns = insert->nsDef;
do {
if ((tmpns->prefix == NULL) == (ns->prefix == NULL)) {
if ((tmpns->prefix == ns->prefix) ||
xmlStrEqual(tmpns->prefix, ns->prefix))
{
/*
* Same prefix.
*/
if (xmlStrEqual(tmpns->href, ns->href))
return(NULL);
goto occupied;
}
}
tmpns = tmpns->next;
} while (tmpns != NULL);
}
tmpns = xmlSearchNs(insert->doc, insert, ns->prefix);
if ((tmpns != NULL) && xmlStrEqual(tmpns->href, ns->href))
return(NULL);
/*
* Declare a new namespace.
* TODO: The problem (wrt efficiency) with this xmlNewNs() is
* that it will again search the already declared namespaces
* for a duplicate :-/
*/
return(xmlNewNs(insert, ns->href, ns->prefix));
occupied:
/*
* TODO: We could as well raise an error here (like Saxon does),
* or at least generate a warning.
*/
return(NULL);
}
/**
* xsltCopyTreeInternal:
* @ctxt: the XSLT transformation context
* @invocNode: responsible node in the stylesheet; used for error reports
* @node: the element node in the source tree
* @insert: the parent in the result tree
* @isLRE: indicates if @node is a Literal Result Element
* @topElemVisited: indicates if a top-most element was already processed
*
* Make a copy of the full tree under the element node @node
* and insert it as last child of @insert
*
* NOTE: Not to be used for Literal Result Elements.
*
* Used by:
* - xsltCopyOf()
*
* Returns a pointer to the new tree, or NULL in case of error
*/
static xmlNodePtr
xsltCopyTreeInternal(xsltTransformContextPtr ctxt,
xmlNodePtr invocNode,
xmlNodePtr node,
xmlNodePtr insert, int isLRE, int topElemVisited)
{
xmlNodePtr copy;
if (node == NULL)
return(NULL);
switch (node->type) {
case XML_ELEMENT_NODE:
case XML_ENTITY_REF_NODE:
case XML_ENTITY_NODE:
case XML_PI_NODE:
case XML_COMMENT_NODE:
case XML_DOCUMENT_NODE:
case XML_HTML_DOCUMENT_NODE:
#ifdef LIBXML_DOCB_ENABLED
case XML_DOCB_DOCUMENT_NODE:
#endif
break;
case XML_TEXT_NODE: {
int noenc = (node->name == xmlStringTextNoenc);
return(xsltCopyTextString(ctxt, insert, node->content, noenc));
}
case XML_CDATA_SECTION_NODE:
return(xsltCopyTextString(ctxt, insert, node->content, 0));
case XML_ATTRIBUTE_NODE:
return((xmlNodePtr)
xsltShallowCopyAttr(ctxt, invocNode, insert, (xmlAttrPtr) node));
case XML_NAMESPACE_DECL:
return((xmlNodePtr) xsltShallowCopyNsNode(ctxt, invocNode,
insert, (xmlNsPtr) node));
case XML_DOCUMENT_TYPE_NODE:
case XML_DOCUMENT_FRAG_NODE:
case XML_NOTATION_NODE:
case XML_DTD_NODE:
case XML_ELEMENT_DECL:
case XML_ATTRIBUTE_DECL:
case XML_ENTITY_DECL:
case XML_XINCLUDE_START:
case XML_XINCLUDE_END:
return(NULL);
}
if (XSLT_IS_RES_TREE_FRAG(node)) {
if (node->children != NULL)
copy = xsltCopyTreeList(ctxt, invocNode,
node->children, insert, 0, 0);
else
copy = NULL;
return(copy);
}
copy = xmlDocCopyNode(node, insert->doc, 0);
if (copy != NULL) {
copy->doc = ctxt->output;
copy = xsltAddChild(insert, copy);
/*
* The node may have been coalesced into another text node.
*/
if (insert->last != copy)
return(insert->last);
copy->next = NULL;
if (node->type == XML_ELEMENT_NODE) {
/*
* Copy in-scope namespace nodes.
*
* REVISIT: Since we try to reuse existing in-scope ns-decls by
* using xmlSearchNsByHref(), this will eventually change
* the prefix of an original ns-binding; thus it might
* break QNames in element/attribute content.
* OPTIMIZE TODO: If we had a xmlNsPtr * on the transformation
* context, plus a ns-lookup function, which writes directly
* to a given list, then we wouldn't need to create/free the
* nsList every time.
*/
if ((topElemVisited == 0) &&
(node->parent != NULL) &&
(node->parent->type != XML_DOCUMENT_NODE) &&
(node->parent->type != XML_HTML_DOCUMENT_NODE))
{
xmlNsPtr *nsList, *curns, ns;
/*
* If this is a top-most element in a tree to be
* copied, then we need to ensure that all in-scope
* namespaces are copied over. For nodes deeper in the
* tree, it is sufficient to reconcile only the ns-decls
* (node->nsDef entries).
*/
nsList = xmlGetNsList(node->doc, node);
if (nsList != NULL) {
curns = nsList;
do {
/*
* Search by prefix first in order to break as less
* QNames in element/attribute content as possible.
*/
ns = xmlSearchNs(insert->doc, insert,
(*curns)->prefix);
if ((ns == NULL) ||
(! xmlStrEqual(ns->href, (*curns)->href)))
{
ns = NULL;
/*
* Search by namespace name.
* REVISIT TODO: Currently disabled.
*/
#if 0
ns = xmlSearchNsByHref(insert->doc,
insert, (*curns)->href);
#endif
}
if (ns == NULL) {
/*
* Declare a new namespace on the copied element.
*/
ns = xmlNewNs(copy, (*curns)->href,
(*curns)->prefix);
/* TODO: Handle errors */
}
if (node->ns == *curns) {
/*
* If this was the original's namespace then set
* the generated counterpart on the copy.
*/
copy->ns = ns;
}
curns++;
} while (*curns != NULL);
xmlFree(nsList);
}
} else if (node->nsDef != NULL) {
/*
* Copy over all namespace declaration attributes.
*/
if (node->nsDef != NULL) {
if (isLRE)
xsltCopyNamespaceList(ctxt, copy, node->nsDef);
else
xsltCopyNamespaceListInternal(copy, node->nsDef);
}
}
/*
* Set the namespace.
*/
if (node->ns != NULL) {
if (copy->ns == NULL) {
/*
* This will map copy->ns to one of the newly created
* in-scope ns-decls, OR create a new ns-decl on @copy.
*/
copy->ns = xsltGetSpecialNamespace(ctxt, invocNode,
node->ns->href, node->ns->prefix, copy);
}
} else if ((insert->type == XML_ELEMENT_NODE) &&
(insert->ns != NULL))
{
/*
* "Undeclare" the default namespace on @copy with xmlns="".
*/
xsltGetSpecialNamespace(ctxt, invocNode, NULL, NULL, copy);
}
/*
* Copy attribute nodes.
*/
if (node->properties != NULL) {
xsltCopyAttrListNoOverwrite(ctxt, invocNode,
copy, node->properties);
}
if (topElemVisited == 0)
topElemVisited = 1;
}
/*
* Copy the subtree.
*/
if (node->children != NULL) {
xsltCopyTreeList(ctxt, invocNode,
node->children, copy, isLRE, topElemVisited);
}
} else {
xsltTransformError(ctxt, NULL, invocNode,
"xsltCopyTreeInternal: Copying of '%s' failed.\n", node->name);
}
return(copy);
}
/**
* xsltCopyTree:
* @ctxt: the XSLT transformation context
* @node: the element node in the source tree
* @insert: the parent in the result tree
* @literal: indicates if @node is a Literal Result Element
*
* Make a copy of the full tree under the element node @node
* and insert it as last child of @insert
* For literal result element, some of the namespaces may not be copied
* over according to section 7.1.
* TODO: Why is this a public function?
*
* Returns a pointer to the new tree, or NULL in case of error
*/
xmlNodePtr
xsltCopyTree(xsltTransformContextPtr ctxt, xmlNodePtr node,
xmlNodePtr insert, int literal)
{
return(xsltCopyTreeInternal(ctxt, node, node, insert, literal, 0));
}
/************************************************************************
* *
* Error/fallback processing *
* *
************************************************************************/
/**
* xsltApplyFallbacks:
* @ctxt: a XSLT process context
* @node: the node in the source tree.
* @inst: the node generating the error
*
* Process possible xsl:fallback nodes present under @inst
*
* Returns the number of xsl:fallback element found and processed
*/
static int
xsltApplyFallbacks(xsltTransformContextPtr ctxt, xmlNodePtr node,
xmlNodePtr inst) {
xmlNodePtr child;
int ret = 0;
if ((ctxt == NULL) || (node == NULL) || (inst == NULL) ||
(inst->children == NULL))
return(0);
child = inst->children;
while (child != NULL) {
if ((IS_XSLT_ELEM(child)) &&
(xmlStrEqual(child->name, BAD_CAST "fallback"))) {
#ifdef WITH_XSLT_DEBUG_PARSING
xsltGenericDebug(xsltGenericDebugContext,
"applying xsl:fallback\n");
#endif
ret++;
xsltApplySequenceConstructor(ctxt, node, child->children,
NULL);
}
child = child->next;
}
return(ret);
}
/************************************************************************
* *
* Default processing *
* *
************************************************************************/
/**
* xsltDefaultProcessOneNode:
* @ctxt: a XSLT process context
* @node: the node in the source tree.
* @params: extra parameters passed to the template if any
*
* Process the source node with the default built-in template rule:
* <xsl:template match="*|/">
* <xsl:apply-templates/>
* </xsl:template>
*
* and
*
* <xsl:template match="text()|@*">
* <xsl:value-of select="."/>
* </xsl:template>
*
* Note also that namespace declarations are copied directly:
*
* the built-in template rule is the only template rule that is applied
* for namespace nodes.
*/
static void
xsltDefaultProcessOneNode(xsltTransformContextPtr ctxt, xmlNodePtr node,
xsltStackElemPtr params) {
xmlNodePtr copy;
xmlNodePtr delete = NULL, cur;
int nbchild = 0, oldSize;
int childno = 0, oldPos;
xsltTemplatePtr template;
CHECK_STOPPED;
/*
* Handling of leaves
*/
switch (node->type) {
case XML_DOCUMENT_NODE:
case XML_HTML_DOCUMENT_NODE:
case XML_ELEMENT_NODE:
break;
case XML_CDATA_SECTION_NODE:
#ifdef WITH_XSLT_DEBUG_PROCESS
XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext,
"xsltDefaultProcessOneNode: copy CDATA %s\n",
node->content));
#endif
copy = xsltCopyText(ctxt, ctxt->insert, node, 0);
if (copy == NULL) {
xsltTransformError(ctxt, NULL, node,
"xsltDefaultProcessOneNode: cdata copy failed\n");
}
return;
case XML_TEXT_NODE:
#ifdef WITH_XSLT_DEBUG_PROCESS
if (node->content == NULL) {
XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext,
"xsltDefaultProcessOneNode: copy empty text\n"));
return;
} else {
XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext,
"xsltDefaultProcessOneNode: copy text %s\n",
node->content));
}
#endif
copy = xsltCopyText(ctxt, ctxt->insert, node, 0);
if (copy == NULL) {
xsltTransformError(ctxt, NULL, node,
"xsltDefaultProcessOneNode: text copy failed\n");
}
return;
case XML_ATTRIBUTE_NODE:
cur = node->children;
while ((cur != NULL) && (cur->type != XML_TEXT_NODE))
cur = cur->next;
if (cur == NULL) {
xsltTransformError(ctxt, NULL, node,
"xsltDefaultProcessOneNode: no text for attribute\n");
} else {
#ifdef WITH_XSLT_DEBUG_PROCESS
if (cur->content == NULL) {
XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext,
"xsltDefaultProcessOneNode: copy empty text\n"));
} else {
XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext,
"xsltDefaultProcessOneNode: copy text %s\n",
cur->content));
}
#endif
copy = xsltCopyText(ctxt, ctxt->insert, cur, 0);
if (copy == NULL) {
xsltTransformError(ctxt, NULL, node,
"xsltDefaultProcessOneNode: text copy failed\n");
}
}
return;
default:
return;
}
/*
* Handling of Elements: first pass, cleanup and counting
*/
cur = node->children;
while (cur != NULL) {
switch (cur->type) {
case XML_TEXT_NODE:
case XML_CDATA_SECTION_NODE:
case XML_DOCUMENT_NODE:
case XML_HTML_DOCUMENT_NODE:
case XML_ELEMENT_NODE:
case XML_PI_NODE:
case XML_COMMENT_NODE:
nbchild++;
break;
case XML_DTD_NODE:
/* Unlink the DTD, it's still reachable using doc->intSubset */
if (cur->next != NULL)
cur->next->prev = cur->prev;
if (cur->prev != NULL)
cur->prev->next = cur->next;
break;
default:
#ifdef WITH_XSLT_DEBUG_PROCESS
XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext,
"xsltDefaultProcessOneNode: skipping node type %d\n",
cur->type));
#endif
delete = cur;
}
cur = cur->next;
if (delete != NULL) {
#ifdef WITH_XSLT_DEBUG_PROCESS
XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext,
"xsltDefaultProcessOneNode: removing ignorable blank node\n"));
#endif
xmlUnlinkNode(delete);
xmlFreeNode(delete);
delete = NULL;
}
}
if (delete != NULL) {
#ifdef WITH_XSLT_DEBUG_PROCESS
XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext,
"xsltDefaultProcessOneNode: removing ignorable blank node\n"));
#endif
xmlUnlinkNode(delete);
xmlFreeNode(delete);
delete = NULL;
}
/*
* Handling of Elements: second pass, actual processing
*/
oldSize = ctxt->xpathCtxt->contextSize;
oldPos = ctxt->xpathCtxt->proximityPosition;
cur = node->children;
while (cur != NULL) {
childno++;
switch (cur->type) {
case XML_DOCUMENT_NODE:
case XML_HTML_DOCUMENT_NODE:
case XML_ELEMENT_NODE:
ctxt->xpathCtxt->contextSize = nbchild;
ctxt->xpathCtxt->proximityPosition = childno;
xsltProcessOneNode(ctxt, cur, params);
break;
case XML_CDATA_SECTION_NODE:
template = xsltGetTemplate(ctxt, cur, NULL);
if (template) {
#ifdef WITH_XSLT_DEBUG_PROCESS
XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext,
"xsltDefaultProcessOneNode: applying template for CDATA %s\n",
cur->content));
#endif
/*
* Instantiate the xsl:template.
*/
xsltApplyXSLTTemplate(ctxt, cur, template->content,
template, params);
} else /* if (ctxt->mode == NULL) */ {
#ifdef WITH_XSLT_DEBUG_PROCESS
XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext,
"xsltDefaultProcessOneNode: copy CDATA %s\n",
cur->content));
#endif
copy = xsltCopyText(ctxt, ctxt->insert, cur, 0);
if (copy == NULL) {
xsltTransformError(ctxt, NULL, cur,
"xsltDefaultProcessOneNode: cdata copy failed\n");
}
}
break;
case XML_TEXT_NODE:
template = xsltGetTemplate(ctxt, cur, NULL);
if (template) {
#ifdef WITH_XSLT_DEBUG_PROCESS
XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext,
"xsltDefaultProcessOneNode: applying template for text %s\n",
cur->content));
#endif
ctxt->xpathCtxt->contextSize = nbchild;
ctxt->xpathCtxt->proximityPosition = childno;
/*
* Instantiate the xsl:template.
*/
xsltApplyXSLTTemplate(ctxt, cur, template->content,
template, params);
} else /* if (ctxt->mode == NULL) */ {
#ifdef WITH_XSLT_DEBUG_PROCESS
if (cur->content == NULL) {
XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext,
"xsltDefaultProcessOneNode: copy empty text\n"));
} else {
XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext,
"xsltDefaultProcessOneNode: copy text %s\n",
cur->content));
}
#endif
copy = xsltCopyText(ctxt, ctxt->insert, cur, 0);
if (copy == NULL) {
xsltTransformError(ctxt, NULL, cur,
"xsltDefaultProcessOneNode: text copy failed\n");
}
}
break;
case XML_PI_NODE:
case XML_COMMENT_NODE:
template = xsltGetTemplate(ctxt, cur, NULL);
if (template) {
#ifdef WITH_XSLT_DEBUG_PROCESS
if (cur->type == XML_PI_NODE) {
XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext,
"xsltDefaultProcessOneNode: template found for PI %s\n",
cur->name));
} else if (cur->type == XML_COMMENT_NODE) {
XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext,
"xsltDefaultProcessOneNode: template found for comment\n"));
}
#endif
ctxt->xpathCtxt->contextSize = nbchild;
ctxt->xpathCtxt->proximityPosition = childno;
/*
* Instantiate the xsl:template.
*/
xsltApplyXSLTTemplate(ctxt, cur, template->content,
template, params);
}
break;
default:
break;
}
cur = cur->next;
}
ctxt->xpathCtxt->contextSize = oldSize;
ctxt->xpathCtxt->proximityPosition = oldPos;
}
/**
* xsltProcessOneNode:
* @ctxt: a XSLT process context
* @contextNode: the "current node" in the source tree
* @withParams: extra parameters (e.g. xsl:with-param) passed to the
* template if any
*
* Process the source node.
*/
void
xsltProcessOneNode(xsltTransformContextPtr ctxt, xmlNodePtr contextNode,
xsltStackElemPtr withParams)
{
xsltTemplatePtr templ;
xmlNodePtr oldNode;
templ = xsltGetTemplate(ctxt, contextNode, NULL);
/*
* If no template is found, apply the default rule.
*/
if (templ == NULL) {
#ifdef WITH_XSLT_DEBUG_PROCESS
if (contextNode->type == XML_DOCUMENT_NODE) {
XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext,
"xsltProcessOneNode: no template found for /\n"));
} else if (contextNode->type == XML_CDATA_SECTION_NODE) {
XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext,
"xsltProcessOneNode: no template found for CDATA\n"));
} else if (contextNode->type == XML_ATTRIBUTE_NODE) {
XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext,
"xsltProcessOneNode: no template found for attribute %s\n",
((xmlAttrPtr) contextNode)->name));
} else {
XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext,
"xsltProcessOneNode: no template found for %s\n", contextNode->name));
}
#endif
oldNode = ctxt->node;
ctxt->node = contextNode;
xsltDefaultProcessOneNode(ctxt, contextNode, withParams);
ctxt->node = oldNode;
return;
}
if (contextNode->type == XML_ATTRIBUTE_NODE) {
xsltTemplatePtr oldCurTempRule = ctxt->currentTemplateRule;
/*
* Set the "current template rule".
*/
ctxt->currentTemplateRule = templ;
#ifdef WITH_XSLT_DEBUG_PROCESS
XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext,
"xsltProcessOneNode: applying template '%s' for attribute %s\n",
templ->match, contextNode->name));
#endif
xsltApplyXSLTTemplate(ctxt, contextNode, templ->content, templ, withParams);
ctxt->currentTemplateRule = oldCurTempRule;
} else {
xsltTemplatePtr oldCurTempRule = ctxt->currentTemplateRule;
/*
* Set the "current template rule".
*/
ctxt->currentTemplateRule = templ;
#ifdef WITH_XSLT_DEBUG_PROCESS
if (contextNode->type == XML_DOCUMENT_NODE) {
XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext,
"xsltProcessOneNode: applying template '%s' for /\n",
templ->match));
} else {
XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext,
"xsltProcessOneNode: applying template '%s' for %s\n",
templ->match, contextNode->name));
}
#endif
xsltApplyXSLTTemplate(ctxt, contextNode, templ->content, templ, withParams);
ctxt->currentTemplateRule = oldCurTempRule;
}
}
static xmlNodePtr
xsltDebuggerStartSequenceConstructor(xsltTransformContextPtr ctxt,
xmlNodePtr contextNode,
xmlNodePtr list,
xsltTemplatePtr templ,
int *addCallResult)
{
xmlNodePtr debugedNode = NULL;
if (ctxt->debugStatus != XSLT_DEBUG_NONE) {
if (templ) {
*addCallResult = xslAddCall(templ, templ->elem);
} else {
*addCallResult = xslAddCall(NULL, list);
}
switch (ctxt->debugStatus) {
case XSLT_DEBUG_RUN_RESTART:
case XSLT_DEBUG_QUIT:
if (*addCallResult)
xslDropCall();
return(NULL);
}
if (templ) {
xslHandleDebugger(templ->elem, contextNode, templ, ctxt);
debugedNode = templ->elem;
} else if (list) {
xslHandleDebugger(list, contextNode, templ, ctxt);
debugedNode = list;
} else if (ctxt->inst) {
xslHandleDebugger(ctxt->inst, contextNode, templ, ctxt);
debugedNode = ctxt->inst;
}
}
return(debugedNode);
}
/**
* xsltLocalVariablePush:
* @ctxt: the transformation context
* @variable: variable to be pushed to the variable stack
* @level: new value for variable's level
*
* Places the variable onto the local variable stack
*
* Returns: 0 for success, -1 for any error
* **NOTE:**
* This is an internal routine and should not be called by users!
*/
int
xsltLocalVariablePush(xsltTransformContextPtr ctxt,
xsltStackElemPtr variable,
int level)
{
if (ctxt->varsMax == 0) {
ctxt->varsMax = 10;
ctxt->varsTab =
(xsltStackElemPtr *) xmlMalloc(ctxt->varsMax *
sizeof(ctxt->varsTab[0]));
if (ctxt->varsTab == NULL) {
xmlGenericError(xmlGenericErrorContext, "malloc failed !\n");
return (-1);
}
}
if (ctxt->varsNr >= ctxt->varsMax) {
ctxt->varsMax *= 2;
ctxt->varsTab =
(xsltStackElemPtr *) xmlRealloc(ctxt->varsTab,
ctxt->varsMax *
sizeof(ctxt->varsTab[0]));
if (ctxt->varsTab == NULL) {
xmlGenericError(xmlGenericErrorContext, "realloc failed !\n");
return (-1);
}
}
ctxt->varsTab[ctxt->varsNr++] = variable;
ctxt->vars = variable;
variable->level = level;
return(0);
}
/**
* xsltReleaseLocalRVTs:
*
* Fragments which are results of extension instructions
* are preserved; all other fragments are freed/cached.
*/
static void
xsltReleaseLocalRVTs(xsltTransformContextPtr ctxt, xmlDocPtr base)
{
xmlDocPtr cur = ctxt->localRVT, tmp;
while ((cur != NULL) && (cur != base)) {
if (cur->psvi == (void *) ((long) 1)) {
cur = (xmlDocPtr) cur->next;
} else {
tmp = cur;
cur = (xmlDocPtr) cur->next;
if (tmp == ctxt->localRVT)
ctxt->localRVT = cur;
/*
* We need ctxt->localRVTBase for extension instructions
* which return values (like EXSLT's function).
*/
if (tmp == ctxt->localRVTBase)
ctxt->localRVTBase = cur;
if (tmp->prev)
tmp->prev->next = (xmlNodePtr) cur;
if (cur)
cur->prev = tmp->prev;
xsltReleaseRVT(ctxt, tmp);
}
}
}
/**
* xsltApplySequenceConstructor:
* @ctxt: a XSLT process context
* @contextNode: the "current node" in the source tree
* @list: the nodes of a sequence constructor;
* (plus leading xsl:param elements)
* @templ: the compiled xsl:template (optional)
*
* Processes a sequence constructor.
*
* NOTE: ctxt->currentTemplateRule was introduced to reflect the
* semantics of "current template rule". I.e. the field ctxt->templ
* is not intended to reflect this, thus always pushed onto the
* template stack.
*/
static void
xsltApplySequenceConstructor(xsltTransformContextPtr ctxt,
xmlNodePtr contextNode, xmlNodePtr list,
xsltTemplatePtr templ)
{
xmlNodePtr oldInsert, oldInst, oldCurInst, oldContextNode;
xmlNodePtr cur, insert, copy = NULL;
int level = 0, oldVarsNr;
xmlDocPtr oldLocalFragmentTop, oldLocalFragmentBase;
#ifdef XSLT_REFACTORED
xsltStylePreCompPtr info;
#endif
#ifdef WITH_DEBUGGER
int addCallResult = 0;
xmlNodePtr debuggedNode = NULL;
#endif
if (ctxt == NULL)
return;
#ifdef WITH_DEBUGGER
if (ctxt->debugStatus != XSLT_DEBUG_NONE) {
debuggedNode =
xsltDebuggerStartSequenceConstructor(ctxt, contextNode,
list, templ, &addCallResult);
if (debuggedNode == NULL)
return;
}
#endif
if (list == NULL)
return;
CHECK_STOPPED;
oldLocalFragmentTop = ctxt->localRVT;
oldInsert = insert = ctxt->insert;
oldInst = oldCurInst = ctxt->inst;
oldContextNode = ctxt->node;
/*
* Save current number of variables on the stack; new vars are popped when
* exiting.
*/
oldVarsNr = ctxt->varsNr;
/*
* Process the sequence constructor.
*/
cur = list;
while (cur != NULL) {
ctxt->inst = cur;
#ifdef WITH_DEBUGGER
switch (ctxt->debugStatus) {
case XSLT_DEBUG_RUN_RESTART:
case XSLT_DEBUG_QUIT:
break;
}
#endif
/*
* Test; we must have a valid insertion point.
*/
if (insert == NULL) {
#ifdef WITH_XSLT_DEBUG_PROCESS
XSLT_TRACE(ctxt,XSLT_TRACE_APPLY_TEMPLATE,xsltGenericDebug(xsltGenericDebugContext,
"xsltApplySequenceConstructor: insert == NULL !\n"));
#endif
goto error;
}
#ifdef WITH_DEBUGGER
if ((ctxt->debugStatus != XSLT_DEBUG_NONE) && (debuggedNode != cur))
xslHandleDebugger(cur, contextNode, templ, ctxt);
#endif
#ifdef XSLT_REFACTORED
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, contextNode, cur);
goto skip_children;
}
/*
* Something really went wrong:
*/
xsltTransformError(ctxt, NULL, cur,
"Internal error in xsltApplySequenceConstructor(): "
"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,
"xsltApplySequenceConstructor: copy literal result "
"element '%s'\n", cur->name));
#endif
/*
* Copy the raw element-node.
* OLD: if ((copy = xsltShallowCopyElem(ctxt, cur, insert))
* == NULL)
* goto error;
*/
copy = xmlDocCopyNode(cur, insert->doc, 0);
if (copy == NULL) {
xsltTransformError(ctxt, NULL, cur,
"Internal error in xsltApplySequenceConstructor(): "
"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;
copy = xsltAddChild(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 "
"xsltApplySequenceConstructor(): "
"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 xsltGetSpecialNamespace() will
* create a ns-decl on the copied node.
*/
copy->ns = xsltGetSpecialNamespace(ctxt, cur,
cur->ns->href, cur->ns->prefix, copy);
} else {
/*
* Undeclare the default namespace if needed.
* This can be skipped, if the result element has
* no ns-decls, in which case the result element
* obviously does not declare a default namespace;
* AND there's either no parent, or the parent
* element is in no namespace; this means there's no
* default namespace is scope to care about.
*
* REVISIT: This might result in massive
* generation of ns-decls if nodes in a default
* namespaces are mixed with nodes in no namespace.
*
*/
if (copy->nsDef ||
((insert != NULL) &&
(insert->type == XML_ELEMENT_NODE) &&
(insert->ns != NULL)))
{
xsltGetSpecialNamespace(ctxt, cur,
NULL, NULL, copy);
}
}
}
/*
* 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."
* NOTE: See bug #341325.
*/
if (cur->properties != NULL) {
xsltAttrListTemplateProcess(ctxt, copy, cur->properties);
}
} 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, contextNode, cur)) {
xsltTransformError(ctxt, NULL, cur,
"The is no fallback behaviour defined for "
"the unknown XSLT element '%s'.\n",
cur->name);
}
ctxt->insert = oldInsert;
} else if (info->func != NULL) {
/*
* Execute the XSLT instruction.
*/
ctxt->insert = insert;
info->func(ctxt, contextNode, cur,
(xsltElemPreCompPtr) info);
/*
* Cleanup temporary tree fragments.
*/
if (oldLocalFragmentTop != ctxt->localRVT)
xsltReleaseLocalRVTs(ctxt, oldLocalFragmentTop);
ctxt->insert = oldInsert;
} else if (info->type == XSLT_FUNC_VARIABLE) {
xsltStackElemPtr tmpvar = ctxt->vars;
xsltParseStylesheetVariable(ctxt, cur);
if (tmpvar != ctxt->vars) {
/*
* TODO: Using a @tmpvar is an annoying workaround, but
* the current mechanisms do not provide any other way
* of knowing if the var was really pushed onto the
* stack.
*/
ctxt->vars->level = level;
}
} else if (info->type == XSLT_FUNC_MESSAGE) {
/*
* TODO: Won't be hit, since we don't compile xsl:message.
*/
xsltMessage(ctxt, contextNode, cur);
} else {
xsltTransformError(ctxt, NULL, cur,
"Unexpected 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,
"xsltApplySequenceConstructor: unknown extension %s\n",
cur->name));
#endif
ctxt->insert = insert;
if (!xsltApplyFallbacks(ctxt, contextNode, 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,
"xsltApplySequenceConstructor: extension construct %s\n",
cur->name));
#endif
ctxt->insert = insert;
/*
* We need the fragment base for extension instructions
* which return values (like EXSLT's function).
*/
oldLocalFragmentBase = ctxt->localRVTBase;
ctxt->localRVTBase = NULL;
func(ctxt, contextNode, cur, cur->psvi);
ctxt->localRVTBase = oldLocalFragmentBase;
/*
* Cleanup temporary tree fragments.
*/
if (oldLocalFragmentTop != ctxt->localRVT)
xsltReleaseLocalRVTs(ctxt, oldLocalFragmentTop);
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,
"xsltApplySequenceConstructor: copy unescaped text '%s'\n",
cur->content));
} else {
XSLT_TRACE(ctxt, XSLT_TRACE_APPLY_TEMPLATE,
xsltGenericDebug(xsltGenericDebugContext,
"xsltApplySequenceConstructor: 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
*/
xsltStylePreCompPtr info = (xsltStylePreCompPtr) cur->psvi;
if (info == NULL) {
if (IS_XSLT_NAME(cur, "message")) {
xsltMessage(ctxt, contextNode, cur);
} else {
/*
* That's an error try to apply one of the fallback cases
*/
ctxt->insert = insert;
if (!xsltApplyFallbacks(ctxt, contextNode, cur)) {
xsltGenericError(xsltGenericErrorContext,
"xsltApplySequenceConstructor: %s was not compiled\n",
cur->name);
}
ctxt->insert = oldInsert;
}
goto skip_children;
}
if (info->func != NULL) {
oldCurInst = ctxt->inst;
ctxt->inst = cur;
ctxt->insert = insert;
oldLocalFragmentBase = ctxt->localRVTBase;
ctxt->localRVTBase = NULL;
info->func(ctxt, contextNode, cur, (xsltElemPreCompPtr) info);
ctxt->localRVTBase = oldLocalFragmentBase;
/*
* Cleanup temporary tree fragments.
*/
if (oldLocalFragmentTop != ctxt->localRVT)
xsltReleaseLocalRVTs(ctxt, oldLocalFragmentTop);
ctxt->insert = oldInsert;
ctxt->inst = oldCurInst;
goto skip_children;
}
if (IS_XSLT_NAME(cur, "variable")) {
xsltStackElemPtr tmpvar = ctxt->vars;
oldCurInst = ctxt->inst;
ctxt->inst = cur;
xsltParseStylesheetVariable(ctxt, cur);
ctxt->inst = oldCurInst;
if (tmpvar != ctxt->vars) {
/*
* TODO: Using a @tmpvar is an annoying workaround, but
* the current mechanisms do not provide any other way
* of knowing if the var was really pushed onto the
* stack.
*/
ctxt->vars->level = level;
}
} else if (IS_XSLT_NAME(cur, "message")) {
xsltMessage(ctxt, contextNode, cur);
} else {
xsltTransformError(ctxt, NULL, cur,
"Unexpected XSLT element '%s'.\n", cur->name);
}
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
* element names consists of just xsl:text.
*/
#ifdef WITH_XSLT_DEBUG_PROCESS
if (cur->type == XML_CDATA_SECTION_NODE) {
XSLT_TRACE(ctxt,XSLT_TRACE_APPLY_TEMPLATE,xsltGenericDebug(xsltGenericDebugContext,
"xsltApplySequenceConstructor: copy CDATA text %s\n",
cur->content));
} else if (cur->name == xmlStringTextNoenc) {
XSLT_TRACE(ctxt,XSLT_TRACE_APPLY_TEMPLATE,xsltGenericDebug(xsltGenericDebugContext,
"xsltApplySequenceConstructor: copy unescaped text %s\n",
cur->content));
} else {
XSLT_TRACE(ctxt,XSLT_TRACE_APPLY_TEMPLATE,xsltGenericDebug(xsltGenericDebugContext,
"xsltApplySequenceConstructor: copy text %s\n",
cur->content));
}
#endif
if (xsltCopyText(ctxt, insert, cur, ctxt->internalized) == NULL)
goto error;
} else if ((cur->type == XML_ELEMENT_NODE) &&
(cur->ns != NULL) && (cur->psvi != NULL)) {
xsltTransformFunction function;
oldCurInst = ctxt->inst;
ctxt->inst = cur;
/*
* Flagged as an extension element
*/
if (cur->psvi == xsltExtMarker)
function = (xsltTransformFunction)
xsltExtElementLookup(ctxt, cur->name, cur->ns->href);
else
function = ((xsltElemPreCompPtr) cur->psvi)->func;
if (function == NULL) {
xmlNodePtr child;
int found = 0;
#ifdef WITH_XSLT_DEBUG_PROCESS
XSLT_TRACE(ctxt,XSLT_TRACE_APPLY_TEMPLATE,xsltGenericDebug(xsltGenericDebugContext,
"xsltApplySequenceConstructor: unknown extension %s\n",
cur->name));
#endif
/*
* Search if there are fallbacks
*/
child = cur->children;
while (child != NULL) {
if ((IS_XSLT_ELEM(child)) &&
(IS_XSLT_NAME(child, "fallback")))
{
found = 1;
xsltApplySequenceConstructor(ctxt, contextNode,
child->children, NULL);
}
child = child->next;
}
if (!found) {
xsltTransformError(ctxt, NULL, cur,
"xsltApplySequenceConstructor: failed to find extension %s\n",
cur->name);
}
} else {
#ifdef WITH_XSLT_DEBUG_PROCESS
XSLT_TRACE(ctxt,XSLT_TRACE_APPLY_TEMPLATE,xsltGenericDebug(xsltGenericDebugContext,
"xsltApplySequenceConstructor: extension construct %s\n",
cur->name));
#endif
ctxt->insert = insert;
/*
* We need the fragment base for extension instructions
* which return values (like EXSLT's function).
*/
oldLocalFragmentBase = ctxt->localRVTBase;
ctxt->localRVTBase = NULL;
function(ctxt, contextNode, cur, cur->psvi);
/*
* Cleanup temporary tree fragments.
*/
if (oldLocalFragmentTop != ctxt->localRVT)
xsltReleaseLocalRVTs(ctxt, oldLocalFragmentTop);
ctxt->localRVTBase = oldLocalFragmentBase;
ctxt->insert = oldInsert;
}
ctxt->inst = oldCurInst;
goto skip_children;
} else if (cur->type == XML_ELEMENT_NODE) {
#ifdef WITH_XSLT_DEBUG_PROCESS
XSLT_TRACE(ctxt,XSLT_TRACE_APPLY_TEMPLATE,xsltGenericDebug(xsltGenericDebugContext,
"xsltApplySequenceConstructor: copy node %s\n",
cur->name));
#endif
oldCurInst = ctxt->inst;
ctxt->inst = cur;
if ((copy = xsltShallowCopyElem(ctxt, cur, insert, 1)) == NULL)
goto error;
/*
* Add extra namespaces inherited from the current template
* if we are in the first level children and this is a
* "real" template.
*/
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];
/* Note that the XSLT namespace was already excluded
* in xsltGetInheritedNsList().
*/
#if 0
if (xmlStrEqual(ns->href, XSLT_NAMESPACE))
continue;
#endif
style = ctxt->style;
while (style != NULL) {
if (style->nsAliases != NULL)
URI = (const xmlChar *)
xmlHashLookup(style->nsAliases, ns->href);
if (URI != NULL)
break;
style = xsltNextImport(style);
}
if (URI == UNDEFINED_DEFAULT_NS)
continue;
if (URI == NULL)
URI = ns->href;
/*
* TODO: The following will still be buggy for the
* non-refactored code.
*/
ret = xmlSearchNs(copy->doc, copy, ns->prefix);
if ((ret == NULL) || (!xmlStrEqual(ret->href, URI)))
{
xmlNewNs(copy, URI, ns->prefix);
}
}
if (copy->ns != NULL) {
/*
* Fix the node namespace if needed
*/
copy->ns = xsltGetNamespace(ctxt, cur, copy->ns, copy);
}
}
/*
* all the attributes are directly inherited
*/
if (cur->properties != NULL) {
xsltAttrListTemplateProcess(ctxt, copy, cur->properties);
}
ctxt->inst = oldCurInst;
}
#endif /* else of XSLT_REFACTORED */
/*
* Descend into content in document order.
*/
if (cur->children != NULL) {
if (cur->children->type != XML_ENTITY_DECL) {
cur = cur->children;
level++;
if (copy != NULL)
insert = copy;
continue;
}
}
skip_children:
/*
* 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;
if (cur->next != NULL) {
cur = cur->next;
continue;
}
do {
cur = cur->parent;
level--;
/*
* Pop variables/params (xsl:variable and xsl:param).
*/
if ((ctxt->varsNr > oldVarsNr) && (ctxt->vars->level > level)) {
xsltLocalVariablePop(ctxt, oldVarsNr, level);
}
insert = insert->parent;
if (cur == NULL)
break;
if (cur == list->parent) {
cur = NULL;
break;
}
if (cur->next != NULL) {
cur = cur->next;
break;
}
} while (cur != NULL);
}
error:
/*
* In case of errors: pop remaining variables.
*/
if (ctxt->varsNr > oldVarsNr)
xsltLocalVariablePop(ctxt, oldVarsNr, -1);
ctxt->node = oldContextNode;
ctxt->inst = oldInst;
ctxt->insert = oldInsert;
#ifdef WITH_DEBUGGER
if ((ctxt->debugStatus != XSLT_DEBUG_NONE) && (addCallResult)) {
xslDropCall();
}
#endif
}
/*
* xsltApplyXSLTTemplate:
* @ctxt: a XSLT transformation context
* @contextNode: the node in the source tree.
* @list: