[json-glib] Add JsonReader



commit 10e5a1d38113b7b6e7c71da76ff11219baf1022d
Author: Emmanuele Bassi <ebassi linux intel com>
Date:   Thu Aug 12 15:29:41 2010 +0100

    Add JsonReader
    
    JsonReader is a simple, cursor-based API for parsing a JSON DOM. It is
    similar, in spirit, to the XmlReader API provided by various platforms
    and XML parsing libraries.

 .gitignore                    |    1 +
 json-glib/Makefile.am         |    2 +
 json-glib/json-glib.h         |    3 +
 json-glib/json-reader.c       |  779 +++++++++++++++++++++++++++++++++++++++++
 json-glib/json-reader.h       |  142 ++++++++
 json-glib/tests/Makefile.am   |   10 +-
 json-glib/tests/reader-test.c |  101 ++++++
 7 files changed, 1035 insertions(+), 3 deletions(-)
---
diff --git a/.gitignore b/.gitignore
index 1119e48..b7fed56 100644
--- a/.gitignore
+++ b/.gitignore
@@ -41,6 +41,7 @@ json-glib.pc
 /json-glib/tests/object-test
 /json-glib/tests/node-test
 /json-glib/tests/parser-test
+/json-glib/tests/reader-test
 libtool
 ltmain.sh
 missing
diff --git a/json-glib/Makefile.am b/json-glib/Makefile.am
index e4d24e1..6340cbf 100644
--- a/json-glib/Makefile.am
+++ b/json-glib/Makefile.am
@@ -34,6 +34,7 @@ source_h = \
 	$(top_srcdir)/json-glib/json-generator.h 	\
 	$(top_srcdir)/json-glib/json-gobject.h 		\
 	$(top_srcdir)/json-glib/json-parser.h 		\
+	$(top_srcdir)/json-glib/json-reader.h		\
 	$(top_srcdir)/json-glib/json-types.h 		\
 	$(NULL)
 
@@ -54,6 +55,7 @@ source_c = \
 	$(srcdir)/json-node.c 		\
 	$(srcdir)/json-object.c 	\
 	$(srcdir)/json-parser.c 	\
+	$(srcdir)/json-reader.c		\
 	$(srcdir)/json-scanner.c 	\
 	$(srcdir)/json-serializable.c	\
 	$(NULL)
diff --git a/json-glib/json-glib.h b/json-glib/json-glib.h
index 9c3d6f0..7a4d783 100644
--- a/json-glib/json-glib.h
+++ b/json-glib/json-glib.h
@@ -27,10 +27,13 @@
 #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-reader.h>
 #include <json-glib/json-version.h>
+
 #include <json-glib/json-enum-types.h>
 
 #include <json-glib/json-gobject.h>
diff --git a/json-glib/json-reader.c b/json-glib/json-reader.c
new file mode 100644
index 0000000..adc859e
--- /dev/null
+++ b/json-glib/json-reader.c
@@ -0,0 +1,779 @@
+/* json-reader.h - JSON cursor parser
+ * 
+ * This file is part of JSON-GLib
+ * Copyright (C) 2010  Intel Corp.
+ *
+ * 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:
+ *   Emmanuele Bassi  <ebassi linux intel com>
+ */
+
+/**
+ * SECTION:json-reader
+ * @Title: JsonReader
+ * @short_description: A cursor-based parser
+ *
+ * #JsonReader provides a simple, cursor-based API for parsing a JSON DOM. It
+ * is similar, in spirit, to the XML Reader API.
+ *
+ * In case of error, #JsonReader will be set in an error state; all subsequent
+ * calls will simply be ignored until a function that resets the error state is
+ * called, e.g.:
+ *
+ * |[
+ * /&ast; ask for the 7th element; if the element does not exist, the
+ *  &ast; reader will be put in an error state
+ *  &ast;/
+ * json_reader_read_element (reader, 6);
+ *
+ * /&ast; in case of error, this will return NULL, otherwise it will
+ *  &ast; return the value of the element
+ *  &ast;/
+ * str = json_reader_get_value_string (value);
+ *
+ * /&ast; this function resets the error state if any was set &ast;/
+ * json_reader_end_element (reader);
+ * ]|
+ *
+ * If you want to detect the error state as soon as possible, you can use
+ * json_reader_get_error():
+ *
+ * |[
+ * /&ast; like the example above, but in this case we print out the
+ *  &ast; error immediately
+ *  &ast;/
+ * if (!json_reader_read_element (reader, 6))
+ *   {
+ *     const GError *error = json_reader_get_error (error);
+ *     g_print ("Unable to read the element: %s", error->message);
+ *   }
+ * ]|
+ *
+ * #JsonReader is available since JSON-GLib 0.12.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <string.h>
+
+#include "json-reader.h"
+
+#include "json-types-private.h"
+
+#include "json-debug.h"
+#include "json-parser.h"
+
+#define json_reader_return_if_error_set(r)      G_STMT_START {  \
+        if (((JsonReader *) (r))->priv->error != NULL)          \
+          return;                               } G_STMT_END
+
+#define json_reader_return_val_if_error_set(r,v) G_STMT_START {  \
+        if (((JsonReader *) (r))->priv->error != NULL)           \
+          return (v);                           } G_STMT_END
+
+struct _JsonReaderPrivate
+{
+  JsonParser *parser;
+
+  JsonNode *root;
+
+  JsonNode *current_node;
+  JsonNode *previous_node;
+
+  GError *error;
+};
+
+G_DEFINE_TYPE (JsonReader, json_reader, G_TYPE_OBJECT);
+
+static void
+json_reader_dispose (GObject *gobject)
+{
+  JsonReaderPrivate *priv = JSON_READER (gobject)->priv;
+
+  if (priv->parser != NULL)
+    {
+      g_object_unref (priv->parser);
+
+      priv->parser = NULL;
+      priv->root = NULL;
+      priv->current_node = NULL;
+      priv->previous_node = NULL;
+    }
+
+  if (priv->error != NULL)
+    g_clear_error (&priv->error);
+
+  G_OBJECT_CLASS (json_reader_parent_class)->dispose (gobject);
+}
+
+static void
+json_reader_class_init (JsonReaderClass *klass)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+  g_type_class_add_private (klass, sizeof (JsonReaderPrivate));
+
+  gobject_class->dispose = json_reader_dispose;
+}
+
+static void
+json_reader_init (JsonReader *self)
+{
+  self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, JSON_TYPE_READER,
+                                            JsonReaderPrivate);
+
+  self->priv->parser = json_parser_new ();
+}
+
+GQuark
+json_reader_error_quark (void)
+{
+  return g_quark_from_static_string ("json-reader-error");
+}
+
+/**
+ * json_reader_new:
+ *
+ * Creates a new #JsonReader instance
+ *
+ * Return value: the newly created #JsonReader. Use g_object_unref() to
+ *   release the allocated resources when done
+ *
+ * Since: 0.12
+ */
+JsonReader *
+json_reader_new (void)
+{
+  return g_object_new (JSON_TYPE_READER, NULL);
+}
+
+/**
+ * json_reader_load_from_data:
+ * @reader: a #JsonReader
+ * @data: the data to be parsed
+ * @length: the length of @data, or -1
+ * @error: return location for a #GError, or %NULL
+ *
+ * Loads a JSON string and parses it.
+ *
+ * If @reader already contained a JSON DOM, it will be reset.
+ *
+ * Return value: %TRUE if the data was successfully parsed, and %FALSE
+ *   otherwise. In case of failure, the #GError will be set accordingly
+ *
+ * Since: 0.12
+ */
+gboolean
+json_reader_load_from_data (JsonReader   *reader,
+                            const gchar  *data,
+                            gssize        length,
+                            GError      **error)
+{
+  JsonReaderPrivate *priv;
+  GError *internal_error;
+  gboolean retval;
+
+  g_return_val_if_fail (JSON_IS_READER (reader), FALSE);
+
+  priv = reader->priv;
+
+  if (priv->root != NULL)
+    {
+      priv->root = NULL;
+
+      priv->current_node = NULL;
+      priv->previous_node = NULL;
+    }
+
+  if (priv->error != NULL)
+    g_clear_error (&priv->error);
+
+  internal_error = NULL;
+  retval = json_parser_load_from_data (priv->parser, data, length, &internal_error);
+  if (retval)
+    {
+      priv->root = json_parser_get_root (priv->parser);
+
+      priv->current_node = priv->root;
+      priv->previous_node = NULL;
+
+      priv->error = NULL;
+    }
+  else
+    {
+      priv->error = g_error_copy (internal_error);
+      g_propagate_error (error, internal_error);
+    }
+
+  return retval;
+}
+
+/*
+ * json_reader_unset_error:
+ * @reader: a #JsonReader
+ *
+ * Unsets the error state of @reader, if set
+ */
+static inline void
+json_reader_unset_error (JsonReader *reader)
+{
+  if (reader->priv->error != NULL)
+    g_clear_error (&(reader->priv->error));
+}
+
+/*
+ * json_reader_ser_error:
+ * @reader: a #JsonReader
+ * @error_code: the #JsonReaderError code for the error
+ * @error_fmt: format string
+ * @Varargs: list of arguments for the @error_fmt string
+ *
+ * Sets the error state of @reader using the given error code
+ * and string
+ *
+ * Return value: %FALSE, to be used to return immediately from
+ *   the caller function
+ */
+static gboolean
+json_reader_set_error (JsonReader      *reader,
+                       JsonReaderError  error_code,
+                       const gchar     *error_fmt,
+                       ...)
+{
+  JsonReaderPrivate *priv = reader->priv;
+  va_list args;
+  gchar *error_msg;
+
+  if (priv->error != NULL)
+    g_clear_error (&priv->error);
+
+  va_start (args, error_fmt);
+  error_msg = g_strdup_vprintf (error_fmt, args);
+  va_end (args);
+
+  g_set_error_literal (&priv->error, JSON_READER_ERROR,
+                       error_code,
+                       error_msg);
+
+  g_free (error_msg);
+
+  return FALSE;
+}
+
+/**
+ * json_reader_get_error:
+ * @reader: a #JsonReader
+ *
+ * Retrieves the #GError currently set on @reader, if the #JsonReader
+ * is in error state
+ *
+ * Return value: (transfer none): the pointer to the error, or %NULL
+ *
+ * Since: 0.12
+ */
+G_CONST_RETURN GError *
+json_reader_get_error (JsonReader *reader)
+{
+  g_return_val_if_fail (JSON_IS_READER (reader), FALSE);
+
+  return reader->priv->error;
+}
+
+/**
+ * json_reader_is_array:
+ * @reader: a #JsonReader
+ *
+ * Checks whether the @reader is currently on an array
+ *
+ * Return value: %TRUE if the #JsonReader is on an array, and %FALSE
+ *   otherwise
+ *
+ * Since: 0.12
+ */
+gboolean
+json_reader_is_array (JsonReader *reader)
+{
+  g_return_val_if_fail (JSON_IS_READER (reader), FALSE);
+  json_reader_return_val_if_error_set (reader, FALSE);
+
+  if (reader->priv->current_node == NULL)
+    return FALSE;
+
+  return JSON_NODE_HOLDS_ARRAY (reader->priv->current_node);
+}
+
+/**
+ * json_reader_is_object:
+ * @reader: a #JsonReader
+ *
+ * Checks whether the @reader is currently on an object
+ *
+ * Return value: %TRUE if the #JsonReader is on an object, and %FALSE
+ *   otherwise
+ *
+ * Since: 0.12
+ */
+gboolean
+json_reader_is_object (JsonReader *reader)
+{
+  g_return_val_if_fail (JSON_IS_READER (reader), FALSE);
+  json_reader_return_val_if_error_set (reader, FALSE);
+
+  if (reader->priv->current_node == NULL)
+    return FALSE;
+
+  return JSON_NODE_HOLDS_OBJECT (reader->priv->current_node);
+}
+
+/**
+ * json_reader_is_value:
+ * @reader: a #JsonReader
+ *
+ * Checks whether the @reader is currently on a value
+ *
+ * Return value: %TRUE if the #JsonReader is on a value, and %FALSE
+ *   otherwise
+ *
+ * Since: 0.12
+ */
+gboolean
+json_reader_is_value (JsonReader *reader)
+{
+  g_return_val_if_fail (JSON_IS_READER (reader), FALSE);
+  json_reader_return_val_if_error_set (reader, FALSE);
+
+  if (reader->priv->current_node == NULL)
+    return FALSE;
+
+  return JSON_NODE_HOLDS_VALUE (reader->priv->current_node);
+}
+
+/**
+ * json_reader_read_element:
+ * @reader: a #JsonReader
+ * @index_: the index of the element
+ *
+ * Advances the cursor of @reader to the element @index_ of array at the
+ * current position.
+ *
+ * You can use the json_reader_get_value* family of functions to retrieve
+ * the value of the element; for instance:
+ *
+ * |[
+ * json_reader_read_element (reader, 0);
+ * int_value = json_reader_get_value_int (reader);
+ * ]|
+ *
+ * After reading the value, json_reader_end_element() should be called to
+ * reposition the cursor inside the #JsonReader, e.g.:
+ *
+ * |[
+ * json_reader_read_element (reader, 1);
+ * str_value = json_reader_get_value_string (reader);
+ * json_reader_end_element (reader);
+ *
+ * json_reader_read_element (reader, 2);
+ * str_value = json_reader_get_value_string (reader);
+ * json_reader_end_element (reader);
+ * ]|
+ *
+ * If @reader is not currently on an array, or if the @index_ is bigger than
+ * the size of the array, the #JsonReader will be put in an error state until
+ * json_reader_end_element() is called.
+ *
+ * Return value: %TRUE on success, and %FALSE otherwise
+ *
+ * Since: 0.12
+ */
+gboolean
+json_reader_read_element (JsonReader *reader,
+                          guint       index_)
+{
+  JsonReaderPrivate *priv;
+  JsonArray *array;
+
+  g_return_val_if_fail (JSON_READER (reader), FALSE);
+  json_reader_return_val_if_error_set (reader, FALSE);
+
+  priv = reader->priv;
+
+  if (priv->current_node == NULL)
+    priv->current_node = priv->root;
+
+  if (!JSON_NODE_HOLDS_ARRAY (priv->current_node))
+    return json_reader_set_error (reader, JSON_READER_ERROR_NO_ARRAY,
+                                  "The current node is of type '%s', but "
+                                  "an array was expected.",
+                                  json_node_type_name (priv->current_node));
+
+  array = json_node_get_array (priv->current_node);
+  if (index_ >= json_array_get_length (array))
+    return json_reader_set_error (reader, JSON_READER_ERROR_INVALID_INDEX,
+                                  "The index '%d' is greater than the size "
+                                  "of the array at the current position.",
+                                  index_);
+
+  priv->previous_node = priv->current_node;
+  priv->current_node = json_array_get_element (array, index_);
+
+  return TRUE;
+}
+
+/**
+ * json_reader_end_element:
+ * @reader: a #JsonReader
+ *
+ * Moves the cursor back to the previous node after being positioned
+ * inside an array
+ *
+ * This function resets the error state of @reader, if any was set
+ *
+ * Since: 0.12
+ */
+void
+json_reader_end_element (JsonReader *reader)
+{
+  JsonReaderPrivate *priv;
+  JsonNode *tmp;
+
+  g_return_if_fail (JSON_IS_READER (reader));
+
+  json_reader_unset_error (reader);
+
+  priv = reader->priv;
+
+  if (priv->previous_node != NULL)
+    tmp = json_node_get_parent (priv->previous_node);
+  else
+    tmp = NULL;
+
+  priv->current_node = priv->previous_node;
+  priv->previous_node = tmp;
+}
+
+/**
+ * json_reader_count_elements:
+ * @reader: a #JsonReader
+ *
+ * Counts the elements of the current position, if @reader is
+ * positioned on an array
+ *
+ * Return value: the number of elements, or -1. In case of failure
+ *   the #JsonReader is set in an error state
+ *
+ * Since: 0.12
+ */
+gint
+json_reader_count_elements (JsonReader *reader)
+{
+  JsonReaderPrivate *priv;
+
+  g_return_val_if_fail (JSON_IS_READER (reader), -1);
+
+  priv = reader->priv;
+
+  if (priv->current_node == NULL)
+    return -1;
+
+  if (!JSON_NODE_HOLDS_ARRAY (priv->current_node))
+    return -1;
+
+  return json_array_get_length (json_node_get_array (priv->current_node));
+}
+
+/**
+ * json_reader_read_member:
+ * @reader: a #JsonReader
+ * @member_name: the name of the member to read
+ *
+ * Advances the cursor of @reader to the @member_name of the object at the
+ * current position.
+ *
+ * You can use the json_reader_get_value* family of functions to retrieve
+ * the value of the member; for instance:
+ *
+ * |[
+ * json_reader_read_member (reader, "width");
+ * width = json_reader_get_value_int (reader);
+ * ]|
+ *
+ * After reading the value, json_reader_end_member() should be called to
+ * reposition the cursor inside the #JsonReader, e.g.:
+ *
+ * |[
+ * json_reader_read_member (reader, "author");
+ * author = json_reader_get_value_string (reader);
+ * json_reader_end_element (reader);
+ *
+ * json_reader_read_element (reader, "title");
+ * title = json_reader_get_value_string (reader);
+ * json_reader_end_element (reader);
+ * ]|
+ *
+ * If @reader is not currently on an object, or if the @member_name is not
+ * defined in the object, the #JsonReader will be put in an error state until
+ * json_reader_end_member() is called.
+ *
+ * Return value: %TRUE on success, and %FALSE otherwise
+ *
+ * Since: 0.12
+ */
+gboolean
+json_reader_read_member (JsonReader  *reader,
+                         const gchar *member_name)
+{
+  JsonReaderPrivate *priv;
+  JsonObject *object;
+
+  g_return_val_if_fail (JSON_READER (reader), FALSE);
+  g_return_val_if_fail (member_name != NULL, FALSE);
+  json_reader_return_val_if_error_set (reader, FALSE);
+
+  priv = reader->priv;
+
+  if (priv->current_node == NULL)
+    priv->current_node = priv->root;
+
+  if (!JSON_NODE_HOLDS_OBJECT (priv->current_node))
+    return json_reader_set_error (reader, JSON_READER_ERROR_NO_OBJECT,
+                                  "The current node is of type '%s', but "
+                                  "an object was expected.",
+                                  json_node_type_name (priv->current_node));
+
+  object = json_node_get_object (priv->current_node);
+  if (!json_object_has_member (object, member_name))
+    return json_reader_set_error (reader, JSON_READER_ERROR_INVALID_MEMBER,
+                                  "The member '%s' is not defined in the "
+                                  "object at the current position.",
+                                  member_name);
+
+  priv->previous_node = priv->current_node;
+  priv->current_node = json_object_get_member (object, member_name);
+
+  return TRUE;
+}
+
+/**
+ * json_reader_end_member:
+ * @reader: a #JsonReader
+ *
+ * Moves the cursor back to the previous node after being positioned
+ * inside an object
+ *
+ * This function resets the error state of @reader, if any was set
+ *
+ * Since: 0.12
+ */
+void
+json_reader_end_member (JsonReader *reader)
+{
+  JsonReaderPrivate *priv;
+  JsonNode *tmp;
+
+  g_return_if_fail (JSON_IS_READER (reader));
+
+  json_reader_unset_error (reader);
+
+  priv = reader->priv;
+
+  if (priv->previous_node != NULL)
+    tmp = json_node_get_parent (priv->previous_node);
+  else
+    tmp = NULL;
+
+  priv->current_node = priv->previous_node;
+  priv->previous_node = tmp;
+}
+
+/**
+ * json_reader_count_members:
+ * @reader: a #JsonReader
+ *
+ * Counts the members of the current position, if @reader is
+ * positioned on an object
+ *
+ * Return value: the number of members, or -1. In case of failure
+ *   the #JsonReader is set in an error state
+ *
+ * Since: 0.12
+ */
+gint
+json_reader_count_members (JsonReader *reader)
+{
+  JsonReaderPrivate *priv;
+
+  g_return_val_if_fail (JSON_IS_READER (reader), -1);
+
+  priv = reader->priv;
+
+  if (priv->current_node == NULL)
+    return -1;
+
+  if (!JSON_NODE_HOLDS_OBJECT (priv->current_node))
+    return -1;
+
+  return json_object_get_size (json_node_get_object (priv->current_node));
+}
+
+/**
+ * json_reader_get_value:
+ * @reader: a #JsonReader
+ *
+ * Retrieves the #JsonNode of the current position of @reader
+ *
+ * Return value: (transfer none): a #JsonNode, or %NULL. The returned node
+ *   is owned by the #JsonReader and it should not be modified or freed
+ *   directly
+ *
+ * Since: 0.12
+ */
+JsonNode *
+json_reader_get_value (JsonReader *reader)
+{
+  g_return_val_if_fail (JSON_IS_READER (reader), NULL);
+  json_reader_return_val_if_error_set (reader, NULL);
+
+  if (reader->priv->current_node == NULL)
+    return NULL;
+
+  if (!JSON_NODE_HOLDS_VALUE (reader->priv->current_node))
+    return NULL;
+
+  return reader->priv->current_node;
+}
+
+/**
+ * json_reader_get_value_int:
+ * @reader: a #JsonReader
+ *
+ * Retrieves the integer value of the current position of @reader
+ *
+ * Return value: the integer value
+ *
+ * Since: 0.12
+ */
+gint64
+json_reader_get_value_int (JsonReader *reader)
+{
+  g_return_val_if_fail (JSON_IS_READER (reader), 0);
+  json_reader_return_val_if_error_set (reader, 0);
+
+  if (reader->priv->current_node == NULL)
+    return 0;
+
+  if (!JSON_NODE_HOLDS_VALUE (reader->priv->current_node))
+    return 0;
+
+  return json_node_get_int (reader->priv->current_node);
+}
+
+/**
+ * json_reader_get_value_double:
+ * @reader: a #JsonReader
+ *
+ * Retrieves the floating point value of the current position of @reader
+ *
+ * Return value: the floating point value
+ *
+ * Since: 0.12
+ */
+gdouble
+json_reader_get_value_double (JsonReader *reader)
+{
+  g_return_val_if_fail (JSON_IS_READER (reader), 0.0);
+  json_reader_return_val_if_error_set (reader, 0.0);
+
+  if (reader->priv->current_node == NULL)
+    return 0.0;
+
+  if (!JSON_NODE_HOLDS_VALUE (reader->priv->current_node))
+    return 0.0;
+
+  return json_node_get_double (reader->priv->current_node);
+}
+
+/**
+ * json_reader_get_value_string:
+ * @reader: a #JsonReader
+ *
+ * Retrieves the string value of the current position of @reader
+ *
+ * Return value: the string value
+ *
+ * Since: 0.12
+ */
+G_CONST_RETURN gchar *
+json_reader_get_value_string (JsonReader *reader)
+{
+  g_return_val_if_fail (JSON_IS_READER (reader), NULL);
+  json_reader_return_val_if_error_set (reader, NULL);
+
+  if (reader->priv->current_node == NULL)
+    return NULL;
+
+  if (!JSON_NODE_HOLDS_VALUE (reader->priv->current_node))
+    return NULL;
+
+  return json_node_get_string (reader->priv->current_node);
+}
+
+/**
+ * json_reader_get_value_boolean:
+ * @reader: a #JsonReader
+ *
+ * Retrieves the boolean value of the current position of @reader
+ *
+ * Return value: the boolean value
+ *
+ * Since: 0.12
+ */
+gboolean
+json_reader_get_value_boolean (JsonReader *reader)
+{
+  g_return_val_if_fail (JSON_IS_READER (reader), FALSE);
+  json_reader_return_val_if_error_set (reader, FALSE);
+
+  if (reader->priv->current_node == NULL)
+    return FALSE;
+
+  if (!JSON_NODE_HOLDS_VALUE (reader->priv->current_node))
+    return FALSE;
+
+  return json_node_get_boolean (reader->priv->current_node);
+}
+
+/**
+ * json_reader_get_value_null:
+ * @reader: a #JsonReader
+ *
+ * Checks whether the value of the current position of @reader is 'null'
+ *
+ * Return value: %TRUE if 'null' is set, and %FALSE otherwise
+ *
+ * Since: 0.12
+ */
+gboolean
+json_reader_get_value_null (JsonReader *reader)
+{
+  g_return_val_if_fail (JSON_IS_READER (reader), FALSE);
+  json_reader_return_val_if_error_set (reader, FALSE);
+
+  if (reader->priv->current_node == NULL)
+    return FALSE;
+
+  return JSON_NODE_HOLDS_NULL (reader->priv->current_node);
+}
diff --git a/json-glib/json-reader.h b/json-glib/json-reader.h
new file mode 100644
index 0000000..a4fb239
--- /dev/null
+++ b/json-glib/json-reader.h
@@ -0,0 +1,142 @@
+/* json-reader.h - JSON cursor parser
+ * 
+ * This file is part of JSON-GLib
+ * Copyright (C) 2010  Intel Corp.
+ *
+ * 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:
+ *   Emmanuele Bassi  <ebassi linux intel com>
+ */
+
+#if !defined(__JSON_GLIB_INSIDE__) && !defined(JSON_COMPILATION)
+#error "Only <json-glib/json-glib.h> can be included directly."
+#endif
+
+#ifndef __JSON_READER_H__
+#define __JSON_READER_H__
+
+#include <json-glib/json-types.h>
+
+G_BEGIN_DECLS
+
+#define JSON_TYPE_READER                (json_reader_get_type ())
+#define JSON_READER(obj)                (G_TYPE_CHECK_INSTANCE_CAST ((obj), JSON_TYPE_READER, JsonReader))
+#define JSON_IS_READER(obj)             (G_TYPE_CHECK_INSTANCE_TYPE ((obj), JSON_TYPE_READER))
+#define JSON_READER_CLASS(klass)        (G_TYPE_CHECK_CLASS_CAST ((klass), JSON_TYPE_READER, JsonReaderClass))
+#define JSON_IS_READER_CLASS(klass)     (G_TYPE_CHECK_CLASS_TYPE ((klass), JSON_TYPE_READER))
+#define JSON_READER_GET_CLASS(obj)      (G_TYPE_INSTANCE_GET_CLASS ((obj), JSON_TYPE_READER, JsonReaderClass))
+
+/**
+ * JSON_READER_ERROR:
+ *
+ * Error domain for #JsonReader errors
+ *
+ * Since: 0.12
+ */
+#define JSON_READER_ERROR               (json_reader_error_quark ())
+
+typedef struct _JsonReader              JsonReader;
+typedef struct _JsonReaderPrivate       JsonReaderPrivate;
+typedef struct _JsonReaderClass         JsonReaderClass;
+
+/**
+ * JsonReaderError:
+ * @JSON_READER_ERROR_NO_ARRAY: No array found at the current position
+ * @JSON_READER_ERROR_INVALID_INDEX: Index out of bounds
+ * @JSON_READER_ERROR_NO_OBJECT: No object found at the current position
+ * @JSON_READER_ERROR_INVALID_MEMBER: Member not found
+ *
+ * Error codes enumeration for #JsonReader errors
+ *
+ * Since: 0.12
+ */
+typedef enum {
+  JSON_READER_ERROR_NO_ARRAY,
+  JSON_READER_ERROR_INVALID_INDEX,
+  JSON_READER_ERROR_NO_OBJECT,
+  JSON_READER_ERROR_INVALID_MEMBER
+} JsonReaderError;
+
+/**
+ * JsonReader:
+ *
+ * The <structname>JsonReader</structname> structure contains only
+ * private data and should only be accessed using the provided API
+ *
+ * Since: 0.12
+ */
+struct _JsonReader
+{
+  /*< private >*/
+  GObject parent_instance;
+
+  JsonReaderPrivate *priv;
+};
+
+/**
+ * JsonReaderClass:
+ *
+ * The <structname>JsonReaderClass</structname> structure contains only
+ * private data
+ *
+ * Since: 0.12
+ */
+struct _JsonReaderClass
+{
+  /*< private >*/
+  GObjectClass parent_class;
+
+  void (*_json_padding0) (void);
+  void (*_json_padding1) (void);
+  void (*_json_padding2) (void);
+  void (*_json_padding3) (void);
+  void (*_json_padding4) (void);
+};
+
+GQuark json_reader_error_quark (void);
+GType json_reader_get_type (void) G_GNUC_CONST;
+
+JsonReader *           json_reader_new               (void);
+
+gboolean               json_reader_load_from_data    (JsonReader   *reader,
+                                                      const gchar  *data,
+                                                      gssize        length,
+                                                      GError      **error);
+
+G_CONST_RETURN GError *json_reader_get_error         (JsonReader   *reader);
+
+gboolean               json_reader_is_array          (JsonReader   *reader);
+gboolean               json_reader_read_element      (JsonReader   *reader,
+                                                      guint         index_);
+void                   json_reader_end_element       (JsonReader   *reader);
+gint                   json_reader_count_elements    (JsonReader   *reader);
+
+gboolean               json_reader_is_object         (JsonReader   *reader);
+gboolean               json_reader_read_member       (JsonReader   *reader,
+                                                      const gchar  *member_name);
+void                   json_reader_end_member        (JsonReader   *reader);
+gint                   json_reader_count_members     (JsonReader   *reader);
+
+gboolean               json_reader_is_value          (JsonReader   *reader);
+JsonNode *             json_reader_get_value         (JsonReader   *reader);
+gint64                 json_reader_get_value_int     (JsonReader   *reader);
+gdouble                json_reader_get_value_double  (JsonReader   *reader);
+G_CONST_RETURN gchar * json_reader_get_value_string  (JsonReader   *reader);
+gboolean               json_reader_get_value_boolean (JsonReader   *reader);
+gboolean               json_reader_get_value_null    (JsonReader   *reader);
+
+G_END_DECLS
+
+#endif /* __JSON_READER_H__ */
diff --git a/json-glib/tests/Makefile.am b/json-glib/tests/Makefile.am
index 917b0de..74b7ba0 100644
--- a/json-glib/tests/Makefile.am
+++ b/json-glib/tests/Makefile.am
@@ -37,6 +37,10 @@ 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)
+TEST_PROGS             += builder-test
+builder_test_SOURCES    = builder-test.c
+builder_test_LDADD      = $(progs_ldadd)
+
+TEST_PROGS             += reader-test
+reader_test_SOURCES     = reader-test.c
+reader_test_LDADD       = $(progs_ldadd)
diff --git a/json-glib/tests/reader-test.c b/json-glib/tests/reader-test.c
new file mode 100644
index 0000000..ebd5381
--- /dev/null
+++ b/json-glib/tests/reader-test.c
@@ -0,0 +1,101 @@
+#include <stdlib.h>
+#include <stdio.h>
+
+#include <glib.h>
+
+#include <json-glib/json-glib.h>
+
+static const gchar *test_base_array_data =
+"[ 0, true, null, \"foo\", 3.14, [ false ], { \"bar\" : 42 } ]";
+
+static const gchar *test_base_object_data =
+"{ \"text\" : \"hello, world!\", \"foo\" : \"bar\", \"blah\" : 47 }";
+
+static void
+test_base_object (void)
+{
+  JsonReader *reader = json_reader_new ();
+  GError *error = NULL;
+
+  json_reader_load_from_data (reader, test_base_object_data, -1, &error);
+  g_assert (error == NULL);
+
+  g_assert (json_reader_is_object (reader));
+  g_assert_cmpint (json_reader_count_members (reader), ==, 3);
+
+  g_assert (json_reader_read_member (reader, "foo"));
+  g_assert (json_reader_is_value (reader));
+  g_assert_cmpstr (json_reader_get_value_string (reader), ==, "bar");
+  json_reader_end_member (reader);
+
+  g_assert (!json_reader_read_member (reader, "bar"));
+  g_assert (json_reader_get_error (reader) != NULL);
+  g_assert_error ((GError *) json_reader_get_error (reader),
+                  JSON_READER_ERROR,
+                  JSON_READER_ERROR_INVALID_MEMBER);
+  json_reader_end_member (reader);
+  g_assert (json_reader_get_error (reader) == NULL);
+
+  g_object_unref (reader);
+}
+
+static void
+test_base_array (void)
+{
+  JsonReader *reader = json_reader_new ();
+  GError *error = NULL;
+
+  json_reader_load_from_data (reader, test_base_array_data, -1, &error);
+  g_assert (error == NULL);
+
+  g_assert (json_reader_is_array (reader));
+  g_assert_cmpint (json_reader_count_elements (reader), ==, 7);
+
+  json_reader_read_element (reader, 0);
+  g_assert (json_reader_is_value (reader));
+  g_assert_cmpint (json_reader_get_value_int (reader), ==, 0);
+  json_reader_end_element (reader);
+
+  json_reader_read_element (reader, 3);
+  g_assert (json_reader_is_value (reader));
+  g_assert_cmpstr (json_reader_get_value_string (reader), ==, "foo");
+  json_reader_end_element (reader);
+
+  json_reader_read_element (reader, 5);
+  g_assert (!json_reader_is_value (reader));
+  g_assert (json_reader_is_array (reader));
+  json_reader_end_element (reader);
+
+  json_reader_read_element (reader, 6);
+  g_assert (json_reader_is_object (reader));
+
+  json_reader_read_member (reader, "bar");
+  g_assert (json_reader_is_value (reader));
+  g_assert_cmpint (json_reader_get_value_int (reader), ==, 42);
+  json_reader_end_member (reader);
+
+  json_reader_end_element (reader);
+
+  g_assert (!json_reader_read_element (reader, 7));
+  g_assert_error ((GError *) json_reader_get_error (reader),
+                  JSON_READER_ERROR,
+                  JSON_READER_ERROR_INVALID_INDEX);
+  json_reader_end_element (reader);
+  g_assert (json_reader_get_error (reader) == NULL);
+
+  g_object_unref (reader);
+}
+
+int
+main (int   argc,
+      char *argv[])
+{
+  g_type_init ();
+  g_test_init (&argc, &argv, NULL);
+  g_test_bug_base ("http://bugzilla.gnome.org/show_bug.cgi?id=";);
+
+  g_test_add_func ("/reader/base-array", test_base_array);
+  g_test_add_func ("/reader/base-object", test_base_object);
+
+  return g_test_run ();
+}



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