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