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