1
0
mirror of https://gitlab.gnome.org/GNOME/libxslt synced 2025-08-08 21:42:07 +03:00

Refactored xsltValueOf(). Changed to use xmlXPathCastToString() directly,

* libxslt/attributes.c libxslt/documents.c
  libxslt/functions.c libxslt/keys.c libxslt/namespaces.c
  libxslt/pattern.c libxslt/preproc.c libxslt/templates.c
  libxslt/templates.h libxslt/transform.c libxslt/variables.c
  libxslt/xslt.c libxslt/xsltInternals.h libxslt/xsltutils.c
  libxslt/xsltutils.h libexslt/common.c libexslt/dynamic.c
  libexslt/functions.c libexslt/strings.c:
  Refactored xsltValueOf(). Changed to use xmlXPathCastToString()
  directly, rather than creating an intermediate object with
  xmlXPathConvertString(). This now does not add a text-node to
  the result if the string is empty (this has impact on
  serialization, since an empty text-node is serialized as
  <foo></foo>, and now it will be serialized as <foo/>).
  Refactored other functions in transform.c:
  Mostly code cleanup/restructuring. Minimized number of
  function variables for instruction which eat up function stack
  memory when recursing templates (xsltIf(), xsltChoose(),
  xsltApplyTemplates(),  xsltCallTemplate()).
  Changed XSLT tests to use xmlXPathCompiledEvalToBoolean().
  Implemented redefinition checks at compilation-time and
  eliminating them at transformation time in the refactored code
  paths.
  Introduced the field @currentTemplateRule on xsltTransformContext to
  reflect the "Current Template Rule" as defined by the spec.
  NOTE that ctxt->currentTemplateRule and ctxt->templ is not the
  same; the former is the "Current Template Rule" as defined by the
  XSLT spec, the latter is simply the template struct being
  currently processed by Libxslt.
  Added XML_COMMENT_NODE and XML_CDATA_SECTION_NODE to the macro
  IS_XSLT_REAL_NODE.
  Misc code cleanup/restructuring and everything else I already forgot.
  Refactored lifetime of temporary result tree fragments.
  Substituted all calls to the now deprecated xsltRegisterTmpRVT()
  for the new xsltRegisterLocalRVT().
  Fragments of xsl:variable and xsl:param are freed when the
  variable/pram is freed.
  Fragments created when evaluating a "select" of xsl:varible and
  xsl:param are also bound to the lifetime of the var/param.
  EXSLT's func:function now uses the following functions to let take
  care the transformation's garbage collector of returned tree
  fragments:
    xsltExtensionInstructionResultRegister(),
    xsltExtensionInstructionResultFinalize()
  Fixes:
  #339222 - xsl:param at invalid position inside an xsl:template is
            not catched
  #346015 - Non-declared caller-parameters are accepted
  #160400 - Compiles invalid XSLT; unbound variable accepted
  #308441 - namespaced parameters become unregistered
  #307103 - problem with proximity position in predicates of match
            patterns
  #328218 - problem with exsl:node-set() when converting strings
            to node sets
  #318088 - infinite recursion detection
  #321505 - Multiple contiguous CDATA in output
  #334493 - "--param" option does not have root context
  #114377 - weird func:result/xsl:variable/exsl:node-set interaction
  #150309 - Regression caused by fix for 142768
This commit is contained in:
Kasimier T. Buchcik
2006-07-14 16:10:25 +00:00
parent 36615d1c6d
commit 90d2d1c289
20 changed files with 31917 additions and 29882 deletions

View File

@@ -1,3 +1,64 @@
Fri Jul 14 17:55:42 CEST 2006 Kasimier Buchcik <libxml2-cvs@cazic.net>
* libxslt/attributes.c libxslt/documents.c
libxslt/functions.c libxslt/keys.c libxslt/namespaces.c
libxslt/pattern.c libxslt/preproc.c libxslt/templates.c
libxslt/templates.h libxslt/transform.c libxslt/variables.c
libxslt/xslt.c libxslt/xsltInternals.h libxslt/xsltutils.c
libxslt/xsltutils.h libexslt/common.c libexslt/dynamic.c
libexslt/functions.c libexslt/strings.c:
Refactored xsltValueOf(). Changed to use xmlXPathCastToString()
directly, rather than creating an intermediate object with
xmlXPathConvertString(). This now does not add a text-node to
the result if the string is empty (this has impact on
serialization, since an empty text-node is serialized as
<foo></foo>, and now it will be serialized as <foo/>).
Refactored other functions in transform.c:
Mostly code cleanup/restructuring. Minimized number of
function variables for instruction which eat up function stack
memory when recursing templates (xsltIf(), xsltChoose(),
xsltApplyTemplates(), xsltCallTemplate()).
Changed XSLT tests to use xmlXPathCompiledEvalToBoolean().
Implemented redefinition checks at compilation-time and
eliminating them at transformation time in the refactored code
paths.
Introduced the field @currentTemplateRule on xsltTransformContext to
reflect the "Current Template Rule" as defined by the spec.
NOTE that ctxt->currentTemplateRule and ctxt->templ is not the
same; the former is the "Current Template Rule" as defined by the
XSLT spec, the latter is simply the template struct being
currently processed by Libxslt.
Added XML_COMMENT_NODE and XML_CDATA_SECTION_NODE to the macro
IS_XSLT_REAL_NODE.
Misc code cleanup/restructuring and everything else I already forgot.
Refactored lifetime of temporary result tree fragments.
Substituted all calls to the now deprecated xsltRegisterTmpRVT()
for the new xsltRegisterLocalRVT().
Fragments of xsl:variable and xsl:param are freed when the
variable/pram is freed.
Fragments created when evaluating a "select" of xsl:varible and
xsl:param are also bound to the lifetime of the var/param.
EXSLT's func:function now uses the following functions to let take
care the transformation's garbage collector of returned tree
fragments:
xsltExtensionInstructionResultRegister(),
xsltExtensionInstructionResultFinalize()
Fixes:
#339222 - xsl:param at invalid position inside an xsl:template is
not catched
#346015 - Non-declared caller-parameters are accepted
#160400 - Compiles invalid XSLT; unbound variable accepted
#308441 - namespaced parameters become unregistered
#307103 - problem with proximity position in predicates of match
patterns
#328218 - problem with exsl:node-set() when converting strings
to node sets
#318088 - infinite recursion detection
#321505 - Multiple contiguous CDATA in output
#334493 - "--param" option does not have root context
#114377 - weird func:result/xsl:variable/exsl:node-set interaction
#150309 - Regression caused by fix for 142768
Wed Jun 21 15:13:27 CEST 2006 Kasimier Buchcik <libxml2-cvs@cazic.net> Wed Jun 21 15:13:27 CEST 2006 Kasimier Buchcik <libxml2-cvs@cazic.net>
* tests/docs/bug-54.xml tests/general/bug-54.out * tests/docs/bug-54.xml tests/general/bug-54.out

View File

@@ -23,34 +23,49 @@
static void static void
exsltNodeSetFunction (xmlXPathParserContextPtr ctxt, int nargs) { exsltNodeSetFunction (xmlXPathParserContextPtr ctxt, int nargs) {
xmlChar *strval;
xmlNodePtr retNode;
xmlXPathObjectPtr ret;
if (nargs != 1) { if (nargs != 1) {
xmlXPathSetArityError(ctxt); xmlXPathSetArityError(ctxt);
return; return;
} }
if (xmlXPathStackIsNodeSet (ctxt)) { if (xmlXPathStackIsNodeSet (ctxt)) {
xsltFunctionNodeSet (ctxt, nargs); xsltFunctionNodeSet (ctxt, nargs);
return; return;
} else {
xmlDocPtr fragment;
xsltTransformContextPtr tctxt = xsltXPathGetTransformContext(ctxt);
xmlNodePtr txt;
xmlChar *strval;
xmlXPathObjectPtr obj;
/*
* SPEC EXSLT:
* "You can also use this function to turn a string into a text
* node, which is helpful if you want to pass a string to a
* function that only accepts a node-set."
*/
fragment = xsltCreateRVT(tctxt);
if (fragment == NULL) {
xsltTransformError(tctxt, NULL, tctxt->inst,
"exsltNodeSetFunction: Failed to create a tree fragment.\n");
tctxt->state = XSLT_STATE_STOPPED;
return;
} }
xsltRegisterLocalRVT(tctxt, fragment);
strval = xmlXPathPopString (ctxt); strval = xmlXPathPopString (ctxt);
retNode = xmlNewDocText (NULL, strval);
ret = xmlXPathNewValueTree (retNode);
if (ret == NULL) {
xsltGenericError(xsltGenericErrorContext,
"exsltNodeSetFunction: ret == NULL\n");
} else {
ret->type = XPATH_NODESET;
}
txt = xmlNewDocText (fragment, strval);
xmlAddChild((xmlNodePtr) fragment, txt);
obj = xmlXPathNewNodeSet(txt);
if (obj == NULL) {
xsltTransformError(tctxt, NULL, tctxt->inst,
"exsltNodeSetFunction: Failed to create a node set object.\n");
tctxt->state = XSLT_STATE_STOPPED;
}
if (strval != NULL) if (strval != NULL)
xmlFree (strval); xmlFree (strval);
valuePush (ctxt, ret); valuePush (ctxt, obj);
}
} }
static void static void

View File

@@ -149,7 +149,7 @@ exsltDynMapFunction(xmlXPathParserContextPtr ctxt, int nargs)
*/ */
container = xsltCreateRVT(xsltXPathGetTransformContext(ctxt)); container = xsltCreateRVT(xsltXPathGetTransformContext(ctxt));
if (container != NULL) if (container != NULL)
xsltRegisterTmpRVT(xsltXPathGetTransformContext(ctxt), container); xsltRegisterLocalRVT(xsltXPathGetTransformContext(ctxt), container);
if (nodeset && nodeset->nodeNr > 0) { if (nodeset && nodeset->nodeNr > 0) {
xmlXPathNodeSetSort(nodeset); xmlXPathNodeSetSort(nodeset);

View File

@@ -35,6 +35,7 @@ struct _exsltFuncData {
xmlHashTablePtr funcs; /* pointer to the stylesheet module data */ xmlHashTablePtr funcs; /* pointer to the stylesheet module data */
xmlXPathObjectPtr result; /* returned by func:result */ xmlXPathObjectPtr result; /* returned by func:result */
int error; /* did an error occur? */ int error; /* did an error occur? */
xmlDocPtr RVT; /* result tree fragment */
}; };
typedef struct _exsltFuncResultPreComp exsltFuncResultPreComp; typedef struct _exsltFuncResultPreComp exsltFuncResultPreComp;
@@ -56,6 +57,8 @@ static void exsltFuncFunctionFunction (xmlXPathParserContextPtr ctxt,
int nargs); int nargs);
static exsltFuncFunctionData *exsltFuncNewFunctionData(void); static exsltFuncFunctionData *exsltFuncNewFunctionData(void);
static const xmlChar *exsltResultDataID = (const xmlChar *) "EXSLT Result";
/** /**
* exsltFuncRegisterFunc: * exsltFuncRegisterFunc:
* @func: the #exsltFuncFunctionData for the function * @func: the #exsltFuncFunctionData for the function
@@ -274,7 +277,7 @@ exsltFuncFunctionFunction (xmlXPathParserContextPtr ctxt, int nargs) {
xmlXPathObjectPtr obj, oldResult, ret; xmlXPathObjectPtr obj, oldResult, ret;
exsltFuncData *data; exsltFuncData *data;
exsltFuncFunctionData *func; exsltFuncFunctionData *func;
xmlNodePtr paramNode, oldInsert, fake, content = NULL; xmlNodePtr paramNode, oldInsert, fake;
int oldBase; int oldBase;
xsltStackElemPtr params = NULL, param; xsltStackElemPtr params = NULL, param;
xsltTransformContextPtr tctxt = xsltXPathGetTransformContext(ctxt); xsltTransformContextPtr tctxt = xsltXPathGetTransformContext(ctxt);
@@ -304,7 +307,6 @@ exsltFuncFunctionFunction (xmlXPathParserContextPtr ctxt, int nargs) {
} }
if (func->content != NULL) { if (func->content != NULL) {
paramNode = func->content->prev; paramNode = func->content->prev;
content = func->content;
} }
else else
paramNode = NULL; paramNode = NULL;
@@ -314,16 +316,36 @@ exsltFuncFunctionFunction (xmlXPathParserContextPtr ctxt, int nargs) {
"param == NULL\n"); "param == NULL\n");
return; return;
} }
/*
/* set params */ * Process xsl:param instructions which were not set by the
* invoking function call.
*/
for (i = func->nargs; (i > nargs) && (paramNode != NULL); i--) { for (i = func->nargs; (i > nargs) && (paramNode != NULL); i--) {
/*
* Those are the xsl:param instructions, which were not
* set by the calling function.
*/
param = xsltParseStylesheetCallerParam (tctxt, paramNode);
param->next = params;
params = param;
paramNode = paramNode->prev; paramNode = paramNode->prev;
if (content != NULL)
content = content->prev;
} }
/*
* Process xsl:param instructions which are set by the
* invoking function call.
*/
while ((i-- > 0) && (paramNode != NULL)) { while ((i-- > 0) && (paramNode != NULL)) {
obj = valuePop(ctxt); obj = valuePop(ctxt);
/* FIXME: this is a bit hackish */ /*
* TODO: Using xsltParseStylesheetCallerParam() is actually
* not correct, since we are processing an xsl:param; but
* using xsltParseStylesheetParam() won't work, as it puts
* the param on the varible stack and does not give access to
* the created xsltStackElemPtr.
* It's also not correct, as xsltParseStylesheetCallerParam()
* will report error messages indicating an "xsl:with-param" and
* not the actual "xsl:param".
*/
param = xsltParseStylesheetCallerParam (tctxt, paramNode); param = xsltParseStylesheetCallerParam (tctxt, paramNode);
param->computed = 1; param->computed = 1;
if (param->value != NULL) if (param->value != NULL)
@@ -348,18 +370,18 @@ exsltFuncFunctionFunction (xmlXPathParserContextPtr ctxt, int nargs) {
oldBase = tctxt->varsBase; oldBase = tctxt->varsBase;
tctxt->varsBase = tctxt->varsNr; tctxt->varsBase = tctxt->varsNr;
xsltApplyOneTemplate (tctxt, xmlXPathGetContextNode(ctxt), xsltApplyOneTemplate (tctxt, xmlXPathGetContextNode(ctxt),
content, NULL, params); func->content, NULL, params);
tctxt->insert = oldInsert; tctxt->insert = oldInsert;
tctxt->varsBase = oldBase; /* restore original scope */ tctxt->varsBase = oldBase; /* restore original scope */
if (params != NULL) if (params != NULL)
xsltFreeStackElemList(params); xsltFreeStackElemList(params);
if (data->error != 0) if (data->error != 0)
return; goto error;
if (data->result != NULL) if (data->result != NULL) {
ret = data->result; ret = data->result;
else } else
ret = xmlXPathNewCString(""); ret = xmlXPathNewCString("");
data->result = oldResult; data->result = oldResult;
@@ -377,10 +399,18 @@ exsltFuncFunctionFunction (xmlXPathParserContextPtr ctxt, int nargs) {
"executing a function\n", "executing a function\n",
ctxt->context->functionURI, ctxt->context->function); ctxt->context->functionURI, ctxt->context->function);
xmlFreeNode(fake); xmlFreeNode(fake);
return; goto error;
} }
xmlFreeNode(fake); xmlFreeNode(fake);
valuePush(ctxt, ret); valuePush(ctxt, ret);
error:
/*
* IMPORTANT: This enables previously tree fragments marked as
* being results of a function, to be garbage-collected after
* the calling process exits.
*/
xsltExtensionInstructionResultFinalize(tctxt);
} }
@@ -569,8 +599,7 @@ exsltFuncResultElem (xsltTransformContextPtr ctxt,
exsltFuncResultPreComp *comp) { exsltFuncResultPreComp *comp) {
exsltFuncData *data; exsltFuncData *data;
xmlXPathObjectPtr ret; xmlXPathObjectPtr ret;
xmlNsPtr *oldNsList;
int oldNsNr;
/* It is an error if instantiating the content of the /* It is an error if instantiating the content of the
* func:function element results in the instantiation of more than * func:function element results in the instantiation of more than
@@ -592,6 +621,9 @@ exsltFuncResultElem (xsltTransformContextPtr ctxt,
* Processing * Processing
*/ */
if (comp->select != NULL) { if (comp->select != NULL) {
xmlNsPtr *oldXPNsList;
int oldXPNsNr;
xmlNodePtr oldXPContextNode;
/* If the func:result element has a select attribute, then the /* If the func:result element has a select attribute, then the
* value of the attribute must be an expression and the * value of the attribute must be an expression and the
* returned value is the object that results from evaluating * returned value is the object that results from evaluating
@@ -604,18 +636,29 @@ exsltFuncResultElem (xsltTransformContextPtr ctxt,
data->error = 1; data->error = 1;
return; return;
} }
oldNsList = ctxt->xpathCtxt->namespaces; oldXPNsList = ctxt->xpathCtxt->namespaces;
oldNsNr = ctxt->xpathCtxt->nsNr; oldXPNsNr = ctxt->xpathCtxt->nsNr;
oldXPContextNode = ctxt->xpathCtxt->node;
ctxt->xpathCtxt->namespaces = comp->nsList; ctxt->xpathCtxt->namespaces = comp->nsList;
ctxt->xpathCtxt->nsNr = comp->nsNr; ctxt->xpathCtxt->nsNr = comp->nsNr;
ret = xmlXPathCompiledEval(comp->select, ctxt->xpathCtxt); ret = xmlXPathCompiledEval(comp->select, ctxt->xpathCtxt);
ctxt->xpathCtxt->nsNr = oldNsNr;
ctxt->xpathCtxt->namespaces = oldNsList; ctxt->xpathCtxt->node = oldXPContextNode;
ctxt->xpathCtxt->nsNr = oldXPNsNr;
ctxt->xpathCtxt->namespaces = oldXPNsList;
if (ret == NULL) { if (ret == NULL) {
xsltGenericError(xsltGenericErrorContext, xsltGenericError(xsltGenericErrorContext,
"exsltFuncResultElem: ret == NULL\n"); "exsltFuncResultElem: ret == NULL\n");
return; return;
} }
/*
* Mark it as a function result in order to avoid garbage
* collecting of tree fragments before the function exits.
*/
xsltExtensionInstructionResultRegister(ctxt, ret);
} else if (inst->children != NULL) { } else if (inst->children != NULL) {
/* If the func:result element does not have a select attribute /* If the func:result element does not have a select attribute
* and has non-empty content (i.e. the func:result element has * and has non-empty content (i.e. the func:result element has
@@ -632,7 +675,8 @@ exsltFuncResultElem (xsltTransformContextPtr ctxt,
data->error = 1; data->error = 1;
return; return;
} }
xsltRegisterTmpRVT(ctxt, container); xsltRegisterLocalRVT(ctxt, container);
oldInsert = ctxt->insert; oldInsert = ctxt->insert;
ctxt->insert = (xmlNodePtr) container; ctxt->insert = (xmlNodePtr) container;
xsltApplyOneTemplate (ctxt, ctxt->xpathCtxt->node, xsltApplyOneTemplate (ctxt, ctxt->xpathCtxt->node,
@@ -646,6 +690,11 @@ exsltFuncResultElem (xsltTransformContextPtr ctxt,
data->error = 1; data->error = 1;
} else { } else {
ret->boolval = 0; /* Freeing is not handled there anymore */ ret->boolval = 0; /* Freeing is not handled there anymore */
/*
* Mark it as a function result in order to avoid garbage
* collecting of tree fragments before the function exits.
*/
xsltExtensionInstructionResultRegister(ctxt, ret);
} }
} else { } else {
/* If the func:result element has empty content and does not /* If the func:result element has empty content and does not

View File

@@ -71,10 +71,9 @@ exsltStrTokenizeFunction(xmlXPathParserContextPtr ctxt, int nargs)
container = xsltCreateRVT(tctxt); container = xsltCreateRVT(tctxt);
if (container != NULL) { if (container != NULL) {
xsltRegisterTmpRVT(tctxt, container); xsltRegisterLocalRVT(tctxt, container);
ret = xmlXPathNewNodeSet(NULL); ret = xmlXPathNewNodeSet(NULL);
if (ret != NULL) { if (ret != NULL) {
ret->boolval = 0; /* Freeing is not handled there anymore */
for (cur = str, token = str; *cur != 0; cur += clen) { for (cur = str, token = str; *cur != 0; cur += clen) {
clen = xmlUTF8Size(cur); clen = xmlUTF8Size(cur);
if (*delimiters == 0) { /* empty string case */ if (*delimiters == 0) { /* empty string case */
@@ -174,12 +173,14 @@ exsltStrSplitFunction(xmlXPathParserContextPtr ctxt, int nargs) {
goto fail; goto fail;
} }
/*
* OPTIMIZE TODO: We are creating an xmlDoc for every split!
*/
container = xsltCreateRVT(tctxt); container = xsltCreateRVT(tctxt);
if (container != NULL) { if (container != NULL) {
xsltRegisterTmpRVT(tctxt, container); xsltRegisterLocalRVT(tctxt, container);
ret = xmlXPathNewNodeSet(NULL); ret = xmlXPathNewNodeSet(NULL);
if (ret != NULL) { if (ret != NULL) {
ret->boolval = 0; /* Freeing is not handled there anymore */
for (cur = str, token = str; *cur != 0; cur++) { for (cur = str, token = str; *cur != 0; cur++) {
if (delimiterLength == 0) { if (delimiterLength == 0) {
if (cur != token) { if (cur != token) {

View File

@@ -638,7 +638,7 @@ xsltResolveStylesheetAttributeSet(xsltStylesheetPtr style) {
*/ */
static void static void
xsltAttributeInternal(xsltTransformContextPtr ctxt, xsltAttributeInternal(xsltTransformContextPtr ctxt,
xmlNodePtr currentNode, xmlNodePtr contextNode,
xmlNodePtr inst, xmlNodePtr inst,
xsltStylePreCompPtr castedComp, xsltStylePreCompPtr castedComp,
int fromAttributeSet) int fromAttributeSet)
@@ -656,7 +656,7 @@ xsltAttributeInternal(xsltTransformContextPtr ctxt,
xmlNsPtr ns = NULL; xmlNsPtr ns = NULL;
xmlAttrPtr attr; xmlAttrPtr attr;
if ((ctxt == NULL) || (currentNode == NULL) || (inst == NULL)) if ((ctxt == NULL) || (contextNode == NULL) || (inst == NULL))
return; return;
/* /*
@@ -681,7 +681,7 @@ xsltAttributeInternal(xsltTransformContextPtr ctxt,
if (comp == NULL) { if (comp == NULL) {
xsltTransformError(ctxt, NULL, inst, xsltTransformError(ctxt, NULL, inst,
"Internal error in xsltAttributeInternal(): " "Internal error in xsltAttributeInternal(): "
"The instruction was no compiled.\n"); "The XSLT 'attribute' instruction was not compiled.\n");
return; return;
} }
/* /*
@@ -731,7 +731,7 @@ xsltAttributeInternal(xsltTransformContextPtr ctxt,
#ifdef WITH_DEBUGGER #ifdef WITH_DEBUGGER
if (ctxt->debugStatus != XSLT_DEBUG_NONE) if (ctxt->debugStatus != XSLT_DEBUG_NONE)
xslHandleDebugger(inst, currentNode, NULL, ctxt); xslHandleDebugger(inst, contextNode, NULL, ctxt);
#endif #endif
if (comp->name == NULL) { if (comp->name == NULL) {
@@ -875,8 +875,10 @@ xsltAttributeInternal(xsltTransformContextPtr ctxt,
* tree fragment. * tree fragment.
*/ */
if (nsName != NULL) { if (nsName != NULL) {
ns = xsltTreeAcquireStoredNs(ctxt->document->doc, nsName, /*
prefix); * TODO: Get the doc of @targetElem.
*/
ns = xsltTreeAcquireStoredNs(some doc, nsName, prefix);
} }
} }
#endif #endif
@@ -980,7 +982,7 @@ xsltAttributeInternal(xsltTransformContextPtr ctxt,
/* /*
* The sequence constructor might be complex, so instantiate it. * The sequence constructor might be complex, so instantiate it.
*/ */
value = xsltEvalTemplateString(ctxt, currentNode, inst); value = xsltEvalTemplateString(ctxt, contextNode, inst);
if (value != NULL) { if (value != NULL) {
attr = xmlSetNsProp(ctxt->insert, ns, name, value); attr = xmlSetNsProp(ctxt->insert, ns, name, value);
xmlFree(value); xmlFree(value);

View File

@@ -410,7 +410,9 @@ xsltLoadStyleDocument(xsltStylesheetPtr style, const xmlChar *URI) {
* @ctxt: an XSLT transformation context * @ctxt: an XSLT transformation context
* @doc: a parsed XML document * @doc: a parsed XML document
* *
* Try to find a document within the XSLT transformation context * Try to find a document within the XSLT transformation context.
* This will not find document infos for temporary
* Result Tree Fragments.
* *
* Returns the desired xsltDocumentPtr or NULL in case of error * Returns the desired xsltDocumentPtr or NULL in case of error
*/ */

View File

@@ -107,14 +107,14 @@ xsltDocumentFunctionLoadDocument(xmlXPathParserContextPtr ctxt, xmlChar* URI)
xsltTransformContextPtr tctxt; xsltTransformContextPtr tctxt;
xmlURIPtr uri; xmlURIPtr uri;
xmlChar *fragment; xmlChar *fragment;
xsltDocumentPtr xsltdoc; xsltDocumentPtr idoc; /* document info */
xmlDocPtr doc; xmlDocPtr doc;
xmlXPathContextPtr xptrctxt = NULL; xmlXPathContextPtr xptrctxt = NULL;
xmlXPathObjectPtr object = NULL; xmlXPathObjectPtr resObj = NULL;
tctxt = xsltXPathGetTransformContext(ctxt); tctxt = xsltXPathGetTransformContext(ctxt);
if (tctxt == NULL) { if (tctxt == NULL) {
xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, xsltTransformError(NULL, NULL, NULL,
"document() : internal error tctxt == NULL\n"); "document() : internal error tctxt == NULL\n");
valuePush(ctxt, xmlXPathNewNodeSet(NULL)); valuePush(ctxt, xmlXPathNewNodeSet(NULL));
return; return;
@@ -122,7 +122,7 @@ xsltDocumentFunctionLoadDocument(xmlXPathParserContextPtr ctxt, xmlChar* URI)
uri = xmlParseURI((const char *) URI); uri = xmlParseURI((const char *) URI);
if (uri == NULL) { if (uri == NULL) {
xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, xsltTransformError(tctxt, NULL, NULL,
"document() : failed to parse URI\n"); "document() : failed to parse URI\n");
valuePush(ctxt, xmlXPathNewNodeSet(NULL)); valuePush(ctxt, xmlXPathNewNodeSet(NULL));
return; return;
@@ -135,16 +135,20 @@ xsltDocumentFunctionLoadDocument(xmlXPathParserContextPtr ctxt, xmlChar* URI)
if (fragment != NULL) { if (fragment != NULL) {
uri->fragment = NULL; uri->fragment = NULL;
URI = xmlSaveUri(uri); URI = xmlSaveUri(uri);
xsltdoc = xsltLoadDocument(tctxt, URI); idoc = xsltLoadDocument(tctxt, URI);
xmlFree(URI); xmlFree(URI);
} else } else
xsltdoc = xsltLoadDocument(tctxt, URI); idoc = xsltLoadDocument(tctxt, URI);
xmlFreeURI(uri); xmlFreeURI(uri);
if (xsltdoc == NULL) { if (idoc == NULL) {
if ((URI == NULL) || if ((URI == NULL) ||
(URI[0] == '#') || (URI[0] == '#') ||
(xmlStrEqual(tctxt->style->doc->URL, URI))) { (xmlStrEqual(tctxt->style->doc->URL, URI)))
{
/*
* This selects the stylesheet's doc itself.
*/
doc = tctxt->style->doc; doc = tctxt->style->doc;
} else { } else {
valuePush(ctxt, xmlXPathNewNodeSet(NULL)); valuePush(ctxt, xmlXPathNewNodeSet(NULL));
@@ -155,11 +159,10 @@ xsltDocumentFunctionLoadDocument(xmlXPathParserContextPtr ctxt, xmlChar* URI)
return; return;
} }
} else } else
doc = xsltdoc->doc; doc = idoc->doc;
if (fragment == NULL) { if (fragment == NULL) {
valuePush(ctxt, valuePush(ctxt, xmlXPathNewNodeSet((xmlNodePtr) doc));
xmlXPathNewNodeSet((xmlNodePtr) doc));
return; return;
} }
@@ -167,21 +170,20 @@ xsltDocumentFunctionLoadDocument(xmlXPathParserContextPtr ctxt, xmlChar* URI)
#ifdef LIBXML_XPTR_ENABLED #ifdef LIBXML_XPTR_ENABLED
xptrctxt = xmlXPtrNewContext(doc, NULL, NULL); xptrctxt = xmlXPtrNewContext(doc, NULL, NULL);
if (xptrctxt == NULL) { if (xptrctxt == NULL) {
xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, xsltTransformError(tctxt, NULL, NULL,
"document() : internal error xptrctxt == NULL\n"); "document() : internal error xptrctxt == NULL\n");
goto out_fragment; goto out_fragment;
} }
object = xmlXPtrEval(fragment, xptrctxt); resObj = xmlXPtrEval(fragment, xptrctxt);
xmlXPathFreeContext(xptrctxt);
#endif #endif
xmlFree(fragment); xmlFree(fragment);
if (xptrctxt != NULL)
xmlXPathFreeContext(xptrctxt);
if (object == NULL) if (resObj == NULL)
goto out_fragment; goto out_fragment;
switch (object->type) { switch (resObj->type) {
case XPATH_NODESET: case XPATH_NODESET:
break; break;
case XPATH_UNDEFINED: case XPATH_UNDEFINED:
@@ -193,17 +195,17 @@ xsltDocumentFunctionLoadDocument(xmlXPathParserContextPtr ctxt, xmlChar* URI)
case XPATH_XSLT_TREE: case XPATH_XSLT_TREE:
case XPATH_RANGE: case XPATH_RANGE:
case XPATH_LOCATIONSET: case XPATH_LOCATIONSET:
xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, xsltTransformError(tctxt, NULL, NULL,
"document() : XPointer does not select a node set: #%s\n", "document() : XPointer does not select a node set: #%s\n",
fragment); fragment);
goto out_object; goto out_object;
} }
valuePush(ctxt, object); valuePush(ctxt, resObj);
return; return;
out_object: out_object:
xmlXPathFreeObject(object); xmlXPathFreeObject(resObj);
out_fragment: out_fragment:
valuePush(ctxt, xmlXPathNewNodeSet(NULL)); valuePush(ctxt, xmlXPathNewNodeSet(NULL));
@@ -346,13 +348,7 @@ xsltDocumentFunction(xmlXPathParserContextPtr ctxt, int nargs)
*/ */
void void
xsltKeyFunction(xmlXPathParserContextPtr ctxt, int nargs){ xsltKeyFunction(xmlXPathParserContextPtr ctxt, int nargs){
xmlNodeSetPtr nodelist;
xmlXPathObjectPtr obj1, obj2; xmlXPathObjectPtr obj1, obj2;
xmlChar *key = NULL, *value;
const xmlChar *keyURI;
xsltTransformContextPtr tctxt;
xsltDocumentPtr oldDocumentPtr;
xmlDocPtr oldXPathDocPtr;
if (nargs != 2) { if (nargs != 2) {
xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL,
@@ -361,6 +357,9 @@ xsltKeyFunction(xmlXPathParserContextPtr ctxt, int nargs){
return; return;
} }
/*
* Get the key's value.
*/
obj2 = valuePop(ctxt); obj2 = valuePop(ctxt);
xmlXPathStringFunction(ctxt, 1); xmlXPathStringFunction(ctxt, 1);
if ((obj2 == NULL) || if ((obj2 == NULL) ||
@@ -372,6 +371,9 @@ xsltKeyFunction(xmlXPathParserContextPtr ctxt, int nargs){
return; return;
} }
/*
* Get the key's name.
*/
obj1 = valuePop(ctxt); obj1 = valuePop(ctxt);
if ((obj2->type == XPATH_NODESET) || (obj2->type == XPATH_XSLT_TREE)) { if ((obj2->type == XPATH_NODESET) || (obj2->type == XPATH_XSLT_TREE)) {
@@ -395,8 +397,26 @@ xsltKeyFunction(xmlXPathParserContextPtr ctxt, int nargs){
} }
valuePush(ctxt, ret); valuePush(ctxt, ret);
} else { } else {
xmlNodeSetPtr nodelist = NULL;
xmlChar *key = NULL, *value;
const xmlChar *keyURI;
xsltTransformContextPtr tctxt;
xmlChar *qname, *prefix; xmlChar *qname, *prefix;
xmlXPathContextPtr xpctxt = ctxt->context;
xmlNodePtr tmpNode = NULL;
xsltDocumentPtr oldDocInfo;
tctxt = xsltXPathGetTransformContext(ctxt);
oldDocInfo = tctxt->document;
if (xpctxt->node == NULL) {
xsltTransformError(tctxt, NULL, tctxt->inst,
"Internal error in xsltKeyFunction(): "
"The context node is not set on the XPath context.\n");
tctxt->state = XSLT_STATE_STOPPED;
goto error;
}
/* /*
* Get the associated namespace URI if qualified name * Get the associated namespace URI if qualified name
*/ */
@@ -409,10 +429,13 @@ xsltKeyFunction(xmlXPathParserContextPtr ctxt, int nargs){
xmlFree(prefix); xmlFree(prefix);
} else { } else {
if (prefix != NULL) { if (prefix != NULL) {
keyURI = xmlXPathNsLookup(ctxt->context, prefix); keyURI = xmlXPathNsLookup(xpctxt, prefix);
if (keyURI == NULL) { if (keyURI == NULL) {
xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, xsltTransformError(tctxt, NULL, tctxt->inst,
"key() : prefix %s is not bound\n", prefix); "key() : prefix %s is not bound\n", prefix);
/*
* TODO: Shouldn't we stop here?
*/
} }
xmlFree(prefix); xmlFree(prefix);
} else { } else {
@@ -426,59 +449,90 @@ xsltKeyFunction(xmlXPathParserContextPtr ctxt, int nargs){
valuePush(ctxt, obj2); valuePush(ctxt, obj2);
xmlXPathStringFunction(ctxt, 1); xmlXPathStringFunction(ctxt, 1);
if ((ctxt->value == NULL) || (ctxt->value->type != XPATH_STRING)) { if ((ctxt->value == NULL) || (ctxt->value->type != XPATH_STRING)) {
xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, xsltTransformError(tctxt, NULL, tctxt->inst,
"key() : invalid arg expecting a string\n"); "key() : invalid arg expecting a string\n");
ctxt->error = XPATH_INVALID_TYPE; ctxt->error = XPATH_INVALID_TYPE;
xmlXPathFreeObject(obj1); goto error;
return;
} }
obj2 = valuePop(ctxt); obj2 = valuePop(ctxt);
value = obj2->stringval; value = obj2->stringval;
tctxt = xsltXPathGetTransformContext(ctxt);
oldDocumentPtr = tctxt->document;
oldXPathDocPtr = tctxt->xpathCtxt->doc;
if ((ctxt->context->doc != NULL) &&
(tctxt->document->doc != ctxt->context->doc)) {
/* /*
* The xpath context document needs to be changed. If the * We need to ensure that ctxt->document is available for
* current context document is a node-set, we must use an * xsltGetKey().
* xsltDocument associated with the node-set, which may or * First find the relevant doc, which is the context node's
* may not currently exist. * owner doc; using context->doc is not safe, since
* the doc could have been acquired via the document() function,
* or the doc might be a Result Tree Fragment.
* FUTURE INFO: In XSLT 2.0 the key() function takes an additional
* argument indicating the doc to use.
*/ */
if (xmlStrEqual((const xmlChar *)ctxt->context->doc->name, if (xpctxt->node->type == XML_NAMESPACE_DECL) {
BAD_CAST " fake node libxslt")) { /* node-set */
/* /*
* Check whether we already have an xsltDocument set up * REVISIT: This is a libxml hack! Check xpath.c for details.
* The XPath module sets the owner element of a ns-node on
* the ns->next field.
*/ */
if (ctxt->context->doc->_private == NULL) /* nope */ if ((((xmlNsPtr) xpctxt->node)->next != NULL) &&
ctxt->context->doc->_private = (((xmlNsPtr) xpctxt->node)->next->type == XML_ELEMENT_NODE))
xsltNewDocument(tctxt, ctxt->context->doc); {
tctxt->document = ctxt->context->doc->_private; tmpNode = (xmlNodePtr) ((xmlNsPtr) xpctxt->node)->next;
} }
else { } else
tctxt->document = xsltFindDocument(tctxt, ctxt->context->doc); tmpNode = xpctxt->node;
if (tctxt->document == NULL)
tctxt->document = oldDocumentPtr; if ((tmpNode == NULL) || (tmpNode->doc == NULL)) {
else xsltTransformError(tctxt, NULL, tctxt->inst,
tctxt->xpathCtxt->doc = ctxt->context->doc; "Internal error in xsltKeyFunction(): "
} "Couldn't get the doc of the XPath context node.\n");
} goto error;
nodelist = xsltGetKey(tctxt, key, keyURI, value);
tctxt->document = oldDocumentPtr;
tctxt->xpathCtxt->doc = oldXPathDocPtr;
valuePush(ctxt, xmlXPathWrapNodeSet(
xmlXPathNodeSetMerge(NULL, nodelist)));
} }
if ((tctxt->document == NULL) ||
(tctxt->document->doc != tmpNode->doc))
{
if (tmpNode->doc->name && (tmpNode->doc->name[0] == ' ')) {
/*
* This is a Result Tree Fragment.
*/
if (tmpNode->doc->_private == NULL) {
tmpNode->doc->_private = xsltNewDocument(tctxt, tmpNode->doc);
if (tmpNode->doc->_private == NULL)
goto error;
}
tctxt->document = (xsltDocumentPtr) tmpNode->doc->_private;
} else {
/*
* May be the initial source doc or a doc acquired via the
* document() function.
*/
tctxt->document = xsltFindDocument(tctxt, tmpNode->doc);
}
if (tctxt->document == NULL) {
xsltTransformError(tctxt, NULL, tctxt->inst,
"Internal error in xsltKeyFunction(): "
"Could not get the document info of a context doc.\n");
tctxt->state = XSLT_STATE_STOPPED;
goto error;
}
}
/*
* Get/compute the key value.
*/
nodelist = xsltGetKey(tctxt, key, keyURI, value);
error:
tctxt->document = oldDocInfo;
valuePush(ctxt, xmlXPathWrapNodeSet(
xmlXPathNodeSetMerge(NULL, nodelist)));
if (key != NULL)
xmlFree(key);
}
if (obj1 != NULL) if (obj1 != NULL)
xmlXPathFreeObject(obj1); xmlXPathFreeObject(obj1);
if (obj2 != NULL) if (obj2 != NULL)
xmlXPathFreeObject(obj2); xmlXPathFreeObject(obj2);
if (key != NULL)
xmlFree(key);
} }
/** /**

View File

@@ -382,7 +382,9 @@ error:
* @nameURI: the name URI or NULL * @nameURI: the name URI or NULL
* @value: the key value to look for * @value: the key value to look for
* *
* Lookup a key * Looks up a key of the in current source doc (the document info
* on @ctxt->document). Computes the key if not already done
* for the current source doc.
* *
* Returns the nodeset resulting from the query or NULL * Returns the nodeset resulting from the query or NULL
*/ */
@@ -403,6 +405,7 @@ xsltGetKey(xsltTransformContextPtr ctxt, const xmlChar *name,
xsltGenericDebug(xsltGenericDebugContext, xsltGenericDebug(xsltGenericDebugContext,
"Get key %s, value %s\n", name, value); "Get key %s, value %s\n", name, value);
#endif #endif
table = (xsltKeyTablePtr) ctxt->document->keys; table = (xsltKeyTablePtr) ctxt->document->keys;
while (table != NULL) { while (table != NULL) {
if (((nameURI != NULL) == (table->nameURI != NULL)) && if (((nameURI != NULL) == (table->nameURI != NULL)) &&
@@ -464,6 +467,7 @@ xsltGetKey(xsltTransformContextPtr ctxt, const xmlChar *name,
return(NULL); return(NULL);
} }
#if 0 /* Merged with xsltInitCtxtKey() */
/** /**
* xsltEvalXPathKeys: * xsltEvalXPathKeys:
* @ctxt: the XSLT transformation context * @ctxt: the XSLT transformation context
@@ -547,104 +551,117 @@ xsltEvalXPathKeys(xsltTransformContextPtr ctxt, xmlXPathCompExprPtr comp,
ctxt->xpathCtxt->namespaces = oldNamespaces; ctxt->xpathCtxt->namespaces = oldNamespaces;
return(ret); return(ret);
} }
#endif
/** /**
* xsltInitCtxtKey: * xsltInitCtxtKey:
* @ctxt: an XSLT transformation context * @ctxt: an XSLT transformation context
* @doc: an XSLT document * @idoc: the document information (holds key values)
* @keyd: the key definition * @keyDef: the key definition
* *
* Computes the key tables this key and for the current input document. * Computes the key tables this key and for the current input document.
*/ */
int int
xsltInitCtxtKey(xsltTransformContextPtr ctxt, xsltDocumentPtr doc, xsltInitCtxtKey(xsltTransformContextPtr ctxt, xsltDocumentPtr idoc,
xsltKeyDefPtr keyd) { xsltKeyDefPtr keyDef)
int i; {
xmlNodeSetPtr nodelist = NULL, keylist; int i, len, k;
xmlXPathObjectPtr res = NULL; xmlNodeSetPtr matchList = NULL, keylist;
xmlChar *str, **list; xmlXPathObjectPtr matchRes = NULL, useRes = NULL;
xmlChar *str = NULL;
xsltKeyTablePtr table; xsltKeyTablePtr table;
int oldPos, oldSize; xmlNodePtr oldInst, cur;
xmlNodePtr oldInst; xmlNodePtr oldContextNode;
xmlNodePtr oldNode; xsltDocumentPtr oldDocInfo;
xsltDocumentPtr oldDoc; int oldXPPos, oldXPSize;
xmlDocPtr oldXDoc; xmlDocPtr oldXPDoc;
int oldNsNr; int oldXPNsNr;
xmlNsPtr *oldNamespaces; xmlNsPtr *oldXPNamespaces;
xmlXPathContextPtr xpctxt;
doc->nbKeysComputed++; if ((keyDef->comp == NULL) || (keyDef->usecomp == NULL))
return(-1);
xpctxt = ctxt->xpathCtxt;
idoc->nbKeysComputed++;
/* /*
* Evaluate the nodelist * Save context state.
*/ */
oldXDoc= ctxt->xpathCtxt->doc;
oldPos = ctxt->xpathCtxt->proximityPosition;
oldSize = ctxt->xpathCtxt->contextSize;
oldNsNr = ctxt->xpathCtxt->nsNr;
oldNamespaces = ctxt->xpathCtxt->namespaces;
oldInst = ctxt->inst; oldInst = ctxt->inst;
oldDoc = ctxt->document; oldDocInfo = ctxt->document;
oldNode = ctxt->node; oldContextNode = ctxt->node;
if (keyd->comp == NULL) oldXPDoc = xpctxt->doc;
goto error; oldXPPos = xpctxt->proximityPosition;
if (keyd->usecomp == NULL) oldXPSize = xpctxt->contextSize;
goto error; oldXPNsNr = xpctxt->nsNr;
oldXPNamespaces = xpctxt->namespaces;
ctxt->document = doc; /*
ctxt->xpathCtxt->doc = doc->doc; * Set up contexts.
ctxt->xpathCtxt->node = (xmlNodePtr) doc->doc; */
ctxt->node = (xmlNodePtr) doc->doc; ctxt->document = idoc;
ctxt->node = (xmlNodePtr) idoc->doc;
ctxt->inst = keyDef->inst;
xpctxt->doc = idoc->doc;
xpctxt->node = (xmlNodePtr) idoc->doc;
/* TODO : clarify the use of namespaces in keys evaluation */ /* TODO : clarify the use of namespaces in keys evaluation */
ctxt->xpathCtxt->namespaces = keyd->nsList; xpctxt->namespaces = keyDef->nsList;
ctxt->xpathCtxt->nsNr = keyd->nsNr; xpctxt->nsNr = keyDef->nsNr;
ctxt->inst = keyd->inst;
res = xmlXPathCompiledEval(keyd->comp, ctxt->xpathCtxt); /*
ctxt->xpathCtxt->contextSize = oldSize; * Evaluate the 'match' expression of the xsl:key.
ctxt->xpathCtxt->proximityPosition = oldPos; * TODO: The 'match' is a *pattern*.
ctxt->inst = oldInst; */
matchRes = xmlXPathCompiledEval(keyDef->comp, xpctxt);
if (matchRes == NULL) {
if (res != NULL) {
if (res->type == XPATH_NODESET) {
nodelist = res->nodesetval;
#ifdef WITH_XSLT_DEBUG_KEYS #ifdef WITH_XSLT_DEBUG_KEYS
if (nodelist != NULL) XSLT_TRACE(ctxt,XSLT_TRACE_KEYS,xsltGenericDebug(xsltGenericDebugContext,
"xsltInitCtxtKey: %s evaluation failed\n", keyDef->match));
#endif
xsltTransformError(ctxt, NULL, keyDef->inst,
"Failed to evaluate the 'match' expression.\n");
ctxt->state = XSLT_STATE_STOPPED;
goto error;
} else {
if (matchRes->type == XPATH_NODESET) {
matchList = matchRes->nodesetval;
#ifdef WITH_XSLT_DEBUG_KEYS
if (matchList != NULL)
XSLT_TRACE(ctxt,XSLT_TRACE_KEYS,xsltGenericDebug(xsltGenericDebugContext, XSLT_TRACE(ctxt,XSLT_TRACE_KEYS,xsltGenericDebug(xsltGenericDebugContext,
"xsltInitCtxtKey: %s evaluates to %d nodes\n", "xsltInitCtxtKey: %s evaluates to %d nodes\n",
keyd->match, nodelist->nodeNr)); keyDef->match, matchList->nodeNr));
#endif #endif
} else { } else {
/*
* Is not a node set, but must be.
*/
#ifdef WITH_XSLT_DEBUG_KEYS #ifdef WITH_XSLT_DEBUG_KEYS
XSLT_TRACE(ctxt,XSLT_TRACE_KEYS,xsltGenericDebug(xsltGenericDebugContext, XSLT_TRACE(ctxt,XSLT_TRACE_KEYS,xsltGenericDebug(xsltGenericDebugContext,
"xsltInitCtxtKey: %s is not a node set\n", keyd->match)); "xsltInitCtxtKey: %s is not a node set\n", keyDef->match));
#endif
goto error;
}
} else {
#ifdef WITH_XSLT_DEBUG_KEYS
XSLT_TRACE(ctxt,XSLT_TRACE_KEYS,xsltGenericDebug(xsltGenericDebugContext,
"xsltInitCtxtKey: %s evaluation failed\n", keyd->match));
#endif #endif
xsltTransformError(ctxt, NULL, keyDef->inst,
"The 'match' expression did not evaluate to a node set.\n");
ctxt->state = XSLT_STATE_STOPPED; ctxt->state = XSLT_STATE_STOPPED;
goto error; goto error;
} }
}
/* if ((matchList == NULL) || (matchList->nodeNr <= 0))
* for each node in the list evaluate the key and insert the node goto exit;
*/
if ((nodelist == NULL) || (nodelist->nodeNr <= 0))
goto error;
/** /**
* Multiple key definitions for the same name are allowed, so * Multiple key definitions for the same name are allowed, so
* we must check if the key is already present for this doc * we must check if the key is already present for this doc
*/ */
table = (xsltKeyTablePtr) doc->keys; table = (xsltKeyTablePtr) idoc->keys;
while (table != NULL) { while (table != NULL) {
if (xmlStrEqual(table->name, keyd->name) && if (xmlStrEqual(table->name, keyDef->name) &&
(((keyd->nameURI == NULL) && (table->nameURI == NULL)) || (((keyDef->nameURI == NULL) && (table->nameURI == NULL)) ||
((keyd->nameURI != NULL) && (table->nameURI != NULL) && ((keyDef->nameURI != NULL) && (table->nameURI != NULL) &&
(xmlStrEqual(table->nameURI, keyd->nameURI))))) (xmlStrEqual(table->nameURI, keyDef->nameURI)))))
break; break;
table = table->next; table = table->next;
} }
@@ -653,112 +670,170 @@ xsltInitCtxtKey(xsltTransformContextPtr ctxt, xsltDocumentPtr doc,
* chain it to the list of keys for the doc * chain it to the list of keys for the doc
*/ */
if (table == NULL) { if (table == NULL) {
table = xsltNewKeyTable(keyd->name, keyd->nameURI); table = xsltNewKeyTable(keyDef->name, keyDef->nameURI);
if (table == NULL) if (table == NULL)
goto error; goto error;
table->next = doc->keys; table->next = idoc->keys;
doc->keys = table; idoc->keys = table;
} }
for (i = 0;i < nodelist->nodeNr;i++) { /*
if (IS_XSLT_REAL_NODE(nodelist->nodeTab[i])) { * SPEC XSLT 1.0 (XSLT 2.0 does not clarify the context size!)
ctxt->node = nodelist->nodeTab[i]; * "...the use attribute of the xsl:key element is evaluated with x as
" the current node and with a node list containing just x as the
* current node list"
*/
xpctxt->contextSize = 1;
xpctxt->proximityPosition = 1;
list = xsltEvalXPathKeys(ctxt, keyd->usecomp, keyd); for (i = 0; i < matchList->nodeNr; i++) {
if (list != NULL) { cur = matchList->nodeTab[i];
int ix = 0; if (! IS_XSLT_REAL_NODE(cur))
continue;
xpctxt->node = cur;
/*
* Process the 'use' of the xsl:key.
* SPEC XSLT 1.0:
* "The use attribute is an expression specifying the values of
* the key; the expression is evaluated once for each node that
* matches the pattern."
*/
if (useRes != NULL)
xmlXPathFreeObject(useRes);
useRes = xmlXPathCompiledEval(keyDef->usecomp, xpctxt);
if (useRes == NULL) {
xsltTransformError(ctxt, NULL, keyDef->inst,
"Failed to evaluate the 'use' expression.\n");
ctxt->state = XSLT_STATE_STOPPED;
break;
}
if (useRes->type == XPATH_NODESET) {
if ((useRes->nodesetval != NULL) &&
(useRes->nodesetval->nodeNr != 0))
{
len = useRes->nodesetval->nodeNr;
str = xmlXPathCastNodeToString(useRes->nodesetval->nodeTab[0]);
} else {
continue;
}
} else {
len = 1;
if (useRes->type == XPATH_STRING) {
/*
* Consume the string value.
*/
str = useRes->stringval;
useRes->stringval = NULL;
} else {
str = xmlXPathCastToString(useRes);
}
}
/*
* Process all strings.
*/
k = 0;
while (1) {
if (str == NULL)
goto next_string;
str = list[ix++];
while (str != NULL) {
#ifdef WITH_XSLT_DEBUG_KEYS #ifdef WITH_XSLT_DEBUG_KEYS
XSLT_TRACE(ctxt,XSLT_TRACE_KEYS,xsltGenericDebug(xsltGenericDebugContext, XSLT_TRACE(ctxt,XSLT_TRACE_KEYS,xsltGenericDebug(xsltGenericDebugContext,
"xsl:key : node associated to(%s,%s)\n", "xsl:key : node associated to ('%s', '%s')\n", keyDef->name, str));
keyd->name, str));
#endif #endif
keylist = xmlHashLookup(table->keys, str); keylist = xmlHashLookup(table->keys, str);
if (keylist == NULL) { if (keylist == NULL) {
keylist = xmlXPathNodeSetCreate(nodelist->nodeTab[i]); keylist = xmlXPathNodeSetCreate(cur);
if (keylist == NULL)
goto error;
xmlHashAddEntry(table->keys, str, keylist); xmlHashAddEntry(table->keys, str, keylist);
} else { } else {
xmlXPathNodeSetAdd(keylist, nodelist->nodeTab[i]); /*
* TODO: How do we know if this function failed?
*/
xmlXPathNodeSetAdd(keylist, cur);
} }
switch (nodelist->nodeTab[i]->type) { switch (cur->type) {
case XML_ELEMENT_NODE: case XML_ELEMENT_NODE:
case XML_TEXT_NODE: case XML_TEXT_NODE:
case XML_CDATA_SECTION_NODE: case XML_CDATA_SECTION_NODE:
case XML_PI_NODE: case XML_PI_NODE:
case XML_COMMENT_NODE: case XML_COMMENT_NODE:
nodelist->nodeTab[i]->psvi = keyd; cur->psvi = keyDef;
break; break;
case XML_ATTRIBUTE_NODE: { case XML_ATTRIBUTE_NODE:
xmlAttrPtr attr = (xmlAttrPtr) ((xmlAttrPtr) cur)->psvi = keyDef;
nodelist->nodeTab[i];
attr->psvi = keyd;
break; break;
}
case XML_DOCUMENT_NODE: case XML_DOCUMENT_NODE:
case XML_HTML_DOCUMENT_NODE: { case XML_HTML_DOCUMENT_NODE:
xmlDocPtr kdoc = (xmlDocPtr) ((xmlDocPtr) cur)->psvi = keyDef;
nodelist->nodeTab[i];
kdoc->psvi = keyd;
break; break;
}
default: default:
break; break;
} }
xmlFree(str); xmlFree(str);
str = list[ix++]; str = NULL;
}
xmlFree(list); next_string:
#ifdef WITH_XSLT_DEBUG_KEYS k++;
} else { if (k >= len)
XSLT_TRACE(ctxt,XSLT_TRACE_KEYS,xsltGenericDebug(xsltGenericDebugContext, break;
"xsl:key : use %s failed to return strings\n", str = xmlXPathCastNodeToString(useRes->nodesetval->nodeTab[k]);
keyd->use));
#endif
}
} }
} }
exit:
error: error:
ctxt->document = oldDoc; /*
ctxt->xpathCtxt->doc = oldXDoc; * Restore context state.
ctxt->xpathCtxt->nsNr = oldNsNr; */
ctxt->xpathCtxt->namespaces = oldNamespaces; xpctxt->doc = oldXPDoc;
ctxt->node = oldNode; xpctxt->nsNr = oldXPNsNr;
if (res != NULL) xpctxt->namespaces = oldXPNamespaces;
xmlXPathFreeObject(res); xpctxt->proximityPosition = oldXPPos;
xpctxt->contextSize = oldXPSize;
ctxt->node = oldContextNode;
ctxt->document = oldDocInfo;
ctxt->inst = oldInst;
if (str)
xmlFree(str);
if (useRes != NULL)
xmlXPathFreeObject(useRes);
if (matchRes != NULL)
xmlXPathFreeObject(matchRes);
return(0); return(0);
} }
/** /**
* xsltInitCtxtKeys: * xsltInitCtxtKeys:
* @ctxt: an XSLT transformation context * @ctxt: an XSLT transformation context
* @doc: an XSLT document * @idoc: a document info
* *
* Computes all the keys tables for the current input document. * Computes all the keys tables for the current input document.
* Should be done before global varibales are initialized. * Should be done before global varibales are initialized.
* NOTE: Not used anymore in the refactored code. * NOTE: Not used anymore in the refactored code.
*/ */
void void
xsltInitCtxtKeys(xsltTransformContextPtr ctxt, xsltDocumentPtr doc) { xsltInitCtxtKeys(xsltTransformContextPtr ctxt, xsltDocumentPtr idoc) {
xsltStylesheetPtr style; xsltStylesheetPtr style;
xsltKeyDefPtr keyd; xsltKeyDefPtr keyDef;
if ((ctxt == NULL) || (doc == NULL)) if ((ctxt == NULL) || (idoc == NULL))
return; return;
#ifdef WITH_XSLT_DEBUG_KEYS #ifdef WITH_XSLT_DEBUG_KEYS
if ((doc->doc != NULL) && (doc->doc->URL != NULL)) if ((idoc->doc != NULL) && (idoc->doc->URL != NULL))
XSLT_TRACE(ctxt,XSLT_TRACE_KEYS,xsltGenericDebug(xsltGenericDebugContext, "Initializing keys on %s\n", XSLT_TRACE(ctxt,XSLT_TRACE_KEYS,xsltGenericDebug(xsltGenericDebugContext, "Initializing keys on %s\n",
doc->doc->URL)); idoc->doc->URL));
#endif #endif
style = ctxt->style; style = ctxt->style;
while (style != NULL) { while (style != NULL) {
keyd = (xsltKeyDefPtr) style->keys; keyDef = (xsltKeyDefPtr) style->keys;
while (keyd != NULL) { while (keyDef != NULL) {
xsltInitCtxtKey(ctxt, doc, keyd); xsltInitCtxtKey(ctxt, idoc, keyDef);
keyd = keyd->next; keyDef = keyDef->next;
} }
style = xsltNextImport(style); style = xsltNextImport(style);
@@ -772,8 +847,8 @@ xsltInitCtxtKeys(xsltTransformContextPtr ctxt, xsltDocumentPtr doc) {
* Free the keys associated to a document * Free the keys associated to a document
*/ */
void void
xsltFreeDocumentKeys(xsltDocumentPtr doc) { xsltFreeDocumentKeys(xsltDocumentPtr idoc) {
if (doc != NULL) if (idoc != NULL)
xsltFreeKeyTableList(doc->keys); xsltFreeKeyTableList(idoc->keys);
} }

View File

@@ -44,8 +44,6 @@
#include "namespaces.h" #include "namespaces.h"
#include "imports.h" #include "imports.h"
/************************************************************************ /************************************************************************
* * * *
* Module interfaces * * Module interfaces *
@@ -617,7 +615,7 @@ declare_new_prefix:
* - xsltCopyPropList() (*not* anymore) * - xsltCopyPropList() (*not* anymore)
* - xsltShallowCopyElement() * - xsltShallowCopyElement()
* - xsltCopyTreeInternal() (*not* anymore) * - xsltCopyTreeInternal() (*not* anymore)
* - xsltApplyOneTemplateInt() (*not* in the refactored code), * - xsltApplySequenceConstructor() (*not* in the refactored code),
* - xsltElement() (*not* anymore) * - xsltElement() (*not* anymore)
* *
* Returns a namespace declaration or NULL in case of * Returns a namespace declaration or NULL in case of

View File

@@ -32,6 +32,7 @@
#include "templates.h" #include "templates.h"
#include "keys.h" #include "keys.h"
#include "pattern.h" #include "pattern.h"
#include "documents.h"
#ifdef WITH_XSLT_DEBUG #ifdef WITH_XSLT_DEBUG
#define WITH_XSLT_DEBUG_PATTERN #define WITH_XSLT_DEBUG_PATTERN
@@ -858,7 +859,8 @@ restart:
(previous->name != NULL) && (previous->name != NULL) &&
(sibling->name != NULL) && (sibling->name != NULL) &&
(previous->name[0] == sibling->name[0]) && (previous->name[0] == sibling->name[0]) &&
(xmlStrEqual(previous->name, sibling->name))) { (xmlStrEqual(previous->name, sibling->name)))
{
if ((sel->value2 == NULL) || if ((sel->value2 == NULL) ||
((sibling->ns != NULL) && ((sibling->ns != NULL) &&
(xmlStrEqual(sel->value2, (xmlStrEqual(sel->value2,
@@ -874,11 +876,20 @@ restart:
while (sibling != NULL) { while (sibling != NULL) {
if (sibling == previous) if (sibling == previous)
break; break;
if ((previous->type == XML_ELEMENT_NODE) &&
(previous->name != NULL) &&
(sibling->name != NULL) &&
(previous->name[0] == sibling->name[0]) &&
(xmlStrEqual(previous->name, sibling->name)))
{
if ((sel->value2 == NULL) || if ((sel->value2 == NULL) ||
((sibling->ns != NULL) && ((sibling->ns != NULL) &&
(xmlStrEqual(sel->value2, (xmlStrEqual(sel->value2,
sibling->ns->href)))) sibling->ns->href))))
{
indx--; indx--;
}
}
sibling = sibling->next; sibling = sibling->next;
} }
} }
@@ -2211,17 +2222,34 @@ xsltAddTemplate(xsltStylesheetPtr style, xsltTemplatePtr cur,
#ifdef XSLT_REFACTORED_KEYCOMP #ifdef XSLT_REFACTORED_KEYCOMP
static int static int
xsltComputeAllKeys(xsltTransformContextPtr ctxt, xsltComputeAllKeys(xsltTransformContextPtr ctxt, xmlNodePtr contextNode)
xsltDocumentPtr document)
{ {
xsltStylesheetPtr style, style2; xsltStylesheetPtr style, style2;
xsltKeyDefPtr keyd, keyd2; xsltKeyDefPtr keyd, keyd2;
xsltKeyTablePtr table; xsltKeyTablePtr table;
if ((ctxt == NULL) || (document == NULL)) if ((ctxt == NULL) || (contextNode == NULL)) {
xsltTransformError(ctxt, NULL, ctxt->inst,
"Internal error in xsltComputeAllKeys(): "
"Bad arguments.\n");
return(-1); return(-1);
}
if (document->nbKeysComputed == ctxt->nbKeys) if (ctxt->document == NULL) {
/*
* The document info will only be NULL if we have a RTF.
*/
if (contextNode->doc->_private != NULL)
goto doc_info_mismatch;
/*
* On-demand creation of the document info (needed for keys).
*/
ctxt->document = xsltNewDocument(ctxt, contextNode->doc);
if (ctxt->document == NULL)
return(-1);
}
if (ctxt->document->nbKeysComputed == ctxt->nbKeys)
return(0); return(0);
/* /*
* TODO: This could be further optimized * TODO: This could be further optimized
@@ -2234,7 +2262,7 @@ xsltComputeAllKeys(xsltTransformContextPtr ctxt,
* Check if keys with this QName have been already * Check if keys with this QName have been already
* computed. * computed.
*/ */
table = (xsltKeyTablePtr) document->keys; table = (xsltKeyTablePtr) ctxt->document->keys;
while (table) { while (table) {
if (((keyd->nameURI != NULL) == (table->nameURI != NULL)) && if (((keyd->nameURI != NULL) == (table->nameURI != NULL)) &&
xmlStrEqual(keyd->name, table->name) && xmlStrEqual(keyd->name, table->name) &&
@@ -2257,8 +2285,8 @@ xsltComputeAllKeys(xsltTransformContextPtr ctxt,
xmlStrEqual(keyd2->name, keyd->name) && xmlStrEqual(keyd2->name, keyd->name) &&
xmlStrEqual(keyd2->nameURI, keyd->nameURI)) xmlStrEqual(keyd2->nameURI, keyd->nameURI))
{ {
xsltInitCtxtKey(ctxt, document, keyd2); xsltInitCtxtKey(ctxt, ctxt->document, keyd2);
if (document->nbKeysComputed == ctxt->nbKeys) if (ctxt->document->nbKeysComputed == ctxt->nbKeys)
return(0); return(0);
} }
keyd2 = keyd2->next; keyd2 = keyd2->next;
@@ -2271,6 +2299,14 @@ xsltComputeAllKeys(xsltTransformContextPtr ctxt,
style = xsltNextImport(style); style = xsltNextImport(style);
} }
return(0); return(0);
doc_info_mismatch:
xsltTransformError(ctxt, NULL, ctxt->inst,
"Internal error in xsltComputeAllKeys(): "
"The context's document info doesn't match the "
"document info of the current result tree.\n");
ctxt->state = XSLT_STATE_STOPPED;
return(-1);
} }
#endif #endif
@@ -2287,7 +2323,8 @@ xsltComputeAllKeys(xsltTransformContextPtr ctxt,
*/ */
xsltTemplatePtr xsltTemplatePtr
xsltGetTemplate(xsltTransformContextPtr ctxt, xmlNodePtr node, xsltGetTemplate(xsltTransformContextPtr ctxt, xmlNodePtr node,
xsltStylesheetPtr style) { xsltStylesheetPtr style)
{
xsltStylesheetPtr curstyle; xsltStylesheetPtr curstyle;
xsltTemplatePtr ret = NULL; xsltTemplatePtr ret = NULL;
const xmlChar *name = NULL; const xmlChar *name = NULL;
@@ -2478,14 +2515,16 @@ keyed_match:
} }
#ifdef XSLT_REFACTORED_KEYCOMP #ifdef XSLT_REFACTORED_KEYCOMP
else if (ctxt->hasTemplKeyPatterns && else if (ctxt->hasTemplKeyPatterns &&
(ctxt->document->nbKeysComputed < ctxt->nbKeys)) ((ctxt->document == NULL) ||
(ctxt->document->nbKeysComputed < ctxt->nbKeys)))
{ {
/* /*
* Compute all remaining keys for this document. * Compute all remaining keys for this document.
* *
* REVISIT TODO: I think this could be further optimized. * REVISIT TODO: I think this could be further optimized.
*/ */
xsltComputeAllKeys(ctxt, ctxt->document); if (xsltComputeAllKeys(ctxt, node) == -1)
goto error;
switch (node->type) { switch (node->type) {
case XML_ELEMENT_NODE: case XML_ELEMENT_NODE:
@@ -2519,6 +2558,8 @@ keyed_match:
*/ */
curstyle = xsltNextImport(curstyle); curstyle = xsltNextImport(curstyle);
} }
error:
return(NULL); return(NULL);
} }

View File

@@ -1007,7 +1007,7 @@ xsltAttributeComp(xsltStylesheetPtr style, xmlNodePtr inst) {
NULL, &comp->has_name); NULL, &comp->has_name);
if (! comp->has_name) { if (! comp->has_name) {
xsltTransformError(NULL, style, inst, xsltTransformError(NULL, style, inst,
"xsl:attribute: The attribute 'name' is missing.\n"); "XSLT-attribute: The attribute 'name' is missing.\n");
style->errors++; style->errors++;
return; return;
} }
@@ -1303,6 +1303,9 @@ xsltWithParamComp(xsltStylesheetPtr style, xmlNodePtr inst) {
} else { } else {
const xmlChar *URI; const xmlChar *URI;
/*
* @prop will be in the string dict afterwards, @URI not.
*/
URI = xsltGetQNameURI2(style, inst, &prop); URI = xsltGetQNameURI2(style, inst, &prop);
if (prop == NULL) { if (prop == NULL) {
if (style != NULL) style->errors++; if (style != NULL) style->errors++;
@@ -1310,7 +1313,12 @@ xsltWithParamComp(xsltStylesheetPtr style, xmlNodePtr inst) {
comp->name = prop; comp->name = prop;
comp->has_name = 1; comp->has_name = 1;
if (URI != NULL) { if (URI != NULL) {
comp->ns = xmlStrdup(URI); /*
* Fixes bug #308441: Put the ns-name in the dict
* in order to pointer compare names during XPath's
* variable lookup.
*/
comp->ns = xmlDictLookup(style->dict, URI, -1);
comp->has_ns = 1; comp->has_ns = 1;
} else { } else {
comp->has_ns = 0; comp->has_ns = 0;
@@ -1530,7 +1538,7 @@ xsltCallTemplateComp(xsltStylesheetPtr style, xmlNodePtr inst) {
comp->name = prop; comp->name = prop;
comp->has_name = 1; comp->has_name = 1;
if (URI != NULL) { if (URI != NULL) {
comp->ns = URI; comp->ns = xmlDictLookup(style->dict, URI, -1);
comp->has_ns = 1; comp->has_ns = 1;
} else { } else {
comp->has_ns = 0; comp->has_ns = 0;
@@ -1791,6 +1799,7 @@ xsltVariableComp(xsltStylesheetPtr style, xmlNodePtr inst) {
xsltStylePreCompPtr comp; xsltStylePreCompPtr comp;
#endif #endif
const xmlChar *prop; const xmlChar *prop;
const xmlChar *URI;
if ((style == NULL) || (inst == NULL)) if ((style == NULL) || (inst == NULL))
return; return;
@@ -1804,51 +1813,65 @@ xsltVariableComp(xsltStylesheetPtr style, xmlNodePtr inst) {
if (comp == NULL) if (comp == NULL)
return; return;
inst->psvi = comp; inst->psvi = comp;
comp->inst = inst; comp->inst = inst;
/* /*
* The full template resolution can be done statically * The full template resolution can be done statically
*/ */
/*
* Attribute "name".
*/
prop = xsltGetCNsProp(style, inst, (const xmlChar *)"name", XSLT_NAMESPACE); prop = xsltGetCNsProp(style, inst, (const xmlChar *)"name", XSLT_NAMESPACE);
if (prop == NULL) { if (prop == NULL) {
xsltTransformError(NULL, style, inst, xsltTransformError(NULL, style, inst,
"xsl:variable : name is missing\n"); "XSLT-variable: The attribute 'name' is missing.\n");
if (style != NULL) style->errors++; style->errors++;
} else { goto error;
const xmlChar *URI; }
if (xmlValidateQName(prop, 0)) {
xsltTransformError(NULL, style, inst,
"XSLT-variable: The value '%s' of the attribute 'name' is "
"not a valid QName.\n", prop);
style->errors++;
goto error;
}
URI = xsltGetQNameURI2(style, inst, &prop); URI = xsltGetQNameURI2(style, inst, &prop);
if (prop == NULL) { if (prop == NULL) {
if (style != NULL) style->errors++; goto error;
} else { }
comp->name = prop; comp->name = prop;
comp->has_name = 1; comp->has_name = 1;
if (URI != NULL) { if (URI != NULL) {
comp->ns = xmlDictLookup(style->dict, URI, -1); comp->ns = xmlDictLookup(style->dict, URI, -1);
comp->has_ns = 1; comp->has_ns = 1;
} else { } else {
comp->has_ns = 0; comp->has_ns = 0;
} }
} /*
} * Attribute "select".
*/
comp->select = xsltGetCNsProp(style, inst, (const xmlChar *)"select", comp->select = xsltGetCNsProp(style, inst, (const xmlChar *)"select",
XSLT_NAMESPACE); XSLT_NAMESPACE);
if (comp->select != NULL) { if (comp->select != NULL) {
comp->comp = xsltXPathCompile(style, comp->select); comp->comp = xsltXPathCompile(style, comp->select);
if (comp->comp == NULL) { if (comp->comp == NULL) {
xsltTransformError(NULL, style, inst, xsltTransformError(NULL, style, inst,
"xsl:variable : could not compile select expression '%s'\n", "XSLT-variable: Failed to compile the XPath expression '%s'.\n",
comp->select); comp->select);
if (style != NULL) style->errors++; style->errors++;
} }
if (inst->children != NULL) { if (inst->children != NULL) {
xsltTransformError(NULL, style, inst, xsltTransformError(NULL, style, inst,
"xsl:variable : content should be empty since select is present \n"); "XSLT-variable: The must be no child nodes, since the "
if (style != NULL) style->warnings++; "attribute 'select' was specified.\n");
style->errors++;
} }
} }
error:
return;
} }
/** /**
@@ -1900,7 +1923,7 @@ xsltParamComp(xsltStylesheetPtr style, xmlNodePtr inst) {
comp->name = prop; comp->name = prop;
comp->has_name = 1; comp->has_name = 1;
if (URI != NULL) { if (URI != NULL) {
comp->ns = xmlStrdup(URI); comp->ns = xmlDictLookup(style->dict, URI, -1);
comp->has_ns = 1; comp->has_ns = 1;
} else { } else {
comp->has_ns = 0; comp->has_ns = 0;

View File

@@ -180,28 +180,28 @@ xsltEvalXPathString(xsltTransformContextPtr ctxt, xmlXPathCompExprPtr comp) {
/** /**
* xsltEvalTemplateString: * xsltEvalTemplateString:
* @ctxt: the XSLT transformation context * @ctxt: the XSLT transformation context
* @currentNode: the current node in the source tree * @contextNode: the current node in the source tree
* @parent: the content parent * @inst: the XSLT instruction (xsl:comment, xsl:processing-instruction)
* *
* Evaluate a template string value, i.e. the parent list is interpreted * Processes the sequence constructor of the given instruction on
* as template content and the resulting tree string value is returned * @contextNode and converts the resulting tree to a string.
* This is needed for example by xsl:comment and xsl:processing-instruction * This is needed by e.g. xsl:comment and xsl:processing-instruction.
* *
* Returns the computed string value or NULL, must be deallocated by the * Returns the computed string value or NULL; it's up to the caller to
* caller. * free the result.
*/ */
xmlChar * xmlChar *
xsltEvalTemplateString(xsltTransformContextPtr ctxt, xsltEvalTemplateString(xsltTransformContextPtr ctxt,
xmlNodePtr currentNode, xmlNodePtr contextNode,
xmlNodePtr parent) xmlNodePtr inst)
{ {
xmlNodePtr oldInsert, insert = NULL; xmlNodePtr oldInsert, insert = NULL;
xmlChar *ret; xmlChar *ret;
if ((ctxt == NULL) || (currentNode == NULL) || (parent == NULL)) if ((ctxt == NULL) || (contextNode == NULL) || (inst == NULL))
return(NULL); return(NULL);
if (parent->children == NULL) if (inst->children == NULL)
return(NULL); return(NULL);
/* /*
@@ -213,14 +213,16 @@ xsltEvalTemplateString(xsltTransformContextPtr ctxt,
insert = xmlNewDocNode(ctxt->output, NULL, insert = xmlNewDocNode(ctxt->output, NULL,
(const xmlChar *)"fake", NULL); (const xmlChar *)"fake", NULL);
if (insert == NULL) { if (insert == NULL) {
xsltTransformError(ctxt, NULL, currentNode, xsltTransformError(ctxt, NULL, contextNode,
"Failed to create temporary node\n"); "Failed to create temporary node\n");
return(NULL); return(NULL);
} }
oldInsert = ctxt->insert; oldInsert = ctxt->insert;
ctxt->insert = insert; ctxt->insert = insert;
/* OPTIMIZE TODO: if parent->children consists only of text-nodes. */ /*
xsltApplyOneTemplate(ctxt, currentNode, parent->children, NULL, NULL); * OPTIMIZE TODO: if inst->children consists only of text-nodes.
*/
xsltApplyOneTemplate(ctxt, contextNode, inst->children, NULL, NULL);
ctxt->insert = oldInsert; ctxt->insert = oldInsert;
@@ -603,7 +605,7 @@ xsltAttrTemplateProcess(xsltTransformContextPtr ctxt, xmlNodePtr target,
* Copies all non XSLT-attributes over to the @target element * Copies all non XSLT-attributes over to the @target element
* and evaluates Attribute Value Templates. * and evaluates Attribute Value Templates.
* *
* Called by xsltApplyOneTemplateInt() (transform.c). * Called by xsltApplySequenceConstructor() (transform.c).
* *
* Returns a new list of attribute nodes, or NULL in case of error. * Returns a new list of attribute nodes, or NULL in case of error.
* (Don't assign the result to @target->properties; if * (Don't assign the result to @target->properties; if

View File

@@ -27,8 +27,8 @@ XSLTPUBFUN int XSLTCALL
int nsNr); int nsNr);
XSLTPUBFUN xmlChar * XSLTCALL XSLTPUBFUN xmlChar * XSLTCALL
xsltEvalTemplateString (xsltTransformContextPtr ctxt, xsltEvalTemplateString (xsltTransformContextPtr ctxt,
xmlNodePtr node, xmlNodePtr contextNode,
xmlNodePtr parent); xmlNodePtr inst);
XSLTPUBFUN xmlChar * XSLTCALL XSLTPUBFUN xmlChar * XSLTCALL
xsltEvalAttrValueTemplate (xsltTransformContextPtr ctxt, xsltEvalAttrValueTemplate (xsltTransformContextPtr ctxt,
xmlNodePtr node, xmlNodePtr node,

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -485,6 +485,24 @@ xsltNewNamespaceMapItem(xsltCompilerCtxtPtr cctxt,
} }
#endif /* XSLT_REFACTORED_XSLT_NSCOMP */ #endif /* XSLT_REFACTORED_XSLT_NSCOMP */
/**
* xsltCompilerVarInfoFree:
* @cctxt: the compilation context
*
* Frees the list of information for vars/params.
*/
static void
xsltCompilerVarInfoFree(xsltCompilerCtxtPtr cctxt)
{
xsltVarInfoPtr ivar = cctxt->ivars, ivartmp;
while (ivar) {
ivartmp = ivar;
ivar = ivar->next;
xmlFree(ivartmp);
}
}
/** /**
* xsltCompilerCtxtFree: * xsltCompilerCtxtFree:
* *
@@ -523,6 +541,9 @@ xsltCompilationCtxtFree(xsltCompilerCtxtPtr cctxt)
if (cctxt->nsAliases != NULL) if (cctxt->nsAliases != NULL)
xsltFreeNsAliasList(cctxt->nsAliases); xsltFreeNsAliasList(cctxt->nsAliases);
if (cctxt->ivars)
xsltCompilerVarInfoFree(cctxt);
xmlFree(cctxt); xmlFree(cctxt);
} }
@@ -1024,16 +1045,6 @@ xsltGetInheritedNsList(xsltStylesheetPtr style,
int maxns = 10; int maxns = 10;
int i; int i;
/*
* TODO: This will gather the ns-decls of elements even if
* outside xsl:stylesheet. Example:
* <doc xmlns:foo="urn:test:foo">
* <xsl:stylesheet ...
* </doc>
* Will have foo="urn:test:foo" in the list.
* Is this OK?
*/
if ((style == NULL) || (template == NULL) || (node == NULL) || if ((style == NULL) || (template == NULL) || (node == NULL) ||
(template->inheritedNsNr != 0) || (template->inheritedNs != NULL)) (template->inheritedNsNr != 0) || (template->inheritedNs != NULL))
return(0); return(0);
@@ -2027,6 +2038,63 @@ xsltLREBuildEffectiveNsNodes(xsltCompilerCtxtPtr cctxt,
if (xmlStrEqual(ns->href, XSLT_NAMESPACE)) if (xmlStrEqual(ns->href, XSLT_NAMESPACE))
goto skip_ns; goto skip_ns;
/*
* Apply namespace aliasing
* ------------------------
*
* SPEC XSLT 2.0
* "- A namespace node whose string value is a literal namespace
* URI is not copied to the result tree.
* - A namespace node whose string value is a target namespace URI
* is copied to the result tree, whether or not the URI
* identifies an excluded namespace."
*
* NOTE: The ns-aliasing machanism is non-cascading.
* (checked with Saxon, Xalan and MSXML .NET).
* URGENT TODO: is style->nsAliases the effective list of
* ns-aliases, or do we need to lookup the whole
* import-tree?
* TODO: Get rid of import-tree lookup.
*/
if (cctxt->hasNsAliases) {
xsltNsAliasPtr alias;
/*
* First check for being a target namespace.
*/
alias = cctxt->nsAliases;
do {
/*
* TODO: Is xmlns="" handled already?
*/
if ((alias->targetNs != NULL) &&
(xmlStrEqual(alias->targetNs->href, ns->href)))
{
/*
* Recognized as a target namespace; use it regardless
* if excluded otherwise.
*/
goto add_effective_ns;
}
alias = alias->next;
} while (alias != NULL);
alias = cctxt->nsAliases;
do {
/*
* TODO: Is xmlns="" handled already?
*/
if ((alias->literalNs != NULL) &&
(xmlStrEqual(alias->literalNs->href, ns->href)))
{
/*
* Recognized as an namespace alias; do not use it.
*/
goto skip_ns;
}
alias = alias->next;
} while (alias != NULL);
}
/* /*
* Exclude excluded result namespaces. * Exclude excluded result namespaces.
*/ */
@@ -2043,6 +2111,8 @@ xsltLREBuildEffectiveNsNodes(xsltCompilerCtxtPtr cctxt,
if (xmlStrEqual(ns->href, BAD_CAST extElemNs->items[j])) if (xmlStrEqual(ns->href, BAD_CAST extElemNs->items[j]))
goto skip_ns; goto skip_ns;
} }
add_effective_ns:
/* /*
* OPTIMIZE TODO: This information may not be needed. * OPTIMIZE TODO: This information may not be needed.
*/ */
@@ -2058,43 +2128,7 @@ xsltLREBuildEffectiveNsNodes(xsltCompilerCtxtPtr cctxt,
} while (tmpns != NULL); } while (tmpns != NULL);
} else } else
holdByElem = 0; holdByElem = 0;
/*
* Apply namespace aliasing
* ------------------------
*
* NOTE: The ns-aliasing machanism is non-cascading.
* (checked with Saxon, Xalan and MSXML .NET).
* URGENT TODO: is style->nsAliases the effective list of
* ns-aliases, or do we need to lookup the whole
* import-tree?
* TODO: Get rid of import-tree lookup.
*/
if (cctxt->hasNsAliases) {
xsltNsAliasPtr alias = cctxt->nsAliases;
do {
/*
* TODO: What to do with xmlns="" ?
*/
if ((alias->literalNs != NULL) &&
(xmlStrEqual(alias->literalNs->href, ns->href)))
{
/*
* Recognized as an namespace alias; convert it to
* the target namespace.
*/
if (alias->targetNs != NULL)
ns = alias->literalNs;
else {
/*
* The target is the NULL namespace.
*/
goto skip_ns;
}
break;
}
alias = alias->next;
} while (alias != NULL);
}
/* /*
* Add the effective namespace declaration. * Add the effective namespace declaration.
@@ -2181,6 +2215,69 @@ xsltLREInfoCreate(xsltCompilerCtxtPtr cctxt,
return(0); return(0);
} }
/**
* xsltCompilerVarInfoPush:
* @cctxt: the compilation context
*
* Pushes a new var/param info onto the stack.
*
* Returns the acquired variable info.
*/
static xsltVarInfoPtr
xsltCompilerVarInfoPush(xsltCompilerCtxtPtr cctxt,
xmlNodePtr inst,
const xmlChar *name,
const xmlChar *nsName)
{
xsltVarInfoPtr ivar;
if ((cctxt->ivar != NULL) && (cctxt->ivar->next != NULL)) {
ivar = cctxt->ivar->next;
} else if ((cctxt->ivar == NULL) && (cctxt->ivars != NULL)) {
ivar = cctxt->ivars;
} else {
ivar = (xsltVarInfoPtr) xmlMalloc(sizeof(xsltVarInfo));
if (ivar == NULL) {
xsltTransformError(NULL, cctxt->style, inst,
"xsltParseInScopeVarPush: xmlMalloc() failed!\n");
cctxt->style->errors++;
return(NULL);
}
/* memset(retVar, 0, sizeof(xsltInScopeVar)); */
if (cctxt->ivars == NULL) {
cctxt->ivars = ivar;
ivar->prev = NULL;
} else {
cctxt->ivar->next = ivar;
ivar->prev = cctxt->ivar;
}
cctxt->ivar = ivar;
ivar->next = NULL;
}
ivar->depth = cctxt->depth;
ivar->name = name;
ivar->nsName = nsName;
return(ivar);
}
/**
* xsltCompilerVarInfoPop:
* @cctxt: the compilation context
*
* Pops all var/param infos from the stack, which
* have the current depth.
*/
static void
xsltCompilerVarInfoPop(xsltCompilerCtxtPtr cctxt)
{
while ((cctxt->ivar != NULL) &&
(cctxt->ivar->depth > cctxt->depth))
{
cctxt->ivar = cctxt->ivar->prev;
}
}
/* /*
* xsltCompilerNodePush: * xsltCompilerNodePush:
* *
@@ -2316,6 +2413,12 @@ xsltCompilerNodePop(xsltCompilerCtxtPtr cctxt, xmlNodePtr node)
"xsltCompilerNodePop: Depth mismatch.\n"); "xsltCompilerNodePop: Depth mismatch.\n");
goto mismatch; goto mismatch;
} }
/*
* Pop information of variables.
*/
if ((cctxt->ivar) && (cctxt->ivar->depth > cctxt->depth))
xsltCompilerVarInfoPop(cctxt);
cctxt->depth--; cctxt->depth--;
cctxt->inode = cctxt->inode->prev; cctxt->inode = cctxt->inode->prev;
if (cctxt->inode != NULL) if (cctxt->inode != NULL)
@@ -2421,7 +2524,7 @@ xsltCompilerBuildInScopeNsList(xsltCompilerCtxtPtr cctxt, xmlNodePtr node)
break; break;
} }
if (i >= nsi->totalNumber) { if (i >= nsi->totalNumber) {
if (nsi->totalNumber >= maxns) { if (nsi->totalNumber +1 >= maxns) {
maxns *= 2; maxns *= 2;
nsi->list = nsi->list =
(xmlNsPtr *) xmlRealloc(nsi->list, (xmlNsPtr *) xmlRealloc(nsi->list,
@@ -3684,6 +3787,17 @@ xsltGetXSLTElementTypeByNode(xsltCompilerCtxtPtr cctxt,
return(0); return(0);
} }
/**
* xsltParseAnyXSLTElem:
*
* @cctxt: the compilation context
* @elem: the element node of the XSLT instruction
*
* Parses, validates the content models and compiles XSLT instructions.
*
* Returns 0 if everything's fine;
* -1 on API or internal errors.
*/
int int
xsltParseAnyXSLTElem(xsltCompilerCtxtPtr cctxt, xmlNodePtr elem) xsltParseAnyXSLTElem(xsltCompilerCtxtPtr cctxt, xmlNodePtr elem)
{ {
@@ -3718,12 +3832,16 @@ xsltParseAnyXSLTElem(xsltCompilerCtxtPtr cctxt, xmlNodePtr elem)
cctxt->inode->inScopeNs = cctxt->inode->inScopeNs =
xsltCompilerBuildInScopeNsList(cctxt, elem); xsltCompilerBuildInScopeNsList(cctxt, elem);
/* /*
* xsltStylePreCompute(): Precompute the XSLT-instruction. * xsltStylePreCompute():
* This will compile the information found on the current * This will compile the information found on the current
* element's attributes. NOTE that this won't process the * element's attributes. NOTE that this won't process the
* children of the current element. * children of the instruction.
*/ */
xsltStylePreCompute(cctxt->style, elem); xsltStylePreCompute(cctxt->style, elem);
/*
* TODO: How to react on errors in xsltStylePreCompute() ?
*/
/* /*
* Validate the content model of the XSLT-element. * Validate the content model of the XSLT-element.
*/ */
@@ -3777,6 +3895,28 @@ xsltParseAnyXSLTElem(xsltCompilerCtxtPtr cctxt, xmlNodePtr elem)
/* EMPTY */ /* EMPTY */
goto empty_content; goto empty_content;
case XSLT_FUNC_PARAM: case XSLT_FUNC_PARAM:
/*
* Check for redefinition.
*/
if ((elem->psvi != NULL) && (cctxt->ivar != NULL)) {
xsltVarInfoPtr ivar = cctxt->ivar;
do {
if ((ivar->name ==
((xsltStyleItemParamPtr) elem->psvi)->name) &&
(ivar->nsName ==
((xsltStyleItemParamPtr) elem->psvi)->ns))
{
elem->psvi = NULL;
xsltTransformError(NULL, cctxt->style, elem,
"Redefinition of variable or parameter '%s'.\n",
ivar->name);
cctxt->style->errors++;
goto error;
}
ivar = ivar->prev;
} while (ivar != NULL);
}
/* <!-- Content: template --> */ /* <!-- Content: template --> */
goto sequence_constructor; goto sequence_constructor;
case XSLT_FUNC_PI: case XSLT_FUNC_PI:
@@ -3792,6 +3932,28 @@ xsltParseAnyXSLTElem(xsltCompilerCtxtPtr cctxt, xmlNodePtr elem)
/* EMPTY */ /* EMPTY */
goto empty_content; goto empty_content;
case XSLT_FUNC_VARIABLE: case XSLT_FUNC_VARIABLE:
/*
* Check for redefinition.
*/
if ((elem->psvi != NULL) && (cctxt->ivar != NULL)) {
xsltVarInfoPtr ivar = cctxt->ivar;
do {
if ((ivar->name ==
((xsltStyleItemVariablePtr) elem->psvi)->name) &&
(ivar->nsName ==
((xsltStyleItemVariablePtr) elem->psvi)->ns))
{
elem->psvi = NULL;
xsltTransformError(NULL, cctxt->style, elem,
"Redefinition of variable or parameter '%s'.\n",
ivar->name);
cctxt->style->errors++;
goto error;
}
ivar = ivar->prev;
} while (ivar != NULL);
}
/* <!-- Content: template --> */ /* <!-- Content: template --> */
goto sequence_constructor; goto sequence_constructor;
case XSLT_FUNC_WHEN: case XSLT_FUNC_WHEN:
@@ -3998,9 +4160,30 @@ for_each:
goto exit; goto exit;
sequence_constructor: sequence_constructor:
/*
* Parse the sequence constructor.
*/
if (elem->children != NULL) if (elem->children != NULL)
xsltParseSequenceConstructor(cctxt, elem->children); xsltParseSequenceConstructor(cctxt, elem->children);
/*
* Register information for vars/params. Only needed if there
* are any following siblings.
*/
if ((elem->next != NULL) &&
((cctxt->inode->type == XSLT_FUNC_VARIABLE) ||
(cctxt->inode->type == XSLT_FUNC_PARAM)))
{
if ((elem->psvi != NULL) &&
(((xsltStyleBasicItemVariablePtr) elem->psvi)->name))
{
xsltCompilerVarInfoPush(cctxt, elem,
((xsltStyleBasicItemVariablePtr) elem->psvi)->name,
((xsltStyleBasicItemVariablePtr) elem->psvi)->ns);
}
}
error:
exit: exit:
xsltCompilerNodePop(cctxt, elem); xsltCompilerNodePop(cctxt, elem);
return(0); return(0);
@@ -4010,6 +4193,16 @@ internal_err:
return(-1); return(-1);
} }
/**
* xsltForwardsCompatUnkownItemCreate:
*
* @cctxt: the compilation context
*
* Creates a compiled representation of the unknown
* XSLT instruction.
*
* Returns the compiled representation.
*/
static xsltStyleItemUknownPtr static xsltStyleItemUknownPtr
xsltForwardsCompatUnkownItemCreate(xsltCompilerCtxtPtr cctxt) xsltForwardsCompatUnkownItemCreate(xsltCompilerCtxtPtr cctxt)
{ {
@@ -4033,6 +4226,20 @@ xsltForwardsCompatUnkownItemCreate(xsltCompilerCtxtPtr cctxt)
return(item); return(item);
} }
/**
* xsltParseUnknownXSLTElem:
*
* @cctxt: the compilation context
* @node: the element of the unknown XSLT instruction
*
* Parses an unknown XSLT element.
* If forwards compatible mode is enabled this will allow
* such an unknown XSLT and; otherwise it is rejected.
*
* Returns 1 in the unknown XSLT instruction is rejected,
* 0 if everything's fine and
* -1 on API or internal errors.
*/
static int static int
xsltParseUnknownXSLTElem(xsltCompilerCtxtPtr cctxt, xsltParseUnknownXSLTElem(xsltCompilerCtxtPtr cctxt,
xmlNodePtr node) xmlNodePtr node)
@@ -4053,7 +4260,7 @@ xsltParseUnknownXSLTElem(xsltCompilerCtxtPtr cctxt,
xsltTransformError(NULL, cctxt->style, node, xsltTransformError(NULL, cctxt->style, node,
"Unknown XSLT element '%s'.\n", node->name); "Unknown XSLT element '%s'.\n", node->name);
cctxt->style->errors++; cctxt->style->errors++;
return(0); return(1);
} }
/* /*
* Forwards-compatible mode. * Forwards-compatible mode.
@@ -4106,6 +4313,7 @@ xsltParseUnknownXSLTElem(xsltCompilerCtxtPtr cctxt,
} }
return(0); return(0);
} }
/** /**
* xsltParseSequenceConstructor: * xsltParseSequenceConstructor:
* *
@@ -4374,10 +4582,12 @@ xsltParseSequenceConstructor(xsltCompilerCtxtPtr cctxt, xmlNodePtr cur)
if (cur->psvi == NULL) { if (cur->psvi == NULL) {
/* /*
* OLD COMMENT: "Unknown element, maybe registered at the * OLD COMMENT: "Unknown element, maybe registered
* context level. Mark it for later recognition." * at the context level. Mark it for later
* recognition."
* QUESTION: What does the xsltExtMarker mean? * QUESTION: What does the xsltExtMarker mean?
* ANSWER: It is used in xsltApplyOneTemplateInt() at * ANSWER: It is used in
* xsltApplySequenceConstructor() at
* transformation-time to look out for extension * transformation-time to look out for extension
* registered in the transformation context. * registered in the transformation context.
*/ */
@@ -4400,7 +4610,7 @@ xsltParseSequenceConstructor(xsltCompilerCtxtPtr cctxt, xmlNodePtr cur)
* transformation-time only, then there's no way of * transformation-time only, then there's no way of
* knowing that content shall be valid, and we'll * knowing that content shall be valid, and we'll
* process the content the same way. * process the content the same way.
* 2) If author *does* set the flag, then we'll assume * 2) If the author *does* set the flag, then we'll assume
* that the author has handled the parsing him/herself * that the author has handled the parsing him/herself
* (e.g. called xsltParseSequenceConstructor(), etc. * (e.g. called xsltParseSequenceConstructor(), etc.
* explicitely in his/her code). * explicitely in his/her code).
@@ -4526,8 +4736,8 @@ xsltParseSequenceConstructor(xsltCompilerCtxtPtr cctxt, xmlNodePtr cur)
* @templ: the node containing the content to be parsed * @templ: the node containing the content to be parsed
* *
* Parses and compiles the content-model of an xsl:template element. * Parses and compiles the content-model of an xsl:template element.
* Note that this is *not* the "template" (or "sequence constructor" * Note that this is *not* the "template" content model (or "sequence
* in XSLT 2.0) content model. Since it allows addional xsl:param * constructor" in XSLT 2.0); it it allows addional xsl:param
* elements as immediate children of @templ. * elements as immediate children of @templ.
* *
* Called by: * Called by:

View File

@@ -27,32 +27,19 @@
extern "C" { extern "C" {
#endif #endif
/* #define XSLT_DEBUG_PROFILE_CACHE */
#define XSLT_IS_TEXT_NODE(n) ((n != NULL) && \ #define XSLT_IS_TEXT_NODE(n) ((n != NULL) && \
(((n)->type == XML_TEXT_NODE) || \ (((n)->type == XML_TEXT_NODE) || \
((n)->type == XML_CDATA_SECTION_NODE))) ((n)->type == XML_CDATA_SECTION_NODE)))
#if 0
extern const xmlChar *xsltDocFragFake;
#define XSLT_MARK_RES_TREE_FRAG(n) (n)->psvi = (void *) xsltDocFragFake;
#define XSLT_IS_RES_TREE_FRAG(n) \
((n != NULL) && ((n)->type == XML_DOCUMENT_NODE) && \
((n)->psvi == xsltDocFragFake))
#else
#define XSLT_MARK_RES_TREE_FRAG(n) \ #define XSLT_MARK_RES_TREE_FRAG(n) \
(n)->name = (char *) xmlStrdup(BAD_CAST " fake node libxslt"); (n)->name = (char *) xmlStrdup(BAD_CAST " fake node libxslt");
#define XSLT_IS_RES_TREE_FRAG(n) \ #define XSLT_IS_RES_TREE_FRAG(n) \
((n != NULL) && ((n)->type == XML_DOCUMENT_NODE) && \ ((n != NULL) && ((n)->type == XML_DOCUMENT_NODE) && \
((n)->name != NULL) && ((n)->name[0] == ' ') && \ ((n)->name != NULL) && ((n)->name[0] == ' '))
xmlStrEqual(BAD_CAST (n)->name, BAD_CAST " fake node libxslt"))
#endif
/** /**
* XSLT_REFACTORED_KEYCOMP: * XSLT_REFACTORED_KEYCOMP:
@@ -61,6 +48,14 @@ extern const xmlChar *xsltDocFragFake;
*/ */
#define XSLT_REFACTORED_KEYCOMP #define XSLT_REFACTORED_KEYCOMP
/**
* XSLT_FAST_IF:
*
* Internal define to enable usage of xmlXPathCompiledEvalToBoolean()
* for XSLT "tests"; e.g. in <xsl:if test="/foo/bar">
*/
#define XSLT_FAST_IF
/** /**
* XSLT_REFACTORED: * XSLT_REFACTORED:
* *
@@ -69,10 +64,13 @@ extern const xmlChar *xsltDocFragFake;
/* #define XSLT_REFACTORED */ /* #define XSLT_REFACTORED */
/* ==================================================================== */ /* ==================================================================== */
#define XSLT_REFACTORED_VARS
#ifdef XSLT_REFACTORED #ifdef XSLT_REFACTORED
extern const xmlChar *xsltXSLTAttrMarker; extern const xmlChar *xsltXSLTAttrMarker;
/* TODO: REMOVE: #define XSLT_REFACTORED_EXCLRESNS */ /* TODO: REMOVE: #define XSLT_REFACTORED_EXCLRESNS */
/* TODO: REMOVE: #define XSLT_REFACTORED_NSALIAS */ /* TODO: REMOVE: #define XSLT_REFACTORED_NSALIAS */
@@ -243,6 +241,7 @@ struct _xsltTemplate {
/* Profiling informations */ /* Profiling informations */
int nbCalls; /* the number of time the template was called */ int nbCalls; /* the number of time the template was called */
unsigned long time; /* the time spent in this template */ unsigned long time; /* the time spent in this template */
void *params; /* xsl:param instructions */
}; };
/** /**
@@ -275,7 +274,6 @@ struct _xsltDecimalFormat {
* *
* Data structure associated to a parsed document. * Data structure associated to a parsed document.
*/ */
typedef struct _xsltDocument xsltDocument; typedef struct _xsltDocument xsltDocument;
typedef xsltDocument *xsltDocumentPtr; typedef xsltDocument *xsltDocumentPtr;
struct _xsltDocument { struct _xsltDocument {
@@ -478,6 +476,8 @@ struct _xsltNsListContainer {
* *
* Fields for API compatibility to the structure * Fields for API compatibility to the structure
* _xsltElemPreComp which is used for extension functions. * _xsltElemPreComp which is used for extension functions.
* Note that @next is used for storage; it does not reflect a next
* sibling in the tree.
* TODO: Evaluate if we really need such a compatibility. * TODO: Evaluate if we really need such a compatibility.
*/ */
#define XSLT_ITEM_COMPATIBILITY_FIELDS \ #define XSLT_ITEM_COMPATIBILITY_FIELDS \
@@ -940,9 +940,21 @@ typedef xsltStyleItemVariable *xsltStyleItemVariablePtr;
* <!-- Content: template --> * <!-- Content: template -->
* </xsl:param> * </xsl:param>
*/ */
typedef xsltStyleBasicItemVariable xsltStyleItemParam; typedef struct _xsltStyleItemParam xsltStyleItemParam;
typedef xsltStyleItemParam *xsltStyleItemParamPtr; typedef xsltStyleItemParam *xsltStyleItemParamPtr;
struct _xsltStyleItemParam {
XSLT_ITEM_COMMON_FIELDS
const xmlChar *select;
xmlXPathCompExprPtr comp;
const xmlChar *name;
int has_name;
const xmlChar *ns;
int has_ns;
};
/** /**
* xsltStyleItemWithParam: * xsltStyleItemWithParam:
* *
@@ -1167,6 +1179,21 @@ struct _xsltNsList {
xmlNsPtr ns; xmlNsPtr ns;
}; };
/*
* xsltVarInfo:
*
* Used at compilation time for parameters and variables.
*/
typedef struct _xsltVarInfo xsltVarInfo;
typedef xsltVarInfo *xsltVarInfoPtr;
struct _xsltVarInfo {
xsltVarInfoPtr next; /* next in the list */
xsltVarInfoPtr prev;
int depth; /* the depth in the tree */
const xmlChar *name;
const xmlChar *nsName;
};
#define XSLT_ELEMENT_CATEGORY_XSLT 0 #define XSLT_ELEMENT_CATEGORY_XSLT 0
#define XSLT_ELEMENT_CATEGORY_EXTENSION 1 #define XSLT_ELEMENT_CATEGORY_EXTENSION 1
#define XSLT_ELEMENT_CATEGORY_LRE 2 #define XSLT_ELEMENT_CATEGORY_LRE 2
@@ -1194,6 +1221,7 @@ struct _xsltCompilerNodeInfo {
xsltPointerListPtr exclResultNs; xsltPointerListPtr exclResultNs;
/* The current extension instruction namespaces */ /* The current extension instruction namespaces */
xsltPointerListPtr extElemNs; xsltPointerListPtr extElemNs;
/* The current info for literal result elements. */ /* The current info for literal result elements. */
xsltStyleItemLRElementInfoPtr litResElemInfo; xsltStyleItemLRElementInfoPtr litResElemInfo;
/* /*
@@ -1263,6 +1291,8 @@ struct _xsltCompilerCtxt {
xsltStyleItemUknownPtr unknownItem; xsltStyleItemUknownPtr unknownItem;
int hasNsAliases; /* Indicator if there was an xsl:namespace-alias. */ int hasNsAliases; /* Indicator if there was an xsl:namespace-alias. */
xsltNsAliasPtr nsAliases; xsltNsAliasPtr nsAliases;
xsltVarInfoPtr ivars; /* Storage of local in-scope variables/params. */
xsltVarInfoPtr ivar; /* topmost local variable/param. */
}; };
#else /* XSLT_REFACTORED */ #else /* XSLT_REFACTORED */
@@ -1329,6 +1359,10 @@ struct _xsltStylePreComp {
#endif /* XSLT_REFACTORED */ #endif /* XSLT_REFACTORED */
#define XSLT_VAR_GLOBAL 1<<0
#define XSLT_VAR_IN_SELECT 1<<1
#define XSLT_TCTXT_VARIABLE(c) ((xsltStackElemPtr) (c)->contextVariable)
/* /*
* The in-memory structure corresponding to an XSLT Variable * The in-memory structure corresponding to an XSLT Variable
* or Param. * or Param.
@@ -1342,8 +1376,16 @@ struct _xsltStackElem {
const xmlChar *name; /* the local part of the name QName */ const xmlChar *name; /* the local part of the name QName */
const xmlChar *nameURI; /* the URI part of the name QName */ const xmlChar *nameURI; /* the URI part of the name QName */
const xmlChar *select; /* the eval string */ const xmlChar *select; /* the eval string */
xmlNodePtr tree; /* the tree if no eval string or the location */ xmlNodePtr tree; /* the sequence constructor if no eval
string or the location */
xmlXPathObjectPtr value; /* The value if computed */ xmlXPathObjectPtr value; /* The value if computed */
xmlDocPtr fragment; /* The Result Tree Fragments (needed for XSLT 1.0)
which are bound to the variable's lifetime. */
int level; /* the depth in the tree;
-1 if persistent (e.g. a given xsl:with-param) */
xsltTransformContextPtr context; /* The transformation context; needed to cache
the variables */
int flags;
}; };
#ifdef XSLT_REFACTORED #ifdef XSLT_REFACTORED
@@ -1537,6 +1579,21 @@ struct _xsltStylesheet {
#endif #endif
}; };
typedef struct _xsltTransformCache xsltTransformCache;
typedef xsltTransformCache *xsltTransformCachePtr;
struct _xsltTransformCache {
xmlDocPtr RVT;
int nbRVT;
xsltStackElemPtr stackItems;
int nbStackItems;
#ifdef XSLT_DEBUG_PROFILE_CACHE
int dbgCachedRVTs;
int dbgReusedRVTs;
int dbgCachedVars;
int dbgReusedVars;
#endif
};
/* /*
* The in-memory structure corresponding to an XSLT Transformation. * The in-memory structure corresponding to an XSLT Transformation.
*/ */
@@ -1579,7 +1636,7 @@ struct _xsltTransformContext {
xsltDocumentPtr docList; /* the document list */ xsltDocumentPtr docList; /* the document list */
xsltDocumentPtr document; /* the current document */ xsltDocumentPtr document; /* the current source document; can be NULL if an RTF */
xmlNodePtr node; /* the current node being processed */ xmlNodePtr node; /* the current node being processed */
xmlNodeSetPtr nodeList; /* the current node list */ xmlNodeSetPtr nodeList; /* the current node list */
/* xmlNodePtr current; the node */ /* xmlNodePtr current; the node */
@@ -1623,6 +1680,7 @@ struct _xsltTransformContext {
/* /*
* handling of temporary Result Value Tree * handling of temporary Result Value Tree
* (XSLT 1.0 term: "Result Tree Fragment")
*/ */
xmlDocPtr tmpRVT; /* list of RVT without persistance */ xmlDocPtr tmpRVT; /* list of RVT without persistance */
xmlDocPtr persistRVT; /* list of persistant RVTs */ xmlDocPtr persistRVT; /* list of persistant RVTs */
@@ -1647,8 +1705,8 @@ struct _xsltTransformContext {
*/ */
xmlDictPtr dict; xmlDictPtr dict;
/* /*
* temporary storage for doc ptr, currently only used for * The current source doc; one of: the initial source doc, a RTF
* global var evaluation * or a source doc aquired via the document() function.
*/ */
xmlDocPtr tmpDoc; xmlDocPtr tmpDoc;
/* /*
@@ -1657,6 +1715,15 @@ struct _xsltTransformContext {
int internalized; int internalized;
int nbKeys; int nbKeys;
int hasTemplKeyPatterns; int hasTemplKeyPatterns;
xsltTemplatePtr currentTemplateRule; /* the Current Template Rule */
xmlNodePtr initialContextNode;
xmlDocPtr initialContextDoc;
xsltTransformCachePtr cache;
void *contextVariable; /* the current variable item */
xmlDocPtr localRVT; /* list of local tree fragments; will be freed when
the instruction which created the fragment
exits */
xmlDocPtr localRVTBase;
}; };
/** /**
@@ -1762,11 +1829,31 @@ XSLTPUBFUN xmlDocPtr XSLTCALL
XSLTPUBFUN int XSLTCALL XSLTPUBFUN int XSLTCALL
xsltRegisterTmpRVT (xsltTransformContextPtr ctxt, xsltRegisterTmpRVT (xsltTransformContextPtr ctxt,
xmlDocPtr RVT); xmlDocPtr RVT);
XSLTPUBFUN int XSLTCALL
xsltRegisterLocalRVT (xsltTransformContextPtr ctxt,
xmlDocPtr RVT);
XSLTPUBFUN int XSLTCALL XSLTPUBFUN int XSLTCALL
xsltRegisterPersistRVT (xsltTransformContextPtr ctxt, xsltRegisterPersistRVT (xsltTransformContextPtr ctxt,
xmlDocPtr RVT); xmlDocPtr RVT);
XSLTPUBFUN int XSLTCALL
xsltExtensionInstructionResultRegister(
xsltTransformContextPtr ctxt,
xmlXPathObjectPtr obj);
XSLTPUBFUN int XSLTCALL
xsltExtensionInstructionResultFinalize(
xsltTransformContextPtr ctxt);
XSLTPUBFUN void XSLTCALL XSLTPUBFUN void XSLTCALL
xsltFreeRVTs (xsltTransformContextPtr ctxt); xsltFreeRVTs (xsltTransformContextPtr ctxt);
XSLTPUBFUN void XSLTCALL
xsltReleaseRVT (xsltTransformContextPtr ctxt,
xmlDocPtr RVT);
XSLTPUBFUN int XSLTCALL
xsltTransStorageAdd (xsltTransformContextPtr ctxt,
void *id,
void *data);
XSLTPUBFUN void * XSLTCALL
xsltTransStorageRemove (xsltTransformContextPtr ctxt,
void *id);
/* /*
* Extra functions for Attribute Value Templates * Extra functions for Attribute Value Templates

View File

@@ -876,9 +876,16 @@ xsltGetQNameURI2(xsltStylesheetPtr style, xmlNodePtr node,
qname = xmlStrndup(*name, len); qname = xmlStrndup(*name, len);
ns = xmlSearchNs(node->doc, node, qname); ns = xmlSearchNs(node->doc, node, qname);
if (ns == NULL) { if (ns == NULL) {
if (style) {
xsltTransformError(NULL, style, node,
"No namespace bound to prefix '%s'.\n",
qname);
style->errors++;
} else {
xsltGenericError(xsltGenericErrorContext, xsltGenericError(xsltGenericErrorContext,
"%s : no namespace bound to prefix %s\n", "%s : no namespace bound to prefix %s\n",
*name, qname); *name, qname);
}
*name = NULL; *name = NULL;
xmlFree(qname); xmlFree(qname);
return(NULL); return(NULL);

View File

@@ -72,9 +72,11 @@ extern "C" {
(((n) != NULL) && \ (((n) != NULL) && \
(((n)->type == XML_ELEMENT_NODE) || \ (((n)->type == XML_ELEMENT_NODE) || \
((n)->type == XML_TEXT_NODE) || \ ((n)->type == XML_TEXT_NODE) || \
((n)->type == XML_CDATA_SECTION_NODE) || \
((n)->type == XML_ATTRIBUTE_NODE) || \ ((n)->type == XML_ATTRIBUTE_NODE) || \
((n)->type == XML_DOCUMENT_NODE) || \ ((n)->type == XML_DOCUMENT_NODE) || \
((n)->type == XML_HTML_DOCUMENT_NODE) || \ ((n)->type == XML_HTML_DOCUMENT_NODE) || \
((n)->type == XML_COMMENT_NODE) || \
((n)->type == XML_PI_NODE))) ((n)->type == XML_PI_NODE)))
/* /*