1 | /*
|
---|
2 | * keys.c: Implemetation of the keys support
|
---|
3 | *
|
---|
4 | * Reference:
|
---|
5 | * http://www.w3.org/TR/1999/REC-xslt-19991116
|
---|
6 | *
|
---|
7 | * See Copyright for the status of this software.
|
---|
8 | *
|
---|
9 | * [email protected]
|
---|
10 | */
|
---|
11 |
|
---|
12 | #define IN_LIBXSLT
|
---|
13 | #include "libxslt.h"
|
---|
14 |
|
---|
15 | #include <string.h>
|
---|
16 |
|
---|
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>
|
---|
24 | #include "xslt.h"
|
---|
25 | #include "xsltInternals.h"
|
---|
26 | #include "xsltutils.h"
|
---|
27 | #include "imports.h"
|
---|
28 | #include "templates.h"
|
---|
29 | #include "keys.h"
|
---|
30 |
|
---|
31 | #ifdef WITH_XSLT_DEBUG
|
---|
32 | #define WITH_XSLT_DEBUG_KEYS
|
---|
33 | #endif
|
---|
34 |
|
---|
35 |
|
---|
36 | /************************************************************************
|
---|
37 | * *
|
---|
38 | * Type functions *
|
---|
39 | * *
|
---|
40 | ************************************************************************/
|
---|
41 |
|
---|
42 | /**
|
---|
43 | * xsltNewKeyDef:
|
---|
44 | * @name: the key name or NULL
|
---|
45 | * @nameURI: the name URI or NULL
|
---|
46 | *
|
---|
47 | * Create a new XSLT KeyDef
|
---|
48 | *
|
---|
49 | * Returns the newly allocated xsltKeyDefPtr or NULL in case of error
|
---|
50 | */
|
---|
51 | static xsltKeyDefPtr
|
---|
52 | xsltNewKeyDef(const xmlChar *name, const xmlChar *nameURI) {
|
---|
53 | xsltKeyDefPtr cur;
|
---|
54 |
|
---|
55 | cur = (xsltKeyDefPtr) xmlMalloc(sizeof(xsltKeyDef));
|
---|
56 | if (cur == NULL) {
|
---|
57 | xsltTransformError(NULL, NULL, NULL,
|
---|
58 | "xsltNewKeyDef : malloc failed\n");
|
---|
59 | return(NULL);
|
---|
60 | }
|
---|
61 | memset(cur, 0, sizeof(xsltKeyDef));
|
---|
62 | if (name != NULL)
|
---|
63 | cur->name = xmlStrdup(name);
|
---|
64 | if (nameURI != NULL)
|
---|
65 | cur->nameURI = xmlStrdup(nameURI);
|
---|
66 | cur->nsList = NULL;
|
---|
67 | return(cur);
|
---|
68 | }
|
---|
69 |
|
---|
70 | /**
|
---|
71 | * xsltFreeKeyDef:
|
---|
72 | * @keyd: an XSLT key definition
|
---|
73 | *
|
---|
74 | * Free up the memory allocated by @keyd
|
---|
75 | */
|
---|
76 | static void
|
---|
77 | xsltFreeKeyDef(xsltKeyDefPtr keyd) {
|
---|
78 | if (keyd == NULL)
|
---|
79 | return;
|
---|
80 | if (keyd->comp != NULL)
|
---|
81 | xmlXPathFreeCompExpr(keyd->comp);
|
---|
82 | if (keyd->usecomp != NULL)
|
---|
83 | xmlXPathFreeCompExpr(keyd->usecomp);
|
---|
84 | if (keyd->name != NULL)
|
---|
85 | xmlFree(keyd->name);
|
---|
86 | if (keyd->nameURI != NULL)
|
---|
87 | xmlFree(keyd->nameURI);
|
---|
88 | if (keyd->match != NULL)
|
---|
89 | xmlFree(keyd->match);
|
---|
90 | if (keyd->use != NULL)
|
---|
91 | xmlFree(keyd->use);
|
---|
92 | if (keyd->nsList != NULL)
|
---|
93 | xmlFree(keyd->nsList);
|
---|
94 | memset(keyd, -1, sizeof(xsltKeyDef));
|
---|
95 | xmlFree(keyd);
|
---|
96 | }
|
---|
97 |
|
---|
98 | /**
|
---|
99 | * xsltFreeKeyDefList:
|
---|
100 | * @keyd: an XSLT key definition list
|
---|
101 | *
|
---|
102 | * Free up the memory allocated by all the elements of @keyd
|
---|
103 | */
|
---|
104 | static void
|
---|
105 | xsltFreeKeyDefList(xsltKeyDefPtr keyd) {
|
---|
106 | xsltKeyDefPtr cur;
|
---|
107 |
|
---|
108 | while (keyd != NULL) {
|
---|
109 | cur = keyd;
|
---|
110 | keyd = keyd->next;
|
---|
111 | xsltFreeKeyDef(cur);
|
---|
112 | }
|
---|
113 | }
|
---|
114 |
|
---|
115 | /**
|
---|
116 | * xsltNewKeyTable:
|
---|
117 | * @name: the key name or NULL
|
---|
118 | * @nameURI: the name URI or NULL
|
---|
119 | *
|
---|
120 | * Create a new XSLT KeyTable
|
---|
121 | *
|
---|
122 | * Returns the newly allocated xsltKeyTablePtr or NULL in case of error
|
---|
123 | */
|
---|
124 | static xsltKeyTablePtr
|
---|
125 | xsltNewKeyTable(const xmlChar *name, const xmlChar *nameURI) {
|
---|
126 | xsltKeyTablePtr cur;
|
---|
127 |
|
---|
128 | cur = (xsltKeyTablePtr) xmlMalloc(sizeof(xsltKeyTable));
|
---|
129 | if (cur == NULL) {
|
---|
130 | xsltTransformError(NULL, NULL, NULL,
|
---|
131 | "xsltNewKeyTable : malloc failed\n");
|
---|
132 | return(NULL);
|
---|
133 | }
|
---|
134 | memset(cur, 0, sizeof(xsltKeyTable));
|
---|
135 | if (name != NULL)
|
---|
136 | cur->name = xmlStrdup(name);
|
---|
137 | if (nameURI != NULL)
|
---|
138 | cur->nameURI = xmlStrdup(nameURI);
|
---|
139 | cur->keys = xmlHashCreate(0);
|
---|
140 | return(cur);
|
---|
141 | }
|
---|
142 |
|
---|
143 | /**
|
---|
144 | * xsltFreeKeyTable:
|
---|
145 | * @keyt: an XSLT key table
|
---|
146 | *
|
---|
147 | * Free up the memory allocated by @keyt
|
---|
148 | */
|
---|
149 | static void
|
---|
150 | xsltFreeKeyTable(xsltKeyTablePtr keyt) {
|
---|
151 | if (keyt == NULL)
|
---|
152 | return;
|
---|
153 | if (keyt->name != NULL)
|
---|
154 | xmlFree(keyt->name);
|
---|
155 | if (keyt->nameURI != NULL)
|
---|
156 | xmlFree(keyt->nameURI);
|
---|
157 | if (keyt->keys != NULL)
|
---|
158 | xmlHashFree(keyt->keys,
|
---|
159 | (xmlHashDeallocator) xmlXPathFreeNodeSet);
|
---|
160 | memset(keyt, -1, sizeof(xsltKeyTable));
|
---|
161 | xmlFree(keyt);
|
---|
162 | }
|
---|
163 |
|
---|
164 | /**
|
---|
165 | * xsltFreeKeyTableList:
|
---|
166 | * @keyt: an XSLT key table list
|
---|
167 | *
|
---|
168 | * Free up the memory allocated by all the elements of @keyt
|
---|
169 | */
|
---|
170 | static void
|
---|
171 | xsltFreeKeyTableList(xsltKeyTablePtr keyt) {
|
---|
172 | xsltKeyTablePtr cur;
|
---|
173 |
|
---|
174 | while (keyt != NULL) {
|
---|
175 | cur = keyt;
|
---|
176 | keyt = keyt->next;
|
---|
177 | xsltFreeKeyTable(cur);
|
---|
178 | }
|
---|
179 | }
|
---|
180 |
|
---|
181 | /************************************************************************
|
---|
182 | * *
|
---|
183 | * The interpreter for the precompiled patterns *
|
---|
184 | * *
|
---|
185 | ************************************************************************/
|
---|
186 |
|
---|
187 |
|
---|
188 | /**
|
---|
189 | * xsltFreeKeys:
|
---|
190 | * @style: an XSLT stylesheet
|
---|
191 | *
|
---|
192 | * Free up the memory used by XSLT keys in a stylesheet
|
---|
193 | */
|
---|
194 | void
|
---|
195 | xsltFreeKeys(xsltStylesheetPtr style) {
|
---|
196 | if (style->keys)
|
---|
197 | xsltFreeKeyDefList((xsltKeyDefPtr) style->keys);
|
---|
198 | }
|
---|
199 |
|
---|
200 | /**
|
---|
201 | * skipString:
|
---|
202 | * @cur: the current pointer
|
---|
203 | * @end: the current offset
|
---|
204 | *
|
---|
205 | * skip a string delimited by " or '
|
---|
206 | *
|
---|
207 | * Returns the byte after the string or -1 in case of error
|
---|
208 | */
|
---|
209 | static int
|
---|
210 | skipString(const xmlChar *cur, int end) {
|
---|
211 | xmlChar limit;
|
---|
212 |
|
---|
213 | if ((cur == NULL) || (end < 0)) return(-1);
|
---|
214 | if ((cur[end] == '\'') || (cur[end] == '"')) limit = cur[end];
|
---|
215 | else return(end);
|
---|
216 | end++;
|
---|
217 | while (cur[end] != 0) {
|
---|
218 | if (cur[end] == limit)
|
---|
219 | return(end + 1);
|
---|
220 | end++;
|
---|
221 | }
|
---|
222 | return(-1);
|
---|
223 | }
|
---|
224 |
|
---|
225 | /**
|
---|
226 | * skipPredicate:
|
---|
227 | * @cur: the current pointer
|
---|
228 | * @end: the current offset
|
---|
229 | *
|
---|
230 | * skip a predicate
|
---|
231 | *
|
---|
232 | * Returns the byte after the predicate or -1 in case of error
|
---|
233 | */
|
---|
234 | static int
|
---|
235 | skipPredicate(const xmlChar *cur, int end) {
|
---|
236 | if ((cur == NULL) || (end < 0)) return(-1);
|
---|
237 | if (cur[end] != '[') return(end);
|
---|
238 | end++;
|
---|
239 | while (cur[end] != 0) {
|
---|
240 | if ((cur[end] == '\'') || (cur[end] == '"')) {
|
---|
241 | end = skipString(cur, end);
|
---|
242 | if (end <= 0)
|
---|
243 | return(-1);
|
---|
244 | continue;
|
---|
245 | } else if (cur[end] == '[') {
|
---|
246 | end = skipPredicate(cur, end);
|
---|
247 | if (end <= 0)
|
---|
248 | return(-1);
|
---|
249 | continue;
|
---|
250 | } else if (cur[end] == ']')
|
---|
251 | return(end + 1);
|
---|
252 | end++;
|
---|
253 | }
|
---|
254 | return(-1);
|
---|
255 | }
|
---|
256 |
|
---|
257 | /**
|
---|
258 | * xsltAddKey:
|
---|
259 | * @style: an XSLT stylesheet
|
---|
260 | * @name: the key name or NULL
|
---|
261 | * @nameURI: the name URI or NULL
|
---|
262 | * @match: the match value
|
---|
263 | * @use: the use value
|
---|
264 | * @inst: the key instruction
|
---|
265 | *
|
---|
266 | * add a key definition to a stylesheet
|
---|
267 | *
|
---|
268 | * Returns 0 in case of success, and -1 in case of failure.
|
---|
269 | */
|
---|
270 | int
|
---|
271 | xsltAddKey(xsltStylesheetPtr style, const xmlChar *name,
|
---|
272 | const xmlChar *nameURI, const xmlChar *match,
|
---|
273 | const xmlChar *use, xmlNodePtr inst) {
|
---|
274 | xsltKeyDefPtr key;
|
---|
275 | xmlChar *pattern = NULL;
|
---|
276 | int current, end, start, i = 0;
|
---|
277 |
|
---|
278 | if ((style == NULL) || (name == NULL) || (match == NULL) || (use == NULL))
|
---|
279 | return(-1);
|
---|
280 |
|
---|
281 | #ifdef WITH_XSLT_DEBUG_KEYS
|
---|
282 | xsltGenericDebug(xsltGenericDebugContext,
|
---|
283 | "Add key %s, match %s, use %s\n", name, match, use);
|
---|
284 | #endif
|
---|
285 |
|
---|
286 | key = xsltNewKeyDef(name, nameURI);
|
---|
287 | key->match = xmlStrdup(match);
|
---|
288 | key->use = xmlStrdup(use);
|
---|
289 | key->inst = inst;
|
---|
290 | key->nsList = xmlGetNsList(inst->doc, inst);
|
---|
291 | if (key->nsList != NULL) {
|
---|
292 | while (key->nsList[i] != NULL)
|
---|
293 | i++;
|
---|
294 | }
|
---|
295 | key->nsNr = i;
|
---|
296 |
|
---|
297 | /*
|
---|
298 | * Split the | and register it as as many keys
|
---|
299 | */
|
---|
300 | current = end = 0;
|
---|
301 | while (match[current] != 0) {
|
---|
302 | start = current;
|
---|
303 | while (IS_BLANK_CH(match[current]))
|
---|
304 | current++;
|
---|
305 | end = current;
|
---|
306 | while ((match[end] != 0) && (match[end] != '|')) {
|
---|
307 | if (match[end] == '[') {
|
---|
308 | end = skipPredicate(match, end);
|
---|
309 | if (end <= 0) {
|
---|
310 | xsltTransformError(NULL, style, inst,
|
---|
311 | "key pattern is malformed: %s",
|
---|
312 | key->match);
|
---|
313 | if (style != NULL) style->errors++;
|
---|
314 | goto error;
|
---|
315 | }
|
---|
316 | } else
|
---|
317 | end++;
|
---|
318 | }
|
---|
319 | if (current == end) {
|
---|
320 | xsltTransformError(NULL, style, inst,
|
---|
321 | "key pattern is empty\n");
|
---|
322 | if (style != NULL) style->errors++;
|
---|
323 | goto error;
|
---|
324 | }
|
---|
325 | if (match[start] != '/') {
|
---|
326 | pattern = xmlStrcat(pattern, (xmlChar *)"//");
|
---|
327 | if (pattern == NULL) {
|
---|
328 | if (style != NULL) style->errors++;
|
---|
329 | goto error;
|
---|
330 | }
|
---|
331 | }
|
---|
332 | pattern = xmlStrncat(pattern, &match[start], end - start);
|
---|
333 | if (pattern == NULL) {
|
---|
334 | if (style != NULL) style->errors++;
|
---|
335 | goto error;
|
---|
336 | }
|
---|
337 |
|
---|
338 | if (match[end] == '|') {
|
---|
339 | pattern = xmlStrcat(pattern, (xmlChar *)"|");
|
---|
340 | end++;
|
---|
341 | }
|
---|
342 | current = end;
|
---|
343 | }
|
---|
344 | #ifdef WITH_XSLT_DEBUG_KEYS
|
---|
345 | xsltGenericDebug(xsltGenericDebugContext,
|
---|
346 | " resulting pattern %s\n", pattern);
|
---|
347 | #endif
|
---|
348 | /*
|
---|
349 | * XSLT-1: "It is an error for the value of either the use
|
---|
350 | * attribute or the match attribute to contain a
|
---|
351 | * VariableReference."
|
---|
352 | * TODO: We should report a variable-reference at compile-time.
|
---|
353 | * Maybe a search for "$", if it occurs outside of quotation
|
---|
354 | * marks, could be sufficient.
|
---|
355 | */
|
---|
356 | key->comp = xsltXPathCompile(style, pattern);
|
---|
357 | if (key->comp == NULL) {
|
---|
358 | xsltTransformError(NULL, style, inst,
|
---|
359 | "xsl:key : XPath pattern compilation failed '%s'\n",
|
---|
360 | pattern);
|
---|
361 | if (style != NULL) style->errors++;
|
---|
362 | }
|
---|
363 | key->usecomp = xsltXPathCompile(style, use);
|
---|
364 | if (key->usecomp == NULL) {
|
---|
365 | xsltTransformError(NULL, style, inst,
|
---|
366 | "xsl:key : XPath pattern compilation failed '%s'\n",
|
---|
367 | use);
|
---|
368 | if (style != NULL) style->errors++;
|
---|
369 | }
|
---|
370 | key->next = style->keys;
|
---|
371 | style->keys = key;
|
---|
372 | error:
|
---|
373 | if (pattern != NULL)
|
---|
374 | xmlFree(pattern);
|
---|
375 | return(0);
|
---|
376 | }
|
---|
377 |
|
---|
378 | /**
|
---|
379 | * xsltGetKey:
|
---|
380 | * @ctxt: an XSLT transformation context
|
---|
381 | * @name: the key name or NULL
|
---|
382 | * @nameURI: the name URI or NULL
|
---|
383 | * @value: the key value to look for
|
---|
384 | *
|
---|
385 | * Looks up a key of the in current source doc (the document info
|
---|
386 | * on @ctxt->document). Computes the key if not already done
|
---|
387 | * for the current source doc.
|
---|
388 | *
|
---|
389 | * Returns the nodeset resulting from the query or NULL
|
---|
390 | */
|
---|
391 | xmlNodeSetPtr
|
---|
392 | xsltGetKey(xsltTransformContextPtr ctxt, const xmlChar *name,
|
---|
393 | const xmlChar *nameURI, const xmlChar *value) {
|
---|
394 | xmlNodeSetPtr ret;
|
---|
395 | xsltKeyTablePtr table;
|
---|
396 | #ifdef XSLT_REFACTORED_KEYCOMP
|
---|
397 | int found = 0;
|
---|
398 | #endif
|
---|
399 |
|
---|
400 | if ((ctxt == NULL) || (name == NULL) || (value == NULL) ||
|
---|
401 | (ctxt->document == NULL))
|
---|
402 | return(NULL);
|
---|
403 |
|
---|
404 | #ifdef WITH_XSLT_DEBUG_KEYS
|
---|
405 | xsltGenericDebug(xsltGenericDebugContext,
|
---|
406 | "Get key %s, value %s\n", name, value);
|
---|
407 | #endif
|
---|
408 |
|
---|
409 | table = (xsltKeyTablePtr) ctxt->document->keys;
|
---|
410 | while (table != NULL) {
|
---|
411 | if (((nameURI != NULL) == (table->nameURI != NULL)) &&
|
---|
412 | xmlStrEqual(table->name, name) &&
|
---|
413 | xmlStrEqual(table->nameURI, nameURI))
|
---|
414 | {
|
---|
415 | #ifdef XSLT_REFACTORED_KEYCOMP
|
---|
416 | found = 1;
|
---|
417 | #endif
|
---|
418 | ret = (xmlNodeSetPtr)xmlHashLookup(table->keys, value);
|
---|
419 | return(ret);
|
---|
420 | }
|
---|
421 | table = table->next;
|
---|
422 | }
|
---|
423 | #ifdef XSLT_REFACTORED_KEYCOMP
|
---|
424 | if (! found) {
|
---|
425 | xsltStylesheetPtr style = ctxt->style;
|
---|
426 | xsltKeyDefPtr keyd;
|
---|
427 | /*
|
---|
428 | * This might be the first call to the key with the specified
|
---|
429 | * name and the specified document.
|
---|
430 | * Find all keys with a matching name and compute them for the
|
---|
431 | * current tree.
|
---|
432 | */
|
---|
433 | found = 0;
|
---|
434 | while (style != NULL) {
|
---|
435 | keyd = (xsltKeyDefPtr) style->keys;
|
---|
436 | while (keyd != NULL) {
|
---|
437 | if (((nameURI != NULL) == (keyd->nameURI != NULL)) &&
|
---|
438 | xmlStrEqual(keyd->name, name) &&
|
---|
439 | xmlStrEqual(keyd->nameURI, nameURI))
|
---|
440 | {
|
---|
441 | found = 1;
|
---|
442 | xsltInitCtxtKey(ctxt, ctxt->document, keyd);
|
---|
443 | }
|
---|
444 | keyd = keyd->next;
|
---|
445 | }
|
---|
446 | style = xsltNextImport(style);
|
---|
447 | }
|
---|
448 | if (found) {
|
---|
449 | /*
|
---|
450 | * The key was computed, so look it up.
|
---|
451 | */
|
---|
452 | table = (xsltKeyTablePtr) ctxt->document->keys;
|
---|
453 | while (table != NULL) {
|
---|
454 | if (((nameURI != NULL) == (table->nameURI != NULL)) &&
|
---|
455 | xmlStrEqual(table->name, name) &&
|
---|
456 | xmlStrEqual(table->nameURI, nameURI))
|
---|
457 | {
|
---|
458 | ret = (xmlNodeSetPtr)xmlHashLookup(table->keys, value);
|
---|
459 | return(ret);
|
---|
460 | }
|
---|
461 | table = table->next;
|
---|
462 | }
|
---|
463 |
|
---|
464 | }
|
---|
465 | }
|
---|
466 | #endif
|
---|
467 | return(NULL);
|
---|
468 | }
|
---|
469 |
|
---|
470 | #if 0 /* Merged with xsltInitCtxtKey() */
|
---|
471 | /**
|
---|
472 | * xsltEvalXPathKeys:
|
---|
473 | * @ctxt: the XSLT transformation context
|
---|
474 | * @comp: the compiled XPath expression
|
---|
475 | *
|
---|
476 | * Process the expression using XPath to get the list of keys
|
---|
477 | *
|
---|
478 | * Returns the array of computed string value or NULL, must be deallocated
|
---|
479 | * by the caller.
|
---|
480 | */
|
---|
481 | static xmlChar **
|
---|
482 | xsltEvalXPathKeys(xsltTransformContextPtr ctxt, xmlXPathCompExprPtr comp,
|
---|
483 | xsltKeyDefPtr keyd) {
|
---|
484 | xmlChar **ret = NULL;
|
---|
485 | xmlXPathObjectPtr res;
|
---|
486 | xmlNodePtr oldInst;
|
---|
487 | xmlNodePtr oldNode;
|
---|
488 | int oldPos, oldSize;
|
---|
489 | int oldNsNr;
|
---|
490 | xmlNsPtr *oldNamespaces;
|
---|
491 |
|
---|
492 | oldInst = ctxt->inst;
|
---|
493 | oldNode = ctxt->node;
|
---|
494 | oldPos = ctxt->xpathCtxt->proximityPosition;
|
---|
495 | oldSize = ctxt->xpathCtxt->contextSize;
|
---|
496 | oldNsNr = ctxt->xpathCtxt->nsNr;
|
---|
497 | oldNamespaces = ctxt->xpathCtxt->namespaces;
|
---|
498 |
|
---|
499 | ctxt->xpathCtxt->node = ctxt->node;
|
---|
500 | ctxt->xpathCtxt->namespaces = keyd->nsList;
|
---|
501 | ctxt->xpathCtxt->nsNr = keyd->nsNr;
|
---|
502 | res = xmlXPathCompiledEval(comp, ctxt->xpathCtxt);
|
---|
503 | if (res != NULL) {
|
---|
504 | if (res->type == XPATH_NODESET) {
|
---|
505 | int len, i, j;
|
---|
506 |
|
---|
507 | if (res->nodesetval != NULL)
|
---|
508 | len = res->nodesetval->nodeNr;
|
---|
509 | else
|
---|
510 | len = 0;
|
---|
511 | if (len != 0) {
|
---|
512 | ret = (xmlChar **) xmlMalloc((len + 1) * sizeof(xmlChar *));
|
---|
513 | if (ret != NULL) {
|
---|
514 | for (i = 0,j = 0;i < len;i++) {
|
---|
515 | ret[j] = xmlXPathCastNodeToString(
|
---|
516 | res->nodesetval->nodeTab[i]);
|
---|
517 | if (ret[j] != NULL)
|
---|
518 | j++;
|
---|
519 | }
|
---|
520 | ret[j] = NULL;
|
---|
521 | }
|
---|
522 | }
|
---|
523 | } else {
|
---|
524 | if (res->type != XPATH_STRING)
|
---|
525 | res = xmlXPathConvertString(res);
|
---|
526 | if (res->type == XPATH_STRING) {
|
---|
527 | ret = (xmlChar **) xmlMalloc(2 * sizeof(xmlChar *));
|
---|
528 | if (ret != NULL) {
|
---|
529 | ret[0] = res->stringval;
|
---|
530 | ret[1] = NULL;
|
---|
531 | res->stringval = NULL;
|
---|
532 | }
|
---|
533 | } else {
|
---|
534 | xsltTransformError(ctxt, NULL, NULL,
|
---|
535 | "xpath : string() function didn't return a String\n");
|
---|
536 | }
|
---|
537 | }
|
---|
538 | xmlXPathFreeObject(res);
|
---|
539 | } else {
|
---|
540 | ctxt->state = XSLT_STATE_STOPPED;
|
---|
541 | }
|
---|
542 | #ifdef WITH_XSLT_DEBUG_TEMPLATES
|
---|
543 | xsltGenericDebug(xsltGenericDebugContext,
|
---|
544 | "xsltEvalXPathString: returns %s\n", ret);
|
---|
545 | #endif
|
---|
546 | ctxt->inst = oldInst;
|
---|
547 | ctxt->node = oldNode;
|
---|
548 | ctxt->xpathCtxt->contextSize = oldSize;
|
---|
549 | ctxt->xpathCtxt->proximityPosition = oldPos;
|
---|
550 | ctxt->xpathCtxt->nsNr = oldNsNr;
|
---|
551 | ctxt->xpathCtxt->namespaces = oldNamespaces;
|
---|
552 | return(ret);
|
---|
553 | }
|
---|
554 | #endif
|
---|
555 |
|
---|
556 | /**
|
---|
557 | * xsltInitCtxtKey:
|
---|
558 | * @ctxt: an XSLT transformation context
|
---|
559 | * @idoc: the document information (holds key values)
|
---|
560 | * @keyDef: the key definition
|
---|
561 | *
|
---|
562 | * Computes the key tables this key and for the current input document.
|
---|
563 | *
|
---|
564 | * Returns: 0 on success, -1 on error
|
---|
565 | */
|
---|
566 | int
|
---|
567 | xsltInitCtxtKey(xsltTransformContextPtr ctxt, xsltDocumentPtr idoc,
|
---|
568 | xsltKeyDefPtr keyDef)
|
---|
569 | {
|
---|
570 | int i, len, k;
|
---|
571 | xmlNodeSetPtr matchList = NULL, keylist;
|
---|
572 | xmlXPathObjectPtr matchRes = NULL, useRes = NULL;
|
---|
573 | xmlChar *str = NULL;
|
---|
574 | xsltKeyTablePtr table;
|
---|
575 | xmlNodePtr oldInst, cur;
|
---|
576 | xmlNodePtr oldContextNode;
|
---|
577 | xsltDocumentPtr oldDocInfo;
|
---|
578 | int oldXPPos, oldXPSize;
|
---|
579 | xmlDocPtr oldXPDoc;
|
---|
580 | int oldXPNsNr;
|
---|
581 | xmlNsPtr *oldXPNamespaces;
|
---|
582 | xmlXPathContextPtr xpctxt;
|
---|
583 |
|
---|
584 | if ((keyDef->comp == NULL) || (keyDef->usecomp == NULL))
|
---|
585 | return(-1);
|
---|
586 |
|
---|
587 | xpctxt = ctxt->xpathCtxt;
|
---|
588 | idoc->nbKeysComputed++;
|
---|
589 | /*
|
---|
590 | * Save context state.
|
---|
591 | */
|
---|
592 | oldInst = ctxt->inst;
|
---|
593 | oldDocInfo = ctxt->document;
|
---|
594 | oldContextNode = ctxt->node;
|
---|
595 |
|
---|
596 | oldXPDoc = xpctxt->doc;
|
---|
597 | oldXPPos = xpctxt->proximityPosition;
|
---|
598 | oldXPSize = xpctxt->contextSize;
|
---|
599 | oldXPNsNr = xpctxt->nsNr;
|
---|
600 | oldXPNamespaces = xpctxt->namespaces;
|
---|
601 |
|
---|
602 | /*
|
---|
603 | * Set up contexts.
|
---|
604 | */
|
---|
605 | ctxt->document = idoc;
|
---|
606 | ctxt->node = (xmlNodePtr) idoc->doc;
|
---|
607 | ctxt->inst = keyDef->inst;
|
---|
608 |
|
---|
609 | xpctxt->doc = idoc->doc;
|
---|
610 | xpctxt->node = (xmlNodePtr) idoc->doc;
|
---|
611 | /* TODO : clarify the use of namespaces in keys evaluation */
|
---|
612 | xpctxt->namespaces = keyDef->nsList;
|
---|
613 | xpctxt->nsNr = keyDef->nsNr;
|
---|
614 |
|
---|
615 | /*
|
---|
616 | * Evaluate the 'match' expression of the xsl:key.
|
---|
617 | * TODO: The 'match' is a *pattern*.
|
---|
618 | */
|
---|
619 | matchRes = xmlXPathCompiledEval(keyDef->comp, xpctxt);
|
---|
620 | if (matchRes == NULL) {
|
---|
621 |
|
---|
622 | #ifdef WITH_XSLT_DEBUG_KEYS
|
---|
623 | XSLT_TRACE(ctxt,XSLT_TRACE_KEYS,xsltGenericDebug(xsltGenericDebugContext,
|
---|
624 | "xsltInitCtxtKey: %s evaluation failed\n", keyDef->match));
|
---|
625 | #endif
|
---|
626 | xsltTransformError(ctxt, NULL, keyDef->inst,
|
---|
627 | "Failed to evaluate the 'match' expression.\n");
|
---|
628 | ctxt->state = XSLT_STATE_STOPPED;
|
---|
629 | goto error;
|
---|
630 | } else {
|
---|
631 | if (matchRes->type == XPATH_NODESET) {
|
---|
632 | matchList = matchRes->nodesetval;
|
---|
633 |
|
---|
634 | #ifdef WITH_XSLT_DEBUG_KEYS
|
---|
635 | if (matchList != NULL)
|
---|
636 | XSLT_TRACE(ctxt,XSLT_TRACE_KEYS,xsltGenericDebug(xsltGenericDebugContext,
|
---|
637 | "xsltInitCtxtKey: %s evaluates to %d nodes\n",
|
---|
638 | keyDef->match, matchList->nodeNr));
|
---|
639 | #endif
|
---|
640 | } else {
|
---|
641 | /*
|
---|
642 | * Is not a node set, but must be.
|
---|
643 | */
|
---|
644 | #ifdef WITH_XSLT_DEBUG_KEYS
|
---|
645 | XSLT_TRACE(ctxt,XSLT_TRACE_KEYS,xsltGenericDebug(xsltGenericDebugContext,
|
---|
646 | "xsltInitCtxtKey: %s is not a node set\n", keyDef->match));
|
---|
647 | #endif
|
---|
648 | xsltTransformError(ctxt, NULL, keyDef->inst,
|
---|
649 | "The 'match' expression did not evaluate to a node set.\n");
|
---|
650 | ctxt->state = XSLT_STATE_STOPPED;
|
---|
651 | goto error;
|
---|
652 | }
|
---|
653 | }
|
---|
654 | if ((matchList == NULL) || (matchList->nodeNr <= 0))
|
---|
655 | goto exit;
|
---|
656 |
|
---|
657 | /**
|
---|
658 | * Multiple key definitions for the same name are allowed, so
|
---|
659 | * we must check if the key is already present for this doc
|
---|
660 | */
|
---|
661 | table = (xsltKeyTablePtr) idoc->keys;
|
---|
662 | while (table != NULL) {
|
---|
663 | if (xmlStrEqual(table->name, keyDef->name) &&
|
---|
664 | (((keyDef->nameURI == NULL) && (table->nameURI == NULL)) ||
|
---|
665 | ((keyDef->nameURI != NULL) && (table->nameURI != NULL) &&
|
---|
666 | (xmlStrEqual(table->nameURI, keyDef->nameURI)))))
|
---|
667 | break;
|
---|
668 | table = table->next;
|
---|
669 | }
|
---|
670 | /**
|
---|
671 | * If the key was not previously defined, create it now and
|
---|
672 | * chain it to the list of keys for the doc
|
---|
673 | */
|
---|
674 | if (table == NULL) {
|
---|
675 | table = xsltNewKeyTable(keyDef->name, keyDef->nameURI);
|
---|
676 | if (table == NULL)
|
---|
677 | goto error;
|
---|
678 | table->next = idoc->keys;
|
---|
679 | idoc->keys = table;
|
---|
680 | }
|
---|
681 |
|
---|
682 | /*
|
---|
683 | * SPEC XSLT 1.0 (XSLT 2.0 does not clarify the context size!)
|
---|
684 | * "...the use attribute of the xsl:key element is evaluated with x as
|
---|
685 | " the current node and with a node list containing just x as the
|
---|
686 | * current node list"
|
---|
687 | */
|
---|
688 | xpctxt->contextSize = 1;
|
---|
689 | xpctxt->proximityPosition = 1;
|
---|
690 |
|
---|
691 | for (i = 0; i < matchList->nodeNr; i++) {
|
---|
692 | cur = matchList->nodeTab[i];
|
---|
693 | if (! IS_XSLT_REAL_NODE(cur))
|
---|
694 | continue;
|
---|
695 | xpctxt->node = cur;
|
---|
696 | /*
|
---|
697 | * Process the 'use' of the xsl:key.
|
---|
698 | * SPEC XSLT 1.0:
|
---|
699 | * "The use attribute is an expression specifying the values of
|
---|
700 | * the key; the expression is evaluated once for each node that
|
---|
701 | * matches the pattern."
|
---|
702 | */
|
---|
703 | if (useRes != NULL)
|
---|
704 | xmlXPathFreeObject(useRes);
|
---|
705 | useRes = xmlXPathCompiledEval(keyDef->usecomp, xpctxt);
|
---|
706 | if (useRes == NULL) {
|
---|
707 | xsltTransformError(ctxt, NULL, keyDef->inst,
|
---|
708 | "Failed to evaluate the 'use' expression.\n");
|
---|
709 | ctxt->state = XSLT_STATE_STOPPED;
|
---|
710 | break;
|
---|
711 | }
|
---|
712 | if (useRes->type == XPATH_NODESET) {
|
---|
713 | if ((useRes->nodesetval != NULL) &&
|
---|
714 | (useRes->nodesetval->nodeNr != 0))
|
---|
715 | {
|
---|
716 | len = useRes->nodesetval->nodeNr;
|
---|
717 | str = xmlXPathCastNodeToString(useRes->nodesetval->nodeTab[0]);
|
---|
718 | } else {
|
---|
719 | continue;
|
---|
720 | }
|
---|
721 | } else {
|
---|
722 | len = 1;
|
---|
723 | if (useRes->type == XPATH_STRING) {
|
---|
724 | /*
|
---|
725 | * Consume the string value.
|
---|
726 | */
|
---|
727 | str = useRes->stringval;
|
---|
728 | useRes->stringval = NULL;
|
---|
729 | } else {
|
---|
730 | str = xmlXPathCastToString(useRes);
|
---|
731 | }
|
---|
732 | }
|
---|
733 | /*
|
---|
734 | * Process all strings.
|
---|
735 | */
|
---|
736 | k = 0;
|
---|
737 | while (1) {
|
---|
738 | if (str == NULL)
|
---|
739 | goto next_string;
|
---|
740 |
|
---|
741 | #ifdef WITH_XSLT_DEBUG_KEYS
|
---|
742 | XSLT_TRACE(ctxt,XSLT_TRACE_KEYS,xsltGenericDebug(xsltGenericDebugContext,
|
---|
743 | "xsl:key : node associated to ('%s', '%s')\n", keyDef->name, str));
|
---|
744 | #endif
|
---|
745 |
|
---|
746 | keylist = xmlHashLookup(table->keys, str);
|
---|
747 | if (keylist == NULL) {
|
---|
748 | keylist = xmlXPathNodeSetCreate(cur);
|
---|
749 | if (keylist == NULL)
|
---|
750 | goto error;
|
---|
751 | xmlHashAddEntry(table->keys, str, keylist);
|
---|
752 | } else {
|
---|
753 | /*
|
---|
754 | * TODO: How do we know if this function failed?
|
---|
755 | */
|
---|
756 | xmlXPathNodeSetAdd(keylist, cur);
|
---|
757 | }
|
---|
758 | switch (cur->type) {
|
---|
759 | case XML_ELEMENT_NODE:
|
---|
760 | case XML_TEXT_NODE:
|
---|
761 | case XML_CDATA_SECTION_NODE:
|
---|
762 | case XML_PI_NODE:
|
---|
763 | case XML_COMMENT_NODE:
|
---|
764 | cur->psvi = keyDef;
|
---|
765 | break;
|
---|
766 | case XML_ATTRIBUTE_NODE:
|
---|
767 | ((xmlAttrPtr) cur)->psvi = keyDef;
|
---|
768 | break;
|
---|
769 | case XML_DOCUMENT_NODE:
|
---|
770 | case XML_HTML_DOCUMENT_NODE:
|
---|
771 | ((xmlDocPtr) cur)->psvi = keyDef;
|
---|
772 | break;
|
---|
773 | default:
|
---|
774 | break;
|
---|
775 | }
|
---|
776 | xmlFree(str);
|
---|
777 | str = NULL;
|
---|
778 |
|
---|
779 | next_string:
|
---|
780 | k++;
|
---|
781 | if (k >= len)
|
---|
782 | break;
|
---|
783 | str = xmlXPathCastNodeToString(useRes->nodesetval->nodeTab[k]);
|
---|
784 | }
|
---|
785 | }
|
---|
786 |
|
---|
787 | exit:
|
---|
788 | error:
|
---|
789 | /*
|
---|
790 | * Restore context state.
|
---|
791 | */
|
---|
792 | xpctxt->doc = oldXPDoc;
|
---|
793 | xpctxt->nsNr = oldXPNsNr;
|
---|
794 | xpctxt->namespaces = oldXPNamespaces;
|
---|
795 | xpctxt->proximityPosition = oldXPPos;
|
---|
796 | xpctxt->contextSize = oldXPSize;
|
---|
797 |
|
---|
798 | ctxt->node = oldContextNode;
|
---|
799 | ctxt->document = oldDocInfo;
|
---|
800 | ctxt->inst = oldInst;
|
---|
801 |
|
---|
802 | if (str)
|
---|
803 | xmlFree(str);
|
---|
804 | if (useRes != NULL)
|
---|
805 | xmlXPathFreeObject(useRes);
|
---|
806 | if (matchRes != NULL)
|
---|
807 | xmlXPathFreeObject(matchRes);
|
---|
808 | return(0);
|
---|
809 | }
|
---|
810 |
|
---|
811 | /**
|
---|
812 | * xsltInitCtxtKeys:
|
---|
813 | * @ctxt: an XSLT transformation context
|
---|
814 | * @idoc: a document info
|
---|
815 | *
|
---|
816 | * Computes all the keys tables for the current input document.
|
---|
817 | * Should be done before global varibales are initialized.
|
---|
818 | * NOTE: Not used anymore in the refactored code.
|
---|
819 | */
|
---|
820 | void
|
---|
821 | xsltInitCtxtKeys(xsltTransformContextPtr ctxt, xsltDocumentPtr idoc) {
|
---|
822 | xsltStylesheetPtr style;
|
---|
823 | xsltKeyDefPtr keyDef;
|
---|
824 |
|
---|
825 | if ((ctxt == NULL) || (idoc == NULL))
|
---|
826 | return;
|
---|
827 | #ifdef WITH_XSLT_DEBUG_KEYS
|
---|
828 | if ((idoc->doc != NULL) && (idoc->doc->URL != NULL))
|
---|
829 | XSLT_TRACE(ctxt,XSLT_TRACE_KEYS,xsltGenericDebug(xsltGenericDebugContext, "Initializing keys on %s\n",
|
---|
830 | idoc->doc->URL));
|
---|
831 | #endif
|
---|
832 | style = ctxt->style;
|
---|
833 | while (style != NULL) {
|
---|
834 | keyDef = (xsltKeyDefPtr) style->keys;
|
---|
835 | while (keyDef != NULL) {
|
---|
836 | xsltInitCtxtKey(ctxt, idoc, keyDef);
|
---|
837 |
|
---|
838 | keyDef = keyDef->next;
|
---|
839 | }
|
---|
840 |
|
---|
841 | style = xsltNextImport(style);
|
---|
842 | }
|
---|
843 | }
|
---|
844 |
|
---|
845 | /**
|
---|
846 | * xsltFreeDocumentKeys:
|
---|
847 | * @idoc: a XSLT document
|
---|
848 | *
|
---|
849 | * Free the keys associated to a document
|
---|
850 | */
|
---|
851 | void
|
---|
852 | xsltFreeDocumentKeys(xsltDocumentPtr idoc) {
|
---|
853 | if (idoc != NULL)
|
---|
854 | xsltFreeKeyTableList(idoc->keys);
|
---|
855 | }
|
---|
856 |
|
---|