diff --git a/coding/CMakeLists.txt b/coding/CMakeLists.txt index 4bf68cbf1f..3020c77966 100644 --- a/coding/CMakeLists.txt +++ b/coding/CMakeLists.txt @@ -24,8 +24,8 @@ set( compressed_bit_vector.cpp compressed_bit_vector.hpp constants.hpp - csv_file_reader.cpp - csv_file_reader.hpp + csv_reader.cpp + csv_reader.hpp dd_vector.hpp diff.hpp diff_patch_common.hpp diff --git a/coding/coding.pro b/coding/coding.pro index 3efe6e381b..12eca71250 100644 --- a/coding/coding.pro +++ b/coding/coding.pro @@ -13,7 +13,7 @@ include($$ROOT_DIR/common.pri) SOURCES += \ base64.cpp \ compressed_bit_vector.cpp \ - csv_file_reader.cpp \ + csv_reader.cpp \ file_container.cpp \ file_name_utils.cpp \ file_reader.cpp \ @@ -47,7 +47,7 @@ HEADERS += \ coder_util.hpp \ compressed_bit_vector.hpp \ constants.hpp \ - csv_file_reader.hpp \ + csv_reader.hpp \ dd_vector.hpp \ diff.hpp \ diff_patch_common.hpp \ diff --git a/coding/coding_tests/csv_reader_test.cpp b/coding/coding_tests/csv_reader_test.cpp index 92a92ffd18..bcdce88b77 100644 --- a/coding/coding_tests/csv_reader_test.cpp +++ b/coding/coding_tests/csv_reader_test.cpp @@ -1,6 +1,7 @@ #include "testing/testing.hpp" -#include "coding/csv_file_reader.hpp" +#include "coding/csv_reader.hpp" +#include "coding/file_reader.hpp" #include "platform/platform_tests_support/scoped_file.hpp" @@ -22,10 +23,9 @@ UNIT_TEST(CSVReaderSmoke) { auto const fileName = "test.csv"; platform::tests_support::ScopedFile sf(fileName, kCSV1); - auto const & filePath = sf.GetFullPath(); - + FileReader fileReader(sf.GetFullPath()); CSVReader reader; - reader.ReadFullFile(filePath, [](File const & file) { + reader.Read(fileReader, [](File const & file) { TEST_EQUAL(file.size(), 1, ()); TEST_EQUAL(file[0].size(), 3, ()); Row const firstRow = {"e", "f", "g h"}; @@ -33,41 +33,40 @@ UNIT_TEST(CSVReaderSmoke) }); CSVReader::Params p; - p.m_shouldReadHeader = true; - reader.ReadFullFile(filePath, - [](File const & file) { - TEST_EQUAL(file.size(), 2, ()); - Row const headerRow = {"a", "b", "c", "d"}; - TEST_EQUAL(file[0], headerRow, ()); - }, - p); + p.m_readHeader = true; + reader.Read(fileReader, + [](File const & file) { + TEST_EQUAL(file.size(), 2, ()); + Row const headerRow = {"a", "b", "c", "d"}; + TEST_EQUAL(file[0], headerRow, ()); + }, + p); } UNIT_TEST(CSVReaderCustomDelimiter) { auto const fileName = "test.csv"; platform::tests_support::ScopedFile sf(fileName, kCSV2); - auto const & filePath = sf.GetFullPath(); - + FileReader fileReader(sf.GetFullPath()); CSVReader reader; CSVReader::Params p; - p.m_shouldReadHeader = true; + p.m_readHeader = true; p.m_delimiter = ' '; - reader.ReadLineByLine(filePath, - [](Row const & row) { - Row const firstRow = {"a,b,cd", "a", "b,", "c"}; - TEST_EQUAL(row, firstRow, ()); - }, - p); + reader.Read(fileReader, + [](Row const & row) { + Row const firstRow = {"a,b,cd", "a", "b,", "c"}; + TEST_EQUAL(row, firstRow, ()); + }, + p); } UNIT_TEST(CSVReaderEmptyFile) { auto const fileName = "test.csv"; platform::tests_support::ScopedFile sf(fileName, kCSV2); - auto const & filePath = sf.GetFullPath(); + FileReader fileReader(sf.GetFullPath()); CSVReader reader; - reader.ReadFullFile(filePath, [](File const & file) { TEST_EQUAL(file.size(), 0, ()); }); + reader.Read(fileReader, [](File const & file) { TEST_EQUAL(file.size(), 0, ()); }); } diff --git a/coding/csv_file_reader.cpp b/coding/csv_file_reader.cpp deleted file mode 100644 index d5984d424b..0000000000 --- a/coding/csv_file_reader.cpp +++ /dev/null @@ -1,45 +0,0 @@ -#include "coding/csv_file_reader.hpp" - -#include "base/logging.hpp" -#include "base/string_utils.hpp" - -#include -#include - -namespace coding -{ -using namespace std; - -void CSVReader::ReadLineByLine(string const & filePath, LineByLineCallback const & fn, - Params const & params) const -{ - ifstream file(filePath); - if (!file) - { - LOG(LERROR, ("File not found at path: ", filePath)); - return; - } - - string line; - bool readFirstLine = params.m_shouldReadHeader; - while (getline(file, line)) - { - vector splitLine; - strings::ParseCSVRow(line, params.m_delimiter, splitLine); - if (!readFirstLine) - { - readFirstLine = true; - continue; - } - fn(splitLine); - } -} - -void CSVReader::ReadFullFile(string const & filePath, FullFileCallback const & fn, - Params const & params) const -{ - vector> file; - ReadLineByLine(filePath, [&file](vector const & row) { file.emplace_back(row); }, params); - fn(file); -} -} // namespace coding diff --git a/coding/csv_file_reader.hpp b/coding/csv_file_reader.hpp deleted file mode 100644 index c6c80cd8e7..0000000000 --- a/coding/csv_file_reader.hpp +++ /dev/null @@ -1,30 +0,0 @@ -#pragma once - -#include -#include -#include - -namespace coding -{ -class CSVReader -{ -public: - struct Params - { - Params(){}; - bool m_shouldReadHeader = false; - char m_delimiter = ','; - }; - - CSVReader() = default; - - using LineByLineCallback = std::function const & line)>; - using FullFileCallback = std::function> const & file)>; - - void ReadLineByLine(std::string const & filePath, LineByLineCallback const & fn, - Params const & params = {}) const; - - void ReadFullFile(std::string const & filePath, FullFileCallback const & fn, - Params const & params = {}) const; -}; -} // namespace coding diff --git a/coding/csv_reader.cpp b/coding/csv_reader.cpp new file mode 100644 index 0000000000..a03279919f --- /dev/null +++ b/coding/csv_reader.cpp @@ -0,0 +1,39 @@ +#include "coding/csv_reader.hpp" + +#include "base/logging.hpp" +#include "base/string_utils.hpp" + +namespace coding +{ +using namespace std; + +void CSVReader::Read(istringstream & stream, LineByLineCallback const & fn, + Params const & params) const +{ + bool readFirstLine = params.m_readHeader; + + for (string line; getline(stream, line);) + { + Line splitLine; + strings::ParseCSVRow(line, params.m_delimiter, splitLine); + if (!readFirstLine) + { + readFirstLine = true; + continue; + } + fn(move(splitLine)); + } +} + +void CSVReader::Read(istringstream & stream, FullFileCallback const & fn, + Params const & params) const +{ + File file; + Read(stream, [&file](Line && line) + { + file.emplace_back(move(line)); + }, params); + + fn(move(file)); +} +} // namespace coding diff --git a/coding/csv_reader.hpp b/coding/csv_reader.hpp new file mode 100644 index 0000000000..86e16ea64e --- /dev/null +++ b/coding/csv_reader.hpp @@ -0,0 +1,45 @@ +#pragma once + +#include "coding/reader.hpp" + +#include +#include +#include +#include + +namespace coding +{ +class CSVReader +{ +public: + struct Params + { + Params() {} + bool m_readHeader = false; + char m_delimiter = ','; + }; + + CSVReader() = default; + + using Line = std::vector; + using File = std::vector; + using LineByLineCallback = std::function; + using FullFileCallback = std::function; + + void Read(std::istringstream & stream, LineByLineCallback const & fn, + Params const & params = {}) const; + + void Read(std::istringstream & stream, FullFileCallback const & fn, + Params const & params = {}) const; + + template + void Read(Reader const & reader, Callback const & fn, Params const & params = {}) const + { + std::string str; + str.resize(reader.Size()); + reader.Read(0, &str[0], reader.Size()); + std::istringstream stream(str); + Read(stream, fn, params); + } +}; +} // namespace coding diff --git a/data/ugc_types.csv b/data/ugc_types.csv index d22713fec4..926669fc1b 100644 --- a/data/ugc_types.csv +++ b/data/ugc_types.csv @@ -1,78 +1,178 @@ -OSM tag,Rating,Reviews,Details -aeroway aerodrome,1,1,1 -amenity atm,1,1,1 -amenity bank,1,1,0 -amenity bar,1,1,1 -amenity bicycle_rental,1,1,0 -amenity biergarten,1,1,1 -amenity cafe,1,1,1 -amenity cinema,1,1,0 -amenity clinic,1,1,0 -amenity doctors,1,1,0 -amenity drinking_water,1,0,0 -amenity driving_school,1,1,0 -amenity fast_food,1,1,1 -amenity fountain,1,0,0 -amenity fuel,1,1,0 -amenity hospital,1,1,0 -amenity ice_cream,1,1,1 -amenity internet_cafe,1,1,0 -amenity kindergarten,1,0,0 -amenity library,1,1,0 -amenity marketplace,1,1,1 -amenity motorcycle_parking,1,1,1 -amenity nightclub,1,1,1 -amenity nursing_home,1,0,0 -amenity parking,1,0,0 -amenity payment_terminal,1,1,0 -amenity pharmacy,1,1,1 -amenity place_of_worship,1,0,0 -amenity police,1,0,0 -amenity post_office,1,1,0 -amenity pub,1,1,1 -amenity restaurant,1,1,1 -amenity school,1,0,0 -amenity shower,1,0,0 -amenity theatre,1,1,0 -amenity toilets,1,0,0 -amenity university,1,0,0 -amenity water_point,1,0,0 -building train_station,1,0,0 -historic archaeological_site,1,0,0 -historic castle,1,0,0 -historic memorial,1,0,0 -historic monument,1,0,0 -historic ruins,1,0,0 -internet_access wlan,1,0,0 -landuse cemetery,1,0,0 -landuse retail,1,1,1 -leisure fitness_centre,1,1,1 -leisure fitness_station,1,1,0 -leisure garden,1,0,0 -leisure golf_course,1,0,0 -leisure nature_reserve,1,0,0 -leisure park,1,0,0 -leisure pitch,1,0,0 -leisure playground,1,0,0 -leisure sports_centre,1,1,0 -leisure stadium,1,0,0 -leisure swimming_pool,1,1,0 -natural beach,1,0,0 -natural cave_entrance,1,0,0 -office,1,0,0 -place city,1,1,0 -place hamlet,1,1,0 -place town,1,1,0 -place village,1,1,0 -shop,1,1,1 -tourism artwork,1,0,0 -tourism attraction,1,0,0 -tourism guest_house,1,1,1 -tourism hostel,1,1,1 -tourism hotel,1,1,1 -tourism information office,1,0,0 -tourism motel,1,1,1 -tourism museum,1,1,0 -tourism viewpoint,1,0,0 -tourism zoo,1,1,0 -waterway waterfall,1,0,0 +OSM tag,Rating,Reviews,Details,Rating keys +aerialway station,1,1,0,place_page_rating +aeroway aerodrome,1,1,1,place_page_rating +aeroway helipad,1,1,0,place_page_rating +amenity,1,1,0,place_page_rating +amenity atm,1,1,1,place_page_rating +amenity bank,1,1,0,quality service value_for_money +amenity bar,1,1,1,value_for_money service atmosphere +amenity bench,1,1,0,place_page_rating +amenity bicycle_rental,1,1,0,experience service value_for_money +amenity bus_station,1,1,0,place_page_rating +amenity cafe,1,1,1,cuisine service atmosphere +amenity car_rental,1,1,0,quality service value_for_money +amenity car_wash,1,1,0,quality service value_for_money +amenity cinema,1,1,0,experience service value_for_money +amenity clinic,1,1,0,expertise equipment value_for_money +amenity college,1,1,0,place_page_rating +amenity dentist,1,1,0,expertise equipment value_for_money +amenity doctors,1,1,0,expertise equipment value_for_money +amenity drinking_water,1,1,0,place_page_rating +amenity embassy,1,1,0,quality service value_for_money +amenity fast_food,1,1,1,cuisine service atmosphere +amenity ferry_terminal,1,1,0,place_page_rating +amenity fountain,1,1,0,experience value_for_money +amenity fuel,1,1,0,assortment service value_for_money +amenity hospital,1,1,0,expertise equipment value_for_money +amenity kindergarten,1,1,0,place_page_rating +amenity library,1,1,0,quality service value_for_money +amenity marketplace,1,1,1,assortment service value_for_money +amenity nightclub,1,1,1,value_for_money service atmosphere +amenity parking,1,1,0,place_page_rating +amenity parking fee,1,1,0,place_page_rating +amenity pharmacy,1,1,1,assortment service value_for_money +amenity police,1,1,0,place_page_rating +amenity post_office,1,1,0,quality service value_for_money +amenity pub,1,1,1,value_for_money service atmosphere +amenity recycling,1,1,0,place_page_rating +amenity restaurant,1,1,1,cuisine service atmosphere +amenity school,1,1,0,place_page_rating +amenity shelter,1,1,0,place_page_rating +amenity taxi,1,1,0,place_page_rating +amenity theatre,1,1,0,experience service value_for_money +amenity toilets,1,1,0,place_page_rating +amenity townhall,1,1,0,place_page_rating +amenity university,1,1,0,place_page_rating +amenity biergarten,1,1,1,value_for_money service atmosphere +amenity driving_school,1,1,0,place_page_rating +amenity ice_cream,1,1,1,cuisine service atmosphere +amenity internet_cafe,1,1,0,quality service value_for_money +amenity motorcycle_parking,1,1,1,place_page_rating +amenity nursing_home,1,1,0,place_page_rating +amenity payment_terminal,1,1,0,place_page_rating +amenity shower,1,1,0,place_page_rating +amenity water_point,1,1,0,place_page_rating +barrier toll_booth,1,1,0,place_page_rating +boundary administrative,1,1,0,place_page_rating +building,1,1,0,place_page_rating +building train_station,1,1,0,place_page_rating +historic archaeological_site,1,1,0,experience value_for_money +historic castle,1,1,0,experience value_for_money +historic memorial,1,1,0,experience value_for_money +historic monument,1,1,0,experience value_for_money +historic museum,1,1,0,experience value_for_money +historic ruins,1,1,0,experience value_for_money +hwtag lit,1,1,0,place_page_rating +internet_access,1,1,0,place_page_rating +internet_access wlan,1,1,0,place_page_rating +landuse cemetery,1,1,0,place_page_rating +landuse forest,1,1,0,place_page_rating +landuse industrial,1,1,0,place_page_rating +landuse residential,1,1,0,place_page_rating +landuse retail,1,1,1,assortment service value_for_money +leisure garden,1,1,0,experience service value_for_money +leisure golf_course,1,1,0,experience service value_for_money +leisure nature_reserve,1,1,0,experience value_for_money +leisure park,1,1,0,experience service value_for_money +leisure pitch,1,1,0,equipment service value_for_money +leisure playground,1,1,0,experience service value_for_money +leisure sports_centre,1,1,0,equipment service value_for_money +leisure stadium,1,1,0,experience service value_for_money +leisure swimming_pool,1,1,0,equipment service value_for_money +leisure fitness_station,1,1,0,equipment service value_for_money +leisure water_park,1,1,0,experience service value_for_money +man_made lighthouse,1,1,0,experience value_for_money +natural beach,1,1,0,experience value_for_money +natural cave_entrance,1,1,0,experience value_for_money +natural peak,1,1,0,place_page_rating +natural spring,1,1,0,place_page_rating +natural volcano,1,1,0,place_page_rating +natural water,1,1,0,place_page_rating +natural wood,1,1,0,place_page_rating +office,1,1,0,place_page_rating +office company,1,1,0,place_page_rating +office government,1,1,0,place_page_rating +office insurance,1,1,0,quality service value_for_money +office ngo,1,1,0,place_page_rating +place island,1,1,0,place_page_rating +place ocean,1,1,0,place_page_rating +place sea,1,1,0,place_page_rating +railway halt,1,1,0,place_page_rating +railway station,1,1,0,place_page_rating +railway station subway,1,1,0,place_page_rating +railway tram_stop,1,1,0,place_page_rating +shop,1,1,1,assortment service value_for_money +shop alcohol,1,1,1,assortment service value_for_money +shop bakery,1,1,1,cuisine service atmosphere +shop beauty,1,1,1,assortment service value_for_money +shop bicycle,1,1,1,assortment service value_for_money +shop books,1,1,1,assortment service value_for_money +shop butcher,1,1,1,assortment service value_for_money +shop car,1,1,1,assortment service value_for_money +shop car_repair,1,1,0,quality service value_for_money +shop clothes,1,1,1,assortment service value_for_money +shop convenience,1,1,0,assortment service value_for_money +shop department_store,1,1,1,assortment service value_for_money +shop doityourself,1,1,1,assortment service value_for_money +shop electronics,1,1,1,assortment service value_for_money +shop florist,1,1,1,assortment service value_for_money +shop furniture,1,1,1,assortment service value_for_money +shop gift,1,1,1,assortment service value_for_money +shop hairdresser,1,1,0,quality service value_for_money +shop hardware,1,1,1,assortment service value_for_money +shop jewelry,1,1,1,assortment service value_for_money +shop kiosk,1,1,0,assortment service value_for_money +shop mall,1,1,0,assortment service value_for_money +shop mobile_phone,1,1,1,assortment service value_for_money +shop shoes,1,1,1,assortment service value_for_money +shop sports,1,1,0,assortment service value_for_money +shop supermarket,1,1,1,assortment service value_for_money +shop beverages,1,1,1,assortment service value_for_money +shop bookmaker,1,1,1,quality service value_for_money +shop car_parts,1,1,1,assortment service value_for_money +shop chemist,1,1,1,assortment service value_for_money +shop chocolate,1,1,1,assortment service value_for_money +shop computer,1,1,1,assortment service value_for_money +shop cosmetics,1,1,1,assortment service value_for_money +shop erotic,1,1,1,assortment service value_for_money +shop funeral_directors,1,1,0,place_page_rating +shop laundry,1,1,0,quality service value_for_money +shop massage,1,1,0,experience service value_for_money +shop motorcycle,1,1,1,assortment service value_for_money +shop newsagent,1,1,1,assortment service value_for_money +shop pawnbroker,1,1,1,quality service value_for_money +shop stationery,1,1,1,assortment service value_for_money +shop tattoo,1,1,0,quality service value_for_money +shop tyres,1,1,1,assortment service value_for_money +shop variety_store,1,1,1,assortment service value_for_money +shop wine,1,1,1,assortment service value_for_money +sport basketball,1,1,0,equipment service value_for_money +sport multi,1,1,0,equipment service value_for_money +sport soccer,1,1,0,equipment service value_for_money +sport swimming,1,1,0,equipment service value_for_money +sport tennis,1,1,0,equipment service value_for_money +tourism artwork,1,1,0,experience value_for_money +tourism attraction,1,1,0,experience value_for_money +tourism camp_site,1,1,0,place_page_rating +tourism guest_house,1,1,1,booking_hotel_room service placepage_hotel_facilities +tourism hostel,1,1,1,booking_hotel_room service placepage_hotel_facilities +tourism hotel,1,1,1,booking_hotel_room service placepage_hotel_facilities +tourism information,1,1,0,place_page_rating +tourism information guidepost,1,1,0,place_page_rating +tourism information office,1,1,0,quality service value_for_money +tourism motel,1,1,1,booking_hotel_room service placepage_hotel_facilities +tourism museum,1,1,0,experience service value_for_money +tourism picnic_site,1,1,0,place_page_rating +tourism viewpoint,1,1,0,experience value_for_money +tourism zoo,1,1,0,experience service value_for_money +tourism alpine_hut,1,1,0,booking_hotel_room service placepage_hotel_facilities +tourism apartment,1,1,0,booking_hotel_room service placepage_hotel_facilities +tourism chalet,1,1,0,booking_hotel_room service placepage_hotel_facilities +tourism resort,1,1,0,booking_hotel_room service placepage_hotel_facilities +tourism theme_park ,1,1,0,experience service value_for_money +tourism wilderness_hut,1,1,0,place_page_rating +tourism wilderness_hut,1,1,0,booking_hotel_room service placepage_hotel_facilities +waterway canal,1,1,0,place_page_rating +waterway river,1,1,0,place_page_rating +waterway stream,1,1,0,place_page_rating +waterway waterfall,1,1,0,experience value_for_money diff --git a/generator/ugc_section_builder.cpp b/generator/ugc_section_builder.cpp index 2b9ef86a0d..7116b40199 100644 --- a/generator/ugc_section_builder.cpp +++ b/generator/ugc_section_builder.cpp @@ -36,7 +36,7 @@ bool BuildUgcMwmSection(std::string const & srcDbFilename, std::string const & m std::vector content; feature::ForEachFromDat(mwmFile, [&](FeatureType const & f, uint32_t featureId) { - auto const ugcMasks = ftraits::UGC::GetValue({f}); + auto const ugcMasks = ftraits::UGC::GetValue({f}).m_mask; if (!ftraits::UGC::IsUGCAvailable(ugcMasks)) return; diff --git a/indexer/ftraits.hpp b/indexer/ftraits.hpp index 0b4cd4cb8e..da21fef04f 100644 --- a/indexer/ftraits.hpp +++ b/indexer/ftraits.hpp @@ -3,7 +3,7 @@ #include "indexer/feature_data.hpp" #include "indexer/ftypes_mapping.hpp" -#include "coding/csv_file_reader.hpp" +#include "coding/csv_reader.hpp" #include "platform/platform.hpp" @@ -15,10 +15,11 @@ #include #include #include +#include namespace ftraits { -template +template class TraitsBase { public: @@ -27,13 +28,13 @@ public: static Base instance; auto const it = instance.m_matcher.Find(types); if (!instance.m_matcher.IsValid(it)) - return notFound; + return Base::GetEmptyValue(); return it->second; } protected: - ftypes::HashMapMatcher m_matcher; + ftypes::Matcher, allowTransitiveDuplications> m_matcher; }; enum UGCType @@ -45,8 +46,21 @@ enum UGCType }; using UGCTypeMask = unsigned; +using UGCRatingCategories = std::vector; -class UGC : public TraitsBase +struct UGCItem +{ + UGCItem() = default; + UGCItem(UGCTypeMask && m, UGCRatingCategories && c) + : m_mask(std::move(m)), m_categories(std::move(c)) + { + } + + UGCTypeMask m_mask = UGCTYPE_NONE; + UGCRatingCategories m_categories; +}; + +class UGC : public TraitsBase { friend class TraitsBase; @@ -55,36 +69,54 @@ class UGC : public TraitsBase UGC() { coding::CSVReader reader; - auto const filePath = GetPlatform().ReadPathForFile("ugc_types.csv", "wr"); - reader.ReadLineByLine(filePath, [this](std::vector const & line) { - auto const lineSize = line.size(); - ASSERT_EQUAL(lineSize, 4, ()); - ASSERT_EQUAL(lineSize - 1, m_masks.size(), ()); + auto const fileReader = GetPlatform().GetReader("ugc_types.csv"); + reader.Read(*fileReader, [this](std::vector const & line) { + size_t constexpr kItemsCount = 5; + size_t constexpr kTypePos = 0; + size_t constexpr kCategoriesPos = 4; - UGCTypeMask maskType = UGCTYPE_NONE; - for (size_t i = 1; i < lineSize; i++) - { - int flag; - if (!strings::to_int(line[i], flag)) - { - LOG(LERROR, ("File ugc_types.csv must contain a bit mask of supported ugc traits!")); - return; - } + ASSERT_EQUAL(line.size(), kItemsCount, ()); - if (flag) - maskType |= m_masks[i - 1]; - } - - auto const & typeInfo = line.front(); - std::istringstream iss(typeInfo); - std::vector types{std::istream_iterator(iss), - std::istream_iterator()}; - - m_matcher.AppendType(types, maskType); + UGCItem item(ReadMasks(line), ParseByWhitespaces(line[kCategoriesPos])); + m_matcher.AppendType(ParseByWhitespaces(line[kTypePos]), std::move(item)); }); } + UGCTypeMask ReadMasks(std::vector const & line) + { + size_t constexpr kMasksBegin = 1; + size_t constexpr kMasksEnd = 4; + + UGCTypeMask maskType = UGCTYPE_NONE; + for (size_t i = kMasksBegin; i < kMasksEnd; i++) + { + int flag; + if (!strings::to_int(line[i], flag)) + { + LOG(LERROR, ("File ugc_types.csv must contain a bit mask of supported ugc traits!")); + return UGCTYPE_NONE; + } + + if (flag) + maskType |= m_masks[i - 1]; + } + + return maskType; + } + + std::vector ParseByWhitespaces(std::string const & str) + { + std::istringstream iss(str); + return {std::istream_iterator(iss), std::istream_iterator()}; + } + public: + static UGCItem const & GetEmptyValue() + { + static const UGCItem item; + return item; + } + static bool IsUGCAvailable(UGCTypeMask mask) { return mask != UGCTYPE_NONE; } static bool IsRatingAvailable(UGCTypeMask mask) { return mask & UGCTYPE_RATING; } static bool IsReviewsAvailable(UGCTypeMask mask) { return mask & UGCTYPE_REVIEWS; } @@ -92,19 +124,23 @@ public: static bool IsUGCAvailable(feature::TypesHolder const & types) { - return IsUGCAvailable(GetValue(types)); + return IsUGCAvailable(GetValue(types).m_mask); } static bool IsRatingAvailable(feature::TypesHolder const & types) { - return IsRatingAvailable(GetValue(types)); + return IsRatingAvailable(GetValue(types).m_mask); } static bool IsReviewsAvailable(feature::TypesHolder const & types) { - return IsReviewsAvailable(GetValue(types)); + return IsReviewsAvailable(GetValue(types).m_mask); } static bool IsDetailsAvailable(feature::TypesHolder const & types) { - return IsDetailsAvailable(GetValue(types)); + return IsDetailsAvailable(GetValue(types).m_mask); + } + static UGCRatingCategories GetCategories(feature::TypesHolder const & types) + { + return GetValue(types).m_categories; } }; @@ -125,8 +161,7 @@ inline std::string DebugPrint(WheelchairAvailability wheelchair) } } -class Wheelchair - : public TraitsBase +class Wheelchair : public TraitsBase { friend class TraitsBase; @@ -139,6 +174,9 @@ class Wheelchair m_matcher.Append({{"wheelchair", "limited"}}, WheelchairAvailability::Limited); } + +public: + static WheelchairAvailability GetEmptyValue() { return WheelchairAvailability::No; } }; } // namespace ftraits diff --git a/indexer/ftypes_mapping.hpp b/indexer/ftypes_mapping.hpp index d21553be1b..5983f8c5e8 100644 --- a/indexer/ftypes_mapping.hpp +++ b/indexer/ftypes_mapping.hpp @@ -11,7 +11,7 @@ namespace ftypes { -template +template class Matcher { public: @@ -49,9 +49,12 @@ public: { { #if defined(DEBUG) - feature::TypesHolder holder; - holder.Assign(classif().GetTypeByPath(type)); - ASSERT(Find(holder) == m_mapping.cend(), ("This type already exists", type)); + if (!allowTransitiveDuplications) + { + feature::TypesHolder holder; + holder.Assign(classif().GetTypeByPath(type)); + ASSERT(Find(holder) == m_mapping.cend(), ("This type already exists", type)); + } #endif } m_mapping.emplace(classif().GetTypeByPath(std::forward(type)), diff --git a/indexer/indexer_tests/CMakeLists.txt b/indexer/indexer_tests/CMakeLists.txt index 101b72eab3..cfd645fb81 100644 --- a/indexer/indexer_tests/CMakeLists.txt +++ b/indexer/indexer_tests/CMakeLists.txt @@ -36,6 +36,7 @@ set( test_polylines.hpp test_type.cpp trie_test.cpp + ugc_types_test.cpp visibility_test.cpp wheelchair_tests.cpp ) diff --git a/indexer/indexer_tests/indexer_tests.pro b/indexer/indexer_tests/indexer_tests.pro index 9485d04fac..7a51e94723 100644 --- a/indexer/indexer_tests/indexer_tests.pro +++ b/indexer/indexer_tests/indexer_tests.pro @@ -59,5 +59,6 @@ SOURCES += \ test_polylines.cpp \ test_type.cpp \ trie_test.cpp \ + ugc_types_test.cpp \ visibility_test.cpp \ wheelchair_tests.cpp \ diff --git a/indexer/indexer_tests/ugc_types_test.cpp b/indexer/indexer_tests/ugc_types_test.cpp new file mode 100644 index 0000000000..a1dfb13290 --- /dev/null +++ b/indexer/indexer_tests/ugc_types_test.cpp @@ -0,0 +1,46 @@ +#include "testing/testing.hpp" + +#include "indexer/classificator.hpp" +#include "indexer/classificator_loader.hpp" +#include "indexer/ftraits.hpp" + +UNIT_TEST(UgcTypes_Full) +{ + classificator::Load(); + Classificator const & c = classif(); + + using ftraits::UGC; + + feature::TypesHolder holder; + { + holder.Assign(c.GetTypeByPath({"amenity", "bank"})); + TEST(UGC::IsUGCAvailable(holder), ()); + TEST(UGC::IsRatingAvailable(holder), ()); + TEST(UGC::IsReviewsAvailable(holder), ()); + TEST(!UGC::IsDetailsAvailable(holder), ()); + ftraits::UGCRatingCategories expected = {"quality", "service", "value_for_money"}; + TEST_EQUAL(UGC::GetCategories(holder), expected, ()); + } + { + holder.Assign(c.GetTypeByPath({"tourism", "information", "office"})); + TEST(UGC::IsUGCAvailable(holder), ()); + TEST(UGC::IsRatingAvailable(holder), ()); + TEST(UGC::IsReviewsAvailable(holder), ()); + TEST(!UGC::IsDetailsAvailable(holder), ()); + ftraits::UGCRatingCategories expected = {"quality", "service", "value_for_money"}; + TEST_EQUAL(UGC::GetCategories(holder), expected, ()); + } + { + holder.Assign(c.GetTypeByPath({"amenity", "hospital"})); + TEST(UGC::IsUGCAvailable(holder), ()); + TEST(UGC::IsRatingAvailable(holder), ()); + TEST(UGC::IsReviewsAvailable(holder), ()); + TEST(!UGC::IsDetailsAvailable(holder), ()); + ftraits::UGCRatingCategories expected = {"expertise", "equipment", "value_for_money"}; + TEST_EQUAL(UGC::GetCategories(holder), expected, ()); + } + { + holder.Assign(c.GetTypeByPath({"traffic_calming", "bump"})); + TEST(!UGC::IsUGCAvailable(holder), ()); + } +} diff --git a/map/framework.cpp b/map/framework.cpp index a3d292bb7a..a59bba96aa 100644 --- a/map/framework.cpp +++ b/map/framework.cpp @@ -2502,7 +2502,6 @@ df::SelectionShape::ESelectedObject Framework::OnTapEventImpl(TapEvent const & t } outInfo.SetAdsEngine(m_adsEngine.get()); - outInfo.SetUGCApi(m_ugcApi.get()); UserMark const * mark = FindUserMarkInTapPosition(tapInfo); if (mark != nullptr) diff --git a/map/place_page_info.cpp b/map/place_page_info.cpp index 926be308a9..de356c35a2 100644 --- a/map/place_page_info.cpp +++ b/map/place_page_info.cpp @@ -179,6 +179,11 @@ bool Info::ShouldShowEditPlace() const !IsMyPosition() && IsFeature(); } +ftraits::UGCRatingCategories Info::GetRatingCategories() const +{ + return ftraits::UGC::GetCategories(m_types); +} + string Info::FormatNewBookmarkName() const { string const title = GetTitle(); diff --git a/map/place_page_info.hpp b/map/place_page_info.hpp index d49e04eb5d..bb56da78ec 100644 --- a/map/place_page_info.hpp +++ b/map/place_page_info.hpp @@ -77,11 +77,11 @@ public: bool ShouldShowEditPlace() const; /// UGC - void SetUGCApi(ugc::Api * const api) { m_ugcApi = api; } bool ShouldShowUGC() const { return ftraits::UGC::IsUGCAvailable(m_types); } bool CanBeRated() const { return ftraits::UGC::IsRatingAvailable(m_types); } bool CanBeReviewed() const { return ftraits::UGC::IsReviewsAvailable(m_types); } bool CanHaveExtendedReview() const { return ftraits::UGC::IsDetailsAvailable(m_types); } + ftraits::UGCRatingCategories GetRatingCategories() const; /// @returns true if Back API button should be displayed. bool HasApiUrl() const { return !m_apiUrl.empty(); } @@ -255,9 +255,6 @@ private: LocalAdsStatus m_localAdsStatus = LocalAdsStatus::NotAvailable; /// Ads source. ads::Engine * m_adsEngine = nullptr; - /// UGC - /// This is a non-owning pointer; - ugc::Api * m_ugcApi = nullptr; /// Sponsored type or None. SponsoredType m_sponsoredType = SponsoredType::None; diff --git a/xcode/coding/coding.xcodeproj/project.pbxproj b/xcode/coding/coding.xcodeproj/project.pbxproj index 832c5d4dc6..da58f63e05 100644 --- a/xcode/coding/coding.xcodeproj/project.pbxproj +++ b/xcode/coding/coding.xcodeproj/project.pbxproj @@ -26,6 +26,8 @@ 3D489BC11D3D21A40052AA38 /* simple_dense_coding_test.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3D489BB91D3D217E0052AA38 /* simple_dense_coding_test.cpp */; }; 3D489BC21D3D21AA0052AA38 /* fixed_bits_ddvector_test.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3D489BB81D3D217E0052AA38 /* fixed_bits_ddvector_test.cpp */; }; 3D489BC31D3D21AE0052AA38 /* elias_coder_test.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3D489BB71D3D217E0052AA38 /* elias_coder_test.cpp */; }; + 3D74EF211F8F55740081202C /* csv_reader.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 3D74EF1F1F8F55740081202C /* csv_reader.hpp */; }; + 3D74EF221F8F55740081202C /* csv_reader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3D74EF201F8F55740081202C /* csv_reader.cpp */; }; 45C108B41E9CFE69000FE1F6 /* point_to_integer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 45C108B21E9CFE69000FE1F6 /* point_to_integer.cpp */; }; 45C108B51E9CFE69000FE1F6 /* point_to_integer.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 45C108B31E9CFE69000FE1F6 /* point_to_integer.hpp */; }; 45C108B81E9CFE7B000FE1F6 /* point_to_integer_test.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 45C108B61E9CFE78000FE1F6 /* point_to_integer_test.cpp */; }; @@ -131,8 +133,6 @@ BB537C601E8490120074D9D3 /* transliteration.hpp in Headers */ = {isa = PBXBuildFile; fileRef = BB537C5E1E8490120074D9D3 /* transliteration.hpp */; }; F65AFA361F18B8AB00979A50 /* libplatform_tests_support.a in Frameworks */ = {isa = PBXBuildFile; fileRef = F65AFA351F18B8AB00979A50 /* libplatform_tests_support.a */; }; F65AFA381F18C7A500979A50 /* libplatform.a in Frameworks */ = {isa = PBXBuildFile; fileRef = F65AFA371F18C7A500979A50 /* libplatform.a */; }; - F6AFCB721F0D633D00E70373 /* csv_file_reader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F6AFCB701F0D633D00E70373 /* csv_file_reader.cpp */; }; - F6AFCB731F0D633D00E70373 /* csv_file_reader.hpp in Headers */ = {isa = PBXBuildFile; fileRef = F6AFCB711F0D633D00E70373 /* csv_file_reader.hpp */; }; F6C269FE1F176FFE00EB6519 /* csv_reader_test.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F6C269FD1F176FFE00EB6519 /* csv_reader_test.cpp */; }; /* End PBXBuildFile section */ @@ -173,6 +173,8 @@ 3D489BB81D3D217E0052AA38 /* fixed_bits_ddvector_test.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = fixed_bits_ddvector_test.cpp; sourceTree = ""; }; 3D489BB91D3D217E0052AA38 /* simple_dense_coding_test.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = simple_dense_coding_test.cpp; sourceTree = ""; }; 3D489BBA1D3D217E0052AA38 /* succinct_mapper_test.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = succinct_mapper_test.cpp; sourceTree = ""; }; + 3D74EF1F1F8F55740081202C /* csv_reader.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = csv_reader.hpp; sourceTree = ""; }; + 3D74EF201F8F55740081202C /* csv_reader.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = csv_reader.cpp; sourceTree = ""; }; 45C108B21E9CFE69000FE1F6 /* point_to_integer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = point_to_integer.cpp; sourceTree = ""; }; 45C108B31E9CFE69000FE1F6 /* point_to_integer.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = point_to_integer.hpp; sourceTree = ""; }; 45C108B61E9CFE78000FE1F6 /* point_to_integer_test.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = point_to_integer_test.cpp; sourceTree = ""; }; @@ -280,8 +282,6 @@ BB537C5E1E8490120074D9D3 /* transliteration.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = transliteration.hpp; sourceTree = ""; }; F65AFA351F18B8AB00979A50 /* libplatform_tests_support.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libplatform_tests_support.a; path = ../platform/build/Debug/libplatform_tests_support.a; sourceTree = ""; }; F65AFA371F18C7A500979A50 /* libplatform.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libplatform.a; path = "../../../../Library/Developer/Xcode/DerivedData/omim-fbvotunmmtqmjnezabjibwxwryev/Build/Products/Debug/libplatform.a"; sourceTree = ""; }; - F6AFCB701F0D633D00E70373 /* csv_file_reader.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = csv_file_reader.cpp; sourceTree = ""; }; - F6AFCB711F0D633D00E70373 /* csv_file_reader.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = csv_file_reader.hpp; sourceTree = ""; }; F6C269FD1F176FFE00EB6519 /* csv_reader_test.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = csv_reader_test.cpp; sourceTree = ""; }; /* End PBXFileReference section */ @@ -408,6 +408,8 @@ 6753421D1A3F586300A0A8C3 /* coding */ = { isa = PBXGroup; children = ( + 3D74EF201F8F55740081202C /* csv_reader.cpp */, + 3D74EF1F1F8F55740081202C /* csv_reader.hpp */, 45C108B21E9CFE69000FE1F6 /* point_to_integer.cpp */, 45C108B31E9CFE69000FE1F6 /* point_to_integer.hpp */, BB537C5D1E8490120074D9D3 /* transliteration.cpp */, @@ -464,8 +466,6 @@ 675342641A3F588B00A0A8C3 /* reader_writer_ops.hpp */, 675342651A3F588B00A0A8C3 /* reader.cpp */, 675342661A3F588B00A0A8C3 /* reader.hpp */, - F6AFCB701F0D633D00E70373 /* csv_file_reader.cpp */, - F6AFCB711F0D633D00E70373 /* csv_file_reader.hpp */, 675342691A3F588B00A0A8C3 /* streams_common.hpp */, 6753426A1A3F588B00A0A8C3 /* streams_sink.hpp */, 6753426B1A3F588B00A0A8C3 /* streams.hpp */, @@ -514,7 +514,6 @@ 675342B51A3F588C00A0A8C3 /* reader_cache.hpp in Headers */, 675342CE1A3F588C00A0A8C3 /* varint.hpp in Headers */, BB537C601E8490120074D9D3 /* transliteration.hpp in Headers */, - F6AFCB731F0D633D00E70373 /* csv_file_reader.hpp in Headers */, 675342D01A3F588C00A0A8C3 /* writer.hpp in Headers */, 675342CA1A3F588C00A0A8C3 /* var_serial_vector.hpp in Headers */, 347F33391C4540F0009758CC /* fixed_bits_ddvector.hpp in Headers */, @@ -560,6 +559,7 @@ 675342C71A3F588C00A0A8C3 /* url_encode.hpp in Headers */, 675342BC1A3F588C00A0A8C3 /* reader.hpp in Headers */, 675342D21A3F588C00A0A8C3 /* zip_creator.hpp in Headers */, + 3D74EF211F8F55740081202C /* csv_reader.hpp in Headers */, 675342B11A3F588C00A0A8C3 /* multilang_utf8_string.hpp in Headers */, 675342D41A3F588C00A0A8C3 /* zip_reader.hpp in Headers */, 675342A41A3F588C00A0A8C3 /* file_writer.hpp in Headers */, @@ -696,6 +696,7 @@ 675342821A3F588C00A0A8C3 /* base64.cpp in Sources */, 675342D31A3F588C00A0A8C3 /* zip_reader.cpp in Sources */, 347F33371C4540F0009758CC /* compressed_bit_vector.cpp in Sources */, + 3D74EF221F8F55740081202C /* csv_reader.cpp in Sources */, 6753429F1A3F588C00A0A8C3 /* file_reader.cpp in Sources */, 34A129D31DF99E43001B4531 /* zlib.cpp in Sources */, 676818201DC3ABD80094C0AC /* traffic_test.cpp in Sources */, @@ -703,7 +704,6 @@ 675342C51A3F588C00A0A8C3 /* uri.cpp in Sources */, 675342BB1A3F588C00A0A8C3 /* reader.cpp in Sources */, 670BAACB1D0B0C1E000302DA /* huffman.cpp in Sources */, - F6AFCB721F0D633D00E70373 /* csv_file_reader.cpp in Sources */, 6753429C1A3F588C00A0A8C3 /* file_name_utils.cpp in Sources */, 675342A71A3F588C00A0A8C3 /* hex.cpp in Sources */, 675342A31A3F588C00A0A8C3 /* file_writer.cpp in Sources */, diff --git a/xcode/indexer/indexer.xcodeproj/project.pbxproj b/xcode/indexer/indexer.xcodeproj/project.pbxproj index 197054f8e6..453a884630 100644 --- a/xcode/indexer/indexer.xcodeproj/project.pbxproj +++ b/xcode/indexer/indexer.xcodeproj/project.pbxproj @@ -62,6 +62,7 @@ 3D51BC431D5E4E2B00F1FA8D /* test_mwm_set.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 56C74C2D1C749E8100B71B9F /* test_mwm_set.hpp */; }; 3D51BC451D5E4EBF00F1FA8D /* libsearch_tests_support.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 3D51BC441D5E4EBF00F1FA8D /* libsearch_tests_support.a */; }; 3D74ABBC1EA67C1E0063A898 /* ftypes_mapping.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 3D74ABBB1EA67C1E0063A898 /* ftypes_mapping.hpp */; }; + 3D74EF241F8F559D0081202C /* ugc_types_test.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3D74EF231F8F559D0081202C /* ugc_types_test.cpp */; }; 3D928F671D50F9FE001670E0 /* index_helpers.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3D928F651D50F9FE001670E0 /* index_helpers.cpp */; }; 3D928F681D50F9FE001670E0 /* index_helpers.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 3D928F661D50F9FE001670E0 /* index_helpers.hpp */; }; 456B3FB41EDEEB65009B3D1F /* scales_patch.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 456B3FB31EDEEB65009B3D1F /* scales_patch.hpp */; }; @@ -285,6 +286,7 @@ 3D51BC401D5E4CFA00F1FA8D /* libtess2.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libtess2.a; path = "../../../omim-xcode-build/Debug/libtess2.a"; sourceTree = ""; }; 3D51BC441D5E4EBF00F1FA8D /* libsearch_tests_support.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libsearch_tests_support.a; path = "../../../omim-xcode-build/Debug/libsearch_tests_support.a"; sourceTree = ""; }; 3D74ABBB1EA67C1E0063A898 /* ftypes_mapping.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = ftypes_mapping.hpp; sourceTree = ""; }; + 3D74EF231F8F559D0081202C /* ugc_types_test.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ugc_types_test.cpp; sourceTree = ""; }; 3D928F651D50F9FE001670E0 /* index_helpers.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = index_helpers.cpp; sourceTree = ""; }; 3D928F661D50F9FE001670E0 /* index_helpers.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = index_helpers.hpp; sourceTree = ""; }; 456B3FB31EDEEB65009B3D1F /* scales_patch.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = scales_patch.hpp; sourceTree = ""; }; @@ -529,6 +531,14 @@ path = ../../indexer/indexer_tests_support; sourceTree = ""; }; + 3D74EEFB1F7CFD2C0081202C /* Recovered References */ = { + isa = PBXGroup; + children = ( + 670BAAC91D0B0BBB000302DA /* string_slice.hpp */, + ); + name = "Recovered References"; + sourceTree = ""; + }; 45C108BB1E9D0067000FE1F6 /* Frameworks */ = { isa = PBXGroup; children = ( @@ -573,6 +583,7 @@ 670C60F81AB0657700C38A8C /* indexer_tests */ = { isa = PBXGroup; children = ( + 3D74EF231F8F559D0081202C /* ugc_types_test.cpp */, 3D452AF71EE6D9F5009EAB9B /* wheelchair_tests.cpp */, 3D452AF81EE6D9F5009EAB9B /* feature_names_test.cpp */, 3D452AF91EE6D9F5009EAB9B /* centers_table_test.cpp */, @@ -636,6 +647,7 @@ 6753409B1A3F53CB00A0A8C3 /* Products */, 3496ABB01DC1FB1500C5DDBA /* Resources */, 45C108BB1E9D0067000FE1F6 /* Frameworks */, + 3D74EEFB1F7CFD2C0081202C /* Recovered References */, ); sourceTree = ""; }; @@ -1064,6 +1076,7 @@ 6758AED31BB4413000C26E27 /* drules_selector.cpp in Sources */, 6753411A1A3F540F00A0A8C3 /* feature_impl.cpp in Sources */, 56C74C1C1C749E4700B71B9F /* categories_holder_loader.cpp in Sources */, + 3D74EF241F8F559D0081202C /* ugc_types_test.cpp in Sources */, 6753410D1A3F540F00A0A8C3 /* drawing_rules.cpp in Sources */, 675341301A3F540F00A0A8C3 /* index.cpp in Sources */, 34664CF61D49FEC1003D7096 /* centers_table.cpp in Sources */,