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/string.h>
15 #include <ucw-xml/xml.h>
16 #include <ucw-xml/internals.h>
19 * This is an implementation of XML namespaces according to
20 * http://www.w3.org/TR/REC-xml-names/.
22 * Currently, we assume that the document does not contain a plethora
23 * of namespaces and prefixes. So we keep them in memory until the
27 struct ns_hash_entry {
32 #define HASH_NODE struct ns_hash_entry
33 #define HASH_PREFIX(x) ns_hash_##x
34 #define HASH_KEY_ENDSTRING name
35 #define HASH_WANT_FIND
36 #define HASH_WANT_LOOKUP
37 #define HASH_TABLE_DYNAMIC
38 #define HASH_TABLE_ALLOC
39 #define HASH_LOOKUP_DETECT_NEW
40 #define HASH_GIVE_ALLOC
42 #include <ucw/hashtable.h>
44 struct xml_ns_prefix {
45 struct xml_ns_prefix *prev;
46 struct xml_node *e; /* Which element defined this prefix */
47 struct ns_hash_entry *he; /* NULL if changing default NS */
48 uint prev_ns; /* Previous NS ID assigned to this prefix */
52 ns_enabled(struct xml_context *ctx)
54 return (ctx->flags & XML_NAMESPACES);
58 xml_ns_enable(struct xml_context *ctx)
63 TRACE(ctx, "NS: Enabling");
65 ctx->flags |= XML_NAMESPACES;
68 // CAVEAT: xml_reset() must handle everything we allocate here
69 TRACE(ctx, "NS: Allocating data structures");
70 ctx->ns_pool = mp_new(4096);
71 GARY_INIT(ctx->ns_by_id, 16);
74 ctx->ns_by_name = xml_hash_new(ctx->ns_pool, sizeof(struct ns_hash_table));
75 ns_hash_init(ctx->ns_by_name);
77 ctx->ns_by_prefix = xml_hash_new(ctx->ns_pool, sizeof(struct ns_hash_table));
78 ns_hash_init(ctx->ns_by_prefix);
80 /* Intern well-known namespaces */
81 GARY_RESIZE(ctx->ns_by_id, 0);
82 uint none_ns = xml_ns_by_name(ctx, "");
83 uint xmlns_ns = xml_ns_by_name(ctx, "http://www.w3.org/2000/xmlns/");
84 uint xml_ns = xml_ns_by_name(ctx, "http://www.w3.org/XML/1998/namespace");
85 ASSERT(none_ns == XML_NS_NONE && xmlns_ns == XML_NS_XMLNS && xml_ns == XML_NS_XML);
87 /* Intern standard prefixes */
88 int new_xmlns, new_xml;
89 ns_hash_lookup(ctx->ns_by_prefix, "xmlns", &new_xmlns)->ns = xmlns_ns;
90 ns_hash_lookup(ctx->ns_by_prefix, "xml", &new_xml)->ns = xml_ns;
91 ASSERT(new_xmlns && new_xml);
95 xml_ns_cleanup(struct xml_context *ctx)
100 TRACE(ctx, "NS: Cleanup");
101 GARY_FREE(ctx->ns_by_id);
102 mp_delete(ctx->ns_pool);
106 xml_ns_reset(struct xml_context *ctx)
108 if (!ns_enabled(ctx))
111 TRACE(ctx, "NS: Reset");
112 GARY_RESIZE(ctx->ns_by_id, 0);
113 mp_flush(ctx->ns_pool);
117 xml_ns_by_id(struct xml_context *ctx, uint ns)
119 if (!ns) // This should work even if namespaces are disabled
121 ASSERT(ns_enabled(ctx) && ns < GARY_SIZE(ctx->ns_by_id));
122 return ctx->ns_by_id[ns];
126 xml_ns_by_name(struct xml_context *ctx, const char *name)
128 ASSERT(ns_enabled(ctx));
130 struct ns_hash_entry *he = ns_hash_lookup(ctx->ns_by_name, (char *) name, &new_p);
133 he->ns = GARY_SIZE(ctx->ns_by_id);
134 ASSERT(he->ns < ~0U);
135 *GARY_PUSH(ctx->ns_by_id) = he->name;
136 TRACE(ctx, "NS: New namespace <%s> with ID %u", he->name, he->ns);
141 static struct xml_ns_prefix *
142 ns_push_prefix(struct xml_context *ctx)
144 struct xml_ns_prefix *px = mp_alloc(ctx->stack, sizeof(*px));
145 px->prev = ctx->ns_prefix_stack;
146 ctx->ns_prefix_stack = px;
152 ns_resolve(struct xml_context *ctx, char **namep, uint default_ns)
155 char *colon = strchr(name, ':');
159 struct ns_hash_entry *he = ns_hash_find(ctx->ns_by_prefix, name);
168 xml_error(ctx, "Unknown namespace prefix for %s", name);
176 void xml_ns_push_element(struct xml_context *ctx)
178 struct xml_node *e = ctx->node;
179 if (!ns_enabled(ctx))
185 /* Scan attributes for prefix definitions */
186 XML_ATTR_FOR_EACH(a, e)
187 if (str_has_prefix(a->name, "xmlns"))
189 uint ns = xml_ns_by_name(ctx, a->val);
190 if (a->name[5] == ':')
193 xml_error(ctx, "Namespace prefixes must not be undeclared");
198 struct ns_hash_entry *he = ns_hash_lookup(ctx->ns_by_prefix, a->name + 6, &new_p);
201 struct xml_ns_prefix *px = ns_push_prefix(ctx);
203 px->prev_ns = he->ns;
205 TRACE(ctx, "NS: New prefix <%s> -> ID %u", he->name, he->ns);
208 xml_error(ctx, "Invalid namespace prefix");
210 else if (!a->name[5])
213 struct xml_ns_prefix *px = ns_push_prefix(ctx);
215 px->prev_ns = ctx->ns_default;
216 ctx->ns_default = ns;
217 TRACE(ctx, "New default NS -> ID %u", ns);
221 /* Resolve namespaces */
222 e->ns = ns_resolve(ctx, &e->name, ctx->ns_default);
223 XML_ATTR_FOR_EACH(a, e)
224 a->ns = ns_resolve(ctx, &a->name, 0);
227 void xml_ns_pop_element(struct xml_context *ctx)
229 if (!ns_enabled(ctx))
232 struct xml_ns_prefix *px;
233 while ((px = ctx->ns_prefix_stack) && px->e == ctx->node)
235 struct ns_hash_entry *he = px->he;
238 TRACE(ctx, "NS: Restoring prefix <%s> -> ID %u", he->name, px->prev_ns);
239 he->ns = px->prev_ns;
243 TRACE(ctx, "NS: Restoring default NS -> ID %u", px->prev_ns);
244 ctx->ns_default = px->prev_ns;
246 ctx->ns_prefix_stack = px->prev;