Make the MRU code not so bizzarely overcomplicated

Originally committed to SVN as r5502.
This commit is contained in:
Thomas Goyne 2011-07-26 19:51:56 +00:00
parent 7824348f10
commit 87abcddd87
6 changed files with 78 additions and 127 deletions

View File

@ -18,18 +18,13 @@
/// @brief Most Recently Used (MRU) Lists /// @brief Most Recently Used (MRU) Lists
/// @ingroup libaegisub /// @ingroup libaegisub
#ifndef LAGI_PRE
#include <fstream>
#include <time.h>
#endif
#include "libaegisub/cajun/writer.h" #include "libaegisub/cajun/writer.h"
#include "libaegisub/access.h" #include "libaegisub/access.h"
#include "libaegisub/io.h"
#include "libaegisub/json.h" #include "libaegisub/json.h"
#include "libaegisub/log.h" #include "libaegisub/log.h"
#include "libaegisub/mru.h" #include "libaegisub/mru.h"
#include "libaegisub/io.h"
namespace agi { namespace agi {
@ -53,72 +48,42 @@ MRUManager::MRUManager(const std::string &config, const std::string &default_con
MRUManager::~MRUManager() { MRUManager::~MRUManager() {
Flush(); Flush();
}
for (MRUMap::iterator i = mru.begin(); i != mru.end(); ++i) { MRUManager::MRUListMap &MRUManager::Find(std::string const& key) {
delete i->second; MRUMap::iterator index = mru.find(key);
} if (index == mru.end())
throw MRUErrorInvalidKey("Invalid key value");
return index->second;
} }
void MRUManager::Add(const std::string &key, const std::string &entry) { void MRUManager::Add(const std::string &key, const std::string &entry) {
MRUMap::iterator index; MRUListMap &map = Find(key);
map.remove(entry);
if ((index = mru.find(key)) != mru.end()) { map.push_front(entry);
MRUListMap &map = *index->second; Prune(map);
// Remove the file before adding it.
Remove(key, entry);
map.insert(std::pair<time_t, std::string>(time(NULL), entry));
Prune(map);
} else {
throw MRUErrorInvalidKey("Invalid key value");
}
} }
void MRUManager::Remove(const std::string &key, const std::string &entry) { void MRUManager::Remove(const std::string &key, const std::string &entry) {
MRUMap::iterator index; Find(key).remove(entry);
if ((index = mru.find(key)) != mru.end()) {
MRUListMap &map = *index->second;
for (MRUListMap::iterator map_idx = map.begin(); map_idx != map.end();) {
if (map_idx->second == entry)
map.erase(map_idx++);
else
++map_idx;
}
} else {
throw MRUErrorInvalidKey("Invalid key value");
}
} }
const MRUManager::MRUListMap* MRUManager::Get(const std::string &key) { const MRUManager::MRUListMap* MRUManager::Get(const std::string &key) {
MRUMap::iterator index; return &Find(key);
if ((index = mru.find(key)) != mru.end()) {
return index->second;
} else {
throw MRUErrorInvalidKey("Invalid key value");
}
} }
std::string const& MRUManager::GetEntry(const std::string &key, size_t entry) {
const std::string MRUManager::GetEntry(const std::string &key, const int entry) {
const MRUManager::MRUListMap *map = Get(key); const MRUManager::MRUListMap *map = Get(key);
MRUListMap::const_iterator index = map->begin(); if (entry > map->size())
if ((unsigned int)entry > map->size())
throw MRUErrorIndexOutOfRange("Requested element index is out of range."); throw MRUErrorIndexOutOfRange("Requested element index is out of range.");
std::advance(index, entry); MRUListMap::const_iterator index = map->begin();
advance(index, entry);
return index->second; return *index;
} }
@ -126,61 +91,34 @@ void MRUManager::Flush() {
json::Object out; json::Object out;
for (MRUMap::const_iterator i = mru.begin(); i != mru.end(); ++i) { for (MRUMap::const_iterator i = mru.begin(); i != mru.end(); ++i) {
json::Array array; json::Array &array = out[i->first];
MRUListMap *map_list = i->second; const MRUListMap &map_list = i->second;
for (MRUListMap::const_iterator i_lst = map_list->begin(); i_lst != map_list->end(); ++i_lst) { for (MRUListMap::const_iterator i_lst = map_list.begin(); i_lst != map_list.end(); ++i_lst) {
json::Object obj; array.Insert(json::String(*i_lst));
obj["time"] = json::Number((double)i_lst->first);
obj["entry"] = json::String(i_lst->second);
array.Insert(obj);
} }
out[i->first] = array;
} }
io::Save file(config_name); json::Writer::Write(out, io::Save(config_name).Get());
std::ofstream& ofp = file.Get();
json::Writer::Write(out, ofp);
} }
/// @brief Prune MRUListMap to the desired length. /// @brief Prune MRUListMap to the desired length.
/// This uses the user-set values for MRU list length. /// This uses the user-set values for MRU list length.
inline void MRUManager::Prune(MRUListMap& map) { inline void MRUManager::Prune(MRUListMap& map) {
unsigned int size = 16; map.resize(std::min(16u, map.size()));
}
MRUListMap::iterator index = map.begin();; static json::String cast_str(json::UnknownElement const& e) {
return static_cast<json::String>(e);
if (map.size() >= size) {
std::advance(index, size);
// Use a range incase the storage number shrinks.
map.erase(index, map.end());
}
} }
/// @brief Load MRU Lists. /// @brief Load MRU Lists.
/// @param key List name. /// @param key List name.
/// @param array json::Array of values. /// @param array json::Array of values.
void MRUManager::Load(const std::string &key, const json::Array& array) { void MRUManager::Load(const std::string &key, const json::Array& array) {
json::Array::const_iterator index(array.Begin()), indexEnd(array.End()); transform(array.Begin(), array.End(), back_inserter(mru[key]), cast_str);
Prune(mru[key]);
MRUListMap *map = new MRUListMap();
for (; index != indexEnd; ++index) {
const json::Object& obj = *index;
time_t time = (time_t)(json::Number)obj["time"];
std::string entry = (json::String)obj["entry"];
map->insert(make_pair(time, entry));
}
mru[key] = map;
Prune(*map);
} }
} }

View File

@ -20,6 +20,7 @@
#ifndef LAGI_PRE #ifndef LAGI_PRE
#include <fstream> #include <fstream>
#include <list>
#include <map> #include <map>
#endif #endif
@ -47,9 +48,7 @@ class MRUManager {
public: public:
/// @brief Map for time->value pairs. /// @brief Map for time->value pairs.
/// @param int Last time loaded typedef std::list<std::string> MRUListMap;
/// @param std::string File or value that was last loaded.
typedef std::multimap<time_t, std::string, std::greater_equal<time_t> > MRUListMap;
/// @brief Constructor /// @brief Constructor
/// @param config File to load MRU values from /// @param config File to load MRU values from
@ -79,7 +78,7 @@ public:
/// @param key List name /// @param key List name
/// @param entry 0-base position of entry /// @param entry 0-base position of entry
/// @exception MRUErrorInvalidKey thrown when an invalid key is used. /// @exception MRUErrorInvalidKey thrown when an invalid key is used.
const std::string GetEntry(const std::string &key, const int entry); std::string const& GetEntry(const std::string &key, size_t entry);
/// Write MRU lists to disk. /// Write MRU lists to disk.
void Flush(); void Flush();
@ -92,13 +91,14 @@ private:
/// @brief Map for MRUListMap values. /// @brief Map for MRUListMap values.
/// @param std::string Name /// @param std::string Name
/// @param MRUListMap instance. /// @param MRUListMap instance.
typedef std::map<std::string, MRUListMap*> MRUMap; typedef std::map<std::string, MRUListMap> MRUMap;
/// Internal MRUMap values. /// Internal MRUMap values.
MRUMap mru; MRUMap mru;
void Load(const std::string &key, const ::json::Array& array); void Load(const std::string &key, const ::json::Array& array);
inline void Prune(MRUListMap& map); inline void Prune(MRUListMap& map);
MRUListMap &Find(std::string const& key);
}; };
} // namespace agi } // namespace agi

View File

@ -1,14 +1,14 @@
#include "compat.h" #include "compat.h"
#include "main.h" #include "main.h"
#ifndef AGI_PRE
#include <algorithm>
#endif
wxArrayString lagi_MRU_wxAS(const wxString &list) { wxArrayString lagi_MRU_wxAS(const wxString &list) {
const agi::MRUManager::MRUListMap *map = config::mru->Get(STD_STR(list));
wxArrayString work; wxArrayString work;
work.reserve(map->size());
const agi::MRUManager::MRUListMap *map_list = config::mru->Get(STD_STR(list)); transform(map->begin(), map->end(), std::back_inserter(work), lagi_wxString);
for (agi::MRUManager::MRUListMap::const_iterator i_lst = map_list->begin(); i_lst != map_list->end(); ++i_lst) {
work.Add(wxString(i_lst->second.c_str(), wxConvUTF8));
}
return work; return work;
} }

View File

@ -648,7 +648,7 @@ void FrameMain::RebuildRecentList(const char *root_command, const char *mru_name
ss << "/"; ss << "/";
ss << i; ss << i;
wxFileName shortname(lagi_wxString(it->second)); wxFileName shortname(lagi_wxString(*it));
menu->Append(cmd::id(ss.str()), menu->Append(cmd::id(ss.str()),
wxString::Format("%s%d %s", i <= 9 ? "&" : "", i + 1, shortname.GetFullName())); wxString::Format("%s%d %s", i <= 9 ? "&" : "", i + 1, shortname.GetFullName()));

View File

@ -57,8 +57,8 @@ Menu::Menu() {
const json::Object::Member& member = *index; const json::Object::Member& member = *index;
const json::Array& array = member.element; const json::Array& array = member.element;
delete BuildMenu(member.name, array); delete BuildMenu(member.name, array);
} }
} }
Menu::~Menu() {} Menu::~Menu() {}
@ -69,11 +69,11 @@ wxMenu* Menu::GetMenu(std::string name) {
if ((index = map.find(name)) != map.end()) { if ((index = map.find(name)) != map.end()) {
return index->second; return index->second;
} }
LOG_E("menu/invalid") << "Invalid index name: " << name; LOG_E("menu/invalid") << "Invalid index name: " << name;
throw MenuInvalidName("Unknown index"); throw MenuInvalidName("Unknown index");
} }
@ -92,7 +92,7 @@ wxMenu* Menu::BuildMenu(std::string name, const json::Array& array, int submenu)
if (type == Menu::Spacer) { if (type == Menu::Spacer) {
menu->AppendSeparator(); menu->AppendSeparator();
continue; continue;
} }
const json::String& command = obj["command"]; const json::String& command = obj["command"];
@ -101,13 +101,13 @@ wxMenu* Menu::BuildMenu(std::string name, const json::Array& array, int submenu)
std::string cmd_name = type == Menu::Submenu ? name_submenu : command.Value(); std::string cmd_name = type == Menu::Submenu ? name_submenu : command.Value();
cmd::Command *cmd; cmd::Command *cmd;
try { try {
cmd = cmd::get(cmd_name); cmd = cmd::get(cmd_name);
} }
catch (CommandNotFound const&) { catch (CommandNotFound const&) {
LOG_W("menu/command/not_found") << "Command '" << cmd_name << "' not found; skipping"; LOG_W("menu/command/not_found") << "Command '" << cmd_name << "' not found; skipping";
continue; continue;
} }
wxString display = cmd->StrMenu() + "\t" + hotkey::get_hotkey_str_first("Default", cmd_name); wxString display = cmd->StrMenu() + "\t" + hotkey::get_hotkey_str_first("Default", cmd_name);
wxString descr = cmd->StrHelp(); wxString descr = cmd->StrHelp();
@ -116,17 +116,17 @@ wxMenu* Menu::BuildMenu(std::string name, const json::Array& array, int submenu)
case Menu::Option: { case Menu::Option: {
wxMenuItem *menu_item = new wxMenuItem(menu, cmd::id(command.Value()), display, descr, wxITEM_NORMAL); wxMenuItem *menu_item = new wxMenuItem(menu, cmd::id(command.Value()), display, descr, wxITEM_NORMAL);
menu->Append(menu_item); menu->Append(menu_item);
} }
break; break;
case Menu::Check: { case Menu::Check: {
menu->AppendCheckItem(cmd::id(command.Value()), display, descr); menu->AppendCheckItem(cmd::id(command.Value()), display, descr);
} }
break; break;
case Menu::Radio: { case Menu::Radio: {
menu->AppendRadioItem(cmd::id(command.Value()), display, descr); menu->AppendRadioItem(cmd::id(command.Value()), display, descr);
} }
break; break;
case Menu::Recent: { case Menu::Recent: {
@ -135,7 +135,7 @@ wxMenu* Menu::BuildMenu(std::string name, const json::Array& array, int submenu)
menu->Append(menu_item); menu->Append(menu_item);
map.insert(MTPair(command.Value(), menu_new)); map.insert(MTPair(command.Value(), menu_new));
} }
break; break;
case Menu::Submenu: { case Menu::Submenu: {
@ -150,10 +150,10 @@ wxMenu* Menu::BuildMenu(std::string name, const json::Array& array, int submenu)
menu->Append(menu_item); menu->Append(menu_item);
} else { } else {
main_menu->Append(menu_new, display); main_menu->Append(menu_new, display);
}
}
break;
} }
}
break;
}
} // for index } // for index

View File

@ -48,13 +48,7 @@ TEST_F(lagi_mru, MRUConstructFromString) {
TEST_F(lagi_mru, MRUConstructInvalid) { TEST_F(lagi_mru, MRUConstructInvalid) {
util::copy("data/mru_invalid.json", "data/mru_tmp"); util::copy("data/mru_invalid.json", "data/mru_tmp");
EXPECT_ANY_THROW(agi::MRUManager("data/mru_tmp", default_mru));
const std::string nonexistent("data/mru_tmp");
agi::MRUManager mru(nonexistent, default_mru);
// Make sure it didn't load from the file.
EXPECT_THROW(mru.Add("Invalid", "/path/to/file"), agi::MRUErrorInvalidKey);
EXPECT_NO_THROW(mru.Add("Valid_Int", "/path/to/file"));
} }
TEST_F(lagi_mru, MRUEntryAdd) { TEST_F(lagi_mru, MRUEntryAdd) {
@ -82,6 +76,25 @@ TEST_F(lagi_mru, MRUKeyValid) {
EXPECT_NO_THROW(mru.Add("Valid", "/path/to/file")); EXPECT_NO_THROW(mru.Add("Valid", "/path/to/file"));
} }
TEST_F(lagi_mru, MRUAddSeveral) {
util::copy("data/mru_ok.json", "data/mru_tmp");
agi::MRUManager mru("data/mru_tmp", default_mru);
EXPECT_NO_THROW(mru.Add("Valid", "/file/1"));
EXPECT_NO_THROW(mru.Add("Valid", "/file/2"));
EXPECT_NO_THROW(mru.Add("Valid", "/file/3"));
EXPECT_NO_THROW(mru.Add("Valid", "/file/1"));
EXPECT_NO_THROW(mru.Add("Valid", "/file/1"));
EXPECT_NO_THROW(mru.Add("Valid", "/file/3"));
agi::MRUManager::MRUListMap::const_iterator entry = mru.Get("Valid")->begin();
EXPECT_STREQ("/file/3", (*entry++).c_str());
EXPECT_STREQ("/file/1", (*entry++).c_str());
EXPECT_STREQ("/file/2", (*entry++).c_str());
EXPECT_EQ(mru.Get("Valid")->end(), entry);
}
// Check to make sure an entry is really removed. This was fixed in // Check to make sure an entry is really removed. This was fixed in
// r4347, the entry was being removed from a copy of the map internally. // r4347, the entry was being removed from a copy of the map internally.
@ -94,6 +107,6 @@ TEST_F(lagi_mru, MRUEntryRemove_r4347) {
const agi::MRUManager::MRUListMap *map_list = mru.Get("Valid"); const agi::MRUManager::MRUListMap *map_list = mru.Get("Valid");
agi::MRUManager::MRUListMap::const_iterator i_lst = map_list->begin(); agi::MRUManager::MRUListMap::const_iterator i_lst = map_list->begin();
if ((i_lst != map_list->end()) && (i_lst->second == "/path/to/file")) if ((i_lst != map_list->end()) && (*i_lst == "/path/to/file"))
FAIL() << "r4347 regression, Entry exists after remove"; FAIL() << "r4347 regression, Entry exists after remove";
} }