Dont add null mimetypes. Fixes bgo# 337431. The patch hasnt been officially accepted...
[beagle.git] / libbeagle / beagle / beagle-parser.c
blob2315b1bfdc9a42bfed0768eab8188ab459e8de4b
1 /*
2 * beagle-parser.c
4 * Copyright (C) 2005 Novell, Inc.
6 */
8 /*
9 * Permission is hereby granted, free of charge, to any person obtaining a
10 * copy of this software and associated documentation files (the "Software"),
11 * to deal in the Software without restriction, including without limitation
12 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
13 * and/or sell copies of the Software, and to permit persons to whom the
14 * Software is furnished to do so, subject to the following conditions:
16 * The above copyright notice and this permission notice shall be included in
17 * all copies or substantial portions of the Software.
19 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
24 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
25 * DEALINGS IN THE SOFTWARE.
28 #include <libxml/parser.h>
29 #include <glib.h>
30 #include <string.h>
32 #include "beagle-private.h"
33 #include "beagle-parser.h"
34 #include "beagle-request.h"
35 #include "beagle-response.h"
37 #undef PARSER_DEBUG
39 /* I would kill a man for some reflection */
41 enum {
42 /* Always present layers */
43 BEAGLE_PARSER_STATE_TOPLEVEL,
44 BEAGLE_PARSER_STATE_RESPONSE_WRAPPER,
45 BEAGLE_PARSER_STATE_MESSAGE,
46 BEAGLE_PARSER_LAST_STATE = BEAGLE_PARSER_STATE_MESSAGE
49 int _beagle_parser_state_index = BEAGLE_PARSER_LAST_STATE;
51 typedef enum {
53 /* Specific message types */
54 /* ErrorResponse */
55 PARSER_STATE_ERROR_MESSAGE,
57 /* HitsAddedResponse */
58 PARSER_STATE_HITS_ADDED_HITS,
59 PARSER_STATE_HITS_ADDED_HIT,
60 PARSER_STATE_HITS_ADDED_PROPERTIES,
61 PARSER_STATE_HITS_ADDED_PROPERTY,
63 /* HitsSubtractedResponse */
64 PARSER_STATE_HITS_SUBTRACTED_URIS,
65 PARSER_STATE_HITS_SUBTRACTED_URI,
67 /* DaemonInformationResponse */
68 } ParserState;
70 struct _BeagleParserContext {
71 xmlParserCtxt *xml_context;
73 ParserState state;
75 char *text_buffer;
76 int buffer_len;
78 char *message_type;
79 BeagleResponse *response;
81 #ifdef PARSER_DEBUG
82 /* Used for debugging */
83 GString *debug_str;
84 #endif
87 static void
88 start_message (BeagleParserContext *ctx, const char **attrs)
90 int i;
91 guint num_gtypes;
92 GType *gtypes;
93 GType gtype_to_match = 0;
95 for (i = 0; attrs [i] != NULL; i += 2) {
96 if (strcmp (attrs [i], "xsi:type") == 0)
97 ctx->message_type = g_strdup (attrs [i + 1]);
100 gtypes = g_type_children (BEAGLE_TYPE_REQUEST, &num_gtypes);
102 for (i = 0; i < num_gtypes; i++) {
103 BeagleRequestClass *klass = g_type_class_peek (gtypes [i]);
106 * This can validly return NULL if we're trying to peek at a
107 * class which has never been referenced. It'll never happen
108 * for the class we're looking for, however, because it must
109 * have been referenced to be instantiated in the first place.
111 if (klass == NULL)
112 continue;
114 gtype_to_match = (GType) g_hash_table_lookup (klass->response_types,
115 ctx->message_type);
117 if (gtype_to_match != 0)
118 break;
121 g_free (gtypes);
122 g_assert (gtype_to_match != 0);
124 ctx->response = g_object_new (gtype_to_match, 0);
128 static BeagleParserHandler parser_handlers[] = {
129 /* Always present layers */
130 { "ResponseWrapper",
131 BEAGLE_PARSER_STATE_TOPLEVEL,
132 BEAGLE_PARSER_STATE_RESPONSE_WRAPPER,
133 NULL,
134 NULL },
136 { "Message",
137 BEAGLE_PARSER_STATE_RESPONSE_WRAPPER,
138 BEAGLE_PARSER_STATE_MESSAGE,
139 start_message,
140 NULL },
142 { 0 }
145 static void
146 sax_start_document (void *data)
148 BeagleParserContext *ctx = (BeagleParserContext *) data;
150 ctx->state = BEAGLE_PARSER_STATE_TOPLEVEL;
153 static void
154 sax_end_document (void *data)
156 BeagleParserContext *ctx = (BeagleParserContext *) data;
158 if (ctx->state != BEAGLE_PARSER_STATE_TOPLEVEL)
159 printf ("Invalid document!\n");
162 static BeagleParserHandler *
163 find_handler (BeagleParserContext *ctx, BeagleParserHandler *handlers, const xmlChar *name, gboolean src)
165 int i;
167 for (i = 0; handlers [i].name != NULL; i++) {
168 BeagleParserHandler handler = handlers [i];
169 int state;
171 if (src)
172 state = handler.src_state;
173 else
174 state = handler.dest_state;
176 /* -1 here is last state before we get to the per
177 message elements */
178 if (state == -1)
179 state = BEAGLE_PARSER_LAST_STATE;
181 if (state == ctx->state &&
182 strcmp (handler.name, name) == 0) {
184 return &handlers [i];
188 return NULL;
191 static BeagleParserHandler *
192 get_handler (BeagleParserContext *ctx, const xmlChar *name, gboolean src)
194 BeagleResponseClass *response_class;
195 BeagleParserHandler *handler;
197 handler = find_handler (ctx, parser_handlers, name, src);
199 if (handler)
200 return handler;
202 /* Now try the per-object handlers */
203 if (ctx->response) {
204 response_class = BEAGLE_RESPONSE_GET_CLASS (ctx->response);
206 if (response_class->parser_handlers) {
207 handler = find_handler (ctx, response_class->parser_handlers, name, src);
209 if (handler)
210 return handler;
214 return NULL;
217 static void
218 sax_start_element (void *data, const xmlChar *name, const xmlChar **attrs)
220 BeagleParserContext *ctx = (BeagleParserContext *) data;
221 BeagleParserHandler *handler;
224 handler = get_handler (ctx, name, TRUE);
226 if (handler != NULL) {
227 ctx->state = handler->dest_state;
229 if (handler->start_element_func != NULL)
230 handler->start_element_func (ctx, (const char **)attrs);
231 } else {
232 g_warning ("Unhandled element: %s!\n", name);
235 g_free (ctx->text_buffer);
236 ctx->text_buffer = NULL;
239 static void
240 sax_end_element (void *data, const xmlChar *name)
242 BeagleParserContext *ctx = (BeagleParserContext *) data;
243 BeagleParserHandler *handler;
245 handler = get_handler (ctx, name, FALSE);
246 if (handler != NULL) {
247 if (handler->end_element_func != NULL)
248 handler->end_element_func (ctx);
250 if (handler->src_state == -1)
251 ctx->state = BEAGLE_PARSER_LAST_STATE;
252 else
253 ctx->state = handler->src_state;
256 g_free (ctx->text_buffer);
257 ctx->text_buffer = NULL;
260 static void
261 sax_characters (void *data, const xmlChar *ch, int len)
263 BeagleParserContext *ctx = (BeagleParserContext *) data;
265 if (ctx->text_buffer != NULL) {
266 char *buf = g_malloc0 (ctx->buffer_len + len + 1);
267 strcpy (buf, ctx->text_buffer);
268 strncpy (buf + ctx->buffer_len, ch, len);
269 g_free (ctx->text_buffer);
270 ctx->text_buffer = buf;
271 ctx->buffer_len += len;
272 } else {
273 ctx->text_buffer = g_strndup (ch, len);
274 ctx->buffer_len = len;
278 static void
279 sax_warning (void *data, const char *msg, ...)
281 va_list args;
283 va_start (args, msg);
285 printf ("warning: ");
286 vprintf (msg, args);
288 va_end (args);
291 static void
292 sax_error (void *data, const char *msg, ...)
294 va_list args;
295 #ifdef PARSER_DEBUG
296 BeagleParserContext *ctx = (BeagleParserContext *)data;
298 g_print ("String is: %s\n", ctx->debug_str->str);
299 #endif
300 va_start (args, msg);
302 printf ("error: ");
303 vprintf (msg, args);
305 va_end (args);
307 g_warning ("got parser error");
310 static xmlSAXHandler sax_handler = {
311 NULL, /* internalSubset */
312 NULL, /* isStandalone */
313 NULL, /* hasInternalSubset */
314 NULL, /* hasExternalSubset */
315 NULL, /* resolveEntity */
316 NULL, /* getEntity */
317 NULL, /* entityDecl */
318 NULL, /* notationDecl */
319 NULL, /* attributeDecl */
320 NULL, /* elementDecl */
321 NULL, /* unparsedEntityDecl */
322 NULL, /* setDocumentLocator */
323 sax_start_document, /* startDocument */
324 sax_end_document, /* endDocument */
325 sax_start_element, /* startElement */
326 sax_end_element, /* endElement */
327 NULL, /* reference */
328 sax_characters, /* characters */
329 NULL, /* ignorableWhitespace */
330 NULL, /* processingInstruction */
331 NULL, /* comment */
332 sax_warning, /* warning */
333 sax_error, /* error */
334 sax_error, /* fatalError */
337 BeagleParserContext *
338 _beagle_parser_context_new (void)
340 BeagleParserContext *ctx = g_new0 (BeagleParserContext, 1);
341 ctx->message_type = NULL;
342 ctx->text_buffer = NULL;
343 ctx->xml_context = NULL;
345 xmlSubstituteEntitiesDefault (1);
347 return ctx;
350 BeagleResponse *
351 _beagle_parser_context_get_response (BeagleParserContext *ctx)
353 return ctx->response;
356 char *
357 _beagle_parser_context_get_text_buffer (BeagleParserContext *ctx)
359 return g_strndup (ctx->text_buffer, ctx->buffer_len);
363 void
364 _beagle_parser_context_parse_chunk (BeagleParserContext *ctx, const char *buf, gsize bytes)
366 if (ctx->xml_context == NULL) {
367 ctx->xml_context = xmlCreatePushParserCtxt (&sax_handler, ctx,
368 NULL, 0, NULL);
369 #ifdef PARSER_DEBUG
370 ctx->debug_str = g_string_new (NULL);
371 #endif
374 #ifdef PARSER_DEBUG
375 g_string_append_len (ctx->debug_str, buf, bytes);
376 #endif
377 xmlParseChunk (ctx->xml_context, buf, bytes, 0);
380 BeagleResponse *
381 _beagle_parser_context_finished (BeagleParserContext *ctx)
383 BeagleResponse *resp;
385 if (ctx->xml_context != NULL) {
386 xmlParseChunk (ctx->xml_context, NULL, 0, 1);
387 xmlFreeParserCtxt (ctx->xml_context);
388 ctx->xml_context = NULL;
390 #ifdef PARSER_DEBUG
391 g_print ("Message: %s\n", ctx->debug_str->str);
392 g_string_free (ctx->debug_str, TRUE);
393 #endif
396 resp = ctx->response;
398 g_free (ctx->message_type);
399 g_free (ctx->text_buffer);
400 g_free (ctx);
402 return resp;