forked from organicmaps/organicmaps
[new downloader] Adding funtionality to SimpleTree and refactoring SimpleTree class. Renaming siblings to children. Fixing tests.
This commit is contained in:
parent
68b411a731
commit
2bbfe6a3ea
5 changed files with 152 additions and 88 deletions
|
@ -130,7 +130,7 @@ namespace update
|
|||
|
||||
// using move semantics for mwmFiles
|
||||
SizeUpdater sizeUpdater(dataDir, mwmFiles);
|
||||
countries.ForEachChildren(sizeUpdater);
|
||||
countries.ForEachDescendant(sizeUpdater);
|
||||
}
|
||||
|
||||
storage::SaveCountries(my::TodayAsYYMMDD(), countries, jsonBuffer);
|
||||
|
|
|
@ -185,28 +185,28 @@ void LoadCountryCode2File(string const & jsonBuffer, multimap<string, string> &
|
|||
template <class T>
|
||||
void SaveImpl(T const & v, json_t * jParent)
|
||||
{
|
||||
size_t const siblingsCount = v.SiblingsCount();
|
||||
CHECK_GREATER(siblingsCount, 0, ());
|
||||
size_t const childrenCount = v.ChildrenCount();
|
||||
CHECK_GREATER(childrenCount, 0, ());
|
||||
|
||||
my::JsonHandle jArray;
|
||||
jArray.AttachNew(json_array());
|
||||
for (size_t i = 0; i < siblingsCount; ++i)
|
||||
for (size_t i = 0; i < childrenCount; ++i)
|
||||
{
|
||||
my::JsonHandle jCountry;
|
||||
jCountry.AttachNew(json_object());
|
||||
|
||||
string const strName = v[i].Value().Name();
|
||||
string const strName = v.Child(i).Value().Name();
|
||||
CHECK(!strName.empty(), ("Empty country name?"));
|
||||
json_object_set_new(jCountry.get(), "n", json_string(strName.c_str()));
|
||||
string const strFlag = v[i].Value().Flag();
|
||||
string const strFlag = v.Child(i).Value().Flag();
|
||||
if (!strFlag.empty())
|
||||
json_object_set_new(jCountry.get(), "c", json_string(strFlag.c_str()));
|
||||
|
||||
size_t countriesCount = v[i].Value().GetFilesCount();
|
||||
size_t countriesCount = v.Child(i).Value().GetFilesCount();
|
||||
ASSERT_LESS_OR_EQUAL(countriesCount, 1, ());
|
||||
if (countriesCount > 0)
|
||||
{
|
||||
CountryFile const & file = v[i].Value().GetFile();
|
||||
CountryFile const & file = v.Child(i).Value().GetFile();
|
||||
string const & strFile = file.GetNameWithoutExt();
|
||||
if (strFile != strName)
|
||||
json_object_set_new(jCountry.get(), "f", json_string(strFile.c_str()));
|
||||
|
@ -215,8 +215,8 @@ void SaveImpl(T const & v, json_t * jParent)
|
|||
json_integer(file.GetRemoteSize(MapOptions::CarRouting)));
|
||||
}
|
||||
|
||||
if (v[i].SiblingsCount())
|
||||
SaveImpl(v[i], jCountry.get());
|
||||
if (v.Child(i).ChildrenCount())
|
||||
SaveImpl(v.Child(i), jCountry.get());
|
||||
|
||||
json_array_append(jArray.get(), jCountry.get());
|
||||
}
|
||||
|
|
|
@ -1,15 +1,23 @@
|
|||
#pragma once
|
||||
|
||||
#include "std/vector.hpp"
|
||||
#include "base/assert.hpp"
|
||||
|
||||
#include "std/algorithm.hpp"
|
||||
#include "std/vector.hpp"
|
||||
|
||||
template <class T>
|
||||
class SimpleTree
|
||||
{
|
||||
typedef std::vector<SimpleTree<T> > internal_container_type;
|
||||
|
||||
T m_value;
|
||||
internal_container_type m_siblings;
|
||||
// @TODO(bykoianko) Remove on unnecessary methods of SimpleTree if any.
|
||||
|
||||
/// \brief m_children contains all first generation descendants of the node.
|
||||
vector<SimpleTree<T>> m_children;
|
||||
|
||||
static bool IsEqual(T const & v1, T const & v2)
|
||||
{
|
||||
return !(v1 < v2) && !(v2 < v1);
|
||||
}
|
||||
|
||||
public:
|
||||
SimpleTree(T const & value = T()) : m_value(value)
|
||||
|
@ -32,22 +40,22 @@ public:
|
|||
T & AddAtDepth(int level, T const & value)
|
||||
{
|
||||
SimpleTree<T> * node = this;
|
||||
while (level-- > 0 && !node->m_siblings.empty())
|
||||
node = &node->m_siblings.back();
|
||||
while (level-- > 0 && !node->m_children.empty())
|
||||
node = &node->m_children.back();
|
||||
return node->Add(value);
|
||||
}
|
||||
|
||||
/// @return reference is valid only up to the next tree structure modification
|
||||
T & Add(T const & value)
|
||||
{
|
||||
m_siblings.push_back(SimpleTree(value));
|
||||
return m_siblings.back().Value();
|
||||
m_children.emplace_back(SimpleTree(value));
|
||||
return m_children.back().Value();
|
||||
}
|
||||
|
||||
/// Deletes all children and makes tree empty
|
||||
void Clear()
|
||||
{
|
||||
m_siblings.clear();
|
||||
m_children.clear();
|
||||
}
|
||||
|
||||
bool operator<(SimpleTree<T> const & other) const
|
||||
|
@ -55,56 +63,112 @@ public:
|
|||
return Value() < other.Value();
|
||||
}
|
||||
|
||||
/// sorts siblings independently on each level by default
|
||||
void Sort(bool onlySiblings = false)
|
||||
/// sorts children independently on each level by default
|
||||
void Sort()
|
||||
{
|
||||
std::sort(m_siblings.begin(), m_siblings.end());
|
||||
if (!onlySiblings)
|
||||
for (typename internal_container_type::iterator it = m_siblings.begin(); it != m_siblings.end(); ++it)
|
||||
it->Sort(false);
|
||||
sort(m_children.begin(), m_children.end());
|
||||
for (auto & child : m_children)
|
||||
child.Sort();
|
||||
}
|
||||
|
||||
SimpleTree<T> const & operator[](size_t index) const
|
||||
/// \brief Checks all nodes in tree to find an equal one. If there're several equal nodes
|
||||
/// returns the first found.
|
||||
/// \returns a poiter item in the tree if found and nullptr otherwise.
|
||||
/// @TODO(bykoianko) The complexity of the method is O(n). But the structure (tree) is built on the start of the program
|
||||
/// and then actively used on run time. This method (and class) should be redesigned to make the function work faster.
|
||||
/// A hash table is being planned to use.
|
||||
SimpleTree<T> const * const Find(T const & value) const
|
||||
{
|
||||
return m_siblings.at(index);
|
||||
}
|
||||
if (IsEqual(m_value, value))
|
||||
return this;
|
||||
|
||||
size_t SiblingsCount() const
|
||||
{
|
||||
return m_siblings.size();
|
||||
}
|
||||
|
||||
template <class TFunctor>
|
||||
void ForEachSibling(TFunctor & f)
|
||||
{
|
||||
for (typename internal_container_type::iterator it = m_siblings.begin(); it != m_siblings.end(); ++it)
|
||||
f(*it);
|
||||
}
|
||||
|
||||
template <class TFunctor>
|
||||
void ForEachSibling(TFunctor & f) const
|
||||
{
|
||||
for (typename internal_container_type::const_iterator it = m_siblings.begin(); it != m_siblings.end(); ++it)
|
||||
f(*it);
|
||||
}
|
||||
|
||||
template <class TFunctor>
|
||||
void ForEachChildren(TFunctor & f)
|
||||
{
|
||||
for (typename internal_container_type::iterator it = m_siblings.begin(); it != m_siblings.end(); ++it)
|
||||
for (auto const & child : m_children)
|
||||
{
|
||||
f(*it);
|
||||
it->ForEachChildren(f);
|
||||
SimpleTree<T> const * const found = child.Find(value);
|
||||
if (found != nullptr)
|
||||
return found;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/// \brief Find only leaves.
|
||||
/// \note It's a termprary fucntion for compatablity with old countries.txt.
|
||||
/// When new countries.txt with unique ids will be added FindLeaf will be removed
|
||||
/// and Find will be used intead.
|
||||
/// @TODO(bykoianko) Remove this method on countries.txt update.
|
||||
SimpleTree<T> const * const FindLeaf(T const & value) const
|
||||
{
|
||||
if (IsEqual(m_value, value) && m_children.empty())
|
||||
return this; // It's a leaf.
|
||||
|
||||
for (auto const & child : m_children)
|
||||
{
|
||||
SimpleTree<T> const * const found = child.FindLeaf(value);
|
||||
if (found != nullptr)
|
||||
return found;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
SimpleTree<T> const & Child(size_t index) const
|
||||
{
|
||||
return m_children.at(index);
|
||||
}
|
||||
|
||||
size_t ChildrenCount() const
|
||||
{
|
||||
return m_children.size();
|
||||
}
|
||||
|
||||
/// \brief Calls functor f for each first generation descendant of the node.
|
||||
template <class TFunctor>
|
||||
void ForEachChild(TFunctor && f)
|
||||
{
|
||||
for (auto const & child : m_children)
|
||||
f(child);
|
||||
}
|
||||
|
||||
template <class TFunctor>
|
||||
void ForEachChild(TFunctor && f) const
|
||||
{
|
||||
for (auto const & child : m_children)
|
||||
f(child);
|
||||
}
|
||||
|
||||
/// \brief Calls functor f for all nodes (add descendant) in the tree.
|
||||
template <class TFunctor>
|
||||
void ForEachDescendant(TFunctor && f)
|
||||
{
|
||||
for (auto & child : m_children)
|
||||
{
|
||||
f(child);
|
||||
child.ForEachDescendant(f);
|
||||
}
|
||||
}
|
||||
|
||||
template <class TFunctor>
|
||||
void ForEachChildren(TFunctor & f) const
|
||||
void ForEachDescendant(TFunctor && f) const
|
||||
{
|
||||
for (typename internal_container_type::const_iterator it = m_siblings.begin(); it != m_siblings.end(); ++it)
|
||||
for (auto const & child: m_children)
|
||||
{
|
||||
f(*it);
|
||||
it->ForEachChildren(f);
|
||||
f(child);
|
||||
child.ForEachDescendant(f);
|
||||
}
|
||||
}
|
||||
|
||||
template <class TFunctor>
|
||||
void ForEachInSubtree(TFunctor && f)
|
||||
{
|
||||
f(*this);
|
||||
for (auto const & child: m_children)
|
||||
child.ForEachInSubtree(f);
|
||||
}
|
||||
|
||||
template <class TFunctor>
|
||||
void ForEachInSubtree(TFunctor && f) const
|
||||
{
|
||||
f(*this);
|
||||
for (auto const & child: m_children)
|
||||
child.ForEachInSubtree(f);
|
||||
}
|
||||
};
|
||||
|
|
|
@ -205,19 +205,19 @@ size_t Storage::GetDownloadedFilesCount() const
|
|||
CountriesContainerT const & NodeFromIndex(CountriesContainerT const & root, TIndex const & index)
|
||||
{
|
||||
// complex logic to avoid [] out_of_bounds exceptions
|
||||
if (index.m_group == TIndex::INVALID || index.m_group >= static_cast<int>(root.SiblingsCount()))
|
||||
if (index.m_group == TIndex::INVALID || index.m_group >= static_cast<int>(root.ChildrenCount()))
|
||||
return root;
|
||||
if (index.m_country == TIndex::INVALID ||
|
||||
index.m_country >= static_cast<int>(root[index.m_group].SiblingsCount()))
|
||||
index.m_country >= static_cast<int>(root.Child(index.m_group).ChildrenCount()))
|
||||
{
|
||||
return root[index.m_group];
|
||||
return root.Child(index.m_group);
|
||||
}
|
||||
if (index.m_region == TIndex::INVALID ||
|
||||
index.m_region >= static_cast<int>(root[index.m_group][index.m_country].SiblingsCount()))
|
||||
index.m_region >= static_cast<int>(root.Child(index.m_group).Child(index.m_country).ChildrenCount()))
|
||||
{
|
||||
return root[index.m_group][index.m_country];
|
||||
return root.Child(index.m_group).Child(index.m_country);
|
||||
}
|
||||
return root[index.m_group][index.m_country][index.m_region];
|
||||
return root.Child(index.m_group).Child(index.m_country).Child(index.m_region);
|
||||
}
|
||||
|
||||
Country const & Storage::CountryByIndex(TIndex const & index) const
|
||||
|
@ -234,7 +234,7 @@ void Storage::GetGroupAndCountry(TIndex const & index, string & group, string &
|
|||
|
||||
size_t Storage::CountriesCount(TIndex const & index) const
|
||||
{
|
||||
return NodeFromIndex(m_countries, index).SiblingsCount();
|
||||
return NodeFromIndex(m_countries, index).ChildrenCount();
|
||||
}
|
||||
|
||||
string const & Storage::CountryName(TIndex const & index) const
|
||||
|
@ -487,7 +487,7 @@ void Storage::LoadCountriesFile(bool forceReload)
|
|||
if (forceReload)
|
||||
m_countries.Clear();
|
||||
|
||||
if (m_countries.SiblingsCount() == 0)
|
||||
if (m_countries.ChildrenCount() == 0)
|
||||
{
|
||||
string json;
|
||||
string const name = migrate::NeedMigrate() ? COUNTRIES_FILE : COUNTRIES_MIGRATE_FILE;
|
||||
|
@ -669,19 +669,19 @@ TIndex Storage::FindIndexByFile(string const & name) const
|
|||
{
|
||||
EqualFileName fn(name);
|
||||
|
||||
for (size_t i = 0; i < m_countries.SiblingsCount(); ++i)
|
||||
for (size_t i = 0; i < m_countries.ChildrenCount(); ++i)
|
||||
{
|
||||
if (fn(m_countries[i]))
|
||||
if (fn(m_countries.Child(i)))
|
||||
return TIndex(static_cast<int>(i));
|
||||
|
||||
for (size_t j = 0; j < m_countries[i].SiblingsCount(); ++j)
|
||||
for (size_t j = 0; j < m_countries.Child(i).ChildrenCount(); ++j)
|
||||
{
|
||||
if (fn(m_countries[i][j]))
|
||||
if (fn(m_countries.Child(i).Child(j)))
|
||||
return TIndex(static_cast<int>(i), static_cast<int>(j));
|
||||
|
||||
for (size_t k = 0; k < m_countries[i][j].SiblingsCount(); ++k)
|
||||
for (size_t k = 0; k < m_countries.Child(i).Child(j).ChildrenCount(); ++k)
|
||||
{
|
||||
if (fn(m_countries[i][j][k]))
|
||||
if (fn(m_countries.Child(i).Child(j).Child(k)))
|
||||
return TIndex(static_cast<int>(i), static_cast<int>(j), static_cast<int>(k));
|
||||
}
|
||||
}
|
||||
|
@ -695,19 +695,19 @@ vector<TIndex> Storage::FindAllIndexesByFile(string const & name) const
|
|||
EqualFileName fn(name);
|
||||
vector<TIndex> res;
|
||||
|
||||
for (size_t i = 0; i < m_countries.SiblingsCount(); ++i)
|
||||
for (size_t i = 0; i < m_countries.ChildrenCount(); ++i)
|
||||
{
|
||||
if (fn(m_countries[i]))
|
||||
if (fn(m_countries.Child(i)))
|
||||
res.emplace_back(static_cast<int>(i));
|
||||
|
||||
for (size_t j = 0; j < m_countries[i].SiblingsCount(); ++j)
|
||||
for (size_t j = 0; j < m_countries.Child(i).ChildrenCount(); ++j)
|
||||
{
|
||||
if (fn(m_countries[i][j]))
|
||||
if (fn(m_countries.Child(i).Child(j)))
|
||||
res.emplace_back(static_cast<int>(i), static_cast<int>(j));
|
||||
|
||||
for (size_t k = 0; k < m_countries[i][j].SiblingsCount(); ++k)
|
||||
for (size_t k = 0; k < m_countries.Child(i).Child(j).ChildrenCount(); ++k)
|
||||
{
|
||||
if (fn(m_countries[i][j][k]))
|
||||
if (fn(m_countries.Child(i).Child(j).Child(k)))
|
||||
res.emplace_back(static_cast<int>(i), static_cast<int>(j), static_cast<int>(k));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -35,25 +35,25 @@ UNIT_TEST(SimpleTree_Smoke)
|
|||
|
||||
tree.Sort();
|
||||
// test sorting
|
||||
TEST_EQUAL(tree[0].Value(), 1, ());
|
||||
TEST_EQUAL(tree[1].Value(), 2, ());
|
||||
TEST_EQUAL(tree[2].Value(), 3, ());
|
||||
TEST_EQUAL(tree[3].Value(), 4, ());
|
||||
TEST_EQUAL(tree[4].Value(), 5, ());
|
||||
TEST_EQUAL(tree[0][0].Value(), 10, ());
|
||||
TEST_EQUAL(tree[0][1].Value(), 20, ());
|
||||
TEST_EQUAL(tree[0][2].Value(), 30, ());
|
||||
TEST_EQUAL(tree.Child(0).Value(), 1, ());
|
||||
TEST_EQUAL(tree.Child(1).Value(), 2, ());
|
||||
TEST_EQUAL(tree.Child(2).Value(), 3, ());
|
||||
TEST_EQUAL(tree.Child(3).Value(), 4, ());
|
||||
TEST_EQUAL(tree.Child(4).Value(), 5, ());
|
||||
TEST_EQUAL(tree.Child(0).Child(0).Value(), 10, ());
|
||||
TEST_EQUAL(tree.Child(0).Child(1).Value(), 20, ());
|
||||
TEST_EQUAL(tree.Child(0).Child(2).Value(), 30, ());
|
||||
|
||||
Calculator<TreeT> c1;
|
||||
tree.ForEachSibling(c1);
|
||||
tree.ForEachChild(c1);
|
||||
TEST_EQUAL(c1.count, 5, ());
|
||||
|
||||
Calculator<TreeT> c2;
|
||||
tree.ForEachChildren(c2);
|
||||
tree.ForEachDescendant(c2);
|
||||
TEST_EQUAL(c2.count, 8, ());
|
||||
|
||||
tree.Clear();
|
||||
Calculator<TreeT> c3;
|
||||
tree.ForEachChildren(c3);
|
||||
tree.ForEachDescendant(c3);
|
||||
TEST_EQUAL(c3.count, 0, ("Tree should be empty"));
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue