json-glib r21 - in trunk: . contrib doc doc/reference json-glib json-glib/tests tests
- From: ebassi svn gnome org
- To: svn-commits-list gnome org
- Subject: json-glib r21 - in trunk: . contrib doc doc/reference json-glib json-glib/tests tests
- Date: Sat, 19 Apr 2008 17:34:24 +0100 (BST)
Author: ebassi
Date: Sat Apr 19 16:34:24 2008
New Revision: 21
URL: http://svn.gnome.org/viewvc/json-glib?rev=21&view=rev
Log:
Resync with the development git repository
Added:
trunk/Makefile.decl
trunk/json-glib/json-scanner.c
trunk/json-glib/json-scanner.h
trunk/json-glib/tests/
trunk/json-glib/tests/Makefile.am
trunk/json-glib/tests/array-test.c
trunk/json-glib/tests/node-test.c
trunk/json-glib/tests/object-test.c
trunk/tests/test-generator.c
trunk/tests/test-parser.c
trunk/tests/test-serialize-complex.c
trunk/tests/test-serialize-full.c
trunk/tests/test-serialize-simple.c
Removed:
trunk/tests/test-01.c
trunk/tests/test-02.c
trunk/tests/test-03.c
trunk/tests/test-04.c
trunk/tests/test-05.c
trunk/tests/test-06.c
trunk/tests/test-07.c
trunk/tests/test-08.c
Modified:
trunk/ChangeLog
trunk/Makefile.am
trunk/README
trunk/configure.ac
trunk/contrib/Makefile.am
trunk/doc/Makefile.am
trunk/doc/reference/Makefile.am
trunk/doc/reference/json-glib-sections.txt
trunk/json-glib/Makefile.am
trunk/json-glib/json-array.c
trunk/json-glib/json-generator.c
trunk/json-glib/json-glib.h
trunk/json-glib/json-gobject.c
trunk/json-glib/json-node.c
trunk/json-glib/json-object.c
trunk/json-glib/json-parser.c
trunk/json-glib/json-parser.h
trunk/tests/Makefile.am
Modified: trunk/Makefile.am
==============================================================================
--- trunk/Makefile.am (original)
+++ trunk/Makefile.am Sat Apr 19 16:34:24 2008
@@ -1,3 +1,5 @@
+include $(top_srcdir)/Makefile.decl
+
SUBDIRS = json-glib doc tests
if HAVE_VALA
@@ -8,14 +10,14 @@
pcfiles = json-glib-1.0.pc
-%-1.0.pc: %.pc
+json-glib-1.0.pc: json-glib.pc
@cp -f $< $@
pkgconfig_DATA = $(pcfiles)
pkgconfigdir = $(libdir)/pkgconfig
-EXTRA_DIST = json-glib.pc.in
+EXTRA_DIST += json-glib.pc.in
-CLEANFILES = $(pcfiles)
+CLEANFILES = $(pcfiles) test-report.xml
DISTCHECK_CONFIGURE_FLAGS = --enable-gtk-doc --enable-maintainer-flags
Added: trunk/Makefile.decl
==============================================================================
--- (empty file)
+++ trunk/Makefile.decl Sat Apr 19 16:34:24 2008
@@ -0,0 +1,61 @@
+# JSON-GLib - JSON reader and writer library
+
+GTESTER = gtester
+GTESTER_REPORT = gtester-report
+
+# initialize variables for unconditional += appending
+EXTRA_DIST =
+TEST_PROGS =
+
+### testing rules
+
+# test: run all tests in cwd and subdirs
+test: ${TEST_PROGS}
+ @test -z "${TEST_PROGS}" || ${GTESTER} --verbose ${TEST_PROGS}
+ @ for subdir in $(SUBDIRS) . ; do \
+ test "$$subdir" = "." -o "$$subdir" = "po" || \
+ ( cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $@ ) || exit $? ; \
+ done
+# test-report: run tests in subdirs and generate report
+# perf-report: run tests in subdirs with -m perf and generate report
+# full-report: like test-report: with -m perf and -m slow
+test-report perf-report full-report: ${TEST_PROGS}
+ @test -z "${TEST_PROGS}" || { \
+ case $@ in \
+ test-report) test_options="-k";; \
+ perf-report) test_options="-k -m=perf";; \
+ full-report) test_options="-k -m=perf -m=slow";; \
+ esac ; \
+ if test -z "$$GTESTER_LOGDIR" ; then \
+ ${GTESTER} --verbose $$test_options -o test-report.xml ${TEST_PROGS} ; \
+ elif test -n "${TEST_PROGS}" ; then \
+ ${GTESTER} --verbose $$test_options -o `mktemp "$$GTESTER_LOGDIR/log-XXXXXX"` ${TEST_PROGS} ; \
+ fi ; \
+ }
+ @ ignore_logdir=true ; \
+ if test -z "$$GTESTER_LOGDIR" ; then \
+ GTESTER_LOGDIR=`mktemp -d "\`pwd\`/.testlogs-XXXXXX"`; export GTESTER_LOGDIR ; \
+ ignore_logdir=false ; \
+ fi ; \
+ for subdir in $(SUBDIRS) . ; do \
+ test "$$subdir" = "." -o "$$subdir" = "po" || \
+ ( cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $@ ) || exit $? ; \
+ done ; \
+ $$ignore_logdir || { \
+ echo '<?xml version="1.0"?>' > $ xml ; \
+ echo '<report-collection>' >> $ xml ; \
+ echo '<info>' >> $ xml ; \
+ echo ' <package>$(PACKAGE)</package>' >> $ xml ; \
+ echo ' <version>$(VERSION)</version>' >> $ xml ; \
+ echo '</info>' >> $ xml ; \
+ for lf in `ls -L "$$GTESTER_LOGDIR"/.` ; do \
+ sed '1,1s/^<?xml\b[^>?]*?>//' <"$$GTESTER_LOGDIR"/"$$lf" >> $ xml ; \
+ done ; \
+ echo >> $ xml ; \
+ echo '</report-collection>' >> $ xml ; \
+ rm -rf "$$GTESTER_LOGDIR"/ ; \
+ ${GTESTER_REPORT} --version 2>/dev/null 1>&2 ; test "$$?" != 0 || ${GTESTER_REPORT} $ xml >$ html ; \
+ }
+.PHONY: test test-report perf-report full-report
+# run make test as part of make check
+check-local: test
Modified: trunk/README
==============================================================================
--- trunk/README (original)
+++ trunk/README Sat Apr 19 16:34:24 2008
@@ -33,6 +33,42 @@
$ make all
# make install
+BUGS
+----
+
+If you find a bug in JSON-GLib, please create a Bugzilla entry here:
+
+ http://bugzilla.openedhand.com/enter_bug.cgi?product=json-glib
+
+Attaching:
+- the version of JSON-GLib
+ - if it is a development version, the branch of the git repository
+- the JSON data that produced the bug (if any)
+- a small test case, if none of the test units exhibit the behaviour
+- in case of a segmentation fault, a full stack trace with debugging
+ symbols obtained through gdb is greatly appreaciated
+
+HACKING
+-------
+
+JSON-GLib is developed mainly inside a GIT repository available at:
+
+ git://github.com/ebassi/json-glib.git
+
+You can clone the GIT repository with:
+
+ git clone git://github.com/ebassi/json-glib.git
+
+If you want to contribute functionality or bug fixes to JSON-GLib you
+can either notify me to pull from your GIT repository or send me a set
+of patches using:
+
+ git format-patch master -k -s
+
+or:
+
+ git send-email -k -s
+
AUTHOR
------
JSON-GLib has been written by Emmanuele Bassi
Modified: trunk/configure.ac
==============================================================================
--- trunk/configure.ac (original)
+++ trunk/configure.ac Sat Apr 19 16:34:24 2008
@@ -15,13 +15,13 @@
m4_define([lt_age],
[m4_eval(json_binary_age - json_interface_age)])
-m4_define([glib_req_version], [2.12])
+m4_define([glib_req_version], [2.15])
-AC_PREREQ(2.59)
+AC_PREREQ([2.59])
AC_INIT([json-glib], [json_version], [], [json-glib])
AC_CONFIG_SRCDIR([json-glib/json-glib.h])
-AM_INIT_AUTOMAKE([1.7])
+AM_INIT_AUTOMAKE([1.9])
AM_CONFIG_HEADER([config.h])
AM_DISABLE_STATIC
@@ -113,6 +113,7 @@
Makefile
json-glib/Makefile
json-glib/json-version.h
+ json-glib/tests/Makefile
tests/Makefile
doc/Makefile
doc/reference/Makefile
Modified: trunk/contrib/Makefile.am
==============================================================================
--- trunk/contrib/Makefile.am (original)
+++ trunk/contrib/Makefile.am Sat Apr 19 16:34:24 2008
@@ -1,4 +1,6 @@
+include $(top_srcdir)/Makefile.decl
+
vapidir = @VAPI_DIR@
vapi_DATA = json-glib-1.0.vapi
-EXTRA_DIST = json-test.vala
+EXTRA_DIST += json-test.vala
Modified: trunk/doc/Makefile.am
==============================================================================
--- trunk/doc/Makefile.am (original)
+++ trunk/doc/Makefile.am Sat Apr 19 16:34:24 2008
@@ -1 +1,3 @@
+include $(top_srcdir)/Makefile.decl
+
SUBDIRS = reference
Modified: trunk/doc/reference/Makefile.am
==============================================================================
--- trunk/doc/reference/Makefile.am (original)
+++ trunk/doc/reference/Makefile.am Sat Apr 19 16:34:24 2008
@@ -1,4 +1,5 @@
## Process this file with automake to produce Makefile.in
+include $(top_srcdir)/Makefile.decl
# We require automake 1.6 at least.
AUTOMAKE_OPTIONS = 1.6
Modified: trunk/doc/reference/json-glib-sections.txt
==============================================================================
--- trunk/doc/reference/json-glib-sections.txt (original)
+++ trunk/doc/reference/json-glib-sections.txt Sat Apr 19 16:34:24 2008
@@ -102,7 +102,6 @@
<SUBSECTION>
json_parser_get_root
-json_parser_peek_root
<SUBSECTION>
json_parser_get_current_line
Modified: trunk/json-glib/Makefile.am
==============================================================================
--- trunk/json-glib/Makefile.am (original)
+++ trunk/json-glib/Makefile.am Sat Apr 19 16:34:24 2008
@@ -1,3 +1,9 @@
+include $(top_srcdir)/Makefile.decl
+
+SUBDIRS = . tests
+
+DIST_SUBDIRS = tests
+
NULL =
INCLUDES = \
@@ -40,6 +46,7 @@
$(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-scanner.h \
$(top_srcdir)/json-glib/json-types.h \
$(top_srcdir)/json-glib/json-version.h \
$(NULL)
@@ -53,6 +60,7 @@
json-node.c \
json-object.c \
json-parser.c \
+ json-scanner.c \
$(NULL)
lib_LTLIBRARIES = libjson-glib-1.0.la
@@ -75,5 +83,6 @@
DISTCLEANFILES = json-version.h
-EXTRA_DIST = json-version.h.in json-glib.h json-marshal.list
+EXTRA_DIST += json-version.h.in json-glib.h json-marshal.list
+TESTS_ENVIRONMENT = srcdir="$(srcdir)" json_all_c_sources="$(source_c)"
Modified: trunk/json-glib/json-array.c
==============================================================================
--- trunk/json-glib/json-array.c (original)
+++ trunk/json-glib/json-array.c Sat Apr 19 16:34:24 2008
@@ -17,7 +17,9 @@
* Emmanuele Bassi <ebassi openedhand com>
*/
+#ifdef HAVE_CONFIG_H
#include "config.h"
+#endif
#include "json-types.h"
Modified: trunk/json-glib/json-generator.c
==============================================================================
--- trunk/json-glib/json-generator.c (original)
+++ trunk/json-glib/json-generator.c Sat Apr 19 16:34:24 2008
@@ -25,7 +25,9 @@
* put it into a buffer or a file.
*/
+#ifdef HAVE_CONFIG_H
#include "config.h"
+#endif
#include <stdlib.h>
#include <string.h>
@@ -329,16 +331,19 @@
case JSON_NODE_VALUE:
value = dump_value (generator, sub_level, NULL, cur);
g_string_append (buffer, value);
+ g_free (value);
break;
case JSON_NODE_ARRAY:
value = dump_array (generator, sub_level, NULL, json_node_get_array (cur), NULL);
g_string_append (buffer, value);
+ g_free (value);
break;
case JSON_NODE_OBJECT:
value = dump_object (generator, sub_level, NULL, json_node_get_object (cur), NULL);
g_string_append (buffer, value);
+ g_free (value);
break;
}
@@ -421,18 +426,21 @@
case JSON_NODE_VALUE:
value = dump_value (generator, sub_level, member_name, cur);
g_string_append (buffer, value);
+ g_free (value);
break;
case JSON_NODE_ARRAY:
value = dump_array (generator, sub_level, member_name,
json_node_get_array (cur), NULL);
g_string_append (buffer, value);
+ g_free (value);
break;
case JSON_NODE_OBJECT:
value = dump_object (generator, sub_level, member_name,
json_node_get_object (cur), NULL);
g_string_append (buffer, value);
+ g_free (value);
break;
}
Modified: trunk/json-glib/json-glib.h
==============================================================================
--- trunk/json-glib/json-glib.h (original)
+++ trunk/json-glib/json-glib.h Sat Apr 19 16:34:24 2008
@@ -2,6 +2,7 @@
#define __JSON_GLIB_H__
#include <json-glib/json-types.h>
+#include <json-glib/json-scanner.h>
#include <json-glib/json-generator.h>
#include <json-glib/json-parser.h>
#include <json-glib/json-version.h>
Modified: trunk/json-glib/json-gobject.c
==============================================================================
--- trunk/json-glib/json-gobject.c (original)
+++ trunk/json-glib/json-gobject.c Sat Apr 19 16:34:24 2008
@@ -32,7 +32,9 @@
* and its virtual functions.
*/
+#ifdef HAVE_CONFIG_H
#include "config.h"
+#endif
#include <string.h>
#include <stdlib.h>
@@ -41,94 +43,6 @@
#include "json-parser.h"
#include "json-generator.h"
-GType
-json_serializable_get_type (void)
-{
- static GType iface_type = 0;
-
- if (!iface_type)
- iface_type =
- g_type_register_static_simple (G_TYPE_INTERFACE, "JsonSerializable",
- sizeof (JsonSerializableIface),
- NULL, 0, NULL, 0);
-
- return iface_type;
-}
-
-/**
- * json_serializable_serialize_property:
- * @serializable: a #JsonSerializable object
- * @property_name: the name of the property
- * @value: the value of the property
- * @pspec: a #GParamSpec
- *
- * Asks a #JsonSerializable implementation to serialize a #GObject
- * property into a #JsonNode object.
- *
- * Return value: a #JsonNode containing the serialize property
- */
-JsonNode *
-json_serializable_serialize_property (JsonSerializable *serializable,
- const gchar *property_name,
- const GValue *value,
- GParamSpec *pspec)
-{
- JsonSerializableIface *iface;
-
- g_return_val_if_fail (JSON_IS_SERIALIZABLE (serializable), NULL);
- g_return_val_if_fail (property_name != NULL, NULL);
- g_return_val_if_fail (value != NULL, NULL);
- g_return_val_if_fail (pspec != NULL, NULL);
-
- iface = JSON_SERIALIZABLE_GET_IFACE (serializable);
- if (!iface->serialize_property)
- return json_node_new (JSON_NODE_NULL);
-
- return iface->serialize_property (serializable, property_name, value, pspec);
-}
-
-/**
- * json_serializable_deserialize_property:
- * @serializable: a #JsonSerializable
- * @property_name: the name of the property
- * @value: a pointer to an uninitialized #GValue
- * @pspec: a #GParamSpec
- * @property_node: a #JsonNode containing the serialized property
- *
- * Asks a #JsonSerializable implementation to deserialize the
- * property contained inside @property_node into @value.
- *
- * Return value: %TRUE if the property was successfully deserialized.
- */
-gboolean
-json_serializable_deserialize_property (JsonSerializable *serializable,
- const gchar *property_name,
- GValue *value,
- GParamSpec *pspec,
- JsonNode *property_node)
-{
- JsonSerializableIface *iface;
-
- g_return_val_if_fail (JSON_IS_SERIALIZABLE (serializable), FALSE);
- g_return_val_if_fail (property_name != NULL, FALSE);
- g_return_val_if_fail (value != NULL, FALSE);
- g_return_val_if_fail (pspec != NULL, FALSE);
- g_return_val_if_fail (property_node != NULL, FALSE);
-
- iface = JSON_SERIALIZABLE_GET_IFACE (serializable);
- if (!iface->deserialize_property)
- {
- g_param_value_defaults (pspec, value);
- return TRUE;
- }
-
- return iface->deserialize_property (serializable,
- property_name,
- value,
- pspec,
- property_node);
-}
-
static gboolean
enum_from_string (GType type,
const gchar *string,
@@ -375,6 +289,8 @@
retval = FALSE;
break;
}
+
+ g_value_unset (&node_value);
break;
case JSON_NODE_NULL:
@@ -493,6 +409,124 @@
}
/**
+ * json_serializable_serialize_property:
+ * @serializable: a #JsonSerializable object
+ * @property_name: the name of the property
+ * @value: the value of the property
+ * @pspec: a #GParamSpec
+ *
+ * Asks a #JsonSerializable implementation to serialize a #GObject
+ * property into a #JsonNode object.
+ *
+ * Return value: a #JsonNode containing the serialize property
+ */
+JsonNode *
+json_serializable_serialize_property (JsonSerializable *serializable,
+ const gchar *property_name,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ JsonSerializableIface *iface;
+
+ g_return_val_if_fail (JSON_IS_SERIALIZABLE (serializable), NULL);
+ g_return_val_if_fail (property_name != NULL, NULL);
+ g_return_val_if_fail (value != NULL, NULL);
+ g_return_val_if_fail (pspec != NULL, NULL);
+
+ iface = JSON_SERIALIZABLE_GET_IFACE (serializable);
+
+ return iface->serialize_property (serializable, property_name, value, pspec);
+}
+
+/**
+ * json_serializable_deserialize_property:
+ * @serializable: a #JsonSerializable
+ * @property_name: the name of the property
+ * @value: a pointer to an uninitialized #GValue
+ * @pspec: a #GParamSpec
+ * @property_node: a #JsonNode containing the serialized property
+ *
+ * Asks a #JsonSerializable implementation to deserialize the
+ * property contained inside @property_node into @value.
+ *
+ * Return value: %TRUE if the property was successfully deserialized.
+ */
+gboolean
+json_serializable_deserialize_property (JsonSerializable *serializable,
+ const gchar *property_name,
+ GValue *value,
+ GParamSpec *pspec,
+ JsonNode *property_node)
+{
+ JsonSerializableIface *iface;
+
+ g_return_val_if_fail (JSON_IS_SERIALIZABLE (serializable), FALSE);
+ g_return_val_if_fail (property_name != NULL, FALSE);
+ g_return_val_if_fail (value != NULL, FALSE);
+ g_return_val_if_fail (pspec != NULL, FALSE);
+ g_return_val_if_fail (property_node != NULL, FALSE);
+
+ iface = JSON_SERIALIZABLE_GET_IFACE (serializable);
+
+ return iface->deserialize_property (serializable,
+ property_name,
+ value,
+ pspec,
+ property_node);
+}
+
+static gboolean
+json_serializable_real_deserialize (JsonSerializable *serializable,
+ const gchar *name,
+ GValue *value,
+ GParamSpec *pspec,
+ JsonNode *node)
+{
+ return json_deserialize_pspec (value, pspec, node);
+}
+
+static JsonNode *
+json_serializable_real_serialize (JsonSerializable *serializable,
+ const gchar *name,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ return json_serialize_pspec (value, pspec);
+}
+
+static void
+json_serializable_base_init (gpointer g_class,
+ gpointer data)
+{
+ static gboolean is_initialized = FALSE;
+
+ if (G_UNLIKELY (!is_initialized))
+ {
+ JsonSerializableIface *iface = g_class;
+
+ iface->serialize_property = json_serializable_real_serialize;
+ iface->deserialize_property = json_serializable_real_deserialize;
+
+ is_initialized = TRUE;
+ }
+}
+
+GType
+json_serializable_get_type (void)
+{
+ static GType iface_type = 0;
+
+ if (!iface_type)
+ iface_type =
+ g_type_register_static_simple (G_TYPE_INTERFACE, "JsonSerializable",
+ sizeof (JsonSerializableIface),
+ json_serializable_base_init,
+ 0, NULL, 0);
+
+ return iface_type;
+}
+
+/**
* json_construct_gobject:
* @gtype: the #GType of object to construct
* @data: a JSON data stream
@@ -547,7 +581,7 @@
}
root = json_parser_get_root (parser);
- if (JSON_NODE_TYPE (root) != JSON_NODE_OBJECT)
+ if (root == NULL || JSON_NODE_TYPE (root) != JSON_NODE_OBJECT)
{
g_set_error (error, JSON_PARSER_ERROR,
JSON_PARSER_ERROR_PARSE,
@@ -618,8 +652,6 @@
g_list_free (members);
- json_node_free (root);
-
g_object_thaw_notify (retval);
g_type_class_unref (klass);
Modified: trunk/json-glib/json-node.c
==============================================================================
--- trunk/json-glib/json-node.c (original)
+++ trunk/json-glib/json-node.c Sat Apr 19 16:34:24 2008
@@ -17,7 +17,9 @@
* Emmanuele Bassi <ebassi openedhand com>
*/
+#ifdef HAVE_CONFIG_H
#include "config.h"
+#endif
#include <glib.h>
@@ -48,7 +50,7 @@
static GType node_type = 0;
if (G_UNLIKELY (node_type == 0))
- node_type = g_boxed_type_register_static ("JsonNode",
+ node_type = g_boxed_type_register_static (g_intern_static_string ("JsonNode"),
(GBoxedCopyFunc) json_node_copy,
(GBoxedFreeFunc) json_node_free);
@@ -103,9 +105,10 @@
{
JsonNode *data;
- g_return_val_if_fail (type >= JSON_NODE_OBJECT && type <= JSON_NODE_NULL, NULL);
+ g_return_val_if_fail (type >= JSON_NODE_OBJECT &&
+ type <= JSON_NODE_NULL, NULL);
- data = g_slice_new (JsonNode);
+ data = g_slice_new0 (JsonNode);
data->type = type;
return data;
@@ -127,7 +130,7 @@
g_return_val_if_fail (node != NULL, NULL);
- copy = g_slice_new (JsonNode);
+ copy = g_slice_new0 (JsonNode);
*copy = *node;
switch (copy->type)
@@ -489,7 +492,9 @@
json_node_get_string (JsonNode *node)
{
g_return_val_if_fail (node != NULL, NULL);
- g_return_val_if_fail (JSON_NODE_TYPE (node) == JSON_NODE_VALUE, NULL);
+
+ if (JSON_NODE_TYPE (node) == JSON_NODE_NULL)
+ return NULL;
if (G_VALUE_TYPE (&(node->data.value)) == G_TYPE_STRING)
return g_value_get_string (&(node->data.value));
@@ -509,7 +514,10 @@
gchar *
json_node_dup_string (JsonNode *node)
{
- g_return_val_if_fail (JSON_NODE_TYPE (node) == JSON_NODE_VALUE, NULL);
+ g_return_val_if_fail (node != NULL, NULL);
+
+ if (JSON_NODE_TYPE (node) == JSON_NODE_NULL)
+ return NULL;
if (G_VALUE_TYPE (&(node->data.value)) == G_TYPE_STRING)
return g_value_dup_string (&(node->data.value));
@@ -559,7 +567,9 @@
json_node_get_int (JsonNode *node)
{
g_return_val_if_fail (node != NULL, 0);
- g_return_val_if_fail (JSON_NODE_TYPE (node) == JSON_NODE_VALUE, 0);
+
+ if (JSON_NODE_TYPE (node) == JSON_NODE_NULL)
+ return 0;
if (G_VALUE_TYPE (&(node->data.value)) == G_TYPE_INT)
return g_value_get_int (&(node->data.value));
@@ -609,7 +619,9 @@
json_node_get_double (JsonNode *node)
{
g_return_val_if_fail (node != NULL, 0.0);
- g_return_val_if_fail (JSON_NODE_TYPE (node) == JSON_NODE_VALUE, 0.0);
+
+ if (JSON_NODE_TYPE (node) == JSON_NODE_NULL)
+ return 0;
if (G_VALUE_TYPE (&(node->data.value)) == G_TYPE_DOUBLE)
return g_value_get_double (&(node->data.value));
@@ -659,11 +671,12 @@
json_node_get_boolean (JsonNode *node)
{
g_return_val_if_fail (node != NULL, FALSE);
- g_return_val_if_fail (JSON_NODE_TYPE (node) == JSON_NODE_VALUE, FALSE);
+
+ if (JSON_NODE_TYPE (node) == JSON_NODE_NULL)
+ return FALSE;
if (G_VALUE_TYPE (&(node->data.value)) == G_TYPE_BOOLEAN)
return g_value_get_boolean (&(node->data.value));
return FALSE;
}
-
Modified: trunk/json-glib/json-object.c
==============================================================================
--- trunk/json-glib/json-object.c (original)
+++ trunk/json-glib/json-object.c Sat Apr 19 16:34:24 2008
@@ -17,7 +17,9 @@
* Emmanuele Bassi <ebassi openedhand com>
*/
+#ifdef HAVE_CONFIG_H
#include "config.h"
+#endif
#include <glib.h>
@@ -330,7 +332,7 @@
g_return_val_if_fail (member_name != NULL, FALSE);
name = g_strdelimit (g_strdup (member_name), G_STR_DELIMITERS, '_');
- retval = (g_hash_table_lookup (object->members, member_name) != NULL);
+ retval = (g_hash_table_lookup (object->members, name) != NULL);
g_free (name);
return retval;
Modified: trunk/json-glib/json-parser.c
==============================================================================
--- trunk/json-glib/json-parser.c (original)
+++ trunk/json-glib/json-parser.c Sat Apr 19 16:34:24 2008
@@ -33,6 +33,7 @@
#include "json-marshal.h"
#include "json-parser.h"
+#include "json-scanner.h"
GQuark
json_parser_error_quark (void)
@@ -48,7 +49,7 @@
JsonNode *root;
JsonNode *current_node;
- GScanner *scanner;
+ JsonScanner *scanner;
GError *last_error;
@@ -56,45 +57,6 @@
gchar *variable_name;
};
-static const GScannerConfig json_scanner_config =
-{
- ( " \t\r\n" ) /* cset_skip_characters */,
- (
- "_"
- G_CSET_a_2_z
- G_CSET_A_2_Z
- ) /* cset_identifier_first */,
- (
- G_CSET_DIGITS
- "-_"
- G_CSET_a_2_z
- G_CSET_A_2_Z
- ) /* cset_identifier_nth */,
- ( "#\n" ) /* cpair_comment_single */,
- TRUE /* case_sensitive */,
- TRUE /* skip_comment_multi */,
- TRUE /* skip_comment_single */,
- FALSE /* scan_comment_multi */,
- TRUE /* scan_identifier */,
- TRUE /* scan_identifier_1char */,
- FALSE /* scan_identifier_NULL */,
- TRUE /* scan_symbols */,
- TRUE /* scan_binary */,
- TRUE /* scan_octal */,
- TRUE /* scan_float */,
- TRUE /* scan_hex */,
- TRUE /* scan_hex_dollar */,
- TRUE /* scan_string_sq */,
- TRUE /* scan_string_dq */,
- TRUE /* numbers_2_int */,
- FALSE /* int_2_float */,
- FALSE /* identifier_2_string */,
- TRUE /* char_2_token */,
- TRUE /* symbol_2_token */,
- FALSE /* scope_0_fallback */,
-};
-
-
static const gchar symbol_names[] =
"true\0"
"false\0"
@@ -134,22 +96,18 @@
G_DEFINE_TYPE (JsonParser, json_parser, G_TYPE_OBJECT);
static guint json_parse_array (JsonParser *parser,
- GScanner *scanner,
+ JsonScanner *scanner,
gboolean nested);
static guint json_parse_object (JsonParser *parser,
- GScanner *scanner,
+ JsonScanner *scanner,
gboolean nested);
-static void
-json_parser_dispose (GObject *gobject)
+static inline void
+json_parser_clear (JsonParser *parser)
{
- JsonParserPrivate *priv = JSON_PARSER_GET_PRIVATE (gobject);
+ JsonParserPrivate *priv = parser->priv;
- if (priv->root)
- {
- json_node_free (priv->root);
- priv->root = NULL;
- }
+ g_free (priv->variable_name);
if (priv->last_error)
{
@@ -157,7 +115,17 @@
priv->last_error = NULL;
}
- g_free (priv->variable_name);
+ if (priv->root)
+ {
+ json_node_free (priv->root);
+ priv->root = NULL;
+ }
+}
+
+static void
+json_parser_dispose (GObject *gobject)
+{
+ json_parser_clear (JSON_PARSER (gobject));
G_OBJECT_CLASS (json_parser_parent_class)->dispose (gobject);
}
@@ -340,7 +308,7 @@
static guint
json_parse_array (JsonParser *parser,
- GScanner *scanner,
+ JsonScanner *scanner,
gboolean nested)
{
JsonParserPrivate *priv = parser->priv;
@@ -350,7 +318,7 @@
if (!nested)
{
/* the caller already swallowed the opening '[' */
- token = g_scanner_get_next_token (scanner);
+ token = json_scanner_get_next_token (scanner);
if (token != G_TOKEN_LEFT_BRACE)
return G_TOKEN_LEFT_BRACE;
}
@@ -359,7 +327,7 @@
array = json_array_new ();
- token = g_scanner_get_next_token (scanner);
+ token = json_scanner_get_next_token (scanner);
while (token != G_TOKEN_RIGHT_BRACE)
{
JsonNode *node = NULL;
@@ -368,7 +336,7 @@
if (token == G_TOKEN_COMMA)
{
/* swallow the comma */
- token = g_scanner_get_next_token (scanner);
+ token = json_scanner_get_next_token (scanner);
continue;
}
@@ -398,7 +366,7 @@
array,
json_array_get_length (array));
- token = g_scanner_get_next_token (scanner);
+ token = json_scanner_get_next_token (scanner);
if (token == G_TOKEN_RIGHT_BRACE)
break;
@@ -434,7 +402,7 @@
array,
json_array_get_length (array));
- token = g_scanner_get_next_token (scanner);
+ token = json_scanner_get_next_token (scanner);
if (token == G_TOKEN_RIGHT_BRACE)
break;
@@ -446,13 +414,13 @@
if (token == '-')
{
- guint next_token = g_scanner_peek_next_token (scanner);
+ guint next_token = json_scanner_peek_next_token (scanner);
if (next_token == G_TOKEN_INT ||
next_token == G_TOKEN_FLOAT)
{
negative = TRUE;
- token = g_scanner_get_next_token (scanner);
+ token = json_scanner_get_next_token (scanner);
}
else
{
@@ -504,7 +472,7 @@
json_array_get_length (array));
}
- token = g_scanner_get_next_token (scanner);
+ token = json_scanner_get_next_token (scanner);
if (token != G_TOKEN_COMMA && token != G_TOKEN_RIGHT_BRACE)
return G_TOKEN_RIGHT_BRACE;
}
@@ -518,7 +486,7 @@
static guint
json_parse_object (JsonParser *parser,
- GScanner *scanner,
+ JsonScanner *scanner,
gboolean nested)
{
JsonParserPrivate *priv = parser->priv;
@@ -528,7 +496,7 @@
if (!nested)
{
/* the caller already swallowed the opening '{' */
- token = g_scanner_get_next_token (scanner);
+ token = json_scanner_get_next_token (scanner);
if (token != G_TOKEN_LEFT_CURLY)
return G_TOKEN_LEFT_CURLY;
}
@@ -537,7 +505,7 @@
object = json_object_new ();
- token = g_scanner_get_next_token (scanner);
+ token = json_scanner_get_next_token (scanner);
while (token != G_TOKEN_RIGHT_CURLY)
{
JsonNode *node = NULL;
@@ -547,7 +515,7 @@
if (token == G_TOKEN_COMMA)
{
/* swallow the comma */
- token = g_scanner_get_next_token (scanner);
+ token = json_scanner_get_next_token (scanner);
continue;
}
@@ -555,7 +523,7 @@
{
name = g_strdup (scanner->value.v_string);
- token = g_scanner_get_next_token (scanner);
+ token = json_scanner_get_next_token (scanner);
if (token != ':')
{
g_free (name);
@@ -565,7 +533,7 @@
else
{
/* swallow the colon */
- token = g_scanner_get_next_token (scanner);
+ token = json_scanner_get_next_token (scanner);
}
}
@@ -606,7 +574,7 @@
g_free (name);
- token = g_scanner_get_next_token (scanner);
+ token = json_scanner_get_next_token (scanner);
if (token == G_TOKEN_RIGHT_CURLY)
break;
@@ -644,7 +612,7 @@
g_free (name);
- token = g_scanner_get_next_token (scanner);
+ token = json_scanner_get_next_token (scanner);
if (token == G_TOKEN_RIGHT_CURLY)
break;
@@ -656,12 +624,12 @@
if (token == '-')
{
- guint next_token = g_scanner_peek_next_token (scanner);
+ guint next_token = json_scanner_peek_next_token (scanner);
if (next_token == G_TOKEN_INT || next_token == G_TOKEN_FLOAT)
{
negative = TRUE;
- token = g_scanner_get_next_token (scanner);
+ token = json_scanner_get_next_token (scanner);
}
else
{
@@ -715,7 +683,7 @@
g_free (name);
- token = g_scanner_get_next_token (scanner);
+ token = json_scanner_get_next_token (scanner);
if (token != G_TOKEN_COMMA && token != G_TOKEN_RIGHT_CURLY)
return G_TOKEN_RIGHT_CURLY;
}
@@ -729,12 +697,12 @@
static guint
json_parse_statement (JsonParser *parser,
- GScanner *scanner)
+ JsonScanner *scanner)
{
JsonParserPrivate *priv = parser->priv;
guint token;
- token = g_scanner_peek_next_token (scanner);
+ token = json_scanner_peek_next_token (scanner);
switch (token)
{
case G_TOKEN_LEFT_CURLY:
@@ -756,17 +724,17 @@
gchar *name;
/* swallow the 'var' token... */
- token = g_scanner_get_next_token (scanner);
+ token = json_scanner_get_next_token (scanner);
/* ... swallow the variable name... */
- next_token = g_scanner_get_next_token (scanner);
+ next_token = json_scanner_get_next_token (scanner);
if (next_token != G_TOKEN_IDENTIFIER)
return G_TOKEN_IDENTIFIER;
name = g_strdup (scanner->value.v_identifier);
/* ... and finally swallow the '=' */
- next_token = g_scanner_get_next_token (scanner);
+ next_token = json_scanner_get_next_token (scanner);
if (next_token != '=')
return '=';
@@ -776,10 +744,10 @@
token = json_parse_statement (parser, scanner);
/* remove the trailing semi-colon */
- next_token = g_scanner_peek_next_token (scanner);
+ next_token = json_scanner_peek_next_token (scanner);
if (next_token == ';')
{
- token = g_scanner_get_next_token (scanner);
+ token = json_scanner_get_next_token (scanner);
return G_TOKEN_NONE;
}
@@ -802,14 +770,14 @@
{
guint next_token;
- token = g_scanner_get_next_token (scanner);
- next_token = g_scanner_peek_next_token (scanner);
+ token = json_scanner_get_next_token (scanner);
+ next_token = json_scanner_peek_next_token (scanner);
if (next_token == G_TOKEN_INT || next_token == G_TOKEN_FLOAT)
{
priv->root = priv->current_node = json_node_new (JSON_NODE_VALUE);
- token = g_scanner_get_next_token (scanner);
+ token = json_scanner_get_next_token (scanner);
switch (token)
{
case G_TOKEN_INT:
@@ -844,13 +812,13 @@
return G_TOKEN_NONE;
default:
- g_scanner_get_next_token (scanner);
+ json_scanner_get_next_token (scanner);
return G_TOKEN_SYMBOL;
}
}
static void
-json_scanner_msg_handler (GScanner *scanner,
+json_scanner_msg_handler (JsonScanner *scanner,
gchar *message,
gboolean is_error)
{
@@ -873,15 +841,23 @@
g_warning ("Line %d: %s", scanner->line, message);
}
-static GScanner *
-json_scanner_new (JsonParser *parser)
+static JsonScanner *
+json_scanner_create (JsonParser *parser)
{
- GScanner *scanner;
+ JsonScanner *scanner;
+ gint i;
- scanner = g_scanner_new (&json_scanner_config);
+ scanner = json_scanner_new ();
scanner->msg_handler = json_scanner_msg_handler;
scanner->user_data = parser;
+ for (i = 0; i < n_symbols; i++)
+ {
+ json_scanner_scope_add_symbol (scanner, 0,
+ symbol_names + symbols[i].name_offset,
+ GINT_TO_POINTER (symbols[i].token));
+ }
+
return scanner;
}
@@ -961,10 +937,11 @@
gboolean
json_parser_load_from_data (JsonParser *parser,
const gchar *data,
- gsize length,
+ gssize length,
GError **error)
{
- GScanner *scanner;
+ JsonParserPrivate *priv;
+ JsonScanner *scanner;
gboolean done;
gboolean retval = TRUE;
gint i;
@@ -972,24 +949,15 @@
g_return_val_if_fail (JSON_IS_PARSER (parser), FALSE);
g_return_val_if_fail (data != NULL, FALSE);
+ json_parser_clear (parser);
+
if (length < 0)
length = strlen (data);
- if (parser->priv->root)
- {
- json_node_free (parser->priv->root);
- parser->priv->root = NULL;
- }
-
- scanner = json_scanner_new (parser);
- g_scanner_input_text (scanner, data, strlen (data));
+ priv = parser->priv;
- for (i = 0; i < n_symbols; i++)
- {
- g_scanner_scope_add_symbol (scanner, 0,
- symbol_names + symbols[i].name_offset,
- GINT_TO_POINTER (symbols[i].token));
- }
+ scanner = json_scanner_create (parser);
+ json_scanner_input_text (scanner, data, length);
parser->priv->scanner = scanner;
@@ -998,7 +966,7 @@
done = FALSE;
while (!done)
{
- if (g_scanner_peek_next_token (scanner) == G_TOKEN_EOF)
+ if (json_scanner_peek_next_token (scanner) == G_TOKEN_EOF)
done = TRUE;
else
{
@@ -1040,18 +1008,18 @@
/* this will emit the ::error signal via the custom
* message handler we install
*/
- g_scanner_unexp_token (scanner, expected_token,
- NULL, "keyword",
- symbol_name, msg,
- TRUE);
+ json_scanner_unexp_token (scanner, expected_token,
+ NULL, "keyword",
+ symbol_name, msg,
+ TRUE);
/* and this will propagate the error we create in the
* same message handler
*/
- if (parser->priv->last_error)
+ if (priv->last_error)
{
- g_propagate_error (error, parser->priv->last_error);
- parser->priv->last_error = NULL;
+ g_propagate_error (error, priv->last_error);
+ priv->last_error = NULL;
}
retval = FALSE;
@@ -1062,52 +1030,31 @@
}
}
- g_scanner_destroy (scanner);
- parser->priv->scanner = NULL;
- parser->priv->current_node = NULL;
-
g_signal_emit (parser, parser_signals[PARSE_END], 0);
+ /* remove the scanner */
+ json_scanner_destroy (scanner);
+ priv->scanner = NULL;
+ priv->current_node = NULL;
+
return retval;
}
/**
- * json_parser_peek_root:
+ * json_parser_get_root:
* @parser: a #JsonParser
*
* Retrieves the top level node from the parsed JSON stream.
*
* Return value: the root #JsonNode . The returned node is owned by
* the #JsonParser and should never be modified or freed.
- *
- * Since: 0.6
- */
-JsonNode *
-json_parser_peek_root (JsonParser *parser)
-{
- g_return_val_if_fail (JSON_IS_PARSER (parser), NULL);
-
- return parser->priv->root;
-}
-
-/**
- * json_parser_get_root:
- * @parser: a #JsonParser
- *
- * Retrieves a copy of the top level node from the parsed JSON stream.
- *
- * Return value: a newly allocated copy of the root #JsonNode. Use
- * json_node_free() to free its allocated resources.
*/
JsonNode *
json_parser_get_root (JsonParser *parser)
{
g_return_val_if_fail (JSON_IS_PARSER (parser), NULL);
- if (!parser->priv->root)
- return NULL;
-
- return json_node_copy (parser->priv->root);
+ return parser->priv->root;
}
/**
@@ -1116,7 +1063,11 @@
*
* Retrieves the line currently parsed, starting from 1.
*
- * Return value: the currently parsed line.
+ * This function has defined behaviour only while parsing; calling this
+ * function from outside the signal handlers emitted by #JsonParser will
+ * yield 0.
+ *
+ * Return value: the currently parsed line, or 0.
*/
guint
json_parser_get_current_line (JsonParser *parser)
@@ -1124,7 +1075,7 @@
g_return_val_if_fail (JSON_IS_PARSER (parser), 0);
if (parser->priv->scanner)
- return g_scanner_cur_line (parser->priv->scanner);
+ return json_scanner_cur_line (parser->priv->scanner);
return 0;
}
@@ -1136,7 +1087,11 @@
* Retrieves the current position inside the current line, starting
* from 0.
*
- * Return value: the position in the current line
+ * This function has defined behaviour only while parsing; calling this
+ * function from outside the signal handlers emitted by #JsonParser will
+ * yield 0.
+ *
+ * Return value: the position in the current line, or 0.
*/
guint
json_parser_get_current_pos (JsonParser *parser)
@@ -1144,7 +1099,7 @@
g_return_val_if_fail (JSON_IS_PARSER (parser), 0);
if (parser->priv->scanner)
- return g_scanner_cur_line (parser->priv->scanner);
+ return json_scanner_cur_line (parser->priv->scanner);
return 0;
}
@@ -1157,7 +1112,7 @@
* A JSON data stream might sometimes contain an assignment, even though
* it would technically constitute a violation of the RFC. #JsonParser
* will ignore the left hand identifier and parse the right hand value
- * of the assignment. It will record, though, the existence of the
+ * of the assignment. #JsonParser will record, though, the existence of the
* assignment in the data stream and the variable name used.
*
* Return value: %TRUE if there was an assignment, %FALSE otherwise. If
Modified: trunk/json-glib/json-parser.h
==============================================================================
--- trunk/json-glib/json-parser.h (original)
+++ trunk/json-glib/json-parser.h Sat Apr 19 16:34:24 2008
@@ -51,17 +51,6 @@
JSON_PARSER_ERROR_UNKNOWN
} JsonParserError;
-typedef enum {
- JSON_TOKEN_INVALID = G_TOKEN_LAST,
-
- JSON_TOKEN_TRUE,
- JSON_TOKEN_FALSE,
- JSON_TOKEN_NULL,
- JSON_TOKEN_VAR,
-
- JSON_TOKEN_LAST
-} JsonTokenType;
-
/**
* JsonParser:
*
@@ -129,8 +118,8 @@
void (* _json_reserved8) (void);
};
-GQuark json_parser_error_quark (void);
-GType json_parser_get_type (void) G_GNUC_CONST;
+GQuark json_parser_error_quark (void);
+GType json_parser_get_type (void) G_GNUC_CONST;
JsonParser *json_parser_new (void);
gboolean json_parser_load_from_file (JsonParser *parser,
@@ -138,10 +127,9 @@
GError **error);
gboolean json_parser_load_from_data (JsonParser *parser,
const gchar *data,
- gsize length,
+ gssize length,
GError **error);
-JsonNode * json_parser_peek_root (JsonParser *parser);
JsonNode * json_parser_get_root (JsonParser *parser);
guint json_parser_get_current_line (JsonParser *parser);
Added: trunk/json-glib/json-scanner.c
==============================================================================
--- (empty file)
+++ trunk/json-glib/json-scanner.c Sat Apr 19 16:34:24 2008
@@ -0,0 +1,1846 @@
+/* json-scanner.c: Tokenizer for JSON
+ * Copyright (C) 2008 OpenedHand
+ *
+ * Based on JsonScanner: Flexible lexical scanner for general purpose.
+ * Copyright (C) 1997, 1998 Tim Janik
+ *
+ * Modified by Emmanuele Bassi <ebassi openedhand 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 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, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <errno.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <stdio.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include <glib.h>
+#include <glib/gprintf.h>
+
+#include "json-scanner.h"
+
+#ifdef G_OS_WIN32
+#include <io.h> /* For _read() */
+#endif
+
+struct _JsonScannerConfig
+{
+ /* Character sets
+ */
+ gchar *cset_skip_characters; /* default: " \t\n" */
+ gchar *cset_identifier_first;
+ gchar *cset_identifier_nth;
+ gchar *cpair_comment_single; /* default: "#\n" */
+
+ /* Should symbol lookup work case sensitive? */
+ guint case_sensitive : 1;
+
+ /* Boolean values to be adjusted "on the fly"
+ * to configure scanning behaviour.
+ */
+ guint skip_comment_multi : 1; /* C like comment */
+ guint skip_comment_single : 1; /* single line comment */
+ guint scan_comment_multi : 1; /* scan multi line comments? */
+ guint scan_identifier : 1;
+ guint scan_identifier_1char : 1;
+ guint scan_identifier_NULL : 1;
+ guint scan_symbols : 1;
+ guint scan_binary : 1;
+ guint scan_octal : 1;
+ guint scan_float : 1;
+ guint scan_hex : 1; /* `0x0ff0' */
+ guint scan_hex_dollar : 1; /* `$0ff0' */
+ guint scan_string_sq : 1; /* string: 'anything' */
+ guint scan_string_dq : 1; /* string: "\\-escapes!\n" */
+ guint numbers_2_int : 1; /* bin, octal, hex => int */
+ guint int_2_float : 1; /* int => G_TOKEN_FLOAT? */
+ guint identifier_2_string : 1;
+ guint char_2_token : 1; /* return G_TOKEN_CHAR? */
+ guint symbol_2_token : 1;
+ guint scope_0_fallback : 1; /* try scope 0 on lookups? */
+ guint store_int64 : 1; /* use value.v_int64 rather than v_int */
+ guint padding_dummy;
+};
+
+static JsonScannerConfig json_scanner_config_template =
+{
+ ( " \t\r\n" ) /* cset_skip_characters */,
+ (
+ "_"
+ G_CSET_a_2_z
+ G_CSET_A_2_Z
+ ) /* cset_identifier_first */,
+ (
+ G_CSET_DIGITS
+ "-_"
+ G_CSET_a_2_z
+ G_CSET_A_2_Z
+ ) /* cset_identifier_nth */,
+ ( "//\n" ) /* cpair_comment_single */,
+ TRUE /* case_sensitive */,
+ TRUE /* skip_comment_multi */,
+ TRUE /* skip_comment_single */,
+ FALSE /* scan_comment_multi */,
+ TRUE /* scan_identifier */,
+ TRUE /* scan_identifier_1char */,
+ FALSE /* scan_identifier_NULL */,
+ TRUE /* scan_symbols */,
+ TRUE /* scan_binary */,
+ TRUE /* scan_octal */,
+ TRUE /* scan_float */,
+ TRUE /* scan_hex */,
+ TRUE /* scan_hex_dollar */,
+ TRUE /* scan_string_sq */,
+ TRUE /* scan_string_dq */,
+ TRUE /* numbers_2_int */,
+ FALSE /* int_2_float */,
+ FALSE /* identifier_2_string */,
+ TRUE /* char_2_token */,
+ TRUE /* symbol_2_token */,
+ FALSE /* scope_0_fallback */,
+};
+
+/* --- defines --- */
+#define to_lower(c) ( \
+ (guchar) ( \
+ ( (((guchar)(c))>='A' && ((guchar)(c))<='Z') * ('a'-'A') ) | \
+ ( (((guchar)(c))>=192 && ((guchar)(c))<=214) * (224-192) ) | \
+ ( (((guchar)(c))>=216 && ((guchar)(c))<=222) * (248-216) ) | \
+ ((guchar)(c)) \
+ ) \
+)
+
+#define READ_BUFFER_SIZE (4000)
+
+static const gchar json_symbol_names[] =
+ "true\0"
+ "false\0"
+ "null\0"
+ "var\0";
+
+static const struct
+{
+ guint name_offset;
+ guint token;
+} json_symbols[] = {
+ { 0, JSON_TOKEN_TRUE },
+ { 5, JSON_TOKEN_FALSE },
+ { 11, JSON_TOKEN_NULL },
+ { 16, JSON_TOKEN_VAR }
+};
+
+static const guint n_json_symbols = G_N_ELEMENTS (json_symbols);
+
+/* --- typedefs --- */
+typedef struct _JsonScannerKey JsonScannerKey;
+
+struct _JsonScannerKey
+{
+ guint scope_id;
+ gchar *symbol;
+ gpointer value;
+};
+
+/* --- prototypes --- */
+static gboolean json_scanner_key_equal (gconstpointer v1,
+ gconstpointer v2);
+static guint json_scanner_key_hash (gconstpointer v);
+
+static inline
+JsonScannerKey *json_scanner_lookup_internal (JsonScanner *scanner,
+ guint scope_id,
+ const gchar *symbol);
+static void json_scanner_get_token_ll (JsonScanner *scanner,
+ GTokenType *token_p,
+ GTokenValue *value_p,
+ guint *line_p,
+ guint *position_p);
+static void json_scanner_get_token_i (JsonScanner *scanner,
+ GTokenType *token_p,
+ GTokenValue *value_p,
+ guint *line_p,
+ guint *position_p);
+
+static guchar json_scanner_peek_next_char (JsonScanner *scanner);
+static guchar json_scanner_get_char (JsonScanner *scanner,
+ guint *line_p,
+ guint *position_p);
+static gunichar json_scanner_get_unichar (JsonScanner *scanner,
+ guint *line_p,
+ guint *position_p);
+static void json_scanner_msg_handler (JsonScanner *scanner,
+ gchar *message,
+ gboolean is_error);
+
+/* --- functions --- */
+static inline gint
+json_scanner_char_2_num (guchar c,
+ guchar base)
+{
+ if (c >= '0' && c <= '9')
+ c -= '0';
+ else if (c >= 'A' && c <= 'Z')
+ c -= 'A' - 10;
+ else if (c >= 'a' && c <= 'z')
+ c -= 'a' - 10;
+ else
+ return -1;
+
+ if (c < base)
+ return c;
+
+ return -1;
+}
+
+JsonScanner *
+json_scanner_new (void)
+{
+ JsonScanner *scanner;
+ JsonScannerConfig *config_templ;
+
+ config_templ = &json_scanner_config_template;
+
+ scanner = g_new0 (JsonScanner, 1);
+
+ scanner->user_data = NULL;
+ scanner->max_parse_errors = 1;
+ scanner->parse_errors = 0;
+ scanner->input_name = NULL;
+ g_datalist_init (&scanner->qdata);
+
+ scanner->config = g_new0 (JsonScannerConfig, 1);
+
+ scanner->config->case_sensitive = config_templ->case_sensitive;
+ scanner->config->cset_skip_characters = config_templ->cset_skip_characters;
+ if (!scanner->config->cset_skip_characters)
+ scanner->config->cset_skip_characters = "";
+ scanner->config->cset_identifier_first = config_templ->cset_identifier_first;
+ scanner->config->cset_identifier_nth = config_templ->cset_identifier_nth;
+ scanner->config->cpair_comment_single = config_templ->cpair_comment_single;
+ scanner->config->skip_comment_multi = config_templ->skip_comment_multi;
+ scanner->config->skip_comment_single = config_templ->skip_comment_single;
+ scanner->config->scan_comment_multi = config_templ->scan_comment_multi;
+ scanner->config->scan_identifier = config_templ->scan_identifier;
+ scanner->config->scan_identifier_1char = config_templ->scan_identifier_1char;
+ scanner->config->scan_identifier_NULL = config_templ->scan_identifier_NULL;
+ scanner->config->scan_symbols = config_templ->scan_symbols;
+ scanner->config->scan_binary = config_templ->scan_binary;
+ scanner->config->scan_octal = config_templ->scan_octal;
+ scanner->config->scan_float = config_templ->scan_float;
+ scanner->config->scan_hex = config_templ->scan_hex;
+ scanner->config->scan_hex_dollar = config_templ->scan_hex_dollar;
+ scanner->config->scan_string_sq = config_templ->scan_string_sq;
+ scanner->config->scan_string_dq = config_templ->scan_string_dq;
+ scanner->config->numbers_2_int = config_templ->numbers_2_int;
+ scanner->config->int_2_float = config_templ->int_2_float;
+ scanner->config->identifier_2_string = config_templ->identifier_2_string;
+ scanner->config->char_2_token = config_templ->char_2_token;
+ scanner->config->symbol_2_token = config_templ->symbol_2_token;
+ scanner->config->scope_0_fallback = config_templ->scope_0_fallback;
+ scanner->config->store_int64 = config_templ->store_int64;
+
+ scanner->token = G_TOKEN_NONE;
+ scanner->value.v_int64 = 0;
+ scanner->line = 1;
+ scanner->position = 0;
+
+ scanner->next_token = G_TOKEN_NONE;
+ scanner->next_value.v_int64 = 0;
+ scanner->next_line = 1;
+ scanner->next_position = 0;
+
+ scanner->symbol_table = g_hash_table_new (json_scanner_key_hash,
+ json_scanner_key_equal);
+ scanner->input_fd = -1;
+ scanner->text = NULL;
+ scanner->text_end = NULL;
+ scanner->buffer = NULL;
+ scanner->scope_id = 0;
+
+ scanner->msg_handler = json_scanner_msg_handler;
+
+ return scanner;
+}
+
+static inline void
+json_scanner_free_value (GTokenType *token_p,
+ GTokenValue *value_p)
+{
+ switch (*token_p)
+ {
+ case G_TOKEN_STRING:
+ case G_TOKEN_IDENTIFIER:
+ case G_TOKEN_IDENTIFIER_NULL:
+ case G_TOKEN_COMMENT_SINGLE:
+ case G_TOKEN_COMMENT_MULTI:
+ g_free (value_p->v_string);
+ break;
+
+ default:
+ break;
+ }
+
+ *token_p = G_TOKEN_NONE;
+}
+
+static void
+json_scanner_destroy_symbol_table_entry (gpointer _key,
+ gpointer _value,
+ gpointer _data)
+{
+ JsonScannerKey *key = _key;
+
+ g_free (key->symbol);
+ g_slice_free (JsonScannerKey, key);
+}
+
+void
+json_scanner_destroy (JsonScanner *scanner)
+{
+ g_return_if_fail (scanner != NULL);
+
+ g_datalist_clear (&scanner->qdata);
+ g_hash_table_foreach (scanner->symbol_table,
+ json_scanner_destroy_symbol_table_entry,
+ NULL);
+ g_hash_table_destroy (scanner->symbol_table);
+ json_scanner_free_value (&scanner->token, &scanner->value);
+ json_scanner_free_value (&scanner->next_token, &scanner->next_value);
+ g_free (scanner->config);
+ g_free (scanner->buffer);
+ g_free (scanner);
+}
+
+static void
+json_scanner_msg_handler (JsonScanner *scanner,
+ gchar *message,
+ gboolean is_error)
+{
+ g_return_if_fail (scanner != NULL);
+
+ g_fprintf (stderr, "%s:%d: ",
+ scanner->input_name ? scanner->input_name : "<memory>",
+ scanner->line);
+ if (is_error)
+ g_fprintf (stderr, "error: ");
+
+ g_fprintf (stderr, "%s\n", message);
+}
+
+void
+json_scanner_error (JsonScanner *scanner,
+ const gchar *format,
+ ...)
+{
+ g_return_if_fail (scanner != NULL);
+ g_return_if_fail (format != NULL);
+
+ scanner->parse_errors++;
+
+ if (scanner->msg_handler)
+ {
+ va_list args;
+ gchar *string;
+
+ va_start (args, format);
+ string = g_strdup_vprintf (format, args);
+ va_end (args);
+
+ scanner->msg_handler (scanner, string, TRUE);
+
+ g_free (string);
+ }
+}
+
+void
+json_scanner_warn (JsonScanner *scanner,
+ const gchar *format,
+ ...)
+{
+ g_return_if_fail (scanner != NULL);
+ g_return_if_fail (format != NULL);
+
+ if (scanner->msg_handler)
+ {
+ va_list args;
+ gchar *string;
+
+ va_start (args, format);
+ string = g_strdup_vprintf (format, args);
+ va_end (args);
+
+ scanner->msg_handler (scanner, string, FALSE);
+
+ g_free (string);
+ }
+}
+
+static gboolean
+json_scanner_key_equal (gconstpointer v1,
+ gconstpointer v2)
+{
+ const JsonScannerKey *key1 = v1;
+ const JsonScannerKey *key2 = v2;
+
+ return (key1->scope_id == key2->scope_id) &&
+ (strcmp (key1->symbol, key2->symbol) == 0);
+}
+
+static guint
+json_scanner_key_hash (gconstpointer v)
+{
+ const JsonScannerKey *key = v;
+ gchar *c;
+ guint h;
+
+ h = key->scope_id;
+ for (c = key->symbol; *c; c++)
+ h = (h << 5) - h + *c;
+
+ return h;
+}
+
+static inline JsonScannerKey *
+json_scanner_lookup_internal (JsonScanner *scanner,
+ guint scope_id,
+ const gchar *symbol)
+{
+ JsonScannerKey *key_p;
+ JsonScannerKey key;
+
+ key.scope_id = scope_id;
+
+ if (!scanner->config->case_sensitive)
+ {
+ gchar *d;
+ const gchar *c;
+
+ key.symbol = g_new (gchar, strlen (symbol) + 1);
+ for (d = key.symbol, c = symbol; *c; c++, d++)
+ *d = to_lower (*c);
+ *d = 0;
+ key_p = g_hash_table_lookup (scanner->symbol_table, &key);
+ g_free (key.symbol);
+ }
+ else
+ {
+ key.symbol = (gchar*) symbol;
+ key_p = g_hash_table_lookup (scanner->symbol_table, &key);
+ }
+
+ return key_p;
+}
+
+void
+json_scanner_scope_add_symbol (JsonScanner *scanner,
+ guint scope_id,
+ const gchar *symbol,
+ gpointer value)
+{
+ JsonScannerKey *key;
+
+ g_return_if_fail (scanner != NULL);
+ g_return_if_fail (symbol != NULL);
+
+ key = json_scanner_lookup_internal (scanner, scope_id, symbol);
+ if (!key)
+ {
+ key = g_slice_new (JsonScannerKey);
+ key->scope_id = scope_id;
+ key->symbol = g_strdup (symbol);
+ key->value = value;
+ if (!scanner->config->case_sensitive)
+ {
+ gchar *c;
+
+ c = key->symbol;
+ while (*c != 0)
+ {
+ *c = to_lower (*c);
+ c++;
+ }
+ }
+
+ g_hash_table_insert (scanner->symbol_table, key, key);
+ }
+ else
+ key->value = value;
+}
+
+void
+json_scanner_scope_remove_symbol (JsonScanner *scanner,
+ guint scope_id,
+ const gchar *symbol)
+{
+ JsonScannerKey *key;
+
+ g_return_if_fail (scanner != NULL);
+ g_return_if_fail (symbol != NULL);
+
+ key = json_scanner_lookup_internal (scanner, scope_id, symbol);
+ if (key)
+ {
+ g_hash_table_remove (scanner->symbol_table, key);
+ g_free (key->symbol);
+ g_slice_free (JsonScannerKey, key);
+ }
+}
+
+gpointer
+json_scanner_lookup_symbol (JsonScanner *scanner,
+ const gchar *symbol)
+{
+ JsonScannerKey *key;
+ guint scope_id;
+
+ g_return_val_if_fail (scanner != NULL, NULL);
+
+ if (!symbol)
+ return NULL;
+
+ scope_id = scanner->scope_id;
+ key = json_scanner_lookup_internal (scanner, scope_id, symbol);
+ if (!key && scope_id && scanner->config->scope_0_fallback)
+ key = json_scanner_lookup_internal (scanner, 0, symbol);
+
+ if (key)
+ return key->value;
+ else
+ return NULL;
+}
+
+gpointer
+json_scanner_scope_lookup_symbol (JsonScanner *scanner,
+ guint scope_id,
+ const gchar *symbol)
+{
+ JsonScannerKey *key;
+
+ g_return_val_if_fail (scanner != NULL, NULL);
+
+ if (!symbol)
+ return NULL;
+
+ key = json_scanner_lookup_internal (scanner, scope_id, symbol);
+
+ if (key)
+ return key->value;
+ else
+ return NULL;
+}
+
+guint
+json_scanner_set_scope (JsonScanner *scanner,
+ guint scope_id)
+{
+ guint old_scope_id;
+
+ g_return_val_if_fail (scanner != NULL, 0);
+
+ old_scope_id = scanner->scope_id;
+ scanner->scope_id = scope_id;
+
+ return old_scope_id;
+}
+
+typedef struct {
+ GHFunc func;
+ gpointer data;
+ guint scope_id;
+} ForeachClosure;
+
+static void
+json_scanner_foreach_internal (gpointer _key,
+ gpointer _value,
+ gpointer _user_data)
+{
+ JsonScannerKey *key = _value;
+ ForeachClosure *closure = _user_data;
+
+ if (key->scope_id == closure->scope_id)
+ closure->func (key->symbol, key->value, closure->data);
+}
+
+void
+json_scanner_scope_foreach_symbol (JsonScanner *scanner,
+ guint scope_id,
+ GHFunc func,
+ gpointer user_data)
+{
+ ForeachClosure closure;
+
+ g_return_if_fail (scanner != NULL);
+ g_return_if_fail (func != NULL);
+
+ closure.func = func;
+ closure.data = user_data;
+ closure.scope_id = scope_id;
+
+ g_hash_table_foreach (scanner->symbol_table,
+ json_scanner_foreach_internal,
+ &closure);
+}
+
+GTokenType
+json_scanner_peek_next_token (JsonScanner *scanner)
+{
+ g_return_val_if_fail (scanner != NULL, G_TOKEN_EOF);
+
+ if (scanner->next_token == G_TOKEN_NONE)
+ {
+ scanner->next_line = scanner->line;
+ scanner->next_position = scanner->position;
+ json_scanner_get_token_i (scanner,
+ &scanner->next_token,
+ &scanner->next_value,
+ &scanner->next_line,
+ &scanner->next_position);
+ }
+
+ return scanner->next_token;
+}
+
+GTokenType
+json_scanner_get_next_token (JsonScanner *scanner)
+{
+ g_return_val_if_fail (scanner != NULL, G_TOKEN_EOF);
+
+ if (scanner->next_token != G_TOKEN_NONE)
+ {
+ json_scanner_free_value (&scanner->token, &scanner->value);
+
+ scanner->token = scanner->next_token;
+ scanner->value = scanner->next_value;
+ scanner->line = scanner->next_line;
+ scanner->position = scanner->next_position;
+ scanner->next_token = G_TOKEN_NONE;
+ }
+ else
+ json_scanner_get_token_i (scanner,
+ &scanner->token,
+ &scanner->value,
+ &scanner->line,
+ &scanner->position);
+
+ return scanner->token;
+}
+
+GTokenType
+json_scanner_cur_token (JsonScanner *scanner)
+{
+ g_return_val_if_fail (scanner != NULL, G_TOKEN_EOF);
+
+ return scanner->token;
+}
+
+GTokenValue
+json_scanner_cur_value (JsonScanner *scanner)
+{
+ GTokenValue v;
+
+ v.v_int64 = 0;
+
+ g_return_val_if_fail (scanner != NULL, v);
+
+ /* MSC isn't capable of handling return scanner->value; ? */
+
+ v = scanner->value;
+
+ return v;
+}
+
+guint
+json_scanner_cur_line (JsonScanner *scanner)
+{
+ g_return_val_if_fail (scanner != NULL, 0);
+
+ return scanner->line;
+}
+
+guint
+json_scanner_cur_position (JsonScanner *scanner)
+{
+ g_return_val_if_fail (scanner != NULL, 0);
+
+ return scanner->position;
+}
+
+gboolean
+json_scanner_eof (JsonScanner *scanner)
+{
+ g_return_val_if_fail (scanner != NULL, TRUE);
+
+ return scanner->token == G_TOKEN_EOF || scanner->token == G_TOKEN_ERROR;
+}
+
+void
+json_scanner_input_file (JsonScanner *scanner,
+ gint input_fd)
+{
+ g_return_if_fail (scanner != NULL);
+ g_return_if_fail (input_fd >= 0);
+
+ if (scanner->input_fd >= 0)
+ json_scanner_sync_file_offset (scanner);
+
+ scanner->token = G_TOKEN_NONE;
+ scanner->value.v_int64 = 0;
+ scanner->line = 1;
+ scanner->position = 0;
+ scanner->next_token = G_TOKEN_NONE;
+
+ scanner->input_fd = input_fd;
+ scanner->text = NULL;
+ scanner->text_end = NULL;
+
+ if (!scanner->buffer)
+ scanner->buffer = g_new (gchar, READ_BUFFER_SIZE + 1);
+}
+
+void
+json_scanner_input_text (JsonScanner *scanner,
+ const gchar *text,
+ guint text_len)
+{
+ g_return_if_fail (scanner != NULL);
+ if (text_len)
+ g_return_if_fail (text != NULL);
+ else
+ text = NULL;
+
+ if (scanner->input_fd >= 0)
+ json_scanner_sync_file_offset (scanner);
+
+ scanner->token = G_TOKEN_NONE;
+ scanner->value.v_int64 = 0;
+ scanner->line = 1;
+ scanner->position = 0;
+ scanner->next_token = G_TOKEN_NONE;
+
+ scanner->input_fd = -1;
+ scanner->text = text;
+ scanner->text_end = text + text_len;
+
+ if (scanner->buffer)
+ {
+ g_free (scanner->buffer);
+ scanner->buffer = NULL;
+ }
+}
+
+static guchar
+json_scanner_peek_next_char (JsonScanner *scanner)
+{
+ if (scanner->text < scanner->text_end)
+ return *scanner->text;
+ else if (scanner->input_fd >= 0)
+ {
+ gint count;
+ gchar *buffer;
+
+ buffer = scanner->buffer;
+ do
+ {
+ count = read (scanner->input_fd, buffer, READ_BUFFER_SIZE);
+ }
+ while (count == -1 && (errno == EINTR || errno == EAGAIN));
+
+ if (count < 1)
+ {
+ scanner->input_fd = -1;
+
+ return 0;
+ }
+ else
+ {
+ scanner->text = buffer;
+ scanner->text_end = buffer + count;
+
+ return *buffer;
+ }
+ }
+ else
+ return 0;
+}
+
+void
+json_scanner_sync_file_offset (JsonScanner *scanner)
+{
+ g_return_if_fail (scanner != NULL);
+
+ /* for file input, rewind the filedescriptor to the current
+ * buffer position and blow the file read ahead buffer. useful
+ * for third party uses of our file descriptor, which hooks
+ * onto the current scanning position.
+ */
+
+ if (scanner->input_fd >= 0 && scanner->text_end > scanner->text)
+ {
+ gint buffered;
+
+ buffered = scanner->text_end - scanner->text;
+ if (lseek (scanner->input_fd, - buffered, SEEK_CUR) >= 0)
+ {
+ /* we succeeded, blow our buffer's contents now */
+ scanner->text = NULL;
+ scanner->text_end = NULL;
+ }
+ else
+ errno = 0;
+ }
+}
+
+static guchar
+json_scanner_get_char (JsonScanner *scanner,
+ guint *line_p,
+ guint *position_p)
+{
+ guchar fchar;
+
+ if (scanner->text < scanner->text_end)
+ fchar = *(scanner->text++);
+ else if (scanner->input_fd >= 0)
+ {
+ gint count;
+ gchar *buffer;
+
+ buffer = scanner->buffer;
+ do
+ {
+ count = read (scanner->input_fd, buffer, READ_BUFFER_SIZE);
+ }
+ while (count == -1 && (errno == EINTR || errno == EAGAIN));
+
+ if (count < 1)
+ {
+ scanner->input_fd = -1;
+ fchar = 0;
+ }
+ else
+ {
+ scanner->text = buffer + 1;
+ scanner->text_end = buffer + count;
+ fchar = *buffer;
+ if (!fchar)
+ {
+ json_scanner_sync_file_offset (scanner);
+ scanner->text_end = scanner->text;
+ scanner->input_fd = -1;
+ }
+ }
+ }
+ else
+ fchar = 0;
+
+ if (fchar == '\n')
+ {
+ (*position_p) = 0;
+ (*line_p)++;
+ }
+ else if (fchar)
+ {
+ (*position_p)++;
+ }
+
+ return fchar;
+}
+
+#define is_hex_digit(c) (((c) >= '0' && (c) <= '9') || \
+ ((c) >= 'a' && (c) <= 'f') || \
+ ((c) >= 'A' && (c) <= 'F'))
+#define to_hex_digit(c) (((c) <= '9') ? (c) - '0' : ((c) & 7) + 9)
+
+static gunichar
+json_scanner_get_unichar (JsonScanner *scanner,
+ guint *line_p,
+ guint *position_p)
+{
+ gunichar uchar;
+ gchar ch;
+ gint i;
+
+ uchar = 0;
+ for (i = 0; i < 4; i++)
+ {
+ ch = json_scanner_get_char (scanner, line_p, position_p);
+
+ if (is_hex_digit (ch))
+ uchar += ((gunichar) to_hex_digit (ch) << ((3 - i) * 4));
+ else
+ break;
+ }
+
+ g_assert (g_unichar_validate (uchar));
+
+ return uchar;
+}
+
+void
+json_scanner_unexp_token (JsonScanner *scanner,
+ GTokenType expected_token,
+ const gchar *identifier_spec,
+ const gchar *symbol_spec,
+ const gchar *symbol_name,
+ const gchar *message,
+ gint is_error)
+{
+ gchar *token_string;
+ guint token_string_len;
+ gchar *expected_string;
+ guint expected_string_len;
+ gchar *message_prefix;
+ gboolean print_unexp;
+ void (*msg_handler) (JsonScanner*, const gchar*, ...);
+
+ g_return_if_fail (scanner != NULL);
+
+ if (is_error)
+ msg_handler = json_scanner_error;
+ else
+ msg_handler = json_scanner_warn;
+
+ if (!identifier_spec)
+ identifier_spec = "identifier";
+ if (!symbol_spec)
+ symbol_spec = "symbol";
+
+ token_string_len = 56;
+ token_string = g_new (gchar, token_string_len + 1);
+ expected_string_len = 64;
+ expected_string = g_new (gchar, expected_string_len + 1);
+ print_unexp = TRUE;
+
+ switch (scanner->token)
+ {
+ case G_TOKEN_EOF:
+ g_snprintf (token_string, token_string_len, "end of file");
+ break;
+
+ default:
+ if (scanner->token >= 1 && scanner->token <= 255)
+ {
+ if ((scanner->token >= ' ' && scanner->token <= '~') ||
+ strchr (scanner->config->cset_identifier_first, scanner->token) ||
+ strchr (scanner->config->cset_identifier_nth, scanner->token))
+ g_snprintf (token_string, token_string_len, "character `%c'", scanner->token);
+ else
+ g_snprintf (token_string, token_string_len, "character `\\%o'", scanner->token);
+ break;
+ }
+ else if (!scanner->config->symbol_2_token)
+ {
+ g_snprintf (token_string, token_string_len, "(unknown) token <%d>", scanner->token);
+ break;
+ }
+ /* fall through */
+ case G_TOKEN_SYMBOL:
+ if (expected_token == G_TOKEN_SYMBOL ||
+ (scanner->config->symbol_2_token &&
+ expected_token > G_TOKEN_LAST))
+ print_unexp = FALSE;
+ if (symbol_name)
+ g_snprintf (token_string, token_string_len,
+ "%s%s `%s'",
+ print_unexp ? "" : "invalid ",
+ symbol_spec,
+ symbol_name);
+ else
+ g_snprintf (token_string, token_string_len,
+ "%s%s",
+ print_unexp ? "" : "invalid ",
+ symbol_spec);
+ break;
+
+ case G_TOKEN_ERROR:
+ print_unexp = FALSE;
+ expected_token = G_TOKEN_NONE;
+ switch (scanner->value.v_error)
+ {
+ case G_ERR_UNEXP_EOF:
+ g_snprintf (token_string, token_string_len, "scanner: unexpected end of file");
+ break;
+
+ case G_ERR_UNEXP_EOF_IN_STRING:
+ g_snprintf (token_string, token_string_len, "scanner: unterminated string constant");
+ break;
+
+ case G_ERR_UNEXP_EOF_IN_COMMENT:
+ g_snprintf (token_string, token_string_len, "scanner: unterminated comment");
+ break;
+
+ case G_ERR_NON_DIGIT_IN_CONST:
+ g_snprintf (token_string, token_string_len, "scanner: non digit in constant");
+ break;
+
+ case G_ERR_FLOAT_RADIX:
+ g_snprintf (token_string, token_string_len, "scanner: invalid radix for floating constant");
+ break;
+
+ case G_ERR_FLOAT_MALFORMED:
+ g_snprintf (token_string, token_string_len, "scanner: malformed floating constant");
+ break;
+
+ case G_ERR_DIGIT_RADIX:
+ g_snprintf (token_string, token_string_len, "scanner: digit is beyond radix");
+ break;
+
+ case G_ERR_UNKNOWN:
+ default:
+ g_snprintf (token_string, token_string_len, "scanner: unknown error");
+ break;
+ }
+ break;
+
+ case G_TOKEN_CHAR:
+ g_snprintf (token_string, token_string_len, "character `%c'", scanner->value.v_char);
+ break;
+
+ case G_TOKEN_IDENTIFIER:
+ case G_TOKEN_IDENTIFIER_NULL:
+ if (expected_token == G_TOKEN_IDENTIFIER ||
+ expected_token == G_TOKEN_IDENTIFIER_NULL)
+ print_unexp = FALSE;
+ g_snprintf (token_string, token_string_len,
+ "%s%s `%s'",
+ print_unexp ? "" : "invalid ",
+ identifier_spec,
+ scanner->token == G_TOKEN_IDENTIFIER ? scanner->value.v_string : "null");
+ break;
+
+ case G_TOKEN_BINARY:
+ case G_TOKEN_OCTAL:
+ case G_TOKEN_INT:
+ case G_TOKEN_HEX:
+ if (scanner->config->store_int64)
+ g_snprintf (token_string, token_string_len, "number `%" G_GUINT64_FORMAT "'", scanner->value.v_int64);
+ else
+ g_snprintf (token_string, token_string_len, "number `%lu'", scanner->value.v_int);
+ break;
+
+ case G_TOKEN_FLOAT:
+ g_snprintf (token_string, token_string_len, "number `%.3f'", scanner->value.v_float);
+ break;
+
+ case G_TOKEN_STRING:
+ if (expected_token == G_TOKEN_STRING)
+ print_unexp = FALSE;
+ g_snprintf (token_string, token_string_len,
+ "%s%sstring constant \"%s\"",
+ print_unexp ? "" : "invalid ",
+ scanner->value.v_string[0] == 0 ? "empty " : "",
+ scanner->value.v_string);
+ token_string[token_string_len - 2] = '"';
+ token_string[token_string_len - 1] = 0;
+ break;
+
+ case G_TOKEN_COMMENT_SINGLE:
+ case G_TOKEN_COMMENT_MULTI:
+ g_snprintf (token_string, token_string_len, "comment");
+ break;
+
+ case G_TOKEN_NONE:
+ /* somehow the user's parsing code is screwed, there isn't much
+ * we can do about it.
+ * Note, a common case to trigger this is
+ * json_scanner_peek_next_token(); json_scanner_unexp_token();
+ * without an intermediate json_scanner_get_next_token().
+ */
+ g_assert_not_reached ();
+ break;
+ }
+
+
+ switch (expected_token)
+ {
+ gboolean need_valid;
+ gchar *tstring;
+ case G_TOKEN_EOF:
+ g_snprintf (expected_string, expected_string_len, "end of file");
+ break;
+ default:
+ if (expected_token >= 1 && expected_token <= 255)
+ {
+ if ((expected_token >= ' ' && expected_token <= '~') ||
+ strchr (scanner->config->cset_identifier_first, expected_token) ||
+ strchr (scanner->config->cset_identifier_nth, expected_token))
+ g_snprintf (expected_string, expected_string_len, "character `%c'", expected_token);
+ else
+ g_snprintf (expected_string, expected_string_len, "character `\\%o'", expected_token);
+ break;
+ }
+ else if (!scanner->config->symbol_2_token)
+ {
+ g_snprintf (expected_string, expected_string_len, "(unknown) token <%d>", expected_token);
+ break;
+ }
+ /* fall through */
+ case G_TOKEN_SYMBOL:
+ need_valid = (scanner->token == G_TOKEN_SYMBOL ||
+ (scanner->config->symbol_2_token &&
+ scanner->token > G_TOKEN_LAST));
+ g_snprintf (expected_string, expected_string_len,
+ "%s%s",
+ need_valid ? "valid " : "",
+ symbol_spec);
+ /* FIXME: should we attempt to lookup the symbol_name for symbol_2_token? */
+ break;
+ case G_TOKEN_CHAR:
+ g_snprintf (expected_string, expected_string_len, "%scharacter",
+ scanner->token == G_TOKEN_CHAR ? "valid " : "");
+ break;
+ case G_TOKEN_BINARY:
+ tstring = "binary";
+ g_snprintf (expected_string, expected_string_len, "%snumber (%s)",
+ scanner->token == expected_token ? "valid " : "", tstring);
+ break;
+ case G_TOKEN_OCTAL:
+ tstring = "octal";
+ g_snprintf (expected_string, expected_string_len, "%snumber (%s)",
+ scanner->token == expected_token ? "valid " : "", tstring);
+ break;
+ case G_TOKEN_INT:
+ tstring = "integer";
+ g_snprintf (expected_string, expected_string_len, "%snumber (%s)",
+ scanner->token == expected_token ? "valid " : "", tstring);
+ break;
+ case G_TOKEN_HEX:
+ tstring = "hexadecimal";
+ g_snprintf (expected_string, expected_string_len, "%snumber (%s)",
+ scanner->token == expected_token ? "valid " : "", tstring);
+ break;
+ case G_TOKEN_FLOAT:
+ tstring = "float";
+ g_snprintf (expected_string, expected_string_len, "%snumber (%s)",
+ scanner->token == expected_token ? "valid " : "", tstring);
+ break;
+ case G_TOKEN_STRING:
+ g_snprintf (expected_string,
+ expected_string_len,
+ "%sstring constant",
+ scanner->token == G_TOKEN_STRING ? "valid " : "");
+ break;
+ case G_TOKEN_IDENTIFIER:
+ case G_TOKEN_IDENTIFIER_NULL:
+ need_valid = (scanner->token == G_TOKEN_IDENTIFIER_NULL ||
+ scanner->token == G_TOKEN_IDENTIFIER);
+ g_snprintf (expected_string,
+ expected_string_len,
+ "%s%s",
+ need_valid ? "valid " : "",
+ identifier_spec);
+ break;
+ case G_TOKEN_COMMENT_SINGLE:
+ tstring = "single-line";
+ g_snprintf (expected_string, expected_string_len, "%scomment (%s)",
+ scanner->token == expected_token ? "valid " : "", tstring);
+ break;
+ case G_TOKEN_COMMENT_MULTI:
+ tstring = "multi-line";
+ g_snprintf (expected_string, expected_string_len, "%scomment (%s)",
+ scanner->token == expected_token ? "valid " : "", tstring);
+ break;
+ case G_TOKEN_NONE:
+ case G_TOKEN_ERROR:
+ /* this is handled upon printout */
+ break;
+ }
+
+ if (message && message[0] != 0)
+ message_prefix = " - ";
+ else
+ {
+ message_prefix = "";
+ message = "";
+ }
+ if (expected_token == G_TOKEN_ERROR)
+ {
+ msg_handler (scanner,
+ "failure around %s%s%s",
+ token_string,
+ message_prefix,
+ message);
+ }
+ else if (expected_token == G_TOKEN_NONE)
+ {
+ if (print_unexp)
+ msg_handler (scanner,
+ "unexpected %s%s%s",
+ token_string,
+ message_prefix,
+ message);
+ else
+ msg_handler (scanner,
+ "%s%s%s",
+ token_string,
+ message_prefix,
+ message);
+ }
+ else
+ {
+ if (print_unexp)
+ msg_handler (scanner,
+ "unexpected %s, expected %s%s%s",
+ token_string,
+ expected_string,
+ message_prefix,
+ message);
+ else
+ msg_handler (scanner,
+ "%s, expected %s%s%s",
+ token_string,
+ expected_string,
+ message_prefix,
+ message);
+ }
+
+ g_free (token_string);
+ g_free (expected_string);
+}
+
+static void
+json_scanner_get_token_i (JsonScanner *scanner,
+ GTokenType *token_p,
+ GTokenValue *value_p,
+ guint *line_p,
+ guint *position_p)
+{
+ do
+ {
+ json_scanner_free_value (token_p, value_p);
+ json_scanner_get_token_ll (scanner, token_p, value_p, line_p, position_p);
+ }
+ while (((*token_p > 0 && *token_p < 256) &&
+ strchr (scanner->config->cset_skip_characters, *token_p)) ||
+ (*token_p == G_TOKEN_CHAR &&
+ strchr (scanner->config->cset_skip_characters, value_p->v_char)) ||
+ (*token_p == G_TOKEN_COMMENT_MULTI &&
+ scanner->config->skip_comment_multi) ||
+ (*token_p == G_TOKEN_COMMENT_SINGLE &&
+ scanner->config->skip_comment_single));
+
+ switch (*token_p)
+ {
+ case G_TOKEN_IDENTIFIER:
+ if (scanner->config->identifier_2_string)
+ *token_p = G_TOKEN_STRING;
+ break;
+
+ case G_TOKEN_SYMBOL:
+ if (scanner->config->symbol_2_token)
+ *token_p = (GTokenType) value_p->v_symbol;
+ break;
+
+ case G_TOKEN_BINARY:
+ case G_TOKEN_OCTAL:
+ case G_TOKEN_HEX:
+ if (scanner->config->numbers_2_int)
+ *token_p = G_TOKEN_INT;
+ break;
+
+ default:
+ break;
+ }
+
+ if (*token_p == G_TOKEN_INT &&
+ scanner->config->int_2_float)
+ {
+ *token_p = G_TOKEN_FLOAT;
+ if (scanner->config->store_int64)
+ {
+#ifdef _MSC_VER
+ /* work around error C2520, see gvaluetransform.c */
+ value_p->v_float = (__int64)value_p->v_int64;
+#else
+ value_p->v_float = value_p->v_int64;
+#endif
+ }
+ else
+ value_p->v_float = value_p->v_int;
+ }
+
+ errno = 0;
+}
+
+static void
+json_scanner_get_token_ll (JsonScanner *scanner,
+ GTokenType *token_p,
+ GTokenValue *value_p,
+ guint *line_p,
+ guint *position_p)
+{
+ JsonScannerConfig *config;
+ GTokenType token;
+ gboolean in_comment_multi;
+ gboolean in_comment_single;
+ gboolean in_string_sq;
+ gboolean in_string_dq;
+ GString *gstring;
+ GTokenValue value;
+ guchar ch;
+
+ config = scanner->config;
+ (*value_p).v_int64 = 0;
+
+ if ((scanner->text >= scanner->text_end && scanner->input_fd < 0) ||
+ scanner->token == G_TOKEN_EOF)
+ {
+ *token_p = G_TOKEN_EOF;
+ return;
+ }
+
+ in_comment_multi = FALSE;
+ in_comment_single = FALSE;
+ in_string_sq = FALSE;
+ in_string_dq = FALSE;
+ gstring = NULL;
+
+ do /* while (ch != 0) */
+ {
+ gboolean dotted_float = FALSE;
+
+ ch = json_scanner_get_char (scanner, line_p, position_p);
+
+ value.v_int64 = 0;
+ token = G_TOKEN_NONE;
+
+ /* this is *evil*, but needed ;(
+ * we first check for identifier first character, because it
+ * might interfere with other key chars like slashes or numbers
+ */
+ if (config->scan_identifier &&
+ ch && strchr (config->cset_identifier_first, ch))
+ goto identifier_precedence;
+
+ switch (ch)
+ {
+ case 0:
+ token = G_TOKEN_EOF;
+ (*position_p)++;
+ /* ch = 0; */
+ break;
+
+ case '/':
+ if (!config->scan_comment_multi ||
+ json_scanner_peek_next_char (scanner) != '*')
+ goto default_case;
+ json_scanner_get_char (scanner, line_p, position_p);
+ token = G_TOKEN_COMMENT_MULTI;
+ in_comment_multi = TRUE;
+ gstring = g_string_new (NULL);
+ while ((ch = json_scanner_get_char (scanner, line_p, position_p)) != 0)
+ {
+ if (ch == '*' && json_scanner_peek_next_char (scanner) == '/')
+ {
+ json_scanner_get_char (scanner, line_p, position_p);
+ in_comment_multi = FALSE;
+ break;
+ }
+ else
+ gstring = g_string_append_c (gstring, ch);
+ }
+ ch = 0;
+ break;
+
+ case '\'':
+ if (!config->scan_string_sq)
+ goto default_case;
+ token = G_TOKEN_STRING;
+ in_string_sq = TRUE;
+ gstring = g_string_new (NULL);
+ while ((ch = json_scanner_get_char (scanner, line_p, position_p)) != 0)
+ {
+ if (ch == '\'')
+ {
+ in_string_sq = FALSE;
+ break;
+ }
+ else
+ gstring = g_string_append_c (gstring, ch);
+ }
+ ch = 0;
+ break;
+
+ case '"':
+ if (!config->scan_string_dq)
+ goto default_case;
+ token = G_TOKEN_STRING;
+ in_string_dq = TRUE;
+ gstring = g_string_new (NULL);
+ while ((ch = json_scanner_get_char (scanner, line_p, position_p)) != 0)
+ {
+ if (ch == '"')
+ {
+ in_string_dq = FALSE;
+ break;
+ }
+ else
+ {
+ if (ch == '\\')
+ {
+ ch = json_scanner_get_char (scanner, line_p, position_p);
+ switch (ch)
+ {
+ guint i;
+ guint fchar;
+
+ case 0:
+ break;
+
+ case '\\':
+ gstring = g_string_append_c (gstring, '\\');
+ break;
+
+ case 'n':
+ gstring = g_string_append_c (gstring, '\n');
+ break;
+
+ case 't':
+ gstring = g_string_append_c (gstring, '\t');
+ break;
+
+ case 'r':
+ gstring = g_string_append_c (gstring, '\r');
+ break;
+
+ case 'b':
+ gstring = g_string_append_c (gstring, '\b');
+ break;
+
+ case 'f':
+ gstring = g_string_append_c (gstring, '\f');
+ break;
+
+ case 'u':
+ fchar = json_scanner_peek_next_char (scanner);
+ if (is_hex_digit (fchar))
+ {
+ gunichar ucs;
+
+ ucs = json_scanner_get_unichar (scanner, line_p, position_p);
+ gstring = g_string_append_unichar (gstring, ucs);
+ }
+ break;
+
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ i = ch - '0';
+ fchar = json_scanner_peek_next_char (scanner);
+ if (fchar >= '0' && fchar <= '7')
+ {
+ ch = json_scanner_get_char (scanner, line_p, position_p);
+ i = i * 8 + ch - '0';
+ fchar = json_scanner_peek_next_char (scanner);
+ if (fchar >= '0' && fchar <= '7')
+ {
+ ch = json_scanner_get_char (scanner, line_p, position_p);
+ i = i * 8 + ch - '0';
+ }
+ }
+ gstring = g_string_append_c (gstring, i);
+ break;
+
+ default:
+ gstring = g_string_append_c (gstring, ch);
+ break;
+ }
+ }
+ else
+ gstring = g_string_append_c (gstring, ch);
+ }
+ }
+ ch = 0;
+ break;
+
+ case '.':
+ if (!config->scan_float)
+ goto default_case;
+ token = G_TOKEN_FLOAT;
+ dotted_float = TRUE;
+ ch = json_scanner_get_char (scanner, line_p, position_p);
+ goto number_parsing;
+
+ case '$':
+ if (!config->scan_hex_dollar)
+ goto default_case;
+ token = G_TOKEN_HEX;
+ ch = json_scanner_get_char (scanner, line_p, position_p);
+ goto number_parsing;
+
+ case '0':
+ if (config->scan_octal)
+ token = G_TOKEN_OCTAL;
+ else
+ token = G_TOKEN_INT;
+ ch = json_scanner_peek_next_char (scanner);
+ if (config->scan_hex && (ch == 'x' || ch == 'X'))
+ {
+ token = G_TOKEN_HEX;
+ json_scanner_get_char (scanner, line_p, position_p);
+ ch = json_scanner_get_char (scanner, line_p, position_p);
+ if (ch == 0)
+ {
+ token = G_TOKEN_ERROR;
+ value.v_error = G_ERR_UNEXP_EOF;
+ (*position_p)++;
+ break;
+ }
+ if (json_scanner_char_2_num (ch, 16) < 0)
+ {
+ token = G_TOKEN_ERROR;
+ value.v_error = G_ERR_DIGIT_RADIX;
+ ch = 0;
+ break;
+ }
+ }
+ else if (config->scan_binary && (ch == 'b' || ch == 'B'))
+ {
+ token = G_TOKEN_BINARY;
+ json_scanner_get_char (scanner, line_p, position_p);
+ ch = json_scanner_get_char (scanner, line_p, position_p);
+ if (ch == 0)
+ {
+ token = G_TOKEN_ERROR;
+ value.v_error = G_ERR_UNEXP_EOF;
+ (*position_p)++;
+ break;
+ }
+ if (json_scanner_char_2_num (ch, 10) < 0)
+ {
+ token = G_TOKEN_ERROR;
+ value.v_error = G_ERR_NON_DIGIT_IN_CONST;
+ ch = 0;
+ break;
+ }
+ }
+ else
+ ch = '0';
+ /* fall through */
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ number_parsing:
+ {
+ gboolean in_number = TRUE;
+ gchar *endptr;
+
+ if (token == G_TOKEN_NONE)
+ token = G_TOKEN_INT;
+
+ gstring = g_string_new (dotted_float ? "0." : "");
+ gstring = g_string_append_c (gstring, ch);
+
+ do /* while (in_number) */
+ {
+ gboolean is_E;
+
+ is_E = token == G_TOKEN_FLOAT && (ch == 'e' || ch == 'E');
+
+ ch = json_scanner_peek_next_char (scanner);
+
+ if (json_scanner_char_2_num (ch, 36) >= 0 ||
+ (config->scan_float && ch == '.') ||
+ (is_E && (ch == '+' || ch == '-')))
+ {
+ ch = json_scanner_get_char (scanner, line_p, position_p);
+
+ switch (ch)
+ {
+ case '.':
+ if (token != G_TOKEN_INT && token != G_TOKEN_OCTAL)
+ {
+ value.v_error = token == G_TOKEN_FLOAT ? G_ERR_FLOAT_MALFORMED : G_ERR_FLOAT_RADIX;
+ token = G_TOKEN_ERROR;
+ in_number = FALSE;
+ }
+ else
+ {
+ token = G_TOKEN_FLOAT;
+ gstring = g_string_append_c (gstring, ch);
+ }
+ break;
+
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ gstring = g_string_append_c (gstring, ch);
+ break;
+
+ case '-':
+ case '+':
+ if (token != G_TOKEN_FLOAT)
+ {
+ token = G_TOKEN_ERROR;
+ value.v_error = G_ERR_NON_DIGIT_IN_CONST;
+ in_number = FALSE;
+ }
+ else
+ gstring = g_string_append_c (gstring, ch);
+ break;
+
+ case 'e':
+ case 'E':
+ if ((token != G_TOKEN_HEX && !config->scan_float) ||
+ (token != G_TOKEN_HEX &&
+ token != G_TOKEN_OCTAL &&
+ token != G_TOKEN_FLOAT &&
+ token != G_TOKEN_INT))
+ {
+ token = G_TOKEN_ERROR;
+ value.v_error = G_ERR_NON_DIGIT_IN_CONST;
+ in_number = FALSE;
+ }
+ else
+ {
+ if (token != G_TOKEN_HEX)
+ token = G_TOKEN_FLOAT;
+ gstring = g_string_append_c (gstring, ch);
+ }
+ break;
+
+ default:
+ if (token != G_TOKEN_HEX)
+ {
+ token = G_TOKEN_ERROR;
+ value.v_error = G_ERR_NON_DIGIT_IN_CONST;
+ in_number = FALSE;
+ }
+ else
+ gstring = g_string_append_c (gstring, ch);
+ break;
+ }
+ }
+ else
+ in_number = FALSE;
+ }
+ while (in_number);
+
+ endptr = NULL;
+ if (token == G_TOKEN_FLOAT)
+ value.v_float = g_strtod (gstring->str, &endptr);
+ else
+ {
+ guint64 ui64 = 0;
+ switch (token)
+ {
+ case G_TOKEN_BINARY:
+ ui64 = g_ascii_strtoull (gstring->str, &endptr, 2);
+ break;
+ case G_TOKEN_OCTAL:
+ ui64 = g_ascii_strtoull (gstring->str, &endptr, 8);
+ break;
+ case G_TOKEN_INT:
+ ui64 = g_ascii_strtoull (gstring->str, &endptr, 10);
+ break;
+ case G_TOKEN_HEX:
+ ui64 = g_ascii_strtoull (gstring->str, &endptr, 16);
+ break;
+ default: ;
+ }
+ if (scanner->config->store_int64)
+ value.v_int64 = ui64;
+ else
+ value.v_int = ui64;
+ }
+ if (endptr && *endptr)
+ {
+ token = G_TOKEN_ERROR;
+ if (*endptr == 'e' || *endptr == 'E')
+ value.v_error = G_ERR_NON_DIGIT_IN_CONST;
+ else
+ value.v_error = G_ERR_DIGIT_RADIX;
+ }
+ g_string_free (gstring, TRUE);
+ gstring = NULL;
+ ch = 0;
+ } /* number_parsing:... */
+ break;
+
+ default:
+ default_case:
+ {
+ if (config->cpair_comment_single &&
+ ch == config->cpair_comment_single[0])
+ {
+ token = G_TOKEN_COMMENT_SINGLE;
+ in_comment_single = TRUE;
+ gstring = g_string_new (NULL);
+ ch = json_scanner_get_char (scanner, line_p, position_p);
+ while (ch != 0)
+ {
+ if (ch == config->cpair_comment_single[1])
+ {
+ in_comment_single = FALSE;
+ ch = 0;
+ break;
+ }
+
+ gstring = g_string_append_c (gstring, ch);
+ ch = json_scanner_get_char (scanner, line_p, position_p);
+ }
+ /* ignore a missing newline at EOF for single line comments */
+ if (in_comment_single &&
+ config->cpair_comment_single[1] == '\n')
+ in_comment_single = FALSE;
+ }
+ else if (config->scan_identifier && ch &&
+ strchr (config->cset_identifier_first, ch))
+ {
+ identifier_precedence:
+
+ if (config->cset_identifier_nth && ch &&
+ strchr (config->cset_identifier_nth,
+ json_scanner_peek_next_char (scanner)))
+ {
+ token = G_TOKEN_IDENTIFIER;
+ gstring = g_string_new (NULL);
+ gstring = g_string_append_c (gstring, ch);
+ do
+ {
+ ch = json_scanner_get_char (scanner, line_p, position_p);
+ gstring = g_string_append_c (gstring, ch);
+ ch = json_scanner_peek_next_char (scanner);
+ }
+ while (ch && strchr (config->cset_identifier_nth, ch));
+ ch = 0;
+ }
+ else if (config->scan_identifier_1char)
+ {
+ token = G_TOKEN_IDENTIFIER;
+ value.v_identifier = g_new0 (gchar, 2);
+ value.v_identifier[0] = ch;
+ ch = 0;
+ }
+ }
+ if (ch)
+ {
+ if (config->char_2_token)
+ token = ch;
+ else
+ {
+ token = G_TOKEN_CHAR;
+ value.v_char = ch;
+ }
+ ch = 0;
+ }
+ } /* default_case:... */
+ break;
+ }
+ g_assert (ch == 0 && token != G_TOKEN_NONE); /* paranoid */
+ }
+ while (ch != 0);
+
+ if (in_comment_multi || in_comment_single ||
+ in_string_sq || in_string_dq)
+ {
+ token = G_TOKEN_ERROR;
+ if (gstring)
+ {
+ g_string_free (gstring, TRUE);
+ gstring = NULL;
+ }
+ (*position_p)++;
+ if (in_comment_multi || in_comment_single)
+ value.v_error = G_ERR_UNEXP_EOF_IN_COMMENT;
+ else /* (in_string_sq || in_string_dq) */
+ value.v_error = G_ERR_UNEXP_EOF_IN_STRING;
+ }
+
+ if (gstring)
+ {
+ value.v_string = g_string_free (gstring, FALSE);
+ gstring = NULL;
+ }
+
+ if (token == G_TOKEN_IDENTIFIER)
+ {
+ if (config->scan_symbols)
+ {
+ JsonScannerKey *key;
+ guint scope_id;
+
+ scope_id = scanner->scope_id;
+ key = json_scanner_lookup_internal (scanner, scope_id, value.v_identifier);
+ if (!key && scope_id && scanner->config->scope_0_fallback)
+ key = json_scanner_lookup_internal (scanner, 0, value.v_identifier);
+
+ if (key)
+ {
+ g_free (value.v_identifier);
+ token = G_TOKEN_SYMBOL;
+ value.v_symbol = key->value;
+ }
+ }
+
+ if (token == G_TOKEN_IDENTIFIER &&
+ config->scan_identifier_NULL &&
+ strlen (value.v_identifier) == 4)
+ {
+ gchar *null_upper = "NULL";
+ gchar *null_lower = "null";
+
+ if (scanner->config->case_sensitive)
+ {
+ if (value.v_identifier[0] == null_upper[0] &&
+ value.v_identifier[1] == null_upper[1] &&
+ value.v_identifier[2] == null_upper[2] &&
+ value.v_identifier[3] == null_upper[3])
+ token = G_TOKEN_IDENTIFIER_NULL;
+ }
+ else
+ {
+ if ((value.v_identifier[0] == null_upper[0] ||
+ value.v_identifier[0] == null_lower[0]) &&
+ (value.v_identifier[1] == null_upper[1] ||
+ value.v_identifier[1] == null_lower[1]) &&
+ (value.v_identifier[2] == null_upper[2] ||
+ value.v_identifier[2] == null_lower[2]) &&
+ (value.v_identifier[3] == null_upper[3] ||
+ value.v_identifier[3] == null_lower[3]))
+ token = G_TOKEN_IDENTIFIER_NULL;
+ }
+ }
+ }
+
+ *token_p = token;
+ *value_p = value;
+}
Added: trunk/json-glib/json-scanner.h
==============================================================================
--- (empty file)
+++ trunk/json-glib/json-scanner.h Sat Apr 19 16:34:24 2008
@@ -0,0 +1,171 @@
+/* json-scanner.h: Tokenizer for JSON
+ *
+ * This file is part of JSON-GLib
+ * Copyright (C) 2008 OpenedHand
+ *
+ * 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 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, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+/*
+ * JsonScanner is a specialized tokenizer for JSON adapted from
+ * the GScanner tokenizer in GLib; GScanner came with this notice:
+ *
+ * Modified by the GLib Team and others 1997-2000. See the AUTHORS
+ * file for a list of people on the GLib Team. See the ChangeLog
+ * files for a list of changes. These files are distributed with
+ * GLib at ftp://ftp.gtk.org/pub/gtk/.
+ *
+ * JsonScanner: modified by Emmanuele Bassi <ebassi openedhand com>
+ */
+
+#ifndef __JSON_SCANNER_H__
+#define __JSON_SCANNER_H__
+
+#include <glib/gdataset.h>
+#include <glib/ghash.h>
+#include <glib/gscanner.h>
+
+G_BEGIN_DECLS
+
+typedef struct _JsonScanner JsonScanner;
+typedef struct _JsonScannerConfig JsonScannerConfig;
+
+typedef void (* JsonScannerMsgFunc) (JsonScanner *scanner,
+ gchar *message,
+ gboolean is_error);
+
+/**
+ * JsonTokenType:
+ * @JSON_TOKEN_INVALID: marker
+ * @JSON_TOKEN_TRUE: symbol for 'true' bareword
+ * @JSON_TOKEN_FALSE: symbol for 'false' bareword
+ * @JSON_TOKEN_NULL: symbol for 'null' bareword
+ * @JSON_TOKEN_VAR: symbol for 'var' bareword
+ * @JSON_TOKEN_LAST: marker
+ *
+ * Tokens for JsonScanner-based parser, extending #GTokenType.
+ */
+typedef enum {
+ JSON_TOKEN_INVALID = G_TOKEN_LAST,
+
+ JSON_TOKEN_TRUE,
+ JSON_TOKEN_FALSE,
+ JSON_TOKEN_NULL,
+ JSON_TOKEN_VAR,
+
+ JSON_TOKEN_LAST
+} JsonTokenType;
+
+/**
+ * JsonScanner:
+ *
+ * Tokenizer scanner for JSON. See #GScanner
+ *
+ * Since: 0.6
+ */
+struct _JsonScanner
+{
+ /*< private >*/
+ /* unused fields */
+ gpointer user_data;
+ guint max_parse_errors;
+
+ /* json_scanner_error() increments this field */
+ guint parse_errors;
+
+ /* name of input stream, featured by the default message handler */
+ const gchar *input_name;
+
+ /* quarked data */
+ GData *qdata;
+
+ /* link into the scanner configuration */
+ JsonScannerConfig *config;
+
+ /* fields filled in after json_scanner_get_next_token() */
+ GTokenType token;
+ GTokenValue value;
+ guint line;
+ guint position;
+
+ /* fields filled in after json_scanner_peek_next_token() */
+ GTokenType next_token;
+ GTokenValue next_value;
+ guint next_line;
+ guint next_position;
+
+ /* to be considered private */
+ GHashTable *symbol_table;
+ gint input_fd;
+ const gchar *text;
+ const gchar *text_end;
+ gchar *buffer;
+ guint scope_id;
+
+ /* handler function for _warn and _error */
+ JsonScannerMsgFunc msg_handler;
+};
+
+JsonScanner *json_scanner_new (void);
+void json_scanner_destroy (JsonScanner *scanner);
+void json_scanner_input_file (JsonScanner *scanner,
+ gint input_fd);
+void json_scanner_sync_file_offset (JsonScanner *scanner);
+void json_scanner_input_text (JsonScanner *scanner,
+ const gchar *text,
+ guint text_len);
+GTokenType json_scanner_get_next_token (JsonScanner *scanner);
+GTokenType json_scanner_peek_next_token (JsonScanner *scanner);
+GTokenType json_scanner_cur_token (JsonScanner *scanner);
+GTokenValue json_scanner_cur_value (JsonScanner *scanner);
+guint json_scanner_cur_line (JsonScanner *scanner);
+guint json_scanner_cur_position (JsonScanner *scanner);
+gboolean json_scanner_eof (JsonScanner *scanner);
+guint json_scanner_set_scope (JsonScanner *scanner,
+ guint scope_id);
+void json_scanner_scope_add_symbol (JsonScanner *scanner,
+ guint scope_id,
+ const gchar *symbol,
+ gpointer value);
+void json_scanner_scope_remove_symbol (JsonScanner *scanner,
+ guint scope_id,
+ const gchar *symbol);
+gpointer json_scanner_scope_lookup_symbol (JsonScanner *scanner,
+ guint scope_id,
+ const gchar *symbol);
+void json_scanner_scope_foreach_symbol (JsonScanner *scanner,
+ guint scope_id,
+ GHFunc func,
+ gpointer user_data);
+gpointer json_scanner_lookup_symbol (JsonScanner *scanner,
+ const gchar *symbol);
+void json_scanner_unexp_token (JsonScanner *scanner,
+ GTokenType expected_token,
+ const gchar *identifier_spec,
+ const gchar *symbol_spec,
+ const gchar *symbol_name,
+ const gchar *message,
+ gint is_error);
+void json_scanner_error (JsonScanner *scanner,
+ const gchar *format,
+ ...) G_GNUC_PRINTF (2,3);
+void json_scanner_warn (JsonScanner *scanner,
+ const gchar *format,
+ ...) G_GNUC_PRINTF (2,3);
+
+G_END_DECLS
+
+#endif /* __JSON_SCANNER_H__ */
Added: trunk/json-glib/tests/Makefile.am
==============================================================================
--- (empty file)
+++ trunk/json-glib/tests/Makefile.am Sat Apr 19 16:34:24 2008
@@ -0,0 +1,24 @@
+include $(top_srcdir)/Makefile.decl
+
+INCLUDES = \
+ -g \
+ -I$(top_srcdir) \
+ -I$(top_srcdir)/json-glib \
+ $(JSON_DEBUG_CFLAGS) \
+ $(JSON_CFLAGS)
+
+noinst_PROGRAMS = $(TEST_PROGS)
+progs_ldadd = $(top_builddir)/json-glib/libjson-glib-1.0.la
+
+TEST_PROGS += array-test
+array_test_SOURCES = array-test.c
+array_test_LDADD = $(progs_ldadd)
+
+TEST_PROGS += object-test
+object_test_SOURCES = object-test.c
+object_test_LDADD = $(progs_ldadd)
+
+TEST_PROGS += node-test
+node_test_SOURCES = node-test.c
+node_test_LDADD = $(progs_ldadd)
+
Added: trunk/json-glib/tests/array-test.c
==============================================================================
--- (empty file)
+++ trunk/json-glib/tests/array-test.c Sat Apr 19 16:34:24 2008
@@ -0,0 +1,62 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <glib.h>
+#include <json-glib/json-types.h>
+
+static void
+test_empty_array (void)
+{
+ JsonArray *array = json_array_new ();
+
+ g_assert_cmpint (json_array_get_length (array), ==, 0);
+ g_assert (json_array_get_elements (array) == NULL);
+
+ json_array_unref (array);
+}
+
+static void
+test_add_element (void)
+{
+ JsonArray *array = json_array_new ();
+ JsonNode *node = json_node_new (JSON_NODE_NULL);
+
+ g_assert_cmpint (json_array_get_length (array), ==, 0);
+
+ json_array_add_element (array, node);
+ g_assert_cmpint (json_array_get_length (array), ==, 1);
+
+ node = json_array_get_element (array, 0);
+ g_assert_cmpint (JSON_NODE_TYPE (node), ==, JSON_NODE_NULL);
+
+ json_array_unref (array);
+}
+
+static void
+test_remove_element (void)
+{
+ JsonArray *array = json_array_new ();
+ JsonNode *node = json_node_new (JSON_NODE_NULL);
+
+ json_array_add_element (array, node);
+
+ json_array_remove_element (array, 0);
+ g_assert_cmpint (json_array_get_length (array), ==, 0);
+
+ json_array_unref (array);
+}
+
+int
+main (int argc,
+ char *argv[])
+{
+ g_type_init ();
+ g_test_init (&argc, &argv, NULL);
+
+ g_test_add_func ("/array/empty-array", test_empty_array);
+ g_test_add_func ("/array/add-element", test_add_element);
+ g_test_add_func ("/array/remove-element", test_remove_element);
+
+ return g_test_run ();
+}
Added: trunk/json-glib/tests/node-test.c
==============================================================================
--- (empty file)
+++ trunk/json-glib/tests/node-test.c Sat Apr 19 16:34:24 2008
@@ -0,0 +1,71 @@
+#include <glib/gtestutils.h>
+#include <json-glib/json-types.h>
+#include <string.h>
+
+static void
+test_copy (void)
+{
+ JsonNode *node = json_node_new (JSON_NODE_NULL);
+ JsonNode *copy = json_node_copy (node);
+
+ g_assert_cmpint (node->type, ==, copy->type);
+ g_assert_cmpint (json_node_get_value_type (node), ==, json_node_get_value_type (copy));
+ g_assert_cmpstr (json_node_type_name (node), ==, json_node_type_name (copy));
+
+ json_node_free (copy);
+ json_node_free (node);
+}
+
+static void
+test_null (void)
+{
+ JsonNode *node = json_node_new (JSON_NODE_NULL);
+
+ g_assert_cmpint (node->type, ==, JSON_NODE_NULL);
+ g_assert_cmpint (json_node_get_value_type (node), ==, G_TYPE_INVALID);
+ g_assert_cmpstr (json_node_type_name (node), ==, "NULL");
+
+ json_node_free (node);
+}
+
+static void
+test_value (void)
+{
+ JsonNode *node = json_node_new (JSON_NODE_VALUE);
+ GValue value = { 0, };
+ GValue check = { 0, };
+
+ g_assert_cmpint (JSON_NODE_TYPE (node), ==, JSON_NODE_VALUE);
+
+ g_value_init (&value, G_TYPE_INT);
+ g_value_set_int (&value, 42);
+
+ g_assert_cmpint (G_VALUE_TYPE (&value), ==, G_TYPE_INT);
+ g_assert_cmpint (g_value_get_int (&value), ==, 42);
+
+ json_node_set_value (node, &value);
+ json_node_get_value (node, &check);
+
+ g_assert_cmpint (G_VALUE_TYPE (&value), ==, G_VALUE_TYPE (&check));
+ g_assert_cmpint (g_value_get_int (&value), ==, g_value_get_int (&check));
+ g_assert_cmpint (G_VALUE_TYPE (&check), ==, G_TYPE_INT);
+ g_assert_cmpint (g_value_get_int (&check), ==, 42);
+
+ g_value_unset (&value);
+ g_value_unset (&check);
+ json_node_free (node);
+}
+
+int
+main (int argc,
+ char *argv[])
+{
+ g_type_init ();
+ g_test_init (&argc, &argv, NULL);
+
+ g_test_add_func ("/nodes/null-node", test_null);
+ g_test_add_func ("/nodes/copy-node", test_copy);
+ g_test_add_func ("/nodes/value", test_value);
+
+ return g_test_run ();
+}
Added: trunk/json-glib/tests/object-test.c
==============================================================================
--- (empty file)
+++ trunk/json-glib/tests/object-test.c Sat Apr 19 16:34:24 2008
@@ -0,0 +1,62 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <glib.h>
+#include <json-glib/json-types.h>
+
+static void
+test_empty_object (void)
+{
+ JsonObject *object = json_object_new ();
+
+ g_assert_cmpint (json_object_get_size (object), ==, 0);
+ g_assert (json_object_get_members (object) == NULL);
+
+ json_object_unref (object);
+}
+
+static void
+test_add_member (void)
+{
+ JsonObject *object = json_object_new ();
+ JsonNode *node = json_node_new (JSON_NODE_NULL);
+
+ g_assert_cmpint (json_object_get_size (object), ==, 0);
+
+ json_object_add_member (object, "Null", node);
+ g_assert_cmpint (json_object_get_size (object), ==, 1);
+
+ node = json_object_get_member (object, "Null");
+ g_assert_cmpint (JSON_NODE_TYPE (node), ==, JSON_NODE_NULL);
+
+ json_object_unref (object);
+}
+
+static void
+test_remove_member (void)
+{
+ JsonObject *object = json_object_new ();
+ JsonNode *node = json_node_new (JSON_NODE_NULL);
+
+ json_object_add_member (object, "Null", node);
+
+ json_object_remove_member (object, "Null");
+ g_assert_cmpint (json_object_get_size (object), ==, 0);
+
+ json_object_unref (object);
+}
+
+int
+main (int argc,
+ char *argv[])
+{
+ g_type_init ();
+ g_test_init (&argc, &argv, NULL);
+
+ g_test_add_func ("/object/empty-object", test_empty_object);
+ g_test_add_func ("/object/add-member", test_add_member);
+ g_test_add_func ("/object/remove-member", test_remove_member);
+
+ return g_test_run ();
+}
Modified: trunk/tests/Makefile.am
==============================================================================
--- trunk/tests/Makefile.am (original)
+++ trunk/tests/Makefile.am Sat Apr 19 16:34:24 2008
@@ -1,24 +1,37 @@
-noinst_PROGRAMS = \
- test-01 \
- test-02 \
- test-03 \
- test-04 \
- test-05 \
- test-06 \
- test-07 \
- test-08
-
-INCLUDES = -I$(top_srcdir)
-LDADD = $(top_builddir)/json-glib/libjson-glib-1.0.la
-
-AM_CFLAGS = $(JSON_CFLAGS)
-AM_LDFLAGS = $(JSON_LIBS)
-
-test_01_SOURCES = test-01.c
-test_02_SOURCES = test-02.c
-test_03_SOURCES = test-03.c
-test_04_SOURCES = test-04.c
-test_05_SOURCES = test-05.c
-test_06_SOURCES = test-06.c
-test_07_SOURCES = test-07.c
-test_08_SOURCES = test-08.c
+include $(top_srcdir)/Makefile.decl
+
+NULL =
+
+noinst_PROGRAMS = $(TEST_PROGS)
+
+INCLUDES = -I$(top_srcdir) $(JSON_CFLAGS)
+progs_ldadd = $(top_builddir)/json-glib/libjson-glib-1.0.la $(JSON_LIBS)
+
+AM_CPPFLAGS = \
+ -I$(top_srcdir) \
+ -I$(top_srcdir)/json-glib \
+ $(JSON_DEBUG_CFLAGS)
+
+AM_CFLAGS = -g
+
+TESTS_ENVIRONMENT = srcdir=$(srcdir)
+
+TEST_PROGS += test-parser
+test_parser_SOURCES = test-parser.c
+test_parser_LDADD = $(progs_ldadd)
+
+TEST_PROGS += test-generator
+test_generator_SOURCES = test-generator.c
+test_generator_LDADD = $(progs_ldadd)
+
+TEST_PROGS += test-serialize-simple
+test_serialize_simple_SOURCES = test-serialize-simple.c
+test_serialize_simple_LDADD = $(progs_ldadd)
+
+TEST_PROGS += test-serialize-complex
+test_serialize_complex_SOURCES = test-serialize-complex.c
+test_serialize_complex_LDADD = $(progs_ldadd)
+
+TEST_PROGS += test-serialize-full
+test_serialize_full_SOURCES = test-serialize-full.c
+test_serialize_full_LDADD = $(progs_ldadd)
Added: trunk/tests/test-generator.c
==============================================================================
--- (empty file)
+++ trunk/tests/test-generator.c Sat Apr 19 16:34:24 2008
@@ -0,0 +1,399 @@
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <glib.h>
+
+#include <json-glib/json-glib.h>
+
+static const gchar *empty_array = "[ ]";
+static const gchar *empty_object = "{ }";
+
+static const gchar *simple_array = "[ true, false, null, 42, \"foo\" ]";
+static const gchar *nested_array = "[ true, [ false, null ], 42 ]";
+
+static const gchar *simple_object = "{ \"Bool1\" : true, \"Bool2\" : false, \"Null\" : null, \"Int\" : 42, \"String\" : \"foo\" }";
+
+static void
+test_empty_array (void)
+{
+ JsonGenerator *gen = json_generator_new ();
+ JsonNode *root;
+ gchar *data;
+ gsize len;
+
+ root = json_node_new (JSON_NODE_ARRAY);
+ json_node_take_array (root, json_array_new ());
+
+ json_generator_set_root (gen, root);
+ g_object_set (gen, "pretty", FALSE, NULL);
+
+ data = json_generator_to_data (gen, &len);
+
+ g_assert_cmpint (len, ==, strlen (empty_array));
+ g_assert_cmpstr (data, ==, empty_array);
+
+ g_free (data);
+ json_node_free (root);
+ g_object_unref (gen);
+}
+
+static void
+test_empty_object (void)
+{
+ JsonGenerator *gen = json_generator_new ();
+ JsonNode *root;
+ gchar *data;
+ gsize len;
+
+ root = json_node_new (JSON_NODE_OBJECT);
+ json_node_take_object (root, json_object_new ());
+
+ json_generator_set_root (gen, root);
+ g_object_set (gen, "pretty", FALSE, NULL);
+
+ data = json_generator_to_data (gen, &len);
+
+ g_assert_cmpint (len, ==, strlen (empty_object));
+ g_assert_cmpstr (data, ==, empty_object);
+
+ g_free (data);
+ json_node_free (root);
+ g_object_unref (gen);
+}
+
+static void
+test_simple_array (void)
+{
+ JsonGenerator *generator = json_generator_new ();
+ JsonNode *root, *val;
+ JsonArray *array;
+ GValue value = { 0, };
+ gchar *data;
+ gsize len;
+
+ root = json_node_new (JSON_NODE_ARRAY);
+ array = json_array_sized_new (6);
+
+ val = json_node_new (JSON_NODE_VALUE);
+ g_value_init (&value, G_TYPE_BOOLEAN);
+ g_value_set_boolean (&value, TRUE);
+ json_node_set_value (val, &value);
+ json_array_add_element (array, val);
+ g_value_unset (&value);
+
+ val = json_node_new (JSON_NODE_VALUE);
+ g_value_init (&value, G_TYPE_BOOLEAN);
+ g_value_set_boolean (&value, FALSE);
+ json_node_set_value (val, &value);
+ json_array_add_element (array, val);
+ g_value_unset (&value);
+
+ val = json_node_new (JSON_NODE_NULL);
+ json_array_add_element (array, val);
+
+ val = json_node_new (JSON_NODE_VALUE);
+ g_value_init (&value, G_TYPE_INT);
+ g_value_set_int (&value, 42);
+ json_node_set_value (val, &value);
+ json_array_add_element (array, val);
+ g_value_unset (&value);
+
+ val = json_node_new (JSON_NODE_VALUE);
+ g_value_init (&value, G_TYPE_STRING);
+ g_value_set_string (&value, "foo");
+ json_node_set_value (val, &value);
+ json_array_add_element (array, val);
+ g_value_unset (&value);
+
+ json_node_take_array (root, array);
+ json_generator_set_root (generator, root);
+
+ g_object_set (generator, "pretty", FALSE, NULL);
+ data = json_generator_to_data (generator, &len);
+
+ if (g_test_verbose ())
+ g_print ("checking simple array `%s' (expected: %s)\n",
+ data,
+ simple_array);
+
+ g_assert_cmpint (len, ==, strlen (simple_array));
+ g_assert_cmpstr (data, ==, simple_array);
+
+ g_free (data);
+ json_node_free (root);
+ g_object_unref (generator);
+}
+
+static void
+test_nested_array (void)
+{
+ JsonGenerator *generator = json_generator_new ();
+ JsonNode *root, *val, *nested_val;
+ JsonArray *array, *nested;
+ GValue value = { 0, };
+ gchar *data;
+ gsize len;
+
+ root = json_node_new (JSON_NODE_ARRAY);
+ array = json_array_sized_new (3);
+
+ val = json_node_new (JSON_NODE_VALUE);
+ g_value_init (&value, G_TYPE_BOOLEAN);
+ g_value_set_boolean (&value, TRUE);
+ json_node_set_value (val, &value);
+ json_array_add_element (array, val);
+ g_value_unset (&value);
+
+ {
+ val = json_node_new (JSON_NODE_ARRAY);
+ nested = json_array_new ();
+
+ nested_val = json_node_new (JSON_NODE_VALUE);
+ g_value_init (&value, G_TYPE_BOOLEAN);
+ g_value_set_boolean (&value, FALSE);
+ json_node_set_value (nested_val, &value);
+ json_array_add_element (nested, nested_val);
+ g_value_unset (&value);
+
+ nested_val = json_node_new (JSON_NODE_NULL);
+ json_array_add_element (nested, nested_val);
+
+ json_node_take_array (val, nested);
+ json_array_add_element (array, val);
+ }
+
+ val = json_node_new (JSON_NODE_VALUE);
+ g_value_init (&value, G_TYPE_INT);
+ g_value_set_int (&value, 42);
+ json_node_set_value (val, &value);
+ json_array_add_element (array, val);
+ g_value_unset (&value);
+
+ json_node_take_array (root, array);
+ json_generator_set_root (generator, root);
+
+ g_object_set (generator, "pretty", FALSE, NULL);
+ data = json_generator_to_data (generator, &len);
+
+ g_assert_cmpint (len, ==, strlen (nested_array));
+ g_assert_cmpstr (data, ==, nested_array);
+
+ g_free (data);
+ json_node_free (root);
+ g_object_unref (generator);
+}
+
+static void
+test_simple_object (void)
+{
+ JsonGenerator *generator = json_generator_new ();
+ JsonNode *root, *val;
+ JsonObject *object;
+ GValue value = { 0, };
+ gchar *data;
+ gsize len;
+
+ root = json_node_new (JSON_NODE_OBJECT);
+ object = json_object_new ();
+
+ val = json_node_new (JSON_NODE_VALUE);
+ g_value_init (&value, G_TYPE_BOOLEAN);
+ g_value_set_boolean (&value, TRUE);
+ json_node_set_value (val, &value);
+ json_object_add_member (object, "Bool1", val);
+ g_value_unset (&value);
+
+ val = json_node_new (JSON_NODE_VALUE);
+ g_value_init (&value, G_TYPE_BOOLEAN);
+ g_value_set_boolean (&value, FALSE);
+ json_node_set_value (val, &value);
+ json_object_add_member (object, "Bool2", val);
+ g_value_unset (&value);
+
+ val = json_node_new (JSON_NODE_NULL);
+ json_object_add_member (object, "Null", val);
+
+ val = json_node_new (JSON_NODE_VALUE);
+ g_value_init (&value, G_TYPE_INT);
+ g_value_set_int (&value, 42);
+ json_node_set_value (val, &value);
+ json_object_add_member (object, "Int", val);
+ g_value_unset (&value);
+
+ val = json_node_new (JSON_NODE_VALUE);
+ g_value_init (&value, G_TYPE_STRING);
+ g_value_set_string (&value, "foo");
+ json_node_set_value (val, &value);
+ json_object_add_member (object, "String", val);
+ g_value_unset (&value);
+
+ json_node_take_object (root, object);
+ json_generator_set_root (generator, root);
+
+ g_object_set (generator, "pretty", FALSE, NULL);
+ data = json_generator_to_data (generator, &len);
+
+ if (g_test_verbose ())
+ g_print ("checking simple array `%s' (expected: %s)\n",
+ data,
+ simple_object);
+
+ g_assert_cmpint (len, ==, strlen (simple_object));
+
+ /* we cannot compare the strings literal because JsonObject does not
+ * guarantee any ordering
+ */
+
+ g_free (data);
+ json_node_free (root);
+ g_object_unref (generator);
+}
+
+#if 0
+/* this is just overkill, but I'll add it commented out, so it
+ * can be enabled if I feel like running this just to compare
+ * the length of the strings
+ */
+static void
+test_nested_object (void)
+{
+ JsonGenerator *generator = json_generator_new ();
+ JsonNode *root, *val, *nested_val;
+ JsonObject *object, *nested;
+ JsonArray *array;
+ GValue value = { 0, };
+ gchar *data;
+ gsize len;
+
+ root = json_node_new (JSON_NODE_OBJECT);
+ object = json_object_new ();
+
+ val = json_node_new (JSON_NODE_VALUE);
+ g_value_init (&value, G_TYPE_STRING);
+ g_value_set_string (&value, "View from 15th Floor");
+ json_node_set_value (val, &value);
+ json_object_add_member (object, "Title", val);
+ g_value_unset (&value);
+
+ val = json_node_new (JSON_NODE_VALUE);
+ g_value_init (&value, G_TYPE_INT);
+ g_value_set_int (&value, 800);
+ json_node_set_value (val, &value);
+ json_object_add_member (object, "Width", val);
+ g_value_unset (&value);
+
+ val = json_node_new (JSON_NODE_VALUE);
+ g_value_init (&value, G_TYPE_INT);
+ g_value_set_int (&value, 600);
+ json_node_set_value (val, &value);
+ json_object_add_member (object, "Height", val);
+ g_value_unset (&value);
+
+ {
+ val = json_node_new (JSON_NODE_ARRAY);
+ array = json_array_new ();
+
+ nested_val = json_node_new (JSON_NODE_VALUE);
+ g_value_init (&value, G_TYPE_INT);
+ g_value_set_int (&value, 116);
+ json_node_set_value (nested_val, &value);
+ json_array_add_element (array, nested_val);
+ g_value_unset (&value);
+
+ nested_val = json_node_new (JSON_NODE_VALUE);
+ g_value_init (&value, G_TYPE_INT);
+ g_value_set_int (&value, 943);
+ json_node_set_value (nested_val, &value);
+ json_array_add_element (array, nested_val);
+ g_value_unset (&value);
+
+ nested_val = json_node_new (JSON_NODE_VALUE);
+ g_value_init (&value, G_TYPE_INT);
+ g_value_set_int (&value, 234);
+ json_node_set_value (nested_val, &value);
+ json_array_add_element (array, nested_val);
+ g_value_unset (&value);
+
+ nested_val = json_node_new (JSON_NODE_VALUE);
+ g_value_init (&value, G_TYPE_INT);
+ g_value_set_int (&value, 38793);
+ json_node_set_value (nested_val, &value);
+ json_array_add_element (array, nested_val);
+ g_value_unset (&value);
+
+ json_node_take_array (val, array);
+ json_object_add_member (object, "IDs", val);
+ }
+
+ {
+ val = json_node_new (JSON_NODE_OBJECT);
+ nested = json_object_new ();
+
+ nested_val = json_node_new (JSON_NODE_VALUE);
+ g_value_init (&value, G_TYPE_STRING);
+ g_value_set_string (&value, "http://www.example.com/image/481989943");
+ json_node_set_value (nested_val, &value);
+ json_object_add_member (nested, "Url", nested_val);
+ g_value_unset (&value);
+
+ nested_val = json_node_new (JSON_NODE_VALUE);
+ g_value_init (&value, G_TYPE_INT);
+ g_value_set_int (&value, 125);
+ json_node_set_value (nested_val, &value);
+ json_object_add_member (nested, "Width", nested_val);
+ g_value_unset (&value);
+
+ nested_val = json_node_new (JSON_NODE_VALUE);
+ g_value_init (&value, G_TYPE_INT);
+ g_value_set_int (&value, 100);
+ json_node_set_value (nested_val, &value);
+ json_object_add_member (nested, "Height", nested_val);
+ g_value_unset (&value);
+
+ json_node_take_object (val, nested);
+ json_object_add_member (object, "Thumbnail", val);
+ }
+
+ json_node_take_object (root, object);
+ json_generator_set_root (generator, root);
+
+ g_object_set (generator, "pretty", FALSE, NULL);
+ data = json_generator_to_data (generator, &len);
+
+ if (g_test_verbose ())
+ g_print ("checking nested object `%s' (expected: %s)\n",
+ data,
+ nested_object);
+
+ g_assert_cmpint (len, ==, strlen (nested_object));
+
+ /* we cannot compare the strings literal because JsonObject does not
+ * guarantee any ordering
+ */
+
+ g_free (data);
+ json_node_free (root);
+ g_object_unref (generator);
+}
+#endif
+
+int
+main (int argc,
+ char *argv[])
+{
+ g_type_init ();
+ g_test_init (&argc, &argv, NULL);
+
+ g_test_add_func ("/json-generator/empty-array", test_empty_array);
+ g_test_add_func ("/json-generator/empty-object", test_empty_object);
+ g_test_add_func ("/json-generator/simple-array", test_simple_array);
+ g_test_add_func ("/json-generator/nested-array", test_nested_array);
+ g_test_add_func ("/json-generator/simple-object", test_simple_object);
+
+ return g_test_run ();
+}
Added: trunk/tests/test-parser.c
==============================================================================
--- (empty file)
+++ trunk/tests/test-parser.c Sat Apr 19 16:34:24 2008
@@ -0,0 +1,523 @@
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+
+#include <glib.h>
+
+#include <json-glib/json-glib.h>
+
+static const gchar *test_empty_string = "";
+static const gchar *test_empty_array_string = "[ ]";
+static const gchar *test_empty_object_string = "{ }";
+
+static const struct {
+ const gchar *str;
+ gint len;
+ gint element;
+ JsonNodeType type;
+ GType gtype;
+} test_simple_arrays[] = {
+ { "[ true ]", 1, 0, JSON_NODE_VALUE, G_TYPE_BOOLEAN },
+ { "[ true, false, null ]", 3, 2, JSON_NODE_NULL, G_TYPE_INVALID },
+ { "[ 1, 2, 3.14, \"test\" ]", 4, 3, JSON_NODE_VALUE, G_TYPE_STRING }
+};
+
+static const gchar *test_nested_arrays[] = {
+ "[ 42, [ ], null ]",
+ "[ [ ], [ true, [ true ] ] ]",
+ "[ [ false, true, 42 ], [ true, false, 3.14 ], \"test\" ]",
+ "[ true, { } ]",
+ "[ false, { \"test\" : 42 } ]",
+ "[ { \"channel\" : \"/meta/connect\" } ]"
+};
+
+static const struct {
+ const gchar *str;
+ gint size;
+ const gchar *member;
+ JsonNodeType type;
+ GType gtype;
+} test_simple_objects[] = {
+ { "{ \"test\" : 42 }", 1, "test", JSON_NODE_VALUE, G_TYPE_INT },
+ { "{ \"foo\" : \"bar\", \"baz\" : null }", 2, "baz", JSON_NODE_NULL, G_TYPE_INVALID },
+ { "{ \"channel\" : \"/meta/connect\" }", 1, "channel", JSON_NODE_VALUE, G_TYPE_STRING }
+};
+
+static const gchar *test_nested_objects[] = {
+ "{ \"array\" : [ false, \"foo\" ], \"object\" : { \"foo\" : true } }"
+};
+
+static const gchar *test_assignments[] = {
+ "var test = [ false, false, true ]",
+ "var test = [ true, 42 ];",
+ "var test = { \"foo\" : false }"
+};
+
+static const struct
+{
+ const gchar *str;
+ const gchar *member;
+ const gchar *match;
+} test_unicode[] = {
+ { "{ \"test\" : \"foo \\u00e8\" }", "test", "foo Ã" }
+};
+
+static guint n_test_simple_arrays = G_N_ELEMENTS (test_simple_arrays);
+static guint n_test_nested_arrays = G_N_ELEMENTS (test_nested_arrays);
+static guint n_test_simple_objects = G_N_ELEMENTS (test_simple_objects);
+static guint n_test_nested_objects = G_N_ELEMENTS (test_nested_objects);
+static guint n_test_assignments = G_N_ELEMENTS (test_assignments);
+static guint n_test_unicode = G_N_ELEMENTS (test_unicode);
+
+static void
+test_empty (void)
+{
+ JsonParser *parser;
+ GError *error = NULL;
+
+ parser = json_parser_new ();
+ g_assert (JSON_IS_PARSER (parser));
+
+ if (g_test_verbose ())
+ g_print ("checking json_parser_load_from_data with empty string...\n");
+
+ if (!json_parser_load_from_data (parser, test_empty_string, -1, &error))
+ {
+ if (g_test_verbose ())
+ g_print ("Error: %s\n", error->message);
+ g_error_free (error);
+ g_object_unref (parser);
+ exit (1);
+ }
+ else
+ {
+ if (g_test_verbose ())
+ g_print ("checking json_parser_get_root...\n");
+
+ g_assert (NULL == json_parser_get_root (parser));
+ }
+
+ g_object_unref (parser);
+}
+
+static void
+test_empty_array (void)
+{
+ JsonParser *parser;
+ GError *error = NULL;
+
+ parser = json_parser_new ();
+ g_assert (JSON_IS_PARSER (parser));
+
+ if (g_test_verbose ())
+ g_print ("checking json_parser_load_from_data with empty array...\n");
+
+ if (!json_parser_load_from_data (parser, test_empty_array_string, -1, &error))
+ {
+ if (g_test_verbose ())
+ g_print ("Error: %s\n", error->message);
+ g_error_free (error);
+ g_object_unref (parser);
+ exit (1);
+ }
+ else
+ {
+ JsonNode *root;
+ JsonArray *array;
+
+ g_assert (NULL != json_parser_get_root (parser));
+
+ if (g_test_verbose ())
+ g_print ("checking root node is an array...\n");
+ root = json_parser_get_root (parser);
+ g_assert_cmpint (JSON_NODE_TYPE (root), ==, JSON_NODE_ARRAY);
+
+ array = json_node_get_array (root);
+ g_assert (array != NULL);
+
+ if (g_test_verbose ())
+ g_print ("checking array is empty...\n");
+ g_assert_cmpint (json_array_get_length (array), ==, 0);
+ }
+
+ g_object_unref (parser);
+}
+
+static void
+test_simple_array (void)
+{
+ gint i;
+ JsonParser *parser;
+
+ parser = json_parser_new ();
+ g_assert (JSON_IS_PARSER (parser));
+
+ if (g_test_verbose ())
+ g_print ("checking json_parser_load_from_data with simple arrays...\n");
+
+ for (i = 0; i < n_test_simple_arrays; i++)
+ {
+ GError *error = NULL;
+
+ if (!json_parser_load_from_data (parser, test_simple_arrays[i].str, -1, &error))
+ {
+ if (g_test_verbose ())
+ g_print ("Error: %s\n", error->message);
+
+ g_error_free (error);
+ g_object_unref (parser);
+ exit (1);
+ }
+ else
+ {
+ JsonNode *root, *node;
+ JsonArray *array;
+
+ g_assert (NULL != json_parser_get_root (parser));
+
+ if (g_test_verbose ())
+ g_print ("checking root node is an array...\n");
+ root = json_parser_get_root (parser);
+ g_assert_cmpint (JSON_NODE_TYPE (root), ==, JSON_NODE_ARRAY);
+
+ array = json_node_get_array (root);
+ g_assert (array != NULL);
+
+ if (g_test_verbose ())
+ g_print ("checking array is of the desired length (%d)...\n",
+ test_simple_arrays[i].len);
+ g_assert_cmpint (json_array_get_length (array), ==, test_simple_arrays[i].len);
+
+ if (g_test_verbose ())
+ g_print ("checking element %d is of the desired type %s...\n",
+ test_simple_arrays[i].element,
+ g_type_name (test_simple_arrays[i].gtype));
+ node = json_array_get_element (array, test_simple_arrays[i].element);
+ g_assert (node != NULL);
+ g_assert_cmpint (JSON_NODE_TYPE (node), ==, test_simple_arrays[i].type);
+ g_assert_cmpint (json_node_get_value_type (node), ==, test_simple_arrays[i].gtype);
+ }
+ }
+
+ g_object_unref (parser);
+}
+
+static void
+test_nested_array (void)
+{
+ gint i;
+ JsonParser *parser;
+
+ parser = json_parser_new ();
+ g_assert (JSON_IS_PARSER (parser));
+
+ if (g_test_verbose ())
+ g_print ("checking json_parser_load_from_data with nested arrays...\n");
+
+ for (i = 0; i < n_test_nested_arrays; i++)
+ {
+ GError *error = NULL;
+
+ if (!json_parser_load_from_data (parser, test_nested_arrays[i], -1, &error))
+ {
+ if (g_test_verbose ())
+ g_print ("Error: %s\n", error->message);
+
+ g_error_free (error);
+ g_object_unref (parser);
+ exit (1);
+ }
+ else
+ {
+ JsonNode *root;
+ JsonArray *array;
+
+ g_assert (NULL != json_parser_get_root (parser));
+
+ if (g_test_verbose ())
+ g_print ("checking root node is an array...\n");
+ root = json_parser_get_root (parser);
+ g_assert_cmpint (JSON_NODE_TYPE (root), ==, JSON_NODE_ARRAY);
+
+ array = json_node_get_array (root);
+ g_assert (array != NULL);
+
+ if (g_test_verbose ())
+ g_print ("checking array is not empty...\n");
+ g_assert_cmpint (json_array_get_length (array), >, 0);
+ }
+ }
+
+ g_object_unref (parser);
+}
+
+static void
+test_empty_object (void)
+{
+ JsonParser *parser;
+ GError *error = NULL;
+
+ parser = json_parser_new ();
+ g_assert (JSON_IS_PARSER (parser));
+
+ if (g_test_verbose ())
+ g_print ("checking json_parser_load_from_data with empty object...\n");
+
+ if (!json_parser_load_from_data (parser, test_empty_object_string, -1, &error))
+ {
+ if (g_test_verbose ())
+ g_print ("Error: %s\n", error->message);
+ g_error_free (error);
+ g_object_unref (parser);
+ exit (1);
+ }
+ else
+ {
+ JsonNode *root;
+ JsonObject *object;
+
+ g_assert (NULL != json_parser_get_root (parser));
+
+ if (g_test_verbose ())
+ g_print ("checking root node is an object...\n");
+ root = json_parser_get_root (parser);
+ g_assert_cmpint (JSON_NODE_TYPE (root), ==, JSON_NODE_OBJECT);
+
+ object = json_node_get_object (root);
+ g_assert (object != NULL);
+
+ if (g_test_verbose ())
+ g_print ("checking object is empty...\n");
+ g_assert_cmpint (json_object_get_size (object), ==, 0);
+ }
+
+ g_object_unref (parser);
+}
+
+static void
+test_simple_object (void)
+{
+ gint i;
+ JsonParser *parser;
+
+ parser = json_parser_new ();
+ g_assert (JSON_IS_PARSER (parser));
+
+ if (g_test_verbose ())
+ g_print ("checking json_parser_load_from_data with simple objects...\n");
+
+ for (i = 0; i < n_test_simple_objects; i++)
+ {
+ GError *error = NULL;
+
+ if (!json_parser_load_from_data (parser, test_simple_objects[i].str, -1, &error))
+ {
+ if (g_test_verbose ())
+ g_print ("Error: %s\n", error->message);
+
+ g_error_free (error);
+ g_object_unref (parser);
+ exit (1);
+ }
+ else
+ {
+ JsonNode *root, *node;
+ JsonObject *object;
+
+ g_assert (NULL != json_parser_get_root (parser));
+
+ if (g_test_verbose ())
+ g_print ("checking root node is an object...\n");
+ root = json_parser_get_root (parser);
+ g_assert_cmpint (JSON_NODE_TYPE (root), ==, JSON_NODE_OBJECT);
+
+ object = json_node_get_object (root);
+ g_assert (object != NULL);
+
+ if (g_test_verbose ())
+ g_print ("checking object is of the desired size (%d)...\n",
+ test_simple_objects[i].size);
+ g_assert_cmpint (json_object_get_size (object), ==, test_simple_objects[i].size);
+
+ if (g_test_verbose ())
+ g_print ("checking member '%s' is of the desired type %s...\n",
+ test_simple_objects[i].member,
+ g_type_name (test_simple_objects[i].gtype));
+ node = json_object_get_member (object, test_simple_objects[i].member);
+ g_assert (node != NULL);
+ g_assert_cmpint (JSON_NODE_TYPE (node), ==, test_simple_objects[i].type);
+ g_assert_cmpint (json_node_get_value_type (node), ==, test_simple_objects[i].gtype);
+ }
+ }
+
+ g_object_unref (parser);
+}
+
+static void
+test_nested_object (void)
+{
+ gint i;
+ JsonParser *parser;
+
+ parser = json_parser_new ();
+ g_assert (JSON_IS_PARSER (parser));
+
+ if (g_test_verbose ())
+ g_print ("checking json_parser_load_from_data with nested objects...\n");
+
+ for (i = 0; i < n_test_nested_objects; i++)
+ {
+ GError *error = NULL;
+
+ if (!json_parser_load_from_data (parser, test_nested_objects[i], -1, &error))
+ {
+ if (g_test_verbose ())
+ g_print ("Error: %s\n", error->message);
+
+ g_error_free (error);
+ g_object_unref (parser);
+ exit (1);
+ }
+ else
+ {
+ JsonNode *root;
+ JsonObject *object;
+
+ g_assert (NULL != json_parser_get_root (parser));
+
+ if (g_test_verbose ())
+ g_print ("checking root node is an object...\n");
+ root = json_parser_get_root (parser);
+ g_assert_cmpint (JSON_NODE_TYPE (root), ==, JSON_NODE_OBJECT);
+
+ object = json_node_get_object (root);
+ g_assert (object != NULL);
+
+ if (g_test_verbose ())
+ g_print ("checking object is not empty...\n");
+ g_assert_cmpint (json_object_get_size (object), >, 0);
+ }
+ }
+
+ g_object_unref (parser);
+}
+
+static void
+test_assignment (void)
+{
+ gint i;
+ JsonParser *parser;
+
+ parser = json_parser_new ();
+ g_assert (JSON_IS_PARSER (parser));
+
+ if (g_test_verbose ())
+ g_print ("checking json_parser_load_from_data with assignments...\n");
+
+ for (i = 0; i < n_test_assignments; i++)
+ {
+ GError *error = NULL;
+
+ if (!json_parser_load_from_data (parser, test_assignments[i], -1, &error))
+ {
+ if (g_test_verbose ())
+ g_print ("Error: %s\n", error->message);
+
+ g_error_free (error);
+ g_object_unref (parser);
+ exit (1);
+ }
+ else
+ {
+ gchar *var;
+
+ if (g_test_verbose ())
+ g_print ("checking assignment...\n");
+ g_assert (json_parser_has_assignment (parser, &var) == TRUE);
+ g_assert (var != NULL);
+
+ g_assert (NULL != json_parser_get_root (parser));
+ }
+ }
+
+ g_object_unref (parser);
+}
+
+static void
+test_unicode_escape (void)
+{
+ gint i;
+ JsonParser *parser;
+
+ parser = json_parser_new ();
+ g_assert (JSON_IS_PARSER (parser));
+
+ if (g_test_verbose ())
+ g_print ("checking json_parser_load_from_data with unicode escape...\n");
+
+ for (i = 0; i < n_test_unicode; i++)
+ {
+ GError *error = NULL;
+
+ if (!json_parser_load_from_data (parser, test_unicode[i].str, -1, &error))
+ {
+ if (g_test_verbose ())
+ g_print ("Error: %s\n", error->message);
+
+ g_error_free (error);
+ g_object_unref (parser);
+ exit (1);
+ }
+ else
+ {
+ JsonNode *root, *node;
+ JsonObject *object;
+
+ g_assert (NULL != json_parser_get_root (parser));
+
+ if (g_test_verbose ())
+ g_print ("checking root node is an object...\n");
+ root = json_parser_get_root (parser);
+ g_assert_cmpint (JSON_NODE_TYPE (root), ==, JSON_NODE_OBJECT);
+
+ object = json_node_get_object (root);
+ g_assert (object != NULL);
+
+ if (g_test_verbose ())
+ g_print ("checking object is not empty...\n");
+ g_assert_cmpint (json_object_get_size (object), >, 0);
+
+ node = json_object_get_member (object, test_unicode[i].member);
+ g_assert_cmpint (JSON_NODE_TYPE (node), ==, JSON_NODE_VALUE);
+
+ g_assert_cmpstr (json_node_get_string (node), ==, test_unicode[i].match);
+
+ g_assert (g_utf8_validate (json_node_get_string (node), -1, NULL));
+ }
+ }
+
+ g_object_unref (parser);
+}
+
+int
+main (int argc,
+ char *argv[])
+{
+ g_type_init ();
+ g_test_init (&argc, &argv, NULL);
+
+ g_test_add_func ("/json-parser/empty-string", test_empty);
+ g_test_add_func ("/json-parser/empty-array", test_empty_array);
+ g_test_add_func ("/json-parser/simple-array", test_simple_array);
+ g_test_add_func ("/json-parser/nested-array", test_nested_array);
+ g_test_add_func ("/json-parser/empty-object", test_empty_object);
+ g_test_add_func ("/json-parser/simple-object", test_simple_object);
+ g_test_add_func ("/json-parser/nested-object", test_nested_object);
+ g_test_add_func ("/json-parser/assignment", test_assignment);
+ g_test_add_func ("/json-parser/unicode-escape", test_unicode_escape);
+
+ return g_test_run ();
+}
Added: trunk/tests/test-serialize-complex.c
==============================================================================
--- (empty file)
+++ trunk/tests/test-serialize-complex.c Sat Apr 19 16:34:24 2008
@@ -0,0 +1,261 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <glib-object.h>
+
+#include <json-glib/json-glib.h>
+#include <json-glib/json-gobject.h>
+
+#define TEST_TYPE_BOXED (test_boxed_get_type ())
+#define TEST_TYPE_OBJECT (test_object_get_type ())
+#define TEST_OBJECT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), TEST_TYPE_OBJECT, TestObject))
+#define TEST_IS_OBJECT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TEST_TYPE_OBJECT))
+#define TEST_OBJECT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), TEST_TYPE_OBJECT, TestObjectClass))
+#define TEST_IS_OBJECT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), TEST_TYPE_OBJECT))
+#define TEST_OBJECT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), TEST_TYPE_OBJECT, TestObjectClass))
+
+typedef struct _TestBoxed TestBoxed;
+typedef struct _TestObject TestObject;
+typedef struct _TestObjectClass TestObjectClass;
+
+struct _TestBoxed
+{
+ gint foo;
+ gboolean bar;
+};
+
+struct _TestObject
+{
+ GObject parent_instance;
+
+ gint foo;
+ gboolean bar;
+ gchar *baz;
+ TestBoxed blah;
+};
+
+struct _TestObjectClass
+{
+ GObjectClass parent_class;
+};
+
+GType test_object_get_type (void);
+
+/*** implementation ***/
+
+static TestBoxed *
+test_boxed_copy (const TestBoxed *src)
+{
+ TestBoxed *copy = g_slice_new (TestBoxed);
+
+ *copy = *src;
+
+ return copy;
+}
+
+static void
+test_boxed_free (TestBoxed *boxed)
+{
+ if (G_LIKELY (boxed))
+ {
+ g_slice_free (TestBoxed, boxed);
+ }
+}
+
+GType
+test_boxed_get_type (void)
+{
+ static GType b_type = 0;
+
+ if (G_UNLIKELY (b_type == 0))
+ b_type = g_boxed_type_register_static ("TestBoxed",
+ (GBoxedCopyFunc) test_boxed_copy,
+ (GBoxedFreeFunc) test_boxed_free);
+
+ return b_type;
+}
+
+enum
+{
+ PROP_0,
+
+ PROP_FOO,
+ PROP_BAR,
+ PROP_BAZ,
+ PROP_BLAH
+};
+
+static void json_serializable_iface_init (gpointer g_iface);
+
+G_DEFINE_TYPE_WITH_CODE (TestObject, test_object, G_TYPE_OBJECT,
+ G_IMPLEMENT_INTERFACE (JSON_TYPE_SERIALIZABLE,
+ json_serializable_iface_init));
+
+static JsonNode *
+test_object_serialize_property (JsonSerializable *serializable,
+ const gchar *name,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ JsonNode *retval = NULL;
+
+ if (strcmp (name, "blah") == 0)
+ {
+ TestBoxed *boxed;
+ JsonObject *obj;
+ JsonNode *val;
+
+ retval = json_node_new (JSON_NODE_OBJECT);
+ obj = json_object_new ();
+
+ boxed = g_value_get_boxed (value);
+
+ val = json_node_new (JSON_NODE_VALUE);
+ json_node_set_int (val, boxed->foo);
+ json_object_add_member (obj, "foo", val);
+
+ val = json_node_new (JSON_NODE_VALUE);
+ json_node_set_boolean (val, boxed->bar);
+ json_object_add_member (obj, "bar", val);
+
+ json_node_take_object (retval, obj);
+ }
+
+ return retval;
+}
+
+static void
+json_serializable_iface_init (gpointer g_iface)
+{
+ JsonSerializableIface *iface = g_iface;
+
+ iface->serialize_property = test_object_serialize_property;
+}
+
+static void
+test_object_finalize (GObject *gobject)
+{
+ g_free (TEST_OBJECT (gobject)->baz);
+
+ G_OBJECT_CLASS (test_object_parent_class)->finalize (gobject);
+}
+
+static void
+test_object_set_property (GObject *gobject,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ switch (prop_id)
+ {
+ case PROP_FOO:
+ TEST_OBJECT (gobject)->foo = g_value_get_int (value);
+ break;
+ case PROP_BAR:
+ TEST_OBJECT (gobject)->bar = g_value_get_boolean (value);
+ break;
+ case PROP_BAZ:
+ g_free (TEST_OBJECT (gobject)->baz);
+ TEST_OBJECT (gobject)->baz = g_value_dup_string (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
+ }
+}
+
+static void
+test_object_get_property (GObject *gobject,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ switch (prop_id)
+ {
+ case PROP_FOO:
+ g_value_set_int (value, TEST_OBJECT (gobject)->foo);
+ break;
+ case PROP_BAR:
+ g_value_set_boolean (value, TEST_OBJECT (gobject)->bar);
+ break;
+ case PROP_BAZ:
+ g_value_set_string (value, TEST_OBJECT (gobject)->baz);
+ break;
+ case PROP_BLAH:
+ g_value_set_boxed (value, &(TEST_OBJECT (gobject)->blah));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
+ }
+}
+
+static void
+test_object_class_init (TestObjectClass *klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+ gobject_class->set_property = test_object_set_property;
+ gobject_class->get_property = test_object_get_property;
+ gobject_class->finalize = test_object_finalize;
+
+ g_object_class_install_property (gobject_class,
+ PROP_FOO,
+ g_param_spec_int ("foo", "Foo", "Foo",
+ 0, G_MAXINT, 42,
+ G_PARAM_READWRITE));
+ g_object_class_install_property (gobject_class,
+ PROP_BAR,
+ g_param_spec_boolean ("bar", "Bar", "Bar",
+ FALSE,
+ G_PARAM_READWRITE));
+ g_object_class_install_property (gobject_class,
+ PROP_BAZ,
+ g_param_spec_string ("baz", "Baz", "Baz",
+ NULL,
+ G_PARAM_READWRITE));
+ g_object_class_install_property (gobject_class,
+ PROP_BLAH,
+ g_param_spec_boxed ("blah", "Blah", "Blah",
+ TEST_TYPE_BOXED,
+ G_PARAM_READABLE));
+}
+
+static void
+test_object_init (TestObject *object)
+{
+ object->foo = 42;
+ object->bar = TRUE;
+ object->baz = g_strdup ("Test");
+
+ object->blah.foo = object->foo;
+ object->blah.bar = object->bar;
+}
+
+static void
+test_serialize (void)
+{
+ TestObject *obj = g_object_new (TEST_TYPE_OBJECT, NULL);
+ gchar *data;
+ gsize len;
+
+ data = json_serialize_gobject (G_OBJECT (obj), &len);
+
+ g_assert_cmpint (len, >, 0);
+ if (g_test_verbose ())
+ g_print ("TestObject:\n%s\n", data);
+
+ g_free (data);
+ g_object_unref (obj);
+}
+
+int
+main (int argc,
+ char *argv[])
+{
+ g_type_init ();
+ g_test_init (&argc, &argv, NULL);
+
+ g_test_add_func ("/serialize/gobject-boxed", test_serialize);
+
+ return g_test_run ();
+}
Added: trunk/tests/test-serialize-full.c
==============================================================================
--- (empty file)
+++ trunk/tests/test-serialize-full.c Sat Apr 19 16:34:24 2008
@@ -0,0 +1,351 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <glib-object.h>
+
+#include <json-glib/json-glib.h>
+#include <json-glib/json-gobject.h>
+
+#define TEST_TYPE_ENUM (test_enum_get_type ())
+#define TEST_TYPE_BOXED (test_boxed_get_type ())
+#define TEST_TYPE_OBJECT (test_object_get_type ())
+#define TEST_OBJECT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), TEST_TYPE_OBJECT, TestObject))
+#define TEST_IS_OBJECT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TEST_TYPE_OBJECT))
+#define TEST_OBJECT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), TEST_TYPE_OBJECT, TestObjectClass))
+#define TEST_IS_OBJECT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), TEST_TYPE_OBJECT))
+#define TEST_OBJECT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), TEST_TYPE_OBJECT, TestObjectClass))
+
+typedef enum {
+ TEST_ENUM_FOO,
+ TEST_ENUM_BAR,
+ TEST_ENUM_BAZ
+} TestEnum;
+
+typedef struct _TestBoxed TestBoxed;
+typedef struct _TestObject TestObject;
+typedef struct _TestObjectClass TestObjectClass;
+
+struct _TestBoxed
+{
+ gint foo;
+ gboolean bar;
+};
+
+struct _TestObject
+{
+ GObject parent_instance;
+
+ gint foo;
+ gboolean bar;
+ gchar *baz;
+ TestBoxed blah;
+ TestEnum meh;
+};
+
+struct _TestObjectClass
+{
+ GObjectClass parent_class;
+};
+
+GType test_object_get_type (void);
+
+/*** implementation ***/
+
+static TestBoxed *
+test_boxed_copy (const TestBoxed *src)
+{
+ TestBoxed *copy = g_slice_new (TestBoxed);
+
+ *copy = *src;
+
+ return copy;
+}
+
+static void
+test_boxed_free (TestBoxed *boxed)
+{
+ if (G_LIKELY (boxed))
+ {
+ g_slice_free (TestBoxed, boxed);
+ }
+}
+
+GType
+test_boxed_get_type (void)
+{
+ static GType b_type = 0;
+
+ if (G_UNLIKELY (b_type == 0))
+ b_type = g_boxed_type_register_static ("TestBoxed",
+ (GBoxedCopyFunc) test_boxed_copy,
+ (GBoxedFreeFunc) test_boxed_free);
+
+ return b_type;
+}
+
+GType
+test_enum_get_type (void)
+{
+ static GType e_type = 0;
+
+ if (G_UNLIKELY (e_type == 0))
+ {
+ const GEnumValue values[] = {
+ { TEST_ENUM_FOO, "TEST_ENUM_FOO", "foo" },
+ { TEST_ENUM_BAR, "TEST_ENUM_BAR", "bar" },
+ { TEST_ENUM_BAZ, "TEST_ENUM_BAZ", "baz" },
+ { 0, NULL, NULL }
+ };
+
+ e_type = g_enum_register_static ("TestEnum", values);
+ }
+
+ return e_type;
+}
+
+enum
+{
+ PROP_0,
+
+ PROP_FOO,
+ PROP_BAR,
+ PROP_BAZ,
+ PROP_BLAH,
+ PROP_MEH
+};
+
+static void json_serializable_iface_init (gpointer g_iface);
+
+G_DEFINE_TYPE_WITH_CODE (TestObject, test_object, G_TYPE_OBJECT,
+ G_IMPLEMENT_INTERFACE (JSON_TYPE_SERIALIZABLE,
+ json_serializable_iface_init));
+
+static gboolean
+test_object_deserialize_property (JsonSerializable *serializable,
+ const gchar *name,
+ GValue *value,
+ GParamSpec *pspec,
+ JsonNode *node)
+{
+ gboolean retval = FALSE;
+
+ return retval;
+}
+
+static JsonNode *
+test_object_serialize_property (JsonSerializable *serializable,
+ const gchar *name,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ JsonNode *retval;
+
+ if (strcmp (name, "blah") == 0)
+ {
+ TestBoxed *boxed;
+ JsonObject *obj;
+ JsonNode *val;
+
+ retval = json_node_new (JSON_NODE_OBJECT);
+ obj = json_object_new ();
+
+ boxed = g_value_get_boxed (value);
+
+ val = json_node_new (JSON_NODE_VALUE);
+ json_node_set_int (val, boxed->foo);
+ json_object_add_member (obj, "foo", val);
+
+ val = json_node_new (JSON_NODE_VALUE);
+ json_node_set_boolean (val, boxed->bar);
+ json_object_add_member (obj, "bar", val);
+
+ json_node_take_object (retval, obj);
+
+ test_boxed_free (boxed);
+ }
+ else
+ {
+ GValue copy = { 0, };
+
+ retval = json_node_new (JSON_NODE_VALUE);
+
+ g_value_init (©, G_PARAM_SPEC_VALUE_TYPE (pspec));
+ g_value_copy (value, ©);
+ json_node_set_value (retval, ©);
+ g_value_unset (©);
+ }
+
+ return retval;
+}
+
+static void
+json_serializable_iface_init (gpointer g_iface)
+{
+ JsonSerializableIface *iface = g_iface;
+
+ iface->serialize_property = test_object_serialize_property;
+ iface->deserialize_property = test_object_deserialize_property;
+}
+
+static void
+test_object_finalize (GObject *gobject)
+{
+ g_free (TEST_OBJECT (gobject)->baz);
+
+ G_OBJECT_CLASS (test_object_parent_class)->finalize (gobject);
+}
+
+static void
+test_object_set_property (GObject *gobject,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ switch (prop_id)
+ {
+ case PROP_FOO:
+ TEST_OBJECT (gobject)->foo = g_value_get_int (value);
+ break;
+ case PROP_BAR:
+ TEST_OBJECT (gobject)->bar = g_value_get_boolean (value);
+ break;
+ case PROP_BAZ:
+ g_free (TEST_OBJECT (gobject)->baz);
+ TEST_OBJECT (gobject)->baz = g_value_dup_string (value);
+ break;
+ case PROP_MEH:
+ TEST_OBJECT (gobject)->meh = g_value_get_enum (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
+ }
+}
+
+static void
+test_object_get_property (GObject *gobject,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ switch (prop_id)
+ {
+ case PROP_FOO:
+ g_value_set_int (value, TEST_OBJECT (gobject)->foo);
+ break;
+ case PROP_BAR:
+ g_value_set_boolean (value, TEST_OBJECT (gobject)->bar);
+ break;
+ case PROP_BAZ:
+ g_value_set_string (value, TEST_OBJECT (gobject)->baz);
+ break;
+ case PROP_BLAH:
+ g_value_set_boxed (value, &(TEST_OBJECT (gobject)->blah));
+ break;
+ case PROP_MEH:
+ g_value_set_enum (value, TEST_OBJECT (gobject)->meh);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
+ }
+}
+
+static void
+test_object_class_init (TestObjectClass *klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+ gobject_class->set_property = test_object_set_property;
+ gobject_class->get_property = test_object_get_property;
+ gobject_class->finalize = test_object_finalize;
+
+ g_object_class_install_property (gobject_class,
+ PROP_FOO,
+ g_param_spec_int ("foo", "Foo", "Foo",
+ 0, G_MAXINT, 42,
+ G_PARAM_READWRITE));
+ g_object_class_install_property (gobject_class,
+ PROP_BAR,
+ g_param_spec_boolean ("bar", "Bar", "Bar",
+ FALSE,
+ G_PARAM_READWRITE));
+ g_object_class_install_property (gobject_class,
+ PROP_BAZ,
+ g_param_spec_string ("baz", "Baz", "Baz",
+ NULL,
+ G_PARAM_READWRITE));
+ g_object_class_install_property (gobject_class,
+ PROP_BLAH,
+ g_param_spec_boxed ("blah", "Blah", "Blah",
+ TEST_TYPE_BOXED,
+ G_PARAM_READABLE));
+ g_object_class_install_property (gobject_class,
+ PROP_MEH,
+ g_param_spec_enum ("meh", "Meh", "Meh",
+ TEST_TYPE_ENUM,
+ TEST_ENUM_BAR,
+ G_PARAM_READWRITE));
+}
+
+static void
+test_object_init (TestObject *object)
+{
+ object->foo = 0;
+ object->bar = TRUE;
+ object->baz = NULL;
+
+ object->blah.foo = object->foo;
+ object->blah.bar = object->bar;
+
+ object->meh = TEST_ENUM_BAR;
+}
+
+static const gchar *var_test =
+"{\n"
+" \"foo\" : 42,\n"
+" \"bar\" : false,\n"
+" \"baz\" : \"hello\",\n"
+" \"meh\" : \"baz\"\n"
+"}";
+
+static void
+test_deserialize (void)
+{
+ GObject *object;
+ GError *error;
+
+ error = NULL;
+ object = json_construct_gobject (TEST_TYPE_OBJECT, var_test, -1, &error);
+ if (error)
+ g_error ("*** Unable to parse buffer: %s\n", error->message);
+
+ if (g_test_verbose ())
+ g_print ("*** TestObject ***\n"
+ " foo: %s\n"
+ " bar: %s\n"
+ " baz: %s\n"
+ " meh: %s\n",
+ TEST_OBJECT (object)->foo == 42 ? "<true>" : "<false>",
+ TEST_OBJECT (object)->bar == FALSE ? "<true>" : "<false>",
+ TEST_OBJECT (object)->baz != NULL ? "<true>" : "<false>",
+ TEST_OBJECT (object)->meh == TEST_ENUM_BAZ ? "<true>" : "<false>");
+
+ g_assert_cmpint (TEST_OBJECT (object)->foo, ==, 42);
+ g_assert_cmpint (TEST_OBJECT (object)->bar, ==, FALSE);
+ g_assert_cmpstr (TEST_OBJECT (object)->baz, ==, "hello");
+ g_assert_cmpint (TEST_OBJECT (object)->meh, ==, TEST_ENUM_BAZ);
+
+ g_object_unref (object);
+}
+
+int
+main (int argc,
+ char *argv[])
+{
+ g_type_init ();
+ g_test_init (&argc, &argv, NULL);
+
+ g_test_add_func ("/deserialize/json-to-gobject", test_deserialize);
+
+ return g_test_run ();
+}
Added: trunk/tests/test-serialize-simple.c
==============================================================================
--- (empty file)
+++ trunk/tests/test-serialize-simple.c Sat Apr 19 16:34:24 2008
@@ -0,0 +1,163 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <glib-object.h>
+
+#include <json-glib/json-glib.h>
+#include <json-glib/json-gobject.h>
+
+#define TEST_TYPE_OBJECT (test_object_get_type ())
+#define TEST_OBJECT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), TEST_TYPE_OBJECT, TestObject))
+#define TEST_IS_OBJECT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TEST_TYPE_OBJECT))
+#define TEST_OBJECT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), TEST_TYPE_OBJECT, TestObjectClass))
+#define TEST_IS_OBJECT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), TEST_TYPE_OBJECT))
+#define TEST_OBJECT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), TEST_TYPE_OBJECT, TestObjectClass))
+
+typedef struct _TestObject TestObject;
+typedef struct _TestObjectClass TestObjectClass;
+
+struct _TestObject
+{
+ GObject parent_instance;
+
+ gint foo;
+ gboolean bar;
+ gchar *baz;
+};
+
+struct _TestObjectClass
+{
+ GObjectClass parent_class;
+};
+
+GType test_object_get_type (void);
+
+/*** implementation ***/
+
+enum
+{
+ PROP_0,
+
+ PROP_FOO,
+ PROP_BAR,
+ PROP_BAZ
+};
+
+G_DEFINE_TYPE (TestObject, test_object, G_TYPE_OBJECT);
+
+static void
+test_object_finalize (GObject *gobject)
+{
+ g_free (TEST_OBJECT (gobject)->baz);
+
+ G_OBJECT_CLASS (test_object_parent_class)->finalize (gobject);
+}
+
+static void
+test_object_set_property (GObject *gobject,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ switch (prop_id)
+ {
+ case PROP_FOO:
+ TEST_OBJECT (gobject)->foo = g_value_get_int (value);
+ break;
+ case PROP_BAR:
+ TEST_OBJECT (gobject)->bar = g_value_get_boolean (value);
+ break;
+ case PROP_BAZ:
+ g_free (TEST_OBJECT (gobject)->baz);
+ TEST_OBJECT (gobject)->baz = g_value_dup_string (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
+ }
+}
+
+static void
+test_object_get_property (GObject *gobject,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ switch (prop_id)
+ {
+ case PROP_FOO:
+ g_value_set_int (value, TEST_OBJECT (gobject)->foo);
+ break;
+ case PROP_BAR:
+ g_value_set_boolean (value, TEST_OBJECT (gobject)->bar);
+ break;
+ case PROP_BAZ:
+ g_value_set_string (value, TEST_OBJECT (gobject)->baz);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
+ }
+}
+
+static void
+test_object_class_init (TestObjectClass *klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+ gobject_class->set_property = test_object_set_property;
+ gobject_class->get_property = test_object_get_property;
+ gobject_class->finalize = test_object_finalize;
+
+ g_object_class_install_property (gobject_class,
+ PROP_FOO,
+ g_param_spec_int ("foo", "Foo", "Foo",
+ 0, G_MAXINT, 42,
+ G_PARAM_READWRITE));
+ g_object_class_install_property (gobject_class,
+ PROP_BAR,
+ g_param_spec_boolean ("bar", "Bar", "Bar",
+ FALSE,
+ G_PARAM_READWRITE));
+ g_object_class_install_property (gobject_class,
+ PROP_BAZ,
+ g_param_spec_string ("baz", "Baz", "Baz",
+ NULL,
+ G_PARAM_READWRITE));
+}
+
+static void
+test_object_init (TestObject *object)
+{
+ object->foo = 42;
+ object->bar = TRUE;
+ object->baz = g_strdup ("Test");
+}
+
+static void
+test_serialize (void)
+{
+ TestObject *obj = g_object_new (TEST_TYPE_OBJECT, NULL);
+ gchar *data;
+ gsize len;
+
+ data = json_serialize_gobject (G_OBJECT (obj), &len);
+
+ g_assert_cmpint (len, >, 0);
+ if (g_test_verbose ())
+ g_print ("TestObject:\n%s\n", data);
+
+ g_free (data);
+ g_object_unref (obj);
+}
+
+int
+main (int argc,
+ char *argv[])
+{
+ g_type_init ();
+ g_test_init (&argc, &argv, NULL);
+
+ g_test_add_func ("/serialize/gobject", test_serialize);
+
+ return g_test_run ();
+}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]