Storage refactoring.

This commit is contained in:
vng 2013-08-05 20:59:12 +03:00
parent 36f3d63540
commit 37494c342d
14 changed files with 237 additions and 163 deletions

View file

@ -17,7 +17,6 @@ LOCAL_SRC_FILES := \
# Storage files
LOCAL_SRC_FILES += \
../../storage/storage.cpp \
../../storage/article_info_storage.cpp \
../../storage/index_storage.cpp \
../../storage/article_info.cpp \
include $(BUILD_SHARED_LIBRARY)

View file

@ -2,14 +2,11 @@
#include "jni_util.hpp"
#include "../../storage/storage.hpp"
#include "../../storage/article_info_storage.hpp"
#include "../../storage/index_storage.hpp"
class AndStorage
{
public:
AndStorage()
: m_storage(new ArticleInfoStorageMock(), new IndexStorageMock()) {}
static AndStorage & Instance()
{
@ -36,7 +33,7 @@ public:
}
private:
Storage m_storage;
StorageMock m_storage;
vector<ArticleInfo> m_result;
};

View file

@ -7,8 +7,7 @@
objects = {
/* Begin PBXBuildFile section */
A3E221EF17AFB4AE0018AB9E /* article_info_storage.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A3E221EE17AFB4AE0018AB9E /* article_info_storage.cpp */; };
A3E221F117AFB4B90018AB9E /* index_storage.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A3E221F017AFB4B90018AB9E /* index_storage.cpp */; };
7722E14817B01B0700CFB817 /* article_info.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7722E14717B01B0700CFB817 /* article_info.cpp */; };
A3E221F317AFB4C20018AB9E /* storage.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A3E221F217AFB4C20018AB9E /* storage.cpp */; };
A3E2220C17AFD5840018AB9E /* data in Resources */ = {isa = PBXBuildFile; fileRef = A3E2220B17AFD5840018AB9E /* data */; };
A3E2220F17AFD9E10018AB9E /* assert.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A3E2220E17AFD9E10018AB9E /* assert.cpp */; };
@ -28,8 +27,7 @@
/* End PBXBuildFile section */
/* Begin PBXFileReference section */
A3E221EE17AFB4AE0018AB9E /* article_info_storage.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = article_info_storage.cpp; path = ../../storage/article_info_storage.cpp; sourceTree = "<group>"; };
A3E221F017AFB4B90018AB9E /* index_storage.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = index_storage.cpp; path = ../../storage/index_storage.cpp; sourceTree = "<group>"; };
7722E14717B01B0700CFB817 /* article_info.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = article_info.cpp; path = ../../storage/article_info.cpp; sourceTree = "<group>"; };
A3E221F217AFB4C20018AB9E /* storage.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = storage.cpp; path = ../../storage/storage.cpp; sourceTree = "<group>"; };
A3E2220B17AFD5840018AB9E /* data */ = {isa = PBXFileReference; lastKnownFileType = folder; name = data; path = ../../data; sourceTree = "<group>"; };
A3E2220E17AFD9E10018AB9E /* assert.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = assert.cpp; path = ../../env/assert.cpp; sourceTree = "<group>"; };
@ -71,9 +69,8 @@
A3E221ED17AFB49B0018AB9E /* storage */ = {
isa = PBXGroup;
children = (
7722E14717B01B0700CFB817 /* article_info.cpp */,
A3E221F217AFB4C20018AB9E /* storage.cpp */,
A3E221F017AFB4B90018AB9E /* index_storage.cpp */,
A3E221EE17AFB4AE0018AB9E /* article_info_storage.cpp */,
);
name = storage;
sourceTree = "<group>";
@ -217,11 +214,10 @@
ED81955717AED762004F3803 /* AppDelegate.mm in Sources */,
ED81956517AEE129004F3803 /* ArticleVC.mm in Sources */,
ED81956A17AEF6B6004F3803 /* GuideVC.mm in Sources */,
A3E221EF17AFB4AE0018AB9E /* article_info_storage.cpp in Sources */,
A3E221F117AFB4B90018AB9E /* index_storage.cpp in Sources */,
A3E221F317AFB4C20018AB9E /* storage.cpp in Sources */,
A3E2220F17AFD9E10018AB9E /* assert.cpp in Sources */,
A3E2221117AFDA0A0018AB9E /* source_address.cpp in Sources */,
7722E14817B01B0700CFB817 /* article_info.cpp in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};

View file

@ -2,17 +2,18 @@
#import "GuideVC.h"
#import "../../storage/storage.hpp"
#import "../../storage/article_info_storage.hpp"
#import "../../storage/article_info.hpp"
#import "../../storage/index_storage.hpp"
#import "../../std/vector.hpp"
#import "../../env/assert.hpp"
#import "../../std/vector.hpp"
#define THUMBNAILSFOLDER @"/data/thumbnails/"
@interface ArticleVC ()
{
Storage * m_storage;
StorageMock m_storage;
vector<ArticleInfo> m_infos;
}
@ -30,7 +31,6 @@
self.searchBar.delegate = self;
self.tableView.tableHeaderView = self.searchBar;
self.searchBar.text = @"";
m_storage = new Storage(new ArticleInfoStorageMock(), new IndexStorageMock());
[self searchBar:self.searchBar textDidChange:@""];
}
return self;
@ -70,7 +70,7 @@
string imageType = info->m_thumbnailUrl.substr(pos+1);
NSString * imagePath = [[NSBundle mainBundle] pathForResource:[NSString stringWithUTF8String:imageName.c_str()] ofType:[NSString stringWithUTF8String:imageType.c_str()] inDirectory:THUMBNAILSFOLDER];
cell.detailTextLabel.text = [NSString stringWithUTF8String:info->m_parentPath.c_str()];
cell.detailTextLabel.text = [NSString stringWithUTF8String:info->m_parentUrl.c_str()];
UIImage * image = [UIImage imageWithContentsOfFile:imagePath];
cell.imageView.image = image;
@ -106,7 +106,7 @@
- (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText
{
//@todo add lat and lon to QueryInfos
m_storage->QueryArticleInfos(m_infos, [searchText UTF8String]);
m_storage.QueryArticleInfos(m_infos, [searchText UTF8String]);
[self.tableView reloadData];
}

28
storage/article_info.cpp Normal file
View file

@ -0,0 +1,28 @@
#include "article_info.hpp"
#include "../std/iterator.hpp"
#include "../std/algorithm.hpp"
void ArticleInfo::GenerateKey()
{
m_key.reserve(m_title.size());
transform(m_title.begin(), m_title.end(), back_inserter(m_key), &::tolower);
}
double ArticleInfo::Score(double currLat, double currLon) const
{
/// @todo
return 0.0;
}
void ArticleInfo::Swap(ArticleInfo & i)
{
m_key.swap(i.m_key);
m_url.swap(i.m_url);
m_title.swap(i.m_title);
m_thumbnailUrl.swap(i.m_thumbnailUrl);
m_parentUrl.swap(i.m_parentUrl);
std::swap(m_lat, i.m_lat);
std::swap(m_lon, i.m_lon);
}

View file

@ -1,31 +1,86 @@
#pragma once
#include "storage_common.hpp"
#include <iostream>
struct ArticleInfo
#include "../std/algorithm.hpp"
class ArticleInfo
{
void GenerateKey();
string m_key;
public:
ArticleInfo() {}
ArticleInfo(string const & url, string const & title, string const & thumbnailUrl, string const & parentPath)
: m_url(url), m_title(title), m_thumbnailUrl(thumbnailUrl), m_parentPath(parentPath) {}
ArticleInfo(string const & title) : m_title(title)
{
GenerateKey();
}
string m_url;
string m_title;
string m_thumbnailUrl;
string m_parentPath;
string m_parentUrl;
double m_lat, m_lon;
double Score(double currLat, double currLon) const;
void Swap(ArticleInfo & i);
class LessScore
{
double m_lat, m_lon;
public:
LessScore(double lat, double lon) : m_lat(lat), m_lon(lon) {}
bool operator() (ArticleInfo const & i1, ArticleInfo const & i2) const
{
return i1.Score(m_lat, m_lon) < i2.Score(m_lat, m_lon);
}
};
struct LessStorage
{
bool operator() (ArticleInfo const & i1, ArticleInfo const & i2) const
{
return (i1.m_key < i2.m_key);
}
};
class LessPrefix
{
int Compare(ArticleInfo const & info, string const & prefix) const
{
size_t const count = min(info.m_key.size(), prefix.size());
for (size_t i = 0; i < count; ++i)
{
if (info.m_key[i] < prefix[i])
return -1;
else if (info.m_key[i] > prefix[i])
return 1;
}
return (info.m_key.size() < prefix.size() ? -1 : 0);
}
public:
bool operator() (ArticleInfo const & info, string const & prefix) const
{
return (Compare(info, prefix) == -1);
}
bool operator() (string const & prefix, ArticleInfo const & info) const
{
return (Compare(info, prefix) == 1);
}
};
};
inline bool operator == (ArticleInfo const & a1, ArticleInfo const & a2)
inline void swap(ArticleInfo & a1, ArticleInfo & a2)
{
return a1.m_url == a2.m_url && a1.m_title == a2.m_title &&
a1.m_thumbnailUrl == a2.m_thumbnailUrl && a1.m_parentPath == a2.m_parentPath;
a1.Swap(a2);
}
// It's important that PrintTo() is defined in the SAME
// namespace that defines Bar. C++'s look-up rules rely on that.
inline void PrintTo(ArticleInfo const & artInfo, ::std::ostream* os) {
*os << "ArticleInfi {" << artInfo.m_url << ", "
<< artInfo.m_title << ", "
<< artInfo.m_thumbnailUrl << ", "
<< artInfo.m_parentPath << "}";
inline string ToString(ArticleInfo const & i)
{
return i.m_title;
}

View file

@ -1,30 +0,0 @@
#include "article_info_storage.hpp"
bool ArticleInfoStorageMock::GetArticleInfoById(ArticleInfo & out, ArticleInfoId const & id)
{
if (id > 2)
return false;
switch (id)
{
case 0:
out.m_thumbnailUrl = "london.jpg";
out.m_url = "London.html";
out.m_title = "London";
out.m_parentPath = "Europe -> Great Britain";
return true;
case 1:
out.m_thumbnailUrl = "lancaster.jpg";
out.m_url = "Lancaster.html";
out.m_title = "Lancaster";
out.m_parentPath = "Europe -> Great Britain";
return true;
case 2:
out.m_thumbnailUrl = "great_britain.jpg";
out.m_url = "GreatBritain.html";
out.m_title = "Great Britain";
out.m_parentPath = "Europe";
return true;
}
return false;
}

View file

@ -1,18 +0,0 @@
#pragma once
#include "../std/string.hpp"
#include "article_info.hpp"
#include "storage_common.hpp"
class ArticleInfoStorage
{
public:
virtual ~ArticleInfoStorage() {}
virtual bool GetArticleInfoById(ArticleInfo & out, ArticleInfoId const & id) = 0;
};
class ArticleInfoStorageMock: public ArticleInfoStorage
{
public:
virtual bool GetArticleInfoById(ArticleInfo & out, ArticleInfoId const & id);
};

View file

@ -1,15 +0,0 @@
#include "index_storage.hpp"
void IndexStorageMock::QueryArticleInfos(vector<ArticleInfoId> & out, string const & prefix,
double /*lat*/, double /*lon*/) const
{
out.clear();
if (prefix.empty())
for (uint32_t i = 0; i < 3; ++i)
out.push_back(i);
else if (prefix == "L")
for (uint32_t i = 0; i < 2; ++i)
out.push_back(i);
else if (prefix.size() <= 6 && string("London").substr(0, prefix.size()) == prefix)
out.push_back(0);
}

View file

@ -1,21 +0,0 @@
#pragma once
#include "article_info.hpp"
#include "../std/vector.hpp"
#include "../std/string.hpp"
class IndexStorage
{
public:
virtual ~IndexStorage() {}
virtual void QueryArticleInfos(vector<ArticleInfoId> & out, string const & prefix,
double lat = INVALID_LAT, double lon = INVALID_LON) const = 0;
};
class IndexStorageMock : public IndexStorage
{
public:
virtual void QueryArticleInfos(vector<ArticleInfoId> & out, string const & prefix,
double lat = INVALID_LAT, double lon = INVALID_LON) const;
};

View file

@ -1,21 +1,42 @@
#include "storage.hpp"
#include "../std/vector.hpp"
#include "../std/algorithm.hpp"
#include "../std/utility.hpp"
Storage::Storage(ArticleInfoStorage * artInfoStorage, IndexStorage * indexStorage)
{
m_articleInfoStorage.reset(artInfoStorage);
m_indexStorage.reset(indexStorage);
}
void Storage::QueryArticleInfos(vector<ArticleInfo> & out, string const & prefix,
double lat, double lon) const
{
vector<ArticleInfoId> ids;
m_indexStorage->QueryArticleInfos(ids, prefix, lat, lon);
out.clear();
out.resize(ids.size());
for (size_t i = 0; i < ids.size(); ++i)
m_articleInfoStorage->GetArticleInfoById(out[i], ids[i]);
typedef vector<ArticleInfo>::const_iterator IterT;
pair<IterT, IterT> const range = equal_range(m_info.begin(), m_info.end(),
prefix, ArticleInfo::LessPrefix());
out.assign(range.first, range.second);
sort(out.begin(), out.end(), ArticleInfo::LessScore(lat, lon));
}
StorageMock::StorageMock()
{
ArticleInfo i1("London");
i1.m_url = "London";
i1.m_thumbnailUrl = "london.jpg";
i1.m_parentUrl = "Europe -> Great Britain";
m_info.push_back(i1);
ArticleInfo i2("Lancaster");
i2.m_url = "Lancaster";
i2.m_thumbnailUrl = "lancaster.jpg";
i2.m_parentUrl = "Europe -> Great Britain";
m_info.push_back(i2);
ArticleInfo i3("Great Britain");
i3.m_url = "Great_Britain";
i3.m_thumbnailUrl = "great_britain.jpg";
i3.m_parentUrl = "Europe";
m_info.push_back(i3);
sort(m_info.begin(), m_info.end(), ArticleInfo::LessStorage());
}

View file

@ -1,21 +1,25 @@
# pragma once
#pragma once
#include "article_info_storage.hpp"
#include "index_storage.hpp"
#include "article_info.hpp"
#include "../std/scoped_ptr.hpp"
#include "../std/vector.hpp"
class Storage
{
public:
/// @note takes ownership of @link(ArticleInfoStorage) and @link(IndexStorage)
Storage(ArticleInfoStorage * artInfoStorage, IndexStorage * indexStorage);
void Load(string const & path);
void QueryArticleInfos(vector<ArticleInfo> & out, string const & prefix,
double lat = INVALID_LAT, double lon = INVALID_LON) const;
private:
scoped_ptr<ArticleInfoStorage> m_articleInfoStorage;
scoped_ptr<IndexStorage> m_indexStorage;
protected:
vector<ArticleInfo> m_info;
};
class StorageMock : public Storage
{
public:
StorageMock();
};

View file

@ -7,15 +7,17 @@ INCLUDEPATH += ../3rdparty/boost ../3rdparty/googletest/include
HEADERS += \
storage.hpp \
article_info_storage.hpp \
article_info.hpp \
index_storage.hpp \
storage_common.hpp \
SOURCES += \
article_info_storage.cpp \
index_storage.cpp \
storage.cpp \
article_info.cpp \
# env sources
SOURCES += \
../env/logging.cpp \
../env/source_address.cpp \
# unit tests
SOURCES += \

View file

@ -1,33 +1,89 @@
#include <gtest/gtest.h>
#include "storage.hpp"
#include "article_info_storage.hpp"
#include "index_storage.hpp"
TEST(Storage, Smoke)
#include "../env/message_std.hpp"
#include "../env/logging.hpp"
#include "../std/array.hpp"
namespace
{
Storage storage(new ArticleInfoStorageMock(), new IndexStorageMock());
vector<ArticleInfo> artInfos;
storage.QueryArticleInfos(artInfos, "");
void CheckBounds(vector<ArticleInfo> const & v, string const & beg, string const & end)
{
EXPECT_FALSE(v.empty());
EXPECT_EQ(v.front().m_title, beg);
EXPECT_EQ(v.back().m_title, end);
}
ArticleInfo expected[] =
class StorageTest : public Storage
{
public:
void FillStorage(char const * arr[], size_t size)
{
ArticleInfo("London", "London", "london.jpg", "Europe -> Great Britain"),
ArticleInfo("Lancaster", "Lancaster", "lancaster.jpg", "Europe -> Great Britain"),
ArticleInfo("Great_Britain", "Great Britain", "great_britain.jpg", "Europe"),
};
m_info.clear();
for (size_t i = 0; i < size; ++i)
m_info.push_back(ArticleInfo(arr[i]));
sort(m_info.begin(), m_info.end(), ArticleInfo::LessStorage());
}
void CheckBounds(string const & beg, string const & end) const
{
::CheckBounds(m_info, beg, end);
}
};
EXPECT_EQ(vector<ArticleInfo>(&expected[0], &expected[0] + 3), artInfos);
}
TEST(Storage, PrefixQuery)
TEST(Storage, PrefixQuery1)
{
Storage storage(new ArticleInfoStorageMock(), new IndexStorageMock());
StorageTest storage;
vector<ArticleInfo> artInfos;
storage.QueryArticleInfos(artInfos, "Lo");
char const * arrTitle[] = { "London", "Lomdon", "Z", "a", "london", "las vegas", "los angeles" };
size_t const count = ArraySize(arrTitle);
EXPECT_EQ(artInfos.size(), 1);
EXPECT_EQ(artInfos[0], ArticleInfo("London", "London", "london.jpg", "Europe -> Great Britain"));
storage.FillStorage(arrTitle, count);
storage.CheckBounds("a", "Z");
vector<ArticleInfo> out;
storage.QueryArticleInfos(out, "");
EXPECT_EQ(out.size(), count);
storage.CheckBounds("a", "Z");
storage.QueryArticleInfos(out, "l");
EXPECT_EQ(out.size(), 5);
CheckBounds(out, "las vegas", "los angeles");
storage.QueryArticleInfos(out, "lo");
EXPECT_EQ(out.size(), 4);
CheckBounds(out, "Lomdon", "los angeles");
storage.QueryArticleInfos(out, "lon");
EXPECT_EQ(out.size(), 2);
CheckBounds(out, "London", "london");
storage.QueryArticleInfos(out, "lx");
EXPECT_EQ(out.size(), 0);
storage.QueryArticleInfos(out, "lor");
EXPECT_EQ(out.size(), 0);
}
TEST(Storage, PrefixQuery2)
{
StorageTest storage;
char const * arrTitle[] = { "London", "Lancaster", "Great Britan" };
size_t const count = ArraySize(arrTitle);
storage.FillStorage(arrTitle, count);
vector<ArticleInfo> out;
storage.QueryArticleInfos(out, "l");
EXPECT_EQ(out.size(), 2);
CheckBounds(out, "Lancaster", "London");
}