summaryrefslogtreecommitdiff
path: root/rest/rest-xml-parser.c
diff options
context:
space:
mode:
Diffstat (limited to 'rest/rest-xml-parser.c')
-rw-r--r--rest/rest-xml-parser.c250
1 files changed, 250 insertions, 0 deletions
diff --git a/rest/rest-xml-parser.c b/rest/rest-xml-parser.c
new file mode 100644
index 0000000..b8c94c1
--- /dev/null
+++ b/rest/rest-xml-parser.c
@@ -0,0 +1,250 @@
+#include <libxml/xmlreader.h>
+#include "rest-xml-parser.h"
+
+G_DEFINE_TYPE (RestXmlParser, rest_xml_parser, G_TYPE_OBJECT)
+
+#define GET_PRIVATE(o) \
+ (G_TYPE_INSTANCE_GET_PRIVATE ((o), REST_TYPE_XML_PARSER, RestXmlParserPrivate))
+
+typedef struct _RestXmlParserPrivate RestXmlParserPrivate;
+
+struct _RestXmlParserPrivate {
+ xmlTextReaderPtr reader;
+};
+
+static void
+rest_xml_parser_get_property (GObject *object, guint property_id,
+ GValue *value, GParamSpec *pspec)
+{
+ switch (property_id) {
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ }
+}
+
+static void
+rest_xml_parser_set_property (GObject *object, guint property_id,
+ const GValue *value, GParamSpec *pspec)
+{
+ switch (property_id) {
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ }
+}
+
+static void
+rest_xml_parser_dispose (GObject *object)
+{
+ if (G_OBJECT_CLASS (rest_xml_parser_parent_class)->dispose)
+ G_OBJECT_CLASS (rest_xml_parser_parent_class)->dispose (object);
+}
+
+static void
+rest_xml_parser_finalize (GObject *object)
+{
+ RestXmlParserPrivate *priv = GET_PRIVATE (object);
+
+ xmlFreeTextReader (priv->reader);
+
+ if (G_OBJECT_CLASS (rest_xml_parser_parent_class)->finalize)
+ G_OBJECT_CLASS (rest_xml_parser_parent_class)->finalize (object);
+}
+
+static void
+rest_xml_parser_class_init (RestXmlParserClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ g_type_class_add_private (klass, sizeof (RestXmlParserPrivate));
+
+ object_class->get_property = rest_xml_parser_get_property;
+ object_class->set_property = rest_xml_parser_set_property;
+ object_class->dispose = rest_xml_parser_dispose;
+ object_class->finalize = rest_xml_parser_finalize;
+}
+
+static void
+rest_xml_parser_init (RestXmlParser *self)
+{
+}
+
+static RestXmlNode *
+rest_xml_node_prepend (RestXmlNode *cur_node, RestXmlNode *new_node)
+{
+ g_assert (new_node->next == NULL);
+ new_node->next = cur_node;
+
+ return new_node;
+}
+
+RestXmlNode *
+rest_xml_node_new ()
+{
+ RestXmlNode *node;
+
+ node = g_slice_new0 (RestXmlNode);
+ node->children = g_hash_table_new (g_str_hash, g_str_equal);
+ node->attrs = g_hash_table_new_full (g_str_hash,
+ g_str_equal,
+ g_free,
+ g_free);
+
+ return node;
+}
+
+void
+rest_xml_node_free (RestXmlNode *node)
+{
+ GList *l;
+
+ for (l = g_hash_table_get_values (node->children); l; l = l->next)
+ {
+ rest_xml_node_free ((RestXmlNode *)l->data);
+ }
+
+ g_hash_table_unref (node->children);
+ g_hash_table_unref (node->attrs);
+ g_free (node->name);
+ g_free (node->content);
+ g_slice_free (RestXmlNode, node);
+}
+
+RestXmlParser *
+rest_xml_parser_new (void)
+{
+ return g_object_new (REST_TYPE_XML_PARSER, NULL);
+}
+
+RestXmlNode *
+rest_xml_parser_parse_from_data (RestXmlParser *parser,
+ const gchar *data,
+ gssize len)
+{
+ RestXmlParserPrivate *priv = GET_PRIVATE (parser);
+ RestXmlNode *cur_node = NULL;
+ RestXmlNode *new_node = NULL;
+ RestXmlNode *tmp_node = NULL;
+ RestXmlNode *root_node = NULL;
+ const gchar *name = NULL;
+ const gchar *attr_name = NULL;
+ const gchar *attr_value = NULL;
+ GQueue *nodes = NULL;
+ gint res = 0;
+
+ priv->reader = xmlReaderForMemory (data,
+ len,
+ NULL, /* URL? */
+ NULL, /* encoding */
+ XML_PARSE_RECOVER | XML_PARSE_NOCDATA);
+ nodes = g_queue_new ();
+
+ while ((res = xmlTextReaderRead (priv->reader)) == 1)
+ {
+ switch (xmlTextReaderNodeType (priv->reader))
+ {
+ case XML_READER_TYPE_ELEMENT:
+ /* Lookup the "name" for the tag */
+ name = xmlTextReaderConstLocalName (priv->reader);
+ g_debug (G_STRLOC ": Opening tag: %s", name);
+
+ /* Create our new node for this tag */
+
+ new_node = rest_xml_node_new ();
+ new_node->name = g_strdup (name);
+
+ if (!root_node)
+ {
+ root_node = new_node;
+ }
+
+ /*
+ * Check if we are not the root node because we need to update it's
+ * children set to include the new node.
+ */
+ if (cur_node)
+ {
+ tmp_node = g_hash_table_lookup (cur_node->children, name);
+
+ if (tmp_node)
+ {
+ g_debug (G_STRLOC ": Existing node found for this name. "
+ "Prepending to the list.");
+ g_hash_table_insert (cur_node->children,
+ (gchar *)new_node->name,
+ rest_xml_node_prepend (tmp_node, new_node));
+ } else {
+ g_debug (G_STRLOC ": Unseen name. Adding to the children table.");
+ g_hash_table_insert (cur_node->children,
+ (gchar *)new_node->name,
+ new_node);
+ }
+ }
+
+ /*
+ * Check for empty element. If empty we needn't worry about children
+ * or text and thus we don't need to update the stack or state
+ */
+ if (xmlTextReaderIsEmptyElement (priv->reader))
+ {
+ g_debug (G_STRLOC ": We have an empty element. No children or text.");
+ } else {
+ g_debug (G_STRLOC ": Non-empty element found."
+ " Pushing to stack and updating current state.");
+ g_queue_push_head (nodes, new_node);
+ cur_node = new_node;
+ }
+
+ /*
+ * Check if we have attributes. These get stored in the node's attrs
+ * hash table.
+ */
+ if (xmlTextReaderHasAttributes (priv->reader))
+ {
+ xmlTextReaderMoveToFirstAttribute (priv->reader);
+
+ do {
+ attr_name = xmlTextReaderConstLocalName (priv->reader);
+ attr_value = xmlTextReaderConstValue (priv->reader);
+ g_hash_table_insert (new_node->attrs,
+ g_strdup (attr_name),
+ g_strdup (attr_value));
+
+ g_debug (G_STRLOC ": Attribute found: %s = %s",
+ attr_name,
+ attr_value);
+
+ } while ((res = xmlTextReaderMoveToNextAttribute (priv->reader)) == 1);
+ }
+
+ break;
+ case XML_READER_TYPE_END_ELEMENT:
+ g_debug (G_STRLOC ": Closing tag: %s",
+ xmlTextReaderConstLocalName (priv->reader));
+
+ g_debug (G_STRLOC ": Popping from stack and updating state.");
+ g_queue_pop_head (nodes);
+ cur_node = (RestXmlNode *)g_queue_peek_head (nodes);
+
+ if (cur_node)
+ {
+ g_debug (G_STRLOC ": Head is now %s", cur_node->name);
+ } else {
+ g_debug (G_STRLOC ": At the top level");
+ }
+ break;
+ case XML_READER_TYPE_TEXT:
+ cur_node->content = g_strdup (xmlTextReaderConstValue (priv->reader));
+ g_debug (G_STRLOC ": Text content found: %s",
+ cur_node->content);
+ default:
+ g_debug (G_STRLOC ": Found unknown content with type: 0x%x",
+ xmlTextReaderNodeType (priv->reader));
+ break;
+ }
+ }
+
+ xmlTextReaderClose (priv->reader);
+ return root_node;
+}
+
+