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