diff Core/Lua/LuaContext.cpp @ 1447:5ba7471780ae

refactoring: HttpToolbox, DumpJson in Lua
author Sebastien Jodogne <s.jodogne@gmail.com>
date Wed, 01 Jul 2015 17:42:06 +0200
parents af112b7d9cba
children 3f7722179467
line wrap: on
line diff
--- a/Core/Lua/LuaContext.cpp	Wed Jul 01 13:16:12 2015 +0200
+++ b/Core/Lua/LuaContext.cpp	Wed Jul 01 17:42:06 2015 +0200
@@ -33,8 +33,10 @@
 #include "../PrecompiledHeaders.h"
 #include "LuaContext.h"
 
+#include <set>
 #include <glog/logging.h>
 #include <cassert>
+#include <boost/lexical_cast.hpp>
 
 extern "C" 
 {
@@ -90,7 +92,7 @@
   }
 
 
-  int LuaContext::ParseJsonString(lua_State *state)
+  int LuaContext::ParseJson(lua_State *state)
   {
     LuaContext& that = GetLuaContext(state);
 
@@ -119,6 +121,32 @@
   }
 
 
+  int LuaContext::DumpJson(lua_State *state)
+  {
+    int nArgs = lua_gettop(state);
+    if (nArgs != 1)
+    {
+      lua_pushnil(state);
+      return 1;
+    }
+
+    Json::Value json;
+    if (GetJson(json, state, 1))
+    {
+      Json::FastWriter writer;
+      std::string s = writer.write(json);
+      lua_pushstring(state, s.c_str());
+    }
+    else
+    {
+      LOG(ERROR) << "Lua: Unable to convert a JSON variable to a string";
+      lua_pushnil(state);
+    }
+
+    return 1;
+  }
+
+
   int LuaContext::SetHttpCredentials(lua_State *state)
   {
     LuaContext& that = GetLuaContext(state);
@@ -348,6 +376,135 @@
   }
 
 
+  static bool CompactObjectToArray(Json::Value& result,
+                                   const Json::Value& source)
+  {
+    Json::Value::Members members = source.getMemberNames();
+
+    std::set<size_t> keys;
+    for (Json::Value::ArrayIndex i = 0; i < members.size(); i++)
+    {
+      try
+      {
+        size_t key = boost::lexical_cast<size_t>(members[i]);
+        keys.insert(key);
+      }
+      catch (boost::bad_lexical_cast&)
+      {
+        return false;
+      }
+    }
+
+    if (keys.size() != members.size())
+    {
+      return false;
+    }
+
+    for (size_t i = 1; i <= members.size(); i++)
+    {
+      if (keys.find(i) == keys.end())
+      {
+        return false;
+      }
+    }
+
+    result = Json::arrayValue;
+    result.resize(members.size());
+    for (size_t i = 0; i < members.size(); i++)
+    {
+      Json::Value::ArrayIndex key = boost::lexical_cast<Json::Value::ArrayIndex>(members[i]);
+      assert(key > 0);
+      result[key - 1] = source[members[i]];
+    }
+
+    return true;
+  }
+
+
+  bool LuaContext::GetJson(Json::Value& result,
+                           lua_State *state,
+                           int i)
+  {
+    // Caution: The order of the calls below is important, otherwise
+    // Lua considers everything as a string.
+
+    if (lua_isnil(state, i))
+    {
+      result = Json::nullValue;
+      return true;
+    }
+
+    if (lua_isboolean(state, i))
+    {
+      result = lua_toboolean(state, i) ? true : false;
+      return true;
+    }
+
+    if (lua_isnumber(state, i))
+    {
+      result = lua_tonumber(state, i);
+      return true;
+    }
+
+    if (lua_isstring(state, i))
+    {
+      result = lua_tostring(state, i);
+      return true;
+    }
+
+    if (lua_istable(state, i))
+    {
+      result = Json::objectValue;
+
+      // http://stackoverflow.com/a/6142700/881731
+      // Push another reference to the table on top of the stack (so we know
+      // where it is, and this function can work for negative, positive and
+      // pseudo indices
+      lua_pushvalue(state, i);
+      // stack now contains: -1 => table
+      lua_pushnil(state);
+      // stack now contains: -1 => nil; -2 => table
+      while (lua_next(state, -2))
+      {
+        // stack now contains: -1 => value; -2 => key; -3 => table
+        // copy the key so that lua_tostring does not modify the original
+        lua_pushvalue(state, -2);
+        // stack now contains: -1 => key; -2 => value; -3 => key; -4 => table
+        const char *key = lua_tostring(state, -1);
+
+        Json::Value item;
+        if (!GetJson(item, state, -2))
+        {
+          lua_pop(state, 3);  // TODO IS THIS CORRECT?
+          return false;
+        }
+
+        result[key] = item;
+
+        // pop value + copy of key, leaving original key
+        lua_pop(state, 2);
+        // stack now contains: -1 => key; -2 => table
+      }
+      // stack now contains: -1 => table (when lua_next returns 0 it pops the key
+      // but does not push anything.)
+      // Pop table
+      lua_pop(state, 1);
+
+      // Stack is now the same as it was on entry to this function
+
+      Json::Value array;
+      if (CompactObjectToArray(array, result))
+      {
+        result = array;
+      }
+
+      return true;
+    }
+
+    return false;
+  }
+
+
   LuaContext::LuaContext()
   {
     lua_ = luaL_newstate();
@@ -358,7 +515,8 @@
 
     luaL_openlibs(lua_);
     lua_register(lua_, "print", PrintToLog);
-    lua_register(lua_, "ParseJson", ParseJsonString);
+    lua_register(lua_, "ParseJson", ParseJson);
+    lua_register(lua_, "DumpJson", DumpJson);
     lua_register(lua_, "HttpGet", CallHttpGet);
     lua_register(lua_, "HttpPost", CallHttpPost);
     lua_register(lua_, "HttpPut", CallHttpPut);