One more day of hacking:
[platform/upstream/libxslt.git] / 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@imag.fr
10  */
11
12 #include "xsltconfig.h"
13
14 #include <string.h>
15
16 #ifdef HAVE_SYS_TYPES_H
17 #include <sys/types.h>
18 #endif
19 #ifdef HAVE_MATH_H
20 #include <math.h>
21 #endif
22 #ifdef HAVE_FLOAT_H
23 #include <float.h>
24 #endif
25 #ifdef HAVE_IEEEFP_H
26 #include <ieeefp.h>
27 #endif
28 #ifdef HAVE_NAN_H
29 #include <nan.h>
30 #endif
31 #ifdef HAVE_CTYPE_H
32 #include <ctype.h>
33 #endif
34
35 #include <libxml/xmlmemory.h>
36 #include <libxml/tree.h>
37 #include <libxml/hash.h>
38 #include <libxml/xmlerror.h>
39 #include <libxml/uri.h>
40 #include "xslt.h"
41 #include "xsltInternals.h"
42 #include "xsltutils.h"
43 #include "attributes.h"
44 #include "namespaces.h"
45 #include "templates.h"
46
47 /*
48  * Useful macros
49  */
50
51 #define IS_BLANK(c) (((c) == 0x20) || ((c) == 0x09) || ((c) == 0xA) ||  \
52                      ((c) == 0x0D))
53
54 #define IS_BLANK_NODE(n)                                                \
55     (((n)->type == XML_TEXT_NODE) && (xsltIsBlank((n)->content)))
56
57
58 /*
59  * The in-memory structure corresponding to an XSLT Attribute in
60  * an attribute set
61  */
62
63 typedef struct _xsltAttrElem xsltAttrElem;
64 typedef xsltAttrElem *xsltAttrElemPtr;
65 struct _xsltAttrElem {
66     struct _xsltAttrElem *next;/* chained list */
67     xmlNodePtr attr;    /* the xsl:attribute definition */
68 };
69
70 /************************************************************************
71  *                                                                      *
72  *                      XSLT Attribute handling                         *
73  *                                                                      *
74  ************************************************************************/
75
76 /**
77  * xsltNewAttrElem:
78  * @attr:  the new xsl:attribute node
79  *
80  * Create a new XSLT AttrElem
81  *
82  * Returns the newly allocated xsltAttrElemPtr or NULL in case of error
83  */
84 xsltAttrElemPtr
85 xsltNewAttrElem(xmlNodePtr attr) {
86     xsltAttrElemPtr cur;
87
88     cur = (xsltAttrElemPtr) xmlMalloc(sizeof(xsltAttrElem));
89     if (cur == NULL) {
90         xsltGenericError(xsltGenericErrorContext,
91                 "xsltNewAttrElem : malloc failed\n");
92         return(NULL);
93     }
94     memset(cur, 0, sizeof(xsltAttrElem));
95     cur->attr = attr;
96     return(cur);
97 }
98
99 /**
100  * xsltFreeAttrElem:
101  * @attr:  an XSLT AttrElem
102  *
103  * Free up the memory allocated by @attr
104  */
105 void
106 xsltFreeAttrElem(xsltAttrElemPtr attr) {
107     memset(attr, -1, sizeof(xsltAttrElem));
108     xmlFree(attr);
109 }
110
111 /**
112  * xsltFreeAttrElemList:
113  * @list:  an XSLT AttrElem list
114  *
115  * Free up the memory allocated by @list
116  */
117 void
118 xsltFreeAttrElemList(xsltAttrElemPtr list) {
119     xsltAttrElemPtr next;
120     
121     while (list != NULL) {
122         next = list->next;
123         xsltFreeAttrElem(list);
124         list = next;
125     }
126 }
127
128 /**
129  * xsltAddAttrElemList:
130  * @list:  an XSLT AttrElem list
131  * @attr:  the new xsl:attribute node
132  *
133  * Add the new attribute to the list.
134  *
135  * Returns the new list pointer
136  */
137 xsltAttrElemPtr
138 xsltAddAttrElemList(xsltAttrElemPtr list, xmlNodePtr attr) {
139     xsltAttrElemPtr next;
140
141     if (attr == NULL)
142         return(list);
143     while (list != NULL) {
144         next = list->next;
145         if (list->attr == attr)
146             return(list);
147         if (next == NULL) {
148             list->next = xsltNewAttrElem(attr);
149             return(list);
150         }
151         list = next;
152     }
153     return(xsltNewAttrElem(attr));
154 }
155 /************************************************************************
156  *                                                                      *
157  *                      Module interfaces                               *
158  *                                                                      *
159  ************************************************************************/
160
161 /**
162  * xsltParseStylesheetAttributeSet:
163  * @style:  the XSLT stylesheet
164  * @template:  the "preserve-space" element
165  *
166  * parse an XSLT stylesheet preserve-space element and record
167  * elements needing preserving
168  */
169
170 void
171 xsltParseStylesheetAttributeSet(xsltStylesheetPtr style, xmlNodePtr cur) {
172     xmlChar *prop = NULL;
173     xmlChar *ncname = NULL;
174     xmlChar *prefix = NULL;
175     xmlChar *attributes;
176     xmlChar *attribute, *end;
177     xmlNodePtr list, delete;
178     xsltAttrElemPtr values;
179
180     if ((cur == NULL) || (style == NULL))
181         return;
182
183     prop = xmlGetNsProp(cur, (const xmlChar *)"name", XSLT_NAMESPACE);
184     if (prop == NULL) {
185         xsltGenericError(xsltGenericErrorContext,
186              "xslt:attribute-set : name is missing\n");
187         goto error;
188     }
189
190     ncname = xmlSplitQName2(prop, &prefix);
191     if (ncname == NULL) {
192         ncname = prop;
193         prop = NULL;
194         prefix = NULL;
195     }
196
197     if (style->attributeSets == NULL)
198         style->attributeSets = xmlHashCreate(10);
199     if (style->attributeSets == NULL)
200         goto error;
201
202     values = xmlHashLookup2(style->attributeSets, ncname, prefix);
203
204     /*
205      * check the children list
206      */
207     list = cur->children;
208     delete = NULL;
209     while (list != NULL) {
210         if (IS_XSLT_ELEM(cur)) {
211             if (!IS_XSLT_NAME(cur, "attribute")) {
212                 xsltGenericError(xsltGenericErrorContext,
213                     "xslt:attribute-set : unexpected child xsl:%s\n",
214                                  cur->name);
215                 delete = cur;
216             } else {
217 #ifdef DEBUG_PARSING
218                 xsltGenericDebug(xsltGenericDebugContext,
219                     "add attribute to list %s\n", ncname);
220 #endif
221                 values = xsltAddAttrElemList(values, cur);
222             }
223         } else {
224             xsltGenericError(xsltGenericErrorContext,
225                 "xslt:attribute-set : unexpected child %s\n", cur->name);
226             delete = cur;
227         }
228         list = list->next;
229     }
230
231     /*
232      * Check a possible use-attribute-sets definition
233      */
234     /* TODO check recursion */
235
236     attributes = xmlGetNsProp(cur, (const xmlChar *)"use-attribute-sets",
237                               XSLT_NAMESPACE);
238     if (attributes == NULL) {
239         goto done;
240     }
241
242     attribute = attributes;
243     while (*attribute != 0) {
244         while (IS_BLANK(*attribute)) attribute++;
245         if (*attribute == 0)
246             break;
247         end = attribute;
248         while ((*end != 0) && (!IS_BLANK(*end))) end++;
249         attribute = xmlStrndup(attribute, end - attribute);
250         if (attribute) {
251 #ifdef DEBUG_PARSING
252             xsltGenericDebug(xsltGenericDebugContext,
253                 "xslt:attribute-set : %s adds use %s\n", ncname);
254 #endif
255             TODO /* add use-attribute-sets support to atribute-set */
256         }
257         attribute = end;
258     }
259     xmlFree(attributes);
260
261 done:
262     /*
263      * Update the value
264      */
265     xmlHashUpdateEntry2(style->attributeSets, ncname, prefix, values, NULL);
266 #ifdef DEBUG_PARSING
267     xsltGenericDebug(xsltGenericDebugContext,
268         "updated attribute list %s\n", ncname);
269 #endif
270
271 error:
272     if (prop != NULL)
273         xmlFree(prop);
274     if (ncname != NULL)
275         xmlFree(ncname);
276     if (prefix != NULL)
277         xmlFree(prefix);
278 }
279
280 /**
281  * xsltAttribute:
282  * @ctxt:  a XSLT process context
283  * @node:  the node in the source tree.
284  * @inst:  the xslt attribute node
285  *
286  * Process the xslt attribute node on the source node
287  */
288 void
289 xsltAttribute(xsltTransformContextPtr ctxt, xmlNodePtr node,
290                    xmlNodePtr inst) {
291     xmlChar *prop = NULL;
292     xmlChar *ncname = NULL;
293     xmlChar *prefix = NULL;
294     xmlChar *value = NULL;
295     xmlNsPtr ns = NULL;
296     xmlAttrPtr attr;
297
298
299     if (ctxt->insert == NULL)
300         return;
301     if (ctxt->insert->children != NULL) {
302         xsltGenericError(xsltGenericErrorContext,
303              "xslt:attribute : node has already children\n");
304         return;
305     }
306     prop = xsltEvalAttrValueTemplate(ctxt, inst, (const xmlChar *)"name");
307     if (prop == NULL) {
308         xsltGenericError(xsltGenericErrorContext,
309              "xslt:attribute : name is missing\n");
310         goto error;
311     }
312
313     ncname = xmlSplitQName2(prop, &prefix);
314     if (ncname == NULL) {
315         ncname = prop;
316         prop = NULL;
317         prefix = NULL;
318     }
319     if (xmlStrEqual(ncname, (const xmlChar *) "xmlns")) {
320         xsltGenericError(xsltGenericErrorContext,
321              "xslt:attribute : xmlns forbidden\n");
322         goto error;
323     }
324     prop = xsltEvalAttrValueTemplate(ctxt, inst, (const xmlChar *)"namespace");
325     if (prop != NULL) {
326         TODO /* xsl:attribute namespace */
327         xmlFree(prop);
328         return;
329     } else {
330         if (prefix != NULL) {
331             ns = xmlSearchNs(inst->doc, inst, prefix);
332             if (ns == NULL) {
333                 xsltGenericError(xsltGenericErrorContext,
334                     "no namespace bound to prefix %s\n", prefix);
335             } else {
336                 ns = xsltGetNamespace(ctxt, inst, ns, ctxt->insert);
337             }
338         }
339     }
340     
341
342     value = xsltEvalTemplateString(ctxt, node, inst);
343     if (value == NULL) {
344         if (ns) {
345             attr = xmlSetNsProp(ctxt->insert, ns, ncname, 
346                                 (const xmlChar *)"");
347         } else
348             attr = xmlSetProp(ctxt->insert, ncname, (const xmlChar *)"");
349     } else {
350         if (ns) {
351             attr = xmlSetNsProp(ctxt->insert, ns, ncname, value);
352         } else
353             attr = xmlSetProp(ctxt->insert, ncname, value);
354         
355     }
356
357 error:
358     if (prop != NULL)
359         xmlFree(prop);
360     if (ncname != NULL)
361         xmlFree(ncname);
362     if (prefix != NULL)
363         xmlFree(prefix);
364     if (value != NULL)
365         xmlFree(value);
366 }
367
368 /**
369  * xsltApplyAttributeSet:
370  * @ctxt:  the XSLT stylesheet
371  * @node:  the node in the source tree.
372  * @inst:  the xslt attribute node
373  * @attributes:  the set list.
374  *
375  * Apply the xsl:use-attribute-sets
376  */
377
378 void
379 xsltApplyAttributeSet(xsltTransformContextPtr ctxt, xmlNodePtr node,
380                       xmlNodePtr inst, xmlChar *attributes) {
381     xmlChar *ncname = NULL;
382     xmlChar *prefix = NULL;
383     xmlChar *attribute, *end;
384     xsltAttrElemPtr values;
385
386     if (attributes == NULL) {
387         return;
388     }
389
390     attribute = attributes;
391     while (*attribute != 0) {
392         while (IS_BLANK(*attribute)) attribute++;
393         if (*attribute == 0)
394             break;
395         end = attribute;
396         while ((*end != 0) && (!IS_BLANK(*end))) end++;
397         attribute = xmlStrndup(attribute, end - attribute);
398         if (attribute) {
399 #ifdef DEBUG_PARSING
400             xsltGenericDebug(xsltGenericDebugContext,
401                 "apply attribute set %s\n", attribute);
402 #endif
403             ncname = xmlSplitQName2(attribute, &prefix);
404             if (ncname == NULL) {
405                 ncname = attribute;
406                 attribute = NULL;
407                 prefix = NULL;
408             }
409
410             /* TODO: apply cascade */
411             values = xmlHashLookup2(ctxt->style->attributeSets, ncname, prefix);
412             while (values != NULL) {
413                 xsltAttribute(ctxt, node, values->attr);
414                 values = values->next;
415             }
416             if (attribute != NULL)
417                 xmlFree(attribute);
418             if (ncname != NULL)
419                 xmlFree(ncname);
420             if (prefix != NULL)
421                 xmlFree(prefix);
422         }
423         attribute = end;
424     }
425 }
426
427 /**
428  * xsltFreeAttributeSetsHashes:
429  * @style: an XSLT stylesheet
430  *
431  * Free up the memory used by attribute sets
432  */
433 void
434 xsltFreeAttributeSetsHashes(xsltStylesheetPtr style) {
435     if (style->attributeSets != NULL)
436         xmlHashFree((xmlHashTablePtr) style->attributeSets,
437                     (xmlHashDeallocator) xsltFreeAttrElemList);
438     style->attributeSets = NULL;
439 }