[grilo-plugins] lua-factory: Lua libraries for sources



commit 20c60d158d8d41b9137f97dba259442e6f2a19a2
Author: Victor Toso <me victortoso com>
Date:   Thu Oct 31 22:59:30 2013 -0200

    lua-factory: Lua libraries for sources
    
    Include libraries for lua sources to use.
    
    Core library:
    - grl.callback: operation to return the content for user application;
    - grl.fetch: A fetch operation that uses GrlNet to fetch web content;
    - grl.get_options: Get options provided by application;
    - grl.get_requested_keys: List of all requested keys;
    - grl.get_media_keys: Current values of media, mainly for resolve op;
    
    Json library:
    - grl.json.string_to_table: parser using json-glib that gets a json
     object as string and return its equivalent in a table;
    
    https://bugzilla.gnome.org/show_bug.cgi?id=711243

 configure.ac                                |   10 +-
 src/lua-factory/Makefile.am                 |    1 +
 src/lua-factory/grl-lua-library.c           |  523 +++++++++++++++++++++++++++
 src/lua-factory/grl-lua-library.h           |    1 +
 src/lua-factory/lua-library/lua-json.c      |  168 +++++++++
 src/lua-factory/lua-library/lua-libraries.h |   36 ++
 6 files changed, 737 insertions(+), 2 deletions(-)
---
diff --git a/configure.ac b/configure.ac
index abced34..73710e8 100644
--- a/configure.ac
+++ b/configure.ac
@@ -842,6 +842,12 @@ AC_ARG_ENABLE(lua_factory,
                         if test "x$HAVE_LUA" = "xno"; then
                            AC_MSG_ERROR([lua not found, install it or use --disable-lua-factory])
                         fi
+                        if test "x$HAVE_GRLNET" = "xno"; then
+                           AC_MSG_ERROR([grilo-net not found, install it or use --disable-lua-factory])
+                        fi
+                        if test "x$HAVE_JSON_GLIB" = "xno"; then
+                           AC_MSG_ERROR([json-glib-1.0 not found, install it or use --disable-lua-factory])
+                        fi
                         ;;
                 esac
         ],
@@ -864,9 +870,9 @@ LUA_FACTORY_PLUGIN_ID="grl-lua-factory"
 AC_SUBST(LUA_FACTORY_PLUGIN_ID)
 AC_DEFINE_UNQUOTED([LUA_FACTORY_PLUGIN_ID], ["$LUA_FACTORY_PLUGIN_ID"], [Lua Factory plugin ID])
 
-DEPS_LUA_FACTORY_CFLAGS="$DEPS_CFLAGS $LUA_CFLAGS"
+DEPS_LUA_FACTORY_CFLAGS="$DEPS_CFLAGS $LUA_CFLAGS $GRLNET_CFLAGS $JSON_CFLAGS"
 AC_SUBST(DEPS_LUA_FACTORY_CFLAGS)
-DEPS_LUA_FACTORY_LIBS="$DEPS_LIBS $LUA_LIBS"
+DEPS_LUA_FACTORY_LIBS="$DEPS_LIBS $LUA_LIBS $GRLNET_LIBS $JSON_LIBS"
 AC_SUBST(DEPS_LUA_FACTORY_LIBS)
 
 LUA_FACTORY_SOURCE_LOCATION="grilo-plugins/${LUA_FACTORY_PLUGIN_ID}"
diff --git a/src/lua-factory/Makefile.am b/src/lua-factory/Makefile.am
index 0af5360..b4edeb0 100644
--- a/src/lua-factory/Makefile.am
+++ b/src/lua-factory/Makefile.am
@@ -26,6 +26,7 @@ libgrlluafactory_la_SOURCES =                                 \
        grl-lua-library.c                                       \
        grl-lua-library.h                                       \
        grl-lua-common.h                                        \
+       lua-library/lua-json.c                                  \
        lua-library/lua-libraries.h
 
 extdir                 = $(GRL_PLUGINS_DIR)
diff --git a/src/lua-factory/grl-lua-library.c b/src/lua-factory/grl-lua-library.c
index 4b6c3ed..f15f3d5 100644
--- a/src/lua-factory/grl-lua-library.c
+++ b/src/lua-factory/grl-lua-library.c
@@ -25,16 +25,529 @@
 
 #include "grl-lua-common.h"
 #include "grl-lua-library.h"
+#include "lua-library/lua-libraries.h"
 
 #define GRL_LOG_DOMAIN_DEFAULT lua_library_log_domain
 GRL_LOG_DOMAIN_STATIC (lua_library_log_domain);
 
+typedef struct _FetchOperation {
+  lua_State *L;
+  gchar *lua_cb;
+} FetchOperation;
+
+/* ================== Lua-Library utils/helpers ============================ */
+
+/* Top of the stack must be a table */
+static void
+grl_util_add_table_to_media (lua_State *L,
+                             GrlMedia *media,
+                             GrlKeyID key_id,
+                             const gchar *key_name,
+                             GType type)
+{
+  gint i = 0;
+  gint array_len = luaL_len (L, -1);
+
+  /* Remove all current values of this key, if any */
+  while (grl_data_length (GRL_DATA (media), key_id) > 0) {
+    grl_data_remove (GRL_DATA (media), key_id);
+  }
+
+  /* Insert new values */
+  for (i = 0; i < array_len; i++) {
+    lua_pushinteger (L, i + 1);
+    lua_gettable (L, -2);
+    switch (type) {
+    case G_TYPE_INT:
+      if (lua_isnumber (L, -1))
+        grl_data_add_int (GRL_DATA (media), key_id, lua_tointeger (L, -1));
+      break;
+
+    case G_TYPE_FLOAT:
+      if (lua_isnumber (L, -1))
+        grl_data_add_float (GRL_DATA (media), key_id, lua_tointeger (L, -1));
+      break;
+
+    case G_TYPE_STRING:
+      if (lua_isstring (L, -1))
+        grl_data_add_string (GRL_DATA (media), key_id, lua_tostring (L, -1));
+      break;
+
+    default:
+        GRL_DEBUG ("'%s' is being ignored when value is a table object",
+                   key_name);
+    }
+    lua_pop (L, 1);
+  }
+}
+
+static GrlMedia *
+grl_util_build_media (lua_State *L,
+                      GrlMedia *user_media)
+{
+  GrlRegistry *registry = NULL;
+  GrlMedia *media = user_media;
+
+  if (!lua_istable (L, 1)) {
+    if (!lua_isnil (L, 1))
+      GRL_DEBUG ("Media in wrong format (neither nil or table).");
+
+    return NULL;
+  }
+
+  if (media == NULL) {
+    lua_getfield (L, 1, "type");
+    if (lua_isstring (L, -1)) {
+      const gchar *media_type = lua_tostring (L, -1);
+
+      if (g_strcmp0 (media_type, "box") == 0)
+        media = grl_media_box_new ();
+      else if (g_strcmp0 (media_type, "image") == 0)
+        media = grl_media_image_new ();
+      else if (g_strcmp0 (media_type, "audio") == 0)
+        media = grl_media_audio_new ();
+      else if (g_strcmp0 (media_type, "video") == 0)
+        media = grl_media_video_new ();
+    }
+    media = (media == NULL) ? grl_media_new () : media;
+    lua_pop (L, 1);
+  }
+
+  registry = grl_registry_get_default ();
+  lua_pushnil (L);
+  while (lua_next (L, 1) != 0) {
+    GrlKeyID key_id = GRL_METADATA_KEY_INVALID;
+    gchar *key_name = g_strdup (lua_tostring (L, -2));
+    gchar *ptr = NULL;
+    GType type = G_TYPE_NONE;
+
+    /* Replace '_' to '-': convenient for the developer */
+    while ((ptr = strstr (key_name, "_")) != NULL) {
+      *ptr = '-';
+    }
+
+    key_id = grl_registry_lookup_metadata_key (registry, key_name);
+    if (key_id != GRL_METADATA_KEY_INVALID) {
+      type = grl_registry_lookup_metadata_key_type (registry, key_id);
+
+      switch (type) {
+      case G_TYPE_INT:
+        if (lua_isnumber (L, -1)) {
+          grl_data_set_int (GRL_DATA (media), key_id, lua_tointeger (L, -1));
+        } else if (lua_istable (L, -1)) {
+          grl_util_add_table_to_media (L, media, key_id, key_name, type);
+        } else {
+          GRL_WARNING ("'%s' is not compatible for '%s'",
+                       lua_typename (L, -1), key_name);
+        }
+        break;
+
+      case G_TYPE_FLOAT:
+        if (lua_isnumber (L, -1)) {
+          grl_data_set_float (GRL_DATA (media), key_id, lua_tonumber (L, -1));
+        } else if (lua_istable (L, -1)) {
+          grl_util_add_table_to_media (L, media, key_id, key_name, type);
+        } else {
+          GRL_WARNING ("'%s' is not compatible for '%s'",
+                       lua_typename (L, -1), key_name);
+        }
+        break;
+
+      case G_TYPE_STRING:
+        if (lua_isstring (L, -1)) {
+          grl_data_set_string (GRL_DATA (media), key_id, lua_tostring (L, -1));
+        } else if (lua_istable (L, -1)) {
+          grl_util_add_table_to_media (L, media, key_id, key_name, type);
+        } else {
+          GRL_WARNING ("'%s' is not compatible for '%s'",
+                       lua_typename (L, -1), key_name);
+        }
+        break;
+
+      default:
+        if (type == G_TYPE_DATE_TIME) {
+          GDateTime *date = grl_date_time_from_iso8601 (lua_tostring (L, -1));
+          grl_data_set_boxed (GRL_DATA (media), key_id, date);
+          g_date_time_unref (date);
+        } else if (type == G_TYPE_BYTE_ARRAY) {
+           gsize size = luaL_len (L, -1);
+           const guint8 *binary = lua_tostring (L, -1);
+           grl_data_set_binary (GRL_DATA (media), key_id, binary, size);
+        } else {
+          GRL_DEBUG ("'%s' is being ignored as G_TYPE is not being handled.",
+                     key_name);
+        }
+      }
+    }
+
+    g_free (key_name);
+    lua_pop (L, 1);
+  }
+  return media;
+}
+
+static void
+grl_util_fetch_done (GObject *source_object,
+                     GAsyncResult *res,
+                     gpointer user_data)
+{
+  gchar *data = NULL;
+  gsize length = 0;
+  GError *err = NULL;
+  FetchOperation *fo = (FetchOperation *) user_data;
+  lua_State *L = fo->L;
+
+  GRL_DEBUG ("fetch_done");
+
+  grl_net_wc_request_finish (GRL_NET_WC (source_object),
+                             res, &data, &length, &err);
+
+  lua_getglobal (L, fo->lua_cb);
+  lua_pushlstring (L, data, length);
+
+  if (lua_pcall (L, 1, 0, 0)) {
+    GRL_WARNING ("%s (%s) '%s'", "calling source callback function fail",
+                 fo->lua_cb, lua_tolstring (L, -1, NULL));
+  }
+  g_free (fo);
+}
+
+/* ================== Lua-Library methods ================================== */
+
+/**
+* grl.get_options
+*
+* @option: (string) Name of the option you want (e.g. count, flags).
+* @key: (string) Name of the key when option request it.
+* @return: The option or nil if none;
+*/
+static gint
+grl_l_operation_get_options (lua_State *L)
+{
+  OperationSpec *os = NULL;
+  const gchar *option = NULL;
+
+  luaL_argcheck (L, lua_isstring (L, 1), 1, "expecting option (string)");
+
+  os = grl_lua_library_load_operation_data (L);
+  option = lua_tostring (L, 1);
+
+  if (g_strcmp0 (option, "count") == 0) {
+    gint count = grl_operation_options_get_count (os->options);
+
+    lua_pushnumber (L, count);
+    return 1;
+  }
+
+  if (g_strcmp0 (option, "skip") == 0) {
+    guint skip = grl_operation_options_get_skip (os->options);
+
+    lua_pushnumber (L, skip);
+    return 1;
+  }
+
+  if (g_strcmp0 (option, "flags") == 0) {
+    GrlResolutionFlags flags = grl_operation_options_get_flags (os->options);
+
+    lua_pushnumber (L, (gint) flags);
+    return 1;
+  }
+
+  if (g_strcmp0 (option, "key-filter") == 0) {
+    GrlKeyID key;
+    GValue *value = NULL;
+    const gchar *key_name = NULL;
+    GrlRegistry *registry = grl_registry_get_default ();
+
+    luaL_argcheck (L, lua_isstring (L, 2), 2, "expecting key name");
+    key_name = lua_tostring (L, 2);
+
+    key = grl_registry_lookup_metadata_key (registry, key_name);
+    value = grl_operation_options_get_key_filter (os->options, key);
+    switch (grl_registry_lookup_metadata_key_type (registry, key)) {
+    case G_TYPE_INT:
+      (value) ? lua_pushnumber (L, g_value_get_int (value)) : lua_pushnil (L);
+      break;
+
+    case G_TYPE_FLOAT:
+      (value) ? lua_pushnumber (L, g_value_get_float (value)) : lua_pushnil (L);
+      break;
+
+    case G_TYPE_STRING:
+      (value) ? lua_pushstring (L, g_value_get_string (value)) : lua_pushnil (L);
+      break;
+
+    default:
+      GRL_DEBUG ("'%s' is being ignored as G_TYPE is not being handled.",
+                 key_name);
+    }
+    return 1;
+  }
+
+  if (g_strcmp0 (option, "range-filter") == 0) {
+    GValue *min = NULL;
+    GValue *max = NULL;
+    GrlKeyID key;
+    const gchar *key_name = NULL;
+    GrlRegistry *registry = grl_registry_get_default ();
+
+    luaL_argcheck (L, lua_isstring (L, 3), 3, "expecting key name");
+    key_name = lua_tostring (L, 3);
+
+    key = grl_registry_lookup_metadata_key (registry, key_name);
+    if (key != GRL_METADATA_KEY_INVALID) {
+      grl_operation_options_get_key_range_filter (os->options, key, &min, &max);
+      switch (grl_registry_lookup_metadata_key_type (registry, key)) {
+      case G_TYPE_INT:
+        (min) ? lua_pushnumber (L, g_value_get_int (min)) : lua_pushnil (L);
+        (max) ? lua_pushnumber (L, g_value_get_int (max)) : lua_pushnil (L);
+        break;
+
+      case G_TYPE_FLOAT:
+        (min) ? lua_pushnumber (L, g_value_get_float (min)) : lua_pushnil (L);
+        (max) ? lua_pushnumber (L, g_value_get_float (max)) : lua_pushnil (L);
+        break;
+
+      case G_TYPE_STRING:
+        (min) ? lua_pushstring (L, g_value_get_string (min)) : lua_pushnil (L);
+        (max) ? lua_pushstring (L, g_value_get_string (max)) : lua_pushnil (L);
+        break;
+
+      default:
+        GRL_DEBUG ("'%s' is being ignored as G_TYPE is not being handled.",
+                   key_name);
+      }
+    }
+    return 2;
+  }
+
+  luaL_error (L, "'%s' is not available nor implemented.", option);
+  return 0;
+}
+
+/**
+* grl.get_media_keys
+*
+* @return: array of all requested keys from application (may be empty);
+*/
+static gint
+grl_l_operation_get_keys (lua_State *L)
+{
+  OperationSpec *os = NULL;
+  GrlRegistry *registry = NULL;
+  GList *it = NULL;
+  GrlKeyID key_id;
+  const gchar *key_name = NULL;
+  gint i = 0;
+
+  os = grl_lua_library_load_operation_data (L);
+
+  registry = grl_registry_get_default ();
+  lua_newtable (L);
+  for (it = os->keys; it; it = g_list_next (it)) {
+    key_id = GRLPOINTER_TO_KEYID (it->data);
+    key_name = grl_registry_lookup_metadata_key_name (registry, key_id);
+    if (key_id != GRL_METADATA_KEY_INVALID) {
+      lua_pushinteger (L, i + 1);
+      lua_pushstring (L, key_name);
+      lua_settable (L, -3);
+      i = i + 1;
+    }
+  }
+  return 1;
+}
+
+/**
+* grl.get_media_keys
+*
+* @return: table with all keys/values of media (may be empty);
+*/
+static gint
+grl_l_media_get_keys (lua_State *L)
+{
+  OperationSpec *os = NULL;
+  GrlRegistry *registry = NULL;
+  GList *it = NULL;
+  GList *list_keys = NULL;
+  GrlKeyID key_id;
+  gchar *key_name = NULL;
+
+  os = grl_lua_library_load_operation_data (L);
+
+  registry = grl_registry_get_default ();
+  lua_newtable (L);
+  list_keys = grl_data_get_keys (GRL_DATA (os->media));
+  for (it = list_keys; it; it = g_list_next (it)) {
+    gchar *ptr = NULL;
+    GType type = G_TYPE_NONE;
+    key_id = GRLPOINTER_TO_KEYID (it->data);
+    key_name = g_strdup (grl_registry_lookup_metadata_key_name (registry,
+                                                                key_id));
+    key_id = grl_registry_lookup_metadata_key (registry, key_name);
+
+    /* Replace '-' to '_': as a convenience for the developer */
+    while ((ptr = strstr (key_name, "-")) != NULL) {
+      *ptr = '_';
+    }
+
+    lua_pushstring (L, key_name);
+    g_free (key_name);
+    if (key_id != GRL_METADATA_KEY_INVALID) {
+      type = grl_registry_lookup_metadata_key_type (registry, key_id);
+      switch (type) {
+      case G_TYPE_INT:
+        lua_pushnumber (L, grl_data_get_int (GRL_DATA (os->media), key_id));
+        break;
+      case G_TYPE_FLOAT:
+        lua_pushnumber (L, grl_data_get_float (GRL_DATA (os->media), key_id));
+        break;
+      case G_TYPE_STRING:
+        lua_pushstring (L, grl_data_get_string (GRL_DATA (os->media), key_id));
+        break;
+      default:
+        if (type == G_TYPE_DATE_TIME) {
+          GDateTime *date = grl_data_get_boxed (GRL_DATA (os->media), key_id);
+          gchar *date_str = g_date_time_format (date, "%F %T");
+          lua_pushstring (L, date_str);
+          g_free(date_str);
+
+        } else {
+          GRL_DEBUG ("'%s' is being ignored as G_TYPE is not being handled.",
+                     key_name);
+          lua_pop (L, 1);
+          continue;
+        }
+      }
+      lua_settable (L, -3);
+    }
+  }
+  g_list_free (list_keys);
+  return 1;
+}
+
+/**
+* grl.fetch
+*
+* @url: (string) The http url to GET the content.
+* @callback: (string) The function to be called after fetch is complete.
+* @return: Nothing.;
+*/
+static gint
+grl_l_fetch (lua_State *L)
+{
+  const gchar *url = NULL;
+  const gchar *lua_callback = NULL;
+  GrlNetWc *wc = NULL;
+  FetchOperation *fo = g_malloc (sizeof (FetchOperation));
+
+  luaL_argcheck (L, lua_isstring (L, 1), 1, "expecting url as string");
+  luaL_argcheck (L, lua_isstring (L, 2), 2,
+                 "expecting callback function as string");
+
+  url = lua_tolstring (L, 1, NULL);
+  lua_callback = lua_tolstring (L, 2, NULL);
+
+  GRL_DEBUG ("grl.fetch() -> '%s'", url);
+
+  wc = grl_net_wc_new ();
+  if (lua_istable (L, 3)) {
+    /* Set GrlNetWc options */
+    lua_pushnil (L);
+    while (lua_next (L, 3) != 0) {
+      const gchar *key = lua_tostring (L, -2);
+      if (g_strcmp0 (key, "user-agent") == 0 ||
+          g_strcmp0 (key, "user_agent") == 0) {
+        const gchar *user_agent = lua_tostring (L, -1);
+        g_object_set (wc, "user-agent", user_agent, NULL);
+
+      } else if (g_strcmp0 (key, "cache-size") == 0 ||
+                 g_strcmp0 (key, "cache_size") == 0) {
+        guint size = lua_tonumber (L, -1);
+        grl_net_wc_set_cache_size (wc, size);
+
+      } else if (g_strcmp0 (key, "cache") == 0) {
+        gboolean use_cache = lua_toboolean (L, -1);
+        grl_net_wc_set_cache (wc, use_cache);
+
+      } else if (g_strcmp0 (key, "throttling") == 0) {
+        guint throttling = lua_tonumber (L, -1);
+        grl_net_wc_set_throttling (wc, throttling);
+
+      } else if (g_strcmp0 (key, "loglevel") == 0) {
+        guint level = lua_tonumber (L, -1);
+        grl_net_wc_set_log_level (wc, level);
+
+      } else {
+        GRL_DEBUG ("GrlNetWc property not know: '%s'", key);
+      }
+      lua_pop (L, 1);
+    }
+  }
+
+  fo->L = L;
+  fo->lua_cb = g_strdup (lua_callback);
+
+  grl_net_wc_request_async (wc, url, NULL, grl_util_fetch_done, fo);
+  g_object_unref (wc);
+  return 1;
+}
+
+/**
+* grl.callback
+*
+* @media: (table) The media content to be returned.
+* @count: (number) Number of media remaining to the application.
+* @return: Nothing;
+*/
+static gint
+grl_l_callback (lua_State *L)
+{
+  gint nparam = 0;
+  gint count = 0;
+  OperationSpec *os = NULL;
+  GrlMedia *media = NULL;
+
+  GRL_DEBUG ("grl.callback()");
+
+  nparam = lua_gettop (L);
+  os = grl_lua_library_load_operation_data (L);
+  if (nparam > 0) {
+    media = (os->op_type == LUA_RESOLVE) ? os->media : NULL;
+    media = grl_util_build_media (L, media);
+    count = (lua_isnumber (L, 2)) ? lua_tonumber (L, 2) : 0;
+  }
+
+  switch (os->op_type) {
+  case LUA_RESOLVE:
+    os->cb.resolve (os->source, os->operation_id, media, os->user_data, NULL);
+    break;
+
+  default:
+    os->cb.result (os->source, os->operation_id, media,
+                   count, os->user_data, NULL);
+  }
+
+  /* Free Operation Spec */
+  if (count == 0) {
+    g_list_free (os->keys);
+    g_object_unref (os->options);
+    g_slice_free (OperationSpec, os);
+  }
+
+  return 0;
+}
+
 /* ================== Lua-Library initialization =========================== */
 
 gint
 luaopen_grilo (lua_State *L)
 {
   static const luaL_Reg library_fn[] = {
+    {"get_options", &grl_l_operation_get_options},
+    {"get_requested_keys", &grl_l_operation_get_keys},
+    {"get_media_keys", &grl_l_media_get_keys},
+    {"callback", &grl_l_callback},
+    {"fetch", &grl_l_fetch},
     {NULL, NULL}
   };
 
@@ -43,6 +556,16 @@ luaopen_grilo (lua_State *L)
   GRL_DEBUG ("Loading grilo lua-library");
   luaL_newlib (L, library_fn);
 
+  /* The following modules are restrict to Lua sources */
+  lua_pushstring (L, LUA_MODULES_NAME);
+  lua_newtable (L);
+
+  lua_pushstring (L, GRILO_LUA_LIBRARY_JSON);
+  luaopen_json (L);
+  lua_settable (L, -3);
+
+  /* Those modules are called in 'lua' table, inside 'grl' */
+  lua_settable (L, -3);
   return 1;
 }
 
diff --git a/src/lua-factory/grl-lua-library.h b/src/lua-factory/grl-lua-library.h
index 13e3ab1..b678f58 100644
--- a/src/lua-factory/grl-lua-library.h
+++ b/src/lua-factory/grl-lua-library.h
@@ -30,6 +30,7 @@
 #define _GRL_LUA_LIBRARY_H_
 
 #define GRILO_LUA_LIBRARY_NAME "grl"
+#define LUA_MODULES_NAME       "lua"
 
 #define LUA_ENV_TABLE "_G"
 
diff --git a/src/lua-factory/lua-library/lua-json.c b/src/lua-factory/lua-library/lua-json.c
new file mode 100644
index 0000000..c3f0640
--- /dev/null
+++ b/src/lua-factory/lua-library/lua-json.c
@@ -0,0 +1,168 @@
+/*
+ * Copyright (C) 2013 Victor Toso.
+ *
+ * Contact: Victor Toso <me victortoso com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include "lua-libraries.h"
+
+#include <json-glib/json-glib.h>
+#include <glib-object.h>
+
+/* ================== Lua-Library Json Handlers ============================ */
+
+/* Save key/values on the table in the stack if the value is an
+ * object or an array, it calls recursively the function again.
+ *
+ * @param L, pointer to the L with nil on top of it;
+ * @param reader, pointed to the first element of main object;
+ *
+ * returns: the table in the stack with all json values
+ */
+static void
+build_table_from_json_reader (lua_State *L,
+                              JsonReader *reader)
+{
+  const GError *err = json_reader_get_error (reader);
+  if (err != NULL) {
+    GRL_WARNING ("Error when building json: %s", err->message);
+    return;
+  }
+
+  if (lua_isnil (L, -1)) {
+    /* In the first execution of this recursive call, the main json object
+     * does not have a member name. The nil is in the top of the stack and
+     * it shall be converted to the table with json content */
+    lua_pop (L, 1);
+
+  } else if (lua_istable (L, -1)) {
+    const gchar *member_name = json_reader_get_member_name (reader);
+    if (member_name)
+      lua_pushstring (L, member_name);
+
+  } else if (!lua_isnumber (L, -1)) {
+    GRL_DEBUG ("getting value to either table or array");
+    return;
+  }
+
+  if (json_reader_is_object (reader)) {
+    guint index_member = 0;
+    guint num_members = json_reader_count_members (reader);
+
+    lua_createtable (L, num_members, 0);
+    for (index_member = 0; index_member < num_members; index_member++) {
+      json_reader_read_element (reader, index_member);
+      build_table_from_json_reader (L, reader);
+      json_reader_end_element (reader);
+    }
+
+  } else if (json_reader_is_array (reader)) {
+    guint index_element = 0;
+    guint num_elements = json_reader_count_elements (reader);
+
+    lua_createtable (L, num_elements, 0);
+    for (index_element = 0; index_element < num_elements; index_element++) {
+      json_reader_read_element (reader, index_element);
+      lua_pushnumber (L, index_element + 1);
+      build_table_from_json_reader (L, reader);
+      json_reader_end_element (reader);
+    }
+
+  } else if (json_reader_is_value (reader)) {
+    if (json_reader_get_null_value (reader)) {
+      lua_pushnil (L);
+    } else {
+      /* value of the element */
+      JsonNode *value = json_reader_get_value (reader);
+      switch (json_node_get_value_type (value)) {
+      case G_TYPE_STRING:
+        lua_pushstring (L, json_reader_get_string_value (reader));
+        break;
+      case G_TYPE_INT64:
+        lua_pushnumber (L, json_reader_get_int_value (reader));
+        break;
+      case G_TYPE_DOUBLE:
+        lua_pushnumber (L, json_reader_get_double_value (reader));
+        break;
+      case G_TYPE_BOOLEAN:
+        lua_pushnumber (L, json_reader_get_boolean_value (reader));
+        break;
+      default:
+        GRL_DEBUG ("'%d' (json-node-type) is not being handled",
+                   (gint) json_node_get_value_type (value));
+        lua_pushnil (L);
+      }
+    }
+  }
+
+  if (lua_gettop (L) > 3) {
+    /* save this key/value on previous table */
+    lua_settable (L, -3);
+  }
+}
+
+/* grl.lua.json.string_to_table
+ *
+ * @json_str: (string) A Json object as a string.
+ *
+ * @return: All json content as a table.
+ */
+static gint
+grl_json_parse_string (lua_State *L)
+{
+  JsonParser *parser = NULL;
+  JsonReader *reader = NULL;
+  const gchar *json_str = NULL;
+  GError *err = NULL;
+
+  luaL_argcheck (L, lua_isstring (L, 1), 1, "json string expected");
+  json_str = lua_tostring (L, 1);
+
+  parser = json_parser_new ();
+  if (!json_parser_load_from_data (parser, json_str, -1, &err)) {
+    GRL_DEBUG ("Can't parse json string: '%s'", err->message);
+    g_error_free (err);
+    g_object_unref (parser);
+    return 0;
+  }
+
+  reader = json_reader_new (json_parser_get_root (parser));
+
+  /* The return of recursive function will be a table with all
+   * json content in it */
+  lua_pushnil (L);
+  build_table_from_json_reader (L, reader);
+
+  g_object_unref (reader);
+  g_object_unref (parser);
+
+  return 1;
+}
+
+gint
+luaopen_json (lua_State *L)
+{
+  static const luaL_Reg json_library_fn[] = {
+    {"string_to_table", &grl_json_parse_string},
+    {NULL, NULL}
+  };
+
+  luaL_newlib (L, json_library_fn);
+  return 1;
+}
diff --git a/src/lua-factory/lua-library/lua-libraries.h b/src/lua-factory/lua-library/lua-libraries.h
new file mode 100644
index 0000000..6905915
--- /dev/null
+++ b/src/lua-factory/lua-library/lua-libraries.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2013 Victor Toso.
+ *
+ * Contact: Victor Toso <me victortoso com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include <lua.h>
+#include <lauxlib.h>
+#include <lualib.h>
+
+#include <grilo.h>
+
+#ifndef _GRL_LUA_LIBRARY_JSON_H_
+#define _GRL_LUA_LIBRARY_JSON_H_
+
+#define GRILO_LUA_LIBRARY_JSON  "json"
+
+gint luaopen_json (lua_State *L);
+
+#endif /* _GRL_LUA_LIBRARY_JSON_H_ */


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