Commit 782f7c46 by Maarten L. Hekkelman

Support for cifv1.0 (empty category names)

parent c45d02cb
Version 5.0.1 Version 5.0.1
- Fix loading dictionaries - Fix loading dictionaries
- Support for cifv1.0 files
Version 5.0.0 Version 5.0.0
- Total rewrite of cif part - Total rewrite of cif part
......
...@@ -86,7 +86,7 @@ class category ...@@ -86,7 +86,7 @@ class category
const std::string &name() const { return m_name; } const std::string &name() const { return m_name; }
iset fields() const; iset key_fields() const;
std::set<uint16_t> key_field_indices() const; std::set<uint16_t> key_field_indices() const;
...@@ -482,6 +482,8 @@ class category ...@@ -482,6 +482,8 @@ class category
return get_column_ix(name) < m_columns.size(); return get_column_ix(name) < m_columns.size();
} }
iset get_columns() const;
// -------------------------------------------------------------------- // --------------------------------------------------------------------
void sort(std::function<int(row_handle,row_handle)> f); void sort(std::function<int(row_handle,row_handle)> f);
......
...@@ -410,7 +410,7 @@ category_index::entry *category_index::insert(entry *h, row *v) ...@@ -410,7 +410,7 @@ category_index::entry *category_index::insert(entry *h, row *v)
row_handle rh(m_category, *v); row_handle rh(m_category, *v);
std::ostringstream os; std::ostringstream os;
for (auto col : m_category.fields()) for (auto col : m_category.key_fields())
{ {
if (rh[col]) if (rh[col])
os << col << ": " << std::quoted(rh[col].text()) << "; "; os << col << ": " << std::quoted(rh[col].text()) << "; ";
...@@ -686,7 +686,17 @@ category::~category() ...@@ -686,7 +686,17 @@ category::~category()
// -------------------------------------------------------------------- // --------------------------------------------------------------------
iset category::fields() const iset category::get_columns() const
{
iset result;
for (auto &col : m_columns)
result.insert(col.m_name);
return result;
}
iset category::key_fields() const
{ {
if (m_validator == nullptr) if (m_validator == nullptr)
throw std::runtime_error("No Validator specified"); throw std::runtime_error("No Validator specified");
...@@ -1853,7 +1863,10 @@ void category::write(std::ostream &os, const std::vector<uint16_t> &order, bool ...@@ -1853,7 +1863,10 @@ void category::write(std::ostream &os, const std::vector<uint16_t> &order, bool
for (auto cix : order) for (auto cix : order)
{ {
auto &col = m_columns[cix]; auto &col = m_columns[cix];
os << '_' << m_name << '.' << col.m_name << ' ' << '\n'; os << '_';
if (not m_name.empty())
os << m_name << '.';
os << col.m_name << ' ' << '\n';
columnWidths[cix] = 2; columnWidths[cix] = 2;
} }
...@@ -1941,7 +1954,10 @@ void category::write(std::ostream &os, const std::vector<uint16_t> &order, bool ...@@ -1941,7 +1954,10 @@ void category::write(std::ostream &os, const std::vector<uint16_t> &order, bool
{ {
auto &col = m_columns[cix]; auto &col = m_columns[cix];
os << '_' << m_name << '.' << col.m_name << std::string(l - col.m_name.length() - m_name.length() - 2, ' '); os << '_';
if (not m_name.empty())
os << m_name << '.';
os << col.m_name << std::string(l - col.m_name.length() - m_name.length() - 2, ' ');
std::string_view s; std::string_view s;
auto iv = m_head->get(cix); auto iv = m_head->get(cix);
...@@ -1978,29 +1994,44 @@ bool category::operator==(const category &rhs) const ...@@ -1978,29 +1994,44 @@ bool category::operator==(const category &rhs) const
// if (tagsA != tagsB) // if (tagsA != tagsB)
// std::cout << "Unequal number of fields" << std::endl; // std::cout << "Unequal number of fields" << std::endl;
const category_validator *catValidator = nullptr;
auto validator = a.get_validator(); auto validator = a.get_validator();
auto catValidator = validator->get_validator_for_category(a.name()); if (validator != nullptr)
if (catValidator == nullptr) catValidator = validator->get_validator_for_category(a.name());
throw std::runtime_error("missing cat validator");
typedef std::function<int(std::string_view,std::string_view)> compType; typedef std::function<int(std::string_view,std::string_view)> compType;
std::vector<std::tuple<std::string,compType>> tags; std::vector<std::tuple<std::string,compType>> tags;
auto keys = catValidator->m_keys; std::vector<std::string> keys;
std::vector<size_t> keyIx; std::vector<size_t> keyIx;
for (auto& tag: a.fields()) if (catValidator == nullptr)
{ {
auto iv = catValidator->get_validator_for_item(tag); for (auto& tag: a.get_columns())
if (iv == nullptr) {
throw std::runtime_error("missing item validator"); tags.push_back(std::make_tuple(tag, [](std::string_view va, std::string_view vb) { return va.compare(vb); }));
auto tv = iv->m_type; keyIx.push_back(keys.size());
if (tv == nullptr) keys.push_back(tag);
throw std::runtime_error("missing type validator"); }
tags.push_back(std::make_tuple(tag, std::bind(&cif::type_validator::compare, tv, std::placeholders::_1, std::placeholders::_2))); }
else
auto pred = [tag](const std::string& s) -> bool { return cif::iequals(tag, s) == 0; }; {
if (find_if(keys.begin(), keys.end(), pred) == keys.end()) keys = catValidator->m_keys;
keyIx.push_back(tags.size() - 1);
for (auto& tag: a.key_fields())
{
auto iv = catValidator->get_validator_for_item(tag);
if (iv == nullptr)
throw std::runtime_error("missing item validator");
auto tv = iv->m_type;
if (tv == nullptr)
throw std::runtime_error("missing type validator");
tags.push_back(std::make_tuple(tag, std::bind(&cif::type_validator::compare, tv, std::placeholders::_1, std::placeholders::_2)));
auto pred = [tag](const std::string& s) -> bool { return cif::iequals(tag, s) == 0; };
if (find_if(keys.begin(), keys.end(), pred) == keys.end())
keyIx.push_back(tags.size() - 1);
}
} }
// a.reorderByIndex(); // a.reorderByIndex();
......
...@@ -32,7 +32,7 @@ namespace cif ...@@ -32,7 +32,7 @@ namespace cif
iset get_category_fields(const category &cat) iset get_category_fields(const category &cat)
{ {
return cat.fields(); return cat.key_fields();
} }
uint16_t get_column_ix(const category &cat, std::string_view col) uint16_t get_column_ix(const category &cat, std::string_view col)
......
...@@ -695,7 +695,8 @@ void sac_parser::parse_global() ...@@ -695,7 +695,8 @@ void sac_parser::parse_global()
void sac_parser::parse_datablock() void sac_parser::parse_datablock()
{ {
std::string cat; static const std::string kUnitializedCategory("<invalid>");
std::string cat = kUnitializedCategory; // intial value acts as a guard for empty category names
while (m_lookahead == CIFToken::LOOP or m_lookahead == CIFToken::Tag or m_lookahead == CIFToken::SAVE) while (m_lookahead == CIFToken::LOOP or m_lookahead == CIFToken::Tag or m_lookahead == CIFToken::SAVE)
{ {
...@@ -703,7 +704,7 @@ void sac_parser::parse_datablock() ...@@ -703,7 +704,7 @@ void sac_parser::parse_datablock()
{ {
case CIFToken::LOOP: case CIFToken::LOOP:
{ {
cat.clear(); // should start a new category cat = kUnitializedCategory; // should start a new category
match(CIFToken::LOOP); match(CIFToken::LOOP);
...@@ -714,7 +715,7 @@ void sac_parser::parse_datablock() ...@@ -714,7 +715,7 @@ void sac_parser::parse_datablock()
std::string catName, itemName; std::string catName, itemName;
std::tie(catName, itemName) = split_tag_name(m_token_value); std::tie(catName, itemName) = split_tag_name(m_token_value);
if (cat.empty()) if (cat == kUnitializedCategory)
{ {
produce_category(catName); produce_category(catName);
cat = catName; cat = catName;
...@@ -800,6 +801,9 @@ void parser::produce_row() ...@@ -800,6 +801,9 @@ void parser::produce_row()
if (VERBOSE >= 4) if (VERBOSE >= 4)
std::cerr << "producing row for category " << m_category->name() << std::endl; std::cerr << "producing row for category " << m_category->name() << std::endl;
if (m_category == nullptr)
error("inconsistent categories in loop_");
m_category->emplace({}); m_category->emplace({});
m_row = m_category->back(); m_row = m_category->back();
// m_row.lineNr(m_line_nr); // m_row.lineNr(m_line_nr);
...@@ -810,7 +814,7 @@ void parser::produce_item(const std::string &category, const std::string &item, ...@@ -810,7 +814,7 @@ void parser::produce_item(const std::string &category, const std::string &item,
if (VERBOSE >= 4) if (VERBOSE >= 4)
std::cerr << "producing _" << category << '.' << item << " -> " << value << std::endl; std::cerr << "producing _" << category << '.' << item << " -> " << value << std::endl;
if (not iequals(category, m_category->name())) if (m_category == nullptr or not iequals(category, m_category->name()))
error("inconsistent categories in loop_"); error("inconsistent categories in loop_");
m_row[item] = m_token_value; m_row[item] = m_token_value;
......
...@@ -1483,7 +1483,7 @@ bool Remark3Parser::parse(const std::string &expMethod, PDBRecord *r, cif::datab ...@@ -1483,7 +1483,7 @@ bool Remark3Parser::parse(const std::string &expMethod, PDBRecord *r, cif::datab
auto r1 = cat1.front(); auto r1 = cat1.front();
auto r2 = cat2.front(); auto r2 = cat2.front();
for (auto column : cat1.fields()) for (auto column : cat1.key_fields())
r2[column] = r1[column].text(); r2[column] = r1[column].text();
} }
} }
......
...@@ -224,9 +224,10 @@ std::tuple<std::string, std::string> split_tag_name(std::string_view tag) ...@@ -224,9 +224,10 @@ std::tuple<std::string, std::string> split_tag_name(std::string_view tag)
auto s = tag.find('.'); auto s = tag.find('.');
if (s == std::string::npos) if (s == std::string::npos)
throw std::runtime_error("tag does not contain dot (" + std::string{ tag } + ')'); // throw std::runtime_error("tag does not contain dot (" + std::string{ tag } + ')');
return std::tuple<std::string, std::string>{ return std::tuple<std::string, std::string>{ "", tag.substr(1) };
tag.substr(1, s - 1), tag.substr(s + 1)}; else
return std::tuple<std::string, std::string>{tag.substr(1, s - 1), tag.substr(s + 1)};
} }
// -------------------------------------------------------------------- // --------------------------------------------------------------------
......
...@@ -2970,3 +2970,91 @@ _cat_1.id_2 ...@@ -2970,3 +2970,91 @@ _cat_1.id_2
for (const auto &[key, test] : TESTS) for (const auto &[key, test] : TESTS)
BOOST_CHECK_EQUAL((bool)cat1[key], test); BOOST_CHECK_EQUAL((bool)cat1[key], test);
} }
// --------------------------------------------------------------------
BOOST_AUTO_TEST_CASE(cifv1_0_1)
{
auto f = R"(data_TEST
#
loop_
_id
_name
1 aap
2 noot
3 mies
4 ?
5 .
)"_cf;
auto &db = f.front();
auto &cat = db[""];
for (auto r : cat)
{
int id;
std::optional<std::string> name;
cif::tie(id, name) = r.get("id", "name");
switch (id)
{
case 1: BOOST_CHECK_EQUAL(*name, "aap"); break;
case 2: BOOST_CHECK_EQUAL(*name, "noot"); break;
case 3: BOOST_CHECK_EQUAL(*name, "mies"); break;
default: BOOST_CHECK(name.has_value() == false);
}
}
std::stringstream ss;
ss << db;
auto f2 = cif::file(ss);
auto &db2 = f2.front();
BOOST_TEST(db == db2);
}
// BOOST_AUTO_TEST_CASE(cifv1_0_2)
// {
// BOOST_CHECK_THROW(R"(data_TEST
// #
// _version 1.0
// loop_
// _id
// _name
// 1 aap
// 2 noot
// 3 mies
// 4 ?
// 5 .
// )"_cf, cif::parse_error);
// }
BOOST_AUTO_TEST_CASE(cifv1_0_3)
{
auto f = R"(data_TEST
#
_version 1.0
_date today
)"_cf;
auto &db = f.front();
auto &cat = db[""];
BOOST_CHECK(not cat.empty());
auto r = cat.front();
BOOST_CHECK_EQUAL(r["version"].as<std::string>(), "1.0");
BOOST_CHECK_EQUAL(r["date"].as<std::string>(), "today");
std::stringstream ss;
ss << db;
auto f2 = cif::file(ss);
auto &db2 = f2.front();
BOOST_TEST(db == db2);
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment