summaryrefslogtreecommitdiffstats
path: root/src/Bindings/LuaJson.cpp
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/Bindings/LuaJson.cpp315
1 files changed, 315 insertions, 0 deletions
diff --git a/src/Bindings/LuaJson.cpp b/src/Bindings/LuaJson.cpp
new file mode 100644
index 000000000..4fa16273c
--- /dev/null
+++ b/src/Bindings/LuaJson.cpp
@@ -0,0 +1,315 @@
+
+// LuaJson.cpp
+
+// Implements the Json exposure bindings to Lua
+
+#include "Globals.h"
+#include <sstream>
+#include "LuaJson.h"
+#include "LuaState.h"
+#include "tolua++/include/tolua++.h"
+#include "json/json.h"
+
+
+
+
+
+// fwd:
+
+static void PushJsonValue(const Json::Value & a_Value, cLuaState & a_LuaState);
+static Json::Value JsonSerializeValue(cLuaState & a_LuaState);
+
+
+
+
+
+/** Pushes the specified Json array as a table on top of the specified Lua state.
+Assumes that a_Value is an array. */
+static void PushJsonArray(const Json::Value & a_Value, cLuaState & a_LuaState)
+{
+ // Create the appropriately-sized Lua table:
+ lua_createtable(a_LuaState, static_cast<int>(a_Value.size()), 0);
+
+ // Insert each value to the appropriate index (1-based):
+ int idx = 1;
+ for (const auto & v: a_Value)
+ {
+ // Include Json null values in the array - it will have holes, but indices will stay the same
+ PushJsonValue(v, a_LuaState);
+ lua_rawseti(a_LuaState, -2, idx);
+ idx += 1;
+ } // for v: a_Value[]
+}
+
+
+
+
+
+/** Pushes the specified Json object as a table on top of the specified Lua state.
+Assumes that a_Value is an object. */
+static void PushJsonObject(const Json::Value & a_Value, cLuaState & a_LuaState)
+{
+ // Create the appropriately-sized Lua table:
+ lua_createtable(a_LuaState, 0, static_cast<int>(a_Value.size()));
+
+ // Json::Value has no means of iterating over children with their names included.
+ // We need to iterate over names and "find" them in the object again:
+ auto names = a_Value.getMemberNames();
+ for (const auto & n: names)
+ {
+ auto v = a_Value[n];
+ if (v.isNull())
+ {
+ // Skip null values
+ continue;
+ }
+
+ // Set the value in Lua's table:
+ a_LuaState.Push(n);
+ PushJsonValue(v, a_LuaState);
+ lua_rawset(a_LuaState, -3);
+ } // for itr - a_Value[]
+}
+
+
+
+
+
+/** Pushes the specified Json value as an appropriate type on top of the specified Lua state. */
+void PushJsonValue(const Json::Value & a_Value, cLuaState & a_LuaState)
+{
+ switch (a_Value.type())
+ {
+ case Json::nullValue:
+ {
+ a_LuaState.PushNil();
+ break;
+ }
+
+ case Json::intValue:
+ case Json::uintValue:
+ case Json::realValue:
+ {
+ a_LuaState.Push(static_cast<lua_Number>(a_Value.asDouble()));
+ break;
+ }
+
+ case Json::booleanValue:
+ {
+ a_LuaState.Push(a_Value.asBool());
+ break;
+ }
+
+ case Json::stringValue:
+ {
+ a_LuaState.Push(a_Value.asString());
+ break;
+ }
+
+ case Json::arrayValue:
+ {
+ PushJsonArray(a_Value, a_LuaState);
+ break;
+ }
+
+ case Json::objectValue:
+ {
+ PushJsonObject(a_Value, a_LuaState);
+ break;
+ }
+ } // switch (v.type())
+}
+
+
+
+
+
+/** Serializes the Lua table at the top of the specified Lua state's stack into a Json value.
+Lets jsoncpp decides whether to serialize into an object or an array. */
+static Json::Value JsonSerializeTable(cLuaState & a_LuaState)
+{
+ Json::Value res;
+ lua_pushnil(a_LuaState);
+ while (lua_next(a_LuaState, -2) != 0)
+ {
+ if (lua_type(a_LuaState, -2) == LUA_TNUMBER)
+ {
+ int idx;
+ a_LuaState.GetStackValue(-2, idx);
+ res[idx - 1] = JsonSerializeValue(a_LuaState);
+ }
+ else
+ {
+ AString name;
+ if (a_LuaState.GetStackValue(-2, name))
+ {
+ res[name] = JsonSerializeValue(a_LuaState);
+ }
+ }
+ lua_pop(a_LuaState, 1);
+ }
+ return res;
+}
+
+
+
+
+
+/** Serializes the Lua value at the top of the specified Lua state into a Json value. */
+static Json::Value JsonSerializeValue(cLuaState & a_LuaState)
+{
+ switch (lua_type(a_LuaState, -1))
+ {
+ case LUA_TNUMBER:
+ {
+ lua_Number v;
+ a_LuaState.GetStackValue(-1, v);
+ return Json::Value(v);
+ }
+ case LUA_TSTRING:
+ {
+ AString v;
+ a_LuaState.GetStackValue(-1, v);
+ return Json::Value(v);
+ }
+ case LUA_TTABLE:
+ {
+ return JsonSerializeTable(a_LuaState);
+ }
+ default:
+ {
+ LOGD("Attempting to serialize an unhandled Lua value type: %d", lua_type(a_LuaState, -1));
+ return Json::Value(Json::nullValue);
+ }
+ }
+}
+
+
+
+
+
+static int tolua_cJson_Parse(lua_State * a_LuaState)
+{
+ // Function signature:
+ // cJson:Parse("string") -> table
+
+ // Check the param types:
+ cLuaState L(a_LuaState);
+ if (
+ !L.CheckParamUserTable(1, "cJson") ||
+ !L.CheckParamString(2) ||
+ !L.CheckParamEnd(3)
+ )
+ {
+ return 0;
+ }
+
+ // Get the input string:
+ AString input;
+ if (!L.GetStackValue(2, input))
+ {
+ LOGWARNING("cJson:Parse(): Cannot read input string");
+ L.LogStackTrace();
+ return 0;
+ }
+
+ // Parse the string:
+ Json::Value root;
+ Json::Reader reader;
+ if (!reader.parse(input, root, false))
+ {
+ L.PushNil();
+ L.Push(Printf("Parsing Json failed: %s", reader.getFormattedErrorMessages().c_str()));
+ return 2;
+ }
+
+ // Push the Json value onto Lua stack:
+ PushJsonValue(root, L);
+ return 1;
+}
+
+
+
+
+
+static int tolua_cJson_Serialize(lua_State * a_LuaState)
+{
+ // Function signature:
+ // cJson:Serialize({table}, [{option1 = value1, option2 = value2}]) -> string
+
+ // Check the param types:
+ cLuaState L(a_LuaState);
+ if (
+ !L.CheckParamUserTable(1, "cJson") ||
+ !L.CheckParamTable(2) ||
+ !L.CheckParamEnd(4)
+ )
+ {
+ return 0;
+ }
+
+ // Push the table to the top of the Lua stack, and call the serializing function:
+ lua_pushvalue(L, 2);
+ Json::Value root = JsonSerializeValue(L);
+ lua_pop(L, 1);
+
+ // Create the writer, with all properties (optional param 3) applied to it:
+ Json::StreamWriterBuilder builder;
+ if (lua_istable(L, 3))
+ {
+ lua_pushnil(L);
+ while (lua_next(L, -2) != 0)
+ {
+ if (lua_type(L, -2) == LUA_TSTRING)
+ {
+ AString propName, propValue;
+ if (L.GetStackValues(-2, propName, propValue))
+ {
+ builder[propName] = propValue;
+ }
+ }
+ lua_pop(L, 1);
+ }
+ // Check for invalid settings:
+ Json::Value invalid;
+ if (!builder.validate(&invalid))
+ {
+ LOGINFO("cJson:Serialize(): detected invalid settings:");
+ for (const auto & n: invalid.getMemberNames())
+ {
+ LOGINFO(" \"%s\" (\"%s\")", n.c_str(), invalid[n].asCString());
+ }
+ }
+ }
+ auto writer(builder.newStreamWriter());
+
+ // Serialize the string and push it as the return value:
+ std::stringstream ss;
+ writer->write(root, &ss);
+ L.Push(ss.str());
+ return 1;
+}
+
+
+
+
+
+void cLuaJson::Bind(cLuaState & a_LuaState)
+{
+ tolua_beginmodule(a_LuaState, nullptr);
+
+ // Create the cJson API class:
+ tolua_usertype(a_LuaState, "cJson");
+ tolua_cclass(a_LuaState, "cJson", "cJson", "", nullptr);
+
+ // Fill in the functions (alpha-sorted):
+ tolua_beginmodule(a_LuaState, "cJson");
+ tolua_function(a_LuaState, "Parse", tolua_cJson_Parse);
+ tolua_function(a_LuaState, "Serialize", tolua_cJson_Serialize);
+ tolua_endmodule(a_LuaState);
+ tolua_endmodule(a_LuaState);
+}
+
+
+
+