diff --git a/android/jni/Android.mk b/android/jni/Android.mk index aeef9b2..71ec99d 100644 --- a/android/jni/Android.mk +++ b/android/jni/Android.mk @@ -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) diff --git a/android/jni/and_storage.cpp b/android/jni/and_storage.cpp index 483e7ad..1ed4506 100644 --- a/android/jni/and_storage.cpp +++ b/android/jni/and_storage.cpp @@ -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 m_result; }; diff --git a/iOS/offlineguides.xcodeproj/project.pbxproj b/iOS/offlineguides.xcodeproj/project.pbxproj index e2efd03..eada405 100644 --- a/iOS/offlineguides.xcodeproj/project.pbxproj +++ b/iOS/offlineguides.xcodeproj/project.pbxproj @@ -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 = ""; }; - A3E221F017AFB4B90018AB9E /* index_storage.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = index_storage.cpp; path = ../../storage/index_storage.cpp; sourceTree = ""; }; + 7722E14717B01B0700CFB817 /* article_info.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = article_info.cpp; path = ../../storage/article_info.cpp; sourceTree = ""; }; A3E221F217AFB4C20018AB9E /* storage.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = storage.cpp; path = ../../storage/storage.cpp; sourceTree = ""; }; A3E2220B17AFD5840018AB9E /* data */ = {isa = PBXFileReference; lastKnownFileType = folder; name = data; path = ../../data; sourceTree = ""; }; A3E2220E17AFD9E10018AB9E /* assert.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = assert.cpp; path = ../../env/assert.cpp; sourceTree = ""; }; @@ -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 = ""; @@ -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; }; diff --git a/iOS/offlineguides/ArticleVC.mm b/iOS/offlineguides/ArticleVC.mm index b126a8f..6fa8a08 100644 --- a/iOS/offlineguides/ArticleVC.mm +++ b/iOS/offlineguides/ArticleVC.mm @@ -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 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]; } diff --git a/storage/article_info.cpp b/storage/article_info.cpp new file mode 100644 index 0000000..7c94f54 --- /dev/null +++ b/storage/article_info.cpp @@ -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); +} diff --git a/storage/article_info.hpp b/storage/article_info.hpp index d568328..7e63a39 100644 --- a/storage/article_info.hpp +++ b/storage/article_info.hpp @@ -1,31 +1,86 @@ #pragma once #include "storage_common.hpp" -#include -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; } diff --git a/storage/article_info_storage.cpp b/storage/article_info_storage.cpp deleted file mode 100644 index 885979d..0000000 --- a/storage/article_info_storage.cpp +++ /dev/null @@ -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; -} diff --git a/storage/article_info_storage.hpp b/storage/article_info_storage.hpp deleted file mode 100644 index ee6f0d6..0000000 --- a/storage/article_info_storage.hpp +++ /dev/null @@ -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); -}; diff --git a/storage/index_storage.cpp b/storage/index_storage.cpp deleted file mode 100644 index 0e3fd93..0000000 --- a/storage/index_storage.cpp +++ /dev/null @@ -1,15 +0,0 @@ -#include "index_storage.hpp" - -void IndexStorageMock::QueryArticleInfos(vector & 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); -} diff --git a/storage/index_storage.hpp b/storage/index_storage.hpp deleted file mode 100644 index dcea3cb..0000000 --- a/storage/index_storage.hpp +++ /dev/null @@ -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 & out, string const & prefix, - double lat = INVALID_LAT, double lon = INVALID_LON) const = 0; -}; - -class IndexStorageMock : public IndexStorage -{ -public: - virtual void QueryArticleInfos(vector & out, string const & prefix, - double lat = INVALID_LAT, double lon = INVALID_LON) const; -}; diff --git a/storage/storage.cpp b/storage/storage.cpp index 7ecfd6d..5013d0a 100644 --- a/storage/storage.cpp +++ b/storage/storage.cpp @@ -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 & out, string const & prefix, double lat, double lon) const { - vector 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::const_iterator IterT; + pair 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()); +} diff --git a/storage/storage.hpp b/storage/storage.hpp index cc598b7..0bebc05 100644 --- a/storage/storage.hpp +++ b/storage/storage.hpp @@ -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 & out, string const & prefix, double lat = INVALID_LAT, double lon = INVALID_LON) const; -private: - scoped_ptr m_articleInfoStorage; - scoped_ptr m_indexStorage; +protected: + vector m_info; +}; + + +class StorageMock : public Storage +{ +public: + StorageMock(); }; diff --git a/storage/storage.pro b/storage/storage.pro index ccd3397..2f2cbe5 100644 --- a/storage/storage.pro +++ b/storage/storage.pro @@ -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 += \ diff --git a/storage/tests/storage_test.cpp b/storage/tests/storage_test.cpp index f7e1303..1ea0f94 100644 --- a/storage/tests/storage_test.cpp +++ b/storage/tests/storage_test.cpp @@ -1,33 +1,89 @@ #include #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 artInfos; - storage.QueryArticleInfos(artInfos, ""); +void CheckBounds(vector 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(&expected[0], &expected[0] + 3), artInfos); } -TEST(Storage, PrefixQuery) +TEST(Storage, PrefixQuery1) { - Storage storage(new ArticleInfoStorageMock(), new IndexStorageMock()); + StorageTest storage; - vector 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 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 out; + storage.QueryArticleInfos(out, "l"); + EXPECT_EQ(out.size(), 2); + CheckBounds(out, "Lancaster", "London"); }