cvsimport
[beagle.git] / libbeagle / beagle / beagle-parser.c
bloba7582e280ed5f88e204e6ed24f857aad815689f5
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 + 1;
51 struct _BeagleParserContext {
52 xmlParserCtxt *xml_context;
54 int state;
56 char *text_buffer;
57 int buffer_len;
59 char *message_type;
60 BeagleResponse *response;
62 #ifdef PARSER_DEBUG
63 /* Used for debugging */
64 GString *debug_str;
65 #endif
68 static void
69 start_message (BeagleParserContext *ctx, const char **attrs)
71 int i;
72 guint num_gtypes;
73 GType *gtypes;
74 GType gtype_to_match = 0;
76 for (i = 0; attrs [i] != NULL; i += 2) {
77 if (strcmp (attrs [i], "xsi:type") == 0)
78 ctx->message_type = g_strdup (attrs [i + 1]);
81 gtypes = g_type_children (BEAGLE_TYPE_REQUEST, &num_gtypes);
83 for (i = 0; i < num_gtypes; i++) {
84 BeagleRequestClass *klass = g_type_class_peek (gtypes [i]);
87 * This can validly return NULL if we're trying to peek at a
88 * class which has never been referenced. It'll never happen
89 * for the class we're looking for, however, because it must
90 * have been referenced to be instantiated in the first place.
92 if (klass == NULL)
93 continue;
95 gtype_to_match = (GType) g_hash_table_lookup (klass->response_types,
96 ctx->message_type);
98 if (gtype_to_match != 0)
99 break;
102 g_free (gtypes);
103 g_assert (gtype_to_match != 0);
105 ctx->response = g_object_new (gtype_to_match, 0);
109 static BeagleParserHandler parser_handlers[] = {
110 /* Always present layers */
111 { "ResponseWrapper",
112 BEAGLE_PARSER_STATE_TOPLEVEL,
113 BEAGLE_PARSER_STATE_RESPONSE_WRAPPER,
114 NULL,
115 NULL },
117 { "Message",
118 BEAGLE_PARSER_STATE_RESPONSE_WRAPPER,
119 BEAGLE_PARSER_STATE_MESSAGE,
120 start_message,
121 NULL },
123 { 0 }
126 static void
127 sax_start_document (void *data)
129 BeagleParserContext *ctx = (BeagleParserContext *) data;
131 ctx->state = BEAGLE_PARSER_STATE_TOPLEVEL;
134 static void
135 sax_end_document (void *data)
137 BeagleParserContext *ctx = (BeagleParserContext *) data;
139 if (ctx->state != BEAGLE_PARSER_STATE_TOPLEVEL)
140 g_warning ("Invalid document!\n");
143 static BeagleParserHandler *
144 find_handler (BeagleParserContext *ctx, BeagleParserHandler *handlers, const xmlChar *name, gboolean src)
146 int i;
148 for (i = 0; handlers [i].name != NULL; i++) {
149 BeagleParserHandler handler = handlers [i];
150 int state;
152 if (src)
153 state = handler.src_state;
154 else
155 state = handler.dest_state;
157 /* -1 here is last state before we get to the per
158 message elements */
159 if (state == -1)
160 state = BEAGLE_PARSER_LAST_STATE;
162 if ((! src || state == ctx->state) &&
163 strcmp (handler.name, name) == 0) {
165 return &handlers [i];
169 return NULL;
172 static BeagleParserHandler *
173 get_handler (BeagleParserContext *ctx, const xmlChar *name, gboolean src)
175 BeagleResponseClass *response_class;
176 BeagleParserHandler *handler;
178 handler = find_handler (ctx, parser_handlers, name, src);
180 if (handler)
181 return handler;
183 /* Now try the per-object handlers */
184 if (ctx->response) {
185 response_class = BEAGLE_RESPONSE_GET_CLASS (ctx->response);
187 if (response_class->parser_handlers) {
188 handler = find_handler (ctx, response_class->parser_handlers, name, src);
190 if (handler)
191 return handler;
195 return NULL;
198 static void
199 sax_start_element (void *data, const xmlChar *name, const xmlChar **attrs)
201 BeagleParserContext *ctx = (BeagleParserContext *) data;
202 BeagleParserHandler *handler;
204 handler = get_handler (ctx, name, TRUE);
206 if (handler != NULL) {
207 ctx->state = handler->dest_state;
209 if (handler->start_element_func != NULL)
210 handler->start_element_func (ctx, (const char **)attrs);
211 } else {
212 g_warning ("Unhandled element: %s!\n", name);
215 g_free (ctx->text_buffer);
216 ctx->text_buffer = NULL;
219 static void
220 sax_end_element (void *data, const xmlChar *name)
222 BeagleParserContext *ctx = (BeagleParserContext *) data;
223 BeagleParserHandler *handler;
225 handler = get_handler (ctx, name, FALSE);
226 if (handler != NULL) {
227 if (handler->end_element_func != NULL)
228 handler->end_element_func (ctx);
230 if (handler->src_state == -1)
231 ctx->state = BEAGLE_PARSER_LAST_STATE;
232 else
233 ctx->state = handler->src_state;
236 g_free (ctx->text_buffer);
237 ctx->text_buffer = NULL;
240 static void
241 sax_characters (void *data, const xmlChar *ch, int len)
243 BeagleParserContext *ctx = (BeagleParserContext *) data;
245 if (ctx->text_buffer != NULL) {
246 char *buf = g_malloc0 (ctx->buffer_len + len + 1);
247 strcpy (buf, ctx->text_buffer);
248 strncpy (buf + ctx->buffer_len, ch, len);
249 g_free (ctx->text_buffer);
250 ctx->text_buffer = buf;
251 ctx->buffer_len += len;
252 } else {
253 ctx->text_buffer = g_strndup (ch, len);
254 ctx->buffer_len = len;
258 static void
259 sax_warning (void *data, const char *msg, ...)
261 va_list args;
263 va_start (args, msg);
265 printf ("warning: ");
266 vprintf (msg, args);
268 va_end (args);
271 static void
272 sax_error (void *data, const char *msg, ...)
274 va_list args;
275 #ifdef PARSER_DEBUG
276 BeagleParserContext *ctx = (BeagleParserContext *)data;
278 g_print ("String is: %s\n", ctx->debug_str->str);
279 #endif
280 va_start (args, msg);
282 printf ("error: ");
283 vprintf (msg, args);
285 va_end (args);
287 g_warning ("got parser error");
290 static xmlSAXHandler sax_handler = {
291 NULL, /* internalSubset */
292 NULL, /* isStandalone */
293 NULL, /* hasInternalSubset */
294 NULL, /* hasExternalSubset */
295 NULL, /* resolveEntity */
296 NULL, /* getEntity */
297 NULL, /* entityDecl */
298 NULL, /* notationDecl */
299 NULL, /* attributeDecl */
300 NULL, /* elementDecl */
301 NULL, /* unparsedEntityDecl */
302 NULL, /* setDocumentLocator */
303 sax_start_document, /* startDocument */
304 sax_end_document, /* endDocument */
305 sax_start_element, /* startElement */
306 sax_end_element, /* endElement */
307 NULL, /* reference */
308 sax_characters, /* characters */
309 NULL, /* ignorableWhitespace */
310 NULL, /* processingInstruction */
311 NULL, /* comment */
312 sax_warning, /* warning */
313 sax_error, /* error */
314 sax_error, /* fatalError */
317 BeagleParserContext *
318 _beagle_parser_context_new (void)
320 BeagleParserContext *ctx = g_new0 (BeagleParserContext, 1);
321 ctx->message_type = NULL;
322 ctx->text_buffer = NULL;
323 ctx->xml_context = NULL;
325 xmlSubstituteEntitiesDefault (1);
327 return ctx;
330 BeagleResponse *
331 _beagle_parser_context_get_response (BeagleParserContext *ctx)
333 return ctx->response;
336 char *
337 _beagle_parser_context_get_text_buffer (BeagleParserContext *ctx)
339 return g_strndup (ctx->text_buffer, ctx->buffer_len);
343 void
344 _beagle_parser_context_parse_chunk (BeagleParserContext *ctx, const char *buf, gsize bytes)
346 if (ctx->xml_context == NULL) {
347 ctx->xml_context = xmlCreatePushParserCtxt (&sax_handler, ctx,
348 NULL, 0, NULL);
349 #ifdef PARSER_DEBUG
350 ctx->debug_str = g_string_new (NULL);
351 #endif
354 #ifdef PARSER_DEBUG
355 g_string_append_len (ctx->debug_str, buf, bytes);
356 #endif
357 xmlParseChunk (ctx->xml_context, buf, bytes, 0);
360 BeagleResponse *
361 _beagle_parser_context_finished (BeagleParserContext *ctx)
363 BeagleResponse *resp;
365 if (ctx->xml_context != NULL) {
366 xmlParseChunk (ctx->xml_context, NULL, 0, 1);
367 xmlFreeParserCtxt (ctx->xml_context);
368 ctx->xml_context = NULL;
370 #ifdef PARSER_DEBUG
371 g_print ("Message: %s\n", ctx->debug_str->str);
372 g_string_free (ctx->debug_str, TRUE);
373 #endif
376 resp = ctx->response;
378 g_free (ctx->message_type);
379 g_free (ctx->text_buffer);
380 g_free (ctx);
382 return resp;