diff options
author | Arsentiy Milchakov <a.milchakov@corp.mail.ru> | 2016-08-31 18:27:16 +0300 |
---|---|---|
committer | Arsentiy Milchakov <a.milchakov@corp.mail.ru> | 2016-08-31 18:27:16 +0300 |
commit | 97f9878607fb15f406d3b52b2f06ca8ecbe4915a (patch) | |
tree | 9853b6922f09488092c8cbb0b396bb3b9bcfb0b2 | |
parent | 39103b30cf1fcb94801d6725f60897c85df45304 (diff) |
editor web config
-rw-r--r-- | data/editor.config | 19 | ||||
-rw-r--r-- | editor/config_loader.cpp | 176 | ||||
-rw-r--r-- | editor/config_loader.hpp | 73 | ||||
-rw-r--r-- | editor/editor.pro | 2 | ||||
-rw-r--r-- | editor/editor_config.cpp | 19 | ||||
-rw-r--r-- | editor/editor_config.hpp | 33 | ||||
-rw-r--r-- | editor/editor_tests/config_loader_test.cpp | 62 | ||||
-rw-r--r-- | editor/editor_tests/editor_config_test.cpp | 11 | ||||
-rw-r--r-- | editor/editor_tests/editor_tests.pro | 4 | ||||
-rw-r--r-- | indexer/osm_editor.cpp | 7 | ||||
-rw-r--r-- | indexer/osm_editor.hpp | 4 | ||||
-rw-r--r-- | std/iterator.hpp | 1 | ||||
-rw-r--r-- | xcode/editor/editor.xcodeproj/project.pbxproj | 8 |
13 files changed, 372 insertions, 47 deletions
diff --git a/data/editor.config b/data/editor.config index c6c31da1ba..9b66ced5ae 100644 --- a/data/editor.config +++ b/data/editor.config @@ -1,9 +1,9 @@ +<?xml version="1.0"?> <mapsme> <editor> <disable everywhere="no"> <!--bbox minlon="37.6120" minlat="55.7479" maxlon="37.6239" maxlat="55.7553" editable="no" /--> </disable> - <fields> <!-- Core fields of a feature. --> <field name="name" multilanguage="yes"> @@ -123,7 +123,6 @@ <field_ref name="website" /> </field_group> </fields> - <!-- Types should be sorted by their priority. --> <!-- Buildings are hardcoded in editor_config.cpp and are mixed with any other type. --> <types> @@ -337,8 +336,7 @@ <type id="amenity-waste_disposal"> <include field="operator" /> </type> - <type id="amenity-waste_basket"> - </type> + <type id="amenity-waste_basket" /> <type id="amenity-car_wash"> <include group="poi" /> <include field="internet" /> @@ -742,13 +740,11 @@ <include field="height" /> <include field="wikipedia" /> </type> - <type id="amenity-bench"> - </type> + <type id="amenity-bench" /> <type id="amenity-shelter"> <include field="internet" /> </type> - <type id="tourism-picnic_site"> - </type> + <type id="tourism-picnic_site" /> <type id="amenity-brothel" can_add="no"> <include group="poi" /> <include field="internet" /> @@ -781,10 +777,8 @@ <type id="leisure-sauna"> <include group="poi" /> </type> - <type id="man_made-chimney"> - </type> - <type id="man_made-water_well"> - </type> + <type id="man_made-chimney" /> + <type id="man_made-water_well" /> <type id="shop-cosmetics" group="shop"> <include group="poi" /> </type> @@ -871,7 +865,6 @@ <include group="address" /> </type> </types> - <preferred_types> <type id="amenity-restaurant" /> <type id="amenity-cafe" /> diff --git a/editor/config_loader.cpp b/editor/config_loader.cpp new file mode 100644 index 0000000000..305910a4ed --- /dev/null +++ b/editor/config_loader.cpp @@ -0,0 +1,176 @@ +#include "editor/config_loader.hpp" +#include "editor/editor_config.hpp" + +#include "platform/platform.hpp" + +#include "coding/internal/file_data.hpp" +#include "coding/reader.hpp" + +#include "std/atomic.hpp" +#include "std/fstream.hpp" +#include "std/iterator.hpp" + +#include "3party/Alohalytics/src/http_client.h" +#include "3party/pugixml/src/pugixml.hpp" + +namespace +{ +using alohalytics::HTTPClientPlatformWrapper; + +auto const kConfigFileName = "editor.config"; +auto const kHashFileName = "editor.config.hash"; + +auto const kSynchroTimeout = hours(4); +auto const kRemoteHashUrl = "http://osmz.ru/mwm/editor.config.date"; +auto const kRemoteConfigUrl = "http://osmz.ru/mwm/editor.config"; + +string GetConfigFilePath() { return GetPlatform().WritablePathForFile(kConfigFileName); } +string GetHashFilePath() { return GetPlatform().WritablePathForFile(kHashFileName); } + +string RunSimpleHttpRequest(string const & url) +{ + HTTPClientPlatformWrapper request(url); + auto const result = request.RunHTTPRequest(); + + if (result && !request.was_redirected() && request.error_code() == 200) // 200 - http status OK + { + return request.server_response(); + } + + return {}; +} +} // namespace + +namespace editor +{ +void Waiter::Interrupt() +{ + { + lock_guard<mutex> lock(m_mutex); + m_interrupted = true; + } + + m_event.notify_all(); +} + +ConfigLoader::ConfigLoader(EditorConfigWrapper & config) : m_config(config) +{ + pugi::xml_document doc; + LoadFromLocal(doc); + ResetConfig(doc); + m_loaderThread = thread(&ConfigLoader::LoadFromServer, this); +} + +ConfigLoader::~ConfigLoader() +{ + m_waiter.Interrupt(); + m_loaderThread.join(); +} + +void ConfigLoader::LoadFromServer() +{ + auto const saveAndReload = [this](pugi::xml_document const & doc) + { + if (doc.empty()) + return false; + + auto const filePath = GetConfigFilePath(); + auto const result = + my::WriteToTempAndRenameToFile(filePath, [&doc](string const & fileName) + { + return doc.save_file(fileName.c_str(), " "); + }); + + if (!result) + return false; + + ResetConfig(doc); + return true; + }; + + auto const hash = LoadHash(GetHashFilePath()); + + try + { + do + { + auto const remoteHash = GetRemoteHash(); + if (remoteHash.empty() || hash == remoteHash) + continue; + + pugi::xml_document doc; + GetRemoteConfig(doc); + + if (saveAndReload(doc)) + SaveHash(remoteHash, GetHashFilePath()); + + } while (m_waiter.Wait(kSynchroTimeout)); + } + catch (RootException const & ex) + { + LOG(LERROR, (ex.Msg())); + } +} + +void ConfigLoader::ResetConfig(pugi::xml_document const & doc) +{ + auto config = make_shared<EditorConfig>(); + config->SetConfig(doc); + m_config.Set(config); +} + +//static +void ConfigLoader::LoadFromLocal(pugi::xml_document & doc) +{ + string content; + auto const reader = GetPlatform().GetReader(kConfigFileName); + reader->ReadAsString(content); + + if (!doc.load_buffer(content.data(), content.size())) + MYTHROW(ConfigLoadError, ("Can't parse config")); +} + +// static +string ConfigLoader::GetRemoteHash() +{ + return RunSimpleHttpRequest(kRemoteHashUrl); +} + +// static +void ConfigLoader::GetRemoteConfig(pugi::xml_document & doc) +{ + auto const result = RunSimpleHttpRequest(kRemoteConfigUrl); + if (result.empty()) + return; + + if (!doc.load_string(result.c_str(), pugi::parse_default | pugi::parse_comments)) + doc.reset(); +} + +// static +bool ConfigLoader::SaveHash(string const & hash, string const & filePath) +{ + auto const result = + my::WriteToTempAndRenameToFile(filePath, [&hash](string const & fileName) + { + ofstream ofs(fileName, ofstream::out); + if (!ofs.is_open()) + return false; + + ofs.write(hash.data(), hash.size()); + return true; + }); + + return result; +} + +// static +string ConfigLoader::LoadHash(string const & filePath) +{ + ifstream ifs(filePath, ifstream::in); + if (!ifs.is_open()) + return {}; + + return {istreambuf_iterator<char>(ifs), istreambuf_iterator<char>()}; +} +} // namespace editor diff --git a/editor/config_loader.hpp b/editor/config_loader.hpp new file mode 100644 index 0000000000..8c252125db --- /dev/null +++ b/editor/config_loader.hpp @@ -0,0 +1,73 @@ +#pragma once + +#include "base/exception.hpp" +#include "base/logging.hpp" + +#include "std/chrono.hpp" +#include "std/condition_variable.hpp" +#include "std/mutex.hpp" +#include "std/shared_ptr.hpp" +#include "std/string.hpp" +#include "std/thread.hpp" + +namespace pugi +{ +class xml_document; +} + +namespace editor +{ +class EditorConfigWrapper; + +// Class for multithreaded interruptable waiting. +class Waiter +{ +public: + template <typename Rep, typename Period> + bool Wait(duration<Rep, Period> const & waitDuration) + { + unique_lock<mutex> lock(m_mutex); + + if (m_interrupted) + return false; + + m_event.wait_for(lock, waitDuration, [this]() { return m_interrupted; }); + + return true; + } + + void Interrupt(); + +private: + bool m_interrupted = false; + mutex m_mutex; + condition_variable m_event; +}; + +DECLARE_EXCEPTION(ConfigLoadError, RootException); + +// Class which loads config from local drive, checks hash +// for config on server and downloads new config if needed. +class ConfigLoader +{ +public: + explicit ConfigLoader(EditorConfigWrapper & config); + ~ConfigLoader(); + + // Static methods for production and testing. + static void LoadFromLocal(pugi::xml_document & doc); + static string GetRemoteHash(); + static void GetRemoteConfig(pugi::xml_document & doc); + static bool SaveHash(string const & hash, string const & filePath); + static string LoadHash(string const & filePath); + +private: + void LoadFromServer(); + void ResetConfig(pugi::xml_document const & doc); + + EditorConfigWrapper & m_config; + + Waiter m_waiter; + thread m_loaderThread; +}; +} // namespace editor diff --git a/editor/editor.pro b/editor/editor.pro index 8f3d5d6fc0..4cf861450c 100644 --- a/editor/editor.pro +++ b/editor/editor.pro @@ -10,6 +10,7 @@ include($$ROOT_DIR/common.pri) SOURCES += \ changeset_wrapper.cpp \ + config_loader.cpp \ editor_config.cpp \ editor_notes.cpp \ editor_storage.cpp \ @@ -23,6 +24,7 @@ SOURCES += \ HEADERS += \ changeset_wrapper.hpp \ + config_loader.hpp \ editor_config.hpp \ editor_notes.hpp \ editor_storage.hpp \ diff --git a/editor/editor_config.cpp b/editor/editor_config.cpp index 9920464136..7dcef62f94 100644 --- a/editor/editor_config.cpp +++ b/editor/editor_config.cpp @@ -1,9 +1,5 @@ #include "editor/editor_config.hpp" -#include "platform/platform.hpp" - -#include "coding/reader.hpp" - #include "base/stl_helpers.hpp" #include "std/algorithm.hpp" @@ -114,12 +110,6 @@ vector<pugi::xml_node> GetPrioritizedTypes(pugi::xml_node const & node) namespace editor { -EditorConfig::EditorConfig(string const & fileName) - : m_fileName(fileName) -{ - Reload(); -} - bool EditorConfig::GetTypeDescription(vector<string> classificatorTypes, TypeAggregatedDescription & outDesc) const { @@ -152,18 +142,15 @@ bool EditorConfig::GetTypeDescription(vector<string> classificatorTypes, vector<string> EditorConfig::GetTypesThatCanBeAdded() const { auto const xpathResult = m_document.select_nodes("/mapsme/editor/types/type[not(@can_add='no' or @editable='no')]"); + vector<string> result; for (auto const xNode : xpathResult) result.emplace_back(xNode.node().attribute("id").value()); return result; } -void EditorConfig::Reload() +void EditorConfig::SetConfig(pugi::xml_document const & doc) { - string content; - auto const reader = GetPlatform().GetReader(m_fileName); - reader->ReadAsString(content); - if (!m_document.load_buffer(content.data(), content.size())) - MYTHROW(ConfigLoadError, ("Can't parse config")); + m_document.reset(doc); } } // namespace editor diff --git a/editor/editor_config.hpp b/editor/editor_config.hpp index 399f03c10a..f4217e1371 100644 --- a/editor/editor_config.hpp +++ b/editor/editor_config.hpp @@ -2,11 +2,8 @@ #include "indexer/feature_meta.hpp" -#include "base/exception.hpp" - -#include "std/set.hpp" +#include "std/shared_ptr.hpp" #include "std/string.hpp" -#include "std/unique_ptr.hpp" #include "std/vector.hpp" #include "3party/pugixml/src/pugixml.hpp" @@ -36,27 +33,39 @@ struct TypeAggregatedDescription bool m_address = false; }; -DECLARE_EXCEPTION(ConfigLoadError, RootException); - class EditorConfig { public: - EditorConfig(string const & fileName = "editor.config"); + EditorConfig() = default; // TODO(mgsergio): Reduce overhead by matching uint32_t types instead of strings. - bool GetTypeDescription(vector<string> classificatorTypes, TypeAggregatedDescription & outDesc) const; + bool GetTypeDescription(vector<string> classificatorTypes, + TypeAggregatedDescription & outDesc) const; vector<string> GetTypesThatCanBeAdded() const; - bool EditingEnable() const; - - void Reload(); + void SetConfig(pugi::xml_document const & doc); // TODO(mgsergio): Implement this getter to avoid hard-code in XMLFeature::ApplyPatch. // It should return [[phone, contact:phone], [website, contact:website, url], ...]. //vector<vector<string>> GetAlternativeFields() const; private: - string const m_fileName; pugi::xml_document m_document; }; + +// Class which provides methods for EditorConfig concurrently using. +class EditorConfigWrapper +{ +public: + EditorConfigWrapper() = default; + + void Set(shared_ptr<EditorConfig> config) { atomic_store(&m_config, config); } + shared_ptr<EditorConfig const> Get() const { return atomic_load(&m_config); } + +private: + shared_ptr<EditorConfig> m_config = make_shared<EditorConfig>(); + + // Just in case someone tryes to pass EditorConfigWrapper by value instead of referense. + DISALLOW_COPY_AND_MOVE(EditorConfigWrapper); +}; } // namespace editor diff --git a/editor/editor_tests/config_loader_test.cpp b/editor/editor_tests/config_loader_test.cpp new file mode 100644 index 0000000000..9ad4a9faba --- /dev/null +++ b/editor/editor_tests/config_loader_test.cpp @@ -0,0 +1,62 @@ +#include "testing/testing.hpp" + +#include "editor/config_loader.hpp" +#include "editor/editor_config.hpp" + +#include "platform/platform_tests_support/scoped_file.hpp" + +#include "3party/pugixml/src/pugixml.hpp" + +namespace +{ +using namespace editor; + +void CheckGeneralTags(pugi::xml_document const & doc) +{ + auto const types = doc.select_nodes("/mapsme/editor/types"); + TEST(!types.empty(), ()); + auto const fields = doc.select_nodes("/mapsme/editor/fields"); + TEST(!fields.empty(), ()); + auto const preferred_types = doc.select_nodes("/mapsme/editor/preferred_types"); + TEST(!preferred_types.empty(), ()); +} + +UNIT_TEST(ConfigLoader_Base) +{ + EditorConfigWrapper config; + ConfigLoader loader(config); + + TEST(!config.Get()->GetTypesThatCanBeAdded().empty(), ()); +} + +UNIT_TEST(ConfigLoader_GetRemoteHash) +{ + auto const hashStr = ConfigLoader::GetRemoteHash(); + TEST_NOT_EQUAL(hashStr, "", ()); + TEST_EQUAL(hashStr, ConfigLoader::GetRemoteHash(), ()); +} + +UNIT_TEST(ConfigLoader_GetRemoteConfig) +{ + pugi::xml_document doc; + ConfigLoader::GetRemoteConfig(doc); + CheckGeneralTags(doc); +} + +UNIT_TEST(ConfigLoader_SaveLoadHash) +{ + platform::tests_support::ScopedFile sf("test.hash"); + auto const testHash = "12345 678909 87654 321 \n 32"; + + ConfigLoader::SaveHash(testHash, sf.GetFullPath()); + auto const loadedHash = ConfigLoader::LoadHash(sf.GetFullPath()); + TEST_EQUAL(testHash, loadedHash, ()); +} + +UNIT_TEST(ConfigLoader_LoadFromLocal) +{ + pugi::xml_document doc; + ConfigLoader::LoadFromLocal(doc); + CheckGeneralTags(doc); +} +} // namespace diff --git a/editor/editor_tests/editor_config_test.cpp b/editor/editor_tests/editor_config_test.cpp index 329ea1488f..92faae7725 100644 --- a/editor/editor_tests/editor_config_test.cpp +++ b/editor/editor_tests/editor_config_test.cpp @@ -1,5 +1,6 @@ #include "testing/testing.hpp" +#include "editor/config_loader.hpp" #include "editor/editor_config.hpp" #include "base/stl_helpers.hpp" @@ -20,7 +21,11 @@ UNIT_TEST(EditorConfig_TypeDescription) feature::Metadata::FMD_EMAIL }; + pugi::xml_document doc; + ConfigLoader::LoadFromLocal(doc); + EditorConfig config; + config.SetConfig(doc); { editor::TypeAggregatedDescription desc; @@ -57,9 +62,13 @@ UNIT_TEST(EditorConfig_TypeDescription) // TODO(mgsergio): Test case with priority="high" when there is one on editor.config. } -UNIT_TEST(EditorConfig_GetTypesThatGenBeAdded) +UNIT_TEST(EditorConfig_GetTypesThatCanBeAdded) { + pugi::xml_document doc; + ConfigLoader::LoadFromLocal(doc); + EditorConfig config; + config.SetConfig(doc); auto const types = config.GetTypesThatCanBeAdded(); TEST(find(begin(types), end(types), "amenity-cafe") != end(types), ()); diff --git a/editor/editor_tests/editor_tests.pro b/editor/editor_tests/editor_tests.pro index f4ead74e60..cfb2704eda 100644 --- a/editor/editor_tests/editor_tests.pro +++ b/editor/editor_tests/editor_tests.pro @@ -4,7 +4,8 @@ CONFIG -= app_bundle TEMPLATE = app ROOT_DIR = ../.. -DEPENDENCIES = editor platform_tests_support platform geometry coding base stats_client opening_hours pugixml oauthcpp +DEPENDENCIES = editor platform_tests_support platform geometry coding base \ + stats_client opening_hours pugixml oauthcpp tomcrypt include($$ROOT_DIR/common.pri) @@ -14,6 +15,7 @@ HEADERS += \ SOURCES += \ $$ROOT_DIR/testing/testingmain.cpp \ + config_loader_test.cpp \ editor_config_test.cpp \ editor_notes_test.cpp \ opening_hours_ui_test.cpp \ diff --git a/indexer/osm_editor.cpp b/indexer/osm_editor.cpp index 30a324770a..e09870dd3b 100644 --- a/indexer/osm_editor.cpp +++ b/indexer/osm_editor.cpp @@ -138,7 +138,8 @@ namespace osm // (e.g. insert/remove spaces after ';' delimeter); Editor::Editor() - : m_notes(editor::Notes::MakeNotes()) + : m_configLoader(m_config) + , m_notes(editor::Notes::MakeNotes()) , m_storage(make_unique<editor::LocalStorage>()) {} @@ -701,7 +702,7 @@ EditableProperties Editor::GetEditableProperties(FeatureType const & feature) co EditableProperties Editor::GetEditablePropertiesForTypes(feature::TypesHolder const & types) const { editor::TypeAggregatedDescription desc; - if (m_config.GetTypeDescription(types.ToObjectNames(), desc)) + if (m_config.Get()->GetTypeDescription(types.ToObjectNames(), desc)) return {desc.GetEditableFields(), desc.IsNameEditable(), desc.IsAddressEditable()}; return {}; } @@ -1062,7 +1063,7 @@ Editor::Stats Editor::GetStats() const NewFeatureCategories Editor::GetNewFeatureCategories() const { - return NewFeatureCategories(m_config); + return NewFeatureCategories(*(m_config.Get())); } FeatureID Editor::GenerateNewFeatureId(MwmSet::MwmId const & id) diff --git a/indexer/osm_editor.hpp b/indexer/osm_editor.hpp index 282f9b98ef..fd42fb965e 100644 --- a/indexer/osm_editor.hpp +++ b/indexer/osm_editor.hpp @@ -8,6 +8,7 @@ #include "indexer/mwm_set.hpp" #include "indexer/new_feature_categories.hpp" +#include "editor/config_loader.hpp" #include "editor/editor_config.hpp" #include "editor/editor_notes.hpp" #include "editor/xml_feature.hpp" @@ -238,7 +239,8 @@ private: TForEachFeaturesNearByFn m_forEachFeatureAtPointFn; /// Contains information about what and how can be edited. - editor::EditorConfig m_config; + editor::EditorConfigWrapper m_config; + editor::ConfigLoader m_configLoader; /// Notes to be sent to osm. shared_ptr<editor::Notes> m_notes; diff --git a/std/iterator.hpp b/std/iterator.hpp index 998fc33f3e..a5447e3f9b 100644 --- a/std/iterator.hpp +++ b/std/iterator.hpp @@ -15,6 +15,7 @@ using std::end; using std::insert_iterator; using std::inserter; using std::istream_iterator; +using std::istreambuf_iterator; using std::iterator_traits; using std::next; using std::reverse_iterator; diff --git a/xcode/editor/editor.xcodeproj/project.pbxproj b/xcode/editor/editor.xcodeproj/project.pbxproj index 74c669d0f8..d58e96f834 100644 --- a/xcode/editor/editor.xcodeproj/project.pbxproj +++ b/xcode/editor/editor.xcodeproj/project.pbxproj @@ -26,6 +26,8 @@ 34EB09201C5F846900F47F1F /* osm_feature_matcher.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 34EB091E1C5F846900F47F1F /* osm_feature_matcher.hpp */; }; 34FFB34C1C316A7600BFF6C3 /* server_api.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 34FFB34A1C316A7600BFF6C3 /* server_api.cpp */; }; 34FFB34D1C316A7600BFF6C3 /* server_api.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 34FFB34B1C316A7600BFF6C3 /* server_api.hpp */; }; + 3D3058741D707DBE004AC712 /* config_loader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3D3058721D707DBE004AC712 /* config_loader.cpp */; }; + 3D3058751D707DBE004AC712 /* config_loader.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 3D3058731D707DBE004AC712 /* config_loader.hpp */; }; 3D489BEF1D4F67E10052AA38 /* editor_storage.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3D489BED1D4F67E10052AA38 /* editor_storage.cpp */; }; 3D489BF01D4F67E10052AA38 /* editor_storage.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 3D489BEE1D4F67E10052AA38 /* editor_storage.hpp */; }; F60F02EE1C92CBF1003A0AF6 /* editor_notes.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F60F02EC1C92CBF1003A0AF6 /* editor_notes.cpp */; }; @@ -54,6 +56,8 @@ 34EB091E1C5F846900F47F1F /* osm_feature_matcher.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = osm_feature_matcher.hpp; sourceTree = "<group>"; }; 34FFB34A1C316A7600BFF6C3 /* server_api.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = server_api.cpp; sourceTree = "<group>"; }; 34FFB34B1C316A7600BFF6C3 /* server_api.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = server_api.hpp; sourceTree = "<group>"; }; + 3D3058721D707DBE004AC712 /* config_loader.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = config_loader.cpp; sourceTree = "<group>"; }; + 3D3058731D707DBE004AC712 /* config_loader.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = config_loader.hpp; sourceTree = "<group>"; }; 3D489BED1D4F67E10052AA38 /* editor_storage.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = editor_storage.cpp; sourceTree = "<group>"; }; 3D489BEE1D4F67E10052AA38 /* editor_storage.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = editor_storage.hpp; sourceTree = "<group>"; }; F60F02EC1C92CBF1003A0AF6 /* editor_notes.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = editor_notes.cpp; sourceTree = "<group>"; }; @@ -91,6 +95,8 @@ 341138731C15AE02002E3B3E /* Editor */ = { isa = PBXGroup; children = ( + 3D3058721D707DBE004AC712 /* config_loader.cpp */, + 3D3058731D707DBE004AC712 /* config_loader.hpp */, 3D489BED1D4F67E10052AA38 /* editor_storage.cpp */, 3D489BEE1D4F67E10052AA38 /* editor_storage.hpp */, 3441CE461CFC1D3C00CF30D4 /* user_stats.cpp */, @@ -134,6 +140,7 @@ 347C71291C295B1100BE9208 /* xml_feature.hpp in Headers */, 34583BC01C8854C100F94664 /* yes_no_unknown.hpp in Headers */, 340C20DF1C3E4DFD00111D22 /* osm_auth.hpp in Headers */, + 3D3058751D707DBE004AC712 /* config_loader.hpp in Headers */, 34527C521C89B1770015050E /* editor_config.hpp in Headers */, 3411387B1C15AE42002E3B3E /* ui2oh.hpp in Headers */, 3441CE491CFC1D3C00CF30D4 /* user_stats.hpp in Headers */, @@ -203,6 +210,7 @@ 3D489BEF1D4F67E10052AA38 /* editor_storage.cpp in Sources */, 3411387A1C15AE42002E3B3E /* ui2oh.cpp in Sources */, 340DC8291C4E71E500EAA2CC /* changeset_wrapper.cpp in Sources */, + 3D3058741D707DBE004AC712 /* config_loader.cpp in Sources */, 34EB091F1C5F846900F47F1F /* osm_feature_matcher.cpp in Sources */, 3441CE481CFC1D3C00CF30D4 /* user_stats.cpp in Sources */, 34527C511C89B1770015050E /* editor_config.cpp in Sources */, |