2 * UCW Library -- A simple XML parser -- Namespaces
4 * (c) 2015 Martin Mares <mj@ucw.cz>
6 * This software may be freely distributed and used according to the terms
7 * of the GNU Lesser General Public License.
14 #include <ucw-xml/xml.h>
15 #include <ucw-xml/internals.h>
18 * This is an implementation of XML namespaces according to
19 * http://www.w3.org/TR/REC-xml-names/.
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
26 struct ns_hash_entry {
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
41 #include <ucw/hashtable.h>
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 */
51 ns_enabled(struct xml_context *ctx)
53 return (ctx->flags & XML_NAMESPACES);
57 xml_ns_enable(struct xml_context *ctx)
62 TRACE(ctx, "NS: Enabling");
63 ctx->flags |= XML_NAMESPACES;
66 TRACE(ctx, "NS: Allocating data structures");
67 ctx->ns_pool = mp_new(4096);
68 GARY_INIT(ctx->ns_by_id, 16);
71 ctx->ns_by_name = xml_hash_new(ctx->ns_pool, sizeof(struct ns_hash_table));
72 ns_hash_init(ctx->ns_by_name);
74 ctx->ns_by_prefix = xml_hash_new(ctx->ns_pool, sizeof(struct ns_hash_table));
75 ns_hash_init(ctx->ns_by_prefix);
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);
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);
92 xml_ns_cleanup(struct xml_context *ctx)
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);
105 xml_ns_reset(struct xml_context *ctx)
107 if (!ns_enabled(ctx))
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);
117 xml_ns_by_id(struct xml_context *ctx, uint ns)
119 ASSERT(ns < GARY_SIZE(ctx->ns_by_id));
120 return ctx->ns_by_id[ns];
124 xml_ns_by_name(struct xml_context *ctx, const char *name)
127 struct ns_hash_entry *he = ns_hash_lookup(ctx->ns_by_name, (char *) name, &new_p);
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);
138 static struct xml_ns_prefix *
139 ns_push_prefix(struct xml_context *ctx)
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;
149 ns_resolve(struct xml_context *ctx, char **namep, uint default_ns)
152 char *colon = strchr(name, ':');
156 struct ns_hash_entry *he = ns_hash_find(ctx->ns_by_prefix, name);
165 xml_error(ctx, "Unknown namespace prefix for %s", name);
173 void xml_ns_push_element(struct xml_context *ctx)
175 struct xml_node *e = ctx->node;
176 if (!ns_enabled(ctx))
182 /* Scan attributes for prefix definitions */
183 XML_ATTR_FOR_EACH(a, e)
184 if (!memcmp(a->name, "xmlns", 5))
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] == ':')
194 struct ns_hash_entry *he = ns_hash_lookup(ctx->ns_by_prefix, a->name + 6, &new_p);
198 px->prev_ns = he->ns;
200 TRACE(ctx, "NS: New prefix <%s> -> ID %u", he->name, he->ns);
207 px->prev_ns = ctx->ns_default;
208 ctx->ns_default = ns;
209 TRACE(ctx, "New default NS -> ID %u", ns);
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);
219 void xml_ns_pop_element(struct xml_context *ctx)
221 if (!ns_enabled(ctx))
224 struct xml_ns_prefix *px;
225 while ((px = ctx->ns_prefix_stack) && px->e == ctx->node)
227 struct ns_hash_entry *he = px->he;
230 TRACE(ctx, "NS: Restoring prefix <%s> -> ID %u", he->name, px->prev_ns);
231 he->ns = px->prev_ns;
235 TRACE(ctx, "NS: Restoring default NS -> ID %u", px->prev_ns);
236 ctx->ns_default = px->prev_ns;
238 ctx->ns_prefix_stack = px->prev;