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

View File

@ -629,17 +629,17 @@ public:
/** /**
* @brief Convert the values collection to a string * @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 * @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 * @brief Fill the values collection from an extractor
*/ */
void from_string (Database *rdb, tl::Extractor &ex); void from_string (tl::Extractor &ex);
private: private:
ValueBase *mp_ptr; ValueBase *mp_ptr;
@ -687,7 +687,7 @@ public:
* *
* The order of the values matters. * 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) * @brief The const iterator (begin)
@ -760,12 +760,12 @@ public:
/** /**
* @brief Convert the values collection to a string * @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 * @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: private:
std::list <ValueWrapper> m_values; std::list <ValueWrapper> m_values;
@ -1057,7 +1057,7 @@ private:
size_t m_multiplicity; size_t m_multiplicity;
std::string m_comment; std::string m_comment;
bool m_visited; bool m_visited;
std::vector <bool> m_tag_ids; std::set <id_type> m_tag_ids;
Database *mp_database; Database *mp_database;
std::string m_image_str; std::string m_image_str;
@ -1982,11 +1982,32 @@ public:
*/ */
void clear (); 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: private:
friend class Database; friend class Database;
mutable std::map <std::pair<std::string, bool>, id_type> m_ids_for_names; mutable std::map<id_type, size_t> m_tags_per_id;
mutable std::vector <Tag> m_tags; mutable std::vector<Tag> m_tags;
}; };
/** /**

View File

@ -40,50 +40,46 @@ struct ValueConverter
{ {
typedef std::string pb_type; typedef std::string pb_type;
ValueConverter (rdb::Database *rdb) ValueConverter ()
: mp_rdb (rdb)
{ {
} }
std::string to_string (const ValueWrapper &value) const 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 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 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 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 // generation of the RDB file XML structure
static tl::XMLStruct <rdb::Database> static tl::XMLStruct <rdb::Database>
make_rdb_structure (rdb::Database *rdb) make_rdb_structure ()
{ {
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", 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::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::original_file, &rdb::Database::set_original_file, "original-file") +
@ -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::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_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_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 // Implementation of rdb::Database::save and write
// TODO: move this somewhere else - with generalized functionality // TODO: move this somewhere else - with generalized functionality
@ -144,7 +142,7 @@ void
rdb::Database::write (const std::string &fn) rdb::Database::write (const std::string &fn)
{ {
tl::OutputStream os (fn, tl::OutputStream::OM_Auto); 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) { if (tl::verbosity () >= 10) {
tl::log << "Saved RDB to " << fn; tl::log << "Saved RDB to " << fn;
@ -168,7 +166,7 @@ public:
{ {
tl::SelfTimer timer (tl::verbosity () >= 11, "Reading marker database file"); tl::SelfTimer timer (tl::verbosity () >= 11, "Reading marker database file");
tl::XMLStreamSource in (m_input_stream, tl::to_string (tr ("Reading RDB"))); 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 virtual const char *format () const

View File

@ -595,7 +595,7 @@ class RDB_TestClass < TestBase
assert_equal(db.is_modified?, false) assert_equal(db.is_modified?, false)
tag_id = db.tag_id("x") 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") db.set_tag_description(tag_id, "xdesc")
assert_equal(db.tag_description(tag_id), "xdesc") assert_equal(db.tag_description(tag_id), "xdesc")