summaryrefslogtreecommitdiff
path: root/rest/rest-xml-node.c
diff options
context:
space:
mode:
authorTomas Frydrych <tf@linux.intel.com>2011-01-31 15:15:36 +0000
committerTomas Frydrych <tf@linux.intel.com>2011-02-10 12:12:34 +0000
commitdb9aefd94e376fb5a984bd59f860adb0abae532d (patch)
treecc36de9293294812aa0ecd9f3f6a1c8af315f855 /rest/rest-xml-node.c
parent880049882106f92ab8db7befa3d6d66ab30d4c37 (diff)
downloadlibrest-db9aefd94e376fb5a984bd59f860adb0abae532d.tar.gz
xml-node: API for manually constructing and outputting XML
Split out RestXmlNode into separate source files and added simple API for manually constructing tree of RextXmlNodes and converting such a tree to a string: * xml_node_add_child() * xml_node_add_attribute() * xml_node_set_content() * xml_node_print() Includes xml test for make check
Diffstat (limited to 'rest/rest-xml-node.c')
-rw-r--r--rest/rest-xml-node.c399
1 files changed, 399 insertions, 0 deletions
diff --git a/rest/rest-xml-node.c b/rest/rest-xml-node.c
new file mode 100644
index 0000000..a691754
--- /dev/null
+++ b/rest/rest-xml-node.c
@@ -0,0 +1,399 @@
+/*
+ * librest - RESTful web services access
+ * Copyright (c) 2008, 2009, 2011, Intel Corporation.
+ *
+ * Authors: Rob Bradford <rob@linux.intel.com>
+ * Ross Burton <ross@linux.intel.com>
+ * Tomas Frydrych <tf@linux.intel.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 2.1, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#include "rest-xml-node.h"
+
+#define G(x) (gchar *)x
+
+static RestXmlNode *
+rest_xml_node_reverse_siblings (RestXmlNode *current)
+{
+ RestXmlNode *next;
+ RestXmlNode *prev = NULL;
+
+ while (current)
+ {
+ next = current->next;
+ current->next = prev;
+
+ prev = current;
+ current = next;
+ }
+
+ return prev;
+}
+
+void
+_rest_xml_node_reverse_children_siblings (RestXmlNode *node)
+{
+ GList *l, *children;
+ RestXmlNode *new_node;
+
+ children = g_hash_table_get_values (node->children);
+
+ for (l = children; l; l = l->next)
+ {
+ new_node = rest_xml_node_reverse_siblings ((RestXmlNode *)l->data);
+ g_hash_table_insert (node->children, new_node->name, new_node);
+ }
+
+ if (children)
+ g_list_free (children);
+}
+
+/*
+ * _rest_xml_node_prepend:
+ * @cur_node: a sibling #RestXmlNode to prepend the new before
+ * @new_node: new #RestXmlNode to prepend to the siblings list
+ *
+ * Prepends new_node to the list of siblings starting at cur_node.
+ *
+ * Return value: (transfer none): returns new start of the sibling list
+ */
+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;
+}
+
+/*
+ * rest_xml_node_append_end:
+ * @cur_node: a member of the sibling #RestXmlNode list
+ * @new_node: new #RestXmlNode to append to the siblings list
+ *
+ * Appends new_node to end of the list of siblings containing cur_node.
+ */
+static void
+rest_xml_node_append_end (RestXmlNode *cur_node, RestXmlNode *new_node)
+{
+ RestXmlNode *tmp = cur_node;
+
+ g_return_if_fail (cur_node);
+
+ while (tmp->next)
+ tmp = tmp->next;
+
+ tmp->next = new_node;
+}
+
+GType
+rest_xml_node_get_type (void)
+{
+ static GType type = 0;
+ if (G_UNLIKELY (type == 0)) {
+ type = g_boxed_type_register_static ("RestXmlNode",
+ (GBoxedCopyFunc)rest_xml_node_ref,
+ (GBoxedFreeFunc)rest_xml_node_unref);
+ }
+ return type;
+}
+
+/*
+ * _rest_xml_node_new:
+ *
+ * Creates a new instance of #RestXmlNode.
+ *
+ * Return value: (transfer full): newly allocated #RestXmlNode.
+ */
+RestXmlNode *
+_rest_xml_node_new ()
+{
+ RestXmlNode *node;
+
+ node = g_slice_new0 (RestXmlNode);
+ node->ref_count = 1;
+ node->children = g_hash_table_new (NULL, NULL);
+ node->attrs = g_hash_table_new_full (g_str_hash,
+ g_str_equal,
+ g_free,
+ g_free);
+
+ return node;
+}
+
+/**
+ * rest_xml_node_ref:
+ * @node: a #RestXmlNode
+ *
+ * Increases the reference count of @node.
+ *
+ * Returns: the same @node.
+ */
+RestXmlNode *
+rest_xml_node_ref (RestXmlNode *node)
+{
+ g_return_val_if_fail (node, NULL);
+ g_return_val_if_fail (node->ref_count > 0, NULL);
+
+ g_atomic_int_inc (&node->ref_count);
+
+ return node;
+}
+
+/**
+ * rest_xml_node_unref:
+ * @node: a #RestXmlNode
+ *
+ * Decreases the reference count of @node. When its reference count drops to 0,
+ * the node is finalized (i.e. its memory is freed).
+ */
+void
+rest_xml_node_unref (RestXmlNode *node)
+{
+ GList *l;
+ RestXmlNode *next = NULL;
+ g_return_if_fail (node);
+ g_return_if_fail (node->ref_count > 0);
+
+ /* Try and unref the chain, this is equivalent to being tail recursively
+ * unreffing the next pointer
+ */
+ while (node && g_atomic_int_dec_and_test (&node->ref_count))
+ {
+ /*
+ * Save this pointer now since we are going to free the structure it
+ * contains soon.
+ */
+ next = node->next;
+
+ l = g_hash_table_get_values (node->children);
+ while (l)
+ {
+ rest_xml_node_unref ((RestXmlNode *)l->data);
+ l = g_list_delete_link (l, l);
+ }
+
+ g_hash_table_unref (node->children);
+ g_hash_table_unref (node->attrs);
+ g_free (node->content);
+ g_slice_free (RestXmlNode, node);
+
+ /*
+ * Free the next in the chain by updating node. If we're at the end or
+ * there are no siblings then the next = NULL definition deals with this
+ * case
+ */
+ node = next;
+ }
+}
+
+G_GNUC_DEPRECATED void
+rest_xml_node_free (RestXmlNode *node)
+{
+ rest_xml_node_unref (node);
+}
+
+/**
+ * rest_xml_node_get_attr:
+ * @node: a #RestXmlNode
+ * @attr_name: the name of an attribute
+ *
+ * Get the value of the attribute named @attr_name, or %NULL if it doesn't
+ * exist.
+ *
+ * Returns: the attribute value. This string is owned by #RestXmlNode and should
+ * not be freed.
+ */
+const gchar *
+rest_xml_node_get_attr (RestXmlNode *node,
+ const gchar *attr_name)
+{
+ return g_hash_table_lookup (node->attrs, attr_name);
+}
+
+/**
+ * rest_xml_node_find:
+ * @start: a #RestXmlNode
+ * @tag: the name of a node
+ *
+ * Searches for the first child node of @start named @tag.
+ *
+ * Returns: the first child node, or %NULL if it doesn't exist.
+ */
+RestXmlNode *
+rest_xml_node_find (RestXmlNode *start,
+ const gchar *tag)
+{
+ RestXmlNode *node;
+ RestXmlNode *tmp;
+ GQueue stack = G_QUEUE_INIT;
+ GList *children, *l;
+ const char *tag_interned;
+
+ g_return_val_if_fail (start, NULL);
+ g_return_val_if_fail (start->ref_count > 0, NULL);
+
+ tag_interned = g_intern_string (tag);
+
+ g_queue_push_head (&stack, start);
+
+ while ((node = g_queue_pop_head (&stack)) != NULL)
+ {
+ if ((tmp = g_hash_table_lookup (node->children, tag_interned)) != NULL)
+ {
+ g_queue_clear (&stack);
+ return tmp;
+ }
+
+ children = g_hash_table_get_values (node->children);
+ for (l = children; l; l = l->next)
+ {
+ g_queue_push_head (&stack, l->data);
+ }
+ g_list_free (children);
+ }
+
+ g_queue_clear (&stack);
+ return NULL;
+}
+
+/**
+ * rest_xml_node_print_node:
+ * @node: #RestXmlNode
+ *
+ * Recursively outputs given node and it's children.
+ *
+ * Return value: (transfer: full): xml string representing the node.
+ */
+char *
+rest_xml_node_print (RestXmlNode *node)
+{
+ GHashTableIter iter;
+ gpointer key, value;
+ char *xml = g_strconcat ("<", node->name, NULL);
+ RestXmlNode *n;
+
+ g_hash_table_iter_init (&iter, node->attrs);
+ while (g_hash_table_iter_next (&iter, &key, &value))
+ xml = g_strconcat (xml, " ", key, "=\'", value, "\'", NULL);
+
+ xml = g_strconcat (xml, ">", NULL);
+
+ g_hash_table_iter_init (&iter, node->children);
+ while (g_hash_table_iter_next (&iter, &key, &value))
+ {
+ char *child = rest_xml_node_print ((RestXmlNode *) value);
+
+ xml = g_strconcat (xml, child, NULL);
+ g_free (child);
+ }
+
+ if (node->content)
+ xml = g_strconcat (xml, node->content, "</", node->name, ">", NULL);
+ else
+ xml = g_strconcat (xml, "</", node->name, ">", NULL);
+
+ for (n = node->next; n; n = n->next)
+ {
+ char *sibling = rest_xml_node_print (n);
+
+ xml = g_strconcat (xml, sibling, NULL);
+ g_free (sibling);
+ }
+
+ return xml;
+}
+
+/**
+ * rest_xml_node_add_child:
+ * @parent: parent #RestXmlNode, or %NULL for the top-level node
+ * @tag: name of the child node
+ *
+ * Adds a new node to the given parent node; to create the top-level node,
+ * parent should be %NULL.
+ *
+ * Return value: (transfer none): the newly added #RestXmlNode; the node object
+ * is owned by, and valid for the life time of, the #RestXmlCreator.
+ */
+RestXmlNode *
+rest_xml_node_add_child (RestXmlNode *parent, const char *tag)
+{
+ RestXmlNode *node;
+ char *escaped;
+
+ g_return_val_if_fail (tag && *tag, NULL);
+
+ escaped = g_markup_escape_text (tag, -1);
+
+ node = _rest_xml_node_new ();
+ node->name = (char *) g_intern_string (escaped);
+
+ if (parent)
+ {
+ RestXmlNode *tmp_node;
+
+ tmp_node = g_hash_table_lookup (parent->children, node->name);
+
+ if (tmp_node)
+ {
+ rest_xml_node_append_end (tmp_node, node);
+ }
+ else
+ {
+ g_hash_table_insert (parent->children, G(node->name), node);
+ }
+ }
+
+ g_free (escaped);
+
+ return node;
+}
+
+/**
+ * rest_xml_node_add_attr:
+ * @node: #RestXmlNode to add attribute to
+ * @attribute: name of the attribute
+ * @value: value to set attribute to
+ *
+ * Adds attribute to the given node.
+ */
+void
+rest_xml_node_add_attr (RestXmlNode *node,
+ const char *attribute,
+ const char *value)
+{
+ g_return_if_fail (node && attribute && *attribute);
+
+ g_hash_table_insert (node->attrs,
+ g_markup_escape_text (attribute, -1),
+ g_markup_escape_text (value, -1));
+}
+
+/**
+ * rest_xml_node_set_content:
+ * @node: #RestXmlNode to set content
+ * @value: the content
+ *
+ * Sets content for the given node.
+ */
+void
+rest_xml_node_set_content (RestXmlNode *node, const char *value)
+{
+ g_return_if_fail (node && value && *value);
+
+ g_free (node->content);
+ node->content = g_markup_escape_text (value, -1);
+}