2007-10-08 Emmanuele Bassi <ebassi@openedhand.com>

Initial implementation of the UI definition files. (#424)

	* clutter/json/Makefile.am:
	* clutter/json/*.[ch]: In-tree copy of JSON-GLib, a GLib-based
	JSON parser/generator library. We use it in-tree because we might
	need to change the API. Ideally, we'd depend on it.

	* clutter/clutter.h:
	* clutter/clutter-script-private.h:
	* clutter/clutter-script.[ch]: ClutterScript, the scenegraph
	generator class. It parses JSON streams in form of buffers and
	files and builds the scene.

	* clutter/clutter-debug.h:
	* clutter/clutter-main.c: Add a "script" debug flag

	* clutter/Makefile.am: Build glue.

	* tests/Makefile.am:
	* tests/test-script.c: Add a test case for the ClutterScript.

	* configure.ac: Depend on GLib 2.14, so we can use the
	g_hash_table_get_key() and g_hash_table_get_values() functions
	for the time being; we can probably reimplement those, but we
	are going to need 2.14 anyway if we are going to implement a
	list model using GSequence.
This commit is contained in:
Emmanuele Bassi 2007-10-08 15:03:22 +00:00
parent 7b3464226c
commit 991562f536
24 changed files with 4202 additions and 13 deletions

View File

@ -1,4 +1,33 @@
2007-10-03 Emmanuele Bassi <ebassi@openedhand.com>
2007-10-08 Emmanuele Bassi <ebassi@openedhand.com>
Initial implementation of the UI definition files. (#424)
* clutter/json/Makefile.am:
* clutter/json/*.[ch]: In-tree copy of JSON-GLib, a GLib-based
JSON parser/generator library. We use it in-tree because we might
need to change the API. Ideally, we'd depend on it.
* clutter/clutter.h:
* clutter/clutter-script-private.h:
* clutter/clutter-script.[ch]: ClutterScript, the scenegraph
generator class. It parses JSON streams in form of buffers and
files and builds the scene.
* clutter/clutter-debug.h:
* clutter/clutter-main.c: Add a "script" debug flag
* clutter/Makefile.am: Build glue.
* tests/Makefile.am:
* tests/test-script.c: Add a test case for the ClutterScript.
* configure.ac: Depend on GLib 2.14, so we can use the
g_hash_table_get_key() and g_hash_table_get_values() functions
for the time being; we can probably reimplement those, but we
are going to need 2.14 anyway if we are going to implement a
list model using GSequence.
2007-10-08 Emmanuele Bassi <ebassi@openedhand.com>
* tests/test-behave.c: Use the right return type for the
event callbacks.

View File

@ -1,8 +1,8 @@
NULL =
SUBDIRS = cogl pango $(clutterbackend)
SUBDIRS = cogl pango json $(clutterbackend)
DIST_SUBDIRS = pango glx eglx eglnative cogl sdl
DIST_SUBDIRS = pango glx eglx eglnative cogl sdl json
target = $(clutterbackend)
@ -17,6 +17,7 @@ INCLUDES = \
-I$(top_srcdir)/clutter/pango \
-I$(top_srcdir)/clutter/cogl \
-I$(top_srcdir)/clutter/cogl/@CLUTTER_COGL@ \
-I$(top_srcdir)/clutter/json \
-DPREFIX=\""$(prefix)"\" \
-DLIBDIR=\""$(libdir)"\" \
-DDATADIR=\""$(datadir)"\" \
@ -65,6 +66,7 @@ source_h = \
$(srcdir)/clutter-main.h \
$(srcdir)/clutter-media.h \
$(srcdir)/clutter-rectangle.h \
$(srcdir)/clutter-script.h \
$(srcdir)/clutter-stage.h \
$(srcdir)/clutter-texture.h \
$(srcdir)/clutter-timeline.h \
@ -153,6 +155,7 @@ source_c = \
clutter-texture.c \
clutter-timeline.c \
clutter-score.c \
clutter-script.c \
clutter-timeout-pool.c \
clutter-util.c \
clutter-vbox.c \
@ -162,13 +165,16 @@ source_h_priv = \
clutter-keysyms-table.h \
clutter-debug.h \
clutter-private.h \
clutter-script-private.h \
$(NULL)
libclutter_@CLUTTER_FLAVOUR@_@CLUTTER_API_VERSION@_la_LIBADD = \
$(CLUTTER_LIBS) pango/libpangoclutter.la \
@CLUTTER_FLAVOUR@/libclutter-@CLUTTER_FLAVOUR@.la \
cogl/@CLUTTER_COGL@/libclutter-cogl.la
libclutter_@CLUTTER_FLAVOUR@_@CLUTTER_API_VERSION@_la_LIBADD = \
$(CLUTTER_LIBS) \
pango/libpangoclutter.la \
@CLUTTER_FLAVOUR@/libclutter-@CLUTTER_FLAVOUR@.la \
cogl/@CLUTTER_COGL@/libclutter-cogl.la \
json/libclutter-json.la
libclutter_@CLUTTER_FLAVOUR@_@CLUTTER_API_VERSION@_la_SOURCES = \
$(source_c) $(source_h) $(source_h_priv)

View File

@ -17,7 +17,8 @@ typedef enum {
CLUTTER_DEBUG_BEHAVIOUR = 1 << 7,
CLUTTER_DEBUG_PANGO = 1 << 8,
CLUTTER_DEBUG_BACKEND = 1 << 9,
CLUTTER_DEBUG_SCHEDULER = 1 << 10
CLUTTER_DEBUG_SCHEDULER = 1 << 10,
CLUTTER_DEBUG_SCRIPT = 1 << 11
} ClutterDebugFlag;
#ifdef CLUTTER_ENABLE_DEBUG

View File

@ -78,6 +78,7 @@ static const GDebugKey clutter_debug_keys[] = {
{ "pango", CLUTTER_DEBUG_PANGO },
{ "backend", CLUTTER_DEBUG_BACKEND },
{ "scheduler", CLUTTER_DEBUG_SCHEDULER },
{ "script", CLUTTER_DEBUG_SCRIPT },
};
#endif /* CLUTTER_ENABLE_DEBUG */

View File

@ -0,0 +1,31 @@
#ifndef __CLUTTER_SCRIPT_PRIVATE_H__
#define __CLUTTER_SCRIPT_PRIVATE_H__
#include <glib-object.h>
#include "clutter-script.h"
G_BEGIN_DECLS
typedef GType (* GTypeGetFunc) (void);
typedef struct {
gchar *class_name;
gchar *id;
GList *properties;
GType gtype;
GObject *object;
} ObjectInfo;
typedef struct {
gchar *property_name;
GValue value;
} PropertyInfo;
GObject *clutter_script_construct_object (ClutterScript *script,
ObjectInfo *info);
G_END_DECLS
#endif /* __CLUTTER_SCRIPT_PRIVATE_H__ */

765
clutter/clutter-script.c Normal file
View File

@ -0,0 +1,765 @@
#include "config.h"
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>
#include <glib.h>
#include <glib-object.h>
#include "clutter-actor.h"
#include "clutter-stage.h"
#include "clutter-container.h"
#include "clutter-scriptable.h"
#include "clutter-script.h"
#include "clutter-script-private.h"
#include "clutter-private.h"
#include "clutter-debug.h"
#include "json/json-parser.h"
#define CLUTTER_SCRIPT_GET_PRIVATE(obj) \
(G_TYPE_INSTANCE_GET_PRIVATE ((obj), CLUTTER_TYPE_SCRIPT, ClutterScriptPrivate))
struct _ClutterScriptPrivate
{
GHashTable *objects;
guint last_merge_id;
JsonParser *parser;
ObjectInfo *current;
gchar *filename;
guint is_filename : 1;
};
G_DEFINE_TYPE (ClutterScript, clutter_script, G_TYPE_OBJECT);
/* tries to map a name in camel case into the corresponding get_type()
* function, e.g.:
*
* ClutterRectangle -> clutter_rectangle_get_type
* ClutterCloneTexture -> clutter_clone_texture_get_type
*
* taken from GTK+, gtkbuilder.c
*/
static GType
resolve_type_lazily (const gchar *name)
{
static GModule *module = NULL;
GTypeGetFunc func;
GString *symbol_name = g_string_new ("");
char c, *symbol;
int i;
GType gtype = G_TYPE_INVALID;
if (!module)
module = g_module_open (NULL, 0);
for (i = 0; name[i] != '\0'; i++)
{
c = name[i];
/* skip if uppercase, first or previous is uppercase */
if ((c == g_ascii_toupper (c) &&
i > 0 && name[i-1] != g_ascii_toupper (name[i-1])) ||
(i > 2 && name[i] == g_ascii_toupper (name[i]) &&
name[i-1] == g_ascii_toupper (name[i-1]) &&
name[i-2] == g_ascii_toupper (name[i-2])))
g_string_append_c (symbol_name, '_');
g_string_append_c (symbol_name, g_ascii_tolower (c));
}
g_string_append (symbol_name, "_get_type");
symbol = g_string_free (symbol_name, FALSE);
if (g_module_symbol (module, symbol, (gpointer)&func))
gtype = func ();
g_free (symbol);
return gtype;
}
static void
warn_missing_attribute (ClutterScript *script,
const gchar *id,
const gchar *attribute)
{
ClutterScriptPrivate *priv = script->priv;
if (G_LIKELY (id))
{
g_warning ("%s: %d: object `%s' has no `%s' attribute",
priv->is_filename ? priv->filename : "<input>",
json_parser_get_current_line (priv->parser),
id,
attribute);
}
else
{
g_warning ("%s: %d: object has no `%s' attribute",
priv->is_filename ? priv->filename : "<input>",
json_parser_get_current_line (priv->parser),
attribute);
}
}
static void
warn_invalid_value (ClutterScript *script,
const gchar *attribute,
JsonNode *node)
{
ClutterScriptPrivate *priv = script->priv;
if (G_LIKELY (node))
{
g_warning ("%s: %d: invalid value of type `%s' for attribute `%s'",
priv->is_filename ? priv->filename : "<input>",
json_parser_get_current_line (priv->parser),
json_node_type_name (node),
attribute);
}
else
{
g_warning ("%s: %d: invalid value for attribute `%s'",
priv->is_filename ? priv->filename : "<input>",
json_parser_get_current_line (priv->parser),
attribute);
}
}
static PropertyInfo *
parse_member_to_property (ClutterScript *script,
ObjectInfo *info,
const gchar *name,
JsonNode *node)
{
PropertyInfo *retval;
GValue value = { 0, };
retval = g_slice_new (PropertyInfo);
retval->property_name = g_strdup (name);
switch (JSON_NODE_TYPE (node))
{
case JSON_NODE_VALUE:
json_node_get_value (node, &value);
g_value_init (&retval->value, G_VALUE_TYPE (&value));
g_value_copy (&value, &retval->value);
g_value_unset (&value);
break;
case JSON_NODE_OBJECT:
break;
case JSON_NODE_ARRAY:
if (strcmp (name, "geometry") == 0)
{
JsonArray *array = json_node_get_array (node);
JsonNode *val;
gint i;
ClutterGeometry geom = { 0, };
/* this is quite evil indeed */
for (i = 0; i < json_array_get_length (array); i++)
{
val = json_array_get_element (array, i);
switch (i)
{
case 0: geom.x = json_node_get_int (val); break;
case 1: geom.y = json_node_get_int (val); break;
case 2: geom.width = json_node_get_int (val); break;
case 3: geom.height = json_node_get_int (val); break;
}
}
g_value_init (&retval->value, CLUTTER_TYPE_GEOMETRY);
g_value_set_boxed (&retval->value, &geom);
}
else if (strcmp (name, "children") == 0)
{
JsonArray *array = json_node_get_array (node);
JsonNode *val;
gint i, array_len;
GList *children;
children = NULL;
array_len = json_array_get_length (array);
for (i = 0; i < array_len; i++)
{
JsonObject *object;
val = json_array_get_element (array, i);
switch (JSON_NODE_TYPE (val))
{
case JSON_NODE_OBJECT:
object = json_node_get_object (val);
if (json_object_has_member (object, "id") &&
json_object_has_member (object, "type"))
{
JsonNode *id = json_object_get_member (object, "id");
children = g_list_prepend (children,
json_node_dup_string (id));
}
break;
case JSON_NODE_VALUE:
if (json_node_get_string (val))
children = g_list_prepend (children,
json_node_dup_string (val));
break;
default:
warn_invalid_value (script, "children", val);
break;
}
}
g_value_init (&retval->value, G_TYPE_POINTER);
g_value_set_pointer (&retval->value, children);
}
break;
case JSON_NODE_NULL:
break;
}
return retval;
}
static void
json_object_end (JsonParser *parser,
JsonObject *object,
gpointer user_data)
{
ClutterScript *script = user_data;
ClutterScriptPrivate *priv = script->priv;
ObjectInfo *oinfo;
JsonNode *val;
GList *members, *l;
if (!json_object_has_member (object, "id"))
return;
if (!json_object_has_member (object, "type"))
{
val = json_object_get_member (object, "id");
warn_missing_attribute (script, json_node_get_string (val), "type");
return;
}
oinfo = g_slice_new0 (ObjectInfo);
val = json_object_get_member (object, "id");
oinfo->id = json_node_dup_string (val);
val = json_object_get_member (object, "type");
oinfo->class_name = json_node_dup_string (val);
members = json_object_get_members (object);
for (l = members; l; l = l->next)
{
const gchar *name = l->data;
val = json_object_get_member (object, name);
if (strcmp (name, "id") == 0 || strcmp (name, "type") == 0)
continue;
else
{
PropertyInfo *pinfo;
pinfo = parse_member_to_property (script, oinfo, name, val);
oinfo->properties = g_list_prepend (oinfo->properties, pinfo);
CLUTTER_NOTE (SCRIPT, "Added property `%s' (type:%s) for class `%s'",
pinfo->property_name,
g_type_name (G_VALUE_TYPE (&pinfo->value)),
oinfo->class_name);
}
}
g_list_free (members);
CLUTTER_NOTE (SCRIPT, "Added object `%s' (type:%s) with %d properties",
oinfo->id,
oinfo->class_name,
g_list_length (oinfo->properties));
g_hash_table_replace (priv->objects, g_strdup (oinfo->id), oinfo);
}
static gboolean
translate_property (const gchar *name,
const GValue *src,
GValue *dest)
{
if (strcmp (name, "color") == 0)
{
ClutterColor color = { 0, };
const gchar *color_str;
if (G_VALUE_HOLDS (src, G_TYPE_STRING))
color_str = g_value_get_string (src);
else
color_str = NULL;
clutter_color_parse (color_str, &color);
g_value_init (dest, CLUTTER_TYPE_COLOR);
g_value_set_boxed (dest, &color);
return TRUE;
}
else if (strcmp (name, "pixbuf") == 0)
{
GdkPixbuf *pixbuf = NULL;
const gchar *path;
if (G_VALUE_HOLDS (src, G_TYPE_STRING))
path = g_value_get_string (src);
else
path = NULL;
if (path && g_path_is_absolute (path))
{
GError *error = NULL;
pixbuf = gdk_pixbuf_new_from_file (path, &error);
if (error)
{
g_warning ("Unable to open pixbuf at path `%s': %s",
path,
error->message);
g_error_free (error);
}
}
g_value_init (dest, GDK_TYPE_PIXBUF);
g_value_set_object (dest, pixbuf);
return TRUE;
}
return FALSE;
}
static void
translate_properties (ClutterScript *script,
ObjectInfo *oinfo,
guint *n_params,
GParameter **params)
{
GList *l;
GParamSpec *pspec;
GObjectClass *oclass;
GArray *parameters;
oclass = g_type_class_ref (oinfo->gtype);
g_assert (oclass != NULL);
parameters = g_array_new (FALSE, FALSE, sizeof (GParameter));
for (l = oinfo->properties; l; l = l->next)
{
PropertyInfo *pinfo = l->data;
GParameter param = { NULL };
if (strcmp (pinfo->property_name, "children") == 0)
continue;
pspec = g_object_class_find_property (oclass, pinfo->property_name);
if (!pspec)
{
g_warning ("Unknown property `%s' for class `%s'",
pinfo->property_name,
g_type_name (oinfo->gtype));
continue;
}
param.name = pinfo->property_name;
if (!translate_property (param.name, &pinfo->value, &param.value))
{
g_value_init (&param.value, G_PARAM_SPEC_VALUE_TYPE (pspec));
g_value_copy (&pinfo->value, &param.value);
}
g_array_append_val (parameters, param);
}
*n_params = parameters->len;
*params = (GParameter *) g_array_free (parameters, FALSE);
g_type_class_unref (oclass);
}
static void
add_children (ClutterScript *script,
ClutterContainer *container,
GList *children)
{
GObject *object;
GList *l;
for (l = children; l != NULL; l = l->next)
{
const gchar *name = l->data;
object = clutter_script_get_object (script, name);
if (!object)
{
ObjectInfo *oinfo;
oinfo = g_hash_table_lookup (script->priv->objects, name);
if (oinfo)
object = clutter_script_construct_object (script, oinfo);
else
continue;
}
CLUTTER_NOTE (SCRIPT, "Adding children `%s' to actor of type `%s'",
name,
g_type_name (G_OBJECT_TYPE (container)));
clutter_container_add_actor (container, CLUTTER_ACTOR (object));
}
}
static GObject *
construct_stage (ClutterScript *script,
ObjectInfo *oinfo)
{
GObjectClass *oclass = g_type_class_ref (CLUTTER_TYPE_STAGE);
GList *l;
if (oinfo->object)
return oinfo->object;
oinfo->object = G_OBJECT (clutter_stage_get_default ());
for (l = oinfo->properties; l; l = l->next)
{
PropertyInfo *pinfo = l->data;
const gchar *name = pinfo->property_name;
GParamSpec *pspec;
GValue value = { 0, };
/* "children" is a fake property: we use it so we can construct
* the list of children of a given container
*/
if (strcmp (name, "children") == 0)
{
GList *children = g_value_get_pointer (&pinfo->value);
/* we know ClutterStage is a ClutterContainer */
add_children (script, CLUTTER_CONTAINER (oinfo->object), children);
/* unset, so we don't leak it later */
g_list_foreach (children, (GFunc) g_free, NULL);
g_list_free (children);
g_value_set_pointer (&pinfo->value, NULL);
continue;
}
pspec = g_object_class_find_property (oclass, name);
if (!pspec)
{
g_warning ("Unknown property `%s' for class `ClutterStage'",
name);
continue;
}
if (!translate_property (name, &pinfo->value, &value))
{
g_value_init (&value, G_PARAM_SPEC_VALUE_TYPE (pspec));
g_value_copy (&pinfo->value, &value);
}
g_object_set_property (oinfo->object, name, &value);
g_value_unset (&value);
}
g_type_class_unref (oclass);
g_object_set_data_full (oinfo->object, "clutter-script-name",
g_strdup (oinfo->id),
g_free);
return oinfo->object;
}
GObject *
clutter_script_construct_object (ClutterScript *script,
ObjectInfo *oinfo)
{
GType gtype;
guint n_params, i;
GParameter *params;
if (oinfo->object)
return oinfo->object;
gtype = resolve_type_lazily (oinfo->class_name);
if (gtype == G_TYPE_INVALID)
return NULL;
/* the stage is a special case: it's a singleton, it cannot
* be created by the user and it's owned by the backend. hence,
* we cannot follow the usual pattern here
*/
if (g_type_is_a (gtype, CLUTTER_TYPE_STAGE))
return construct_stage (script, oinfo);
oinfo->gtype = gtype;
params = NULL;
translate_properties (script, oinfo, &n_params, &params);
CLUTTER_NOTE (SCRIPT, "Creating instance for type `%s' (params:%d)",
g_type_name (gtype),
n_params);
oinfo->object = g_object_newv (gtype, n_params, params);
g_object_set_data_full (oinfo->object, "clutter-script-name",
g_strdup (oinfo->id),
g_free);
for (i = 0; i < n_params; i++)
{
GParameter param = params[i];
g_value_unset (&param.value);
}
g_free (params);
return oinfo->object;
}
static void
json_parse_end (JsonParser *parser,
gpointer user_data)
{
ClutterScript *script = user_data;
ClutterScriptPrivate *priv = script->priv;
GList *objects, *l;
objects = g_hash_table_get_values (priv->objects);
for (l = objects; l; l = l->next)
{
ObjectInfo *oinfo = l->data;
oinfo->object = clutter_script_construct_object (script, oinfo);
}
g_list_free (objects);
}
static void
object_info_free (gpointer data)
{
if (G_LIKELY (data))
{
ObjectInfo *oinfo = data;
GList *l;
g_free (oinfo->class_name);
g_free (oinfo->id);
for (l = oinfo->properties; l; l = l->next)
{
PropertyInfo *pinfo = l->data;
g_free (pinfo->property_name);
g_value_unset (&pinfo->value);
}
g_list_free (oinfo->properties);
if (oinfo->object)
g_object_unref (oinfo->object);
g_slice_free (ObjectInfo, oinfo);
}
}
static void
clutter_script_finalize (GObject *gobject)
{
ClutterScriptPrivate *priv = CLUTTER_SCRIPT_GET_PRIVATE (gobject);
g_object_unref (priv->parser);
g_hash_table_destroy (priv->objects);
g_free (priv->filename);
G_OBJECT_CLASS (clutter_script_parent_class)->finalize (gobject);
}
static void
clutter_script_class_init (ClutterScriptClass *klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
g_type_class_add_private (klass, sizeof (ClutterScriptPrivate));
gobject_class->finalize = clutter_script_finalize;
}
static void
clutter_script_init (ClutterScript *script)
{
ClutterScriptPrivate *priv;
script->priv = priv = CLUTTER_SCRIPT_GET_PRIVATE (script);
priv->parser = json_parser_new ();
g_signal_connect (priv->parser,
"object-end", G_CALLBACK (json_object_end),
script);
g_signal_connect (priv->parser,
"parse-end", G_CALLBACK (json_parse_end),
script);
priv->is_filename = FALSE;
priv->last_merge_id = 0;
priv->objects = g_hash_table_new_full (g_str_hash, g_str_equal,
g_free,
object_info_free);
}
ClutterScript *
clutter_script_new (void)
{
return g_object_new (CLUTTER_TYPE_SCRIPT, NULL);
}
guint
clutter_script_load_from_file (ClutterScript *script,
const gchar *filename,
GError **error)
{
ClutterScriptPrivate *priv;
GError *internal_error;
g_return_val_if_fail (CLUTTER_IS_SCRIPT (script), 0);
g_return_val_if_fail (filename != NULL, 0);
priv = script->priv;
g_free (priv->filename);
priv->filename = g_strdup (filename);
priv->is_filename = TRUE;
internal_error = NULL;
json_parser_load_from_file (priv->parser, filename, &internal_error);
if (internal_error)
{
g_propagate_error (error, internal_error);
return 0;
}
else
priv->last_merge_id += 1;
return priv->last_merge_id;
}
guint
clutter_script_load_from_data (ClutterScript *script,
const gchar *data,
gsize length,
GError **error)
{
ClutterScriptPrivate *priv;
GError *internal_error;
g_return_val_if_fail (CLUTTER_IS_SCRIPT (script), 0);
g_return_val_if_fail (data != NULL, 0);
priv = script->priv;
g_free (priv->filename);
priv->filename = NULL;
priv->is_filename = FALSE;
internal_error = NULL;
json_parser_load_from_data (priv->parser, data, length, &internal_error);
if (internal_error)
{
g_propagate_error (error, internal_error);
return 0;
}
else
priv->last_merge_id += 1;
return priv->last_merge_id;
}
GObject *
clutter_script_get_object (ClutterScript *script,
const gchar *name)
{
ObjectInfo *oinfo;
g_return_val_if_fail (CLUTTER_IS_SCRIPT (script), NULL);
g_return_val_if_fail (name != NULL, NULL);
oinfo = g_hash_table_lookup (script->priv->objects, name);
if (!oinfo)
return NULL;
return oinfo->object;
}
static GList *
clutter_script_get_objects_valist (ClutterScript *script,
const gchar *first_name,
va_list args)
{
GList *retval = NULL;
const gchar *name;
name = first_name;
while (name)
{
retval =
g_list_prepend (retval, clutter_script_get_object (script, name));
name = va_arg (args, gchar*);
}
return g_list_reverse (retval);
}
GList *
clutter_script_get_objects (ClutterScript *script,
const gchar *first_name,
...)
{
GList *retval = NULL;
va_list var_args;
g_return_val_if_fail (CLUTTER_IS_SCRIPT (script), NULL);
g_return_val_if_fail (first_name != NULL, NULL);
va_start (var_args, first_name);
retval = clutter_script_get_objects_valist (script, first_name, var_args);
va_end (var_args);
return retval;
}
gboolean
clutter_script_value_from_data (ClutterScript *script,
GType gtype,
const gchar *data,
GValue *value,
GError **error)
{
return FALSE;
}

78
clutter/clutter-script.h Normal file
View File

@ -0,0 +1,78 @@
#ifndef __CLUTTER_SCRIPT_H__
#define __CLUTTER_SCRIPT_H__
#include <glib-object.h>
G_BEGIN_DECLS
#define CLUTTER_TYPE_SCRIPT (clutter_script_get_type ())
#define CLUTTER_SCRIPT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CLUTTER_TYPE_SCRIPT, ClutterScript))
#define CLUTTER_IS_SCRIPT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CLUTTER_TYPE_SCRIPT))
#define CLUTTER_SCRIPT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CLUTTER_TYPE_SCRIPT, ClutterScriptClass))
#define CLUTTER_IS_SCRIPT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CLUTTER_TYPE_SCRIPT))
#define CLUTTER_SCRIPT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CLUTTER_TYPE_SCRIPT, ClutterScript))
typedef struct _ClutterScript ClutterScript;
typedef struct _ClutterScriptPrivate ClutterScriptPrivate;
typedef struct _ClutterScriptClass ClutterScriptClass;
/**
* ClutterScriptError:
* @CLUTTER_SCRIPT_ERROR_INVALID_VALUE: Invalid value
*
* #ClutterScript error enumeration.
*
* Since: 0.6
*/
typedef enum {
CLUTTER_SCRIPT_ERROR_INVALID_TYPE_FUNCTION,
CLUTTER_SCRIPT_ERROR_INVALID_PROPERTY,
CLUTTER_SCRIPT_ERROR_INVALID_VALUE
} ClutterScriptError;
#define CLUTTER_SCRIPT_ERROR (clutter_script_error_quark ())
GQuark clutter_script_error_quark (void);
struct _ClutterScript
{
/*< private >*/
GObject parent_instance;
ClutterScriptPrivate *priv;
};
struct _ClutterScriptClass
{
/*< private >*/
GObjectClass parent_class;
/* padding, for future expansion */
void (*_clutter_reserved1) (void);
void (*_clutter_reserved2) (void);
void (*_clutter_reserved3) (void);
void (*_clutter_reserved4) (void);
void (*_clutter_reserved5) (void);
void (*_clutter_reserved6) (void);
void (*_clutter_reserved7) (void);
void (*_clutter_reserved8) (void);
};
GType clutter_script_get_type (void) G_GNUC_CONST;
ClutterScript *clutter_script_new (void);
guint clutter_script_load_from_file (ClutterScript *script,
const gchar *filename,
GError **error);
guint clutter_script_load_from_data (ClutterScript *script,
const gchar *data,
gsize length,
GError **error);
GObject * clutter_script_get_object (ClutterScript *script,
const gchar *name);
GList * clutter_script_get_objects (ClutterScript *script,
const gchar *first_name,
...) G_GNUC_NULL_TERMINATED;
G_END_DECLS
#endif /* __CLUTTER_SCRIPT_H__ */

View File

@ -58,6 +58,7 @@
#include "clutter-timeout-pool.h"
#include "clutter-timeline.h"
#include "clutter-score.h"
#include "clutter-script.h"
#include "clutter-types.h"
#include "clutter-units.h"
#include "clutter-util.h"

26
clutter/json/Makefile.am Normal file
View File

@ -0,0 +1,26 @@
source_c = \
json-array.c \
json-generator.c \
json-marshal.c \
json-node.c \
json-object.c \
json-parser.c
source_h = \
json-generator.h \
json-glib.h \
json-marshal.h \
json-parser.h \
json-types.h
noinst_LTLIBRARIES = libclutter-json.la
libclutter_json_la_SOURCES = $(source_c) $(source_h)
INCLUDES = \
-I$(top_srcdir) \
-DG_DISABLE_DEPRECATED \
$(GCC_FLAGS) \
$(CLUTTER_CFLAGS) \
$(CLUTTER_DEBUG_CFLAGS)

231
clutter/json/json-array.c Normal file
View File

@ -0,0 +1,231 @@
/* json-array.c - JSON array implementation
*
* This file is part of JSON-GLib
* Copyright (C) 2007 OpenedHand Ltd.
*
* 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.
*
* Author:
* Emmanuele Bassi <ebassi@openedhand.com>
*/
#include "config.h"
#include "json-types.h"
/**
* SECTION:json-array
* @short_description: a JSON array representation
*
* #JsonArray is the representation of the array type inside JSON. It contains
* #JsonNode<!-- -->s, which may contain fundamental types, other arrays or
* objects.
*
* Since arrays can be expensive, they are reference counted. You can control
* the lifetime of a #JsonArray using json_array_ref() and json_array_unref().
*
* To extract an element at a given index, use json_array_get_element().
* To retrieve the entire array in list form, use json_array_get_elements().
* To retrieve the length of the array, use json_array_get_length().
*/
struct _JsonArray
{
GPtrArray *elements;
volatile gint ref_count;
};
GType
json_array_get_type (void)
{
static GType array_type = 0;
if (G_UNLIKELY (!array_type))
array_type = g_boxed_type_register_static ("JsonArray",
(GBoxedCopyFunc) json_array_ref,
(GBoxedFreeFunc) json_array_unref);
return array_type;
}
/**
* json_array_new:
*
* Creates a new #JsonArray.
*
* Return value: the newly created #JsonArray
*/
JsonArray *
json_array_new (void)
{
JsonArray *array;
array = g_slice_new (JsonArray);
array->ref_count = 1;
array->elements = g_ptr_array_new ();
return array;
}
/**
* json_array_sized_new:
* @n_elements: number of slots to pre-allocate
*
* Creates a new #JsonArray with @n_elements slots already allocated.
*
* Return value: the newly created #JsonArray
*/
JsonArray *
json_array_sized_new (guint n_elements)
{
JsonArray *array;
array = g_slice_new (JsonArray);
array->ref_count = 1;
array->elements = g_ptr_array_sized_new (n_elements);
return array;
}
/**
* json_array_ref:
* @array: a #JsonArray
*
* Increase by one the reference count of a #JsonArray.
*
* Return value: the passed #JsonArray, with the reference count
* increased by one.
*/
JsonArray *
json_array_ref (JsonArray *array)
{
g_return_val_if_fail (array != NULL, NULL);
g_return_val_if_fail (array->ref_count > 0, NULL);
g_atomic_int_exchange_and_add (&array->ref_count, 1);
return array;
}
/**
* json_array_unref:
* @array: a #JsonArray
*
* Decreases by one the reference count of a #JsonArray. If the
* reference count reaches zero, the array is destroyed and all
* its allocated resources are freed.
*/
void
json_array_unref (JsonArray *array)
{
gint old_ref;
g_return_if_fail (array != NULL);
g_return_if_fail (array->ref_count > 0);
old_ref = g_atomic_int_get (&array->ref_count);
if (old_ref > 1)
g_atomic_int_compare_and_exchange (&array->ref_count, old_ref, old_ref - 1);
else
{
gint i;
for (i = 0; i < array->elements->len; i++)
json_node_free (g_ptr_array_index (array->elements, i));
g_ptr_array_free (array->elements, TRUE);
array->elements = NULL;
g_slice_free (JsonArray, array);
}
}
/**
* json_array_get_elements:
* @array: a #JsonArray
*
* Gets the elements of a #JsonArray as a list of #JsonNode<!-- -->s.
*
* Return value: a #GList containing the elements of the array. The
* contents of the list are owned by the array and should never be
* modified or freed. Use g_list_free() on the returned list when
* done using it
*/
GList *
json_array_get_elements (JsonArray *array)
{
GList *retval;
guint i;
g_return_val_if_fail (array != NULL, NULL);
retval = NULL;
for (i = 0; i < array->elements->len; i++)
retval = g_list_prepend (retval,
g_ptr_array_index (array->elements, i));
return g_list_reverse (retval);
}
/**
* json_array_get_element:
* @array: a #JsonArray
* @index_: the index of the element to retrieve
*
* Retrieves the element at @index_ inside a #JsonArray.
*
* Return value: a pointer to the #JsonNode at the requested index
*/
JsonNode *
json_array_get_element (JsonArray *array,
guint index_)
{
g_return_val_if_fail (array != NULL, NULL);
g_return_val_if_fail (index_ < array->elements->len, NULL);
return g_ptr_array_index (array->elements, index_);
}
/**
* json_array_get_length:
* @array: a #JsonArray
*
* Retrieves the length of a #JsonArray
*
* Return value: the length of the array
*/
guint
json_array_get_length (JsonArray *array)
{
g_return_val_if_fail (array != NULL, 0);
return array->elements->len;
}
/**
* json_array_add_element:
* @array: a #JsonArray
* @node: a #JsonNode
*
* Appends @node inside @array.
*/
void
json_array_add_element (JsonArray *array,
JsonNode *node)
{
g_return_if_fail (array != NULL);
g_return_if_fail (node != NULL);
g_ptr_array_add (array->elements, node);
}

View File

@ -0,0 +1,533 @@
/* json-generator.c - JSON streams generator
*
* This file is part of JSON-GLib
* Copyright (C) 2007 OpenedHand Ltd.
*
* 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.
*
* Author:
* Emmanuele Bassi <ebassi@openedhand.com>
*/
/**
* SECTION:json-generator
* @short_description: Generates JSON data streams
*
* #JsonGenerator provides an object for generating a JSON data stream and
* put it into a buffer or a file.
*/
#include "config.h"
#include <stdlib.h>
#include <string.h>
#include "json-marshal.h"
#include "json-generator.h"
#define JSON_GENERATOR_GET_PRIVATE(obj) \
(G_TYPE_INSTANCE_GET_PRIVATE ((obj), JSON_TYPE_GENERATOR, JsonGeneratorPrivate))
struct _JsonGeneratorPrivate
{
JsonNode *root;
guint indent;
guint pretty : 1;
};
enum
{
PROP_0,
PROP_PRETTY,
PROP_INDENT
};
static gchar *dump_value (JsonGenerator *generator,
gint level,
const gchar *name,
JsonNode *node);
static gchar *dump_array (JsonGenerator *generator,
gint level,
const gchar *name,
JsonArray *array,
gsize *length);
static gchar *dump_object (JsonGenerator *generator,
gint level,
const gchar *name,
JsonObject *object,
gsize *length);
G_DEFINE_TYPE (JsonGenerator, json_generator, G_TYPE_OBJECT);
static void
json_generator_finalize (GObject *gobject)
{
JsonGeneratorPrivate *priv = JSON_GENERATOR_GET_PRIVATE (gobject);
if (priv->root)
json_node_free (priv->root);
G_OBJECT_CLASS (json_generator_parent_class)->finalize (gobject);
}
static void
json_generator_set_property (GObject *gobject,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
JsonGeneratorPrivate *priv = JSON_GENERATOR_GET_PRIVATE (gobject);
switch (prop_id)
{
case PROP_PRETTY:
priv->pretty = g_value_get_boolean (value);
break;
case PROP_INDENT:
priv->indent = g_value_get_uint (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
break;
}
}
static void
json_generator_get_property (GObject *gobject,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
JsonGeneratorPrivate *priv = JSON_GENERATOR_GET_PRIVATE (gobject);
switch (prop_id)
{
case PROP_PRETTY:
g_value_set_boolean (value, priv->pretty);
break;
case PROP_INDENT:
g_value_set_uint (value, priv->indent);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
break;
}
}
static void
json_generator_class_init (JsonGeneratorClass *klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
g_type_class_add_private (klass, sizeof (JsonGeneratorPrivate));
gobject_class->set_property = json_generator_set_property;
gobject_class->get_property = json_generator_get_property;
gobject_class->finalize = json_generator_finalize;
/**
* JsonGenerator:pretty:
*
* Whether the output should be "pretty-printed", with indentation and
* newlines. The indentation level can be controlled by using the
* JsonGenerator:indent property
*/
g_object_class_install_property (gobject_class,
PROP_PRETTY,
g_param_spec_boolean ("pretty",
"Pretty",
"Pretty-print the output",
FALSE,
G_PARAM_READWRITE));
/**
* JsonGenerator:indent:
*
* Number of spaces to be used to indent when pretty printing.
*/
g_object_class_install_property (gobject_class,
PROP_INDENT,
g_param_spec_uint ("indent",
"Indent",
"Number of indentation spaces",
0, G_MAXUINT,
2,
G_PARAM_READWRITE));
}
static void
json_generator_init (JsonGenerator *generator)
{
JsonGeneratorPrivate *priv;
generator->priv = priv = JSON_GENERATOR_GET_PRIVATE (generator);
priv->pretty = FALSE;
priv->indent = 2;
}
static gchar *
dump_value (JsonGenerator *generator,
gint level,
const gchar *name,
JsonNode *node)
{
gboolean pretty = generator->priv->pretty;
guint indent = generator->priv->indent;
GValue value = { 0, };
GString *buffer;
gint i;
buffer = g_string_new ("");
if (pretty)
{
for (i = 0; i < (level * indent); i++)
g_string_append_c (buffer, ' ');
}
if (name && name[0] != '\0')
g_string_append_printf (buffer, "\"%s\" : ", name);
json_node_get_value (node, &value);
switch (G_VALUE_TYPE (&value))
{
case G_TYPE_INT:
g_string_append_printf (buffer, "%d", g_value_get_int (&value));
break;
case G_TYPE_STRING:
g_string_append_printf (buffer, "\"%s\"", g_value_get_string (&value));
break;
case G_TYPE_FLOAT:
g_string_append_printf (buffer, "%f", g_value_get_float (&value));
break;
case G_TYPE_BOOLEAN:
g_string_append_printf (buffer, "%s",
g_value_get_boolean (&value) ? "true" : "false");
break;
default:
break;
}
g_value_unset (&value);
return g_string_free (buffer, FALSE);
}
static gchar *
dump_array (JsonGenerator *generator,
gint level,
const gchar *name,
JsonArray *array,
gsize *length)
{
gint array_len = json_array_get_length (array);
gint i;
GString *buffer;
gboolean pretty = generator->priv->pretty;
guint indent = generator->priv->indent;
buffer = g_string_new ("");
if (pretty)
{
for (i = 0; i < (level * indent); i++)
g_string_append_c (buffer, ' ');
}
if (name && name[0] != '\0')
g_string_append_printf (buffer, "\"%s\" : ", name);
g_string_append_c (buffer, '[');
if (pretty)
g_string_append_c (buffer, '\n');
else
g_string_append_c (buffer, ' ');
for (i = 0; i < array_len; i++)
{
JsonNode *cur = json_array_get_element (array, i);
guint sub_level = level + 1;
gint j;
gchar *value;
switch (JSON_NODE_TYPE (cur))
{
case JSON_NODE_NULL:
if (pretty)
{
for (j = 0; j < (sub_level * indent); j++)
g_string_append_c (buffer, ' ');
}
g_string_append (buffer, "null");
break;
case JSON_NODE_VALUE:
value = dump_value (generator, sub_level, NULL, cur);
g_string_append (buffer, value);
break;
case JSON_NODE_ARRAY:
value = dump_array (generator, sub_level, NULL, json_node_get_array (cur), NULL);
g_string_append (buffer, value);
break;
case JSON_NODE_OBJECT:
value = dump_object (generator, sub_level, NULL, json_node_get_object (cur), NULL);
g_string_append (buffer, value);
break;
}
if ((i + 1) != array_len)
g_string_append_c (buffer, ',');
if (pretty)
g_string_append_c (buffer, '\n');
else
g_string_append_c (buffer, ' ');
}
if (pretty)
{
for (i = 0; i < (level * indent); i++)
g_string_append_c (buffer, ' ');
}
g_string_append_c (buffer, ']');
if (length)
*length = buffer->len;
return g_string_free (buffer, FALSE);
}
static gchar *
dump_object (JsonGenerator *generator,
gint level,
const gchar *name,
JsonObject *object,
gsize *length)
{
GList *members, *l;
GString *buffer;
gboolean pretty = generator->priv->pretty;
guint indent = generator->priv->indent;
gint i;
buffer = g_string_new ("");
if (pretty)
{
for (i = 0; i < (level * indent); i++)
g_string_append_c (buffer, ' ');
}
if (name && name[0] != '\0')
g_string_append_printf (buffer, "\"%s\" : ", name);
g_string_append_c (buffer, '{');
if (pretty)
g_string_append_c (buffer, '\n');
else
g_string_append_c (buffer, ' ');
members = json_object_get_members (object);
for (l = members; l != NULL; l = l->next)
{
const gchar *name = l->data;
JsonNode *cur = json_object_get_member (object, name);
guint sub_level = level + 1;
gint j;
gchar *value;
switch (JSON_NODE_TYPE (cur))
{
case JSON_NODE_NULL:
if (pretty)
{
for (j = 0; j < (sub_level * indent); j++)
g_string_append_c (buffer, ' ');
}
g_string_append_printf (buffer, "\"%s\" : null", name);
break;
case JSON_NODE_VALUE:
value = dump_value (generator, sub_level, name, cur);
g_string_append (buffer, value);
break;
case JSON_NODE_ARRAY:
value = dump_array (generator, sub_level, name, json_node_get_array (cur), NULL);
g_string_append (buffer, value);
break;
case JSON_NODE_OBJECT:
value = dump_object (generator, sub_level, name, json_node_get_object (cur), NULL);
g_string_append (buffer, value);
break;
}
if (l->next != NULL)
g_string_append_c (buffer, ',');
if (pretty)
g_string_append_c (buffer, '\n');
else
g_string_append_c (buffer, ' ');
}
g_list_free (members);
if (pretty)
{
for (i = 0; i < (level * indent); i++)
g_string_append_c (buffer, ' ');
}
g_string_append_c (buffer, '}');
if (length)
*length = buffer->len;
return g_string_free (buffer, FALSE);
}
/**
* json_generator_new:
*
* Creates a new #JsonGenerator. You can use this object to generate a
* JSON data stream starting from a data object model composed by
* #JsonNode<!-- -->s.
*
* Return value: the newly created #JsonGenerator instance
*/
JsonGenerator *
json_generator_new (void)
{
return g_object_new (JSON_TYPE_GENERATOR, NULL);
}
/**
* json_generator_to_data:
* @generator: a #JsonGenerator
* @length: return location for the length of the returned buffer, or %NULL
*
* Generates a JSON data stream from @generator and returns it as a
* buffer.
*
* Return value: a newly allocated buffer holding a JSON data stream. Use
* g_free() to free the allocated resources.
*/
gchar *
json_generator_to_data (JsonGenerator *generator,
gsize *length)
{
JsonNode *root;
gchar *retval = NULL;
g_return_val_if_fail (JSON_IS_GENERATOR (generator), NULL);
root = generator->priv->root;
if (!root)
{
if (length)
*length = 0;
return NULL;
}
switch (JSON_NODE_TYPE (root))
{
case JSON_NODE_ARRAY:
retval = dump_array (generator, 0, NULL, json_node_get_array (root), length);
break;
case JSON_NODE_OBJECT:
retval = dump_object (generator, 0, NULL, json_node_get_object (root), length);
break;
case JSON_NODE_NULL:
retval = g_strdup ("null");
if (length)
*length = 4;
break;
case JSON_NODE_VALUE:
retval = NULL;
break;
}
return retval;
}
/**
* json_generator_to_file:
* @generator: a #JsonGenerator
* @filename: path to the target file
* @error: return location for a #GError, or %NULL
*
* Creates a JSON data stream and puts it inside @filename, overwriting the
* current file contents. This operation is atomic.
*
* Return value: %TRUE if saving was successful.
*/
gboolean
json_generator_to_file (JsonGenerator *generator,
const gchar *filename,
GError **error)
{
gchar *buffer;
gsize len;
gboolean retval;
g_return_val_if_fail (JSON_IS_GENERATOR (generator), FALSE);
g_return_val_if_fail (filename != NULL, FALSE);
buffer = json_generator_to_data (generator, &len);
retval = g_file_set_contents (filename, buffer, len, error);
g_free (buffer);
return retval;
}
/**
* json_generator_set_root:
* @generator: a #JsonGenerator
* @node: a #JsonNode
*
* Sets @node as the root of the JSON data stream to be serialized by
* the #JsonGenerator.
*/
void
json_generator_set_root (JsonGenerator *generator,
JsonNode *node)
{
g_return_if_fail (JSON_IS_GENERATOR (generator));
if (generator->priv->root)
{
json_node_free (generator->priv->root);
generator->priv->root = NULL;
}
if (node)
generator->priv->root = node;
}

View File

@ -0,0 +1,83 @@
/* json-generator.h - JSON streams generator
*
* This file is part of JSON-GLib
* Copyright (C) 2007 OpenedHand Ltd.
*
* 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.
*
* Author:
* Emmanuele Bassi <ebassi@openedhand.com>
*/
#ifndef __JSON_GENERATOR_H__
#define __JSON_GENERATOR_H__
#include <glib-object.h>
#include "json-types.h"
G_BEGIN_DECLS
#define JSON_TYPE_GENERATOR (json_generator_get_type ())
#define JSON_GENERATOR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), JSON_TYPE_GENERATOR, JsonGenerator))
#define JSON_IS_GENERATOR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), JSON_TYPE_GENERATOR))
#define JSON_GENERATOR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), JSON_TYPE_GENERATOR, JsonGeneratorClass))
#define JSON_IS_GENERATOR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), JSON_TYPE_GENERATOR))
#define JSON_GENERATOR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), JSON_TYPE_GENERATOR, JsonGeneratorClass))
typedef struct _JsonGenerator JsonGenerator;
typedef struct _JsonGeneratorPrivate JsonGeneratorPrivate;
typedef struct _JsonGeneratorClass JsonGeneratorClass;
/**
* JsonGenerator:
*
* JSON data streams generator. The contents of the #JsonGenerator structure
* are private and should only be accessed via the provided API.
*/
struct _JsonGenerator
{
/*< private >*/
GObject parent_instance;
JsonGeneratorPrivate *priv;
};
/**
* JsonGeneratorClass:
*
* #JsonGenerator class
*/
struct _JsonGeneratorClass
{
/*< private >*/
GObjectClass parent_class;
/* padding, for future expansion */
void (* _json_reserved1) (void);
void (* _json_reserved2) (void);
void (* _json_reserved3) (void);
void (* _json_reserved4) (void);
};
GType json_generator_get_type (void) G_GNUC_CONST;
JsonGenerator *json_generator_new (void);
gchar * json_generator_to_data (JsonGenerator *generator,
gsize *length);
gboolean json_generator_to_file (JsonGenerator *generator,
const gchar *filename,
GError **error);
void json_generator_set_root (JsonGenerator *generator,
JsonNode *node);
G_END_DECLS
#endif /* __JSON_GENERATOR_H__ */

8
clutter/json/json-glib.h Normal file
View File

@ -0,0 +1,8 @@
#ifndef __JSON_GLIB_H__
#define __JSON_GLIB_H__
#include "json-types.h"
#include "json-generator.h"
#include "json-parser.h"
#endif /* __JSON_GLIB_H__ */

130
clutter/json/json-marshal.c Normal file
View File

@ -0,0 +1,130 @@
#include "json-marshal.h"
#include <glib-object.h>
#ifdef G_ENABLE_DEBUG
#define g_marshal_value_peek_boolean(v) g_value_get_boolean (v)
#define g_marshal_value_peek_char(v) g_value_get_char (v)
#define g_marshal_value_peek_uchar(v) g_value_get_uchar (v)
#define g_marshal_value_peek_int(v) g_value_get_int (v)
#define g_marshal_value_peek_uint(v) g_value_get_uint (v)
#define g_marshal_value_peek_long(v) g_value_get_long (v)
#define g_marshal_value_peek_ulong(v) g_value_get_ulong (v)
#define g_marshal_value_peek_int64(v) g_value_get_int64 (v)
#define g_marshal_value_peek_uint64(v) g_value_get_uint64 (v)
#define g_marshal_value_peek_enum(v) g_value_get_enum (v)
#define g_marshal_value_peek_flags(v) g_value_get_flags (v)
#define g_marshal_value_peek_float(v) g_value_get_float (v)
#define g_marshal_value_peek_double(v) g_value_get_double (v)
#define g_marshal_value_peek_string(v) (char*) g_value_get_string (v)
#define g_marshal_value_peek_param(v) g_value_get_param (v)
#define g_marshal_value_peek_boxed(v) g_value_get_boxed (v)
#define g_marshal_value_peek_pointer(v) g_value_get_pointer (v)
#define g_marshal_value_peek_object(v) g_value_get_object (v)
#else /* !G_ENABLE_DEBUG */
/* WARNING: This code accesses GValues directly, which is UNSUPPORTED API.
* Do not access GValues directly in your code. Instead, use the
* g_value_get_*() functions
*/
#define g_marshal_value_peek_boolean(v) (v)->data[0].v_int
#define g_marshal_value_peek_char(v) (v)->data[0].v_int
#define g_marshal_value_peek_uchar(v) (v)->data[0].v_uint
#define g_marshal_value_peek_int(v) (v)->data[0].v_int
#define g_marshal_value_peek_uint(v) (v)->data[0].v_uint
#define g_marshal_value_peek_long(v) (v)->data[0].v_long
#define g_marshal_value_peek_ulong(v) (v)->data[0].v_ulong
#define g_marshal_value_peek_int64(v) (v)->data[0].v_int64
#define g_marshal_value_peek_uint64(v) (v)->data[0].v_uint64
#define g_marshal_value_peek_enum(v) (v)->data[0].v_long
#define g_marshal_value_peek_flags(v) (v)->data[0].v_ulong
#define g_marshal_value_peek_float(v) (v)->data[0].v_float
#define g_marshal_value_peek_double(v) (v)->data[0].v_double
#define g_marshal_value_peek_string(v) (v)->data[0].v_pointer
#define g_marshal_value_peek_param(v) (v)->data[0].v_pointer
#define g_marshal_value_peek_boxed(v) (v)->data[0].v_pointer
#define g_marshal_value_peek_pointer(v) (v)->data[0].v_pointer
#define g_marshal_value_peek_object(v) (v)->data[0].v_pointer
#endif /* !G_ENABLE_DEBUG */
/* VOID:VOID (./json-marshal.list:1) */
/* VOID:BOXED (./json-marshal.list:2) */
/* VOID:BOXED,STRING (./json-marshal.list:3) */
void
_json_marshal_VOID__BOXED_STRING (GClosure *closure,
GValue *return_value,
guint n_param_values,
const GValue *param_values,
gpointer invocation_hint,
gpointer marshal_data)
{
typedef void (*GMarshalFunc_VOID__BOXED_STRING) (gpointer data1,
gpointer arg_1,
gpointer arg_2,
gpointer data2);
register GMarshalFunc_VOID__BOXED_STRING callback;
register GCClosure *cc = (GCClosure*) closure;
register gpointer data1, data2;
g_return_if_fail (n_param_values == 3);
if (G_CCLOSURE_SWAP_DATA (closure))
{
data1 = closure->data;
data2 = g_value_peek_pointer (param_values + 0);
}
else
{
data1 = g_value_peek_pointer (param_values + 0);
data2 = closure->data;
}
callback = (GMarshalFunc_VOID__BOXED_STRING) (marshal_data ? marshal_data : cc->callback);
callback (data1,
g_marshal_value_peek_boxed (param_values + 1),
g_marshal_value_peek_string (param_values + 2),
data2);
}
/* VOID:BOXED,INT (./json-marshal.list:4) */
void
_json_marshal_VOID__BOXED_INT (GClosure *closure,
GValue *return_value,
guint n_param_values,
const GValue *param_values,
gpointer invocation_hint,
gpointer marshal_data)
{
typedef void (*GMarshalFunc_VOID__BOXED_INT) (gpointer data1,
gpointer arg_1,
gint arg_2,
gpointer data2);
register GMarshalFunc_VOID__BOXED_INT callback;
register GCClosure *cc = (GCClosure*) closure;
register gpointer data1, data2;
g_return_if_fail (n_param_values == 3);
if (G_CCLOSURE_SWAP_DATA (closure))
{
data1 = closure->data;
data2 = g_value_peek_pointer (param_values + 0);
}
else
{
data1 = g_value_peek_pointer (param_values + 0);
data2 = closure->data;
}
callback = (GMarshalFunc_VOID__BOXED_INT) (marshal_data ? marshal_data : cc->callback);
callback (data1,
g_marshal_value_peek_boxed (param_values + 1),
g_marshal_value_peek_int (param_values + 2),
data2);
}
/* VOID:POINTER (./json-marshal.list:5) */

View File

@ -0,0 +1,37 @@
#ifndef ___json_marshal_MARSHAL_H__
#define ___json_marshal_MARSHAL_H__
#include <glib-object.h>
G_BEGIN_DECLS
/* VOID:VOID (./json-marshal.list:1) */
#define _json_marshal_VOID__VOID g_cclosure_marshal_VOID__VOID
/* VOID:BOXED (./json-marshal.list:2) */
#define _json_marshal_VOID__BOXED g_cclosure_marshal_VOID__BOXED
/* VOID:BOXED,STRING (./json-marshal.list:3) */
extern void _json_marshal_VOID__BOXED_STRING (GClosure *closure,
GValue *return_value,
guint n_param_values,
const GValue *param_values,
gpointer invocation_hint,
gpointer marshal_data);
/* VOID:BOXED,INT (./json-marshal.list:4) */
extern void _json_marshal_VOID__BOXED_INT (GClosure *closure,
GValue *return_value,
guint n_param_values,
const GValue *param_values,
gpointer invocation_hint,
gpointer marshal_data);
/* VOID:POINTER (./json-marshal.list:5) */
#define _json_marshal_VOID__POINTER g_cclosure_marshal_VOID__POINTER
G_END_DECLS
#endif /* ___json_marshal_MARSHAL_H__ */

610
clutter/json/json-node.c Normal file
View File

@ -0,0 +1,610 @@
/* json-node.c - JSON object model node
*
* This file is part of JSON-GLib
* Copyright (C) 2007 OpenedHand Ltd.
*
* 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.
*
* Author:
* Emmanuele Bassi <ebassi@openedhand.com>
*/
#include "config.h"
#include <glib.h>
#include "json-types.h"
/**
* SECTION:json-node
* @short_description: Node in a JSON object model
*
* A #JsonNode is a generic container of elements inside a JSON stream.
* It can contain fundamental types (integers, booleans, floating point
* numbers, strings) and complex types (arrays and objects).
*
* When parsing a JSON data stream you extract the root node and walk
* the node tree by retrieving the type of data contained inside the
* node with the %JSON_NODE_TYPE macro. If the node contains a fundamental
* type you can retrieve a copy of the GValue holding it with the
* json_node_get_value() function, and then use the GValue API to extract
* the data; if the node contains a complex type you can retrieve the
* #JsonObject or the #JsonArray using json_node_get_object() or
* json_node_get_array() respectively, and then retrieve the nodes
* they contain.
*/
/**
* json_node_new:
* @type: a #JsonNodeType
*
* Creates a new #JsonNode of @type.
*
* Return value: the newly created #JsonNode
*/
JsonNode *
json_node_new (JsonNodeType type)
{
JsonNode *data;
g_return_val_if_fail (type >= JSON_NODE_OBJECT && type <= JSON_NODE_NULL, NULL);
data = g_slice_new (JsonNode);
data->type = type;
return data;
}
/**
* json_node_copy:
* @node: a #JsonNode
*
* Copies @node. If the node contains complex data types then the reference
* count of the objects is increased.
*
* Return value: the copied #JsonNode
*/
JsonNode *
json_node_copy (JsonNode *node)
{
JsonNode *copy;
g_return_val_if_fail (node != NULL, NULL);
copy = g_slice_new (JsonNode);
*copy = *node;
switch (copy->type)
{
case JSON_NODE_OBJECT:
copy->data.object = json_object_ref (node->data.object);
break;
case JSON_NODE_ARRAY:
copy->data.array = json_array_ref (node->data.array);
break;
case JSON_NODE_VALUE:
g_value_init (&(copy->data.value), G_VALUE_TYPE (&(node->data.value)));
g_value_copy (&(node->data.value), &(copy->data.value));
break;
case JSON_NODE_NULL:
break;
default:
g_assert_not_reached ();
}
return copy;
}
/**
* json_node_set_object:
* @node: a #JsonNode
* @object: a #JsonObject
*
* Sets @objects inside @node. The reference count of @object is increased.
*/
void
json_node_set_object (JsonNode *node,
JsonObject *object)
{
g_return_if_fail (node != NULL);
g_return_if_fail (JSON_NODE_TYPE (node) == JSON_NODE_OBJECT);
if (node->data.object)
json_object_unref (node->data.object);
if (object)
node->data.object = json_object_ref (object);
else
node->data.object = NULL;
}
/**
* json_node_take_object:
* @node: a #JsonNode
* @object: a #JsonObject
*
* Sets @object inside @node. The reference count of @object is not increased.
*/
void
json_node_take_object (JsonNode *node,
JsonObject *object)
{
g_return_if_fail (node != NULL);
g_return_if_fail (JSON_NODE_TYPE (node) == JSON_NODE_OBJECT);
if (node->data.object)
{
json_object_unref (node->data.object);
node->data.object = NULL;
}
if (object)
node->data.object = object;
}
/**
* json_node_get_object:
* @node: a #JsonNode
*
* Retrieves the #JsonObject stored inside a #JsonNode
*
* Return value: the #JsonObject
*/
JsonObject *
json_node_get_object (JsonNode *node)
{
g_return_val_if_fail (node != NULL, NULL);
g_return_val_if_fail (JSON_NODE_TYPE (node) == JSON_NODE_OBJECT, NULL);
return node->data.object;
}
/**
* json_node_dup_object:
* @node: a #JsonNode
*
* Retrieves the #JsonObject inside @node. The reference count of
* the returned object is increased.
*
* Return value: the #JsonObject
*/
JsonObject *
json_node_dup_object (JsonNode *node)
{
g_return_val_if_fail (node != NULL, NULL);
g_return_val_if_fail (JSON_NODE_TYPE (node) == JSON_NODE_OBJECT, NULL);
if (node->data.object)
return json_object_ref (node->data.object);
return NULL;
}
/**
* json_node_set_array:
* @node: a #JsonNode
* @array: a #JsonArray
*
* Sets @array inside @node and increases the #JsonArray reference count
*/
void
json_node_set_array (JsonNode *node,
JsonArray *array)
{
g_return_if_fail (node != NULL);
g_return_if_fail (JSON_NODE_TYPE (node) == JSON_NODE_ARRAY);
if (node->data.array)
json_array_unref (node->data.array);
if (array)
node->data.array = json_array_ref (array);
else
node->data.array = NULL;
}
/**
* json_node_take_array:
* @node: a #JsonNode
* @array: a #JsonArray
*
* Sets @array into @node without increasing the #JsonArray reference count.
*/
void
json_node_take_array (JsonNode *node,
JsonArray *array)
{
g_return_if_fail (node != NULL);
g_return_if_fail (JSON_NODE_TYPE (node) == JSON_NODE_ARRAY);
if (node->data.array)
{
json_array_unref (node->data.array);
node->data.array = NULL;
}
if (array)
node->data.array = array;
}
/**
* json_node_get_array:
* @node: a #JsonNode
*
* Retrieves the #JsonArray stored inside a #JsonNode
*
* Return value: the #JsonArray
*/
JsonArray *
json_node_get_array (JsonNode *node)
{
g_return_val_if_fail (node != NULL, NULL);
g_return_val_if_fail (JSON_NODE_TYPE (node) == JSON_NODE_ARRAY, NULL);
return node->data.array;
}
/**
* json_node_dup_array
* @node: a #JsonNode
*
* Retrieves the #JsonArray stored inside a #JsonNode and returns it
* with its reference count increased by one.
*
* Return value: the #JsonArray with its reference count increased.
*/
JsonArray *
json_node_dup_array (JsonNode *node)
{
g_return_val_if_fail (node != NULL, NULL);
g_return_val_if_fail (JSON_NODE_TYPE (node) == JSON_NODE_ARRAY, NULL);
if (node->data.array)
return json_array_ref (node->data.array);
return NULL;
}
/**
* json_node_get_value:
* @node: a #JsonNode
* @value: return location for an uninitialized value
*
* Retrieves a value from a #JsonNode and copies into @value. When done
* using it, call g_value_unset() on the #GValue.
*/
void
json_node_get_value (JsonNode *node,
GValue *value)
{
g_return_if_fail (node != NULL);
g_return_if_fail (JSON_NODE_TYPE (node) == JSON_NODE_VALUE);
if (G_VALUE_TYPE (&(node->data.value)) != 0)
{
g_value_init (value, G_VALUE_TYPE (&(node->data.value)));
g_value_copy (&(node->data.value), value);
}
}
/**
* json_node_set_value:
* @node: a #JsonNode
* @value: the #GValue to set
*
* Sets @value inside @node. The passed #GValue is copied into the #JsonNode
*/
void
json_node_set_value (JsonNode *node,
const GValue *value)
{
g_return_if_fail (node != NULL);
g_return_if_fail (JSON_NODE_TYPE (node) == JSON_NODE_VALUE);
if (G_VALUE_TYPE (&(node->data.value)) != 0)
g_value_unset (&(node->data.value));
g_value_init (&(node->data.value), G_VALUE_TYPE (value));
g_value_copy (value, &(node->data.value));
}
/**
* json_node_free:
* @node: a #JsonNode
*
* Frees the resources allocated by @node.
*/
void
json_node_free (JsonNode *node)
{
if (G_LIKELY (node))
{
switch (node->type)
{
case JSON_NODE_OBJECT:
json_object_unref (node->data.object);
break;
case JSON_NODE_ARRAY:
json_array_unref (node->data.array);
break;
case JSON_NODE_VALUE:
g_value_unset (&(node->data.value));
break;
case JSON_NODE_NULL:
break;
}
g_slice_free (JsonNode, node);
}
}
/**
* json_node_type_name:
* @node: a #JsonNode
*
* Retrieves the user readable name of the data type contained by @node.
*
* Return value: a string containing the name of the type. The returned string
* is owned by the node and should never be modified or freed
*/
G_CONST_RETURN gchar *
json_node_type_name (JsonNode *node)
{
g_return_val_if_fail (node != NULL, "(null)");
switch (node->type)
{
case JSON_NODE_OBJECT:
return "JsonObject";
case JSON_NODE_ARRAY:
return "JsonArray";
case JSON_NODE_NULL:
return "NULL";
case JSON_NODE_VALUE:
return g_type_name (G_VALUE_TYPE (&(node->data.value)));
}
return "unknown";
}
/**
* json_node_get_parent:
* @node: a #JsonNode
*
* Retrieves the parent #JsonNode of @node.
*
* Return value: the parent node, or %NULL if @node is the root node
*/
JsonNode *
json_node_get_parent (JsonNode *node)
{
g_return_val_if_fail (node != NULL, NULL);
return node->parent;
}
/**
* json_node_set_string:
* @node: a #JsonNode of type %JSON_NODE_VALUE
* @value: a string value
*
* Sets @value as the string content of the @node, replacing any existing
* content.
*/
void
json_node_set_string (JsonNode *node,
const gchar *value)
{
g_return_if_fail (node != NULL);
g_return_if_fail (JSON_NODE_TYPE (node) == JSON_NODE_VALUE);
if (G_VALUE_TYPE (&(node->data.value)) == G_TYPE_STRING)
g_value_set_string (&(node->data.value), value);
else
{
GValue copy = { 0, };
g_value_init (&copy, G_TYPE_STRING);
g_value_set_string (&copy, value);
json_node_set_value (node, &copy);
g_value_unset (&copy);
}
}
/**
* json_node_get_string:
* @node: a #JsonNode of type %JSON_NODE_VALUE
*
* Gets the string value stored inside a #JsonNode
*
* Return value: a string value.
*/
G_CONST_RETURN gchar *
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 (G_VALUE_TYPE (&(node->data.value)) == G_TYPE_STRING)
return g_value_get_string (&(node->data.value));
return NULL;
}
gchar *
json_node_dup_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 (G_VALUE_TYPE (&(node->data.value)) == G_TYPE_STRING)
return g_strdup (g_value_get_string (&(node->data.value)));
return NULL;
}
/**
* json_node_set_int:
* @node: a #JsonNode of type %JSON_NODE_VALUE
* @value: an integer value
*
* Sets @value as the integer content of the @node, replacing any existing
* content.
*/
void
json_node_set_int (JsonNode *node,
gint value)
{
g_return_if_fail (node != NULL);
g_return_if_fail (JSON_NODE_TYPE (node) == JSON_NODE_VALUE);
if (G_VALUE_TYPE (&(node->data.value)) == G_TYPE_INT)
g_value_set_int (&(node->data.value), value);
else
{
GValue copy = { 0, };
g_value_init (&copy, G_TYPE_INT);
g_value_set_int (&copy, value);
json_node_set_value (node, &copy);
g_value_unset (&copy);
}
}
/**
* json_node_get_int:
* @node: a #JsonNode of type %JSON_NODE_VALUE
*
* Gets the integer value stored inside a #JsonNode
*
* Return value: an integer value.
*/
gint
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 (G_VALUE_TYPE (&(node->data.value)) == G_TYPE_INT)
return g_value_get_int (&(node->data.value));
return 0;
}
/**
* json_node_set_double:
* @node: a #JsonNode of type %JSON_NODE_VALUE
* @value: a double value
*
* Sets @value as the double content of the @node, replacing any existing
* content.
*/
void
json_node_set_double (JsonNode *node,
gdouble value)
{
g_return_if_fail (node != NULL);
g_return_if_fail (JSON_NODE_TYPE (node) == JSON_NODE_VALUE);
if (G_VALUE_TYPE (&(node->data.value)) == G_TYPE_DOUBLE)
g_value_set_double (&(node->data.value), value);
else
{
GValue copy = { 0, };
g_value_init (&copy, G_TYPE_DOUBLE);
g_value_set_double (&copy, value);
json_node_set_value (node, &copy);
g_value_unset (&copy);
}
}
/**
* json_node_get_double:
* @node: a #JsonNode of type %JSON_NODE_VALUE
*
* Gets the double value stored inside a #JsonNode
*
* Return value: a double value.
*/
gdouble
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 (G_VALUE_TYPE (&(node->data.value)) == G_TYPE_DOUBLE)
return g_value_get_double (&(node->data.value));
return 0.0;
}
/**
* json_node_set_boolean:
* @node: a #JsonNode of type %JSON_NODE_VALUE
* @value: a boolean value
*
* Sets @value as the boolean content of the @node, replacing any existing
* content.
*/
void
json_node_set_boolean (JsonNode *node,
gboolean value)
{
g_return_if_fail (node != NULL);
g_return_if_fail (JSON_NODE_TYPE (node) == JSON_NODE_VALUE);
if (G_VALUE_TYPE (&(node->data.value)) == G_TYPE_BOOLEAN)
g_value_set_boolean (&(node->data.value), value);
else
{
GValue copy = { 0, };
g_value_init (&copy, G_TYPE_BOOLEAN);
g_value_set_boolean (&copy, value);
json_node_set_value (node, &copy);
g_value_unset (&copy);
}
}
/**
* json_node_get_boolean:
* @node: a #JsonNode of type %JSON_NODE_VALUE
*
* Gets the boolean value stored inside a #JsonNode
*
* Return value: a boolean value.
*/
gboolean
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 (G_VALUE_TYPE (&(node->data.value)) == G_TYPE_BOOLEAN)
return g_value_get_boolean (&(node->data.value));
return FALSE;
}

234
clutter/json/json-object.c Normal file
View File

@ -0,0 +1,234 @@
/* json-object.c - JSON object implementation
*
* This file is part of JSON-GLib
* Copyright (C) 2007 OpenedHand Ltd.
*
* 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.
*
* Author:
* Emmanuele Bassi <ebassi@openedhand.com>
*/
#include "config.h"
#include <glib.h>
#include "json-types.h"
/**
* SECTION:json-object
* @short_description: a JSON object representation
*
* #JsonArray is the representation of the object type inside JSON. It contains
* #JsonNode<!-- -->s, which may contain fundamental types, arrays or other
* objects. Each member of an object is accessed using its name.
*
* Since objects can be expensive, they are reference counted. You can control
* the lifetime of a #JsonObject using json_object_ref() and json_object_unref().
*
* To extract a member with a given name, use json_object_get_member().
* To retrieve the list of members, use json_object_get_members().
* To retrieve the size of the object (that is, the number of members it has), use
* json_object_get_size().
*/
struct _JsonObject
{
GHashTable *members;
volatile gint ref_count;
};
GType
json_object_get_type (void)
{
static GType object_type = 0;
if (G_UNLIKELY (!object_type))
object_type = g_boxed_type_register_static ("JsonObject",
(GBoxedCopyFunc) json_object_ref,
(GBoxedFreeFunc) json_object_unref);
return object_type;
}
/**
* json_object_new:
*
* Creates a new #JsonObject, an JSON object type representation.
*
* Return value: the newly created #JsonObject
*/
JsonObject *
json_object_new (void)
{
JsonObject *object;
object = g_slice_new (JsonObject);
object->ref_count = 1;
object->members = g_hash_table_new_full (g_str_hash, g_str_equal,
g_free,
(GDestroyNotify) json_node_free);
return object;
}
/**
* json_object_ref:
* @object: a #JsonObject
*
* Increase by one the reference count of a #JsonObject.
*
* Return value: the passed #JsonObject, with the reference count
* increased by one.
*/
JsonObject *
json_object_ref (JsonObject *object)
{
g_return_val_if_fail (object != NULL, NULL);
g_return_val_if_fail (object->ref_count > 0, NULL);
g_atomic_int_exchange_and_add (&object->ref_count, 1);
return object;
}
/**
* json_object_unref:
* @object: a #JsonObject
*
* Decreases by one the reference count of a #JsonObject. If the
* reference count reaches zero, the object is destroyed and all
* its allocated resources are freed.
*/
void
json_object_unref (JsonObject *object)
{
gint old_ref;
g_return_if_fail (object != NULL);
g_return_if_fail (object->ref_count > 0);
old_ref = g_atomic_int_get (&object->ref_count);
if (old_ref > 1)
g_atomic_int_compare_and_exchange (&object->ref_count, old_ref, old_ref - 1);
else
{
g_hash_table_destroy (object->members);
object->members = NULL;
g_slice_free (JsonObject, object);
}
}
/**
* json_object_add_member:
* @object: a #JsonObject
* @member_name: the name of the member
* @node: the value of the member
*
* Adds a member named @member_name and containing @node into a #JsonObject.
*/
void
json_object_add_member (JsonObject *object,
const gchar *member_name,
JsonNode *node)
{
g_return_if_fail (object != NULL);
g_return_if_fail (member_name != NULL);
g_return_if_fail (node != NULL);
if (json_object_has_member (object, member_name))
{
g_warning ("JsonObject already has a `%s' member of type `%s'",
member_name,
json_node_type_name (node));
return;
}
g_hash_table_replace (object->members, g_strdup (member_name), node);
}
/**
* json_object_get_members:
* @object: a #JsonObject
*
* Retrieves all the names of the members of a #JsonObject. You can
* obtain the value for each member using json_object_get_member().
*
* Return value: a #GList of member names. The content of the list
* is owned by the #JsonObject and should never be modified or
* freed. When you have finished using the returned list, use
* g_slist_free() to free the resources it has allocated.
*/
GList *
json_object_get_members (JsonObject *object)
{
g_return_val_if_fail (object != NULL, NULL);
return g_hash_table_get_keys (object->members);
}
/**
* json_object_get_member:
* @object: a #JsonObject
* @member_name: the name of the JSON object member to access
*
* Retrieves the value of @member_name inside a #JsonObject.
*
* Return value: a pointer to the value for the requested object
* member, or %NULL
*/
JsonNode *
json_object_get_member (JsonObject *object,
const gchar *member_name)
{
g_return_val_if_fail (object != NULL, NULL);
g_return_val_if_fail (member_name != NULL, NULL);
return g_hash_table_lookup (object->members, member_name);
}
/**
* json_object_has_member:
* @object: a #JsonObject
* @member_name: the name of a JSON object member
*
* Checks whether @object has a member named @member_name.
*
* Return value: %TRUE if the JSON object has the requested member
*/
gboolean
json_object_has_member (JsonObject *object,
const gchar *member_name)
{
g_return_val_if_fail (object != NULL, FALSE);
g_return_val_if_fail (member_name != NULL, FALSE);
return (g_hash_table_lookup (object->members, member_name) != NULL);
}
/**
* json_object_get_size:
* @object: a #JsonObject
*
* Retrieves the size of a #JsonObject.
*
* Return value: the number of members the JSON object has
*/
guint
json_object_get_size (JsonObject *object)
{
g_return_val_if_fail (object != NULL, 0);
return g_hash_table_size (object->members);
}

998
clutter/json/json-parser.c Normal file
View File

@ -0,0 +1,998 @@
/* json-parser.c - JSON streams parser
*
* This file is part of JSON-GLib
* Copyright (C) 2007 OpenedHand Ltd.
*
* 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.
*
* Author:
* Emmanuele Bassi <ebassi@openedhand.com>
*/
/**
* SECTION:json-parser
* @short_description: Parse JSON data streams
*
* #JsonParser provides an object for parsing a JSON data stream, either
* inside a file or inside a static buffer.
*/
#include "config.h"
#include <string.h>
#include "json-marshal.h"
#include "json-parser.h"
GQuark
json_parser_error_quark (void)
{
return g_quark_from_static_string ("json-parser-error");
}
#define JSON_PARSER_GET_PRIVATE(obj) \
(G_TYPE_INSTANCE_GET_PRIVATE ((obj), JSON_TYPE_PARSER, JsonParserPrivate))
struct _JsonParserPrivate
{
JsonNode *root;
JsonNode *current_node;
GScanner *scanner;
};
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"
"null\0";
static const struct
{
guint name_offset;
guint token;
} symbols[] = {
{ 0, JSON_TOKEN_TRUE },
{ 5, JSON_TOKEN_FALSE },
{ 11, JSON_TOKEN_NULL }
};
static const guint n_symbols = G_N_ELEMENTS (symbols);
enum
{
PARSE_START,
OBJECT_START,
OBJECT_MEMBER,
OBJECT_END,
ARRAY_START,
ARRAY_ELEMENT,
ARRAY_END,
PARSE_END,
ERROR,
LAST_SIGNAL
};
static guint parser_signals[LAST_SIGNAL] = { 0, };
G_DEFINE_TYPE (JsonParser, json_parser, G_TYPE_OBJECT);
static guint json_parse_array (JsonParser *parser,
GScanner *scanner,
gboolean nested);
static guint json_parse_object (JsonParser *parser,
GScanner *scanner,
gboolean nested);
static void
json_parser_dispose (GObject *gobject)
{
JsonParserPrivate *priv = JSON_PARSER_GET_PRIVATE (gobject);
if (priv->root)
{
json_node_free (priv->root);
priv->root = NULL;
}
G_OBJECT_CLASS (json_parser_parent_class)->dispose (gobject);
}
static void
json_parser_class_init (JsonParserClass *klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
g_type_class_add_private (klass, sizeof (JsonParserPrivate));
gobject_class->dispose = json_parser_dispose;
/**
* JsonParser::parse-start:
* @parser: the #JsonParser that received the signal
*
* The ::parse-start signal is emitted when the parser began parsing
* a JSON data stream.
*/
parser_signals[PARSE_START] =
g_signal_new ("parse-start",
G_OBJECT_CLASS_TYPE (gobject_class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (JsonParserClass, parse_start),
NULL, NULL,
_json_marshal_VOID__VOID,
G_TYPE_NONE, 0);
/**
* JsonParser::parse-end:
* @parser: the #JsonParser that received the signal
*
* The ::parse-end signal is emitted when the parser successfully
* finished parsing a JSON data stream
*/
parser_signals[PARSE_END] =
g_signal_new ("parse-end",
G_OBJECT_CLASS_TYPE (gobject_class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (JsonParserClass, parse_end),
NULL, NULL,
_json_marshal_VOID__VOID,
G_TYPE_NONE, 0);
/**
* JsonParser::object-start:
* @parser: the #JsonParser that received the signal
*
* The ::object-start signal is emitted each time the #JsonParser
* starts parsing a #JsonObject.
*/
parser_signals[OBJECT_START] =
g_signal_new ("object-start",
G_OBJECT_CLASS_TYPE (gobject_class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (JsonParserClass, object_start),
NULL, NULL,
_json_marshal_VOID__VOID,
G_TYPE_NONE, 0);
/**
* JsonParser::object-member:
* @parser: the #JsonParser that received the signal
* @object: a #JsonObject
* @member_name: the name of the newly parsed member
*
* The ::object-member signal is emitted each time the #JsonParser
* has successfully parsed a single member of a #JsonObject. The
* object and member are passed to the signal handlers.
*/
parser_signals[OBJECT_MEMBER] =
g_signal_new ("object-member",
G_OBJECT_CLASS_TYPE (gobject_class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (JsonParserClass, object_member),
NULL, NULL,
_json_marshal_VOID__BOXED_STRING,
G_TYPE_NONE, 2,
JSON_TYPE_OBJECT,
G_TYPE_STRING);
/**
* JsonParser::object-end:
* @parser: the #JsonParser that received the signal
* @object: the parsed #JsonObject
*
* The ::object-end signal is emitted each time the #JsonParser
* has successfully parsed an entire #JsonObject.
*/
parser_signals[OBJECT_END] =
g_signal_new ("object-end",
G_OBJECT_CLASS_TYPE (gobject_class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (JsonParserClass, object_end),
NULL, NULL,
_json_marshal_VOID__BOXED,
G_TYPE_NONE, 1,
JSON_TYPE_OBJECT);
/**
* JsonParser::array-start:
* @parser: the #JsonParser that received the signal
*
* The ::array-start signal is emitted each time the #JsonParser
* starts parsing a #JsonArray
*/
parser_signals[ARRAY_START] =
g_signal_new ("array-start",
G_OBJECT_CLASS_TYPE (gobject_class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (JsonParserClass, array_start),
NULL, NULL,
_json_marshal_VOID__VOID,
G_TYPE_NONE, 0);
/**
* JsonParser::array-element:
* @parser: the #JsonParser that received the signal
* @array: a #JsonArray
* @index_: the index of the newly parsed element
*
* The ::array-element signal is emitted each time the #JsonParser
* has successfully parsed a single element of a #JsonArray. The
* array and element index are passed to the signal handlers.
*/
parser_signals[ARRAY_ELEMENT] =
g_signal_new ("array-element",
G_OBJECT_CLASS_TYPE (gobject_class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (JsonParserClass, array_element),
NULL, NULL,
_json_marshal_VOID__BOXED_INT,
G_TYPE_NONE, 2,
JSON_TYPE_ARRAY,
G_TYPE_INT);
/**
* JsonParser::array-end:
* @parser: the #JsonParser that received the signal
* @array: the parsed #JsonArrary
*
* The ::array-end signal is emitted each time the #JsonParser
* has successfully parsed an entire #JsonArray
*/
parser_signals[ARRAY_END] =
g_signal_new ("array-end",
G_OBJECT_CLASS_TYPE (gobject_class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (JsonParserClass, array_end),
NULL, NULL,
_json_marshal_VOID__BOXED,
G_TYPE_NONE, 1,
JSON_TYPE_ARRAY);
/**
* JsonParser::error:
* @parser: the parser instance that received the signal
* @error: a pointer to the #GError
*
* The ::error signal is emitted each time a #JsonParser encounters
* an error in a JSON stream.
*/
parser_signals[ERROR] =
g_signal_new ("error",
G_OBJECT_CLASS_TYPE (gobject_class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (JsonParserClass, error),
NULL, NULL,
_json_marshal_VOID__POINTER,
G_TYPE_NONE, 1,
G_TYPE_POINTER);
}
static void
json_parser_init (JsonParser *parser)
{
JsonParserPrivate *priv;
parser->priv = priv = JSON_PARSER_GET_PRIVATE (parser);
priv->root = NULL;
priv->current_node = NULL;
}
static guint
json_parse_array (JsonParser *parser,
GScanner *scanner,
gboolean nested)
{
JsonParserPrivate *priv = parser->priv;
JsonArray *array;
guint token;
if (!nested)
{
/* the caller already swallowed the opening '[' */
token = g_scanner_get_next_token (scanner);
if (token != G_TOKEN_LEFT_BRACE)
return G_TOKEN_LEFT_BRACE;
}
g_signal_emit (parser, parser_signals[ARRAY_START], 0);
array = json_array_new ();
token = g_scanner_get_next_token (scanner);
while (token != G_TOKEN_RIGHT_BRACE)
{
JsonNode *node = NULL;
GValue value = { 0, };
if (token == G_TOKEN_COMMA)
{
/* swallow the comma */
token = g_scanner_get_next_token (scanner);
continue;
}
/* nested object */
if (token == G_TOKEN_LEFT_CURLY)
{
JsonNode *old_node = priv->current_node;
priv->current_node = json_node_new (JSON_NODE_OBJECT);
token = json_parse_object (parser, scanner, TRUE);
node = priv->current_node;
priv->current_node = old_node;
if (token != G_TOKEN_NONE)
{
json_node_free (node);
json_array_unref (array);
return token;
}
json_array_add_element (array, node);
node->parent = priv->current_node;
g_signal_emit (parser, parser_signals[ARRAY_ELEMENT], 0,
array,
json_array_get_length (array));
token = g_scanner_get_next_token (scanner);
if (token == G_TOKEN_RIGHT_BRACE)
break;
continue;
}
/* nested array */
if (token == G_TOKEN_LEFT_BRACE)
{
JsonNode *old_node = priv->current_node;
priv->current_node = json_node_new (JSON_NODE_ARRAY);
token = json_parse_array (parser, scanner, TRUE);
node = priv->current_node;
priv->current_node = old_node;
if (token != G_TOKEN_NONE)
{
json_node_free (node);
json_array_unref (array);
return token;
}
json_array_add_element (array, node);
node->parent = priv->current_node;
g_signal_emit (parser, parser_signals[ARRAY_ELEMENT], 0,
array,
json_array_get_length (array));
token = g_scanner_get_next_token (scanner);
if (token == G_TOKEN_RIGHT_BRACE)
break;
continue;
}
switch (token)
{
case G_TOKEN_INT:
g_value_init (&value, G_TYPE_INT);
g_value_set_int (&value, scanner->value.v_int);
node = json_node_new (JSON_NODE_VALUE);
json_node_set_value (node, &value);
g_value_unset (&value);
break;
case G_TOKEN_FLOAT:
g_value_init (&value, G_TYPE_DOUBLE);
g_value_set_double (&value, scanner->value.v_float);
node = json_node_new (JSON_NODE_VALUE);
json_node_set_value (node, &value);
g_value_unset (&value);
break;
case G_TOKEN_STRING:
g_value_init (&value, G_TYPE_STRING);
g_value_set_string (&value, scanner->value.v_string);
node = json_node_new (JSON_NODE_VALUE);
json_node_set_value (node, &value);
g_value_unset (&value);
break;
case JSON_TOKEN_TRUE:
case JSON_TOKEN_FALSE:
g_value_init (&value, G_TYPE_BOOLEAN);
g_value_set_boolean (&value, token == JSON_TOKEN_TRUE ? TRUE : FALSE);
node = json_node_new (JSON_NODE_VALUE);
json_node_set_value (node, &value);
g_value_unset (&value);
break;
case JSON_TOKEN_NULL:
node = json_node_new (JSON_NODE_NULL);
break;
default:
return G_TOKEN_RIGHT_BRACE;
}
if (node)
{
json_array_add_element (array, node);
node->parent = priv->current_node;
g_signal_emit (parser, parser_signals[ARRAY_ELEMENT], 0,
array,
json_array_get_length (array));
}
token = g_scanner_get_next_token (scanner);
}
json_node_take_array (priv->current_node, array);
g_signal_emit (parser, parser_signals[ARRAY_END], 0, array);
return G_TOKEN_NONE;
}
static guint
json_parse_object (JsonParser *parser,
GScanner *scanner,
gboolean nested)
{
JsonParserPrivate *priv = parser->priv;
JsonObject *object;
guint token;
if (!nested)
{
/* the caller already swallowed the opening '{' */
token = g_scanner_get_next_token (scanner);
if (token != G_TOKEN_LEFT_CURLY)
return G_TOKEN_LEFT_CURLY;
}
g_signal_emit (parser, parser_signals[OBJECT_START], 0);
object = json_object_new ();
token = g_scanner_get_next_token (scanner);
while (token != G_TOKEN_RIGHT_CURLY)
{
JsonNode *node = NULL;
gchar *name = NULL;
GValue value = { 0, };
if (token == G_TOKEN_COMMA)
{
/* swallow the comma */
token = g_scanner_get_next_token (scanner);
continue;
}
if (token == G_TOKEN_STRING)
{
name = g_strdup (scanner->value.v_string);
token = g_scanner_get_next_token (scanner);
if (token != ':')
{
g_free (name);
json_object_unref (object);
return ':';
}
else
{
/* swallow the colon */
token = g_scanner_get_next_token (scanner);
}
}
if (!name)
{
json_object_unref (object);
return G_TOKEN_STRING;
}
if (token == G_TOKEN_LEFT_CURLY)
{
JsonNode *old_node = priv->current_node;
priv->current_node = json_node_new (JSON_NODE_OBJECT);
token = json_parse_object (parser, scanner, TRUE);
node = priv->current_node;
priv->current_node = old_node;
if (token != G_TOKEN_NONE)
{
g_free (name);
json_node_free (node);
json_object_unref (object);
return token;
}
json_object_add_member (object, name, node);
node->parent = priv->current_node;
g_signal_emit (parser, parser_signals[OBJECT_MEMBER], 0,
object,
name);
g_free (name);
token = g_scanner_get_next_token (scanner);
if (token == G_TOKEN_RIGHT_CURLY)
break;
continue;
}
if (token == G_TOKEN_LEFT_BRACE)
{
JsonNode *old_node = priv->current_node;
priv->current_node = json_node_new (JSON_NODE_ARRAY);
token = json_parse_array (parser, scanner, TRUE);
node = priv->current_node;
priv->current_node = old_node;
if (token != G_TOKEN_NONE)
{
g_free (name);
json_node_free (node);
json_object_unref (object);
return token;
}
json_object_add_member (object, name, node);
node->parent = priv->current_node;
g_signal_emit (parser, parser_signals[OBJECT_MEMBER], 0,
object,
name);
g_free (name);
token = g_scanner_get_next_token (scanner);
if (token == G_TOKEN_RIGHT_BRACE)
break;
continue;
}
switch (token)
{
case G_TOKEN_INT:
g_value_init (&value, G_TYPE_INT);
g_value_set_int (&value, scanner->value.v_int);
node = json_node_new (JSON_NODE_VALUE);
json_node_set_value (node, &value);
g_value_unset (&value);
break;
case G_TOKEN_FLOAT:
g_value_init (&value, G_TYPE_DOUBLE);
g_value_set_double (&value, scanner->value.v_float);
node = json_node_new (JSON_NODE_VALUE);
json_node_set_value (node, &value);
g_value_unset (&value);
break;
case G_TOKEN_STRING:
g_value_init (&value, G_TYPE_STRING);
g_value_set_string (&value, scanner->value.v_string);
node = json_node_new (JSON_NODE_VALUE);
json_node_set_value (node, &value);
g_value_unset (&value);
break;
case JSON_TOKEN_TRUE:
case JSON_TOKEN_FALSE:
g_value_init (&value, G_TYPE_BOOLEAN);
g_value_set_boolean (&value, token == JSON_TOKEN_TRUE ? TRUE : FALSE);
node = json_node_new (JSON_NODE_VALUE);
json_node_set_value (node, &value);
g_value_unset (&value);
break;
case JSON_TOKEN_NULL:
node = json_node_new (JSON_NODE_NULL);
break;
default:
return G_TOKEN_RIGHT_BRACE;
}
if (node)
{
json_object_add_member (object, name, node);
node->parent = priv->current_node;
g_signal_emit (parser, parser_signals[OBJECT_MEMBER], 0,
object,
name);
}
g_free (name);
token = g_scanner_get_next_token (scanner);
}
json_node_take_object (priv->current_node, object);
g_signal_emit (parser, parser_signals[OBJECT_END], 0, object);
return G_TOKEN_NONE;
}
static guint
json_parse_statement (JsonParser *parser,
GScanner *scanner)
{
JsonParserPrivate *priv = parser->priv;
guint token;
token = g_scanner_peek_next_token (scanner);
switch (token)
{
case G_TOKEN_LEFT_CURLY:
priv->root = priv->current_node = json_node_new (JSON_NODE_OBJECT);
return json_parse_object (parser, scanner, FALSE);
case G_TOKEN_LEFT_BRACE:
priv->root = priv->current_node = json_node_new (JSON_NODE_ARRAY);
return json_parse_array (parser, scanner, FALSE);
case JSON_TOKEN_NULL:
priv->root = priv->current_node = json_node_new (JSON_NODE_NULL);
return G_TOKEN_NONE;
default:
g_scanner_get_next_token (scanner);
return G_TOKEN_SYMBOL;
}
}
static void
json_scanner_msg_handler (GScanner *scanner,
gchar *message,
gboolean error)
{
JsonParser *parser = scanner->user_data;
if (error)
{
GError *error = NULL;
g_set_error (&error, JSON_PARSER_ERROR,
JSON_PARSER_ERROR_PARSE,
"Parse error on line %d: %s",
scanner->line,
message);
g_signal_emit (parser, parser_signals[ERROR], 0, error);
g_error_free (error);
}
else
g_warning ("Line %d: %s", scanner->line, message);
}
static GScanner *
json_scanner_new (JsonParser *parser)
{
GScanner *scanner;
scanner = g_scanner_new (&json_scanner_config);
scanner->msg_handler = json_scanner_msg_handler;
scanner->user_data = parser;
return scanner;
}
/**
* json_parser_new:
*
* Creates a new #JsonParser instance. You can use the #JsonParser to
* load a JSON stream from either a file or a buffer and then walk the
* hierarchy using the data types API.
*
* Return value: the newly created #JsonParser. Use g_object_unref()
* to release all the memory it allocates.
*/
JsonParser *
json_parser_new (void)
{
return g_object_new (JSON_TYPE_PARSER, NULL);
}
/**
* json_parser_load_from_file:
* @parser: a #JsonParser
* @filename: the path for the file to parse
* @error: return location for a #GError, or %NULL
*
* Loads a JSON stream from the content of @filename and parses it. See
* json_parser_load_from_data().
*
* Return value: %TRUE if the file was successfully loaded and parsed.
* In case of error, @error is set accordingly and %FALSE is returned
*/
gboolean
json_parser_load_from_file (JsonParser *parser,
const gchar *filename,
GError **error)
{
GError *internal_error;
gchar *data;
gsize length;
gboolean retval = TRUE;
g_return_val_if_fail (JSON_IS_PARSER (parser), FALSE);
g_return_val_if_fail (filename != NULL, FALSE);
internal_error = NULL;
if (!g_file_get_contents (filename, &data, &length, &internal_error))
{
g_propagate_error (error, internal_error);
return FALSE;
}
if (!json_parser_load_from_data (parser, data, length, &internal_error))
{
g_propagate_error (error, internal_error);
retval = FALSE;
}
g_free (data);
return retval;
}
/**
* json_parser_load_from_data:
* @parser: a #JsonParser
* @data: the buffer to parse
* @length: the length of the buffer, or -1
* @error: return location for a #GError, or %NULL
*
* Loads a JSON stream from a buffer and parses it. You can call this function
* multiple times with the same #JsonParser object, but the contents of the
* parser will be destroyed each time.
*
* Return value: %TRUE if the buffer was succesfully parser. In case
* of error, @error is set accordingly and %FALSE is returned
*/
gboolean
json_parser_load_from_data (JsonParser *parser,
const gchar *data,
gsize length,
GError **error)
{
GScanner *scanner;
gboolean done;
gboolean retval = TRUE;
gint i;
g_return_val_if_fail (JSON_IS_PARSER (parser), FALSE);
g_return_val_if_fail (data != NULL, FALSE);
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));
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));
}
parser->priv->scanner = scanner;
g_signal_emit (parser, parser_signals[PARSE_START], 0);
done = FALSE;
while (!done)
{
if (g_scanner_peek_next_token (scanner) == G_TOKEN_EOF)
done = TRUE;
else
{
guint expected_token;
expected_token = json_parse_statement (parser, scanner);
if (expected_token != G_TOKEN_NONE)
{
const gchar *symbol_name;
gchar *msg;
msg = NULL;
symbol_name = NULL;
if (scanner->scope_id == 0)
{
if (expected_token > JSON_TOKEN_INVALID &&
expected_token < JSON_TOKEN_LAST)
{
for (i = 0; i < n_symbols; i++)
if (symbols[i].token == expected_token)
msg = (gchar *) symbol_names + symbols[i].name_offset;
if (msg)
msg = g_strconcat ("e.g. `", msg, "'", NULL);
}
if (scanner->token > JSON_TOKEN_INVALID &&
scanner->token < JSON_TOKEN_LAST)
{
symbol_name = "???";
for (i = 0; i < n_symbols; i++)
if (symbols[i].token == scanner->token)
symbol_name = symbol_names + symbols[i].name_offset;
}
}
/* 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);
/* we set a generic error here; the message from
* GScanner is relayed in the ::error signal
*/
g_set_error (error, JSON_PARSER_ERROR,
JSON_PARSER_ERROR_PARSE,
"Invalid token `%s' found: expecting %s",
symbol_name ? symbol_name : "???",
msg ? msg : "unknown");
retval = FALSE;
g_free (msg);
done = TRUE;
}
}
}
g_scanner_destroy (scanner);
parser->priv->scanner = NULL;
parser->priv->current_node = NULL;
g_signal_emit (parser, parser_signals[PARSE_END], 0);
return retval;
}
/**
* 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.
*/
JsonNode *
json_parser_get_root (JsonParser *parser)
{
g_return_val_if_fail (JSON_IS_PARSER (parser), NULL);
return parser->priv->root;
}
/**
* json_parser_get_current_line:
* @parser: a #JsonParser
*
* Retrieves the line currently parsed, starting from 1.
*
* Return value: the currently parsed line.
*/
guint
json_parser_get_current_line (JsonParser *parser)
{
g_return_val_if_fail (JSON_IS_PARSER (parser), 0);
if (parser->priv->scanner)
return g_scanner_cur_line (parser->priv->scanner);
return 0;
}
/**
* json_parser_get_current_pos:
* @parser: a #JsonParser
*
* Retrieves the current position inside the current line, starting
* from 0.
*
* Return value: the position in the current line
*/
guint
json_parser_get_current_pos (JsonParser *parser)
{
g_return_val_if_fail (JSON_IS_PARSER (parser), 0);
if (parser->priv->scanner)
return g_scanner_cur_line (parser->priv->scanner);
return 0;
}

147
clutter/json/json-parser.h Normal file
View File

@ -0,0 +1,147 @@
/* json-parser.h - JSON streams parser
*
* This file is part of JSON-GLib
* Copyright (C) 2007 OpenedHand Ltd.
*
* 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.
*
* Author:
* Emmanuele Bassi <ebassi@openedhand.com>
*/
#ifndef __JSON_PARSER_H__
#define __JSON_PARSER_H__
#include <glib-object.h>
#include "json-types.h"
G_BEGIN_DECLS
#define JSON_TYPE_PARSER (json_parser_get_type ())
#define JSON_PARSER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), JSON_TYPE_PARSER, JsonParser))
#define JSON_IS_PARSER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), JSON_TYPE_PARSER))
#define JSON_PARSER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), JSON_TYPE_PARSER, JsonParserClass))
#define JSON_IS_PARSER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), JSON_TYPE_PARSER))
#define JSON_PARSER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), JSON_TYPE_PARSER, JsonParserClass))
#define JSON_PARSER_ERROR (json_parser_error_quark ())
typedef struct _JsonParser JsonParser;
typedef struct _JsonParserPrivate JsonParserPrivate;
typedef struct _JsonParserClass JsonParserClass;
/**
* JsonParserError:
* @JSON_PARSER_ERROR_PARSE: parse error
* @JSON_PARSER_ERROR_UNKNOWN: unknown error
*
* Error enumeration for #JsonParser
*/
typedef enum {
JSON_PARSER_ERROR_PARSE,
JSON_PARSER_ERROR_UNKNOWN
} JsonParserError;
typedef enum {
JSON_TOKEN_INVALID = G_TOKEN_LAST,
JSON_TOKEN_TRUE,
JSON_TOKEN_FALSE,
JSON_TOKEN_NULL,
JSON_TOKEN_LAST
} JsonTokenType;
/**
* JsonParser:
*
* JSON data streams parser. The contents of the #JsonParser structure are
* private and should only be accessed via the provided API.
*/
struct _JsonParser
{
/*< private >*/
GObject parent_instance;
JsonParserPrivate *priv;
};
/**
* JsonParserClass:
* @parse_start: class handler for the JsonParser::parse-start signal
* @object_start: class handler for the JsonParser::object-start signal
* @object_member: class handler for the JsonParser::object-member signal
* @object_end: class handler for the JsonParser::object-end signal
* @array_start: class handler for the JsonParser::array-start signal
* @array_element: class handler for the JsonParser::array-element signal
* @array_end: class handler for the JsonParser::array-end signal
* @parse_end: class handler for the JsonParser::parse-end signal
* @error: class handler for the JsonParser::error signal
*
* #JsonParser class.
*/
struct _JsonParserClass
{
/*< private >*/
GObjectClass parent_class;
/*< public >*/
void (* parse_start) (JsonParser *parser);
void (* object_start) (JsonParser *parser);
void (* object_member) (JsonParser *parser,
JsonObject *object,
const gchar *member_name);
void (* object_end) (JsonParser *parser,
JsonObject *object);
void (* array_start) (JsonParser *parser);
void (* array_element) (JsonParser *parser,
JsonArray *array,
gint index_);
void (* array_end) (JsonParser *parser,
JsonArray *array);
void (* parse_end) (JsonParser *parser);
void (* error) (JsonParser *parser,
const GError *error);
/*< private >*/
/* padding for future expansion */
void (* _json_reserved1) (void);
void (* _json_reserved2) (void);
void (* _json_reserved3) (void);
void (* _json_reserved4) (void);
void (* _json_reserved5) (void);
void (* _json_reserved6) (void);
void (* _json_reserved7) (void);
void (* _json_reserved8) (void);
};
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,
const gchar *filename,
GError **error);
gboolean json_parser_load_from_data (JsonParser *parser,
const gchar *data,
gsize length,
GError **error);
JsonNode * json_parser_get_root (JsonParser *parser);
guint json_parser_get_current_line (JsonParser *parser);
guint json_parser_get_current_pos (JsonParser *parser);
G_END_DECLS
#endif /* __JSON_PARSER_H__ */

156
clutter/json/json-types.h Normal file
View File

@ -0,0 +1,156 @@
/* json-types.h - JSON data types
*
* This file is part of JSON-GLib
* Copyright (C) 2007 OpenedHand Ltd.
*
* 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.
*
* Author:
* Emmanuele Bassi <ebassi@openedhand.com>
*/
#ifndef __JSON_TYPES_H__
#define __JSON_TYPES_H__
#include <glib-object.h>
G_BEGIN_DECLS
/**
* JSON_NODE_TYPE:
* @node: a #JsonNode
*
* Evaluates to the #JsonNodeType contained by @node
*/
#define JSON_NODE_TYPE(node) (((JsonNode *) (node))->type)
#define JSON_TYPE_OBJECT (json_object_get_type ())
#define JSON_TYPE_ARRAY (json_array_get_type ())
/**
* JsonObject:
*
* A JSON object type. The contents of the #JsonObject structure are private
* and should only be accessed by the provided API
*/
typedef struct _JsonObject JsonObject;
/**
* JsonArray:
*
* A JSON array type. The contents of the #JsonArray structure are private
* and should only be accessed by the provided API
*/
typedef struct _JsonArray JsonArray;
typedef struct _JsonNode JsonNode;
/**
* JsonNodeType:
* @JSON_NODE_OBJECT: The node contains a #JsonObject
* @JSON_NODE_ARRAY: The node contains a #JsonArray
* @JSON_NODE_VALUE: The node contains a #GValue
* @JSON_NODE_NULL: Special type, for nodes containing %NULL
*
* Indicates the content of a #JsonNode.
*/
typedef enum {
JSON_NODE_OBJECT,
JSON_NODE_ARRAY,
JSON_NODE_VALUE,
JSON_NODE_NULL
} JsonNodeType;
/**
* JsonNode:
*
* A generic container of JSON data types. The contents of the #JsonNode
* structure are private and should only be accessed via the provided
* functions and never directly.
*/
struct _JsonNode
{
/*< private >*/
JsonNodeType type;
union {
JsonObject *object;
JsonArray *array;
GValue value;
} data;
JsonNode *parent;
};
JsonNode * json_node_new (JsonNodeType type);
JsonNode * json_node_copy (JsonNode *node);
void json_node_free (JsonNode *node);
void json_node_set_object (JsonNode *node,
JsonObject *object);
void json_node_take_object (JsonNode *node,
JsonObject *object);
JsonObject * json_node_get_object (JsonNode *node);
JsonObject * json_node_dup_object (JsonNode *node);
void json_node_set_array (JsonNode *node,
JsonArray *array);
void json_node_take_array (JsonNode *node,
JsonArray *array);
JsonArray * json_node_get_array (JsonNode *node);
JsonArray * json_node_dup_array (JsonNode *node);
void json_node_set_value (JsonNode *node,
const GValue *value);
void json_node_get_value (JsonNode *node,
GValue *value);
void json_node_set_string (JsonNode *node,
const gchar *value);
G_CONST_RETURN gchar *json_node_get_string (JsonNode *node);
gchar * json_node_dup_string (JsonNode *node);
void json_node_set_int (JsonNode *node,
gint value);
gint json_node_get_int (JsonNode *node);
void json_node_set_double (JsonNode *node,
gdouble value);
gdouble json_node_get_double (JsonNode *node);
void json_node_set_boolean (JsonNode *node,
gboolean value);
gboolean json_node_get_boolean (JsonNode *node);
JsonNode * json_node_get_parent (JsonNode *node);
G_CONST_RETURN gchar *json_node_type_name (JsonNode *node);
GType json_object_get_type (void) G_GNUC_CONST;
JsonObject * json_object_new (void);
JsonObject * json_object_ref (JsonObject *object);
void json_object_unref (JsonObject *object);
void json_object_add_member (JsonObject *object,
const gchar *member_name,
JsonNode *node);
GList * json_object_get_members (JsonObject *object);
JsonNode * json_object_get_member (JsonObject *object,
const gchar *member_name);
gboolean json_object_has_member (JsonObject *object,
const gchar *member_name);
guint json_object_get_size (JsonObject *object);
GType json_array_get_type (void) G_GNUC_CONST;
JsonArray * json_array_new (void);
JsonArray * json_array_sized_new (guint n_elements);
JsonArray * json_array_ref (JsonArray *array);
void json_array_unref (JsonArray *array);
void json_array_add_element (JsonArray *array,
JsonNode *node);
GList * json_array_get_elements (JsonArray *array);
JsonNode * json_array_get_element (JsonArray *array,
guint index_);
guint json_array_get_length (JsonArray *array);
G_END_DECLS
#endif /* __JSON_TYPES_H__ */

View File

@ -273,7 +273,7 @@ AC_SUBST([clutterbackendlib])
dnl ========================================================================
pkg_modules="pangoft2 glib-2.0 >= 2.10 gobject-2.0 gthread-2.0 gdk-pixbuf-2.0 $BACKEND_PC_FILES"
pkg_modules="pangoft2 glib-2.0 >= 2.14 gobject-2.0 gthread-2.0 gdk-pixbuf-2.0 $BACKEND_PC_FILES"
PKG_CHECK_MODULES(CLUTTER_DEPS, [$pkg_modules])
dnl ========================================================================
@ -359,7 +359,6 @@ AC_SUBST(CLUTTER_LIBS)
AC_CONFIG_FILES([
Makefile
clutter/pango/Makefile
clutter/Makefile
clutter/clutter-version.h
clutter/glx/Makefile
@ -369,6 +368,8 @@ AC_CONFIG_FILES([
clutter/cogl/Makefile
clutter/cogl/gl/Makefile
clutter/cogl/gles/Makefile
clutter/json/Makefile
clutter/pango/Makefile
tests/Makefile
doc/Makefile
doc/reference/Makefile

View File

@ -60,8 +60,9 @@ IGNORE_HFILES=\
eglnative \
eglx \
glx \
sdl \
pango
json \
pango \
sdl
EXTRA_HFILES=\
../../clutter/glx/clutter-glx.h

View File

@ -1,7 +1,7 @@
noinst_PROGRAMS = test-textures test-events test-offscreen test-scale \
test-actors test-behave test-text test-entry test-project \
test-boxes test-perspective test-rotate test-depth \
test-threads test-timeline test-score
test-threads test-timeline test-score test-script
INCLUDES = -I$(top_srcdir)/
LDADD = $(top_builddir)/clutter/libclutter-@CLUTTER_FLAVOUR@-@CLUTTER_MAJORMINOR@.la
@ -24,5 +24,6 @@ test_depth_SOURCES = test-depth.c
test_threads_SOURCES = test-threads.c
test_timeline_SOURCES = test-timeline.c
test_score_SOURCES = test-score.c
test_script_SOURCES = test-script.c
EXTRA_DIST = redhand.png

81
tests/test-script.c Normal file
View File

@ -0,0 +1,81 @@
#include <stdlib.h>
#include <stdio.h>
#include <glib.h>
#include <clutter/clutter.h>
static const gchar *test_ui =
"{"
" \"Scene\" : {"
" \"id\" : \"main-stage\","
" \"type\" : \"ClutterStage\","
" \"color\" : \"white\","
" \"width\" : 500,"
" \"height\" : 200,"
" \"children\" : ["
" {"
" \"id\" : \"red-button\","
" \"type\" : \"ClutterRectangle\","
" \"color\" : \"#ff0000ff\","
" \"x\" : 50,"
" \"y\" : 50,"
" \"width\" : 100,"
" \"height\" : 100,"
" \"visible\" : true,"
" },"
" {"
" \"id\" : \"green-button\","
" \"type\" : \"ClutterRectangle\","
" \"color\" : \"#00ff00ff\","
" \"x\" : 200,"
" \"y\" : 50,"
" \"width\" : 100,"
" \"height\" : 100,"
" \"visible\" : true,"
" },"
" {"
" \"id\" : \"blue-button\","
" \"type\" : \"ClutterRectangle\","
" \"color\" : \"#0000ffff\","
" \"x\" : 350,"
" \"y\" : 50,"
" \"width\" : 100,"
" \"height\" : 100,"
" \"visible\" : true,"
" }"
" ]"
" }"
"}";
int
main (int argc, char *argv[])
{
ClutterActor *stage;
ClutterActor *rect;
ClutterScript *script;
GError *error;
clutter_init (&argc, &argv);
script = clutter_script_new ();
error = NULL;
clutter_script_load_from_data (script, test_ui, -1, &error);
if (error)
{
g_print ("*** Error:\n"
"*** %s\n", error->message);
g_error_free (error);
g_object_unref (script);
return EXIT_FAILURE;
}
stage = CLUTTER_ACTOR (clutter_script_get_object (script, "main-stage"));
clutter_actor_show (stage);
clutter_main ();
g_object_unref (script);
return EXIT_SUCCESS;
}