source: trunk/third/libxslt/libxslt/namespaces.c @ 20733

Revision 20733, 15.1 KB checked in by ghudson, 20 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r20732, which included commits to RCS files with non-trunk default branches.
Line 
1/*
2 * namespaces.c: Implementation of the XSLT namespaces 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#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 "xslt.h"
42#include "xsltInternals.h"
43#include "xsltutils.h"
44#include "namespaces.h"
45#include "imports.h"
46
47
48
49/************************************************************************
50 *                                                                      *
51 *                      Module interfaces                               *
52 *                                                                      *
53 ************************************************************************/
54
55/**
56 * xsltNamespaceAlias:
57 * @style:  the XSLT stylesheet
58 * @node:  the xsl:namespace-alias node
59 *
60 * Read the stylesheet-prefix and result-prefix attributes, register
61 * them as well as the corresponding namespace.
62 */
63void
64xsltNamespaceAlias(xsltStylesheetPtr style, xmlNodePtr node) {
65    xmlChar *sprefix;
66    xmlNsPtr sNs;
67    const xmlChar *shref;
68    xmlChar *rprefix;
69    xmlNsPtr rNs;
70    const xmlChar *rhref;
71
72    sprefix = xsltGetNsProp(node, (const xmlChar *)"stylesheet-prefix",
73                           XSLT_NAMESPACE);
74    if (sprefix == NULL) {
75        xsltTransformError(NULL, style, node,
76            "namespace-alias: stylesheet-prefix attribute missing\n");
77        return;
78    }
79    rprefix = xsltGetNsProp(node, (const xmlChar *)"result-prefix",
80                           XSLT_NAMESPACE);
81    if (rprefix == NULL) {
82        xsltTransformError(NULL, style, node,
83            "namespace-alias: result-prefix attribute missing\n");
84        goto error;
85    }
86   
87    if (xmlStrEqual(sprefix, (const xmlChar *)"#default")) {
88        /*
89         * Do we have a default namespace previously declared?
90         */
91        sNs = xmlSearchNs(node->doc, node, NULL);
92        if (sNs == NULL)
93            shref = NULL;       /* No - set NULL */
94        else
95            shref = sNs->href;  /* Yes - set for nsAlias table */
96    } else {
97        sNs = xmlSearchNs(node->doc, node, sprefix);
98 
99        if ((sNs == NULL) || (sNs->href == NULL)) {
100            xsltTransformError(NULL, style, node,
101                "namespace-alias: prefix %s not bound to any namespace\n",
102                                        sprefix);
103            goto error;
104        } else
105            shref = sNs->href;
106    }
107
108    /*
109     * When "#default" is used for result, if a default namespace has not
110     * been explicitly declared the special value UNDEFINED_DEFAULT_NS is
111     * put into the nsAliases table
112     */
113    if (xmlStrEqual(rprefix, (const xmlChar *)"#default")) {
114        rNs = xmlSearchNs(node->doc, node, NULL);
115        if (rNs == NULL)
116            rhref = UNDEFINED_DEFAULT_NS;
117        else
118            rhref = rNs->href;
119    } else {
120        rNs = xmlSearchNs(node->doc, node, rprefix);
121
122        if ((rNs == NULL) || (rNs->href == NULL)) {
123            xsltTransformError(NULL, style, node,
124                "namespace-alias: prefix %s not bound to any namespace\n",
125                                        rprefix);
126            goto error;
127        } else
128            rhref = rNs->href;
129    }
130    /*
131     * Special case if #default is used for stylesheet and no default has
132     * been explicitly declared.  We use style->defaultAlias for this
133     */
134    if (shref == NULL) {
135        if (rNs != NULL)
136            style->defaultAlias = rNs->href;
137    } else {
138        if (style->nsAliases == NULL)
139            style->nsAliases = xmlHashCreate(10);
140        if (style->nsAliases == NULL) {
141            xsltTransformError(NULL, style, node,
142                "namespace-alias: cannot create hash table\n");
143            goto error;
144        }
145        xmlHashAddEntry((xmlHashTablePtr) style->nsAliases,
146                    shref, (void *) rhref);
147    }
148
149error:
150    if (sprefix != NULL)
151        xmlFree(sprefix);
152    if (rprefix != NULL)
153        xmlFree(rprefix);
154}
155
156/**
157 * xsltNsInScope:
158 * @doc:  the document
159 * @node:  the current node
160 * @ancestor:  the ancestor carrying the namespace
161 * @prefix:  the namespace prefix
162 *
163 * Copy of xmlNsInScope which is not public ...
164 *
165 * Returns 1 if true, 0 if false and -1 in case of error.
166 */
167static int
168xsltNsInScope(xmlDocPtr doc ATTRIBUTE_UNUSED, xmlNodePtr node,
169             xmlNodePtr ancestor, const xmlChar * prefix)
170{
171    xmlNsPtr tst;
172
173    while ((node != NULL) && (node != ancestor)) {
174        if ((node->type == XML_ENTITY_REF_NODE) ||
175            (node->type == XML_ENTITY_NODE) ||
176            (node->type == XML_ENTITY_DECL))
177            return (-1);
178        if (node->type == XML_ELEMENT_NODE) {
179            tst = node->nsDef;
180            while (tst != NULL) {
181                if ((tst->prefix == NULL)
182                    && (prefix == NULL))
183                    return (0);
184                if ((tst->prefix != NULL)
185                    && (prefix != NULL)
186                    && (xmlStrEqual(tst->prefix, prefix)))
187                    return (0);
188                tst = tst->next;
189            }
190        }
191        node = node->parent;
192    }
193    if (node != ancestor)
194        return (-1);
195    return (1);
196}
197
198/**
199 * xsltSearchPlainNsByHref:
200 * @doc:  the document
201 * @node:  the current node
202 * @href:  the namespace value
203 *
204 * Search a Ns aliasing a given URI and without a NULL prefix.
205 * Recurse on the parents until it finds
206 * the defined namespace or return NULL otherwise.
207 * Returns the namespace pointer or NULL.
208 */
209static xmlNsPtr
210xsltSearchPlainNsByHref(xmlDocPtr doc, xmlNodePtr node, const xmlChar * href)
211{
212    xmlNsPtr cur;
213    xmlNodePtr orig = node;
214
215    if ((node == NULL) || (href == NULL))
216        return (NULL);
217
218    while (node != NULL) {
219        if ((node->type == XML_ENTITY_REF_NODE) ||
220            (node->type == XML_ENTITY_NODE) ||
221            (node->type == XML_ENTITY_DECL))
222            return (NULL);
223        if (node->type == XML_ELEMENT_NODE) {
224            cur = node->nsDef;
225            while (cur != NULL) {
226                if ((cur->href != NULL) && (cur->prefix != NULL) &&
227                    (href != NULL) && (xmlStrEqual(cur->href, href))) {
228                    if (xsltNsInScope(doc, orig, node, cur->href) == 1)
229                        return (cur);
230                }
231                cur = cur->next;
232            }
233            if (orig != node) {
234                cur = node->ns;
235                if (cur != NULL) {
236                    if ((cur->href != NULL) && (cur->prefix != NULL) &&
237                        (href != NULL) && (xmlStrEqual(cur->href, href))) {
238                        if (xsltNsInScope(doc, orig, node, cur->href) == 1)
239                            return (cur);
240                    }
241                }
242            }   
243        }
244        node = node->parent;
245    }
246    return (NULL);
247}
248
249/**
250 * xsltGetPlainNamespace:
251 * @ctxt:  a transformation context
252 * @cur:  the input node
253 * @ns:  the namespace
254 * @out:  the output node (or its parent)
255 *
256 * Find the right namespace value for this prefix, if needed create
257 * and add a new namespace decalaration on the node
258 * Handle namespace aliases and make sure the prefix is not NULL, this
259 * is needed for attributes.
260 *
261 * Returns the namespace node to use or NULL
262 */
263xmlNsPtr
264xsltGetPlainNamespace(xsltTransformContextPtr ctxt, xmlNodePtr cur,
265                      xmlNsPtr ns, xmlNodePtr out) {
266    xsltStylesheetPtr style;
267    xmlNsPtr ret;
268    const xmlChar *URI = NULL; /* the replacement URI */
269
270    if ((ctxt == NULL) || (cur == NULL) || (out == NULL) || (ns == NULL))
271        return(NULL);
272
273    style = ctxt->style;
274    while (style != NULL) {
275        if (style->nsAliases != NULL)
276            URI = (const xmlChar *) xmlHashLookup(style->nsAliases, ns->href);
277        if (URI != NULL)
278            break;
279
280        style = xsltNextImport(style);
281    }
282
283    if (URI == UNDEFINED_DEFAULT_NS) {
284        xmlNsPtr dflt;
285        dflt = xmlSearchNs(cur->doc, cur, NULL);
286        if (dflt == NULL)
287            return NULL;
288        else
289            URI = dflt->href;
290    }
291
292    if (URI == NULL)
293        URI = ns->href;
294
295    if ((out->parent != NULL) &&
296        (out->parent->type == XML_ELEMENT_NODE) &&
297        (out->parent->ns != NULL) &&
298        (out->parent->ns->prefix != NULL) &&
299        (xmlStrEqual(out->parent->ns->href, URI)))
300        ret = out->parent->ns;
301    else {
302        if (ns->prefix != NULL) {
303            ret = xmlSearchNs(out->doc, out, ns->prefix);
304            if ((ret == NULL) || (!xmlStrEqual(ret->href, URI)) ||
305                (ret->prefix == NULL)) {
306                ret = xsltSearchPlainNsByHref(out->doc, out, URI);
307            }
308        } else {
309            ret = xsltSearchPlainNsByHref(out->doc, out, URI);
310        }
311    }
312
313    if (ret == NULL) {
314        if (out->type == XML_ELEMENT_NODE)
315            ret = xmlNewNs(out, URI, ns->prefix);
316    }
317    return(ret);
318}
319
320/**
321 * xsltGetSpecialNamespace:
322 * @ctxt:  a transformation context
323 * @cur:  the input node
324 * @URI:  the namespace URI
325 * @prefix:  the suggested prefix
326 * @out:  the output node (or its parent)
327 *
328 * Find the right namespace value for this URI, if needed create
329 * and add a new namespace decalaration on the node
330 *
331 * Returns the namespace node to use or NULL
332 */
333xmlNsPtr
334xsltGetSpecialNamespace(xsltTransformContextPtr ctxt, xmlNodePtr cur,
335                const xmlChar *URI, const xmlChar *prefix, xmlNodePtr out) {
336    xmlNsPtr ret;
337    static int prefixno = 1;
338    char nprefix[10];
339
340    if ((ctxt == NULL) || (cur == NULL) || (out == NULL) || (URI == NULL))
341        return(NULL);
342
343    if ((prefix == NULL) && (URI[0] == 0)) {
344        ret = xmlSearchNs(out->doc, out, NULL);
345        if (ret != NULL) {
346            ret = xmlNewNs(out, URI, prefix);
347            return(ret);
348        }
349        return(NULL);
350    }
351
352    if ((out->parent != NULL) &&
353        (out->parent->type == XML_ELEMENT_NODE) &&
354        (out->parent->ns != NULL) &&
355        (xmlStrEqual(out->parent->ns->href, URI)))
356        ret = out->parent->ns;
357    else
358        ret = xmlSearchNsByHref(out->doc, out, URI);
359
360    if ((ret == NULL) || (ret->prefix == NULL)) {
361        if (prefix == NULL) {
362            do {
363                sprintf(nprefix, "ns%d", prefixno++);
364                ret = xmlSearchNs(out->doc, out, (xmlChar *)nprefix);
365            } while (ret != NULL);
366            prefix = (const xmlChar *) &nprefix[0];
367        } else if ((ret != NULL) && (ret->prefix == NULL)) {
368            /* found ns but no prefix - search for the prefix */
369            ret = xmlSearchNs(out->doc, out, prefix);
370            if (ret != NULL)
371                return(ret);    /* found it */
372        }
373        if (out->type == XML_ELEMENT_NODE)
374            ret = xmlNewNs(out, URI, prefix);
375    }
376    return(ret);
377}
378
379/**
380 * xsltGetNamespace:
381 * @ctxt:  a transformation context
382 * @cur:  the input node
383 * @ns:  the namespace
384 * @out:  the output node (or its parent)
385 *
386 * Find the right namespace value for this prefix, if needed create
387 * and add a new namespace decalaration on the node
388 * Handle namespace aliases
389 *
390 * Returns the namespace node to use or NULL
391 */
392xmlNsPtr
393xsltGetNamespace(xsltTransformContextPtr ctxt, xmlNodePtr cur, xmlNsPtr ns,
394                 xmlNodePtr out) {
395    xsltStylesheetPtr style;
396    xmlNsPtr ret;
397    const xmlChar *URI = NULL; /* the replacement URI */
398
399    if ((ctxt == NULL) || (cur == NULL) || (out == NULL) || (ns == NULL))
400        return(NULL);
401
402    style = ctxt->style;
403    while (style != NULL) {
404        if (style->nsAliases != NULL)
405            URI = (const xmlChar *)
406                xmlHashLookup(style->nsAliases, ns->href);
407        if (URI != NULL)
408            break;
409
410        style = xsltNextImport(style);
411    }
412
413    if (URI == UNDEFINED_DEFAULT_NS) {
414        xmlNsPtr dflt;
415        dflt = xmlSearchNs(cur->doc, cur, NULL);
416        if (dflt != NULL)
417            URI = dflt->href;
418        else
419            return NULL;
420    } else if (URI == NULL)
421        URI = ns->href;
422
423    /*
424     * If the parent is an XML_ELEMENT_NODE, and has the "equivalent"
425     * namespace as ns (either both default, or both with a prefix
426     * with the same href) then return the parent's ns record
427     */
428    if ((out->parent != NULL) &&
429        (out->parent->type == XML_ELEMENT_NODE) &&
430        (out->parent->ns != NULL) &&
431        (((out->parent->ns->prefix == NULL) && (ns->prefix == NULL)) ||
432         ((out->parent->ns->prefix != NULL) && (ns->prefix != NULL))) &&
433        (xmlStrEqual(out->parent->ns->href, URI)))
434        ret = out->parent->ns;
435    else {
436        /*
437         * do a standard namespace search for ns in the output doc
438         */
439        ret = xmlSearchNs(out->doc, out, ns->prefix);
440        /*
441         * if the search fails and it's not for the default prefix
442         * do a search by href
443         */
444        if ((ret == NULL) && (ns->prefix != NULL))
445            ret = xmlSearchNsByHref(out->doc, out, URI);
446        }
447
448    if (ret == NULL) {  /* if no success and an element node, create the ns */
449        if (out->type == XML_ELEMENT_NODE)
450            ret = xmlNewNs(out, URI, ns->prefix);
451    }
452    return(ret);
453}
454
455/**
456 * xsltCopyNamespaceList:
457 * @ctxt:  a transformation context
458 * @node:  the target node
459 * @cur:  the first namespace
460 *
461 * Do a copy of an namespace list. If @node is non-NULL the
462 * new namespaces are added automatically. This handles namespaces
463 * aliases
464 *
465 * Returns: a new xmlNsPtr, or NULL in case of error.
466 */
467xmlNsPtr
468xsltCopyNamespaceList(xsltTransformContextPtr ctxt, xmlNodePtr node,
469                      xmlNsPtr cur) {
470    xmlNsPtr ret = NULL, tmp;
471    xmlNsPtr p = NULL,q;
472    const xmlChar *URI;
473
474    if (cur == NULL)
475        return(NULL);
476    if (cur->type != XML_NAMESPACE_DECL)
477        return(NULL);
478
479    /*
480     * One can add namespaces only on element nodes
481     */
482    if ((node != NULL) && (node->type != XML_ELEMENT_NODE))
483        node = NULL;
484
485    while (cur != NULL) {
486        if (cur->type != XML_NAMESPACE_DECL)
487            break;
488
489        /*
490         * Avoid duplicating namespace declrations on the tree
491         */
492        if ((node != NULL) && (node->ns != NULL) &&
493            (xmlStrEqual(node->ns->href, cur->href)) &&
494            (xmlStrEqual(node->ns->prefix, cur->prefix))) {
495            cur = cur->next;
496            continue;
497        }
498        tmp = xmlSearchNs(node->doc, node, cur->prefix);
499        if ((tmp != NULL) && (xmlStrEqual(tmp->href, cur->href))) {
500            cur = cur->next;
501            continue;
502        }
503       
504        if (!xmlStrEqual(cur->href, XSLT_NAMESPACE)) {
505            /* TODO apply cascading */
506            URI = (const xmlChar *) xmlHashLookup(ctxt->style->nsAliases,
507                                                  cur->href);
508            if (URI == UNDEFINED_DEFAULT_NS)
509                continue;
510            if (URI != NULL) {
511                q = xmlNewNs(node, URI, cur->prefix);
512            } else {
513                q = xmlNewNs(node, cur->href, cur->prefix);
514            }
515            if (p == NULL) {
516                ret = p = q;
517            } else {
518                p->next = q;
519                p = q;
520            }
521        }
522        cur = cur->next;
523    }
524    return(ret);
525}
526
527/**
528 * xsltCopyNamespace:
529 * @ctxt:  a transformation context
530 * @node:  the target node
531 * @cur:  the namespace node
532 *
533 * Do a copy of an namespace node. If @node is non-NULL the
534 * new namespaces are added automatically. This handles namespaces
535 * aliases
536 *
537 * Returns: a new xmlNsPtr, or NULL in case of error.
538 */
539xmlNsPtr
540xsltCopyNamespace(xsltTransformContextPtr ctxt, xmlNodePtr node,
541                  xmlNsPtr cur) {
542    xmlNsPtr ret = NULL;
543    const xmlChar *URI;
544
545    if (cur == NULL)
546        return(NULL);
547    if (cur->type != XML_NAMESPACE_DECL)
548        return(NULL);
549
550    /*
551     * One can add namespaces only on element nodes
552     */
553    if ((node != NULL) && (node->type != XML_ELEMENT_NODE))
554        node = NULL;
555
556    if (!xmlStrEqual(cur->href, XSLT_NAMESPACE)) {
557        URI = (const xmlChar *) xmlHashLookup(ctxt->style->nsAliases,
558                                              cur->href);
559        if (URI == UNDEFINED_DEFAULT_NS)
560            return(NULL);
561        if (URI != NULL) {
562            ret = xmlNewNs(node, URI, cur->prefix);
563        } else {
564            ret = xmlNewNs(node, cur->href, cur->prefix);
565        }
566    }
567    return(ret);
568}
569
570
571/**
572 * xsltFreeNamespaceAliasHashes:
573 * @style: an XSLT stylesheet
574 *
575 * Free up the memory used by namespaces aliases
576 */
577void
578xsltFreeNamespaceAliasHashes(xsltStylesheetPtr style) {
579    if (style->nsAliases != NULL)
580        xmlHashFree((xmlHashTablePtr) style->nsAliases, NULL);
581    style->nsAliases = NULL;
582}
Note: See TracBrowser for help on using the repository browser.