/** * xsltXPathFunctionLookup: * @ctxt: a void * but the XSLT transformation context actually * @name: the function name * @ns_uri: the function namespace URI * * This is the entry point when a function is needed by the XPath * interpretor. * * Returns the callback function or NULL if not found */ xmlXPathFunction xsltXPathFunctionLookup (xmlXPathContextPtr ctxt, const xmlChar *name, const xmlChar *ns_uri) { xmlXPathFunction ret; if ((ctxt == NULL) || (name == NULL) || (ns_uri == NULL)) return (NULL); #ifdef WITH_XSLT_DEBUG_FUNCTION xsltGenericDebug(xsltGenericDebugContext, "Lookup function {%s}%s\n", ns_uri, name); #endif /* give priority to context-level functions */ /* ret = (xmlXPathFunction) xmlHashLookup2(ctxt->funcHash, name, ns_uri); */ XML_CAST_FPTR(ret) = xmlHashLookup2(ctxt->funcHash, name, ns_uri); if (ret == NULL) ret = xsltExtModuleFunctionLookup(name, ns_uri); #ifdef WITH_XSLT_DEBUG_FUNCTION if (ret != NULL) xsltGenericDebug(xsltGenericDebugContext, "found function %s\n", name); #endif return(ret); }
/** * xsltMergeSASCallback,: * @style: the XSLT stylesheet * * Merge an attribute set from an imported stylesheet. */ static void xsltMergeSASCallback(xsltAttrElemPtr values, xsltStylesheetPtr style, const xmlChar *name, const xmlChar *ns, ATTRIBUTE_UNUSED const xmlChar *ignored) { int ret; xsltAttrElemPtr topSet; ret = xmlHashAddEntry2(style->attributeSets, name, ns, values); if (ret < 0) { /* * Add failed, this attribute set can be removed. */ #ifdef WITH_XSLT_DEBUG_ATTRIBUTES xsltGenericDebug(xsltGenericDebugContext, "attribute set %s present already in top stylesheet" " - merging\n", name); #endif topSet = xmlHashLookup2(style->attributeSets, name, ns); if (topSet==NULL) { xsltGenericError(xsltGenericErrorContext, "xsl:attribute-set : logic error merging from imports for" " attribute-set %s\n", name); } else { topSet = xsltMergeAttrElemList(style, topSet, values); xmlHashUpdateEntry2(style->attributeSets, name, ns, topSet, NULL); } xsltFreeAttrElemList(values); #ifdef WITH_XSLT_DEBUG_ATTRIBUTES } else { xsltGenericDebug(xsltGenericDebugContext, "attribute set %s moved to top stylesheet\n", name); #endif } }
/** * xsltInitCtxtKeys: * @ctxt: an XSLT transformation context * @idoc: a document info * * Computes all the keys tables for the current input document. * Should be done before global varibales are initialized. * NOTE: Not used anymore in the refactored code. */ void xsltInitCtxtKeys(xsltTransformContextPtr ctxt, xsltDocumentPtr idoc) { xsltStylesheetPtr style; xsltKeyDefPtr keyDef; if ((ctxt == NULL) || (idoc == NULL)) return; #ifdef KEY_INIT_DEBUG fprintf(stderr, "xsltInitCtxtKeys on document\n"); #endif #ifdef WITH_XSLT_DEBUG_KEYS if ((idoc->doc != NULL) && (idoc->doc->URL != NULL)) XSLT_TRACE(ctxt,XSLT_TRACE_KEYS,xsltGenericDebug(xsltGenericDebugContext, "Initializing keys on %s\n", idoc->doc->URL)); #endif style = ctxt->style; while (style != NULL) { keyDef = (xsltKeyDefPtr) style->keys; while (keyDef != NULL) { xsltInitCtxtKey(ctxt, idoc, keyDef); keyDef = keyDef->next; } style = xsltNextImport(style); } #ifdef KEY_INIT_DEBUG fprintf(stderr, "xsltInitCtxtKeys on document: done\n"); #endif }
/** * xsltEvalAttrValueTemplate: * @ctxt: the XSLT transformation context * @inst: the instruction (or LRE) in the stylesheet holding the * attribute with an AVT * @name: the attribute QName * @ns: the attribute namespace URI * * Evaluate a attribute value template, i.e. the attribute value can * contain expressions contained in curly braces ({}) and those are * substituted by they computed value. * * Returns the computed string value or NULL, must be deallocated by the * caller. */ xmlChar * xsltEvalAttrValueTemplate(xsltTransformContextPtr ctxt, xmlNodePtr inst, const xmlChar *name, const xmlChar *ns) { xmlChar *ret; xmlChar *expr; if ((ctxt == NULL) || (inst == NULL) || (name == NULL)) return(NULL); expr = xsltGetNsProp(inst, name, ns); if (expr == NULL) return(NULL); /* * TODO: though now {} is detected ahead, it would still be good to * optimize both functions to keep the splitted value if the * attribute content and the XPath precompiled expressions around */ ret = xsltAttrTemplateValueProcessNode(ctxt, expr, inst); #ifdef WITH_XSLT_DEBUG_TEMPLATES XSLT_TRACE(ctxt,XSLT_TRACE_TEMPLATES,xsltGenericDebug(xsltGenericDebugContext, "xsltEvalAttrValueTemplate: %s returns %s\n", expr, ret)); #endif if (expr != NULL) xmlFree(expr); return(ret); }
/** * xsltExtElementPreCompTest: * @style: the stylesheet * @inst: the instruction in the stylesheet * * Process a libxslt:test node */ static xsltElemPreCompPtr xsltExtElementPreCompTest(xsltStylesheetPtr style, xmlNodePtr inst, xsltTransformFunction function) { xsltElemPreCompPtr ret; if (style == NULL) { xsltTransformError(NULL, NULL, inst, "xsltExtElementTest: no transformation context\n"); return (NULL); } if (testStyleData == NULL) { xsltGenericDebug(xsltGenericDebugContext, "xsltExtElementPreCompTest: not initialized," " calling xsltStyleGetExtData\n"); xsltStyleGetExtData(style, (const xmlChar *) XSLT_TESTPLUGIN_URL); if (testStyleData == NULL) { xsltTransformError(NULL, style, inst, "xsltExtElementPreCompTest: not initialized\n"); if (style != NULL) style->errors++; return (NULL); } } if (inst == NULL) { xsltTransformError(NULL, style, inst, "xsltExtElementPreCompTest: no instruction\n"); if (style != NULL) style->errors++; return (NULL); } ret = xsltNewElemPreComp(style, inst, function); return (ret); }
/* * exsltFuncRegisterImportFunc * @data: the exsltFuncFunctionData for the function * @ch: structure containing context and hash table * @URI: the function namespace URI * @name: the function name * * Checks if imported function is already registered in top-level * stylesheet. If not, copies function data and registers function */ static void exsltFuncRegisterImportFunc (exsltFuncFunctionData *data, exsltFuncImportRegData *ch, const xmlChar *URI, const xmlChar *name, ATTRIBUTE_UNUSED const xmlChar *ignored) { exsltFuncFunctionData *func=NULL; if ((data == NULL) || (ch == NULL) || (URI == NULL) || (name == NULL)) return; if (ch->ctxt == NULL || ch->hash == NULL) return; /* Check if already present */ func = (exsltFuncFunctionData*)xmlHashLookup2(ch->hash, URI, name); if (func == NULL) { /* Not yet present - copy it in */ func = exsltFuncNewFunctionData(); memcpy(func, data, sizeof(exsltFuncFunctionData)); if (xmlHashAddEntry2(ch->hash, URI, name, func) < 0) { xsltGenericError(xsltGenericErrorContext, "Failed to register function {%s}%s\n", URI, name); } else { /* Do the registration */ xsltGenericDebug(xsltGenericDebugContext, "exsltFuncRegisterImportFunc: register {%s}%s\n", URI, name); xsltRegisterExtFunction(ch->ctxt, name, URI, exsltFuncFunctionFunction); } } }
void xsltApplyAttributeSet(xsltTransformContextPtr ctxt, xmlNodePtr node, xmlNodePtr inst ATTRIBUTE_UNUSED, const xmlChar * attributes) { const xmlChar *ncname = NULL; const xmlChar *prefix = NULL; const xmlChar *attrib, *endattr; xsltAttrElemPtr values; xsltStylesheetPtr style; if (attributes == NULL) { return; } attrib = attributes; while (*attrib != 0) { while (IS_BLANK(*attrib)) attrib++; if (*attrib == 0) break; endattr = attrib; while ((*endattr != 0) && (!IS_BLANK(*endattr))) endattr++; attrib = xmlDictLookup(ctxt->dict, attrib, endattr - attrib); if (attrib) { #ifdef WITH_XSLT_DEBUG_ATTRIBUTES xsltGenericDebug(xsltGenericDebugContext, "apply attribute set %s\n", attrib); #endif ncname = xsltSplitQName(ctxt->dict, attrib, &prefix); style = ctxt->style; #ifdef WITH_DEBUGGER if ((style != NULL) && (style->attributeSets != NULL) && (ctxt->debugStatus != XSLT_DEBUG_NONE)) { values = xmlHashLookup2(style->attributeSets, ncname, prefix); if ((values != NULL) && (values->attr != NULL)) xslHandleDebugger(values->attr->parent, node, NULL, ctxt); } #endif while (style != NULL) { values = xmlHashLookup2(style->attributeSets, ncname, prefix); while (values != NULL) { if (values->attr != NULL) { xsltAttributeInternal(ctxt, node, values->attr, values->attr->psvi, 1); } values = values->next; } style = xsltNextImport(style); } } attrib = endattr; } }
/** * xsltEvalXPathPredicate: * @ctxt: the XSLT transformation context * @comp: the XPath compiled expression * @nsList: the namespaces in scope * @nsNr: the number of namespaces in scope * * Process the expression using XPath and evaluate the result as * an XPath predicate * * Returns 1 is the predicate was true, 0 otherwise */ int xsltEvalXPathPredicate(xsltTransformContextPtr ctxt, xmlXPathCompExprPtr comp, xmlNsPtr *nsList, int nsNr) { int ret; xmlXPathObjectPtr res; int oldNsNr; xmlNsPtr *oldNamespaces; xmlNodePtr oldInst; int oldProximityPosition, oldContextSize; oldContextSize = ctxt->xpathCtxt->contextSize; oldProximityPosition = ctxt->xpathCtxt->proximityPosition; oldNsNr = ctxt->xpathCtxt->nsNr; oldNamespaces = ctxt->xpathCtxt->namespaces; oldInst = ctxt->inst; ctxt->xpathCtxt->node = ctxt->node; ctxt->xpathCtxt->namespaces = nsList; ctxt->xpathCtxt->nsNr = nsNr; res = xmlXPathCompiledEval(comp, ctxt->xpathCtxt); if (res != NULL) { ret = xmlXPathEvalPredicate(ctxt->xpathCtxt, res); xmlXPathFreeObject(res); #ifdef WITH_XSLT_DEBUG_TEMPLATES XSLT_TRACE(ctxt,XSLT_TRACE_TEMPLATES,xsltGenericDebug(xsltGenericDebugContext, "xsltEvalXPathPredicate: returns %d\n", ret)); #endif } else { #ifdef WITH_XSLT_DEBUG_TEMPLATES XSLT_TRACE(ctxt,XSLT_TRACE_TEMPLATES,xsltGenericDebug(xsltGenericDebugContext, "xsltEvalXPathPredicate: failed\n")); #endif ctxt->state = XSLT_STATE_STOPPED; ret = 0; } ctxt->xpathCtxt->nsNr = oldNsNr; ctxt->xpathCtxt->namespaces = oldNamespaces; ctxt->inst = oldInst; ctxt->xpathCtxt->contextSize = oldContextSize; ctxt->xpathCtxt->proximityPosition = oldProximityPosition; return(ret); }
/** * xsltResolveSASCallbackInt: * @style: the XSLT stylesheet * * resolve the references in an attribute set. */ static void xsltResolveSASCallbackInt(xsltAttrElemPtr values, xsltStylesheetPtr style, const xmlChar *name, const xmlChar *ns, int depth) { xsltAttrElemPtr tmp; xsltAttrElemPtr refs; tmp = values; if ((name == NULL) || (name[0] == 0)) return; if (depth > 100) { xsltGenericError(xsltGenericErrorContext, "xsl:attribute-set : use-attribute-sets recursion detected on %s\n", name); return; } while (tmp != NULL) { if (tmp->set != NULL) { /* * Check against cycles ! */ if ((xmlStrEqual(name, tmp->set)) && (xmlStrEqual(ns, tmp->ns))) { xsltGenericError(xsltGenericErrorContext, "xsl:attribute-set : use-attribute-sets recursion detected on %s\n", name); } else { #ifdef WITH_XSLT_DEBUG_ATTRIBUTES xsltGenericDebug(xsltGenericDebugContext, "Importing attribute list %s\n", tmp->set); #endif refs = xsltGetSAS(style, tmp->set, tmp->ns); if (refs == NULL) { xsltGenericError(xsltGenericErrorContext, "xsl:attribute-set : use-attribute-sets %s reference missing %s\n", name, tmp->set); } else { /* * recurse first for cleanup */ xsltResolveSASCallbackInt(refs, style, name, ns, depth + 1); /* * Then merge */ xsltMergeAttrElemList(style, values, refs); /* * Then suppress the reference */ tmp->set = NULL; tmp->ns = NULL; } } } tmp = tmp->next; } }
/** * xsltExtFunctionTest: * @ctxt: the XPath Parser context * @nargs: the number of arguments * * function libxslt:test() for testing the extensions support. */ static void xsltExtFunctionTest(xmlXPathParserContextPtr ctxt, int nargs ATTRIBUTE_UNUSED) { xsltTransformContextPtr tctxt; void *data = NULL; tctxt = xsltXPathGetTransformContext(ctxt); if (testData == NULL) { xsltGenericDebug(xsltGenericDebugContext, "xsltExtFunctionTest: not initialized," " calling xsltGetExtData\n"); data = xsltGetExtData(tctxt, (const xmlChar *) XSLT_TESTPLUGIN_URL); if (data == NULL) { xsltTransformError(tctxt, NULL, NULL, "xsltExtElementTest: not initialized\n"); return; } } if (tctxt == NULL) { xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, "xsltExtFunctionTest: failed to get the transformation context\n"); return; } if (data == NULL) data = xsltGetExtData(tctxt, (const xmlChar *) XSLT_TESTPLUGIN_URL); if (data == NULL) { xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, "xsltExtFunctionTest: failed to get module data\n"); return; } if (data != testData) { xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, "xsltExtFunctionTest: got wrong module data\n"); return; } #ifdef WITH_XSLT_DEBUG_FUNCTION xsltGenericDebug(xsltGenericDebugContext, "libxslt:test() called with %d args\n", nargs); #endif }
/** * xsltResolveStylesheetAttributeSet: * @style: the XSLT stylesheet * * resolve the references between attribute sets. */ void xsltResolveStylesheetAttributeSet(xsltStylesheetPtr style) { xsltStylesheetPtr cur; #ifdef WITH_XSLT_DEBUG_ATTRIBUTES xsltGenericDebug(xsltGenericDebugContext, "Resolving attribute sets references\n"); #endif /* * First aggregate all the attribute sets definitions from the imports */ cur = xsltNextImport(style); while (cur != NULL) { if (cur->attributeSets != NULL) { if (style->attributeSets == NULL) { #ifdef WITH_XSLT_DEBUG_ATTRIBUTES xsltGenericDebug(xsltGenericDebugContext, "creating attribute set table\n"); #endif style->attributeSets = xmlHashCreate(10); } xmlHashScanFull(cur->attributeSets, (xmlHashScannerFull) xsltMergeSASCallback, style); /* * the attribute lists have either been migrated to style * or freed directly in xsltMergeSASCallback() */ xmlHashFree(cur->attributeSets, NULL); cur->attributeSets = NULL; } cur = xsltNextImport(cur); } /* * Then resolve all the references and computes the resulting sets */ if (style->attributeSets != NULL) { xmlHashScanFull(style->attributeSets, (xmlHashScannerFull) xsltResolveSASCallback, style); } }
/** * xsltGetKey: * @ctxt: an XSLT transformation context * @name: the key name or NULL * @nameURI: the name URI or NULL * @value: the key value to look for * * 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 */ xmlNodeSetPtr xsltGetKey(xsltTransformContextPtr ctxt, const xmlChar *name, const xmlChar *nameURI, const xmlChar *value) { xmlNodeSetPtr ret; xsltKeyTablePtr table; int init_table = 0; if ((ctxt == NULL) || (name == NULL) || (value == NULL) || (ctxt->document == NULL)) return(NULL); #ifdef WITH_XSLT_DEBUG_KEYS xsltGenericDebug(xsltGenericDebugContext, "Get key %s, value %s\n", name, value); #endif /* * keys are computed only on-demand on first key access for a document */ if ((ctxt->document->nbKeysComputed < ctxt->nbKeys) && (ctxt->keyInitLevel == 0)) { /* * If non-recursive behaviour, just try to initialize all keys */ if (xsltInitAllDocKeys(ctxt)) return(NULL); } retry: table = (xsltKeyTablePtr) ctxt->document->keys; while (table != NULL) { if (((nameURI != NULL) == (table->nameURI != NULL)) && xmlStrEqual(table->name, name) && xmlStrEqual(table->nameURI, nameURI)) { ret = (xmlNodeSetPtr)xmlHashLookup(table->keys, value); return(ret); } table = table->next; } if ((ctxt->keyInitLevel != 0) && (init_table == 0)) { /* * Apparently one key is recursive and this one is needed, * initialize just it, that time and retry */ xsltInitDocKeyTable(ctxt, name, nameURI); init_table = 1; goto retry; } return(NULL); }
/** * xsltResolveAttrSet: * @set: the attribute set * @asctx: the context for attribute set resolution * @name: the local name of the attirbute set * @ns: the namespace of the attribute set * @depth: recursion depth * * resolve the references in an attribute set. */ static void xsltResolveAttrSet(xsltAttrSetPtr set, xsltStylesheetPtr topStyle, xsltStylesheetPtr style, const xmlChar *name, const xmlChar *ns, int depth) { xsltStylesheetPtr cur; xsltAttrSetPtr other; if (set->state == ATTRSET_RESOLVED) return; if (set->state == ATTRSET_RESOLVING) { xsltTransformError(NULL, topStyle, NULL, "xsl:attribute-set : use-attribute-sets recursion detected" " on %s\n", name); topStyle->errors++; set->state = ATTRSET_RESOLVED; return; } if (depth > 100) { xsltTransformError(NULL, topStyle, NULL, "xsl:attribute-set : use-attribute-sets maximum recursion " "depth exceeded on %s\n", name); topStyle->errors++; return; } set->state = ATTRSET_RESOLVING; xsltResolveUseAttrSets(set, topStyle, depth); /* Merge imported sets. */ cur = xsltNextImport(style); while (cur != NULL) { if (cur->attributeSets != NULL) { other = xmlHashLookup2(cur->attributeSets, name, ns); if (other != NULL) { #ifdef WITH_XSLT_DEBUG_ATTRIBUTES xsltGenericDebug(xsltGenericDebugContext, "xsl:attribute-set : merging import for %s\n", name); #endif xsltResolveUseAttrSets(other, topStyle, depth); xsltMergeAttrSets(set, other); xmlHashRemoveEntry2(cur->attributeSets, name, ns, NULL); xsltFreeAttrSet(other); } } cur = xsltNextImport(cur); } set->state = ATTRSET_RESOLVED; }
/** * exsltFuncRegisterFunc: * @func: the #exsltFuncFunctionData for the function * @ctxt: an XSLT transformation context * @URI: the function namespace URI * @name: the function name * * Registers a function declared by a func:function element */ static void exsltFuncRegisterFunc (exsltFuncFunctionData *data, xsltTransformContextPtr ctxt, const xmlChar *URI, const xmlChar *name, ATTRIBUTE_UNUSED const xmlChar *ignored) { if ((data == NULL) || (ctxt == NULL) || (URI == NULL) || (name == NULL)) return; xsltGenericDebug(xsltGenericDebugContext, "exsltFuncRegisterFunc: register {%s}%s\n", URI, name); xsltRegisterExtFunction(ctxt, name, URI, exsltFuncFunctionFunction); }
/** * xsltExtStyleInitTest: * @style: an XSLT stylesheet * @URI: the namespace URI for the extension * * A function called at initialization time of an XSLT extension module * * Returns a pointer to the module specific data for this transformation */ static void * xsltExtStyleInitTest(xsltStylesheetPtr style ATTRIBUTE_UNUSED, const xmlChar * URI) { if (testStyleData != NULL) { xsltTransformError(NULL, NULL, NULL, "xsltExtInitTest: already initialized\n"); return (NULL); } testStyleData = (void *) "test data"; xsltGenericDebug(xsltGenericDebugContext, "Registered test plugin module : %s\n", URI); return (testStyleData); }
/** * xsltEvalXPathStringNs: * @ctxt: the XSLT transformation context * @comp: the compiled XPath expression * @nsNr: the number of namespaces in the list * @nsList: the list of in-scope namespaces to use * * Process the expression using XPath, allowing to pass a namespace mapping * context and get a string * * Returns the computed string value or NULL, must be deallocated by the * caller. */ xmlChar * xsltEvalXPathStringNs(xsltTransformContextPtr ctxt, xmlXPathCompExprPtr comp, int nsNr, xmlNsPtr *nsList) { xmlChar *ret = NULL; xmlXPathObjectPtr res; xmlNodePtr oldInst; xmlNodePtr oldNode; int oldPos, oldSize; int oldNsNr; xmlNsPtr *oldNamespaces; oldInst = ctxt->inst; oldNode = ctxt->node; oldPos = ctxt->xpathCtxt->proximityPosition; oldSize = ctxt->xpathCtxt->contextSize; oldNsNr = ctxt->xpathCtxt->nsNr; oldNamespaces = ctxt->xpathCtxt->namespaces; ctxt->xpathCtxt->node = ctxt->node; /* TODO: do we need to propagate the namespaces here ? */ ctxt->xpathCtxt->namespaces = nsList; ctxt->xpathCtxt->nsNr = nsNr; res = xmlXPathCompiledEval(comp, ctxt->xpathCtxt); if (res != NULL) { if (res->type != XPATH_STRING) res = xmlXPathConvertString(res); if (res->type == XPATH_STRING) { ret = res->stringval; res->stringval = NULL; } else { xsltTransformError(ctxt, NULL, NULL, "xpath : string() function didn't return a String\n"); } xmlXPathFreeObject(res); } else { ctxt->state = XSLT_STATE_STOPPED; } #ifdef WITH_XSLT_DEBUG_TEMPLATES XSLT_TRACE(ctxt,XSLT_TRACE_TEMPLATES,xsltGenericDebug(xsltGenericDebugContext, "xsltEvalXPathString: returns %s\n", ret)); #endif ctxt->inst = oldInst; ctxt->node = oldNode; ctxt->xpathCtxt->contextSize = oldSize; ctxt->xpathCtxt->proximityPosition = oldPos; ctxt->xpathCtxt->nsNr = oldNsNr; ctxt->xpathCtxt->namespaces = oldNamespaces; return(ret); }
/** * xsltExtInitTest: * @ctxt: an XSLT transformation context * @URI: the namespace URI for the extension * * A function called at initialization time of an XSLT extension module * * Returns a pointer to the module specific data for this transformation */ static void * xsltExtInitTest(xsltTransformContextPtr ctxt, const xmlChar * URI) { if (testStyleData == NULL) { xsltGenericDebug(xsltGenericErrorContext, "xsltExtInitTest: not initialized," " calling xsltStyleGetExtData\n"); xsltStyleGetExtData(ctxt->style, URI); if (testStyleData == NULL) { xsltTransformError(ctxt, NULL, NULL, "xsltExtInitTest: not initialized\n"); return (NULL); } } if (testData != NULL) { xsltTransformError(ctxt, NULL, NULL, "xsltExtInitTest: already initialized\n"); return (NULL); } testData = (void *) "test data"; xsltGenericDebug(xsltGenericDebugContext, "Registered test plugin module : %s\n", URI); return (testData); }
/** * xsltResolveStylesheetAttributeSet: * @style: the XSLT stylesheet * * resolve the references between attribute sets. */ void xsltResolveStylesheetAttributeSet(xsltStylesheetPtr style) { xsltStylesheetPtr cur; xsltAttrSetContext asctx; #ifdef WITH_XSLT_DEBUG_ATTRIBUTES xsltGenericDebug(xsltGenericDebugContext, "Resolving attribute sets references\n"); #endif asctx.topStyle = style; cur = style; while (cur != NULL) { if (cur->attributeSets != NULL) { if (style->attributeSets == NULL) { #ifdef WITH_XSLT_DEBUG_ATTRIBUTES xsltGenericDebug(xsltGenericDebugContext, "creating attribute set table\n"); #endif style->attributeSets = xmlHashCreate(10); } asctx.style = cur; xmlHashScanFull(cur->attributeSets, (xmlHashScannerFull) xsltResolveSASCallback, &asctx); if (cur != style) { /* * the attribute lists have either been migrated to style * or freed directly in xsltResolveSASCallback() */ xmlHashFree(cur->attributeSets, NULL); cur->attributeSets = NULL; } } cur = xsltNextImport(cur); } }
/** * xsltExtShutdownTest: * @ctxt: an XSLT transformation context * @URI: the namespace URI for the extension * @data: the data associated to this module * * A function called at shutdown time of an XSLT extension module */ static void xsltExtShutdownTest(xsltTransformContextPtr ctxt, const xmlChar * URI, void *data) { if (testData == NULL) { xsltTransformError(ctxt, NULL, NULL, "xsltExtShutdownTest: not initialized\n"); return; } if (data != testData) { xsltTransformError(ctxt, NULL, NULL, "xsltExtShutdownTest: wrong data\n"); } testData = NULL; xsltGenericDebug(xsltGenericDebugContext, "Unregistered test plugin module : %s\n", URI); }
/** * xsltExtStyleShutdownTest: * @style: an XSLT stylesheet * @URI: the namespace URI for the extension * @data: the data associated to this module * * A function called at shutdown time of an XSLT extension module */ static void xsltExtStyleShutdownTest(xsltStylesheetPtr style ATTRIBUTE_UNUSED, const xmlChar * URI, void *data) { if (testStyleData == NULL) { xsltGenericError(xsltGenericErrorContext, "xsltExtShutdownTest: not initialized\n"); return; } if (data != testStyleData) { xsltTransformError(NULL, NULL, NULL, "xsltExtShutdownTest: wrong data\n"); } testStyleData = NULL; xsltGenericDebug(xsltGenericDebugContext, "Unregistered test plugin module : %s\n", URI); }
/** * xsltInitDocKeyTable: * * INTERNAL ROUTINE ONLY * * Check if any keys on the current document need to be computed */ static int xsltInitDocKeyTable(xsltTransformContextPtr ctxt, const xmlChar *name, const xmlChar *nameURI) { xsltStylesheetPtr style; xsltKeyDefPtr keyd = NULL; int found = 0; #ifdef KEY_INIT_DEBUG fprintf(stderr, "xsltInitDocKeyTable %s\n", name); #endif style = ctxt->style; while (style != NULL) { keyd = (xsltKeyDefPtr) style->keys; while (keyd != NULL) { if (((keyd->nameURI != NULL) == (nameURI != NULL)) && xmlStrEqual(keyd->name, name) && xmlStrEqual(keyd->nameURI, nameURI)) { xsltInitCtxtKey(ctxt, ctxt->document, keyd); if (ctxt->document->nbKeysComputed == ctxt->nbKeys) return(0); found = 1; } keyd = keyd->next; } style = xsltNextImport(style); } if (found == 0) { #ifdef WITH_XSLT_DEBUG_KEYS XSLT_TRACE(ctxt,XSLT_TRACE_KEYS,xsltGenericDebug(xsltGenericDebugContext, "xsltInitDocKeyTable: did not found %s\n", name)); #endif xsltTransformError(ctxt, NULL, keyd? keyd->inst : NULL, "Failed to find key definition for %s\n", name); ctxt->state = XSLT_STATE_STOPPED; return(-1); } #ifdef KEY_INIT_DEBUG fprintf(stderr, "xsltInitDocKeyTable %s done\n", name); #endif return(0); }
/** * xsltExtElementTest: * @ctxt: an XSLT processing context * @node: The current node * @inst: the instruction in the stylesheet * @comp: precomputed informations * * Process a libxslt:test node */ static void xsltExtElementTest(xsltTransformContextPtr ctxt, xmlNodePtr node, xmlNodePtr inst, xsltElemPreCompPtr comp ATTRIBUTE_UNUSED) { xmlNodePtr commentNode; if (testData == NULL) { xsltGenericDebug(xsltGenericDebugContext, "xsltExtElementTest: not initialized," " calling xsltGetExtData\n"); xsltGetExtData(ctxt, (const xmlChar *) XSLT_TESTPLUGIN_URL); if (testData == NULL) { xsltTransformError(ctxt, NULL, inst, "xsltExtElementTest: not initialized\n"); return; } } if (ctxt == NULL) { xsltTransformError(ctxt, NULL, inst, "xsltExtElementTest: no transformation context\n"); return; } if (node == NULL) { xsltTransformError(ctxt, NULL, inst, "xsltExtElementTest: no current node\n"); return; } if (inst == NULL) { xsltTransformError(ctxt, NULL, inst, "xsltExtElementTest: no instruction\n"); return; } if (ctxt->insert == NULL) { xsltTransformError(ctxt, NULL, inst, "xsltExtElementTest: no insertion point\n"); return; } commentNode = xmlNewComment((const xmlChar *) "libxslt:testplugin element test worked"); xmlAddChild(ctxt->insert, commentNode); }
/** * xsltInitCtxtKeys: * @ctxt: an XSLT transformation context * @doc: an XSLT document * * Computes all the keys tables for the current input document. * Should be done before global varibales are initialized. * NOTE: Not used anymore in the refactored code. */ void xsltInitCtxtKeys(xsltTransformContextPtr ctxt, xsltDocumentPtr doc) { xsltStylesheetPtr style; xsltKeyDefPtr keyd; if ((ctxt == NULL) || (doc == NULL)) return; #ifdef WITH_XSLT_DEBUG_KEYS if ((doc->doc != NULL) && (doc->doc->URL != NULL)) XSLT_TRACE(ctxt,XSLT_TRACE_KEYS,xsltGenericDebug(xsltGenericDebugContext, "Initializing keys on %s\n", doc->doc->URL)); #endif style = ctxt->style; while (style != NULL) { keyd = (xsltKeyDefPtr) style->keys; while (keyd != NULL) { xsltInitCtxtKey(ctxt, doc, keyd); keyd = keyd->next; } style = xsltNextImport(style); } }
xmlChar * xsltEvalAttrValueTemplate(xsltTransformContextPtr ctxt, xmlNodePtr inst, const xmlChar *name, const xmlChar *ns) { xmlChar *ret; xmlChar *expr; if ((ctxt == NULL) || (inst == NULL) || (name == NULL)) return(NULL); expr = xsltGetNsProp(inst, name, ns); if (expr == NULL) return(NULL); ret = xsltAttrTemplateValueProcessNode(ctxt, expr, inst); #ifdef WITH_XSLT_DEBUG_TEMPLATES XSLT_TRACE(ctxt,XSLT_TRACE_TEMPLATES,xsltGenericDebug(xsltGenericDebugContext, "xsltEvalAttrValueTemplate: %s returns %s\n", expr, ret)); #endif if (expr != NULL) xmlFree(expr); return(ret); }
/** * xsltApplyAttributeSet: * @ctxt: the XSLT stylesheet * @node: the node in the source tree. * @inst: the attribute node "xsl:use-attribute-sets" * @attrSets: the list of QNames of the attribute-sets to be applied * * Apply the xsl:use-attribute-sets. * If @attrSets is NULL, then @inst will be used to exctract this * value. * If both, @attrSets and @inst, are NULL, then this will do nothing. */ void xsltApplyAttributeSet(xsltTransformContextPtr ctxt, xmlNodePtr node, xmlNodePtr inst, const xmlChar *attrSets) { const xmlChar *ncname = NULL; const xmlChar *prefix = NULL; const xmlChar *curstr, *endstr; xsltAttrElemPtr attrs; xsltStylesheetPtr style; if (attrSets == NULL) { if (inst == NULL) return; else { /* * Extract the value from @inst. */ if (inst->type == XML_ATTRIBUTE_NODE) { if ( ((xmlAttrPtr) inst)->children != NULL) attrSets = ((xmlAttrPtr) inst)->children->content; } if (attrSets == NULL) { /* * TODO: Return an error? */ return; } } } /* * Parse/apply the list of QNames. */ curstr = attrSets; while (*curstr != 0) { while (IS_BLANK(*curstr)) curstr++; if (*curstr == 0) break; endstr = curstr; while ((*endstr != 0) && (!IS_BLANK(*endstr))) endstr++; curstr = xmlDictLookup(ctxt->dict, curstr, endstr - curstr); if (curstr) { /* * TODO: Validate the QName. */ #ifdef WITH_XSLT_DEBUG_curstrUTES xsltGenericDebug(xsltGenericDebugContext, "apply curstrute set %s\n", curstr); #endif ncname = xsltSplitQName(ctxt->dict, curstr, &prefix); style = ctxt->style; #ifdef WITH_DEBUGGER if ((style != NULL) && (style->attributeSets != NULL) && (ctxt->debugStatus != XSLT_DEBUG_NONE)) { attrs = xmlHashLookup2(style->attributeSets, ncname, prefix); if ((attrs != NULL) && (attrs->attr != NULL)) xslHandleDebugger(attrs->attr->parent, node, NULL, ctxt); } #endif /* * Lookup the referenced curstrute-set. */ while (style != NULL) { attrs = xmlHashLookup2(style->attributeSets, ncname, prefix); while (attrs != NULL) { if (attrs->attr != NULL) { xsltAttributeInternal(ctxt, node, attrs->attr, attrs->attr->psvi, 1); } attrs = attrs->next; } style = xsltNextImport(style); } } curstr = endstr; } }
void xsltParseStylesheetAttributeSet(xsltStylesheetPtr style, xmlNodePtr cur) { const xmlChar *ncname; const xmlChar *prefix; xmlChar *value; xmlNodePtr child; xsltAttrElemPtr attrItems; if ((cur == NULL) || (style == NULL) || (cur->type != XML_ELEMENT_NODE)) return; value = xmlGetNsProp(cur, (const xmlChar *)"name", NULL); if ((value == NULL) || (*value == 0)) { xsltGenericError(xsltGenericErrorContext, "xsl:attribute-set : name is missing\n"); if (value) xmlFree(value); return; } ncname = xsltSplitQName(style->dict, value, &prefix); xmlFree(value); value = NULL; if (style->attributeSets == NULL) { #ifdef WITH_XSLT_DEBUG_ATTRIBUTES xsltGenericDebug(xsltGenericDebugContext, "creating attribute set table\n"); #endif style->attributeSets = xmlHashCreate(10); } if (style->attributeSets == NULL) return; attrItems = xmlHashLookup2(style->attributeSets, ncname, prefix); /* * Parse the content. Only xsl:attribute elements are allowed. */ child = cur->children; while (child != NULL) { /* * Report invalid nodes. */ if ((child->type != XML_ELEMENT_NODE) || (child->ns == NULL) || (! IS_XSLT_ELEM(child))) { if (child->type == XML_ELEMENT_NODE) xsltTransformError(NULL, style, child, "xsl:attribute-set : unexpected child %s\n", child->name); else xsltTransformError(NULL, style, child, "xsl:attribute-set : child of unexpected type\n"); } else if (!IS_XSLT_NAME(child, "attribute")) { xsltTransformError(NULL, style, child, "xsl:attribute-set : unexpected child xsl:%s\n", child->name); } else { #ifdef XSLT_REFACTORED xsltAttrElemPtr nextAttr, curAttr; /* * Process xsl:attribute * --------------------- */ #ifdef WITH_XSLT_DEBUG_ATTRIBUTES xsltGenericDebug(xsltGenericDebugContext, "add attribute to list %s\n", ncname); #endif /* * The following was taken over from * xsltAddAttrElemList(). */ if (attrItems == NULL) { attrItems = xsltNewAttrElem(child); } else { curAttr = attrItems; while (curAttr != NULL) { nextAttr = curAttr->next; if (curAttr->attr == child) { /* * URGENT TODO: Can somebody explain * why attrItems is set to curAttr * here? Is this somehow related to * avoidance of recursions? */ attrItems = curAttr; goto next_child; } if (curAttr->next == NULL) curAttr->next = xsltNewAttrElem(child); curAttr = nextAttr; } } /* * Parse the xsl:attribute and its content. */ xsltParseAnyXSLTElem(XSLT_CCTXT(style), child); #else #ifdef WITH_XSLT_DEBUG_ATTRIBUTES xsltGenericDebug(xsltGenericDebugContext, "add attribute to list %s\n", ncname); #endif /* * OLD behaviour: */ attrItems = xsltAddAttrElemList(attrItems, child); #endif } #ifdef XSLT_REFACTORED next_child: #endif child = child->next; } /* * Process attribue "use-attribute-sets". */ /* TODO check recursion */ value = xmlGetNsProp(cur, (const xmlChar *)"use-attribute-sets", NULL); if (value != NULL) { const xmlChar *curval, *endval; curval = value; while (*curval != 0) { while (IS_BLANK(*curval)) curval++; if (*curval == 0) break; endval = curval; while ((*endval != 0) && (!IS_BLANK(*endval))) endval++; curval = xmlDictLookup(style->dict, curval, endval - curval); if (curval) { const xmlChar *ncname2 = NULL; const xmlChar *prefix2 = NULL; xsltAttrElemPtr refAttrItems; #ifdef WITH_XSLT_DEBUG_ATTRIBUTES xsltGenericDebug(xsltGenericDebugContext, "xsl:attribute-set : %s adds use %s\n", ncname, curval); #endif ncname2 = xsltSplitQName(style->dict, curval, &prefix2); refAttrItems = xsltNewAttrElem(NULL); if (refAttrItems != NULL) { refAttrItems->set = ncname2; refAttrItems->ns = prefix2; attrItems = xsltMergeAttrElemList(style, attrItems, refAttrItems); xsltFreeAttrElem(refAttrItems); } } curval = endval; } xmlFree(value); value = NULL; } /* * Update the value */ /* * TODO: Why is this dummy entry needed.? */ if (attrItems == NULL) attrItems = xsltNewAttrElem(NULL); xmlHashUpdateEntry2(style->attributeSets, ncname, prefix, attrItems, NULL); #ifdef WITH_XSLT_DEBUG_ATTRIBUTES xsltGenericDebug(xsltGenericDebugContext, "updated attribute list %s\n", ncname); #endif }
/** * xsltApplyAttributeSet: * @ctxt: the XSLT stylesheet * @node: the node in the source tree. * @inst: the attribute node "xsl:use-attribute-sets" * @attrSets: the list of QNames of the attribute-sets to be applied * * Apply the xsl:use-attribute-sets. * If @attrSets is NULL, then @inst will be used to exctract this * value. * If both, @attrSets and @inst, are NULL, then this will do nothing. */ void xsltApplyAttributeSet(xsltTransformContextPtr ctxt, xmlNodePtr node, xmlNodePtr inst, const xmlChar *attrSets) { const xmlChar *ncname = NULL; const xmlChar *prefix = NULL; const xmlChar *curstr, *endstr; xsltAttrSetPtr set; xsltStylesheetPtr style; if (attrSets == NULL) { if (inst == NULL) return; else { /* * Extract the value from @inst. */ if (inst->type == XML_ATTRIBUTE_NODE) { if ( ((xmlAttrPtr) inst)->children != NULL) attrSets = ((xmlAttrPtr) inst)->children->content; } if (attrSets == NULL) { /* * TODO: Return an error? */ return; } } } /* * Parse/apply the list of QNames. */ curstr = attrSets; while (*curstr != 0) { while (IS_BLANK(*curstr)) curstr++; if (*curstr == 0) break; endstr = curstr; while ((*endstr != 0) && (!IS_BLANK(*endstr))) endstr++; curstr = xmlDictLookup(ctxt->dict, curstr, endstr - curstr); if (curstr) { xmlNsPtr ns; const xmlChar *nsUri = NULL; #ifdef WITH_XSLT_DEBUG_ATTRIBUTES xsltGenericDebug(xsltGenericDebugContext, "apply attribute set %s\n", curstr); #endif if (xmlValidateQName(curstr, 0)) { xsltTransformError(ctxt, NULL, inst, "The name '%s' in use-attribute-sets is not a valid " "QName.\n", curstr); return; } ncname = xsltSplitQName(ctxt->dict, curstr, &prefix); if (prefix != NULL) { ns = xmlSearchNs(inst->doc, inst, prefix); if (ns == NULL) { xsltTransformError(ctxt, NULL, inst, "use-attribute-set : No namespace found for QName " "'%s:%s'\n", prefix, ncname); return; } nsUri = ns->href; } style = ctxt->style; #ifdef WITH_DEBUGGER if ((style != NULL) && (style->attributeSets != NULL) && (ctxt->debugStatus != XSLT_DEBUG_NONE)) { set = xmlHashLookup2(style->attributeSets, ncname, nsUri); if ((set != NULL) && (set->attrs != NULL) && (set->attrs->attr != NULL)) xslHandleDebugger(set->attrs->attr->parent, node, NULL, ctxt); } #endif /* * Lookup the referenced attribute-set. All attribute sets were * moved to the top stylesheet so there's no need to iterate * imported stylesheets */ set = xmlHashLookup2(style->attributeSets, ncname, nsUri); if (set != NULL) { xsltAttrElemPtr cur = set->attrs; while (cur != NULL) { if (cur->attr != NULL) { xsltAttribute(ctxt, node, cur->attr, cur->attr->psvi); } cur = cur->next; } } } curstr = endstr; } }
void xsltParseStylesheetAttributeSet(xsltStylesheetPtr style, xmlNodePtr cur) { const xmlChar *ncname; const xmlChar *prefix; const xmlChar *nsUri = NULL; xmlChar *value; xmlNodePtr child; xsltAttrSetPtr set; if ((cur == NULL) || (style == NULL) || (cur->type != XML_ELEMENT_NODE)) return; value = xmlGetNsProp(cur, (const xmlChar *)"name", NULL); if ((value == NULL) || (*value == 0)) { xsltGenericError(xsltGenericErrorContext, "xsl:attribute-set : name is missing\n"); if (value) xmlFree(value); return; } if (xmlValidateQName(value, 0)) { xsltTransformError(NULL, style, cur, "xsl:attribute-set : The name '%s' is not a valid QName.\n", value); style->errors++; xmlFree(value); return; } ncname = xsltSplitQName(style->dict, value, &prefix); xmlFree(value); value = NULL; if (prefix != NULL) { xmlNsPtr ns = xmlSearchNs(style->doc, cur, prefix); if (ns == NULL) { xsltTransformError(NULL, style, cur, "xsl:attribute-set : No namespace found for QName '%s:%s'\n", prefix, ncname); style->errors++; return; } nsUri = ns->href; } if (style->attributeSets == NULL) { #ifdef WITH_XSLT_DEBUG_ATTRIBUTES xsltGenericDebug(xsltGenericDebugContext, "creating attribute set table\n"); #endif style->attributeSets = xmlHashCreate(10); } if (style->attributeSets == NULL) return; set = xmlHashLookup2(style->attributeSets, ncname, nsUri); if (set == NULL) { set = xsltNewAttrSet(); if (set == NULL) return; xmlHashAddEntry2(style->attributeSets, ncname, nsUri, set); } /* * Parse the content. Only xsl:attribute elements are allowed. */ child = cur->children; while (child != NULL) { /* * Report invalid nodes. */ if ((child->type != XML_ELEMENT_NODE) || (child->ns == NULL) || (! IS_XSLT_ELEM(child))) { if (child->type == XML_ELEMENT_NODE) xsltTransformError(NULL, style, child, "xsl:attribute-set : unexpected child %s\n", child->name); else xsltTransformError(NULL, style, child, "xsl:attribute-set : child of unexpected type\n"); } else if (!IS_XSLT_NAME(child, "attribute")) { xsltTransformError(NULL, style, child, "xsl:attribute-set : unexpected child xsl:%s\n", child->name); } else { #ifdef WITH_XSLT_DEBUG_ATTRIBUTES xsltGenericDebug(xsltGenericDebugContext, "add attribute to list %s\n", ncname); #endif if (child->psvi == NULL) { xsltTransformError(NULL, style, child, "xsl:attribute-set : internal error, attribute %s not " "compiled\n", child->name); } else { set->attrs = xsltAddAttrElemList(set->attrs, child); } } child = child->next; } /* * Process attribute "use-attribute-sets". */ value = xmlGetNsProp(cur, BAD_CAST "use-attribute-sets", NULL); if (value != NULL) { const xmlChar *curval, *endval; curval = value; while (*curval != 0) { while (IS_BLANK(*curval)) curval++; if (*curval == 0) break; endval = curval; while ((*endval != 0) && (!IS_BLANK(*endval))) endval++; curval = xmlDictLookup(style->dict, curval, endval - curval); if (curval) { const xmlChar *ncname2 = NULL; const xmlChar *prefix2 = NULL; const xmlChar *nsUri2 = NULL; #ifdef WITH_XSLT_DEBUG_ATTRIBUTES xsltGenericDebug(xsltGenericDebugContext, "xsl:attribute-set : %s adds use %s\n", ncname, curval); #endif if (xmlValidateQName(curval, 0)) { xsltTransformError(NULL, style, cur, "xsl:attribute-set : The name '%s' in " "use-attribute-sets is not a valid QName.\n", curval); style->errors++; xmlFree(value); return; } ncname2 = xsltSplitQName(style->dict, curval, &prefix2); if (prefix2 != NULL) { xmlNsPtr ns2 = xmlSearchNs(style->doc, cur, prefix2); if (ns2 == NULL) { xsltTransformError(NULL, style, cur, "xsl:attribute-set : No namespace found for QName " "'%s:%s' in use-attribute-sets\n", prefix2, ncname2); style->errors++; xmlFree(value); return; } nsUri2 = ns2->href; } set->useAttrSets = xsltAddUseAttrSetList(set->useAttrSets, ncname2, nsUri2); } curval = endval; } xmlFree(value); value = NULL; } #ifdef WITH_XSLT_DEBUG_ATTRIBUTES xsltGenericDebug(xsltGenericDebugContext, "updated attribute list %s\n", ncname); #endif }
/** * xsltAddKey: * @style: an XSLT stylesheet * @name: the key name or NULL * @nameURI: the name URI or NULL * @match: the match value * @use: the use value * @inst: the key instruction * * add a key definition to a stylesheet * * Returns 0 in case of success, and -1 in case of failure. */ int xsltAddKey(xsltStylesheetPtr style, const xmlChar *name, const xmlChar *nameURI, const xmlChar *match, const xmlChar *use, xmlNodePtr inst) { xsltKeyDefPtr key; xmlChar *pattern = NULL; int current, end, start, i = 0; if ((style == NULL) || (name == NULL) || (match == NULL) || (use == NULL)) return(-1); #ifdef WITH_XSLT_DEBUG_KEYS xsltGenericDebug(xsltGenericDebugContext, "Add key %s, match %s, use %s\n", name, match, use); #endif key = xsltNewKeyDef(name, nameURI); key->match = xmlStrdup(match); key->use = xmlStrdup(use); key->inst = inst; key->nsList = xmlGetNsList(inst->doc, inst); if (key->nsList != NULL) { while (key->nsList[i] != NULL) i++; } key->nsNr = i; /* * Split the | and register it as as many keys */ current = end = 0; while (match[current] != 0) { start = current; while (IS_BLANK_CH(match[current])) current++; end = current; while ((match[end] != 0) && (match[end] != '|')) { if (match[end] == '[') { end = skipPredicate(match, end); if (end <= 0) { xsltTransformError(NULL, style, inst, "xsl:key : 'match' pattern is malformed: %s", key->match); if (style != NULL) style->errors++; goto error; } } else end++; } if (current == end) { xsltTransformError(NULL, style, inst, "xsl:key : 'match' pattern is empty\n"); if (style != NULL) style->errors++; goto error; } if (match[start] != '/') { pattern = xmlStrcat(pattern, (xmlChar *)"//"); if (pattern == NULL) { if (style != NULL) style->errors++; goto error; } } pattern = xmlStrncat(pattern, &match[start], end - start); if (pattern == NULL) { if (style != NULL) style->errors++; goto error; } if (match[end] == '|') { pattern = xmlStrcat(pattern, (xmlChar *)"|"); end++; } current = end; } if (pattern == NULL) { xsltTransformError(NULL, style, inst, "xsl:key : 'match' pattern is empty\n"); if (style != NULL) style->errors++; goto error; } #ifdef WITH_XSLT_DEBUG_KEYS xsltGenericDebug(xsltGenericDebugContext, " resulting pattern %s\n", pattern); #endif /* * XSLT-1: "It is an error for the value of either the use * attribute or the match attribute to contain a * VariableReference." * TODO: We should report a variable-reference at compile-time. * Maybe a search for "$", if it occurs outside of quotation * marks, could be sufficient. */ #ifdef XML_XPATH_NOVAR key->comp = xsltXPathCompileFlags(style, pattern, XML_XPATH_NOVAR); #else key->comp = xsltXPathCompile(style, pattern); #endif if (key->comp == NULL) { xsltTransformError(NULL, style, inst, "xsl:key : 'match' pattern compilation failed '%s'\n", pattern); if (style != NULL) style->errors++; } #ifdef XML_XPATH_NOVAR key->usecomp = xsltXPathCompileFlags(style, use, XML_XPATH_NOVAR); #else key->usecomp = xsltXPathCompile(style, use); #endif if (key->usecomp == NULL) { xsltTransformError(NULL, style, inst, "xsl:key : 'use' expression compilation failed '%s'\n", use); if (style != NULL) style->errors++; } /* * Sometimes the stylesheet writer use the order to ease the * resolution of keys when they are dependant, keep the provided * order so add the new one at the end. */ if (style->keys == NULL) { style->keys = key; } else { xsltKeyDefPtr prev = style->keys; while (prev->next != NULL) prev = prev->next; prev->next = key; } key->next = NULL; error: if (pattern != NULL) xmlFree(pattern); return(0); }
/** * xsltInitCtxtKey: * @ctxt: an XSLT transformation context * @idoc: the document information (holds key values) * @keyDef: the key definition * * Computes the key tables this key and for the current input document. * * Returns: 0 on success, -1 on error */ int xsltInitCtxtKey(xsltTransformContextPtr ctxt, xsltDocumentPtr idoc, xsltKeyDefPtr keyDef) { int i, len, k; xmlNodeSetPtr matchList = NULL, keylist; xmlXPathObjectPtr matchRes = NULL, useRes = NULL; xmlChar *str = NULL; xsltKeyTablePtr table; xmlNodePtr oldInst, cur; xmlNodePtr oldContextNode; xsltDocumentPtr oldDocInfo; int oldXPPos, oldXPSize; xmlDocPtr oldXPDoc; int oldXPNsNr; xmlNsPtr *oldXPNamespaces; xmlXPathContextPtr xpctxt; #ifdef KEY_INIT_DEBUG fprintf(stderr, "xsltInitCtxtKey %s : %d\n", keyDef->name, ctxt->keyInitLevel); #endif if ((keyDef->comp == NULL) || (keyDef->usecomp == NULL)) return(-1); /* * Detect recursive keys */ if (ctxt->keyInitLevel > ctxt->nbKeys) { #ifdef WITH_XSLT_DEBUG_KEYS XSLT_TRACE(ctxt,XSLT_TRACE_KEYS, xsltGenericDebug(xsltGenericDebugContext, "xsltInitCtxtKey: key definition of %s is recursive\n", keyDef->name)); #endif xsltTransformError(ctxt, NULL, keyDef->inst, "Key definition for %s is recursive\n", keyDef->name); ctxt->state = XSLT_STATE_STOPPED; return(-1); } ctxt->keyInitLevel++; xpctxt = ctxt->xpathCtxt; idoc->nbKeysComputed++; /* * Save context state. */ oldInst = ctxt->inst; oldDocInfo = ctxt->document; oldContextNode = ctxt->node; oldXPDoc = xpctxt->doc; oldXPPos = xpctxt->proximityPosition; oldXPSize = xpctxt->contextSize; oldXPNsNr = xpctxt->nsNr; oldXPNamespaces = xpctxt->namespaces; /* * Set up contexts. */ 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 */ xpctxt->namespaces = keyDef->nsList; xpctxt->nsNr = keyDef->nsNr; /* * Evaluate the 'match' expression of the xsl:key. * TODO: The 'match' is a *pattern*. */ matchRes = xmlXPathCompiledEval(keyDef->comp, xpctxt); if (matchRes == NULL) { #ifdef WITH_XSLT_DEBUG_KEYS 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, "xsltInitCtxtKey: %s evaluates to %d nodes\n", keyDef->match, matchList->nodeNr)); #endif } else { /* * Is not a node set, but must be. */ #ifdef WITH_XSLT_DEBUG_KEYS XSLT_TRACE(ctxt,XSLT_TRACE_KEYS,xsltGenericDebug(xsltGenericDebugContext, "xsltInitCtxtKey: %s is not a node set\n", keyDef->match)); #endif xsltTransformError(ctxt, NULL, keyDef->inst, "The 'match' expression did not evaluate to a node set.\n"); ctxt->state = XSLT_STATE_STOPPED; goto error; } } if ((matchList == NULL) || (matchList->nodeNr <= 0)) goto exit; /** * Multiple key definitions for the same name are allowed, so * we must check if the key is already present for this doc */ table = (xsltKeyTablePtr) idoc->keys; while (table != NULL) { if (xmlStrEqual(table->name, keyDef->name) && (((keyDef->nameURI == NULL) && (table->nameURI == NULL)) || ((keyDef->nameURI != NULL) && (table->nameURI != NULL) && (xmlStrEqual(table->nameURI, keyDef->nameURI))))) break; table = table->next; } /** * If the key was not previously defined, create it now and * chain it to the list of keys for the doc */ if (table == NULL) { table = xsltNewKeyTable(keyDef->name, keyDef->nameURI); if (table == NULL) goto error; table->next = idoc->keys; idoc->keys = table; } /* * SPEC XSLT 1.0 (XSLT 2.0 does not clarify the context size!) * "...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; for (i = 0; i < matchList->nodeNr; i++) { cur = matchList->nodeTab[i]; 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; #ifdef WITH_XSLT_DEBUG_KEYS XSLT_TRACE(ctxt,XSLT_TRACE_KEYS,xsltGenericDebug(xsltGenericDebugContext, "xsl:key : node associated to ('%s', '%s')\n", keyDef->name, str)); #endif keylist = xmlHashLookup(table->keys, str); if (keylist == NULL) { keylist = xmlXPathNodeSetCreate(cur); if (keylist == NULL) goto error; xmlHashAddEntry(table->keys, str, keylist); } else { /* * TODO: How do we know if this function failed? */ xmlXPathNodeSetAdd(keylist, cur); } switch (cur->type) { case XML_ELEMENT_NODE: case XML_TEXT_NODE: case XML_CDATA_SECTION_NODE: case XML_PI_NODE: case XML_COMMENT_NODE: cur->psvi = keyDef; break; case XML_ATTRIBUTE_NODE: ((xmlAttrPtr) cur)->psvi = keyDef; break; case XML_DOCUMENT_NODE: case XML_HTML_DOCUMENT_NODE: ((xmlDocPtr) cur)->psvi = keyDef; break; default: break; } xmlFree(str); str = NULL; next_string: k++; if (k >= len) break; str = xmlXPathCastNodeToString(useRes->nodesetval->nodeTab[k]); } } exit: error: ctxt->keyInitLevel--; /* * Restore context state. */ xpctxt->doc = oldXPDoc; xpctxt->nsNr = oldXPNsNr; xpctxt->namespaces = oldXPNamespaces; 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); }