mirror of
https://github.com/brl/mutter.git
synced 2024-11-22 16:10:41 -05:00
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:
parent
7b3464226c
commit
991562f536
31
ChangeLog
31
ChangeLog
@ -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.
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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 */
|
||||
|
||||
|
31
clutter/clutter-script-private.h
Normal file
31
clutter/clutter-script-private.h
Normal 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
765
clutter/clutter-script.c
Normal 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, ¶m.value))
|
||||
{
|
||||
g_value_init (¶m.value, G_PARAM_SPEC_VALUE_TYPE (pspec));
|
||||
g_value_copy (&pinfo->value, ¶m.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, ¶ms);
|
||||
|
||||
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 (¶m.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
78
clutter/clutter-script.h
Normal 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__ */
|
@ -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
26
clutter/json/Makefile.am
Normal 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
231
clutter/json/json-array.c
Normal 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);
|
||||
}
|
533
clutter/json/json-generator.c
Normal file
533
clutter/json/json-generator.c
Normal 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;
|
||||
}
|
83
clutter/json/json-generator.h
Normal file
83
clutter/json/json-generator.h
Normal 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
8
clutter/json/json-glib.h
Normal 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
130
clutter/json/json-marshal.c
Normal 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) */
|
||||
|
37
clutter/json/json-marshal.h
Normal file
37
clutter/json/json-marshal.h
Normal 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
610
clutter/json/json-node.c
Normal 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 (©, G_TYPE_STRING);
|
||||
g_value_set_string (©, value);
|
||||
|
||||
json_node_set_value (node, ©);
|
||||
|
||||
g_value_unset (©);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 (©, G_TYPE_INT);
|
||||
g_value_set_int (©, value);
|
||||
|
||||
json_node_set_value (node, ©);
|
||||
|
||||
g_value_unset (©);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 (©, G_TYPE_DOUBLE);
|
||||
g_value_set_double (©, value);
|
||||
|
||||
json_node_set_value (node, ©);
|
||||
|
||||
g_value_unset (©);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 (©, G_TYPE_BOOLEAN);
|
||||
g_value_set_boolean (©, value);
|
||||
|
||||
json_node_set_value (node, ©);
|
||||
|
||||
g_value_unset (©);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
234
clutter/json/json-object.c
Normal 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
998
clutter/json/json-parser.c
Normal 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
147
clutter/json/json-parser.h
Normal 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
156
clutter/json/json-types.h
Normal 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__ */
|
@ -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
|
||||
|
@ -60,8 +60,9 @@ IGNORE_HFILES=\
|
||||
eglnative \
|
||||
eglx \
|
||||
glx \
|
||||
sdl \
|
||||
pango
|
||||
json \
|
||||
pango \
|
||||
sdl
|
||||
|
||||
EXTRA_HFILES=\
|
||||
../../clutter/glx/clutter-glx.h
|
||||
|
@ -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
81
tests/test-script.c
Normal 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;
|
||||
}
|
Loading…
Reference in New Issue
Block a user