[json-glib] builder: Add convenience API for building JSON trees.



commit 08f3073eeb0f7e2e0973abd23ba4ec978ad302d7
Author: Luca Bruno <lethalman88 gmail com>
Date:   Wed Jun 9 21:31:06 2010 +0200

    builder: Add convenience API for building JSON trees.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=621141
    
    Signed-off-by: Emmanuele Bassi <ebassi linux intel com>

 .gitignore                     |    1 +
 json-glib/Makefile.am          |    2 +
 json-glib/json-builder.c       |  682 ++++++++++++++++++++++++++++++++++++++++
 json-glib/json-builder.h       |  100 ++++++
 json-glib/json-glib.h          |    1 +
 json-glib/tests/Makefile.am    |    3 +
 json-glib/tests/builder-test.c |  121 +++++++
 7 files changed, 910 insertions(+), 0 deletions(-)
---
diff --git a/.gitignore b/.gitignore
index b65b18e..248ce34 100644
--- a/.gitignore
+++ b/.gitignore
@@ -39,6 +39,7 @@ json-glib.pc
 /json-glib/stamp-enum-types
 /json-glib/stamp-marshal
 /json-glib/tests/array-test
+/json-glib/tests/builder-test
 /json-glib/tests/generator-test
 /json-glib/tests/object-test
 /json-glib/tests/node-test
diff --git a/json-glib/Makefile.am b/json-glib/Makefile.am
index 717393c..2e61785 100644
--- a/json-glib/Makefile.am
+++ b/json-glib/Makefile.am
@@ -30,6 +30,7 @@ CLEANFILES =
 DISTCLEANFILES = json-version.h
 
 source_h = \
+	$(top_srcdir)/json-glib/json-builder.h		\
 	$(top_srcdir)/json-glib/json-generator.h 	\
 	$(top_srcdir)/json-glib/json-gobject.h 		\
 	$(top_srcdir)/json-glib/json-parser.h 		\
@@ -45,6 +46,7 @@ source_h_private = \
 
 source_c = \
 	$(srcdir)/json-array.c 		\
+	$(srcdir)/json-builder.c	\
 	$(srcdir)/json-debug.c		\
 	$(srcdir)/json-gboxed.c		\
 	$(srcdir)/json-generator.c 	\
diff --git a/json-glib/json-builder.c b/json-glib/json-builder.c
new file mode 100644
index 0000000..ea9bc05
--- /dev/null
+++ b/json-glib/json-builder.c
@@ -0,0 +1,682 @@
+/* json-generator.c - JSON tree builder
+ *
+ * This file is part of JSON-GLib
+ * Copyright (C) 2010  Luca Bruno <lethalman88 gmail com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that 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 library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Author:
+ *   Luca Bruno  <lethalman88 gmail com>
+ */
+
+/**
+ * SECTION:json-builder
+ * @short_description: Generates JSON trees
+ *
+ * #JsonBuilder provides an object for generating a JSON tree.
+ * You can generate only one tree with one #JsonBuilder instance.
+ *
+ * The root of the JSON tree can be either a #JsonObject or a #JsonArray.
+ * Thus the first call must necessarily be either
+ * json_builder_begin_object() or json_builder_begin_array().
+ *
+ * For convenience to language bindings, #JsonBuilder returns itself from
+ * most of functions, making it easy to chain function calls.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "json-types-private.h"
+
+#include "json-builder.h"
+
+#define JSON_BUILDER_GET_PRIVATE(obj) \
+        (G_TYPE_INSTANCE_GET_PRIVATE ((obj), JSON_TYPE_BUILDER, JsonBuilderPrivate))
+
+struct _JsonBuilderPrivate
+{
+  GQueue *stack;
+  JsonNode *root;
+};
+
+typedef enum
+{
+  JSON_BUILDER_MODE_OBJECT,
+  JSON_BUILDER_MODE_ARRAY,
+  JSON_BUILDER_MODE_MEMBER
+} JsonBuilderMode;
+
+typedef struct
+{
+  JsonBuilderMode mode;
+
+  union
+  {
+    JsonObject *object;
+    JsonArray *array;
+  } data;
+  gchar *member_name;
+} JsonBuilderState;
+
+static void
+json_builder_state_free (JsonBuilderState *state)
+{
+  if (G_LIKELY (state))
+    {
+      switch (state->mode)
+        {
+        case JSON_BUILDER_MODE_OBJECT:
+        case JSON_BUILDER_MODE_MEMBER:
+          json_object_unref (state->data.object);
+          g_free (state->member_name);
+          state->data.object = NULL;
+          state->member_name = NULL;
+          break;
+        case JSON_BUILDER_MODE_ARRAY:
+          json_array_unref (state->data.array);
+          state->data.array = NULL;
+          break;
+        default:
+          g_assert_not_reached ();
+        }
+
+      g_slice_free (JsonBuilderState, state);
+    }
+}
+
+G_DEFINE_TYPE (JsonBuilder, json_builder, G_TYPE_OBJECT);
+
+static void
+json_builder_free_all_state (JsonBuilder *builder)
+{
+  JsonBuilderState *state;
+
+  while (!g_queue_is_empty (builder->priv->stack))
+    {
+      state = g_queue_pop_head (builder->priv->stack);
+      json_builder_state_free (state);
+    }
+
+  if (builder->priv->root)
+    {
+      json_node_free (builder->priv->root);
+      builder->priv->root = NULL;
+    }
+}
+
+static void
+json_builder_finalize (GObject *gobject)
+{
+  JsonBuilderPrivate *priv = JSON_BUILDER_GET_PRIVATE (gobject);
+
+  json_builder_free_all_state (JSON_BUILDER (gobject));
+
+  g_queue_free (priv->stack);
+  priv->stack = NULL;
+
+  G_OBJECT_CLASS (json_builder_parent_class)->finalize (gobject);
+}
+
+static void
+json_builder_class_init (JsonBuilderClass *klass)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+  g_type_class_add_private (klass, sizeof (JsonBuilderPrivate));
+
+  gobject_class->finalize = json_builder_finalize;
+}
+
+static void
+json_builder_init (JsonBuilder *builder)
+{
+  JsonBuilderPrivate *priv;
+
+  builder->priv = priv = JSON_BUILDER_GET_PRIVATE (builder);
+
+  priv->stack = g_queue_new ();
+  priv->root = NULL;
+}
+
+static inline JsonBuilderMode
+json_builder_current_mode (JsonBuilder *builder)
+{
+  JsonBuilderState *state = g_queue_peek_head (builder->priv->stack);
+  return state->mode;
+}
+
+static inline gboolean
+json_builder_is_valid_add_mode (JsonBuilder *builder)
+{
+  JsonBuilderMode mode = json_builder_current_mode (builder);
+  return mode == JSON_BUILDER_MODE_MEMBER || mode == JSON_BUILDER_MODE_ARRAY;
+}
+
+/**
+ * json_builder_new:
+ *
+ * Creates a new #JsonBuilder. You can use this object to generate a
+ * JSON tree and obtain the root #JsonNode<!-- -->s.
+ *
+ * Return value: the newly created #JsonBuilder instance
+ */
+JsonBuilder *
+json_builder_new (void)
+{
+  return g_object_new (JSON_TYPE_BUILDER, NULL);
+}
+
+/**
+ * json_builder_get_root:
+ * @builder: a #JsonBuilder
+ *
+ * Returns the root of the current constructed tree, if the build is complete
+ * (ie: all opened objects, object members and arrays are being closed).
+ *
+ * Return value: (transfer full): the #JsonNode, or %NULL if the build is not complete.
+ *   Free the returned value with json_node_free().
+ */
+JsonNode *
+json_builder_get_root (JsonBuilder *builder)
+{
+  JsonNode *root = NULL;
+
+  g_return_val_if_fail (JSON_IS_BUILDER (builder), NULL);
+
+  if (builder->priv->root)
+    root = json_node_copy (builder->priv->root);
+
+  return root;
+}
+
+/**
+ * json_builder_reset:
+ * @builder: a #JsonBuilder
+ *
+ * Resets the state of the @builder back to its initial state.
+ */
+void
+json_builder_reset (JsonBuilder *builder)
+{
+  g_return_if_fail (JSON_IS_BUILDER (builder));
+
+  json_builder_free_all_state (builder);
+}
+
+/**
+ * json_builder_begin_object:
+ * @builder: a #JsonBuilder
+ *
+ * Opens a subobject inside the given @builder. When done adding members to
+ * the subobject, json_builder_end_object() must be called.
+ *
+ * Can be called for first or only if the call is associated to an object member
+ * or an array element.
+ *
+ * Return value: the #JsonBuilder, or %NULL if the call was inconsistent
+ */
+JsonBuilder *
+json_builder_begin_object (JsonBuilder *builder)
+{
+  JsonObject *object;
+  JsonBuilderState *state;
+  JsonBuilderState *cur_state;
+
+  g_return_val_if_fail (JSON_IS_BUILDER (builder), NULL);
+  g_return_val_if_fail (builder->priv->root == NULL, NULL);
+  g_return_val_if_fail (g_queue_is_empty (builder->priv->stack) || json_builder_is_valid_add_mode (builder), NULL);
+
+  object = json_object_new ();
+  cur_state = g_queue_peek_head (builder->priv->stack);
+  if (cur_state)
+    {
+      switch (cur_state->mode)
+        {
+        case JSON_BUILDER_MODE_ARRAY:
+          json_array_add_object_element (cur_state->data.array, json_object_ref (object));
+          break;
+
+        case JSON_BUILDER_MODE_MEMBER:
+          json_object_set_object_member (cur_state->data.object, cur_state->member_name, json_object_ref (object));
+          g_free (cur_state->member_name);
+          cur_state->member_name = NULL;
+          cur_state->mode = JSON_BUILDER_MODE_OBJECT;
+          break;
+
+        default:
+          g_assert_not_reached ();
+        }
+    }
+
+  state = g_slice_new (JsonBuilderState);
+  state->data.object = object;
+  state->mode = JSON_BUILDER_MODE_OBJECT;
+  g_queue_push_head (builder->priv->stack, state);
+
+  return builder;
+}
+
+/**
+ * json_builder_end_object:
+ * @builder: a #JsonBuilder
+ *
+ * Closes the subobject inside the given @builder that was opened by the most
+ * recent call to json_builder_begin_object().
+ *
+ * Cannot be called after json_builder_set_member_name().
+ *
+ * Return value: the #JsonBuilder, or %NULL if the call was inconsistent
+ */
+JsonBuilder *
+json_builder_end_object (JsonBuilder *builder)
+{
+  JsonBuilderState *state;
+
+  g_return_val_if_fail (JSON_IS_BUILDER (builder), NULL);
+  g_return_val_if_fail (!g_queue_is_empty (builder->priv->stack), NULL);
+  g_return_val_if_fail (json_builder_current_mode (builder) == JSON_BUILDER_MODE_OBJECT, NULL);
+
+  state = g_queue_pop_head (builder->priv->stack);
+
+  if (g_queue_is_empty (builder->priv->stack))
+    {
+      builder->priv->root = json_node_new (JSON_NODE_OBJECT);
+      json_node_take_object (builder->priv->root, json_object_ref (state->data.object));
+    }
+
+  json_builder_state_free (state);
+
+  return builder;
+}
+
+/**
+ * json_builder_begin_array:
+ * @builder: a #JsonBuilder
+ *
+ * Opens a subarray inside the given @builder. When done adding members to
+ * the subarray, json_builder_end_array() must be called.
+ *
+ * Can be called for first or only if the call is associated to an object member
+ * or an array element.
+ *
+ * Return value: the #JsonBuilder, or %NULL if the call was inconsistent
+ */
+JsonBuilder *
+json_builder_begin_array (JsonBuilder *builder)
+{
+  JsonArray *array;
+  JsonBuilderState *state;
+  JsonBuilderState *cur_state;
+
+  g_return_val_if_fail (JSON_IS_BUILDER (builder), NULL);
+  g_return_val_if_fail (builder->priv->root == NULL, NULL);
+  g_return_val_if_fail (g_queue_is_empty (builder->priv->stack) || json_builder_is_valid_add_mode (builder), NULL);
+
+  array = json_array_new ();
+  cur_state = g_queue_peek_head (builder->priv->stack);
+  if (cur_state)
+    {
+      switch (cur_state->mode)
+        {
+        case JSON_BUILDER_MODE_ARRAY:
+          json_array_add_array_element (cur_state->data.array, json_array_ref (array));
+          break;
+
+        case JSON_BUILDER_MODE_MEMBER:
+          json_object_set_array_member (cur_state->data.object, cur_state->member_name, json_array_ref (array));
+          g_free (cur_state->member_name);
+          cur_state->member_name = NULL;
+          cur_state->mode = JSON_BUILDER_MODE_OBJECT;
+          break;
+
+        default:
+          g_assert_not_reached ();
+        }
+    }
+
+  state = g_slice_new (JsonBuilderState);
+  state->data.array = array;
+  state->mode = JSON_BUILDER_MODE_ARRAY;
+  g_queue_push_head (builder->priv->stack, state);
+
+  return builder;
+}
+
+/**
+ * json_builder_end_array:
+ * @builder: a #JsonBuilder
+ *
+ * Closes the subarray inside the given @builder that was opened by the most
+ * recent call to json_builder_begin_array().
+ *
+ * Cannot be called after json_builder_set_member_name().
+ *
+ * Return value: the #JsonBuilder, or %NULL if the call was inconsistent
+ */
+JsonBuilder *
+json_builder_end_array (JsonBuilder *builder)
+{
+  JsonBuilderState *state;
+
+  g_return_val_if_fail (JSON_IS_BUILDER (builder), NULL);
+  g_return_val_if_fail (!g_queue_is_empty (builder->priv->stack), NULL);
+  g_return_val_if_fail (json_builder_current_mode (builder) == JSON_BUILDER_MODE_ARRAY, NULL);
+
+  state = g_queue_pop_head (builder->priv->stack);
+
+  if (g_queue_is_empty (builder->priv->stack))
+    {
+      builder->priv->root = json_node_new (JSON_NODE_ARRAY);
+      json_node_take_array (builder->priv->root, json_array_ref (state->data.array));
+    }
+
+  json_builder_state_free (state);
+
+  return builder;
+}
+
+/**
+ * json_builder_set_member_name:
+ * @builder: a #JsonBuilder
+ * @member_name: the name of the member
+ *
+ * Set the name of the next member in an object. The next call must add a value,
+ * open an object or an array.
+ *
+ * Can be called only if the call is associated to an object.
+ *
+ * Return value: the #JsonBuilder, or %NULL if the call was inconsistent
+ */
+JsonBuilder *
+json_builder_set_member_name (JsonBuilder *builder, const gchar *member_name)
+{
+  JsonBuilderState *state;
+
+  g_return_val_if_fail (JSON_IS_BUILDER (builder), NULL);
+  g_return_val_if_fail (member_name != NULL, NULL);
+  g_return_val_if_fail (!g_queue_is_empty (builder->priv->stack), NULL);
+  g_return_val_if_fail (json_builder_current_mode (builder) == JSON_BUILDER_MODE_OBJECT, NULL);
+
+  state = g_queue_peek_head (builder->priv->stack);
+  state->member_name = g_strdup (member_name);
+  state->mode = JSON_BUILDER_MODE_MEMBER;
+
+  return builder;
+}
+
+/**
+ * json_builder_add_value:
+ * @builder: a #JsonBuilder
+ * @node: the value of the member or element
+ *
+ * If called after json_builder_set_member_name(), sets @node as member of the
+ * most recent opened object, otherwise @node is added as element of the most
+ * recent opened array.
+ *
+ * Return value: the #JsonBuilder, or %NULL if the call was inconsistent
+ */
+JsonBuilder *
+json_builder_add_value (JsonBuilder *builder, JsonNode *node)
+{
+  JsonBuilderState *state;
+
+  g_return_val_if_fail (JSON_IS_BUILDER (builder), NULL);
+  g_return_val_if_fail (node != NULL, NULL);
+  g_return_val_if_fail (!g_queue_is_empty (builder->priv->stack), NULL);
+  g_return_val_if_fail (json_builder_is_valid_add_mode (builder), NULL);
+
+  state = g_queue_peek_head (builder->priv->stack);
+  switch (state->mode)
+    {
+    case JSON_BUILDER_MODE_MEMBER:
+      json_object_set_member (state->data.object, state->member_name, node);
+      g_free (state->member_name);
+      state->member_name = NULL;
+      state->mode = JSON_BUILDER_MODE_OBJECT;
+      break;
+
+    case JSON_BUILDER_MODE_ARRAY:
+      json_array_add_element (state->data.array, node);
+      break;
+
+    default:
+      g_assert_not_reached ();
+    }
+
+  return builder;
+}
+
+/**
+ * json_builder_add_int_value:
+ * @builder: a #JsonBuilder
+ * @value: the value of the member or element
+ *
+ * If called after json_builder_set_member_name(), sets @value as member of the
+ * most recent opened object, otherwise @value is added as element of the most
+ * recent opened array.
+ *
+ * See also: json_builder_add_value()
+ *
+ * Return value: the #JsonBuilder, or %NULL if the call was inconsistent
+ */
+JsonBuilder *
+json_builder_add_int_value (JsonBuilder *builder, gint64 value)
+{
+  JsonBuilderState *state;
+
+  g_return_val_if_fail (JSON_IS_BUILDER (builder), NULL);
+  g_return_val_if_fail (!g_queue_is_empty (builder->priv->stack), NULL);
+  g_return_val_if_fail (json_builder_is_valid_add_mode (builder), NULL);
+
+  state = g_queue_peek_head (builder->priv->stack);
+  switch (state->mode)
+    {
+    case JSON_BUILDER_MODE_MEMBER:
+      json_object_set_int_member (state->data.object, state->member_name, value);
+      g_free (state->member_name);
+      state->member_name = NULL;
+      state->mode = JSON_BUILDER_MODE_OBJECT;
+      break;
+
+    case JSON_BUILDER_MODE_ARRAY:
+      json_array_add_int_element (state->data.array, value);
+      break;
+
+    default:
+      g_assert_not_reached ();
+    }
+
+  return builder;
+}
+
+/**
+ * json_builder_add_double_value:
+ * @builder: a #JsonBuilder
+ * @value: the value of the member or element
+ *
+ * If called after json_builder_set_member_name(), sets @value as member of the
+ * most recent opened object, otherwise @value is added as element of the most
+ * recent opened array.
+ *
+ * See also: json_builder_add_value()
+ *
+ * Return value: the #JsonBuilder, or %NULL if the call was inconsistent
+ */
+JsonBuilder *
+json_builder_add_double_value (JsonBuilder *builder, gdouble value)
+{
+  JsonBuilderState *state;
+
+  g_return_val_if_fail (JSON_IS_BUILDER (builder), NULL);
+  g_return_val_if_fail (!g_queue_is_empty (builder->priv->stack), NULL);
+  g_return_val_if_fail (json_builder_is_valid_add_mode (builder), NULL);
+
+  state = g_queue_peek_head (builder->priv->stack);
+
+  switch (state->mode)
+    {
+    case JSON_BUILDER_MODE_MEMBER:
+      json_object_set_double_member (state->data.object, state->member_name, value);
+      g_free (state->member_name);
+      state->member_name = NULL;
+      state->mode = JSON_BUILDER_MODE_OBJECT;
+      break;
+
+    case JSON_BUILDER_MODE_ARRAY:
+      json_array_add_double_element (state->data.array, value);
+      break;
+
+    default:
+      g_assert_not_reached ();
+    }
+
+  return builder;
+}
+
+/**
+ * json_builder_add_boolean_value:
+ * @builder: a #JsonBuilder
+ * @value: the value of the member or element
+ *
+ * If called after json_builder_set_member_name(), sets @value as member of the
+ * most recent opened object, otherwise @value is added as element of the most
+ * recent opened array.
+ *
+ * See also: json_builder_add_value()
+ *
+ * Return value: the #JsonBuilder, or %NULL if the call was inconsistent
+ */
+JsonBuilder *
+json_builder_add_boolean_value (JsonBuilder *builder, gboolean value)
+{
+  JsonBuilderState *state;
+
+  g_return_val_if_fail (JSON_IS_BUILDER (builder), NULL);
+  g_return_val_if_fail (!g_queue_is_empty (builder->priv->stack), NULL);
+  g_return_val_if_fail (json_builder_is_valid_add_mode (builder), NULL);
+
+  state = g_queue_peek_head (builder->priv->stack);
+
+  switch (state->mode)
+    {
+    case JSON_BUILDER_MODE_MEMBER:
+      json_object_set_boolean_member (state->data.object, state->member_name, value);
+      g_free (state->member_name);
+      state->member_name = NULL;
+      state->mode = JSON_BUILDER_MODE_OBJECT;
+      break;
+
+    case JSON_BUILDER_MODE_ARRAY:
+      json_array_add_boolean_element (state->data.array, value);
+      break;
+
+    default:
+      g_assert_not_reached ();
+    }
+
+  return builder;
+}
+
+/**
+ * json_builder_add_string_value:
+ * @builder: a #JsonBuilder
+ * @value: the value of the member or element
+ *
+ * If called after json_builder_set_member_name(), sets @value as member of the
+ * most recent opened object, otherwise @value is added as element of the most
+ * recent opened array.
+ *
+ * See also: json_builder_add_value()
+ *
+ * Return value: the #JsonBuilder, or %NULL if the call was inconsistent
+ */
+JsonBuilder *
+json_builder_add_string_value (JsonBuilder *builder, const gchar *value)
+{
+  JsonBuilderState *state;
+
+  g_return_val_if_fail (JSON_IS_BUILDER (builder), NULL);
+  g_return_val_if_fail (!g_queue_is_empty (builder->priv->stack), NULL);
+  g_return_val_if_fail (json_builder_is_valid_add_mode (builder), NULL);
+
+  state = g_queue_peek_head (builder->priv->stack);
+
+  switch (state->mode)
+    {
+    case JSON_BUILDER_MODE_MEMBER:
+      json_object_set_string_member (state->data.object, state->member_name, value);
+      g_free (state->member_name);
+      state->member_name = NULL;
+      state->mode = JSON_BUILDER_MODE_OBJECT;
+      break;
+
+    case JSON_BUILDER_MODE_ARRAY:
+      json_array_add_string_element (state->data.array, value);
+      break;
+
+    default:
+      g_assert_not_reached ();
+    }
+
+  return builder;
+}
+
+/**
+ * json_builder_add_null_value:
+ * @builder: a #JsonBuilder
+ * @value: the value of the member or element
+ *
+ * If called after json_builder_set_member_name(), sets null as member of the
+ * most recent opened object, otherwise null is added as element of the most
+ * recent opened array.
+ *
+ * See also: json_builder_add_value()
+ *
+ * Return value: the #JsonBuilder, or %NULL if the call was inconsistent
+ */
+JsonBuilder *
+json_builder_add_null_value (JsonBuilder *builder)
+{
+  JsonBuilderState *state;
+
+  g_return_val_if_fail (JSON_IS_BUILDER (builder), NULL);
+  g_return_val_if_fail (!g_queue_is_empty (builder->priv->stack), NULL);
+  g_return_val_if_fail (json_builder_is_valid_add_mode (builder), NULL);
+
+  state = g_queue_peek_head (builder->priv->stack);
+
+  switch (state->mode)
+    {
+    case JSON_BUILDER_MODE_MEMBER:
+      json_object_set_null_member (state->data.object, state->member_name);
+      g_free (state->member_name);
+      state->member_name = NULL;
+      state->mode = JSON_BUILDER_MODE_OBJECT;
+      break;
+
+    case JSON_BUILDER_MODE_ARRAY:
+      json_array_add_null_element (state->data.array);
+      break;
+
+    default:
+      g_assert_not_reached ();
+    }
+
+  return builder;
+}
diff --git a/json-glib/json-builder.h b/json-glib/json-builder.h
new file mode 100644
index 0000000..33b16ab
--- /dev/null
+++ b/json-glib/json-builder.h
@@ -0,0 +1,100 @@
+/* json-builder.h: JSON tree builder
+ *
+ * This file is part of JSON-GLib
+ * Copyright (C) 2010  Luca Bruno <lethalman88 gmail com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that 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 library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Author:
+ *   Luca Bruno  <lethalman88 gmail com>
+ */
+
+#if !defined(__JSON_GLIB_INSIDE__) && !defined(JSON_COMPILATION)
+#error "Only <json-glib/json-glib.h> can be included directly."
+#endif
+
+#ifndef __JSON_BUILDER_H__
+#define __JSON_BUILDER_H__
+
+#include <json-glib/json-types.h>
+
+G_BEGIN_DECLS
+
+#define JSON_TYPE_BUILDER             (json_builder_get_type ())
+#define JSON_BUILDER(obj)             (G_TYPE_CHECK_INSTANCE_CAST ((obj), JSON_TYPE_BUILDER, JsonBuilder))
+#define JSON_IS_BUILDER(obj)          (G_TYPE_CHECK_INSTANCE_TYPE ((obj), JSON_TYPE_BUILDER))
+#define JSON_BUILDER_CLASS(klass)     (G_TYPE_CHECK_CLASS_CAST ((klass), JSON_TYPE_BUILDER, JsonBuilderClass))
+#define JSON_IS_BUILDER_CLASS(klass)  (G_TYPE_CHECK_CLASS_TYPE ((klass), JSON_TYPE_BUILDER))
+#define JSON_BUILDER_GET_CLASS(obj)   (G_TYPE_INSTANCE_GET_CLASS ((obj), JSON_TYPE_BUILDER, JsonBuilderClass))
+
+typedef struct _JsonBuilder           JsonBuilder;
+typedef struct _JsonBuilderPrivate    JsonBuilderPrivate;
+typedef struct _JsonBuilderClass      JsonBuilderClass;
+
+/**
+ * JsonBuilder:
+ *
+ * JSON tree builder.
+ */
+struct _JsonBuilder
+{
+  /*< private >*/
+  GObject parent_instance;
+
+  JsonBuilderPrivate *priv;
+};
+
+/**
+ * JsonBuilderClass:
+ *
+ * #JsonBuilder class
+ */
+struct _JsonBuilderClass
+{
+  /*< private >*/
+  GObjectClass parent_class;
+
+  /* padding, for future expansion */
+  void (* _json_reserved1) (void);
+  void (* _json_reserved2) (void);
+};
+
+GType json_builder_get_type (void) G_GNUC_CONST;
+
+JsonBuilder   *json_builder_new                (void);
+JsonNode      *json_builder_get_root           (JsonBuilder  *builder);
+void           json_builder_reset              (JsonBuilder  *builder);
+
+JsonBuilder   *json_builder_begin_array        (JsonBuilder  *builder);
+JsonBuilder   *json_builder_end_array          (JsonBuilder  *builder);
+JsonBuilder   *json_builder_begin_object       (JsonBuilder  *builder);
+JsonBuilder   *json_builder_end_object         (JsonBuilder  *builder);
+
+JsonBuilder   *json_builder_set_member_name    (JsonBuilder  *builder,
+                                                const gchar  *member_name);
+JsonBuilder   *json_builder_add_value          (JsonBuilder  *builder,
+                                                JsonNode     *node);
+JsonBuilder   *json_builder_add_int_value      (JsonBuilder  *builder,
+                                                gint64        value);
+JsonBuilder   *json_builder_add_double_value   (JsonBuilder  *builder,
+                                                gdouble       value);
+JsonBuilder   *json_builder_add_boolean_value  (JsonBuilder  *builder,
+                                                gboolean      value);
+JsonBuilder   *json_builder_add_string_value   (JsonBuilder  *builder,
+                                                const gchar  *value);
+JsonBuilder   *json_builder_add_null_value     (JsonBuilder  *builder);
+
+G_END_DECLS
+
+#endif /* __JSON_BUILDER_H__ */
diff --git a/json-glib/json-glib.h b/json-glib/json-glib.h
index 794a8be..9c3d6f0 100644
--- a/json-glib/json-glib.h
+++ b/json-glib/json-glib.h
@@ -27,6 +27,7 @@
 #define __JSON_GLIB_INSIDE__
 
 #include <json-glib/json-types.h>
+#include <json-glib/json-builder.h>
 #include <json-glib/json-generator.h>
 #include <json-glib/json-parser.h>
 #include <json-glib/json-version.h>
diff --git a/json-glib/tests/Makefile.am b/json-glib/tests/Makefile.am
index 3fd3ce3..a027a70 100644
--- a/json-glib/tests/Makefile.am
+++ b/json-glib/tests/Makefile.am
@@ -34,3 +34,6 @@ TEST_PROGS             += generator-test
 generator_test_SOURCES  = generator-test.c
 generator_test_LDADD    = $(progs_ldadd)
 
+TEST_PROGS           += builder-test
+builder_test_SOURCES  = builder-test.c
+builder_test_LDADD    = $(progs_ldadd)
diff --git a/json-glib/tests/builder-test.c b/json-glib/tests/builder-test.c
new file mode 100644
index 0000000..eba54c2
--- /dev/null
+++ b/json-glib/tests/builder-test.c
@@ -0,0 +1,121 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <glib-object.h>
+
+#include <json-glib/json-glib.h>
+
+static const gchar *complex_object = "{ \"depth1\" : [ 1, { \"depth2\" : [ 2.3, [ null ], \"after array\" ], \"value2\" : true } ], \"object1\" : { } }\") == 0)";
+
+static const gchar *reset_object = "{ \"test\" : \"reset\" }";
+static const gchar *reset_array = "[ \"reset\" ]";
+
+static void
+test_builder_complex (void)
+{
+  JsonBuilder *builder = json_builder_new ();
+  JsonNode *node;
+  JsonGenerator *generator;
+  gsize length;
+  gchar *data;
+
+  json_builder_begin_object (builder);
+
+  json_builder_set_member_name (builder, "depth1");
+  json_builder_begin_array (builder);
+  json_builder_add_int_value (builder, 1);
+
+  json_builder_begin_object (builder);
+
+  json_builder_set_member_name (builder, "depth2");
+  json_builder_begin_array (builder);
+  json_builder_add_double_value (builder, 2.3);
+
+  json_builder_begin_array (builder);
+  json_builder_add_null_value (builder);
+  json_builder_end_array (builder);
+
+  json_builder_add_string_value (builder, "after array");
+  json_builder_end_array (builder); /* depth2 */
+
+  json_builder_set_member_name (builder, "value2");
+  json_builder_add_boolean_value (builder, TRUE);
+  json_builder_end_object (builder);
+
+  json_builder_end_array (builder); /* depth1 */
+
+  json_builder_set_member_name (builder, "object1");
+  json_builder_begin_object (builder);
+  json_builder_end_object (builder);
+
+  json_builder_end_object (builder);
+
+  node = json_builder_get_root (builder);
+  g_object_unref (builder);
+
+  generator = json_generator_new ();
+  json_generator_set_root (generator, node);
+  data = json_generator_to_data (generator, &length);
+  if (g_test_verbose ())
+    {
+      g_print ("Builder complex: %*s", (int)length, data);
+    }
+  g_assert (strncmp (data, complex_object, length) == 0);
+
+  g_free (data);
+  json_node_free (node);
+  g_object_unref (generator);
+}
+
+static void
+test_builder_reset (oid)
+{
+  JsonBuilder *builder = json_builder_new ();
+  JsonGenerator *generator = json_generator_new ();
+  JsonNode *node;
+  gsize length;
+  gchar *data;
+
+  json_builder_begin_object (builder);
+  json_builder_set_member_name (builder, "test");
+  json_builder_add_string_value (builder, "reset");
+  json_builder_end_object (builder);
+
+  node = json_builder_get_root (builder);
+  json_generator_set_root (generator, node);
+  data = json_generator_to_data (generator, &length);
+  g_assert (strncmp (data, reset_object, length) == 0);
+
+  g_free (data);
+  json_node_free (node);
+
+  json_builder_reset (builder);
+
+  json_builder_begin_array (builder);
+  json_builder_add_string_value (builder, "reset");
+  json_builder_end_array (builder);
+
+  node = json_builder_get_root (builder);
+  json_generator_set_root (generator, node);
+  data = json_generator_to_data (generator, &length);
+  g_assert (strncmp (data, reset_array, length) == 0);
+
+  g_free (data);
+  json_node_free (node);
+  g_object_unref (builder);
+  g_object_unref (generator);
+}
+
+int
+main (int   argc,
+      char *argv[])
+{
+  g_type_init ();
+  g_test_init (&argc, &argv, NULL);
+
+  g_test_add_func ("/builder/complex", test_builder_complex);
+  g_test_add_func ("/builder/reset", test_builder_reset);
+
+  return g_test_run ();
+}



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]