comparison Core/Lua/LuaContext.cpp @ 1448:3f7722179467

refactoring: GetJson in Lua
author Sebastien Jodogne <s.jodogne@gmail.com>
date Thu, 02 Jul 2015 09:10:25 +0200
parents 5ba7471780ae
children 905842836ad4
comparison
equal deleted inserted replaced
1447:5ba7471780ae 1448:3f7722179467
121 } 121 }
122 122
123 123
124 int LuaContext::DumpJson(lua_State *state) 124 int LuaContext::DumpJson(lua_State *state)
125 { 125 {
126 LuaContext& that = GetLuaContext(state);
127
126 int nArgs = lua_gettop(state); 128 int nArgs = lua_gettop(state);
127 if (nArgs != 1) 129 if (nArgs != 1)
128 { 130 {
129 lua_pushnil(state); 131 lua_pushnil(state);
130 return 1; 132 return 1;
131 } 133 }
132 134
133 Json::Value json; 135 Json::Value json;
134 if (GetJson(json, state, 1)) 136 that.GetJson(json, 1);
135 { 137
136 Json::FastWriter writer; 138 Json::FastWriter writer;
137 std::string s = writer.write(json); 139 std::string s = writer.write(json);
138 lua_pushstring(state, s.c_str()); 140 lua_pushstring(state, s.c_str());
139 }
140 else
141 {
142 LOG(ERROR) << "Lua: Unable to convert a JSON variable to a string";
143 lua_pushnil(state);
144 }
145 141
146 return 1; 142 return 1;
147 } 143 }
148 144
149 145
374 throw LuaException("Unsupported JSON conversion"); 370 throw LuaException("Unsupported JSON conversion");
375 } 371 }
376 } 372 }
377 373
378 374
379 static bool CompactObjectToArray(Json::Value& result, 375 void LuaContext::GetJson(Json::Value& result,
380 const Json::Value& source) 376 int top)
381 { 377 {
382 Json::Value::Members members = source.getMemberNames(); 378 if (lua_istable(lua_, top))
383 379 {
384 std::set<size_t> keys; 380 Json::Value tmp = Json::objectValue;
385 for (Json::Value::ArrayIndex i = 0; i < members.size(); i++) 381 bool isArray = true;
386 { 382 size_t size = 0;
387 try 383
388 { 384 // Code adapted from: http://stackoverflow.com/a/6142700/881731
389 size_t key = boost::lexical_cast<size_t>(members[i]); 385
390 keys.insert(key);
391 }
392 catch (boost::bad_lexical_cast&)
393 {
394 return false;
395 }
396 }
397
398 if (keys.size() != members.size())
399 {
400 return false;
401 }
402
403 for (size_t i = 1; i <= members.size(); i++)
404 {
405 if (keys.find(i) == keys.end())
406 {
407 return false;
408 }
409 }
410
411 result = Json::arrayValue;
412 result.resize(members.size());
413 for (size_t i = 0; i < members.size(); i++)
414 {
415 Json::Value::ArrayIndex key = boost::lexical_cast<Json::Value::ArrayIndex>(members[i]);
416 assert(key > 0);
417 result[key - 1] = source[members[i]];
418 }
419
420 return true;
421 }
422
423
424 bool LuaContext::GetJson(Json::Value& result,
425 lua_State *state,
426 int i)
427 {
428 // Caution: The order of the calls below is important, otherwise
429 // Lua considers everything as a string.
430
431 if (lua_isnil(state, i))
432 {
433 result = Json::nullValue;
434 return true;
435 }
436
437 if (lua_isboolean(state, i))
438 {
439 result = lua_toboolean(state, i) ? true : false;
440 return true;
441 }
442
443 if (lua_isnumber(state, i))
444 {
445 result = lua_tonumber(state, i);
446 return true;
447 }
448
449 if (lua_isstring(state, i))
450 {
451 result = lua_tostring(state, i);
452 return true;
453 }
454
455 if (lua_istable(state, i))
456 {
457 result = Json::objectValue;
458
459 // http://stackoverflow.com/a/6142700/881731
460 // Push another reference to the table on top of the stack (so we know 386 // Push another reference to the table on top of the stack (so we know
461 // where it is, and this function can work for negative, positive and 387 // where it is, and this function can work for negative, positive and
462 // pseudo indices 388 // pseudo indices
463 lua_pushvalue(state, i); 389 lua_pushvalue(lua_, top);
464 // stack now contains: -1 => table 390 // stack now contains: -1 => table
465 lua_pushnil(state); 391 lua_pushnil(lua_);
466 // stack now contains: -1 => nil; -2 => table 392 // stack now contains: -1 => nil; -2 => table
467 while (lua_next(state, -2)) 393 while (lua_next(lua_, -2))
468 { 394 {
469 // stack now contains: -1 => value; -2 => key; -3 => table 395 // stack now contains: -1 => value; -2 => key; -3 => table
470 // copy the key so that lua_tostring does not modify the original 396 // copy the key so that lua_tostring does not modify the original
471 lua_pushvalue(state, -2); 397 lua_pushvalue(lua_, -2);
472 // stack now contains: -1 => key; -2 => value; -3 => key; -4 => table 398 // stack now contains: -1 => key; -2 => value; -3 => key; -4 => table
473 const char *key = lua_tostring(state, -1); 399 std::string key(lua_tostring(lua_, -1));
474 400 Json::Value v;
475 Json::Value item; 401 GetJson(v, -2);
476 if (!GetJson(item, state, -2)) 402
403 tmp[key] = v;
404
405 size += 1;
406 try
477 { 407 {
478 lua_pop(state, 3); // TODO IS THIS CORRECT? 408 if (boost::lexical_cast<size_t>(key) != size)
479 return false; 409 {
410 isArray = false;
411 }
480 } 412 }
481 413 catch (boost::bad_lexical_cast&)
482 result[key] = item; 414 {
483 415 isArray = false;
416 }
417
484 // pop value + copy of key, leaving original key 418 // pop value + copy of key, leaving original key
485 lua_pop(state, 2); 419 lua_pop(lua_, 2);
486 // stack now contains: -1 => key; -2 => table 420 // stack now contains: -1 => key; -2 => table
487 } 421 }
488 // stack now contains: -1 => table (when lua_next returns 0 it pops the key 422 // stack now contains: -1 => table (when lua_next returns 0 it pops the key
489 // but does not push anything.) 423 // but does not push anything.)
490 // Pop table 424 // Pop table
491 lua_pop(state, 1); 425 lua_pop(lua_, 1);
492 426
493 // Stack is now the same as it was on entry to this function 427 // Stack is now the same as it was on entry to this function
494 428
495 Json::Value array; 429 if (isArray)
496 if (CompactObjectToArray(array, result)) 430 {
497 { 431 result = Json::arrayValue;
498 result = array; 432 for (size_t i = 0; i < size; i++)
499 } 433 {
500 434 result.append(tmp[boost::lexical_cast<std::string>(i + 1)]);
501 return true; 435 }
502 } 436 }
503 437 else
504 return false; 438 {
439 result = tmp;
440 }
441 }
442 else if (lua_isnil(lua_, top))
443 {
444 result = Json::nullValue;
445 }
446 else if (lua_isboolean(lua_, top))
447 {
448 result = lua_toboolean(lua_, top) ? true : false;
449 }
450 else if (lua_isnumber(lua_, top))
451 {
452 // Convert to "int" if truncation does not loose precision
453 double value = static_cast<double>(lua_tonumber(lua_, top));
454 int truncated = static_cast<int>(value);
455
456 if (std::abs(value - static_cast<double>(truncated)) <=
457 std::numeric_limits<double>::epsilon())
458 {
459 result = truncated;
460 }
461 else
462 {
463 result = value;
464 }
465 }
466 else if (lua_isstring(lua_, top))
467 {
468 // Caution: The "lua_isstring()" case must be the last, since
469 // Lua can convert most types to strings by default.
470 result = std::string(lua_tostring(lua_, top));
471 }
472 else
473 {
474 LOG(WARNING) << "Unsupported Lua type when returning Json";
475 result = Json::nullValue;
476 }
505 } 477 }
506 478
507 479
508 LuaContext::LuaContext() 480 LuaContext::LuaContext()
509 { 481 {