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