2 * keys.c: Implemetation of the keys support
5 * http://www.w3.org/TR/1999/REC-xslt-19991116
7 * See Copyright for the status of this software.
17 #include <libxml/xmlmemory.h>
18 #include <libxml/tree.h>
19 #include <libxml/valid.h>
20 #include <libxml/hash.h>
21 #include <libxml/xmlerror.h>
22 #include <libxml/parserInternals.h>
23 #include <libxml/xpathInternals.h>
25 #include "xsltInternals.h"
26 #include "xsltutils.h"
28 #include "templates.h"
31 #ifdef WITH_XSLT_DEBUG
32 #define WITH_XSLT_DEBUG_KEYS
35 typedef struct _xsltKeyDef xsltKeyDef;
36 typedef xsltKeyDef *xsltKeyDefPtr;
38 struct _xsltKeyDef *next;
44 xmlXPathCompExprPtr comp;
45 xmlXPathCompExprPtr usecomp;
48 typedef struct _xsltKeyTable xsltKeyTable;
49 typedef xsltKeyTable *xsltKeyTablePtr;
50 struct _xsltKeyTable {
51 struct _xsltKeyTable *next;
58 /************************************************************************
62 ************************************************************************/
66 * @name: the key name or NULL
67 * @nameURI: the name URI or NULL
69 * Create a new XSLT KeyDef
71 * Returns the newly allocated xsltKeyDefPtr or NULL in case of error
74 xsltNewKeyDef(const xmlChar *name, const xmlChar *nameURI) {
77 cur = (xsltKeyDefPtr) xmlMalloc(sizeof(xsltKeyDef));
79 xsltPrintErrorContext(NULL, NULL, NULL);
80 xsltGenericError(xsltGenericErrorContext,
81 "xsltNewKeyDef : malloc failed\n");
84 memset(cur, 0, sizeof(xsltKeyDef));
86 cur->name = xmlStrdup(name);
88 cur->nameURI = xmlStrdup(nameURI);
94 * @keyd: an XSLT key definition
96 * Free up the memory allocated by @keyd
99 xsltFreeKeyDef(xsltKeyDefPtr keyd) {
102 if (keyd->comp != NULL)
103 xmlXPathFreeCompExpr(keyd->comp);
104 if (keyd->usecomp != NULL)
105 xmlXPathFreeCompExpr(keyd->usecomp);
106 if (keyd->name != NULL)
108 if (keyd->nameURI != NULL)
109 xmlFree(keyd->nameURI);
110 if (keyd->match != NULL)
111 xmlFree(keyd->match);
112 if (keyd->use != NULL)
114 memset(keyd, -1, sizeof(xsltKeyDef));
119 * xsltFreeKeyDefList:
120 * @keyd: an XSLT key definition list
122 * Free up the memory allocated by all the elements of @keyd
125 xsltFreeKeyDefList(xsltKeyDefPtr keyd) {
128 while (keyd != NULL) {
137 * @name: the key name or NULL
138 * @nameURI: the name URI or NULL
140 * Create a new XSLT KeyTable
142 * Returns the newly allocated xsltKeyTablePtr or NULL in case of error
144 static xsltKeyTablePtr
145 xsltNewKeyTable(const xmlChar *name, const xmlChar *nameURI) {
148 cur = (xsltKeyTablePtr) xmlMalloc(sizeof(xsltKeyTable));
150 xsltPrintErrorContext(NULL, NULL, NULL);
151 xsltGenericError(xsltGenericErrorContext,
152 "xsltNewKeyTable : malloc failed\n");
155 memset(cur, 0, sizeof(xsltKeyTable));
157 cur->name = xmlStrdup(name);
159 cur->nameURI = xmlStrdup(nameURI);
160 cur->keys = xmlHashCreate(0);
166 * @keyt: an XSLT key table
168 * Free up the memory allocated by @keyt
171 xsltFreeKeyTable(xsltKeyTablePtr keyt) {
174 if (keyt->name != NULL)
176 if (keyt->nameURI != NULL)
177 xmlFree(keyt->nameURI);
178 if (keyt->keys != NULL)
179 xmlHashFree(keyt->keys,
180 (xmlHashDeallocator) xmlXPathFreeNodeSet);
181 memset(keyt, -1, sizeof(xsltKeyTable));
186 * xsltFreeKeyTableList:
187 * @keyt: an XSLT key table list
189 * Free up the memory allocated by all the elements of @keyt
192 xsltFreeKeyTableList(xsltKeyTablePtr keyt) {
195 while (keyt != NULL) {
198 xsltFreeKeyTable(cur);
202 /************************************************************************
204 * The interpreter for the precompiled patterns *
206 ************************************************************************/
211 * @style: an XSLT stylesheet
213 * Free up the memory used by XSLT keys in a stylesheet
216 xsltFreeKeys(xsltStylesheetPtr style) {
218 xsltFreeKeyDefList((xsltKeyDefPtr) style->keys);
223 * @style: an XSLT stylesheet
224 * @name: the key name or NULL
225 * @nameURI: the name URI or NULL
226 * @match: the match value
227 * @use: the use value
228 * @inst: the key instruction
230 * add a key definition to a stylesheet
232 * Returns 0 in case of success, and -1 in case of failure.
235 xsltAddKey(xsltStylesheetPtr style, const xmlChar *name,
236 const xmlChar *nameURI, const xmlChar *match,
237 const xmlChar *use, xmlNodePtr inst) {
239 xmlChar *pattern = NULL;
240 int current, end, start;
242 if ((style == NULL) || (name == NULL) || (match == NULL) || (use == NULL))
245 #ifdef WITH_XSLT_DEBUG_KEYS
246 xsltGenericDebug(xsltGenericDebugContext,
247 "Add key %s, match %s, use %s\n", name, match, use);
250 key = xsltNewKeyDef(name, nameURI);
251 key->match = xmlStrdup(match);
252 key->use = xmlStrdup(use);
256 * Split the | and register it as as many keys
259 while (match[current] != 0) {
261 while (IS_BLANK(match[current]))
264 while ((match[end] != 0) && (match[end] != '|')) {
267 if (current == end) {
268 xsltPrintErrorContext(NULL, style, inst);
269 xsltGenericError(xsltGenericErrorContext,
270 "key pattern is empty\n");
274 if (match[start] != '/') {
275 pattern = xmlStrcat(pattern, (xmlChar *)"//");
276 if (pattern == NULL) {
281 pattern = xmlStrncat(pattern, &match[start], end - start);
282 if (pattern == NULL) {
287 if (match[end] == '|') {
288 pattern = xmlStrcat(pattern, (xmlChar *)"|");
293 #ifdef WITH_XSLT_DEBUG_KEYS
294 xsltGenericDebug(xsltGenericDebugContext,
295 " resulting pattern %s\n", pattern);
297 key->comp = xmlXPathCompile(pattern);
298 if (key->comp == NULL) {
299 xsltPrintErrorContext(NULL, style, inst);
300 xsltGenericError(xsltGenericErrorContext,
301 "xsl:key : XPath pattern compilation failed '%s'\n",
305 key->usecomp = xmlXPathCompile(use);
306 if (key->usecomp == NULL) {
307 xsltPrintErrorContext(NULL, style, inst);
308 xsltGenericError(xsltGenericErrorContext,
309 "xsl:key : XPath pattern compilation failed '%s'\n",
313 key->next = style->keys;
323 * @ctxt: an XSLT transformation context
324 * @name: the key name or NULL
325 * @nameURI: the name URI or NULL
326 * @value: the key value to look for
330 * Returns the nodeset resulting from the query or NULL
333 xsltGetKey(xsltTransformContextPtr ctxt, const xmlChar *name,
334 const xmlChar *nameURI, const xmlChar *value) {
336 xsltKeyTablePtr table;
338 if ((ctxt == NULL) || (name == NULL) || (value == NULL))
341 #ifdef WITH_XSLT_DEBUG_KEYS
342 xsltGenericDebug(xsltGenericDebugContext,
343 "Get key %s, value %s\n", name, value);
345 table = (xsltKeyTablePtr) ctxt->document->keys;
346 while (table != NULL) {
347 if (xmlStrEqual(table->name, name) &&
348 (((nameURI == NULL) && (table->nameURI == NULL)) ||
349 ((nameURI != NULL) && (table->nameURI != NULL) &&
350 (xmlStrEqual(table->nameURI, nameURI))))) {
351 ret = (xmlNodeSetPtr)xmlHashLookup(table->keys, value);
361 * @ctxt: an XSLT transformation context
362 * @doc: an XSLT document
363 * @keyd: the key definition
365 * Computes the key tables this key and for the current input document.
368 xsltInitCtxtKey(xsltTransformContextPtr ctxt, xsltDocumentPtr doc,
369 xsltKeyDefPtr keyd) {
371 xmlNodeSetPtr nodelist = NULL, keylist;
372 xmlXPathObjectPtr res = NULL;
374 xsltKeyTablePtr table;
377 xsltDocumentPtr oldDoc;
380 xmlNsPtr *oldNamespaces;
383 * Evaluate the nodelist
386 oldXDoc= ctxt->xpathCtxt->doc;
387 oldPos = ctxt->xpathCtxt->proximityPosition;
388 oldSize = ctxt->xpathCtxt->contextSize;
389 oldInst = ctxt->inst;
390 oldDoc = ctxt->document;
391 oldNsNr = ctxt->xpathCtxt->nsNr;
392 oldNamespaces = ctxt->xpathCtxt->namespaces;
394 if (keyd->comp == NULL)
396 if (keyd->usecomp == NULL)
399 ctxt->document = doc;
400 ctxt->xpathCtxt->doc = doc->doc;
401 ctxt->xpathCtxt->node = (xmlNodePtr) doc->doc;
402 ctxt->node = (xmlNodePtr) doc->doc;
403 /* TODO : clarify the use of namespaces in keys evaluation */
404 ctxt->xpathCtxt->namespaces = NULL;
405 ctxt->xpathCtxt->nsNr = 0;
406 ctxt->inst = keyd->inst;
407 res = xmlXPathCompiledEval(keyd->comp, ctxt->xpathCtxt);
408 ctxt->xpathCtxt->contextSize = oldSize;
409 ctxt->xpathCtxt->proximityPosition = oldPos;
410 ctxt->inst = oldInst;
413 if (res->type == XPATH_NODESET) {
414 nodelist = res->nodesetval;
415 #ifdef WITH_XSLT_DEBUG_KEYS
416 if (nodelist != NULL)
417 xsltGenericDebug(xsltGenericDebugContext,
418 "xsltInitCtxtKey: %s evaluates to %d nodes\n",
419 keyd->match, nodelist->nodeNr);
422 #ifdef WITH_XSLT_DEBUG_KEYS
423 xsltGenericDebug(xsltGenericDebugContext,
424 "xsltInitCtxtKey: %s is not a node set\n", keyd->match);
429 #ifdef WITH_XSLT_DEBUG_KEYS
430 xsltGenericDebug(xsltGenericDebugContext,
431 "xsltInitCtxtKey: %s evaluation failed\n", keyd->match);
433 ctxt->state = XSLT_STATE_STOPPED;
438 * for each node in the list evaluate the key and insert the node
440 if ((nodelist == NULL) || (nodelist->nodeNr <= 0))
443 table = xsltNewKeyTable(keyd->name, keyd->nameURI);
447 for (i = 0;i < nodelist->nodeNr;i++) {
448 if (IS_XSLT_REAL_NODE(nodelist->nodeTab[i])) {
449 ctxt->node = nodelist->nodeTab[i];
450 str = xsltEvalXPathString(ctxt, keyd->usecomp);
452 #ifdef WITH_XSLT_DEBUG_KEYS
453 xsltGenericDebug(xsltGenericDebugContext,
454 "xsl:key : node associated to(%s,%s)\n",
457 keylist = xmlHashLookup(table->keys, str);
458 if (keylist == NULL) {
459 keylist = xmlXPathNodeSetCreate(nodelist->nodeTab[i]);
460 xmlHashAddEntry(table->keys, str, keylist);
462 xmlXPathNodeSetAdd(keylist, nodelist->nodeTab[i]);
464 nodelist->nodeTab[i]->_private = keyd;
466 #ifdef WITH_XSLT_DEBUG_KEYS
468 xsltGenericDebug(xsltGenericDebugContext,
469 "xsl:key : use %s failed to return a string\n",
476 table->next = doc->keys;
480 ctxt->document = oldDoc;
481 ctxt->xpathCtxt->doc = oldXDoc;
482 ctxt->xpathCtxt->nsNr = oldNsNr;
483 ctxt->xpathCtxt->namespaces = oldNamespaces;
485 xmlXPathFreeObject(res);
490 * @ctxt: an XSLT transformation context
491 * @doc: an XSLT document
493 * Computes all the keys tables for the current input document.
494 * Should be done before global varibales are initialized.
497 xsltInitCtxtKeys(xsltTransformContextPtr ctxt, xsltDocumentPtr doc) {
498 xsltStylesheetPtr style;
501 if ((ctxt == NULL) || (doc == NULL))
503 #ifdef WITH_XSLT_DEBUG_KEYS
504 if ((doc->doc != NULL) && (doc->doc->URL != NULL))
505 xsltGenericDebug(xsltGenericDebugContext, "Initializing keys on %s\n",
509 while (style != NULL) {
510 keyd = (xsltKeyDefPtr) style->keys;
511 while (keyd != NULL) {
512 xsltInitCtxtKey(ctxt, doc, keyd);
517 style = xsltNextImport(style);
522 * xsltFreeDocumentKeys:
523 * @doc: a XSLT document
525 * Free the keys associated to a document
528 xsltFreeDocumentKeys(xsltDocumentPtr doc) {
530 xsltFreeKeyTableList(doc->keys);