# HG changeset patch # User Sebastien Jodogne # Date 1435821025 -7200 # Node ID 3f772217946729b3eee2098a37ea30781c81b3e1 # Parent 5ba7471780ae63b9130e93b12413dfdb8bccc1fd refactoring: GetJson in Lua diff -r 5ba7471780ae -r 3f7722179467 Core/Lua/LuaContext.cpp --- a/Core/Lua/LuaContext.cpp Wed Jul 01 17:42:06 2015 +0200 +++ b/Core/Lua/LuaContext.cpp Thu Jul 02 09:10:25 2015 +0200 @@ -123,6 +123,8 @@ int LuaContext::DumpJson(lua_State *state) { + LuaContext& that = GetLuaContext(state); + int nArgs = lua_gettop(state); if (nArgs != 1) { @@ -131,17 +133,11 @@ } 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); - } + that.GetJson(json, 1); + + Json::FastWriter writer; + std::string s = writer.write(json); + lua_pushstring(state, s.c_str()); return 1; } @@ -376,132 +372,108 @@ } - static bool CompactObjectToArray(Json::Value& result, - const Json::Value& source) + void LuaContext::GetJson(Json::Value& result, + int top) { - Json::Value::Members members = source.getMemberNames(); - - std::set keys; - for (Json::Value::ArrayIndex i = 0; i < members.size(); i++) + if (lua_istable(lua_, top)) { - try - { - size_t key = boost::lexical_cast(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(members[i]); - assert(key > 0); - result[key - 1] = source[members[i]]; - } + Json::Value tmp = Json::objectValue; + bool isArray = true; + size_t size = 0; - 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 + // Code adapted from: 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); + lua_pushvalue(lua_, top); // stack now contains: -1 => table - lua_pushnil(state); + lua_pushnil(lua_); // stack now contains: -1 => nil; -2 => table - while (lua_next(state, -2)) + while (lua_next(lua_, -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); + lua_pushvalue(lua_, -2); // stack now contains: -1 => key; -2 => value; -3 => key; -4 => table - const char *key = lua_tostring(state, -1); + std::string key(lua_tostring(lua_, -1)); + Json::Value v; + GetJson(v, -2); - Json::Value item; - if (!GetJson(item, state, -2)) + tmp[key] = v; + + size += 1; + try { - lua_pop(state, 3); // TODO IS THIS CORRECT? - return false; + if (boost::lexical_cast(key) != size) + { + isArray = false; + } } - - result[key] = item; - + catch (boost::bad_lexical_cast&) + { + isArray = false; + } + // pop value + copy of key, leaving original key - lua_pop(state, 2); + lua_pop(lua_, 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); + lua_pop(lua_, 1); // Stack is now the same as it was on entry to this function - Json::Value array; - if (CompactObjectToArray(array, result)) + if (isArray) { - result = array; + result = Json::arrayValue; + for (size_t i = 0; i < size; i++) + { + result.append(tmp[boost::lexical_cast(i + 1)]); + } + } + else + { + result = tmp; } - - return true; + } + else if (lua_isnil(lua_, top)) + { + result = Json::nullValue; + } + else if (lua_isboolean(lua_, top)) + { + result = lua_toboolean(lua_, top) ? true : false; } + else if (lua_isnumber(lua_, top)) + { + // Convert to "int" if truncation does not loose precision + double value = static_cast(lua_tonumber(lua_, top)); + int truncated = static_cast(value); - return false; + if (std::abs(value - static_cast(truncated)) <= + std::numeric_limits::epsilon()) + { + result = truncated; + } + else + { + result = value; + } + } + else if (lua_isstring(lua_, top)) + { + // Caution: The "lua_isstring()" case must be the last, since + // Lua can convert most types to strings by default. + result = std::string(lua_tostring(lua_, top)); + } + else + { + LOG(WARNING) << "Unsupported Lua type when returning Json"; + result = Json::nullValue; + } } diff -r 5ba7471780ae -r 3f7722179467 Core/Lua/LuaContext.h --- a/Core/Lua/LuaContext.h Wed Jul 01 17:42:06 2015 +0200 +++ b/Core/Lua/LuaContext.h Thu Jul 02 09:10:25 2015 +0200 @@ -76,9 +76,8 @@ void PushJson(const Json::Value& value); - static bool GetJson(Json::Value& result, - lua_State *state, - int index); + void GetJson(Json::Value& result, + int top); public: LuaContext(); diff -r 5ba7471780ae -r 3f7722179467 Core/Lua/LuaFunctionCall.cpp --- a/Core/Lua/LuaFunctionCall.cpp Wed Jul 01 17:42:06 2015 +0200 +++ b/Core/Lua/LuaFunctionCall.cpp Thu Jul 02 09:10:25 2015 +0200 @@ -126,97 +126,25 @@ } - static void PopJson(Json::Value& result, - lua_State* lua, - int top) + void LuaFunctionCall::ExecuteToJson(Json::Value& result) { - if (lua_istable(lua, top)) - { - Json::Value tmp = Json::objectValue; - bool isArray = true; - size_t size = 0; + ExecuteInternal(1); + context_.GetJson(result, lua_gettop(context_.lua_)); + } - // 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(lua, top); - // stack now contains: -1 => table - lua_pushnil(lua); - // stack now contains: -1 => nil; -2 => table - while (lua_next(lua, -2)) - { - // stack now contains: -1 => value; -2 => key; -3 => table - // copy the key so that lua_tostring does not modify the original - lua_pushvalue(lua, -2); - // stack now contains: -1 => key; -2 => value; -3 => key; -4 => table - std::string key(lua_tostring(lua, -1)); - Json::Value v; - PopJson(v, lua, -2); - - tmp[key] = v; - size += 1; - try - { - if (boost::lexical_cast(key) != size) - { - isArray = false; - } - } - catch (boost::bad_lexical_cast&) - { - isArray = false; - } - - // pop value + copy of key, leaving original key - lua_pop(lua, 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(lua, 1); - - // Stack is now the same as it was on entry to this function - - if (isArray) - { - result = Json::arrayValue; - for (size_t i = 0; i < size; i++) - { - result.append(tmp[boost::lexical_cast(i + 1)]); - } - } - else - { - result = tmp; - } - } - else if (lua_isnumber(lua, top)) + void LuaFunctionCall::ExecuteToString(std::string& result) + { + ExecuteInternal(1); + + int top = lua_gettop(context_.lua_); + if (lua_isstring(context_.lua_, top)) { - result = static_cast(lua_tonumber(lua, top)); - } - else if (lua_isstring(lua, top)) - { - result = std::string(lua_tostring(lua, top)); - } - else if (lua_isboolean(lua, top)) - { - result = static_cast(lua_toboolean(lua, top)); + result = lua_tostring(context_.lua_, top); } else { - LOG(WARNING) << "Unsupported Lua type when returning Json"; - result = Json::nullValue; + throw LuaException("The function does not return a string"); } } - - - void LuaFunctionCall::ExecuteToJson(Json::Value& result) - { - ExecuteInternal(1); - PopJson(result, context_.lua_, lua_gettop(context_.lua_)); - } } diff -r 5ba7471780ae -r 3f7722179467 Core/Lua/LuaFunctionCall.h --- a/Core/Lua/LuaFunctionCall.h Wed Jul 01 17:42:06 2015 +0200 +++ b/Core/Lua/LuaFunctionCall.h Thu Jul 02 09:10:25 2015 +0200 @@ -70,5 +70,7 @@ bool ExecutePredicate(); void ExecuteToJson(Json::Value& result); + + void ExecuteToString(std::string& result); }; } diff -r 5ba7471780ae -r 3f7722179467 UnitTestsSources/LuaTests.cpp --- a/UnitTestsSources/LuaTests.cpp Wed Jul 01 17:42:06 2015 +0200 +++ b/UnitTestsSources/LuaTests.cpp Thu Jul 02 09:10:25 2015 +0200 @@ -147,8 +147,8 @@ { Json::Value b = Json::objectValue; b["a"] = 42; - b["b"] = 44; - b["c"] = 43; + b["b"] = 44.37; + b["c"] = -43; Json::Value c = Json::arrayValue; c.append("test3"); @@ -184,6 +184,14 @@ { Orthanc::LuaFunctionCall f(lua, "identity"); + f.PushJson(-42); + Json::Value v; + f.ExecuteToJson(v); + ASSERT_EQ(-42, v.asInt()); + } + + { + Orthanc::LuaFunctionCall f(lua, "identity"); Json::Value vv = Json::arrayValue; f.PushJson(vv); Json::Value v; @@ -208,8 +216,8 @@ f.ExecuteToJson(v); ASSERT_EQ(Json::objectValue, v.type()); ASSERT_FLOAT_EQ(42.0f, v["a"].asFloat()); - ASSERT_FLOAT_EQ(44.0f, v["b"].asFloat()); - ASSERT_FLOAT_EQ(43.0f, v["c"].asFloat()); + ASSERT_FLOAT_EQ(44.37f, v["b"].asFloat()); + ASSERT_FLOAT_EQ(-43.0f, v["c"].asFloat()); } { @@ -231,13 +239,27 @@ ASSERT_EQ("World", v["Hello"].asString()); ASSERT_EQ(42, v["List"][0]["a"].asInt()); ASSERT_EQ(44, v["List"][0]["b"].asInt()); - ASSERT_EQ(43, v["List"][0]["c"].asInt()); + ASSERT_EQ(-43, v["List"][0]["c"].asInt()); ASSERT_EQ("test3", v["List"][1][0].asString()); ASSERT_EQ("test1", v["List"][1][1].asString()); ASSERT_EQ("test2", v["List"][1][2].asString()); } + + { + Orthanc::LuaFunctionCall f(lua, "DumpJson"); + f.PushJson(a); + std::string s; + f.ExecuteToString(s); + + Json::FastWriter writer; + std::string t = writer.write(a); + + ASSERT_EQ(s, t); + } } + + TEST(Lua, Http) { Orthanc::LuaContext lua;