Mercurial > hg > orthanc
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 { |