Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
wine-mirror
GitHub Repository: wine-mirror/wine
Path: blob/master/libs/xslt/libxslt/attributes.c
4389 views
1
/*
2
* attributes.c: Implementation of the XSLT attributes handling
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/hash.h>
20
#include <libxml/xmlerror.h>
21
#include <libxml/uri.h>
22
#include <libxml/parserInternals.h>
23
#include "xslt.h"
24
#include "xsltInternals.h"
25
#include "xsltutils.h"
26
#include "attributes.h"
27
#include "namespaces.h"
28
#include "templates.h"
29
#include "imports.h"
30
#include "transform.h"
31
#include "preproc.h"
32
33
#define WITH_XSLT_DEBUG_ATTRIBUTES
34
#ifdef WITH_XSLT_DEBUG
35
#define WITH_XSLT_DEBUG_ATTRIBUTES
36
#endif
37
38
/*
39
* Useful macros
40
*/
41
#ifdef IS_BLANK
42
#undef IS_BLANK
43
#endif
44
45
#define IS_BLANK(c) (((c) == 0x20) || ((c) == 0x09) || ((c) == 0xA) || \
46
((c) == 0x0D))
47
48
#define IS_BLANK_NODE(n) \
49
(((n)->type == XML_TEXT_NODE) && (xsltIsBlank((n)->content)))
50
51
#define ATTRSET_UNRESOLVED 0
52
#define ATTRSET_RESOLVING 1
53
#define ATTRSET_RESOLVED 2
54
55
56
/*
57
* The in-memory structure corresponding to an XSLT Attribute in
58
* an attribute set
59
*/
60
61
62
typedef struct _xsltAttrElem xsltAttrElem;
63
typedef xsltAttrElem *xsltAttrElemPtr;
64
struct _xsltAttrElem {
65
struct _xsltAttrElem *next;/* chained list */
66
xmlNodePtr attr; /* the xsl:attribute definition */
67
};
68
69
typedef struct _xsltUseAttrSet xsltUseAttrSet;
70
typedef xsltUseAttrSet *xsltUseAttrSetPtr;
71
struct _xsltUseAttrSet {
72
struct _xsltUseAttrSet *next; /* chained list */
73
const xmlChar *ncname;
74
const xmlChar *ns;
75
};
76
77
typedef struct _xsltAttrSet xsltAttrSet;
78
typedef xsltAttrSet *xsltAttrSetPtr;
79
struct _xsltAttrSet {
80
int state;
81
xsltAttrElemPtr attrs; /* list head */
82
xsltUseAttrSetPtr useAttrSets; /* list head */
83
};
84
85
typedef struct _xsltAttrSetContext xsltAttrSetContext;
86
typedef xsltAttrSetContext *xsltAttrSetContextPtr;
87
struct _xsltAttrSetContext {
88
xsltStylesheetPtr topStyle;
89
xsltStylesheetPtr style;
90
int error;
91
};
92
93
static void
94
xsltResolveAttrSet(xsltAttrSetPtr set, xsltStylesheetPtr topStyle,
95
xsltStylesheetPtr style, const xmlChar *name,
96
const xmlChar *ns, int depth);
97
98
/************************************************************************
99
* *
100
* XSLT Attribute handling *
101
* *
102
************************************************************************/
103
104
/**
105
* xsltNewAttrElem:
106
* @attr: the new xsl:attribute node
107
*
108
* Create a new XSLT AttrElem
109
*
110
* Returns the newly allocated xsltAttrElemPtr or NULL in case of error
111
*/
112
static xsltAttrElemPtr
113
xsltNewAttrElem(xmlNodePtr attr) {
114
xsltAttrElemPtr cur;
115
116
cur = (xsltAttrElemPtr) xmlMalloc(sizeof(xsltAttrElem));
117
if (cur == NULL) {
118
xsltGenericError(xsltGenericErrorContext,
119
"xsltNewAttrElem : malloc failed\n");
120
return(NULL);
121
}
122
memset(cur, 0, sizeof(xsltAttrElem));
123
cur->attr = attr;
124
return(cur);
125
}
126
127
/**
128
* xsltFreeAttrElem:
129
* @attr: an XSLT AttrElem
130
*
131
* Free up the memory allocated by @attr
132
*/
133
static void
134
xsltFreeAttrElem(xsltAttrElemPtr attr) {
135
xmlFree(attr);
136
}
137
138
/**
139
* xsltFreeAttrElemList:
140
* @list: an XSLT AttrElem list
141
*
142
* Free up the memory allocated by @list
143
*/
144
static void
145
xsltFreeAttrElemList(xsltAttrElemPtr list) {
146
xsltAttrElemPtr next;
147
148
while (list != NULL) {
149
next = list->next;
150
xsltFreeAttrElem(list);
151
list = next;
152
}
153
}
154
155
/**
156
* xsltAddAttrElemList:
157
* @list: an XSLT AttrElem list
158
* @attr: the new xsl:attribute node
159
*
160
* Add the new attribute to the list.
161
*
162
* Returns the new list pointer
163
*/
164
static xsltAttrElemPtr
165
xsltAddAttrElemList(xsltAttrElemPtr list, xmlNodePtr attr) {
166
xsltAttrElemPtr next, cur;
167
168
if (attr == NULL)
169
return(list);
170
if (list == NULL)
171
return(xsltNewAttrElem(attr));
172
cur = list;
173
while (cur != NULL) {
174
next = cur->next;
175
if (next == NULL) {
176
cur->next = xsltNewAttrElem(attr);
177
return(list);
178
}
179
cur = next;
180
}
181
return(list);
182
}
183
184
/**
185
* xsltNewUseAttrSet:
186
* @ncname: local name
187
* @ns: namespace URI
188
*
189
* Create a new XSLT UseAttrSet
190
*
191
* Returns the newly allocated xsltUseAttrSetPtr or NULL in case of error.
192
*/
193
static xsltUseAttrSetPtr
194
xsltNewUseAttrSet(const xmlChar *ncname, const xmlChar *ns) {
195
xsltUseAttrSetPtr cur;
196
197
cur = (xsltUseAttrSetPtr) xmlMalloc(sizeof(xsltUseAttrSet));
198
if (cur == NULL) {
199
xsltGenericError(xsltGenericErrorContext,
200
"xsltNewUseAttrSet : malloc failed\n");
201
return(NULL);
202
}
203
memset(cur, 0, sizeof(xsltUseAttrSet));
204
cur->ncname = ncname;
205
cur->ns = ns;
206
return(cur);
207
}
208
209
/**
210
* xsltFreeUseAttrSet:
211
* @use: an XSLT UseAttrSet
212
*
213
* Free up the memory allocated by @use
214
*/
215
static void
216
xsltFreeUseAttrSet(xsltUseAttrSetPtr use) {
217
xmlFree(use);
218
}
219
220
/**
221
* xsltFreeUseAttrSetList:
222
* @list: an XSLT UseAttrSet list
223
*
224
* Free up the memory allocated by @list
225
*/
226
static void
227
xsltFreeUseAttrSetList(xsltUseAttrSetPtr list) {
228
xsltUseAttrSetPtr next;
229
230
while (list != NULL) {
231
next = list->next;
232
xsltFreeUseAttrSet(list);
233
list = next;
234
}
235
}
236
237
/**
238
* xsltAddUseAttrSetList:
239
* @list: a xsltUseAttrSet list
240
* @ncname: local name
241
* @ns: namespace URI
242
*
243
* Add the use-attribute-set name to the list.
244
*
245
* Returns the new list pointer.
246
*/
247
static xsltUseAttrSetPtr
248
xsltAddUseAttrSetList(xsltUseAttrSetPtr list, const xmlChar *ncname,
249
const xmlChar *ns) {
250
xsltUseAttrSetPtr next, cur;
251
252
if (ncname == NULL)
253
return(list);
254
if (list == NULL)
255
return(xsltNewUseAttrSet(ncname, ns));
256
cur = list;
257
while (cur != NULL) {
258
if ((cur->ncname == ncname) && (cur->ns == ns))
259
return(list);
260
next = cur->next;
261
if (next == NULL) {
262
cur->next = xsltNewUseAttrSet(ncname, ns);
263
return(list);
264
}
265
cur = next;
266
}
267
return(list);
268
}
269
270
/**
271
* xsltNewAttrSet:
272
*
273
* Create a new attribute set.
274
*
275
* Returns the newly allocated xsltAttrSetPtr or NULL in case of error.
276
*/
277
static xsltAttrSetPtr
278
xsltNewAttrSet(void) {
279
xsltAttrSetPtr cur;
280
281
cur = (xsltAttrSetPtr) xmlMalloc(sizeof(xsltAttrSet));
282
if (cur == NULL) {
283
xsltGenericError(xsltGenericErrorContext,
284
"xsltNewAttrSet : malloc failed\n");
285
return(NULL);
286
}
287
memset(cur, 0, sizeof(xsltAttrSet));
288
return(cur);
289
}
290
291
/**
292
* xsltFreeAttrSet:
293
* @set: an attribute set
294
*
295
* Free memory allocated by @set
296
*/
297
static void
298
xsltFreeAttrSet(xsltAttrSetPtr set) {
299
if (set == NULL)
300
return;
301
302
xsltFreeAttrElemList(set->attrs);
303
xsltFreeUseAttrSetList(set->useAttrSets);
304
xmlFree(set);
305
}
306
307
/**
308
* xsltMergeAttrSets:
309
* @set: an attribute set
310
* @other: another attribute set
311
*
312
* Add all the attributes from @other to @set,
313
* but drop redefinition of existing values.
314
*/
315
static void
316
xsltMergeAttrSets(xsltAttrSetPtr set, xsltAttrSetPtr other) {
317
xsltAttrElemPtr cur;
318
xsltAttrElemPtr old = other->attrs;
319
int add;
320
321
while (old != NULL) {
322
/*
323
* Check that the attribute is not yet in the list
324
*/
325
cur = set->attrs;
326
add = 1;
327
while (cur != NULL) {
328
xsltStylePreCompPtr curComp = cur->attr->psvi;
329
xsltStylePreCompPtr oldComp = old->attr->psvi;
330
331
if ((curComp->name == oldComp->name) &&
332
(curComp->ns == oldComp->ns)) {
333
add = 0;
334
break;
335
}
336
if (cur->next == NULL)
337
break;
338
cur = cur->next;
339
}
340
341
if (add == 1) {
342
if (cur == NULL) {
343
set->attrs = xsltNewAttrElem(old->attr);
344
} else if (add) {
345
cur->next = xsltNewAttrElem(old->attr);
346
}
347
}
348
349
old = old->next;
350
}
351
}
352
353
/************************************************************************
354
* *
355
* Module interfaces *
356
* *
357
************************************************************************/
358
359
/**
360
* xsltParseStylesheetAttributeSet:
361
* @style: the XSLT stylesheet
362
* @cur: the "attribute-set" element
363
*
364
* parse an XSLT stylesheet attribute-set element
365
*/
366
367
void
368
xsltParseStylesheetAttributeSet(xsltStylesheetPtr style, xmlNodePtr cur) {
369
const xmlChar *ncname;
370
const xmlChar *prefix;
371
const xmlChar *nsUri = NULL;
372
xmlChar *value;
373
xmlNodePtr child;
374
xsltAttrSetPtr set;
375
376
if ((cur == NULL) || (style == NULL) || (cur->type != XML_ELEMENT_NODE))
377
return;
378
379
value = xmlGetNsProp(cur, (const xmlChar *)"name", NULL);
380
if ((value == NULL) || (*value == 0)) {
381
xsltGenericError(xsltGenericErrorContext,
382
"xsl:attribute-set : name is missing\n");
383
if (value)
384
xmlFree(value);
385
return;
386
}
387
388
if (xmlValidateQName(value, 0)) {
389
xsltTransformError(NULL, style, cur,
390
"xsl:attribute-set : The name '%s' is not a valid QName.\n",
391
value);
392
style->errors++;
393
xmlFree(value);
394
return;
395
}
396
397
ncname = xsltSplitQName(style->dict, value, &prefix);
398
xmlFree(value);
399
value = NULL;
400
if (prefix != NULL) {
401
xmlNsPtr ns = xmlSearchNs(style->doc, cur, prefix);
402
if (ns == NULL) {
403
xsltTransformError(NULL, style, cur,
404
"xsl:attribute-set : No namespace found for QName '%s:%s'\n",
405
prefix, ncname);
406
style->errors++;
407
return;
408
}
409
nsUri = ns->href;
410
}
411
412
if (style->attributeSets == NULL) {
413
#ifdef WITH_XSLT_DEBUG_ATTRIBUTES
414
xsltGenericDebug(xsltGenericDebugContext,
415
"creating attribute set table\n");
416
#endif
417
style->attributeSets = xmlHashCreate(10);
418
}
419
if (style->attributeSets == NULL)
420
return;
421
422
set = xmlHashLookup2(style->attributeSets, ncname, nsUri);
423
if (set == NULL) {
424
set = xsltNewAttrSet();
425
if ((set == NULL) ||
426
(xmlHashAddEntry2(style->attributeSets, ncname, nsUri, set) < 0)) {
427
xsltGenericError(xsltGenericErrorContext, "memory error\n");
428
xsltFreeAttrSet(set);
429
return;
430
}
431
}
432
433
/*
434
* Parse the content. Only xsl:attribute elements are allowed.
435
*/
436
child = cur->children;
437
while (child != NULL) {
438
/*
439
* Report invalid nodes.
440
*/
441
if ((child->type != XML_ELEMENT_NODE) ||
442
(child->ns == NULL) ||
443
(! IS_XSLT_ELEM(child)))
444
{
445
if (child->type == XML_ELEMENT_NODE)
446
xsltTransformError(NULL, style, child,
447
"xsl:attribute-set : unexpected child %s\n",
448
child->name);
449
else
450
xsltTransformError(NULL, style, child,
451
"xsl:attribute-set : child of unexpected type\n");
452
} else if (!IS_XSLT_NAME(child, "attribute")) {
453
xsltTransformError(NULL, style, child,
454
"xsl:attribute-set : unexpected child xsl:%s\n",
455
child->name);
456
} else {
457
#ifdef WITH_XSLT_DEBUG_ATTRIBUTES
458
xsltGenericDebug(xsltGenericDebugContext,
459
"add attribute to list %s\n", ncname);
460
#endif
461
xsltStylePreCompute(style, child);
462
if (child->children != NULL) {
463
#ifdef XSLT_REFACTORED
464
xsltParseSequenceConstructor(XSLT_CCTXT(style),
465
child->children);
466
#else
467
xsltParseTemplateContent(style, child);
468
#endif
469
}
470
if (child->psvi == NULL) {
471
xsltTransformError(NULL, style, child,
472
"xsl:attribute-set : internal error, attribute %s not "
473
"compiled\n", child->name);
474
}
475
else {
476
set->attrs = xsltAddAttrElemList(set->attrs, child);
477
}
478
}
479
480
child = child->next;
481
}
482
483
/*
484
* Process attribute "use-attribute-sets".
485
*/
486
value = xmlGetNsProp(cur, BAD_CAST "use-attribute-sets", NULL);
487
if (value != NULL) {
488
const xmlChar *curval, *endval;
489
curval = value;
490
while (*curval != 0) {
491
while (IS_BLANK(*curval)) curval++;
492
if (*curval == 0)
493
break;
494
endval = curval;
495
while ((*endval != 0) && (!IS_BLANK(*endval))) endval++;
496
curval = xmlDictLookup(style->dict, curval, endval - curval);
497
if (curval) {
498
const xmlChar *ncname2 = NULL;
499
const xmlChar *prefix2 = NULL;
500
const xmlChar *nsUri2 = NULL;
501
502
#ifdef WITH_XSLT_DEBUG_ATTRIBUTES
503
xsltGenericDebug(xsltGenericDebugContext,
504
"xsl:attribute-set : %s adds use %s\n", ncname, curval);
505
#endif
506
507
if (xmlValidateQName(curval, 0)) {
508
xsltTransformError(NULL, style, cur,
509
"xsl:attribute-set : The name '%s' in "
510
"use-attribute-sets is not a valid QName.\n", curval);
511
style->errors++;
512
xmlFree(value);
513
return;
514
}
515
516
ncname2 = xsltSplitQName(style->dict, curval, &prefix2);
517
if (prefix2 != NULL) {
518
xmlNsPtr ns2 = xmlSearchNs(style->doc, cur, prefix2);
519
if (ns2 == NULL) {
520
xsltTransformError(NULL, style, cur,
521
"xsl:attribute-set : No namespace found for QName "
522
"'%s:%s' in use-attribute-sets\n",
523
prefix2, ncname2);
524
style->errors++;
525
xmlFree(value);
526
return;
527
}
528
nsUri2 = ns2->href;
529
}
530
set->useAttrSets = xsltAddUseAttrSetList(set->useAttrSets,
531
ncname2, nsUri2);
532
}
533
curval = endval;
534
}
535
xmlFree(value);
536
value = NULL;
537
}
538
539
#ifdef WITH_XSLT_DEBUG_ATTRIBUTES
540
xsltGenericDebug(xsltGenericDebugContext,
541
"updated attribute list %s\n", ncname);
542
#endif
543
}
544
545
/**
546
* xsltResolveUseAttrSets:
547
* @set: the attribute set
548
* @asctx: the context for attribute set resolution
549
* @depth: recursion depth
550
*
551
* Process "use-attribute-sets".
552
*/
553
static void
554
xsltResolveUseAttrSets(xsltAttrSetPtr set, xsltStylesheetPtr topStyle,
555
int depth) {
556
xsltStylesheetPtr cur;
557
xsltAttrSetPtr other;
558
xsltUseAttrSetPtr use = set->useAttrSets;
559
xsltUseAttrSetPtr next;
560
561
while (use != NULL) {
562
/*
563
* Iterate top stylesheet and all imports.
564
*/
565
cur = topStyle;
566
while (cur != NULL) {
567
if (cur->attributeSets) {
568
other = xmlHashLookup2(cur->attributeSets, use->ncname,
569
use->ns);
570
if (other != NULL) {
571
xsltResolveAttrSet(other, topStyle, cur, use->ncname,
572
use->ns, depth + 1);
573
xsltMergeAttrSets(set, other);
574
break;
575
}
576
}
577
cur = xsltNextImport(cur);
578
}
579
580
next = use->next;
581
/* Free useAttrSets early. */
582
xsltFreeUseAttrSet(use);
583
use = next;
584
}
585
586
set->useAttrSets = NULL;
587
}
588
589
/**
590
* xsltResolveAttrSet:
591
* @set: the attribute set
592
* @asctx: the context for attribute set resolution
593
* @name: the local name of the attirbute set
594
* @ns: the namespace of the attribute set
595
* @depth: recursion depth
596
*
597
* resolve the references in an attribute set.
598
*/
599
static void
600
xsltResolveAttrSet(xsltAttrSetPtr set, xsltStylesheetPtr topStyle,
601
xsltStylesheetPtr style, const xmlChar *name,
602
const xmlChar *ns, int depth) {
603
xsltStylesheetPtr cur;
604
xsltAttrSetPtr other;
605
606
if (set->state == ATTRSET_RESOLVED)
607
return;
608
if (set->state == ATTRSET_RESOLVING) {
609
xsltTransformError(NULL, topStyle, NULL,
610
"xsl:attribute-set : use-attribute-sets recursion detected"
611
" on %s\n", name);
612
topStyle->errors++;
613
set->state = ATTRSET_RESOLVED;
614
return;
615
}
616
if (depth > 100) {
617
xsltTransformError(NULL, topStyle, NULL,
618
"xsl:attribute-set : use-attribute-sets maximum recursion "
619
"depth exceeded on %s\n", name);
620
topStyle->errors++;
621
return;
622
}
623
624
set->state = ATTRSET_RESOLVING;
625
626
xsltResolveUseAttrSets(set, topStyle, depth);
627
628
/* Merge imported sets. */
629
cur = xsltNextImport(style);
630
while (cur != NULL) {
631
if (cur->attributeSets != NULL) {
632
other = xmlHashLookup2(cur->attributeSets, name, ns);
633
634
if (other != NULL) {
635
#ifdef WITH_XSLT_DEBUG_ATTRIBUTES
636
xsltGenericDebug(xsltGenericDebugContext,
637
"xsl:attribute-set : merging import for %s\n", name);
638
#endif
639
xsltResolveUseAttrSets(other, topStyle, depth);
640
xsltMergeAttrSets(set, other);
641
xmlHashRemoveEntry2(cur->attributeSets, name, ns, NULL);
642
xsltFreeAttrSet(other);
643
}
644
}
645
646
cur = xsltNextImport(cur);
647
}
648
649
set->state = ATTRSET_RESOLVED;
650
}
651
652
/**
653
* xsltResolveSASCallback:
654
* @set: the attribute set
655
* @asctx: the context for attribute set resolution
656
* @name: the local name of the attirbute set
657
* @ns: the namespace of the attribute set
658
*
659
* resolve the references in an attribute set.
660
*/
661
static void
662
xsltResolveSASCallback(void *payload, void *data,
663
const xmlChar *name, const xmlChar *ns,
664
ATTRIBUTE_UNUSED const xmlChar *ignored) {
665
xsltAttrSetPtr set = (xsltAttrSetPtr) payload;
666
xsltAttrSetContextPtr asctx = (xsltAttrSetContextPtr) data;
667
xsltStylesheetPtr topStyle = asctx->topStyle;
668
xsltStylesheetPtr style = asctx->style;
669
670
if (asctx->error) {
671
if (style != topStyle)
672
xsltFreeAttrSet(set);
673
return;
674
}
675
676
xsltResolveAttrSet(set, topStyle, style, name, ns, 1);
677
678
/* Move attribute sets to top stylesheet. */
679
if (style != topStyle) {
680
/*
681
* This imported stylesheet won't be visited anymore. Don't bother
682
* removing the hash entry.
683
*/
684
if (xmlHashAddEntry2(topStyle->attributeSets, name, ns, set) < 0) {
685
xsltGenericError(xsltGenericErrorContext,
686
"xsl:attribute-set : internal error, can't move imported "
687
" attribute set %s\n", name);
688
asctx->error = 1;
689
xsltFreeAttrSet(set);
690
}
691
}
692
}
693
694
/**
695
* xsltResolveStylesheetAttributeSet:
696
* @style: the XSLT stylesheet
697
*
698
* resolve the references between attribute sets.
699
*/
700
void
701
xsltResolveStylesheetAttributeSet(xsltStylesheetPtr style) {
702
xsltStylesheetPtr cur;
703
xsltAttrSetContext asctx;
704
705
#ifdef WITH_XSLT_DEBUG_ATTRIBUTES
706
xsltGenericDebug(xsltGenericDebugContext,
707
"Resolving attribute sets references\n");
708
#endif
709
asctx.topStyle = style;
710
asctx.error = 0;
711
cur = style;
712
while (cur != NULL) {
713
if (cur->attributeSets != NULL) {
714
if (style->attributeSets == NULL) {
715
#ifdef WITH_XSLT_DEBUG_ATTRIBUTES
716
xsltGenericDebug(xsltGenericDebugContext,
717
"creating attribute set table\n");
718
#endif
719
style->attributeSets = xmlHashCreate(10);
720
}
721
asctx.style = cur;
722
xmlHashScanFull(cur->attributeSets, xsltResolveSASCallback,
723
&asctx);
724
725
if (cur != style) {
726
/*
727
* the attribute lists have either been migrated to style
728
* or freed directly in xsltResolveSASCallback()
729
*/
730
xmlHashFree(cur->attributeSets, NULL);
731
cur->attributeSets = NULL;
732
}
733
}
734
cur = xsltNextImport(cur);
735
}
736
}
737
738
/**
739
* xsltAttribute:
740
* @ctxt: a XSLT process context
741
* @contextNode: the current node in the source tree
742
* @inst: the xsl:attribute element
743
* @castedComp: precomputed information
744
*
745
* Process the xslt attribute node on the source node
746
*/
747
void
748
xsltAttribute(xsltTransformContextPtr ctxt,
749
xmlNodePtr contextNode,
750
xmlNodePtr inst,
751
xsltElemPreCompPtr castedComp)
752
{
753
#ifdef XSLT_REFACTORED
754
xsltStyleItemAttributePtr comp =
755
(xsltStyleItemAttributePtr) castedComp;
756
#else
757
xsltStylePreCompPtr comp = (xsltStylePreCompPtr) castedComp;
758
#endif
759
xmlNodePtr targetElem;
760
xmlChar *prop = NULL;
761
const xmlChar *name = NULL, *prefix = NULL, *nsName = NULL;
762
xmlChar *value = NULL;
763
xmlNsPtr ns = NULL;
764
xmlAttrPtr attr;
765
766
if ((ctxt == NULL) || (contextNode == NULL) || (inst == NULL) ||
767
(inst->type != XML_ELEMENT_NODE) )
768
return;
769
770
/*
771
* A comp->has_name == 0 indicates that we need to skip this instruction,
772
* since it was evaluated to be invalid already during compilation.
773
*/
774
if (!comp->has_name)
775
return;
776
/*
777
* BIG NOTE: This previously used xsltGetSpecialNamespace() and
778
* xsltGetNamespace(), but since both are not appropriate, we
779
* will process namespace lookup here to avoid adding yet another
780
* ns-lookup function to namespaces.c.
781
*/
782
/*
783
* SPEC XSLT 1.0: Error cases:
784
* - Creating nodes other than text nodes during the instantiation of
785
* the content of the xsl:attribute element; implementations may
786
* either signal the error or ignore the offending nodes."
787
*/
788
789
if (comp == NULL) {
790
xsltTransformError(ctxt, NULL, inst,
791
"Internal error in xsltAttribute(): "
792
"The XSLT 'attribute' instruction was not compiled.\n");
793
return;
794
}
795
/*
796
* TODO: Shouldn't ctxt->insert == NULL be treated as an internal error?
797
* So report an internal error?
798
*/
799
if (ctxt->insert == NULL)
800
return;
801
/*
802
* SPEC XSLT 1.0:
803
* "Adding an attribute to a node that is not an element;
804
* implementations may either signal the error or ignore the attribute."
805
*
806
* TODO: I think we should signal such errors in the future, and maybe
807
* provide an option to ignore such errors.
808
*/
809
targetElem = ctxt->insert;
810
if (targetElem->type != XML_ELEMENT_NODE)
811
return;
812
813
/*
814
* SPEC XSLT 1.0:
815
* "Adding an attribute to an element after children have been added
816
* to it; implementations may either signal the error or ignore the
817
* attribute."
818
*
819
* TODO: We should decide whether not to report such errors or
820
* to ignore them; note that we *ignore* if the parent is not an
821
* element, but here we report an error.
822
*/
823
if (targetElem->children != NULL) {
824
/*
825
* NOTE: Ah! This seems to be intended to support streamed
826
* result generation!.
827
*/
828
xsltTransformError(ctxt, NULL, inst,
829
"xsl:attribute: Cannot add attributes to an "
830
"element if children have been already added "
831
"to the element.\n");
832
return;
833
}
834
835
/*
836
* Process the name
837
* ----------------
838
*/
839
840
#ifdef WITH_DEBUGGER
841
if (ctxt->debugStatus != XSLT_DEBUG_NONE)
842
xslHandleDebugger(inst, contextNode, NULL, ctxt);
843
#endif
844
845
if (comp->name == NULL) {
846
/* TODO: fix attr acquisition wrt to the XSLT namespace */
847
prop = xsltEvalAttrValueTemplate(ctxt, inst,
848
(const xmlChar *) "name", XSLT_NAMESPACE);
849
if (prop == NULL) {
850
xsltTransformError(ctxt, NULL, inst,
851
"xsl:attribute: The attribute 'name' is missing.\n");
852
goto error;
853
}
854
if (xmlValidateQName(prop, 0)) {
855
xsltTransformError(ctxt, NULL, inst,
856
"xsl:attribute: The effective name '%s' is not a "
857
"valid QName.\n", prop);
858
/* we fall through to catch any further errors, if possible */
859
}
860
861
/*
862
* Reject a name of "xmlns".
863
*/
864
if (xmlStrEqual(prop, BAD_CAST "xmlns")) {
865
xsltTransformError(ctxt, NULL, inst,
866
"xsl:attribute: The effective name 'xmlns' is not allowed.\n");
867
xmlFree(prop);
868
goto error;
869
}
870
871
name = xsltSplitQName(ctxt->dict, prop, &prefix);
872
xmlFree(prop);
873
} else {
874
/*
875
* The "name" value was static.
876
*/
877
#ifdef XSLT_REFACTORED
878
prefix = comp->nsPrefix;
879
name = comp->name;
880
#else
881
name = xsltSplitQName(ctxt->dict, comp->name, &prefix);
882
#endif
883
}
884
885
/*
886
* Process namespace semantics
887
* ---------------------------
888
*
889
* Evaluate the namespace name.
890
*/
891
if (comp->has_ns) {
892
/*
893
* The "namespace" attribute was existent.
894
*/
895
if (comp->ns != NULL) {
896
/*
897
* No AVT; just plain text for the namespace name.
898
*/
899
if (comp->ns[0] != 0)
900
nsName = comp->ns;
901
} else {
902
xmlChar *tmpNsName;
903
/*
904
* Eval the AVT.
905
*/
906
/* TODO: check attr acquisition wrt to the XSLT namespace */
907
tmpNsName = xsltEvalAttrValueTemplate(ctxt, inst,
908
(const xmlChar *) "namespace", XSLT_NAMESPACE);
909
/*
910
* This fixes bug #302020: The AVT might also evaluate to the
911
* empty string; this means that the empty string also indicates
912
* "no namespace".
913
* SPEC XSLT 1.0:
914
* "If the string is empty, then the expanded-name of the
915
* attribute has a null namespace URI."
916
*/
917
if ((tmpNsName != NULL) && (tmpNsName[0] != 0))
918
nsName = xmlDictLookup(ctxt->dict, BAD_CAST tmpNsName, -1);
919
xmlFree(tmpNsName);
920
}
921
922
if (xmlStrEqual(nsName, BAD_CAST "http://www.w3.org/2000/xmlns/")) {
923
xsltTransformError(ctxt, NULL, inst,
924
"xsl:attribute: Namespace http://www.w3.org/2000/xmlns/ "
925
"forbidden.\n");
926
goto error;
927
}
928
if (xmlStrEqual(nsName, XML_XML_NAMESPACE)) {
929
prefix = BAD_CAST "xml";
930
} else if (xmlStrEqual(prefix, BAD_CAST "xml")) {
931
prefix = NULL;
932
}
933
} else if (prefix != NULL) {
934
/*
935
* SPEC XSLT 1.0:
936
* "If the namespace attribute is not present, then the QName is
937
* expanded into an expanded-name using the namespace declarations
938
* in effect for the xsl:attribute element, *not* including any
939
* default namespace declaration."
940
*/
941
ns = xmlSearchNs(inst->doc, inst, prefix);
942
if (ns == NULL) {
943
/*
944
* Note that this is treated as an error now (checked with
945
* Saxon, Xalan-J and MSXML).
946
*/
947
xsltTransformError(ctxt, NULL, inst,
948
"xsl:attribute: The QName '%s:%s' has no "
949
"namespace binding in scope in the stylesheet; "
950
"this is an error, since the namespace was not "
951
"specified by the instruction itself.\n", prefix, name);
952
} else
953
nsName = ns->href;
954
}
955
956
/*
957
* Find/create a matching ns-decl in the result tree.
958
*/
959
ns = NULL;
960
961
#if 0
962
if (0) {
963
/*
964
* OPTIMIZE TODO: How do we know if we are adding to a
965
* fragment or to the result tree?
966
*
967
* If we are adding to a result tree fragment (i.e., not to the
968
* actual result tree), we'll don't bother searching for the
969
* ns-decl, but just store it in the dummy-doc of the result
970
* tree fragment.
971
*/
972
if (nsName != NULL) {
973
/*
974
* TODO: Get the doc of @targetElem.
975
*/
976
ns = xsltTreeAcquireStoredNs(some doc, nsName, prefix);
977
}
978
}
979
#endif
980
981
if (nsName != NULL) {
982
/*
983
* Something about ns-prefixes:
984
* SPEC XSLT 1.0:
985
* "XSLT processors may make use of the prefix of the QName specified
986
* in the name attribute when selecting the prefix used for outputting
987
* the created attribute as XML; however, they are not required to do
988
* so and, if the prefix is xmlns, they must not do so"
989
*/
990
/*
991
* xsl:attribute can produce a scenario where the prefix is NULL,
992
* so generate a prefix.
993
*/
994
if ((prefix == NULL) || xmlStrEqual(prefix, BAD_CAST "xmlns")) {
995
xmlChar *pref = xmlStrdup(BAD_CAST "ns_1");
996
997
ns = xsltGetSpecialNamespace(ctxt, inst, nsName, pref, targetElem);
998
999
xmlFree(pref);
1000
} else {
1001
ns = xsltGetSpecialNamespace(ctxt, inst, nsName, prefix,
1002
targetElem);
1003
}
1004
if (ns == NULL) {
1005
xsltTransformError(ctxt, NULL, inst,
1006
"Namespace fixup error: Failed to acquire an in-scope "
1007
"namespace binding for the generated attribute '{%s}%s'.\n",
1008
nsName, name);
1009
goto error;
1010
}
1011
}
1012
/*
1013
* Construction of the value
1014
* -------------------------
1015
*/
1016
if (inst->children == NULL) {
1017
/*
1018
* No content.
1019
* TODO: Do we need to put the empty string in ?
1020
*/
1021
attr = xmlSetNsProp(ctxt->insert, ns, name, (const xmlChar *) "");
1022
} else if ((inst->children->next == NULL) &&
1023
((inst->children->type == XML_TEXT_NODE) ||
1024
(inst->children->type == XML_CDATA_SECTION_NODE)))
1025
{
1026
xmlNodePtr copyTxt;
1027
1028
/*
1029
* xmlSetNsProp() will take care of duplicates.
1030
*/
1031
attr = xmlSetNsProp(ctxt->insert, ns, name, NULL);
1032
if (attr == NULL) /* TODO: report error ? */
1033
goto error;
1034
/*
1035
* This was taken over from xsltCopyText() (transform.c).
1036
*/
1037
if (ctxt->internalized &&
1038
(ctxt->insert->doc != NULL) &&
1039
(ctxt->insert->doc->dict == ctxt->dict))
1040
{
1041
copyTxt = xmlNewText(NULL);
1042
if (copyTxt == NULL) /* TODO: report error */
1043
goto error;
1044
/*
1045
* This is a safe scenario where we don't need to lookup
1046
* the dict.
1047
*/
1048
copyTxt->content = inst->children->content;
1049
/*
1050
* Copy "disable-output-escaping" information.
1051
* TODO: Does this have any effect for attribute values
1052
* anyway?
1053
*/
1054
if (inst->children->name == xmlStringTextNoenc)
1055
copyTxt->name = xmlStringTextNoenc;
1056
} else {
1057
/*
1058
* Copy the value.
1059
*/
1060
copyTxt = xmlNewText(inst->children->content);
1061
if (copyTxt == NULL) /* TODO: report error */
1062
goto error;
1063
}
1064
attr->children = attr->last = copyTxt;
1065
copyTxt->parent = (xmlNodePtr) attr;
1066
copyTxt->doc = attr->doc;
1067
/*
1068
* Copy "disable-output-escaping" information.
1069
* TODO: Does this have any effect for attribute values
1070
* anyway?
1071
*/
1072
if (inst->children->name == xmlStringTextNoenc)
1073
copyTxt->name = xmlStringTextNoenc;
1074
1075
/*
1076
* since we create the attribute without content IDness must be
1077
* asserted as a second step
1078
*/
1079
if ((copyTxt->content != NULL) &&
1080
(xmlIsID(attr->doc, attr->parent, attr)))
1081
xmlAddID(NULL, attr->doc, copyTxt->content, attr);
1082
} else {
1083
/*
1084
* The sequence constructor might be complex, so instantiate it.
1085
*/
1086
value = xsltEvalTemplateString(ctxt, contextNode, inst);
1087
if (value != NULL) {
1088
attr = xmlSetNsProp(ctxt->insert, ns, name, value);
1089
xmlFree(value);
1090
} else {
1091
/*
1092
* TODO: Do we have to add the empty string to the attr?
1093
* TODO: Does a value of NULL indicate an
1094
* error in xsltEvalTemplateString() ?
1095
*/
1096
attr = xmlSetNsProp(ctxt->insert, ns, name,
1097
(const xmlChar *) "");
1098
}
1099
}
1100
1101
error:
1102
return;
1103
}
1104
1105
/**
1106
* xsltApplyAttributeSet:
1107
* @ctxt: the XSLT stylesheet
1108
* @node: the node in the source tree.
1109
* @inst: the attribute node "xsl:use-attribute-sets"
1110
* @attrSets: the list of QNames of the attribute-sets to be applied
1111
*
1112
* Apply the xsl:use-attribute-sets.
1113
* If @attrSets is NULL, then @inst will be used to exctract this
1114
* value.
1115
* If both, @attrSets and @inst, are NULL, then this will do nothing.
1116
*/
1117
void
1118
xsltApplyAttributeSet(xsltTransformContextPtr ctxt, xmlNodePtr node,
1119
xmlNodePtr inst,
1120
const xmlChar *attrSets)
1121
{
1122
const xmlChar *ncname = NULL;
1123
const xmlChar *prefix = NULL;
1124
const xmlChar *curstr, *endstr;
1125
xsltAttrSetPtr set;
1126
xsltStylesheetPtr style;
1127
1128
if (attrSets == NULL) {
1129
if (inst == NULL)
1130
return;
1131
else {
1132
/*
1133
* Extract the value from @inst.
1134
*/
1135
if (inst->type == XML_ATTRIBUTE_NODE) {
1136
if ( ((xmlAttrPtr) inst)->children != NULL)
1137
attrSets = ((xmlAttrPtr) inst)->children->content;
1138
1139
}
1140
if (attrSets == NULL) {
1141
/*
1142
* TODO: Return an error?
1143
*/
1144
return;
1145
}
1146
}
1147
}
1148
/*
1149
* Parse/apply the list of QNames.
1150
*/
1151
curstr = attrSets;
1152
while (*curstr != 0) {
1153
while (IS_BLANK(*curstr))
1154
curstr++;
1155
if (*curstr == 0)
1156
break;
1157
endstr = curstr;
1158
while ((*endstr != 0) && (!IS_BLANK(*endstr)))
1159
endstr++;
1160
curstr = xmlDictLookup(ctxt->dict, curstr, endstr - curstr);
1161
if (curstr) {
1162
xmlNsPtr ns;
1163
const xmlChar *nsUri = NULL;
1164
1165
#ifdef WITH_XSLT_DEBUG_ATTRIBUTES
1166
xsltGenericDebug(xsltGenericDebugContext,
1167
"apply attribute set %s\n", curstr);
1168
#endif
1169
1170
if (xmlValidateQName(curstr, 0)) {
1171
xsltTransformError(ctxt, NULL, inst,
1172
"The name '%s' in use-attribute-sets is not a valid "
1173
"QName.\n", curstr);
1174
return;
1175
}
1176
1177
ncname = xsltSplitQName(ctxt->dict, curstr, &prefix);
1178
if (prefix != NULL) {
1179
ns = xmlSearchNs(inst->doc, inst, prefix);
1180
if (ns == NULL) {
1181
xsltTransformError(ctxt, NULL, inst,
1182
"use-attribute-set : No namespace found for QName "
1183
"'%s:%s'\n", prefix, ncname);
1184
return;
1185
}
1186
nsUri = ns->href;
1187
}
1188
1189
style = ctxt->style;
1190
1191
#ifdef WITH_DEBUGGER
1192
if ((style != NULL) &&
1193
(style->attributeSets != NULL) &&
1194
(ctxt->debugStatus != XSLT_DEBUG_NONE))
1195
{
1196
set = xmlHashLookup2(style->attributeSets, ncname, nsUri);
1197
if ((set != NULL) && (set->attrs != NULL) &&
1198
(set->attrs->attr != NULL))
1199
xslHandleDebugger(set->attrs->attr->parent, node, NULL,
1200
ctxt);
1201
}
1202
#endif
1203
/*
1204
* Lookup the referenced attribute-set. All attribute sets were
1205
* moved to the top stylesheet so there's no need to iterate
1206
* imported stylesheets
1207
*/
1208
set = xmlHashLookup2(style->attributeSets, ncname, nsUri);
1209
if (set != NULL) {
1210
xsltAttrElemPtr cur = set->attrs;
1211
while (cur != NULL) {
1212
if (cur->attr != NULL) {
1213
xsltAttribute(ctxt, node, cur->attr,
1214
cur->attr->psvi);
1215
}
1216
cur = cur->next;
1217
}
1218
}
1219
}
1220
curstr = endstr;
1221
}
1222
}
1223
1224
static void
1225
xsltFreeAttributeSetsEntry(void *payload,
1226
const xmlChar *name ATTRIBUTE_UNUSED) {
1227
xsltFreeAttrSet((xsltAttrSetPtr) payload);
1228
}
1229
1230
/**
1231
* xsltFreeAttributeSetsHashes:
1232
* @style: an XSLT stylesheet
1233
*
1234
* Free up the memory used by attribute sets
1235
*/
1236
void
1237
xsltFreeAttributeSetsHashes(xsltStylesheetPtr style) {
1238
if (style->attributeSets != NULL)
1239
xmlHashFree((xmlHashTablePtr) style->attributeSets,
1240
xsltFreeAttributeSetsEntry);
1241
style->attributeSets = NULL;
1242
}
1243
1244