This commit is contained in:
Matthias Koefferlein 2024-08-17 19:58:24 +02:00
parent 06815fc1dc
commit 2eef9c38d6
4 changed files with 147 additions and 99 deletions

View File

@ -417,18 +417,18 @@ ValueBase::create_from_shape (const db::Shape &shape, const db::CplxTrans &trans
// ValueWrapper implementation
std::string
ValueWrapper::to_string (const Database *rdb) const
ValueWrapper::to_string () const
{
std::string r;
r.reserve (200);
if (tag_id () > 0 && rdb) {
if (tag_id () > 0) {
r += "[";
const Tag &tag = rdb->tags ().tag (tag_id ());
if (tag.is_user_tag ()) {
auto np = rdb::Tags::name_for_id (tag_id ());
if (np.second) {
r += "#";
}
r += tl::to_word_or_quoted_string (tag.name ());
r += tl::to_word_or_quoted_string (np.first);
r += "] ";
}
@ -438,14 +438,14 @@ ValueWrapper::to_string (const Database *rdb) const
}
void
ValueWrapper::from_string (Database *rdb, const std::string &s)
ValueWrapper::from_string (const std::string &s)
{
tl::Extractor ex (s.c_str ());
from_string (rdb, ex);
from_string (ex);
}
void
ValueWrapper::from_string (Database *rdb, tl::Extractor &ex)
ValueWrapper::from_string (tl::Extractor &ex)
{
id_type tag_id = 0;
@ -456,7 +456,7 @@ ValueWrapper::from_string (Database *rdb, tl::Extractor &ex)
std::string tn;
ex.read_word_or_quoted (tn);
tag_id = rdb->tags ().tag (tn, user_tag).id ();
tag_id = rdb::Tags::id_for_name (tn, user_tag);
ex.test ("]");
@ -477,16 +477,16 @@ Values::operator= (const Values &d)
}
bool
Values::compare (const Values &other, const std::map<id_type, id_type> &tag_map, const std::map<id_type, id_type> &rev_tag_map) const
Values::compare (const Values &other, const std::set<id_type> &common_tags) const
{
Values::const_iterator a = begin (), b = other.begin ();
while (a != end () && b != other.end ()) {
id_type t12 = 0;
while (a != end () && a->tag_id () != 0) {
auto j = tag_map.find (a->tag_id ());
if (j != tag_map.end ()) {
t12 = j->second;
auto j = common_tags.find (a->tag_id ());
if (j != common_tags.end ()) {
t12 = a->tag_id ();
break;
}
++a;
@ -494,9 +494,9 @@ Values::compare (const Values &other, const std::map<id_type, id_type> &tag_map,
id_type t2 = 0;
while (b != other.end () && b->tag_id () != 0) {
auto j = rev_tag_map.find (b->tag_id ());
if (j != rev_tag_map.end ()) {
t2 = j->first;
auto j = common_tags.find (b->tag_id ());
if (j != common_tags.end ()) {
t2 = b->tag_id ();
break;
}
++b;
@ -529,7 +529,7 @@ Values::compare (const Values &other, const std::map<id_type, id_type> &tag_map,
}
std::string
Values::to_string (const Database *rdb) const
Values::to_string () const
{
std::string r;
r.reserve (200);
@ -540,7 +540,7 @@ Values::to_string (const Database *rdb) const
r += ";";
}
r += v->to_string (rdb);
r += v->to_string ();
}
@ -548,14 +548,14 @@ Values::to_string (const Database *rdb) const
}
void
Values::from_string (Database *rdb, const std::string &s)
Values::from_string (const std::string &s)
{
tl::Extractor ex (s.c_str ());
while (! ex.at_end ()) {
ValueWrapper v;
v.from_string (rdb, ex);
v.from_string (ex);
add (v);
@ -905,6 +905,41 @@ Categories::import_category (Category *category)
// ------------------------------------------------------------------------------------------
// Tags implementation
static tl::Mutex s_tag_namespace_mutex;
static std::map<std::pair<std::string, bool>, id_type> s_tag_namespace;
static std::vector<std::pair<std::string, bool> > s_tags;
const std::pair<std::string, bool> &
Tags::name_for_id (id_type tag_id)
{
tl::MutexLocker locker (&s_tag_namespace_mutex);
tl_assert (tag_id > 0 && tag_id <= s_tags.size ());
return s_tags [tag_id - 1];
}
id_type
Tags::id_for_name (const std::string &name, bool user_flag)
{
tl::MutexLocker locker (&s_tag_namespace_mutex);
auto k = std::make_pair (name, user_flag);
auto t = s_tag_namespace.find (k);
if (t != s_tag_namespace.end ()) {
return t->second;
}
id_type id = s_tags.size () + 1;
s_tag_namespace [k] = id;
s_tags.push_back (k);
return id;
}
Tag
Tags::tag_for_name (const std::string &name, bool user_flag)
{
return Tag (id_for_name (name, user_flag), name, user_flag);
}
Tags::Tags ()
{
// .. nothing yet ..
@ -913,7 +948,7 @@ Tags::Tags ()
void
Tags::clear ()
{
m_ids_for_names.clear ();
m_tags_per_id.clear ();
m_tags.clear ();
}
@ -926,26 +961,31 @@ Tags::tag (const std::string &name, bool user_tag) const
Tag &
Tags::tag (const std::string &name, bool user_tag)
{
std::map <std::pair<std::string, bool>, id_type>::const_iterator i = m_ids_for_names.find (std::make_pair (name, user_tag));
if (i == m_ids_for_names.end ()) {
i = m_ids_for_names.insert (std::make_pair (std::make_pair (name, user_tag), m_tags.size () + 1)).first;
m_tags.push_back (Tag (i->second, name, user_tag));
id_type id = id_for_name (name, user_tag);
auto i = m_tags_per_id.find (id);
if (i == m_tags_per_id.end ()) {
m_tags.push_back (Tag (id, name, user_tag));
m_tags_per_id [id] = m_tags.size () - 1;
return m_tags.back ();
} else {
return m_tags [i->second];
}
return m_tags [i->second - 1];
}
const Tag &
Tags::tag (id_type id) const
{
tl_assert (id < m_tags.size () + 1 && id > 0);
return m_tags [id - 1];
auto i = m_tags_per_id.find (id);
tl_assert (i != m_tags_per_id.end ());
return m_tags [i->second];
}
Tag &
Tags::tag (id_type id)
{
tl_assert (id < m_tags.size () + 1 && id > 0);
return m_tags [id - 1];
auto i = m_tags_per_id.find (id);
tl_assert (i != m_tags_per_id.end ());
return m_tags [i->second];
}
void
@ -958,7 +998,7 @@ Tags::import_tag (const Tag &t)
bool
Tags::has_tag (const std::string &name, bool user_tag) const
{
return m_ids_for_names.find (std::make_pair (name, user_tag)) != m_ids_for_names.end ();
return m_tags_per_id.find (id_for_name (name, user_tag)) != m_tags_per_id.end ();
}
// ------------------------------------------------------------------------------------------
@ -1006,30 +1046,25 @@ Item &Item::operator= (const Item &d)
void
Item::add_tag (id_type tag_id)
{
if (m_tag_ids.size () <= tag_id) {
m_tag_ids.resize (tag_id + 1, false);
}
m_tag_ids [tag_id] = true;
m_tag_ids.insert (tag_id);
}
void
Item::remove_tag (id_type tag_id)
{
if (m_tag_ids.size () > tag_id) {
m_tag_ids [tag_id] = false;
}
m_tag_ids.erase (tag_id);
}
void
Item::remove_tags ()
{
m_tag_ids = std::vector <bool> ();
m_tag_ids.clear ();
}
bool
Item::has_tag (id_type tag_id) const
{
return m_tag_ids.size () > tag_id && m_tag_ids [tag_id];
return m_tag_ids.find (tag_id) != m_tag_ids.end ();
}
std::string
@ -1082,13 +1117,12 @@ Item::tag_str () const
if (! m_tag_ids.empty ()) {
id_type tag_id = 0;
for (std::vector<bool>::const_iterator t = m_tag_ids.begin (); t != m_tag_ids.end (); ++t, ++tag_id) {
for (std::set<id_type>::const_iterator t = m_tag_ids.begin (); t != m_tag_ids.end (); ++t) {
if (*t) {
if (! r.empty ()) {
r += ",";
}
const Tag &tag = mp_database->tags ().tag (tag_id);
const Tag &tag = mp_database->tags ().tag (*t);
if (tag.is_user_tag ()) {
r += "#";
}
@ -1801,33 +1835,30 @@ namespace
class ValueMapEntryCompare
{
public:
ValueMapEntryCompare (const std::map<id_type, id_type> &tag2tag, const std::map<id_type, id_type> &rev_tag2tag)
ValueMapEntryCompare (const std::set<id_type> &common_tags)
{
mp_tag2tag = &tag2tag;
mp_rev_tag2tag = &rev_tag2tag;
mp_common_tags = &common_tags;
}
bool operator() (const Item *a, const Item *b) const
{
return a->values ().compare (b->values (), *mp_tag2tag, *mp_rev_tag2tag);
return a->values ().compare (b->values (), *mp_common_tags);
}
private:
const std::map<id_type, id_type> *mp_tag2tag;
const std::map<id_type, id_type> *mp_rev_tag2tag;
const std::set<id_type> *mp_common_tags;
};
class ValueMapEntry
{
public:
ValueMapEntry ()
: mp_tag2tag (0), mp_rev_tag2tag (0)
: mp_common_tags (0)
{ }
void build (const rdb::Database &rdb, id_type cell_id, id_type cat_id, const std::map<id_type, id_type> &tag2tag, const std::map<id_type, id_type> &rev_tag2tag)
void build (const rdb::Database &rdb, id_type cell_id, id_type cat_id, const std::set<id_type> &common_tags)
{
mp_tag2tag = &tag2tag;
mp_rev_tag2tag = &rev_tag2tag;
mp_common_tags = &common_tags;
auto i2i = rdb.items_by_cell_and_category (cell_id, cat_id);
@ -1841,13 +1872,13 @@ namespace
m_items.push_back ((*i).operator-> ());
}
ValueMapEntryCompare cmp (*mp_tag2tag, *mp_rev_tag2tag);
ValueMapEntryCompare cmp (*mp_common_tags);
std::sort (m_items.begin (), m_items.end (), cmp);
}
const Item *find (const rdb::Item &item) const
{
ValueMapEntryCompare cmp (*mp_tag2tag, *mp_rev_tag2tag);
ValueMapEntryCompare cmp (*mp_common_tags);
auto i = std::lower_bound (m_items.begin (), m_items.end (), &item, cmp);
if (i == m_items.end ()) {
@ -1863,8 +1894,7 @@ namespace
public:
std::vector<const Item *> m_items;
const std::map<id_type, id_type> *mp_tag2tag;
const std::map<id_type, id_type> *mp_rev_tag2tag;
const std::set<id_type> *mp_common_tags;
};
}
@ -1901,16 +1931,15 @@ Database::apply (const rdb::Database &other)
map_category (*c, *this, cat2cat);
}
std::map<std::string, id_type> tags_by_name;
std::set<id_type> t1;
for (auto c = tags ().begin_tags (); c != tags ().end_tags (); ++c) {
tags_by_name.insert (std::make_pair (c->name (), c->id ()));
t1.insert (c->id ());
}
std::set<id_type> common_tags;
for (auto c = other.tags ().begin_tags (); c != other.tags ().end_tags (); ++c) {
auto t = tags_by_name.find (c->name ());
if (t != tags_by_name.end ()) {
tag2tag.insert (std::make_pair (t->second, c->id ()));
rev_tag2tag.insert (std::make_pair (c->id (), t->second));
if (t1.find (c->id ()) != t1.end ()) {
common_tags.insert (c->id ());
}
}
@ -1932,7 +1961,7 @@ Database::apply (const rdb::Database &other)
auto vmap = value_map.find (std::make_pair (icell->second, icat->second));
if (vmap == value_map.end ()) {
vmap = value_map.insert (std::make_pair (std::make_pair (icell->second, icat->second), ValueMapEntry ())).first;
vmap->second.build (other, icell->second, icat->second, tag2tag, rev_tag2tag);
vmap->second.build (other, icell->second, icat->second, common_tags);
}
// find a value in the reference DB

View File

@ -629,17 +629,17 @@ public:
/**
* @brief Convert the values collection to a string
*/
std::string to_string (const Database *rdb = 0) const;
std::string to_string () const;
/**
* @brief Fill the values collection from the string
*/
void from_string (Database *rdb, const std::string &s);
void from_string (const std::string &s);
/**
* @brief Fill the values collection from an extractor
*/
void from_string (Database *rdb, tl::Extractor &ex);
void from_string (tl::Extractor &ex);
private:
ValueBase *mp_ptr;
@ -687,7 +687,7 @@ public:
*
* The order of the values matters.
*/
bool compare (const Values &other, const std::map<id_type, id_type> &tag_map, const std::map<id_type, id_type> &rev_tag_map) const;
bool compare (const Values &other, const std::set<id_type> &common_tags) const;
/**
* @brief The const iterator (begin)
@ -760,12 +760,12 @@ public:
/**
* @brief Convert the values collection to a string
*/
std::string to_string (const Database *rdb) const;
std::string to_string () const;
/**
* @brief Fill the values collection from the string
*/
void from_string (Database *rdb, const std::string &s);
void from_string (const std::string &s);
private:
std::list <ValueWrapper> m_values;
@ -1057,7 +1057,7 @@ private:
size_t m_multiplicity;
std::string m_comment;
bool m_visited;
std::vector <bool> m_tag_ids;
std::set <id_type> m_tag_ids;
Database *mp_database;
std::string m_image_str;
@ -1982,11 +1982,32 @@ public:
*/
void clear ();
/**
* @brief Gets the name and user flag for a tag ID
*
* This method will assert if the tag ID is not valid in the global namespace.
*/
static const std::pair<std::string, bool> &name_for_id (id_type tag_id);
/**
* @brief Gets the id for a given name and user flag
*
* This will pull the ID from the global namespace.
*/
static id_type id_for_name (const std::string &name, bool user_flag);
/**
* @brief Gets a tag object for a given name and user flag
*
* This will create the tag from the global namespace.
*/
static Tag tag_for_name (const std::string &name, bool user_flag);
private:
friend class Database;
mutable std::map <std::pair<std::string, bool>, id_type> m_ids_for_names;
mutable std::vector <Tag> m_tags;
mutable std::map<id_type, size_t> m_tags_per_id;
mutable std::vector<Tag> m_tags;
};
/**

View File

@ -40,51 +40,47 @@ struct ValueConverter
{
typedef std::string pb_type;
ValueConverter (rdb::Database *rdb)
: mp_rdb (rdb)
ValueConverter ()
{
}
std::string to_string (const ValueWrapper &value) const
{
return value.to_string (mp_rdb);
return value.to_string ();
}
void from_string (const std::string &s, ValueWrapper &value) const
{
value.from_string (mp_rdb, s);
value.from_string (s);
}
std::string pb_encode (const ValueWrapper &value) const
{
return value.to_string (mp_rdb);
return value.to_string ();
}
void pb_decode (const std::string &s, ValueWrapper &value) const
{
value.from_string (mp_rdb, s);
value.from_string (s);
}
private:
rdb::Database *mp_rdb;
};
static
tl::XMLElementList categories_format =
tl::make_element_with_parent_ref<rdb::Category, rdb::Categories::const_iterator, rdb::Categories> (&rdb::Categories::begin, &rdb::Categories::end, &rdb::Categories::import_category, "category",
tl::make_member<std::string, rdb::Category> (&rdb::Category::name, &rdb::Category::set_name, "name") +
tl::make_member<std::string, rdb::Category> (&rdb::Category::description, &rdb::Category::set_description, "description") +
tl::make_element_with_parent_ref<rdb::Categories, rdb::Category> (&rdb::Category::sub_categories, &rdb::Category::import_sub_categories, "categories",
&categories_format
)
)
;
// generation of the RDB file XML structure
static tl::XMLStruct <rdb::Database>
make_rdb_structure (rdb::Database *rdb)
make_rdb_structure ()
{
return tl::XMLStruct <rdb::Database>("report-database",
static
tl::XMLElementList categories_format =
tl::make_element_with_parent_ref<rdb::Category, rdb::Categories::const_iterator, rdb::Categories> (&rdb::Categories::begin, &rdb::Categories::end, &rdb::Categories::import_category, "category",
tl::make_member<std::string, rdb::Category> (&rdb::Category::name, &rdb::Category::set_name, "name") +
tl::make_member<std::string, rdb::Category> (&rdb::Category::description, &rdb::Category::set_description, "description") +
tl::make_element_with_parent_ref<rdb::Categories, rdb::Category> (&rdb::Category::sub_categories, &rdb::Category::import_sub_categories, "categories",
&categories_format
)
)
;
return tl::XMLStruct <rdb::Database>("report-database",
tl::make_member<std::string, rdb::Database> (&rdb::Database::description, &rdb::Database::set_description, "description") +
tl::make_member<std::string, rdb::Database> (&rdb::Database::original_file, &rdb::Database::set_original_file, "original-file") +
tl::make_member<std::string, rdb::Database> (&rdb::Database::generator, &rdb::Database::set_generator, "generator") +
@ -122,13 +118,15 @@ make_rdb_structure (rdb::Database *rdb)
tl::make_member<std::string, rdb::Item> (&rdb::Item::comment, &rdb::Item::set_comment, "comment") +
tl::make_member<std::string, rdb::Item> (&rdb::Item::image_str, &rdb::Item::set_image_str, "image") +
tl::make_element<rdb::Values, rdb::Item> (&rdb::Item::values, &rdb::Item::set_values, "values",
tl::make_member<rdb::ValueWrapper, rdb::Values::const_iterator, rdb::Values> (&rdb::Values::begin, &rdb::Values::end, &rdb::Values::add, "value", ValueConverter (rdb))
tl::make_member<rdb::ValueWrapper, rdb::Values::const_iterator, rdb::Values> (&rdb::Values::begin, &rdb::Values::end, &rdb::Values::add, "value", ValueConverter ())
)
)
)
);
}
static tl::XMLStruct <rdb::Database> s_rdb_struct = make_rdb_structure ();
// -------------------------------------------------------------
// Implementation of rdb::Database::save and write
// TODO: move this somewhere else - with generalized functionality
@ -144,7 +142,7 @@ void
rdb::Database::write (const std::string &fn)
{
tl::OutputStream os (fn, tl::OutputStream::OM_Auto);
make_rdb_structure (this).write (os, *this);
s_rdb_struct.write (os, *this);
if (tl::verbosity () >= 10) {
tl::log << "Saved RDB to " << fn;
@ -168,7 +166,7 @@ public:
{
tl::SelfTimer timer (tl::verbosity () >= 11, "Reading marker database file");
tl::XMLStreamSource in (m_input_stream, tl::to_string (tr ("Reading RDB")));
make_rdb_structure (&db).parse (in, db);
s_rdb_struct.parse (in, db);
}
virtual const char *format () const

View File

@ -595,7 +595,7 @@ class RDB_TestClass < TestBase
assert_equal(db.is_modified?, false)
tag_id = db.tag_id("x")
assert_equal(tag_id, 1)
assert_equal(db.tag_name(tag_id), "x")
db.set_tag_description(tag_id, "xdesc")
assert_equal(db.tag_description(tag_id), "xdesc")