[generator] Fix TagReplacer.

This commit is contained in:
tatiana-yan 2019-12-27 15:56:25 +03:00 committed by Maksim Andrianov
parent f82ec33ed7
commit db753eef57
3 changed files with 131 additions and 28 deletions

View file

@ -1,3 +1,11 @@
# Format:
# <replacement> ::= <original tag> ' : ' <replacement tags list>
# <original tag> ::= <tag>
# <replacement tags list> ::= <tag>[', ' <replacement tags list>]
# <tag> ::= <key> '=' <value>
# <key> ::= string without spaces, '=' and ',' symbols
# <value> ::= string without spaces, '=' and ',' symbols
atm=yes : amenity=atm
bench=yes : amenity=bench
shelter=yes : amenity=shelter

View file

@ -2,9 +2,42 @@
#include "generator/tag_admixer.hpp"
#include "platform/platform_tests_support/scoped_file.hpp"
#include <algorithm>
#include <map>
#include <set>
#include <sstream>
#include <string>
using platform::tests_support::ScopedFile;
namespace
{
void TestReplacer(std::string const & source,
TagReplacer::Replacements const & expectedReplacements)
{
auto const filename = "test.txt";
ScopedFile sf(filename, source);
TagReplacer replacer(sf.GetFullPath());
auto const & replacements = replacer.GetReplacementsForTesting();
TEST_EQUAL(replacements.size(), expectedReplacements.size(),
(source, replacements, expectedReplacements));
for (auto const & replacement : replacements)
{
auto const it = expectedReplacements.find(replacement.first);
TEST(it != expectedReplacements.end(),
("Unexpected replacement for key", replacement.first, ":", replacement.second));
TEST_EQUAL(replacement.second.size(), it->second.size(),
("Different rules number for tag", replacement.first));
for (auto const & tag : replacement.second)
{
auto const tagIt = std::find(it->second.begin(), it->second.end(), tag);
TEST(tagIt != it->second.end(), ("Unexpected rule for tag", replacement.first));
}
}
}
} // namespace
UNIT_TEST(WaysParserTests)
{
@ -32,3 +65,43 @@ UNIT_TEST(CapitalsParserTests)
TEST(capitals.find(448768937) != capitals.end(), ());
TEST(capitals.find(140247101) == capitals.end(), ());
}
UNIT_TEST(TagsReplacer_Smoke)
{
{
std::string const source = "";
TagReplacer::Replacements replacements = {};
TestReplacer(source, replacements);
}
{
std::string const source = "aerodrome:type=international : aerodrome=international";
TagReplacer::Replacements replacements = {
{{"aerodrome:type", "international"}, {{"aerodrome", "international"}}}};
TestReplacer(source, replacements);
}
{
std::string const source =
" aerodrome:type = international : aerodrome = international ";
TagReplacer::Replacements replacements = {
{{"aerodrome:type", "international"}, {{"aerodrome", "international"}}}};
TestReplacer(source, replacements);
}
{
std::string const source = "natural=marsh : natural=wetland, wetland=marsh";
TagReplacer::Replacements replacements = {
{{"natural", "marsh"}, {{"natural", "wetland"}, {"wetland", "marsh"}}}};
TestReplacer(source, replacements);
}
{
std::string const source =
"natural = forest : natural = wood\n"
"# TODO\n"
"# natural = ridge + cliff=yes -> natural=cliff\n"
"\n"
"office=travel_agent : shop=travel_agency";
TagReplacer::Replacements replacements = {
{{"natural", "forest"}, {{"natural", "wood"}}},
{{"office", "travel_agent"}, {{"shop", "travel_agency"}}}};
TestReplacer(source, replacements);
}
}

View file

@ -147,66 +147,88 @@ private:
class TagReplacer
{
public:
using Replacements = std::map<OsmElement::Tag, std::vector<OsmElement::Tag>>;
TagReplacer() = default;
explicit TagReplacer(std::string const & filePath)
{
std::ifstream stream(filePath);
OsmElement::Tag tag;
std::vector<std::string> values;
std::string line;
size_t lineNumber = 0;
while (std::getline(stream, line))
{
if (line.empty())
++lineNumber;
strings::Trim(line);
if (line.empty() || line[0] == '#')
continue;
strings::SimpleTokenizer iter(line, " \t=,:");
if (!iter)
continue;
tag.m_key = *iter;
++iter;
if (!iter)
continue;
tag.m_value = *iter;
auto keyPos = line.find("=");
CHECK(keyPos != std::string::npos, ("Cannot find source tag key in", line));
auto key = line.substr(0, keyPos);
strings::Trim(key);
values.clear();
while (++iter)
values.push_back(*iter);
// Skip '='.
++keyPos;
auto valuePos = line.find(" : ", keyPos);
CHECK(valuePos != std::string::npos, ("Cannot find source tag value in", line));
auto value = line.substr(keyPos, valuePos - keyPos);
strings::Trim(value);
if (values.size() >= 2 && values.size() % 2 == 0)
m_entries[tag].swap(values);
// Skip ' : '.
valuePos += 3;
auto rawReplacements = line.substr(valuePos);
strings::Trim(rawReplacements);
CHECK(!rawReplacements.empty(), ("Empty replacement in", line));
auto const replacements = strings::Tokenize(rawReplacements, ",");
for (auto const & replacement : replacements)
{
auto kv = strings::Tokenize(replacement, "=");
CHECK_EQUAL(kv.size(), 2,
("Cannot parse replacement tag:", replacement, "in line", lineNumber));
strings::Trim(kv[0]);
strings::Trim(kv[1]);
m_replacements[{key, value}].emplace_back(kv[0], kv[1]);
}
}
}
TagReplacer(TagReplacer const & other) : m_entries(other.m_entries) {}
TagReplacer(TagReplacer const & other) : m_replacements(other.m_replacements) {}
TagReplacer & operator=(TagReplacer const & other)
{
if (this != &other)
m_entries = other.m_entries;
m_replacements = other.m_replacements;
return *this;
}
void Process(OsmElement & element) const
{
for (auto & tag : element.m_tags)
std::vector<OsmElement::Tag> add;
base::EraseIf(element.m_tags, [&](auto const & tag)
{
auto const it = m_entries.find(tag);
if (it != m_entries.end())
auto const it = m_replacements.find(tag);
if (it != m_replacements.end())
{
auto const & v = it->second;
tag.m_key = v[0];
tag.m_value = v[1];
for (size_t i = 2; i < v.size(); i += 2)
element.AddTag(v[i], v[i + 1]);
for (auto const & replacement : it->second)
add.push_back(replacement);
return true;
}
}
return false;
});
for (auto const & tag : add)
element.AddTag(tag);
}
Replacements const & GetReplacementsForTesting() const { return m_replacements; }
private:
std::map<OsmElement::Tag, std::vector<std::string>> m_entries;
Replacements m_replacements;
};
class OsmTagMixer