]> mj.ucw.cz Git - libucw.git/blob - ucw-xml/ns.c
XML: Implementation of XML namespaces
[libucw.git] / ucw-xml / ns.c
1 /*
2  *      UCW Library -- A simple XML parser -- Namespaces
3  *
4  *      (c) 2015 Martin Mares <mj@ucw.cz>
5  *
6  *      This software may be freely distributed and used according to the terms
7  *      of the GNU Lesser General Public License.
8  */
9
10 #undef LOCAL_DEBUG
11
12 #include <ucw/lib.h>
13 #include <ucw/gary.h>
14 #include <ucw-xml/xml.h>
15 #include <ucw-xml/internals.h>
16
17 /*
18  *  This is an implementation of XML namespaces according to
19  *  http://www.w3.org/TR/REC-xml-names/.
20  *
21  *  Currently, we assume that the document does not contain a plethora
22  *  of namespaces and prefixes. So we keep them in memory until the
23  *  document ends.
24  */
25
26 struct ns_hash_entry {
27   uint ns;
28   char name[1];
29 };
30
31 #define HASH_NODE struct ns_hash_entry
32 #define HASH_PREFIX(x) ns_hash_##x
33 #define HASH_KEY_ENDSTRING name
34 #define HASH_WANT_CLEANUP
35 #define HASH_WANT_FIND
36 #define HASH_WANT_LOOKUP
37 #define HASH_TABLE_DYNAMIC
38 #define HASH_LOOKUP_DETECT_NEW
39 #define HASH_GIVE_ALLOC
40 XML_HASH_GIVE_ALLOC
41 #include <ucw/hashtable.h>
42
43 struct xml_ns_prefix {
44   struct xml_ns_prefix *prev;
45   struct xml_node *e;                   /* Which element defined this prefix */
46   struct ns_hash_entry *he;             /* NULL if changing default NS */
47   uint prev_ns;                         /* Previous NS ID assigned to this prefix */
48 };
49
50 static bool
51 ns_enabled(struct xml_context *ctx)
52 {
53   return (ctx->flags & XML_NAMESPACES);
54 }
55
56 void
57 xml_ns_enable(struct xml_context *ctx)
58 {
59   if (ns_enabled(ctx))
60     return;
61
62   TRACE(ctx, "NS: Enabling");
63   ctx->flags |= XML_NAMESPACES;
64   if (!ctx->ns_pool)
65     {
66       TRACE(ctx, "NS: Allocating data structures");
67       ctx->ns_pool = mp_new(4096);
68       GARY_INIT(ctx->ns_by_id, 16);
69     }
70
71   ctx->ns_by_name = xml_hash_new(ctx->ns_pool, sizeof(struct ns_hash_table));
72   ns_hash_init(ctx->ns_by_name);
73
74   ctx->ns_by_prefix = xml_hash_new(ctx->ns_pool, sizeof(struct ns_hash_table));
75   ns_hash_init(ctx->ns_by_prefix);
76
77   /* Intern well-known namespaces */
78   GARY_RESIZE(ctx->ns_by_id, 0);
79   uint none_ns = xml_ns_by_name(ctx, "");
80   uint xmlns_ns = xml_ns_by_name(ctx, "http://www.w3.org/2000/xmlns/");
81   uint xml_ns = xml_ns_by_name(ctx, "http://www.w3.org/XML/1998/namespace");
82   ASSERT(none_ns == XML_NS_NONE && xmlns_ns == XML_NS_XMLNS && xml_ns == XML_NS_XML);
83
84   /* Intern standard prefixes */
85   int new_xmlns, new_xml;
86   ns_hash_lookup(ctx->ns_by_prefix, "xmlns", &new_xmlns)->ns = xmlns_ns;
87   ns_hash_lookup(ctx->ns_by_prefix, "xml", &new_xml)->ns = xml_ns;
88   ASSERT(new_xmlns && new_xml);
89 }
90
91 void
92 xml_ns_cleanup(struct xml_context *ctx)
93 {
94   if (!ctx->ns_pool)
95     return;
96
97   TRACE(ctx, "NS: Cleanup");
98   ns_hash_cleanup(ctx->ns_by_prefix);
99   ns_hash_cleanup(ctx->ns_by_name);
100   GARY_FREE(ctx->ns_by_id);
101   mp_delete(ctx->ns_pool);
102 }
103
104 void
105 xml_ns_reset(struct xml_context *ctx)
106 {
107   if (!ns_enabled(ctx))
108     return;
109
110   TRACE(ctx, "NS: Reset");
111   GARY_RESIZE(ctx->ns_by_id, 1);
112   ctx->ns_by_id[0] = "";
113   mp_flush(ctx->ns_pool);
114 }
115
116 const char *
117 xml_ns_by_id(struct xml_context *ctx, uint ns)
118 {
119   ASSERT(ns < GARY_SIZE(ctx->ns_by_id));
120   return ctx->ns_by_id[ns];
121 }
122
123 uint
124 xml_ns_by_name(struct xml_context *ctx, const char *name)
125 {
126   int new_p;
127   struct ns_hash_entry *he = ns_hash_lookup(ctx->ns_by_name, (char *) name, &new_p);
128   if (new_p)
129     {
130       he->ns = GARY_SIZE(ctx->ns_by_id);
131       ASSERT(he->ns < ~0U);
132       *GARY_PUSH(ctx->ns_by_id) = he->name;
133       TRACE(ctx, "NS: New namespace <%s> with ID %u", he->name, he->ns);
134     }
135   return he->ns;
136 }
137
138 static struct xml_ns_prefix *
139 ns_push_prefix(struct xml_context *ctx)
140 {
141   struct xml_ns_prefix *px = mp_alloc(ctx->stack, sizeof(*px));
142   px->prev = ctx->ns_prefix_stack;
143   ctx->ns_prefix_stack = px;
144   px->e = ctx->node;
145   return px;
146 }
147
148 static uint
149 ns_resolve(struct xml_context *ctx, char **namep, uint default_ns)
150 {
151   char *name = *namep;
152   char *colon = strchr(name, ':');
153   if (colon)
154     {
155       *colon = 0;
156       struct ns_hash_entry *he = ns_hash_find(ctx->ns_by_prefix, name);
157       *colon = ':';
158       if (he && he->ns)
159         {
160           *namep = colon + 1;
161           return he->ns;
162         }
163       else
164         {
165           xml_error(ctx, "Unknown namespace prefix for %s", name);
166           return 0;
167         }
168     }
169   else
170     return default_ns;
171 }
172
173 void xml_ns_push_element(struct xml_context *ctx)
174 {
175   struct xml_node *e = ctx->node;
176   if (!ns_enabled(ctx))
177     {
178       e->ns = 0;
179       return;
180     }
181
182   /* Scan attributes for prefix definitions */
183   XML_ATTR_FOR_EACH(a, e)
184     if (!memcmp(a->name, "xmlns", 5))
185       {
186         struct xml_ns_prefix *px = ns_push_prefix(ctx);
187         uint ns = xml_ns_by_name(ctx, a->val);
188         if (a->name[5] == ':')
189           {
190             if (a->name[6])
191               {
192                 /* New NS prefix */
193                 int new_p;
194                 struct ns_hash_entry *he = ns_hash_lookup(ctx->ns_by_prefix, a->name + 6, &new_p);
195                 if (new_p)
196                   he->ns = 0;
197                 px->he = he;
198                 px->prev_ns = he->ns;
199                 he->ns = ns;
200                 TRACE(ctx, "NS: New prefix <%s> -> ID %u", he->name, he->ns);
201               }
202           }
203         else
204           {
205             /* New default NS */
206             px->he = NULL;
207             px->prev_ns = ctx->ns_default;
208             ctx->ns_default = ns;
209             TRACE(ctx, "New default NS -> ID %u", ns);
210           }
211       }
212
213   /* Resolve namespaces */
214   e->ns = ns_resolve(ctx, &e->name, ctx->ns_default);
215   XML_ATTR_FOR_EACH(a, e)
216     a->ns = ns_resolve(ctx, &a->name, 0);
217 }
218
219 void xml_ns_pop_element(struct xml_context *ctx)
220 {
221   if (!ns_enabled(ctx))
222     return;
223
224   struct xml_ns_prefix *px;
225   while ((px = ctx->ns_prefix_stack) && px->e == ctx->node)
226     {
227       struct ns_hash_entry *he = px->he;
228       if (he)
229         {
230           TRACE(ctx, "NS: Restoring prefix <%s> -> ID %u", he->name, px->prev_ns);
231           he->ns = px->prev_ns;
232         }
233       else
234         {
235           TRACE(ctx, "NS: Restoring default NS -> ID %u", px->prev_ns);
236           ctx->ns_default = px->prev_ns;
237         }
238       ctx->ns_prefix_stack = px->prev;
239     }
240 }