--- /dev/null
+/*
+ * keys.c: Implemetation of the keys support
+ *
+ * Reference:
+ * http://www.w3.org/TR/1999/REC-xslt-19991116
+ *
+ * See Copyright for the status of this software.
+ *
+ * Daniel.Veillard@imag.fr
+ */
+
+#include "xsltconfig.h"
+
+#include <string.h>
+
+#include <libxml/xmlmemory.h>
+#include <libxml/tree.h>
+#include <libxml/valid.h>
+#include <libxml/hash.h>
+#include <libxml/xmlerror.h>
+#include <libxml/parserInternals.h>
+#include <libxml/xpathInternals.h>
+#include "xslt.h"
+#include "xsltInternals.h"
+#include "xsltutils.h"
+#include "imports.h"
+#include "templates.h"
+
+#define DEBUG_KEYS
+
+typedef struct _xsltKeyDef xsltKeyDef;
+typedef xsltKeyDef *xsltKeyDefPtr;
+struct _xsltKeyDef {
+ struct _xsltKeyDef *next;
+ xmlChar *name;
+ xmlChar *nameURI;
+ xmlChar *match;
+ xmlChar *use;
+};
+
+typedef struct _xsltKeyTable xsltKeyTable;
+typedef xsltKeyTable *xsltKeyTablePtr;
+struct _xsltKeyTable {
+ struct _xsltKeyTable *next;
+ xmlChar *name;
+ xmlChar *nameURI;
+ xmlHashTablePtr keys;
+};
+
+
+/************************************************************************
+ * *
+ * Type functions *
+ * *
+ ************************************************************************/
+
+/**
+ * xsltNewKeyDef:
+ * @name: the key name or NULL
+ * @nameURI: the name URI or NULL
+ *
+ * Create a new XSLT KeyDef
+ *
+ * Returns the newly allocated xsltKeyDefPtr or NULL in case of error
+ */
+xsltKeyDefPtr
+xsltNewKeyDef(const xmlChar *name, const xmlChar *nameURI) {
+ xsltKeyDefPtr cur;
+
+ cur = (xsltKeyDefPtr) xmlMalloc(sizeof(xsltKeyDef));
+ if (cur == NULL) {
+ xsltGenericError(xsltGenericErrorContext,
+ "xsltNewKeyDef : malloc failed\n");
+ return(NULL);
+ }
+ memset(cur, 0, sizeof(xsltKeyDef));
+ if (name != NULL)
+ cur->name = xmlStrdup(name);
+ if (nameURI != NULL)
+ cur->nameURI = xmlStrdup(nameURI);
+ return(cur);
+}
+
+/**
+ * xsltFreeKeyDef:
+ * @keyd: an XSLT key definition
+ *
+ * Free up the memory allocated by @keyd
+ */
+void
+xsltFreeKeyDef(xsltKeyDefPtr keyd) {
+ if (keyd == NULL)
+ return;
+ if (keyd->name != NULL)
+ xmlFree(keyd->name);
+ if (keyd->nameURI != NULL)
+ xmlFree(keyd->nameURI);
+ if (keyd->match != NULL)
+ xmlFree(keyd->match);
+ if (keyd->use != NULL)
+ xmlFree(keyd->use);
+ memset(keyd, -1, sizeof(xsltKeyDef));
+ xmlFree(keyd);
+}
+
+/**
+ * xsltFreeKeyDefList:
+ * @keyd: an XSLT key definition list
+ *
+ * Free up the memory allocated by all the elements of @keyd
+ */
+void
+xsltFreeKeyDefList(xsltKeyDefPtr keyd) {
+ xsltKeyDefPtr cur;
+
+ while (keyd != NULL) {
+ cur = keyd;
+ keyd = keyd->next;
+ xsltFreeKeyDef(cur);
+ }
+}
+
+/**
+ * xsltNewKeyTable:
+ * @name: the key name or NULL
+ * @nameURI: the name URI or NULL
+ *
+ * Create a new XSLT KeyTable
+ *
+ * Returns the newly allocated xsltKeyTablePtr or NULL in case of error
+ */
+xsltKeyTablePtr
+xsltNewKeyTable(const xmlChar *name, const xmlChar *nameURI) {
+ xsltKeyTablePtr cur;
+
+ cur = (xsltKeyTablePtr) xmlMalloc(sizeof(xsltKeyTable));
+ if (cur == NULL) {
+ xsltGenericError(xsltGenericErrorContext,
+ "xsltNewKeyTable : malloc failed\n");
+ return(NULL);
+ }
+ memset(cur, 0, sizeof(xsltKeyTable));
+ if (name != NULL)
+ cur->name = xmlStrdup(name);
+ if (nameURI != NULL)
+ cur->nameURI = xmlStrdup(nameURI);
+ cur->keys = xmlHashCreate(0);
+ return(cur);
+}
+
+/**
+ * xsltFreeKeyTable:
+ * @keyt: an XSLT key table
+ *
+ * Free up the memory allocated by @keyt
+ */
+void
+xsltFreeKeyTable(xsltKeyTablePtr keyt) {
+ if (keyt == NULL)
+ return;
+ if (keyt->name != NULL)
+ xmlFree(keyt->name);
+ if (keyt->nameURI != NULL)
+ xmlFree(keyt->nameURI);
+ if (keyt->keys != NULL)
+ xmlHashFree(keyt->keys,
+ (xmlHashDeallocator) xmlXPathFreeNodeSet);
+ memset(keyt, -1, sizeof(xsltKeyTable));
+ xmlFree(keyt);
+}
+
+/**
+ * xsltFreeKeyTableList:
+ * @keyt: an XSLT key table list
+ *
+ * Free up the memory allocated by all the elements of @keyt
+ */
+void
+xsltFreeKeyTableList(xsltKeyTablePtr keyt) {
+ xsltKeyTablePtr cur;
+
+ while (keyt != NULL) {
+ cur = keyt;
+ keyt = keyt->next;
+ xsltFreeKeyTable(cur);
+ }
+}
+
+/************************************************************************
+ * *
+ * The interpreter for the precompiled patterns *
+ * *
+ ************************************************************************/
+
+
+/**
+ * xsltFreeKeys:
+ * @style: an XSLT stylesheet
+ *
+ * Free up the memory used by XSLT keys in a stylesheet
+ */
+void
+xsltFreeKeys(xsltStylesheetPtr style) {
+ if (style->keys)
+ xsltFreeKeyDefList((xsltKeyDefPtr) style->keys);
+}
+
+/**
+ * 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
+ *
+ * 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) {
+ xsltKeyDefPtr key;
+
+ if ((style == NULL) || (name == NULL) || (match == NULL) || (use == NULL))
+ return(-1);
+
+#ifdef 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->next = style->keys;
+ style->keys = key;
+ return(0);
+}
+
+/**
+ * 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
+ *
+ * Lookup a key
+ *
+ * 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;
+
+ if ((ctxt == NULL) || (name == NULL) || (value == NULL))
+ return(NULL);
+
+#ifdef DEBUG_KEYS
+ xsltGenericDebug(xsltGenericDebugContext,
+ "Get key %s, value %s\n", name, value);
+#endif
+
+ table = (xsltKeyTablePtr) ctxt->keys;
+ while (table != NULL) {
+ if (xmlStrEqual(table->name, name) &&
+ (((nameURI == NULL) && (table->nameURI == NULL)) ||
+ ((nameURI != NULL) && (table->nameURI != NULL) &&
+ (xmlStrEqual(table->nameURI, nameURI))))) {
+ ret = (xmlNodeSetPtr)xmlHashLookup(table->keys, value);
+ return(ret);
+ }
+ }
+ return(NULL);
+}
+
+/**
+ * xsltInitCtxtKey:
+ * @ctxt: an XSLT transformation context
+ * @keyd: the key definition
+ *
+ * Computes the key tables this key and for the current input document.
+ */
+void
+xsltInitCtxtKey(xsltTransformContextPtr ctxt, xsltKeyDefPtr keyd) {
+ int i;
+ xmlChar *pattern = NULL;
+ xmlNodeSetPtr nodelist = NULL, keylist;
+ xmlXPathObjectPtr res, tmp;
+ xmlChar *str;
+ xmlXPathParserContextPtr xpathParserCtxt;
+ xsltKeyTablePtr table;
+
+ /*
+ * Prepare the search
+ */
+ if (keyd->match[0] != '/') {
+ pattern = xmlStrdup((xmlChar *)"//");
+ pattern = xmlStrcat(pattern, keyd->match);
+ } else {
+ pattern = xmlStrdup(keyd->match);
+ }
+
+ /*
+ * Evaluate the nodelist
+ */
+
+ xpathParserCtxt =
+ xmlXPathNewParserContext(pattern, ctxt->xpathCtxt);
+ if (xpathParserCtxt == NULL)
+ goto error;
+ ctxt->node = (xmlNodePtr) ctxt->doc;
+ ctxt->xpathCtxt->node = (xmlNodePtr) ctxt->doc;
+ xmlXPathEvalExpr(xpathParserCtxt);
+ res = valuePop(xpathParserCtxt);
+ do {
+ tmp = valuePop(xpathParserCtxt);
+ if (tmp != NULL) {
+ xmlXPathFreeObject(tmp);
+ }
+ } while (tmp != NULL);
+ if (res != NULL) {
+ if (res->type == XPATH_NODESET) {
+ nodelist = res->nodesetval;
+#ifdef DEBUG_KEYS
+ xsltGenericDebug(xsltGenericDebugContext,
+ "xsltInitCtxtKey: %s evaluates to %d nodes\n",
+ pattern, nodelist->nodeNr);
+#endif
+ } else {
+#ifdef DEBUG_KEYS
+ xsltGenericDebug(xsltGenericDebugContext,
+ "xsltInitCtxtKey: %s is not a node set\n", pattern);
+#endif
+ goto error;
+ }
+ } else {
+#ifdef DEBUG_KEYS
+ xsltGenericDebug(xsltGenericDebugContext,
+ "xsltInitCtxtKey: %s evaluation failed\n", pattern);
+#endif
+ goto error;
+ }
+
+ /*
+ * for each node in the list evaluate the key and insert the node
+ */
+ if ((nodelist == NULL) || (nodelist->nodeNr <= 0))
+ goto error;
+
+ table = xsltNewKeyTable(keyd->name, keyd->nameURI);
+ if (table == NULL)
+ goto error;
+
+ for (i = 0;i < nodelist->nodeNr;i++) {
+ ctxt->node = nodelist->nodeTab[i];
+ str = xsltEvalXPathString(ctxt, keyd->use);
+ if (str != NULL) {
+#ifdef DEBUG_KEYS
+ xsltGenericDebug(xsltGenericDebugContext,
+ "xsl:key : node associated to(%s,%s)\n",
+ keyd->name, str);
+#endif
+ keylist = xmlHashLookup(table->keys, str);
+ if (keylist == NULL) {
+ keylist = xmlXPathNodeSetCreate(nodelist->nodeTab[i]);
+ xmlHashAddEntry(table->keys, str, keylist);
+ } else {
+ xmlXPathNodeSetAdd(keylist, nodelist->nodeTab[i]);
+ }
+ xmlFree(str);
+#ifdef DEBUG_KEYS
+ } else {
+ xsltGenericDebug(xsltGenericDebugContext,
+ "xsl:key : use %s failed to return a string\n",
+ keyd->use);
+#endif
+ }
+ }
+
+ table->next = ctxt->keys;
+ ctxt->keys = table;
+
+error:
+ if (res != NULL)
+ xmlXPathFreeObject(res);
+ if (xpathParserCtxt != NULL)
+ xmlXPathFreeParserContext(xpathParserCtxt);
+ if (pattern != NULL)
+ xmlFree(pattern);
+}
+
+/**
+ * xsltInitCtxtKeys:
+ * @ctxt: an XSLT transformation context
+ *
+ * Computes all the keys tables for the current input document.
+ * Should be done before global varibales are initialized.
+ */
+void
+xsltInitCtxtKeys(xsltTransformContextPtr ctxt) {
+ xsltStylesheetPtr style;
+ xsltKeyDefPtr keyd;
+
+ if (ctxt == NULL)
+ return;
+ style = ctxt->style;
+ while (style != NULL) {
+ keyd = (xsltKeyDefPtr) style->keys;
+ while (keyd != NULL) {
+ xsltInitCtxtKey(ctxt, keyd);
+
+ keyd = keyd->next;
+ }
+
+ style = xsltNextImport(style);
+ }
+}
+
+/**
+ * xsltFreeCtxtKeys:
+ * @ctxt: an XSLT transformation context
+ *
+ * Free up all the space used by the key tables
+ */
+void
+xsltFreeCtxtKeys(xsltTransformContextPtr ctxt) {
+ if (ctxt->keys)
+ xsltFreeKeyTableList((xsltKeyTablePtr) ctxt->keys);
+}
+
#include "attributes.h"
#include "xsltutils.h"
#include "imports.h"
+#include "keys.h"
#define DEBUG_PARSING
/* #define DEBUG_BLANKS */
if (sheet == NULL)
return;
+ xsltFreeKeys(sheet);
xsltFreeTemplateHashes(sheet);
xsltFreeDecimalFormatList(sheet);
xsltFreeTemplateList(sheet->templates);
}
/**
+ * xsltParseStylesheetKey:
+ * @style: the XSLT stylesheet
+ * @key: the "key" element
+ *
+ * parse an XSLT stylesheet key definition and register it
+ */
+
+void
+xsltParseStylesheetKey(xsltStylesheetPtr style, xmlNodePtr key) {
+ xmlChar *prop;
+ xmlChar *use;
+ xmlChar *match;
+ xmlChar *name;
+ xmlChar *nameURI;
+
+ if (key == NULL)
+ return;
+
+ /*
+ * Get arguments
+ */
+ prop = xmlGetNsProp(key, (const xmlChar *)"name", XSLT_NAMESPACE);
+ if (prop != NULL) {
+ xmlChar *prefix = NULL;
+
+ name = xmlSplitQName2(prop, &prefix);
+ if (name != NULL) {
+ if (prefix != NULL) {
+ xmlNsPtr ns;
+
+ ns = xmlSearchNs(key->doc, key, prefix);
+ if (ns == NULL) {
+ xsltGenericError(xsltGenericErrorContext,
+ "no namespace bound to prefix %s\n", prefix);
+ xmlFree(prefix);
+ xmlFree(name);
+ name = prop;
+ nameURI = NULL;
+ } else {
+ nameURI = xmlStrdup(ns->href);
+ xmlFree(prefix);
+ xmlFree(prop);
+ }
+ } else {
+ xmlFree(prop);
+ nameURI = NULL;
+ }
+ } else {
+ name = prop;
+ nameURI = NULL;
+ }
+#ifdef DEBUG_PARSING
+ xsltGenericDebug(xsltGenericDebugContext,
+ "xslt:key: name %s\n", name);
+#endif
+ } else {
+ xsltGenericError(xsltGenericErrorContext,
+ "xsl:key : error missing name\n");
+ goto error;
+ }
+
+ match = xmlGetNsProp(key, (const xmlChar *)"match", XSLT_NAMESPACE);
+ if (match == NULL) {
+ xsltGenericError(xsltGenericErrorContext,
+ "xsl:key : error missing match\n");
+ goto error;
+ }
+
+ use = xmlGetNsProp(key, (const xmlChar *)"use", XSLT_NAMESPACE);
+ if (use == NULL) {
+ xsltGenericError(xsltGenericErrorContext,
+ "xsl:key : error missing use\n");
+ goto error;
+ }
+
+ /*
+ * register the key
+ */
+ xsltAddKey(style, name, nameURI, match, use);
+
+error:
+ if (use != NULL)
+ xmlFree(use);
+ if (match != NULL)
+ xmlFree(match);
+ if (name != NULL)
+ xmlFree(name);
+ if (nameURI != NULL)
+ xmlFree(nameURI);
+}
+
+/**
* xsltParseStylesheetTemplate:
* @style: the XSLT stylesheet
* @template: the "template" element
} else if (IS_XSLT_NAME(cur, "output")) {
xsltParseStylesheetOutput(style, cur);
} else if (IS_XSLT_NAME(cur, "key")) {
- TODO /* Handle key */
+ xsltParseStylesheetKey(style, cur);
} else if (IS_XSLT_NAME(cur, "decimal-format")) {
xsltParseStylesheetDecimalFormat(style, cur);
} else if (IS_XSLT_NAME(cur, "attribute-set")) {