4 * Copyright (C) 2005 Novell, Inc.
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>
32 #include "beagle-private.h"
33 #include "beagle-parser.h"
34 #include "beagle-request.h"
35 #include "beagle-response.h"
39 /* I would kill a man for some reflection */
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
;
60 BeagleResponse
*response
;
63 /* Used for debugging */
69 start_message (BeagleParserContext
*ctx
, const char **attrs
)
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.
95 gtype_to_match
= (GType
) g_hash_table_lookup (klass
->response_types
,
98 if (gtype_to_match
!= 0)
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 */
112 BEAGLE_PARSER_STATE_TOPLEVEL
,
113 BEAGLE_PARSER_STATE_RESPONSE_WRAPPER
,
118 BEAGLE_PARSER_STATE_RESPONSE_WRAPPER
,
119 BEAGLE_PARSER_STATE_MESSAGE
,
127 sax_start_document (void *data
)
129 BeagleParserContext
*ctx
= (BeagleParserContext
*) data
;
131 ctx
->state
= BEAGLE_PARSER_STATE_TOPLEVEL
;
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
)
148 for (i
= 0; handlers
[i
].name
!= NULL
; i
++) {
149 BeagleParserHandler handler
= handlers
[i
];
153 state
= handler
.src_state
;
155 state
= handler
.dest_state
;
157 /* -1 here is last state before we get to the per
160 state
= BEAGLE_PARSER_LAST_STATE
;
162 if ((! src
|| state
== ctx
->state
) &&
163 strcmp (handler
.name
, name
) == 0) {
165 return &handlers
[i
];
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
);
183 /* Now try the per-object handlers */
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
);
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
);
212 g_warning ("Unhandled element: %s!\n", name
);
215 g_free (ctx
->text_buffer
);
216 ctx
->text_buffer
= NULL
;
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
;
233 ctx
->state
= handler
->src_state
;
236 g_free (ctx
->text_buffer
);
237 ctx
->text_buffer
= NULL
;
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
;
253 ctx
->text_buffer
= g_strndup (ch
, len
);
254 ctx
->buffer_len
= len
;
259 sax_warning (void *data
, const char *msg
, ...)
263 va_start (args
, msg
);
265 printf ("warning: ");
272 sax_error (void *data
, const char *msg
, ...)
276 BeagleParserContext
*ctx
= (BeagleParserContext
*)data
;
278 g_print ("String is: %s\n", ctx
->debug_str
->str
);
280 va_start (args
, msg
);
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 */
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);
331 _beagle_parser_context_get_response (BeagleParserContext
*ctx
)
333 return ctx
->response
;
337 _beagle_parser_context_get_text_buffer (BeagleParserContext
*ctx
)
339 return g_strndup (ctx
->text_buffer
, ctx
->buffer_len
);
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
,
350 ctx
->debug_str
= g_string_new (NULL
);
355 g_string_append_len (ctx
->debug_str
, buf
, bytes
);
357 xmlParseChunk (ctx
->xml_context
, buf
, bytes
, 0);
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
;
371 g_print ("Message: %s\n", ctx
->debug_str
->str
);
372 g_string_free (ctx
->debug_str
, TRUE
);
376 resp
= ctx
->response
;
378 g_free (ctx
->message_type
);
379 g_free (ctx
->text_buffer
);