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