Commit 4dd4f663 by Maarten L. Hekkelman

backup

parent 04b7828a
...@@ -54,88 +54,25 @@ class category ...@@ -54,88 +54,25 @@ class category
category() = default; category() = default;
category(std::string_view name) category(std::string_view name);
: m_name(name)
{
}
category(const category &rhs) category(const category &rhs);
: m_name(rhs.m_name)
, m_columns(rhs.m_columns)
{
for (auto r = rhs.m_head; r != nullptr; r = r->m_next)
insert_impl(end(), clone_row(*r));
}
category(category &&rhs) category(category &&rhs);
: m_name(std::move(rhs.m_name))
, m_columns(std::move(rhs.m_columns))
, m_head(rhs.m_head)
, m_tail(rhs.m_tail)
{
rhs.m_head = nullptr;
rhs.m_tail = nullptr;
}
category &operator=(const category &rhs) category &operator=(const category &rhs);
{
if (this != &rhs)
{
if (not empty())
clear();
m_name = rhs.m_name; category &operator=(category &&rhs);
m_columns = rhs.m_columns;
for (auto r = rhs.m_head; r != nullptr; r = r->m_next) ~category();
insert_impl(cend(), clone_row(*r));
}
return *this;
}
category &operator=(category &&rhs)
{
if (this != &rhs)
{
if (not empty())
clear();
m_name = std::move(rhs.m_name);
m_columns = std::move(rhs.m_columns);
m_head = rhs.m_head;
m_tail = rhs.m_tail;
rhs.m_head = rhs.m_tail = nullptr;
}
return *this;
}
~category()
{
clear();
}
// -------------------------------------------------------------------- // --------------------------------------------------------------------
const std::string &name() const { return m_name; } const std::string &name() const { return m_name; }
iset fields() const iset fields() const;
{
if (m_validator == nullptr)
throw std::runtime_error("No Validator specified");
if (m_cat_validator == nullptr)
m_validator->report_error("undefined Category", true);
iset result; std::set<uint16_t> key_field_indices() const;
for (auto &iv : m_cat_validator->m_item_validators)
result.insert(iv.m_tag);
return result;
}
void set_validator(const validator *v, datablock &db); void set_validator(const validator *v, datablock &db);
void update_links(datablock &db); void update_links(datablock &db);
...@@ -374,6 +311,20 @@ class category ...@@ -374,6 +311,20 @@ class category
} }
// -------------------------------------------------------------------- // --------------------------------------------------------------------
bool has_children(row_handle r) const;
bool has_parents(row_handle r) const;
std::vector<row_handle> get_children(row_handle r, category &childCat);
// std::vector<row_handle> getChildren(row_handle r, const char *childCat);
std::vector<row_handle> get_parents(row_handle r, category &parentCat);
// std::vector<row_handle> getParents(row_handle r, const char *parentCat);
std::vector<row_handle> get_linked(row_handle r, category &cat);
// std::vector<row_handle> getLinked(row_handle r, const char *cat);
// --------------------------------------------------------------------
// void insert(const_iterator pos, const row &row) // void insert(const_iterator pos, const row &row)
// { // {
...@@ -500,7 +451,7 @@ class category ...@@ -500,7 +451,7 @@ class category
{ {
auto iv = m_cat_validator->get_validator_for_item(column_name); auto iv = m_cat_validator->get_validator_for_item(column_name);
if (iv == nullptr) if (iv == nullptr)
std::cerr << "Invalid name used '" << name << "' is not a known column in " + m_name << std::endl; std::cerr << "Invalid name used '" << column_name << "' is not a known column in " + m_name << std::endl;
} }
return result; return result;
...@@ -690,6 +641,7 @@ class category ...@@ -690,6 +641,7 @@ class category
const category_validator *m_cat_validator = nullptr; const category_validator *m_cat_validator = nullptr;
std::vector<link> m_parent_links, m_child_links; std::vector<link> m_parent_links, m_child_links;
bool m_cascade = true; bool m_cascade = true;
class category_index* m_index = nullptr;
row *m_head = nullptr, *m_tail = nullptr; row *m_head = nullptr, *m_tail = nullptr;
}; };
......
...@@ -604,11 +604,6 @@ inline condition all() ...@@ -604,11 +604,6 @@ inline condition all()
return condition(new detail::all_condition_impl()); return condition(new detail::all_condition_impl());
} }
// inline condition_t<category> Not(condition_t<category> &&cond)
// {
// return condition_t<category>(new detail::not_condition_impl(std::move(cond)));
// }
namespace literals namespace literals
{ {
inline key operator""_key(const char *text, size_t length) inline key operator""_key(const char *text, size_t length)
......
...@@ -35,16 +35,9 @@ namespace cif::v2 ...@@ -35,16 +35,9 @@ namespace cif::v2
// -------------------------------------------------------------------- // --------------------------------------------------------------------
class datablock class datablock : public std::list<category>
{ {
public: public:
using category_list = std::list<category>;
using iterator = category_list::iterator;
using const_iterator = category_list::const_iterator;
using reference = typename category_list::reference;
datablock() = default; datablock() = default;
datablock(std::string_view name) datablock(std::string_view name)
...@@ -90,48 +83,31 @@ class datablock ...@@ -90,48 +83,31 @@ class datablock
// -------------------------------------------------------------------- // --------------------------------------------------------------------
bool empty() const { return m_categories.empty(); }
size_t size() const { return m_categories.size(); }
reference front() { return m_categories.front(); }
reference back() { return m_categories.back(); }
iterator begin() { return m_categories.begin(); }
iterator end() { return m_categories.end(); }
const_iterator cbegin() { return m_categories.cbegin(); }
const_iterator cend() { return m_categories.cend(); }
const_iterator begin() const { return m_categories.begin(); }
const_iterator end() const { return m_categories.end(); }
// --------------------------------------------------------------------
category &operator[](std::string_view name) category &operator[](std::string_view name)
{ {
auto i = std::find_if(m_categories.begin(), m_categories.end(), [name](const category &c) auto i = std::find_if(begin(), end(), [name](const category &c)
{ return iequals(c.name(), name); }); { return iequals(c.name(), name); });
if (i != m_categories.end()) if (i != end())
return *i; return *i;
m_categories.emplace_back(name); emplace_back(name);
return m_categories.back(); return back();
} }
const category &operator[](std::string_view name) const const category &operator[](std::string_view name) const
{ {
static const category s_empty; static const category s_empty;
auto i = std::find_if(m_categories.begin(), m_categories.end(), [name](const category &c) auto i = std::find_if(begin(), end(), [name](const category &c)
{ return iequals(c.name(), name); }); { return iequals(c.name(), name); });
return i == m_categories.end() ? s_empty : *i; return i == end() ? s_empty : *i;
} }
category *get(std::string_view name) category *get(std::string_view name)
{ {
auto i = std::find_if(m_categories.begin(), m_categories.end(), [name](const category &c) auto i = std::find_if(begin(), end(), [name](const category &c)
{ return iequals(c.name(), name); }); { return iequals(c.name(), name); });
return i == m_categories.end() ? nullptr : &*i; return i == end() ? nullptr : &*i;
} }
const category *get(std::string_view name) const const category *get(std::string_view name) const
...@@ -143,17 +119,17 @@ class datablock ...@@ -143,17 +119,17 @@ class datablock
{ {
bool is_new = true; bool is_new = true;
auto i = m_categories.begin(); auto i = begin();
while (i != m_categories.end()) while (i != end())
{ {
if (iequals(name, i->name())) if (iequals(name, i->name()))
{ {
is_new = false; is_new = false;
if (i != m_categories.begin()) if (i != begin())
{ {
auto n = std::next(i); auto n = std::next(i);
m_categories.splice(m_categories.begin(), m_categories, i, n); splice(begin(), *this, i, n);
} }
break; break;
...@@ -164,14 +140,11 @@ class datablock ...@@ -164,14 +140,11 @@ class datablock
if (is_new) if (is_new)
{ {
m_categories.emplace(m_categories.begin(), name); auto &c = emplace_front(name);
// m_categories.emplace(begin(), *this, std::string(name), mValidator); c.set_validator(m_validator, *this);
// for (auto &cat : mCategories)
// cat.updateLinks();
} }
return std::make_tuple(m_categories.begin(), is_new); return std::make_tuple(begin(), is_new);
} }
// void write(std::ostream &os) const // void write(std::ostream &os) const
...@@ -217,7 +190,6 @@ class datablock ...@@ -217,7 +190,6 @@ class datablock
// } // }
private: private:
category_list m_categories;
std::string m_name; std::string m_name;
const validator *m_validator = nullptr; const validator *m_validator = nullptr;
}; };
......
...@@ -36,17 +36,9 @@ namespace cif::v2 ...@@ -36,17 +36,9 @@ namespace cif::v2
// -------------------------------------------------------------------- // --------------------------------------------------------------------
class file class file : public std::list<datablock>
{ {
public: public:
using datablock_list = std::list<datablock>;
using value_type = datablock_list::value_type;
using reference = datablock_list::reference;
using pointer = datablock_list::pointer;
using iterator = datablock_list::iterator;
using const_iterator = datablock_list::const_iterator;
file() = default; file() = default;
...@@ -113,39 +105,39 @@ class file ...@@ -113,39 +105,39 @@ class file
datablock &operator[](std::string_view name) datablock &operator[](std::string_view name)
{ {
auto i = std::find_if(m_datablocks.begin(), m_datablocks.end(), [name](const datablock &c) auto i = std::find_if(begin(), end(), [name](const datablock &c)
{ return iequals(c.name(), name); }); { return iequals(c.name(), name); });
if (i != m_datablocks.end()) if (i != end())
return *i; return *i;
m_datablocks.emplace_back(name); emplace_back(name);
return m_datablocks.back(); return back();
} }
const datablock &operator[](std::string_view name) const const datablock &operator[](std::string_view name) const
{ {
static const datablock s_empty; static const datablock s_empty;
auto i = std::find_if(m_datablocks.begin(), m_datablocks.end(), [name](const datablock &c) auto i = std::find_if(begin(), end(), [name](const datablock &c)
{ return iequals(c.name(), name); }); { return iequals(c.name(), name); });
return i == m_datablocks.end() ? s_empty : *i; return i == end() ? s_empty : *i;
} }
std::tuple<iterator, bool> emplace(std::string_view name) std::tuple<iterator, bool> emplace(std::string_view name)
{ {
bool is_new = true; bool is_new = true;
auto i = m_datablocks.begin(); auto i = begin();
while (i != m_datablocks.end()) while (i != end())
{ {
if (iequals(name, i->name())) if (iequals(name, i->name()))
{ {
is_new = false; is_new = false;
if (i != m_datablocks.begin()) if (i != begin())
{ {
auto n = std::next(i); auto n = std::next(i);
m_datablocks.splice(m_datablocks.begin(), m_datablocks, i, n); splice(begin(), *this, i, n);
} }
break; break;
...@@ -155,26 +147,14 @@ class file ...@@ -155,26 +147,14 @@ class file
} }
if (is_new) if (is_new)
m_datablocks.emplace(m_datablocks.begin(), name); {
auto &db = emplace_front(name);
db.set_validator(m_validator);
}
return std::make_tuple(m_datablocks.begin(), is_new); return std::make_tuple(begin(), is_new);
} }
bool empty() const { return m_datablocks.empty(); }
size_t size() const { return m_datablocks.size(); }
iterator begin() { return m_datablocks.begin(); }
iterator end() { return m_datablocks.end(); }
const_iterator cbegin() { return m_datablocks.begin(); }
const_iterator cend() { return m_datablocks.end(); }
const_iterator begin() const { return m_datablocks.begin(); }
const_iterator end() const { return m_datablocks.end(); }
reference front() { return m_datablocks.front(); }
reference back() { return m_datablocks.back(); }
void load(std::istream &is) void load(std::istream &is)
{ {
auto saved = m_validator; auto saved = m_validator;
...@@ -191,7 +171,6 @@ class file ...@@ -191,7 +171,6 @@ class file
} }
private: private:
datablock_list m_datablocks;
const validator* m_validator = nullptr; const validator* m_validator = nullptr;
}; };
......
...@@ -89,7 +89,7 @@ class iterator_impl ...@@ -89,7 +89,7 @@ class iterator_impl
template <typename IRowType> template <typename IRowType>
iterator_impl(iterator_impl<IRowType, Ts...> &rhs) iterator_impl(iterator_impl<IRowType, Ts...> &rhs)
: m_category(rhs.m_category) : m_category(rhs.m_category)
, m_current(rhs.m_current) , m_current(const_cast<row_type *>(rhs.m_current))
, m_value(rhs.m_value) , m_value(rhs.m_value)
, m_column_ix(rhs.m_column_ix) , m_column_ix(rhs.m_column_ix)
{ {
...@@ -274,7 +274,7 @@ class conditional_iterator_proxy ...@@ -274,7 +274,7 @@ class conditional_iterator_proxy
using value_type = conditional_iterator_proxy::value_type; using value_type = conditional_iterator_proxy::value_type;
using difference_type = std::ptrdiff_t; using difference_type = std::ptrdiff_t;
using pointer = value_type *; using pointer = value_type *;
using reference = row_handle; using reference = value_type;
conditional_iterator_impl(CategoryType &cat, row_iterator pos, const condition &cond, const std::array<size_t, N> &cix); conditional_iterator_impl(CategoryType &cat, row_iterator pos, const condition &cond, const std::array<size_t, N> &cix);
conditional_iterator_impl(const conditional_iterator_impl &i) = default; conditional_iterator_impl(const conditional_iterator_impl &i) = default;
......
...@@ -110,6 +110,7 @@ class row ...@@ -110,6 +110,7 @@ class row
private: private:
friend class item_handle; friend class item_handle;
friend class category_index;
template <typename, typename...> template <typename, typename...>
friend class iterator_impl; friend class iterator_impl;
...@@ -136,6 +137,7 @@ class row_handle ...@@ -136,6 +137,7 @@ class row_handle
public: public:
friend class item_handle; friend class item_handle;
friend class category; friend class category;
friend class category_index;
row_handle() = default; row_handle() = default;
...@@ -258,6 +260,9 @@ class row_handle ...@@ -258,6 +260,9 @@ class row_handle
void assign(size_t column, std::string_view value, bool updateLinked, bool validate = true); void assign(size_t column, std::string_view value, bool updateLinked, bool validate = true);
bool operator==(const row_handle &rhs) const { return m_category == rhs.m_category and m_row == rhs.m_row; }
bool operator!=(const row_handle &rhs) const { return m_category != rhs.m_category or m_row != rhs.m_row; }
private: private:
uint16_t get_column_ix(std::string_view name) const; uint16_t get_column_ix(std::string_view name) const;
......
...@@ -82,7 +82,7 @@ struct type_validator ...@@ -82,7 +82,7 @@ struct type_validator
return icompare(m_name, rhs.m_name) < 0; return icompare(m_name, rhs.m_name) < 0;
} }
int compare(const char *a, const char *b) const; int compare(std::string_view a, std::string_view b) const;
}; };
struct item_validator struct item_validator
......
...@@ -27,6 +27,10 @@ ...@@ -27,6 +27,10 @@
#include <cif++/v2/category.hpp> #include <cif++/v2/category.hpp>
#include <cif++/v2/datablock.hpp> #include <cif++/v2/datablock.hpp>
// TODO: Find out what the rules are exactly for linked items, the current implementation
// is inconsistent. It all depends whether a link is satified if a field taking part in the
// set of linked items is null at one side and not null in the other.
namespace cif::v2 namespace cif::v2
{ {
...@@ -56,29 +60,638 @@ std::string join(const V &arr, std::string_view sep) ...@@ -56,29 +60,638 @@ std::string join(const V &arr, std::string_view sep)
return s.str(); return s.str();
} }
// --------------------------------------------------------------------
class row_comparator
{
public:
row_comparator(category &cat)
: m_category(cat)
{
auto cv = cat.get_cat_validator();
for (auto k : cv->m_keys)
{
size_t ix = cat.get_column_ix(k);
auto iv = cv->get_validator_for_item(k);
if (iv == nullptr)
throw std::runtime_error("Incomplete dictionary, no Item Validator for Key " + k);
auto tv = iv->m_type;
if (tv == nullptr)
throw std::runtime_error("Incomplete dictionary, no type Validator for Item " + k);
using namespace std::placeholders;
m_comparator.emplace_back(ix, std::bind(&type_validator::compare, tv, _1, _2));
}
}
int operator()(const row *a, const row *b) const
{
assert(a);
assert(b);
row_handle rha(m_category, *a);
row_handle rhb(m_category, *b);
int d = 0;
for (auto &c : m_comparator)
{
size_t k;
compareFunc f;
std::tie(k, f) = c;
std::string_view ka = rha[k].text();
std::string_view kb = rhb[k].text();
d = f(ka, kb);
if (d != 0)
break;
}
return d;
}
private:
typedef std::function<int(std::string_view, std::string_view)> compareFunc;
typedef std::tuple<size_t, compareFunc> key_comparator;
std::vector<key_comparator> m_comparator;
category &m_category;
};
// --------------------------------------------------------------------
//
// class to keep an index on the keys of a category. This is a red/black
// tree implementation.
class category_index
{
public:
category_index(category *cat)
: m_category(*cat)
, m_row_comparator(m_category)
, m_root(nullptr)
{
reconstruct();
}
~category_index()
{
delete m_root;
}
row *find(row *k) const;
void insert(row *r);
void erase(row *r);
// batch create
void reconstruct();
// reorder the row's and returns new head and tail
std::tuple<row *, row *> reorder()
{
std::tuple<row *, row *> result = std::make_tuple(nullptr, nullptr);
if (m_root != nullptr)
{
entry *head = find_min(m_root);
entry *tail = reorder(m_root);
tail->m_row->m_next = nullptr;
result = std::make_tuple(head->m_row, tail->m_row);
}
return result;
}
size_t size() const;
// bool isValid() const;
private:
struct entry
{
entry(row *r)
: m_row(r)
, m_left(nullptr)
, m_right(nullptr)
, m_red(true)
{
}
~entry()
{
delete m_left;
delete m_right;
}
row *m_row;
entry *m_left;
entry *m_right;
bool m_red;
};
entry *insert(entry *h, row *v);
entry *erase(entry *h, row *k);
// void validate(entry* h, bool isParentRed, uint32_t blackDepth, uint32_t& minBlack, uint32_t& maxBlack) const;
entry *rotateLeft(entry *h)
{
entry *x = h->m_right;
h->m_right = x->m_left;
x->m_left = h;
x->m_red = h->m_red;
h->m_red = true;
return x;
}
entry *rotateRight(entry *h)
{
entry *x = h->m_left;
h->m_left = x->m_right;
x->m_right = h;
x->m_red = h->m_red;
h->m_red = true;
return x;
}
void flipColour(entry *h)
{
h->m_red = not h->m_red;
if (h->m_left != nullptr)
h->m_left->m_red = not h->m_left->m_red;
if (h->m_right != nullptr)
h->m_right->m_red = not h->m_right->m_red;
}
bool is_red(entry *h) const
{
return h != nullptr and h->m_red;
}
entry *move_red_left(entry *h)
{
flipColour(h);
if (h->m_right != nullptr and is_red(h->m_right->m_left))
{
h->m_right = rotateRight(h->m_right);
h = rotateLeft(h);
flipColour(h);
}
return h;
}
entry *move_red_right(entry *h)
{
flipColour(h);
if (h->m_left != nullptr and is_red(h->m_left->m_left))
{
h = rotateRight(h);
flipColour(h);
}
return h;
}
entry *fix_up(entry *h)
{
if (is_red(h->m_right))
h = rotateLeft(h);
if (is_red(h->m_left) and is_red(h->m_left->m_left))
h = rotateRight(h);
if (is_red(h->m_left) and is_red(h->m_right))
flipColour(h);
return h;
}
entry *find_min(entry *h)
{
while (h->m_left != nullptr)
h = h->m_left;
return h;
}
entry *erase_min(entry *h)
{
if (h->m_left == nullptr)
{
delete h;
h = nullptr;
}
else
{
if (not is_red(h->m_left) and not is_red(h->m_left->m_left))
h = move_red_left(h);
h->m_left = erase_min(h->m_left);
h = fix_up(h);
}
return h;
}
// Fix m_next fields for rows in order of this index
entry *reorder(entry *e)
{
auto result = e;
if (e->m_left != nullptr)
{
auto l = reorder(e->m_left);
l->m_row->m_next = e->m_row;
}
if (e->m_right != nullptr)
{
auto mr = find_min(e->m_right);
e->m_row->m_next = mr->m_row;
result = reorder(e->m_right);
}
return result;
}
category &m_category;
row_comparator m_row_comparator;
entry *m_root;
};
row *category_index::find(row *k) const
{
const entry *r = m_root;
while (r != nullptr)
{
int d = m_row_comparator(k, r->m_row);
if (d < 0)
r = r->m_left;
else if (d > 0)
r = r->m_right;
else
break;
}
return r ? r->m_row : nullptr;
}
void category_index::insert(row *k)
{
m_root = insert(m_root, k);
m_root->m_red = false;
}
category_index::entry *category_index::insert(entry *h, row *v)
{
if (h == nullptr)
return new entry(v);
int d = m_row_comparator(v, h->m_row);
if (d < 0)
h->m_left = insert(h->m_left, v);
else if (d > 0)
h->m_right = insert(h->m_right, v);
else
{
row_handle rh(m_category, *v);
std::ostringstream os;
for (auto col : m_category.fields())
os << col << ": " << std::quoted(rh[col].text()) << "; ";
throw std::runtime_error("Duplicate Key violation, cat: " + m_category.name() + " values: " + os.str());
}
if (is_red(h->m_right) and not is_red(h->m_left))
h = rotateLeft(h);
if (is_red(h->m_left) and is_red(h->m_left->m_left))
h = rotateRight(h);
if (is_red(h->m_left) and is_red(h->m_right))
flipColour(h);
return h;
}
void category_index::erase(row *k)
{
m_root = erase(m_root, k);
if (m_root != nullptr)
m_root->m_red = false;
}
category_index::entry *category_index::erase(entry *h, row *k)
{
if (m_row_comparator(k, h->m_row) < 0)
{
if (h->m_left != nullptr)
{
if (not is_red(h->m_left) and not is_red(h->m_left->m_left))
h = move_red_left(h);
h->m_left = erase(h->m_left, k);
}
}
else
{
if (is_red(h->m_left))
h = rotateRight(h);
if (m_row_comparator(k, h->m_row) == 0 and h->m_right == nullptr)
{
delete h;
return nullptr;
}
if (h->m_right != nullptr)
{
if (not is_red(h->m_right) and not is_red(h->m_right->m_left))
h = move_red_right(h);
if (m_row_comparator(k, h->m_row) == 0)
{
h->m_row = find_min(h->m_right)->m_row;
h->m_right = erase_min(h->m_right);
}
else
h->m_right = erase(h->m_right, k);
}
}
return fix_up(h);
}
void category_index::reconstruct()
{
delete m_root;
m_root = nullptr;
for (auto r : m_category)
insert(r);
// maybe reconstruction can be done quicker by using the following commented code.
// however, I've not had the time to think of a way to set the red/black flag correctly in that case.
// std::vector<row*> rows;
// transform(mCat.begin(), mCat.end(), backInserter(rows),
// [](Row r) -> row* { assert(r.mData); return r.mData; });
//
// assert(std::find(rows.begin(), rows.end(), nullptr) == rows.end());
//
// // don't use sort here, it will run out of the stack of something.
// // quicksort is notorious for using excessive recursion.
// // Besides, most of the time, the data is ordered already anyway.
//
// stable_sort(rows.begin(), rows.end(), [this](row* a, row* b) -> bool { return this->mComp(a, b) < 0; });
//
// for (size_t i = 0; i < rows.size() - 1; ++i)
// assert(mComp(rows[i], rows[i + 1]) < 0);
//
// deque<entry*> e;
// transform(rows.begin(), rows.end(), back_inserter(e),
// [](row* r) -> entry* { return new entry(r); });
//
// while (e.size() > 1)
// {
// deque<entry*> ne;
//
// while (not e.empty())
// {
// entry* a = e.front();
// e.pop_front();
//
// if (e.empty())
// ne.push_back(a);
// else
// {
// entry* b = e.front();
// b->mLeft = a;
//
// assert(mComp(a->mRow, b->mRow) < 0);
//
// e.pop_front();
//
// if (not e.empty())
// {
// entry* c = e.front();
// e.pop_front();
//
// assert(mComp(b->mRow, c->mRow) < 0);
//
// b->mRight = c;
// }
//
// ne.push_back(b);
//
// if (not e.empty())
// {
// ne.push_back(e.front());
// e.pop_front();
// }
// }
// }
//
// swap (e, ne);
// }
//
// assert(e.size() == 1);
// mRoot = e.front();
}
size_t category_index::size() const
{
std::stack<entry *> s;
s.push(m_root);
size_t result = 0;
while (not s.empty())
{
entry *e = s.top();
s.pop();
if (e == nullptr)
continue;
++result;
s.push(e->m_left);
s.push(e->m_right);
}
return result;
}
// --------------------------------------------------------------------
category::category(std::string_view name)
: m_name(name)
{
}
category::category(const category &rhs)
: m_name(rhs.m_name)
, m_columns(rhs.m_columns)
, m_validator(rhs.m_validator)
, m_cat_validator(rhs.m_cat_validator)
, m_parent_links(rhs.m_parent_links)
, m_child_links(rhs.m_child_links)
, m_cascade(rhs.m_cascade)
{
for (auto r = rhs.m_head; r != nullptr; r = r->m_next)
insert_impl(end(), clone_row(*r));
if (m_validator != nullptr)
m_index = new category_index(this);
}
category::category(category &&rhs)
: m_name(std::move(rhs.m_name))
, m_columns(std::move(rhs.m_columns))
, m_validator(rhs.m_validator)
, m_cat_validator(rhs.m_cat_validator)
, m_parent_links(std::move(rhs.m_parent_links))
, m_child_links(std::move(rhs.m_child_links))
, m_cascade(rhs.m_cascade)
, m_index(rhs.m_index)
, m_head(rhs.m_head)
, m_tail(rhs.m_tail)
{
rhs.m_head = nullptr;
rhs.m_tail = nullptr;
rhs.m_index = nullptr;
}
category &category::operator=(const category &rhs)
{
if (this != &rhs)
{
if (not empty())
clear();
m_name = rhs.m_name;
m_columns = rhs.m_columns;
m_cascade = rhs.m_cascade;
m_validator = nullptr;
m_cat_validator = nullptr;
delete m_index;
m_index = nullptr;
for (auto r = rhs.m_head; r != nullptr; r = r->m_next)
insert_impl(cend(), clone_row(*r));
m_validator = rhs.m_validator;
m_cat_validator = rhs.m_cat_validator;
m_parent_links = rhs.m_parent_links;
m_child_links = rhs.m_child_links;
if (m_validator != nullptr)
m_index = new category_index(this);
}
return *this;
}
category &category::operator=(category &&rhs)
{
if (this != &rhs)
{
if (not empty())
clear();
m_name = std::move(rhs.m_name);
m_columns = std::move(rhs.m_columns);
m_cascade = rhs.m_cascade;
m_validator = rhs.m_validator;
m_cat_validator = rhs.m_cat_validator;
m_parent_links = rhs.m_parent_links;
m_child_links = rhs.m_child_links;
m_index = rhs.m_index;
m_head = rhs.m_head;
m_tail = rhs.m_tail;
rhs.m_head = rhs.m_tail = nullptr;
rhs.m_index = nullptr;
}
return *this;
}
category::~category()
{
clear();
delete m_index;
}
// --------------------------------------------------------------------
iset category::fields() const
{
if (m_validator == nullptr)
throw std::runtime_error("No Validator specified");
if (m_cat_validator == nullptr)
m_validator->report_error("undefined Category", true);
iset result;
for (auto &iv : m_cat_validator->m_item_validators)
result.insert(iv.m_tag);
return result;
}
std::set<uint16_t> category::key_field_indices() const
{
if (m_validator == nullptr)
throw std::runtime_error("No Validator specified");
if (m_cat_validator == nullptr)
m_validator->report_error("undefined Category", true);
std::set<uint16_t> result;
for (auto &k : m_cat_validator->m_keys)
result.insert(get_column_ix(k));
return result;
}
// --------------------------------------------------------------------
void category::set_validator(const validator *v, datablock &db) void category::set_validator(const validator *v, datablock &db)
{ {
m_validator = v; m_validator = v;
// if (m_index != nullptr) if (m_index != nullptr)
// { {
// delete m_index; delete m_index;
// m_index = nullptr; m_index = nullptr;
// } }
if (m_validator != nullptr) if (m_validator != nullptr)
{ {
m_cat_validator = m_validator->get_validator_for_category(m_name); m_cat_validator = m_validator->get_validator_for_category(m_name);
// if (m_cat_validator != nullptr) if (m_cat_validator != nullptr)
// { m_index = new category_index(this);
// m_index = new CatIndex(this);
// m_index->reconstruct();
// //#if DEBUG
// // assert(m_index->size() == size());
// // m_index->validate();
// //#endif
// }
} }
else else
m_cat_validator = nullptr; m_cat_validator = nullptr;
...@@ -124,13 +737,13 @@ bool category::is_valid() const ...@@ -124,13 +737,13 @@ bool category::is_valid() const
if (empty()) if (empty())
{ {
if (VERBOSE > 2) if (VERBOSE > 2)
std::cerr << "Skipping validation of empty Category " << m_name << std::endl; std::cerr << "Skipping validation of empty category " << m_name << std::endl;
return true; return true;
} }
if (m_cat_validator == nullptr) if (m_cat_validator == nullptr)
{ {
m_validator->report_error("undefined Category " + m_name, false); m_validator->report_error("undefined category " + m_name, false);
return false; return false;
} }
...@@ -141,7 +754,7 @@ bool category::is_valid() const ...@@ -141,7 +754,7 @@ bool category::is_valid() const
auto iv = m_cat_validator->get_validator_for_item(col.m_name); auto iv = m_cat_validator->get_validator_for_item(col.m_name);
if (iv == nullptr) if (iv == nullptr)
{ {
m_validator->report_error("Field " + col.m_name + " is not valid in Category " + m_name, false); m_validator->report_error("Field " + col.m_name + " is not valid in category " + m_name, false);
result = false; result = false;
} }
...@@ -154,22 +767,22 @@ bool category::is_valid() const ...@@ -154,22 +767,22 @@ bool category::is_valid() const
if (not mandatory.empty()) if (not mandatory.empty())
{ {
m_validator->report_error("In Category " + m_name + " the following mandatory fields are missing: " + join(mandatory, ", "), false); m_validator->report_error("In category " + m_name + " the following mandatory fields are missing: " + join(mandatory, ", "), false);
result = false; result = false;
} }
//#if not defined(NDEBUG) #if not defined(NDEBUG)
// // check index? // check index?
// if (m_index) if (m_index)
// { {
// m_index->validate(); // m_index->validate();
// for (auto r: *this) for (auto r : *this)
// { {
// if (m_index->find(r.mData) != r.mData) if (m_index->find(r) != r)
// m_validator->report_error("Key not found in index for Category " + m_name); m_validator->report_error("Key not found in index for category " + m_name, true);
// } }
// } }
//#endif #endif
// validate all values // validate all values
mandatory = m_cat_validator->m_mandatory_fields; mandatory = m_cat_validator->m_mandatory_fields;
...@@ -183,7 +796,7 @@ bool category::is_valid() const ...@@ -183,7 +796,7 @@ bool category::is_valid() const
if (iv == nullptr) if (iv == nullptr)
{ {
m_validator->report_error("invalid field " + m_columns[cix].m_name + " for Category " + m_name, false); m_validator->report_error("invalid field " + m_columns[cix].m_name + " for category " + m_name, false);
result = false; result = false;
continue; continue;
} }
...@@ -210,7 +823,7 @@ bool category::is_valid() const ...@@ -210,7 +823,7 @@ bool category::is_valid() const
if (iv != nullptr and iv->m_mandatory) if (iv != nullptr and iv->m_mandatory)
{ {
m_validator->report_error("missing mandatory field " + m_columns[cix].m_name + " for Category " + m_name, false); m_validator->report_error("missing mandatory field " + m_columns[cix].m_name + " for category " + m_name, false);
result = false; result = false;
} }
} }
...@@ -219,6 +832,136 @@ bool category::is_valid() const ...@@ -219,6 +832,136 @@ bool category::is_valid() const
return result; return result;
} }
// --------------------------------------------------------------------
bool category::has_children(row_handle r) const
{
assert(m_validator != nullptr);
assert(m_cat_validator != nullptr);
bool result = false;
for (auto &&[childCat, link] : m_child_links)
{
condition cond;
for (size_t ix = 0; ix < link->m_parent_keys.size(); ++ix)
{
std::string_view value = r[link->m_parent_keys[ix]].text();
// cond = std::move(cond) and (key(link->m_child_keys[ix]) == value or key(link->m_child_keys[ix]) == null);
cond = std::move(cond) and (key(link->m_child_keys[ix]) == value);
}
result = not childCat->find(std::move(cond)).empty();
if (result)
break;
}
return result;
}
bool category::has_parents(row_handle r) const
{
assert(m_validator != nullptr);
assert(m_cat_validator != nullptr);
bool result = false;
for (auto &&[parentCat, link] : m_parent_links)
{
condition cond;
for (size_t ix = 0; ix < link->m_child_keys.size(); ++ix)
{
std::string_view value = r[link->m_child_keys[ix]].text();
cond = std::move(cond) and (key(link->m_parent_keys[ix]) == value);
}
result = not parentCat->find(std::move(cond)).empty();
if (result)
break;
}
return result;
}
std::vector<row_handle> category::get_children(row_handle r, category &childCat)
{
assert(m_validator != nullptr);
assert(m_cat_validator != nullptr);
std::vector<row_handle> result;
for (auto &link : m_validator->get_links_for_parent(m_name))
{
if (link->m_child_category != childCat.m_name)
continue;
condition cond;
for (size_t ix = 0; ix < link->m_parent_keys.size(); ++ix)
{
std::string_view value = r[link->m_parent_keys[ix]].text();
// cond = std::move(cond) and (key(link->m_child_keys[ix]) == value or key(link->m_child_keys[ix]) == null);
cond = std::move(cond) and (key(link->m_child_keys[ix]) == value);
}
for (auto child: childCat.find(std::move(cond)))
{
if (std::find(result.begin(), result.end(), child) == result.end())
result.push_back(child);
}
}
return result;
}
std::vector<row_handle> category::get_parents(row_handle r, category &parentCat)
{
assert(m_validator != nullptr);
assert(m_cat_validator != nullptr);
std::vector<row_handle> result;
for (auto &link : m_validator->get_links_for_child(m_name))
{
if (link->m_parent_category != parentCat.m_name)
continue;
condition cond;
for (size_t ix = 0; ix < link->m_child_keys.size(); ++ix)
{
std::string_view value = r[link->m_child_keys[ix]].text();
cond = std::move(cond) and (key(link->m_parent_keys[ix]) == value);
}
for (auto parent: parentCat.find(std::move(cond)))
{
if (std::find(result.begin(), result.end(), parent) == result.end())
result.push_back(parent);
}
}
return result;
}
std::vector<row_handle> category::get_linked(row_handle r, category &cat)
{
std::vector<row_handle> result = get_children(r, cat);
if (result.empty())
result = get_parents(r, cat);
return result;
}
// --------------------------------------------------------------------
category::iterator category::erase(iterator pos) category::iterator category::erase(iterator pos)
{ {
row_handle rh = *pos; row_handle rh = *pos;
...@@ -232,8 +975,8 @@ category::iterator category::erase(iterator pos) ...@@ -232,8 +975,8 @@ category::iterator category::erase(iterator pos)
if (m_head == nullptr) if (m_head == nullptr)
throw std::runtime_error("erase"); throw std::runtime_error("erase");
// if (mIndex != nullptr) if (m_index != nullptr)
// mIndex->erase(r.mData); m_index->erase(r);
if (r == m_head) if (r == m_head)
{ {
...@@ -270,6 +1013,7 @@ category::iterator category::erase(iterator pos) ...@@ -270,6 +1013,7 @@ category::iterator category::erase(iterator pos)
for (size_t ix = 0; ix < link->m_parent_keys.size(); ++ix) for (size_t ix = 0; ix < link->m_parent_keys.size(); ++ix)
{ {
std::string_view value = rh[link->m_parent_keys[ix]].text(); std::string_view value = rh[link->m_parent_keys[ix]].text();
// cond = std::move(cond) and (key(link->m_child_keys[ix]) == value or key(link->m_child_keys[ix]) == null);
cond = std::move(cond) and (key(link->m_child_keys[ix]) == value); cond = std::move(cond) and (key(link->m_child_keys[ix]) == value);
} }
...@@ -288,7 +1032,7 @@ category::iterator category::erase(iterator pos) ...@@ -288,7 +1032,7 @@ category::iterator category::erase(iterator pos)
m_tail = m_tail->m_next; m_tail = m_tail->m_next;
} }
return result; return result;
} }
size_t category::erase(condition &&cond) size_t category::erase(condition &&cond)
...@@ -351,7 +1095,7 @@ bool category::is_orphan(row_handle r) const ...@@ -351,7 +1095,7 @@ bool category::is_orphan(row_handle r) const
} }
// if (VERBOSE > 2) // if (VERBOSE > 2)
// std::cerr << "Check condition '" << cond << "' in parent category " << link->mParentCategory << " for child cat " << mName << std::endl; // std::cerr << "Check condition '" << cond << "' in parent category " << link->mParentcategory << " for child cat " << m_name << std::endl;
if (parentCat->exists(std::move(cond))) if (parentCat->exists(std::move(cond)))
{ {
...@@ -389,7 +1133,6 @@ void category::erase_orphans(condition &&cond) ...@@ -389,7 +1133,6 @@ void category::erase_orphans(condition &&cond)
erase(iterator(*this, r)); erase(iterator(*this, r));
} }
void category::update_value(row *row, size_t column, std::string_view value, bool updateLinked, bool validate) void category::update_value(row *row, size_t column, std::string_view value, bool updateLinked, bool validate)
{ {
auto &col = m_columns[column]; auto &col = m_columns[column];
...@@ -415,18 +1158,18 @@ void category::update_value(row *row, size_t column, std::string_view value, boo ...@@ -415,18 +1158,18 @@ void category::update_value(row *row, size_t column, std::string_view value, boo
if (col.m_validator and validate) if (col.m_validator and validate)
col.m_validator->operator()(value); col.m_validator->operator()(value);
// If the field is part of the Key for this Category, remove it from the index // If the field is part of the Key for this category, remove it from the index
// before updating // before updating
bool reinsert = false; bool reinsert = false;
// if (updateLinked and // an update of an Item's value if (updateLinked and // an update of an Item's value
// cat->m_index != nullptr and cat->keyFieldsByIndex().count(column)) m_index != nullptr and key_field_indices().count(column))
// { {
// reinsert = cat->m_index->find(mData); reinsert = m_index->find(row);
// if (reinsert) if (reinsert)
// cat->m_index->erase(mData); m_index->erase(row);
// } }
// first remove old value with cix // first remove old value with cix
...@@ -470,8 +1213,8 @@ void category::update_value(row *row, size_t column, std::string_view value, boo ...@@ -470,8 +1213,8 @@ void category::update_value(row *row, size_t column, std::string_view value, boo
} }
} }
// if (reinsert) if (reinsert)
// cat->mIndex->insert(mData); m_index->insert(row);
// see if we need to update any child categories that depend on this value // see if we need to update any child categories that depend on this value
auto iv = col.m_validator; auto iv = col.m_validator;
...@@ -515,7 +1258,7 @@ void category::update_value(row *row, size_t column, std::string_view value, boo ...@@ -515,7 +1258,7 @@ void category::update_value(row *row, size_t column, std::string_view value, boo
// if (cif::VERBOSE > 2) // if (cif::VERBOSE > 2)
// { // {
// std::cerr << "Parent: " << linked->mParentCategory << " Child: " << linked->mChildCategory << std::endl // std::cerr << "Parent: " << linked->mParentcategory << " Child: " << linked->m_child_category << std::endl
// << cond << std::endl; // << cond << std::endl;
// } // }
......
...@@ -45,7 +45,7 @@ std::string_view item_handle::text() const ...@@ -45,7 +45,7 @@ std::string_view item_handle::text() const
void item_handle::assign_value(const item &v) void item_handle::assign_value(const item &v)
{ {
m_row_handle.assign(m_column, v.value(), false); m_row_handle.assign(m_column, v.value(), true);
} }
} }
...@@ -31,6 +31,8 @@ ...@@ -31,6 +31,8 @@
#include <cif++/v2/dictionary_parser.hpp> #include <cif++/v2/dictionary_parser.hpp>
#include <cif++/v2/validate.hpp> #include <cif++/v2/validate.hpp>
#include <cif++/CifUtils.hpp>
namespace cif namespace cif
{ {
extern int VERBOSE; extern int VERBOSE;
...@@ -69,25 +71,27 @@ DDL_PrimitiveType map_to_primitive_type(std::string_view s) ...@@ -69,25 +71,27 @@ DDL_PrimitiveType map_to_primitive_type(std::string_view s)
// -------------------------------------------------------------------- // --------------------------------------------------------------------
int type_validator::compare(const char *a, const char *b) const int type_validator::compare(std::string_view a, std::string_view b) const
{ {
int result = 0; int result = 0;
if (*a == 0) if (a.empty())
result = *b == 0 ? 0 : -1; result = b.empty() ? 0 : -1;
else if (*b == 0) else if (b.empty())
result = *a == 0 ? 0 : +1; result = a.empty() ? 0 : +1;
else else
{ {
try switch (m_primitive_type)
{ {
switch (m_primitive_type) case DDL_PrimitiveType::Numb:
{ {
case DDL_PrimitiveType::Numb: double da, db;
{
double da = strtod(a, nullptr); auto ra = cif::from_chars(a.begin(), a.end(), da);
double db = strtod(b, nullptr); auto rb = cif::from_chars(b.begin(), b.end(), db);
if (ra.ec == std::errc() and rb.ec == std::errc())
{
auto d = da - db; auto d = da - db;
if (std::abs(d) > std::numeric_limits<double>::epsilon()) if (std::abs(d) > std::numeric_limits<double>::epsilon())
{ {
...@@ -96,64 +100,64 @@ int type_validator::compare(const char *a, const char *b) const ...@@ -96,64 +100,64 @@ int type_validator::compare(const char *a, const char *b) const
else if (d < 0) else if (d < 0)
result = -1; result = -1;
} }
break;
} }
else if (ra.ec == std::errc())
result = 1;
else
result = -1;
break;
}
case DDL_PrimitiveType::UChar: case DDL_PrimitiveType::UChar:
case DDL_PrimitiveType::Char: case DDL_PrimitiveType::Char:
{ {
// CIF is guaranteed to have ascii only, therefore this primitive code will do // CIF is guaranteed to have ascii only, therefore this primitive code will do
// also, we're collapsing spaces // also, we're collapsing spaces
auto ai = a, bi = b; auto ai = a.begin(), bi = b.begin();
for (;;) for (;;)
{
if (ai == a.end())
{ {
if (*ai == 0) if (bi != b.end())
{ result = -1;
if (*bi != 0) break;
result = -1; }
break; else if (bi == b.end())
} {
else if (*bi == 0) result = 1;
{ break;
result = 1; }
break;
}
char ca = *ai;
char cb = *bi;
if (m_primitive_type == DDL_PrimitiveType::UChar) char ca = *ai;
{ char cb = *bi;
ca = tolower(ca);
cb = tolower(cb);
}
result = ca - cb; if (m_primitive_type == DDL_PrimitiveType::UChar)
{
ca = tolower(ca);
cb = tolower(cb);
}
if (result != 0) result = ca - cb;
break;
if (ca == ' ') if (result != 0)
{ break;
while (ai[1] == ' ')
++ai;
while (bi[1] == ' ')
++bi;
}
++ai; if (ca == ' ')
++bi; {
while (ai[1] == ' ')
++ai;
while (bi[1] == ' ')
++bi;
} }
break; ++ai;
++bi;
} }
break;
} }
} }
catch (const std::invalid_argument &ex)
{
result = 1;
}
} }
return result; return result;
......
...@@ -553,1215 +553,1216 @@ _cat_2.desc ...@@ -553,1215 +553,1216 @@ _cat_2.desc
std::exception); std::exception);
} }
// // -------------------------------------------------------------------- // --------------------------------------------------------------------
BOOST_AUTO_TEST_CASE(d2)
{
const char dict[] = R"(
data_test_dict.dic
_datablock.id test_dict.dic
_datablock.description
;
A test dictionary
;
_dictionary.title test_dict.dic
_dictionary.datablock_id test_dict.dic
_dictionary.version 1.0
loop_
_item_type_list.code
_item_type_list.primitive_code
_item_type_list.construct
_item_type_list.detail
code char
'[][_,.;:"&<>()/\{}'`~!@#$%A-Za-z0-9*|+-]*'
; code item types/single words ...
;
ucode uchar
'[][_,.;:"&<>()/\{}'`~!@#$%A-Za-z0-9*|+-]*'
; code item types/single words, case insensitive
;
text char
'[][ \n\t()_,.;:"&<>/\{}'`~!@#$%?+=*A-Za-z0-9|^-]*'
; text item types / multi-line text ...
;
int numb
'[+-]?[0-9]+'
; int item types are the subset of numbers that are the negative
or positive integers.
;
save_cat_1
_category.description 'A simple test category'
_category.id cat_1
_category.mandatory_code no
_category_key.name '_cat_1.id'
save_
save__cat_1.id
_item.name '_cat_1.id'
_item.category_id cat_1
_item.mandatory_code yes
_item_type.code code
save_
save__cat_1.c
_item.name '_cat_1.c'
_item.category_id cat_1
_item.mandatory_code yes
_item_type.code ucode
save_
)";
struct membuf : public std::streambuf
{
membuf(char *text, size_t length)
{
this->setg(text, text, text + length);
}
} buffer(const_cast<char *>(dict), sizeof(dict) - 1);
std::istream is_dict(&buffer);
auto validator = cif::v2::parse_dictionary("test", is_dict);
cif::v2::file f;
f.set_validator(&validator);
// --------------------------------------------------------------------
const char data[] = R"(
data_test
loop_
_cat_1.id
_cat_1.c
aap Aap
noot Noot
mies Mies
)";
struct data_membuf : public std::streambuf
{
data_membuf(char *text, size_t length)
{
this->setg(text, text, text + length);
}
} data_buffer(const_cast<char *>(data), sizeof(data) - 1);
std::istream is_data(&data_buffer);
f.load(is_data);
auto &cat1 = f.front()["cat_1"];
BOOST_CHECK(cat1.size() == 3);
cat1.erase(cif::v2::key("id") == "AAP");
BOOST_CHECK(cat1.size() == 3);
cat1.erase(cif::v2::key("id") == "noot");
BOOST_CHECK(cat1.size() == 2);
}
// --------------------------------------------------------------------
BOOST_AUTO_TEST_CASE(d3)
{
const char dict[] = R"(
data_test_dict.dic
_datablock.id test_dict.dic
_datablock.description
;
A test dictionary
;
_dictionary.title test_dict.dic
_dictionary.datablock_id test_dict.dic
_dictionary.version 1.0
loop_
_item_type_list.code
_item_type_list.primitive_code
_item_type_list.construct
code char
'[][_,.;:"&<>()/\{}'`~!@#$%A-Za-z0-9*|+-]*'
text char
'[][ \n\t()_,.;:"&<>/\{}'`~!@#$%?+=*A-Za-z0-9|^-]*'
int numb
'[+-]?[0-9]+'
save_cat_1
_category.description 'A simple test category'
_category.id cat_1
_category.mandatory_code no
_category_key.name '_cat_1.id'
save_
save__cat_1.id
_item.name '_cat_1.id'
_item.category_id cat_1
_item.mandatory_code yes
_item_linked.child_name '_cat_2.parent_id'
_item_linked.parent_name '_cat_1.id'
_item_type.code code
save_
save__cat_1.name1
_item.name '_cat_1.name1'
_item.category_id cat_1
_item.mandatory_code yes
_item_type.code text
save_
save__cat_1.name2
_item.name '_cat_1.name2'
_item.category_id cat_1
_item.mandatory_code no
_item_linked.child_name '_cat_2.name2'
_item_linked.parent_name '_cat_1.name2'
_item_type.code text
save_
save_cat_2
_category.description 'A second simple test category'
_category.id cat_2
_category.mandatory_code no
_category_key.name '_cat_2.id'
save_
save__cat_2.id
_item.name '_cat_2.id'
_item.category_id cat_2
_item.mandatory_code yes
_item_type.code int
save_
save__cat_2.parent_id
_item.name '_cat_2.parent_id'
_item.category_id cat_2
_item.mandatory_code yes
_item_type.code code
save_
save__cat_2.name2
_item.name '_cat_2.name2'
_item.category_id cat_2
_item.mandatory_code no
_item_type.code text
save_
save__cat_2.desc
_item.name '_cat_2.desc'
_item.category_id cat_2
_item.mandatory_code yes
_item_type.code text
save_
)";
struct membuf : public std::streambuf
{
membuf(char *text, size_t length)
{
this->setg(text, text, text + length);
}
} buffer(const_cast<char *>(dict), sizeof(dict) - 1);
std::istream is_dict(&buffer);
auto validator = cif::v2::parse_dictionary("test", is_dict);
cif::v2::file f;
f.set_validator(&validator);
// --------------------------------------------------------------------
const char data[] = R"(
data_test
loop_
_cat_1.id
_cat_1.name1
_cat_1.name2
1 Aap aap
2 Noot noot
3 Mies mies
loop_
_cat_2.id
_cat_2.parent_id
_cat_2.name2
_cat_2.desc
1 1 aap 'Een dier'
2 1 . 'Een andere aap'
3 2 noot 'walnoot bijvoorbeeld'
4 2 n2 hazelnoot
)";
struct data_membuf : public std::streambuf
{
data_membuf(char *text, size_t length)
{
this->setg(text, text, text + length);
}
} data_buffer(const_cast<char *>(data), sizeof(data) - 1);
std::istream is_data(&data_buffer);
f.load(is_data);
auto &cat1 = f.front()["cat_1"];
auto &cat2 = f.front()["cat_2"];
// check a rename in parent and child
for (auto r : cat1.find(cif::v2::key("id") == 1))
{
r["id"] = 10;
break;
}
BOOST_CHECK(cat1.size() == 3);
BOOST_CHECK(cat2.size() == 4);
BOOST_CHECK(cat1.find(cif::v2::key("id") == 1).size() == 0);
BOOST_CHECK(cat1.find(cif::v2::key("id") == 10).size() == 1);
BOOST_CHECK(cat2.find(cif::v2::key("parent_id") == 1).size() == 0);
BOOST_CHECK(cat2.find(cif::v2::key("parent_id") == 10).size() == 2);
// check a rename in parent and child, this time only one child should be renamed
for (auto r : cat1.find(cif::v2::key("id") == 2))
{
r["id"] = 20;
break;
}
BOOST_CHECK(cat1.size() == 3);
BOOST_CHECK(cat2.size() == 4);
BOOST_CHECK(cat1.find(cif::v2::key("id") == 2).size() == 0);
BOOST_CHECK(cat1.find(cif::v2::key("id") == 20).size() == 1);
BOOST_CHECK(cat2.find(cif::v2::key("parent_id") == 2).size() == 1);
BOOST_CHECK(cat2.find(cif::v2::key("parent_id") == 20).size() == 1);
BOOST_CHECK(cat2.find(cif::v2::key("parent_id") == 2 and cif::v2::key("name2") == "noot").size() == 0);
BOOST_CHECK(cat2.find(cif::v2::key("parent_id") == 2 and cif::v2::key("name2") == "n2").size() == 1);
BOOST_CHECK(cat2.find(cif::v2::key("parent_id") == 20 and cif::v2::key("name2") == "noot").size() == 1);
BOOST_CHECK(cat2.find(cif::v2::key("parent_id") == 20 and cif::v2::key("name2") == "n2").size() == 0);
// --------------------------------------------------------------------
cat1.erase(cif::v2::key("id") == 10);
BOOST_CHECK(cat1.size() == 2);
BOOST_CHECK(cat2.size() == 2);
cat1.erase(cif::v2::key("id") == 20);
BOOST_CHECK(cat1.size() == 1);
BOOST_CHECK(cat2.size() == 1);
}
// --------------------------------------------------------------------
BOOST_AUTO_TEST_CASE(d4)
{
const char dict[] = R"(
data_test_dict.dic
_datablock.id test_dict.dic
_datablock.description
;
A test dictionary
;
_dictionary.title test_dict.dic
_dictionary.datablock_id test_dict.dic
_dictionary.version 1.0
loop_
_item_type_list.code
_item_type_list.primitive_code
_item_type_list.construct
code char
'[][_,.;:"&<>()/\{}'`~!@#$%A-Za-z0-9*|+-]*'
text char
'[][ \n\t()_,.;:"&<>/\{}'`~!@#$%?+=*A-Za-z0-9|^-]*'
int numb
'[+-]?[0-9]+'
save_cat_1
_category.description 'A simple test category'
_category.id cat_1
_category.mandatory_code no
_category_key.name '_cat_1.id'
save_
save__cat_1.id
_item.name '_cat_1.id'
_item.category_id cat_1
_item.mandatory_code yes
_item_linked.child_name '_cat_2.parent_id'
_item_linked.parent_name '_cat_1.id'
_item_type.code int
save_
save__cat_1.id2
_item.name '_cat_1.id2'
_item.category_id cat_1
_item.mandatory_code no
_item_linked.child_name '_cat_2.parent_id2'
_item_linked.parent_name '_cat_1.id2'
_item_type.code code
save_
save__cat_1.id3
_item.name '_cat_1.id3'
_item.category_id cat_1
_item.mandatory_code no
_item_linked.child_name '_cat_2.parent_id3'
_item_linked.parent_name '_cat_1.id3'
_item_type.code text
save_
save_cat_2
_category.description 'A second simple test category'
_category.id cat_2
_category.mandatory_code no
_category_key.name '_cat_2.id'
save_
save__cat_2.id
_item.name '_cat_2.id'
_item.category_id cat_2
_item.mandatory_code yes
_item_type.code int
save_
save__cat_2.parent_id
_item.name '_cat_2.parent_id'
_item.category_id cat_2
_item.mandatory_code yes
_item_type.code int
save_
save__cat_2.parent_id2
_item.name '_cat_2.parent_id2'
_item.category_id cat_2
_item.mandatory_code no
_item_type.code code
save_
save__cat_2.parent_id3
_item.name '_cat_2.parent_id3'
_item.category_id cat_2
_item.mandatory_code no
_item_type.code code
save_
)";
// BOOST_AUTO_TEST_CASE(d2) struct membuf : public std::streambuf
// { {
// const char dict[] = R"( membuf(char *text, size_t length)
// data_test_dict.dic {
// _datablock.id test_dict.dic this->setg(text, text, text + length);
// _datablock.description }
// ; } buffer(const_cast<char *>(dict), sizeof(dict) - 1);
// A test dictionary
// ;
// _dictionary.title test_dict.dic
// _dictionary.datablock_id test_dict.dic
// _dictionary.version 1.0
// loop_
// _item_type_list.code
// _item_type_list.primitive_code
// _item_type_list.construct
// _item_type_list.detail
// code char
// '[][_,.;:"&<>()/\{}'`~!@#$%A-Za-z0-9*|+-]*'
// ; code item types/single words ...
// ;
// ucode uchar
// '[][_,.;:"&<>()/\{}'`~!@#$%A-Za-z0-9*|+-]*'
// ; code item types/single words, case insensitive
// ;
// text char
// '[][ \n\t()_,.;:"&<>/\{}'`~!@#$%?+=*A-Za-z0-9|^-]*'
// ; text item types / multi-line text ...
// ;
// int numb
// '[+-]?[0-9]+'
// ; int item types are the subset of numbers that are the negative
// or positive integers.
// ;
// save_cat_1
// _category.description 'A simple test category'
// _category.id cat_1
// _category.mandatory_code no
// _category_key.name '_cat_1.id'
// save_
// save__cat_1.id
// _item.name '_cat_1.id'
// _item.category_id cat_1
// _item.mandatory_code yes
// _item_type.code code
// save_
// save__cat_1.c
// _item.name '_cat_1.c'
// _item.category_id cat_1
// _item.mandatory_code yes
// _item_type.code ucode
// save_
// )";
// struct membuf : public std::streambuf
// {
// membuf(char *text, size_t length)
// {
// this->setg(text, text, text + length);
// }
// } buffer(const_cast<char *>(dict), sizeof(dict) - 1);
// std::istream is_dict(&buffer); std::istream is_dict(&buffer);
// cif::v2::validator validator("test", is_dict); auto validator = cif::v2::parse_dictionary("test", is_dict);
// cif::File f; cif::v2::file f;
// f.setValidator(&validator); f.set_validator(&validator);
// // -------------------------------------------------------------------- // --------------------------------------------------------------------
// const char data[] = R"( const char data[] = R"(
// data_test data_test
// loop_ loop_
// _cat_1.id _cat_1.id
// _cat_1.c _cat_1.id2
// aap Aap _cat_1.id3
// noot Noot 1 aap aap
// mies Mies 2 . noot
// )"; 3 mies .
4 . .
// struct data_membuf : public std::streambuf
// {
// data_membuf(char *text, size_t length)
// {
// this->setg(text, text, text + length);
// }
// } data_buffer(const_cast<char *>(data), sizeof(data) - 1);
// std::istream is_data(&data_buffer); loop_
// f.load(is_data); _cat_2.id
_cat_2.parent_id
_cat_2.parent_id2
_cat_2.parent_id3
1 1 aap aap
2 1 . x
3 1 aap .
4 2 noot noot
5 2 . noot
6 2 noot .
7 2 . .
8 3 mies mies
9 3 . mies
10 3 mies .
11 4 roos roos
12 4 . roos
13 4 roos .
)";
// auto &cat1 = f.front()["cat_1"]; struct data_membuf : public std::streambuf
{
data_membuf(char *text, size_t length)
{
this->setg(text, text, text + length);
}
} data_buffer(const_cast<char *>(data), sizeof(data) - 1);
// BOOST_CHECK(cat1.size() == 3); std::istream is_data(&data_buffer);
f.load(is_data);
// cat1.erase(cif::v2::key("id") == "AAP"); auto &cat1 = f.front()["cat_1"];
auto &cat2 = f.front()["cat_2"];
// BOOST_CHECK(cat1.size() == 3); // check a rename in parent and child
// cat1.erase(cif::v2::key("id") == "noot"); for (auto r : cat1.find(cif::v2::key("id") == 1))
{
r["id"] = 10;
break;
}
// BOOST_CHECK(cat1.size() == 2); BOOST_CHECK(cat1.size() == 4);
// } BOOST_CHECK(cat2.size() == 13);
// // -------------------------------------------------------------------- BOOST_CHECK(cat1.find(cif::v2::key("id") == 1).size() == 0);
BOOST_CHECK(cat1.find(cif::v2::key("id") == 10).size() == 1);
// BOOST_AUTO_TEST_CASE(d3) BOOST_CHECK(cat2.find(cif::v2::key("parent_id") == 1).size() == 1);
// { BOOST_CHECK(cat2.find(cif::v2::key("parent_id") == 10).size() == 2);
// const char dict[] = R"(
// data_test_dict.dic
// _datablock.id test_dict.dic
// _datablock.description
// ;
// A test dictionary
// ;
// _dictionary.title test_dict.dic
// _dictionary.datablock_id test_dict.dic
// _dictionary.version 1.0
// loop_
// _item_type_list.code
// _item_type_list.primitive_code
// _item_type_list.construct
// code char
// '[][_,.;:"&<>()/\{}'`~!@#$%A-Za-z0-9*|+-]*'
// text char
// '[][ \n\t()_,.;:"&<>/\{}'`~!@#$%?+=*A-Za-z0-9|^-]*'
// int numb
// '[+-]?[0-9]+'
// save_cat_1
// _category.description 'A simple test category'
// _category.id cat_1
// _category.mandatory_code no
// _category_key.name '_cat_1.id'
// save_
// save__cat_1.id
// _item.name '_cat_1.id'
// _item.category_id cat_1
// _item.mandatory_code yes
// _item_linked.child_name '_cat_2.parent_id'
// _item_linked.parent_name '_cat_1.id'
// _item_type.code code
// save_
// save__cat_1.name1
// _item.name '_cat_1.name1'
// _item.category_id cat_1
// _item.mandatory_code yes
// _item_type.code text
// save_
// save__cat_1.name2
// _item.name '_cat_1.name2'
// _item.category_id cat_1
// _item.mandatory_code no
// _item_linked.child_name '_cat_2.name2'
// _item_linked.parent_name '_cat_1.name2'
// _item_type.code text
// save_
// save_cat_2
// _category.description 'A second simple test category'
// _category.id cat_2
// _category.mandatory_code no
// _category_key.name '_cat_2.id'
// save_
// save__cat_2.id
// _item.name '_cat_2.id'
// _item.category_id cat_2
// _item.mandatory_code yes
// _item_type.code int
// save_
// save__cat_2.parent_id
// _item.name '_cat_2.parent_id'
// _item.category_id cat_2
// _item.mandatory_code yes
// _item_type.code code
// save_
// save__cat_2.name2
// _item.name '_cat_2.name2'
// _item.category_id cat_2
// _item.mandatory_code no
// _item_type.code text
// save_
// save__cat_2.desc
// _item.name '_cat_2.desc'
// _item.category_id cat_2
// _item.mandatory_code yes
// _item_type.code text
// save_
// )";
// struct membuf : public std::streambuf
// {
// membuf(char *text, size_t length)
// {
// this->setg(text, text, text + length);
// }
// } buffer(const_cast<char *>(dict), sizeof(dict) - 1);
// std::istream is_dict(&buffer); for (auto r : cat1.find(cif::v2::key("id") == 2))
{
r["id"] = 20;
break;
}
// cif::v2::validator validator("test", is_dict); BOOST_CHECK(cat1.size() == 4);
BOOST_CHECK(cat2.size() == 13);
// cif::File f; BOOST_CHECK(cat1.find(cif::v2::key("id") == 2).size() == 0);
// f.setValidator(&validator); BOOST_CHECK(cat1.find(cif::v2::key("id") == 20).size() == 1);
// // -------------------------------------------------------------------- BOOST_CHECK(cat2.find(cif::v2::key("parent_id") == 2).size() == 2);
BOOST_CHECK(cat2.find(cif::v2::key("parent_id") == 20).size() == 2);
// const char data[] = R"( for (auto r : cat1.find(cif::v2::key("id") == 3))
// data_test {
// loop_ r["id"] = 30;
// _cat_1.id break;
// _cat_1.name1 }
// _cat_1.name2
// 1 Aap aap
// 2 Noot noot
// 3 Mies mies
// loop_ BOOST_CHECK(cat1.size() == 4);
// _cat_2.id BOOST_CHECK(cat2.size() == 13);
// _cat_2.parent_id
// _cat_2.name2
// _cat_2.desc
// 1 1 aap 'Een dier'
// 2 1 . 'Een andere aap'
// 3 2 noot 'walnoot bijvoorbeeld'
// 4 2 n2 hazelnoot
// )";
// struct data_membuf : public std::streambuf
// {
// data_membuf(char *text, size_t length)
// {
// this->setg(text, text, text + length);
// }
// } data_buffer(const_cast<char *>(data), sizeof(data) - 1);
// std::istream is_data(&data_buffer); BOOST_CHECK(cat1.find(cif::v2::key("id") == 3).size() == 0);
// f.load(is_data); BOOST_CHECK(cat1.find(cif::v2::key("id") == 30).size() == 1);
// auto &cat1 = f.front()["cat_1"]; BOOST_CHECK(cat2.find(cif::v2::key("parent_id") == 3).size() == 2);
// auto &cat2 = f.front()["cat_2"]; BOOST_CHECK(cat2.find(cif::v2::key("parent_id") == 30).size() == 1);
// // check a rename in parent and child for (auto r : cat1.find(cif::v2::key("id") == 4))
{
r["id"] = 40;
break;
}
// for (auto r : cat1.find(cif::v2::key("id") == 1)) BOOST_CHECK(cat1.size() == 4);
// { BOOST_CHECK(cat2.size() == 13);
// r["id"] = 10;
// break;
// }
// BOOST_CHECK(cat1.size() == 3); BOOST_CHECK(cat1.find(cif::v2::key("id") == 4).size() == 0);
// BOOST_CHECK(cat2.size() == 4); BOOST_CHECK(cat1.find(cif::v2::key("id") == 10).size() == 1);
// BOOST_CHECK(cat1.find(cif::v2::key("id") == 1).size() == 0); BOOST_CHECK(cat2.find(cif::v2::key("parent_id") == 4).size() == 3);
// BOOST_CHECK(cat1.find(cif::v2::key("id") == 10).size() == 1); BOOST_CHECK(cat2.find(cif::v2::key("parent_id") == 40).size() == 0);
}
// BOOST_CHECK(cat2.find(cif::v2::key("parent_id") == 1).size() == 0); // --------------------------------------------------------------------
// BOOST_CHECK(cat2.find(cif::v2::key("parent_id") == 10).size() == 2);
// // check a rename in parent and child, this time only one child should be renamed BOOST_AUTO_TEST_CASE(d5)
{
const char dict[] = R"(
data_test_dict.dic
_datablock.id test_dict.dic
_datablock.description
;
A test dictionary
;
_dictionary.title test_dict.dic
_dictionary.datablock_id test_dict.dic
_dictionary.version 1.0
// for (auto r : cat1.find(cif::v2::key("id") == 2)) loop_
// { _item_type_list.code
// r["id"] = 20; _item_type_list.primitive_code
// break; _item_type_list.construct
// } code char
'[][_,.;:"&<>()/\{}'`~!@#$%A-Za-z0-9*|+-]*'
// BOOST_CHECK(cat1.size() == 3); text char
// BOOST_CHECK(cat2.size() == 4); '[][ \n\t()_,.;:"&<>/\{}'`~!@#$%?+=*A-Za-z0-9|^-]*'
// BOOST_CHECK(cat1.find(cif::v2::key("id") == 2).size() == 0); int numb
// BOOST_CHECK(cat1.find(cif::v2::key("id") == 20).size() == 1); '[+-]?[0-9]+'
// BOOST_CHECK(cat2.find(cif::v2::key("parent_id") == 2).size() == 1); save_cat_1
// BOOST_CHECK(cat2.find(cif::v2::key("parent_id") == 20).size() == 1); _category.description 'A simple test category'
_category.id cat_1
_category.mandatory_code no
_category_key.name '_cat_1.id'
save_
// BOOST_CHECK(cat2.find(cif::v2::key("parent_id") == 2 and cif::v2::key("name2") == "noot").size() == 0); save__cat_1.id
// BOOST_CHECK(cat2.find(cif::v2::key("parent_id") == 2 and cif::v2::key("name2") == "n2").size() == 1); _item.name '_cat_1.id'
// BOOST_CHECK(cat2.find(cif::v2::key("parent_id") == 20 and cif::v2::key("name2") == "noot").size() == 1); _item.category_id cat_1
// BOOST_CHECK(cat2.find(cif::v2::key("parent_id") == 20 and cif::v2::key("name2") == "n2").size() == 0); _item.mandatory_code yes
_item_type.code int
save_
// // // -------------------------------------------------------------------- save_cat_2
_category.description 'A second simple test category'
_category.id cat_2
_category.mandatory_code no
_category_key.name '_cat_2.id'
save_
// // cat1.erase(cif::v2::key("id") == 10); save__cat_2.id
_item.name '_cat_2.id'
_item.category_id cat_2
_item.mandatory_code yes
_item_type.code int
save_
// // BOOST_CHECK(cat1.size() == 2); save__cat_2.parent_id
// // BOOST_CHECK(cat2.size() == 2); _item.name '_cat_2.parent_id'
_item.category_id cat_2
_item.mandatory_code yes
_item_type.code int
save_
// // cat1.erase(cif::v2::key("id") == 20); save__cat_2.parent_id2
_item.name '_cat_2.parent_id2'
_item.category_id cat_2
_item.mandatory_code no
_item_type.code code
save_
// // BOOST_CHECK(cat1.size() == 1); save__cat_2.parent_id3
// // BOOST_CHECK(cat2.size() == 1); _item.name '_cat_2.parent_id3'
// } _item.category_id cat_2
_item.mandatory_code no
_item_type.code code
save_
// // -------------------------------------------------------------------- loop_
_pdbx_item_linked_group_list.child_category_id
_pdbx_item_linked_group_list.link_group_id
_pdbx_item_linked_group_list.child_name
_pdbx_item_linked_group_list.parent_name
_pdbx_item_linked_group_list.parent_category_id
cat_2 1 '_cat_2.parent_id' '_cat_1.id' cat_1
cat_2 2 '_cat_2.parent_id2' '_cat_1.id' cat_1
cat_2 3 '_cat_2.parent_id3' '_cat_1.id' cat_1
// BOOST_AUTO_TEST_CASE(d4) loop_
// { _pdbx_item_linked_group.category_id
// const char dict[] = R"( _pdbx_item_linked_group.link_group_id
// data_test_dict.dic _pdbx_item_linked_group.label
// _datablock.id test_dict.dic cat_2 1 cat_2:cat_1:1
// _datablock.description cat_2 2 cat_2:cat_1:2
// ; cat_2 3 cat_2:cat_1:3
// A test dictionary )";
// ;
// _dictionary.title test_dict.dic
// _dictionary.datablock_id test_dict.dic
// _dictionary.version 1.0
// loop_
// _item_type_list.code
// _item_type_list.primitive_code
// _item_type_list.construct
// code char
// '[][_,.;:"&<>()/\{}'`~!@#$%A-Za-z0-9*|+-]*'
// text char
// '[][ \n\t()_,.;:"&<>/\{}'`~!@#$%?+=*A-Za-z0-9|^-]*'
// int numb
// '[+-]?[0-9]+'
// save_cat_1
// _category.description 'A simple test category'
// _category.id cat_1
// _category.mandatory_code no
// _category_key.name '_cat_1.id'
// save_
// save__cat_1.id
// _item.name '_cat_1.id'
// _item.category_id cat_1
// _item.mandatory_code yes
// _item_linked.child_name '_cat_2.parent_id'
// _item_linked.parent_name '_cat_1.id'
// _item_type.code int
// save_
// save__cat_1.id2
// _item.name '_cat_1.id2'
// _item.category_id cat_1
// _item.mandatory_code no
// _item_linked.child_name '_cat_2.parent_id2'
// _item_linked.parent_name '_cat_1.id2'
// _item_type.code code
// save_
// save__cat_1.id3
// _item.name '_cat_1.id3'
// _item.category_id cat_1
// _item.mandatory_code no
// _item_linked.child_name '_cat_2.parent_id3'
// _item_linked.parent_name '_cat_1.id3'
// _item_type.code text
// save_
// save_cat_2
// _category.description 'A second simple test category'
// _category.id cat_2
// _category.mandatory_code no
// _category_key.name '_cat_2.id'
// save_
// save__cat_2.id
// _item.name '_cat_2.id'
// _item.category_id cat_2
// _item.mandatory_code yes
// _item_type.code int
// save_
// save__cat_2.parent_id
// _item.name '_cat_2.parent_id'
// _item.category_id cat_2
// _item.mandatory_code yes
// _item_type.code int
// save_
// save__cat_2.parent_id2
// _item.name '_cat_2.parent_id2'
// _item.category_id cat_2
// _item.mandatory_code no
// _item_type.code code
// save_
// save__cat_2.parent_id3
// _item.name '_cat_2.parent_id3'
// _item.category_id cat_2
// _item.mandatory_code no
// _item_type.code code
// save_
// )";
// struct membuf : public std::streambuf
// {
// membuf(char *text, size_t length)
// {
// this->setg(text, text, text + length);
// }
// } buffer(const_cast<char *>(dict), sizeof(dict) - 1);
// std::istream is_dict(&buffer); struct membuf : public std::streambuf
{
membuf(char *text, size_t length)
{
this->setg(text, text, text + length);
}
} buffer(const_cast<char *>(dict), sizeof(dict) - 1);
// cif::v2::validator validator("test", is_dict); std::istream is_dict(&buffer);
// cif::File f; auto validator = cif::v2::parse_dictionary("test", is_dict);
// f.setValidator(&validator);
// // -------------------------------------------------------------------- cif::v2::file f;
f.set_validator(&validator);
// const char data[] = R"( // --------------------------------------------------------------------
// data_test
// loop_
// _cat_1.id
// _cat_1.id2
// _cat_1.id3
// 1 aap aap
// 2 . noot
// 3 mies .
// 4 . .
// loop_ const char data[] = R"(
// _cat_2.id data_test
// _cat_2.parent_id loop_
// _cat_2.parent_id2 _cat_1.id
// _cat_2.parent_id3 1
// 1 1 aap aap 2
// 2 1 . x 3
// 3 1 aap .
// 4 2 noot noot loop_
// 5 2 . noot _cat_2.id
// 6 2 noot . _cat_2.parent_id
// 7 2 . . _cat_2.parent_id2
// 8 3 mies mies _cat_2.parent_id3
// 9 3 . mies 1 1 ? ?
// 10 3 mies . 2 ? 1 ?
// 11 4 roos roos 3 ? ? 1
// 12 4 . roos 4 2 2 ?
// 13 4 roos . 5 2 ? 2
// )"; 6 ? 2 2
7 3 3 3
// struct data_membuf : public std::streambuf )";
// {
// data_membuf(char *text, size_t length)
// {
// this->setg(text, text, text + length);
// }
// } data_buffer(const_cast<char *>(data), sizeof(data) - 1);
// std::istream is_data(&data_buffer); // --------------------------------------------------------------------
// f.load(is_data);
// auto &cat1 = f.front()["cat_1"]; struct data_membuf : public std::streambuf
// auto &cat2 = f.front()["cat_2"]; {
data_membuf(char *text, size_t length)
{
this->setg(text, text, text + length);
}
} data_buffer(const_cast<char *>(data), sizeof(data) - 1);
// // check a rename in parent and child std::istream is_data(&data_buffer);
f.load(is_data);
// for (auto r : cat1.find(cif::v2::key("id") == 1)) auto &cat1 = f.front()["cat_1"];
// { auto &cat2 = f.front()["cat_2"];
// r["id"] = 10;
// break;
// }
// BOOST_CHECK(cat1.size() == 4); // --------------------------------------------------------------------
// BOOST_CHECK(cat2.size() == 13); // check iterate children
// BOOST_CHECK(cat1.find(cif::v2::key("id") == 1).size() == 0); auto PR2set = cat1.find(cif::v2::key("id") == 2);
// BOOST_CHECK(cat1.find(cif::v2::key("id") == 10).size() == 1); BOOST_ASSERT(PR2set.size() == 1);
auto PR2 = PR2set.front();
BOOST_CHECK(PR2["id"].as<int>() == 2);
// BOOST_CHECK(cat2.find(cif::v2::key("parent_id") == 1).size() == 1); auto CR2set = cat1.get_children(PR2, cat2);
// BOOST_CHECK(cat2.find(cif::v2::key("parent_id") == 10).size() == 2); BOOST_ASSERT(CR2set.size() == 3);
// for (auto r : cat1.find(cif::v2::key("id") == 2)) std::vector<int> CRids;
// { std::transform(CR2set.begin(), CR2set.end(), std::back_inserter(CRids), [](cif::v2::row_handle r)
// r["id"] = 20; { return r["id"].as<int>(); });
// break; std::sort(CRids.begin(), CRids.end());
// } BOOST_CHECK(CRids == std::vector<int>({4, 5, 6}));
// BOOST_CHECK(cat1.size() == 4); // check a rename in parent and child
// BOOST_CHECK(cat2.size() == 13);
// BOOST_CHECK(cat1.find(cif::v2::key("id") == 2).size() == 0); for (auto r : cat1.find(cif::v2::key("id") == 1))
// BOOST_CHECK(cat1.find(cif::v2::key("id") == 20).size() == 1); {
r["id"] = 10;
break;
}
// BOOST_CHECK(cat2.find(cif::v2::key("parent_id") == 2).size() == 2); BOOST_CHECK(cat1.size() == 3);
// BOOST_CHECK(cat2.find(cif::v2::key("parent_id") == 20).size() == 2); BOOST_CHECK(cat2.size() == 7);
// for (auto r : cat1.find(cif::v2::key("id") == 3)) BOOST_CHECK(cat1.find(cif::v2::key("id") == 1).size() == 0);
// { BOOST_CHECK(cat1.find(cif::v2::key("id") == 10).size() == 1);
// r["id"] = 30;
// break;
// }
// BOOST_CHECK(cat1.size() == 4); BOOST_CHECK(cat2.find(cif::v2::key("parent_id") == 1).size() == 0);
// BOOST_CHECK(cat2.size() == 13); BOOST_CHECK(cat2.find(cif::v2::key("parent_id2") == 1).size() == 0);
BOOST_CHECK(cat2.find(cif::v2::key("parent_id3") == 1).size() == 0);
BOOST_CHECK(cat2.find(cif::v2::key("parent_id") == 10).size() == 1);
BOOST_CHECK(cat2.find(cif::v2::key("parent_id2") == 10).size() == 1);
BOOST_CHECK(cat2.find(cif::v2::key("parent_id3") == 10).size() == 1);
// BOOST_CHECK(cat1.find(cif::v2::key("id") == 3).size() == 0); for (auto r : cat1.find(cif::v2::key("id") == 2))
// BOOST_CHECK(cat1.find(cif::v2::key("id") == 30).size() == 1); {
r["id"] = 20;
break;
}
// BOOST_CHECK(cat2.find(cif::v2::key("parent_id") == 3).size() == 2); BOOST_CHECK(cat1.size() == 3);
// BOOST_CHECK(cat2.find(cif::v2::key("parent_id") == 30).size() == 1); BOOST_CHECK(cat2.size() == 7);
// for (auto r : cat1.find(cif::v2::key("id") == 4)) BOOST_CHECK(cat1.find(cif::v2::key("id") == 2).size() == 0);
// { BOOST_CHECK(cat1.find(cif::v2::key("id") == 20).size() == 1);
// r["id"] = 40;
// break;
// }
// BOOST_CHECK(cat1.size() == 4); BOOST_CHECK(cat2.find(cif::v2::key("parent_id") == 2).size() == 0);
// BOOST_CHECK(cat2.size() == 13); BOOST_CHECK(cat2.find(cif::v2::key("parent_id2") == 2).size() == 0);
BOOST_CHECK(cat2.find(cif::v2::key("parent_id3") == 2).size() == 0);
BOOST_CHECK(cat2.find(cif::v2::key("parent_id") == 20).size() == 2);
BOOST_CHECK(cat2.find(cif::v2::key("parent_id2") == 20).size() == 2);
BOOST_CHECK(cat2.find(cif::v2::key("parent_id3") == 20).size() == 2);
// BOOST_CHECK(cat1.find(cif::v2::key("id") == 4).size() == 0); for (auto r : cat1.find(cif::v2::key("id") == 3))
// BOOST_CHECK(cat1.find(cif::v2::key("id") == 10).size() == 1); {
r["id"] = 30;
break;
}
// BOOST_CHECK(cat2.find(cif::v2::key("parent_id") == 4).size() == 3); BOOST_CHECK(cat1.size() == 3);
// BOOST_CHECK(cat2.find(cif::v2::key("parent_id") == 40).size() == 0); BOOST_CHECK(cat2.size() == 7);
// }
// // -------------------------------------------------------------------- BOOST_CHECK(cat1.find(cif::v2::key("id") == 3).size() == 0);
BOOST_CHECK(cat1.find(cif::v2::key("id") == 30).size() == 1);
// BOOST_AUTO_TEST_CASE(d5) BOOST_CHECK(cat2.find(cif::v2::key("parent_id") == 3).size() == 0);
// { BOOST_CHECK(cat2.find(cif::v2::key("parent_id2") == 3).size() == 0);
// const char dict[] = R"( BOOST_CHECK(cat2.find(cif::v2::key("parent_id3") == 3).size() == 0);
// data_test_dict.dic BOOST_CHECK(cat2.find(cif::v2::key("parent_id") == 30).size() == 1);
// _datablock.id test_dict.dic BOOST_CHECK(cat2.find(cif::v2::key("parent_id2") == 30).size() == 1);
// _datablock.description BOOST_CHECK(cat2.find(cif::v2::key("parent_id3") == 30).size() == 1);
// ;
// A test dictionary
// ;
// _dictionary.title test_dict.dic
// _dictionary.datablock_id test_dict.dic
// _dictionary.version 1.0
// loop_
// _item_type_list.code
// _item_type_list.primitive_code
// _item_type_list.construct
// code char
// '[][_,.;:"&<>()/\{}'`~!@#$%A-Za-z0-9*|+-]*'
// text char
// '[][ \n\t()_,.;:"&<>/\{}'`~!@#$%?+=*A-Za-z0-9|^-]*'
// int numb
// '[+-]?[0-9]+'
// save_cat_1
// _category.description 'A simple test category'
// _category.id cat_1
// _category.mandatory_code no
// _category_key.name '_cat_1.id'
// save_
// save__cat_1.id
// _item.name '_cat_1.id'
// _item.category_id cat_1
// _item.mandatory_code yes
// _item_type.code int
// save_
// save_cat_2
// _category.description 'A second simple test category'
// _category.id cat_2
// _category.mandatory_code no
// _category_key.name '_cat_2.id'
// save_
// save__cat_2.id
// _item.name '_cat_2.id'
// _item.category_id cat_2
// _item.mandatory_code yes
// _item_type.code int
// save_
// save__cat_2.parent_id
// _item.name '_cat_2.parent_id'
// _item.category_id cat_2
// _item.mandatory_code yes
// _item_type.code int
// save_
// save__cat_2.parent_id2
// _item.name '_cat_2.parent_id2'
// _item.category_id cat_2
// _item.mandatory_code no
// _item_type.code code
// save_
// save__cat_2.parent_id3
// _item.name '_cat_2.parent_id3'
// _item.category_id cat_2
// _item.mandatory_code no
// _item_type.code code
// save_
// loop_ // test delete
// _pdbx_item_linked_group_list.child_category_id
// _pdbx_item_linked_group_list.link_group_id
// _pdbx_item_linked_group_list.child_name
// _pdbx_item_linked_group_list.parent_name
// _pdbx_item_linked_group_list.parent_category_id
// cat_2 1 '_cat_2.parent_id' '_cat_1.id' cat_1
// cat_2 2 '_cat_2.parent_id2' '_cat_1.id' cat_1
// cat_2 3 '_cat_2.parent_id3' '_cat_1.id' cat_1
// loop_ cat1.erase(cif::v2::key("id") == 10);
// _pdbx_item_linked_group.category_id BOOST_CHECK(cat1.size() == 2);
// _pdbx_item_linked_group.link_group_id BOOST_CHECK(cat2.size() == 4);
// _pdbx_item_linked_group.label
// cat_2 1 cat_2:cat_1:1
// cat_2 2 cat_2:cat_1:2
// cat_2 3 cat_2:cat_1:3
// )";
// struct membuf : public std::streambuf
// {
// membuf(char *text, size_t length)
// {
// this->setg(text, text, text + length);
// }
// } buffer(const_cast<char *>(dict), sizeof(dict) - 1);
// std::istream is_dict(&buffer); cat1.erase(cif::v2::key("id") == 20);
BOOST_CHECK(cat1.size() == 1);
BOOST_CHECK(cat2.size() == 1);
// cif::v2::validator validator("test", is_dict); cat1.erase(cif::v2::key("id") == 30);
BOOST_CHECK(cat1.size() == 0);
BOOST_CHECK(cat2.size() == 0);
}
// cif::File f; // --------------------------------------------------------------------
// f.setValidator(&validator);
// // -------------------------------------------------------------------- BOOST_AUTO_TEST_CASE(c1)
{
cif::VERBOSE = 1;
// const char data[] = R"( auto f = R"(data_TEST
// data_test #
// loop_ loop_
// _cat_1.id _test.id
// 1 _test.name
// 2 1 aap
// 3 2 noot
3 mies
4 .
5 ?
)"_cf;
// loop_ auto &db = f.front();
// _cat_2.id
// _cat_2.parent_id
// _cat_2.parent_id2
// _cat_2.parent_id3
// 1 1 ? ?
// 2 ? 1 ?
// 3 ? ? 1
// 4 2 2 ?
// 5 2 ? 2
// 6 ? 2 2
// 7 3 3 3
// )";
// // --------------------------------------------------------------------
// struct data_membuf : public std::streambuf
// {
// data_membuf(char *text, size_t length)
// {
// this->setg(text, text, text + length);
// }
// } data_buffer(const_cast<char *>(data), sizeof(data) - 1);
// std::istream is_data(&data_buffer); for (auto r : db["test"].find(cif::v2::key("id") == 1))
// f.load(is_data); {
const auto &[id, name] = r.get<int, std::string>({"id", "name"});
BOOST_CHECK(id == 1);
BOOST_CHECK(name == "aap");
}
// auto &cat1 = f.front()["cat_1"]; for (auto r : db["test"].find(cif::v2::key("id") == 4))
// auto &cat2 = f.front()["cat_2"]; {
const auto &[id, name] = r.get<int, std::string>({"id", "name"});
BOOST_CHECK(id == 4);
BOOST_CHECK(name.empty());
}
// // -------------------------------------------------------------------- for (auto r : db["test"].find(cif::v2::key("id") == 5))
// // check iterate children {
const auto &[id, name] = r.get<int, std::string>({"id", "name"});
BOOST_CHECK(id == 5);
BOOST_CHECK(name.empty());
}
// auto PR2set = cat1.find(cif::v2::key("id") == 2); // optional
// BOOST_ASSERT(PR2set.size() == 1);
// auto PR2 = PR2set.front();
// BOOST_CHECK(PR2["id"].as<int>() == 2);
// auto CR2set = cat1.getChildren(PR2, "cat_2"); for (auto r : db["test"])
// BOOST_ASSERT(CR2set.size() == 3); {
const auto &[id, name] = r.get<int, std::optional<std::string>>({"id", "name"});
switch (id)
{
case 1: BOOST_CHECK(name == "aap"); break;
case 2: BOOST_CHECK(name == "noot"); break;
case 3: BOOST_CHECK(name == "mies"); break;
case 4:
case 5: BOOST_CHECK(not name); break;
default:
BOOST_CHECK(false);
}
}
}
// std::vector<int> CRids; BOOST_AUTO_TEST_CASE(c2)
// std::transform(CR2set.begin(), CR2set.end(), std::back_inserter(CRids), [](cif::Row r) {
// { return r["id"].as<int>(); }); cif::VERBOSE = 1;
// std::sort(CRids.begin(), CRids.end());
// BOOST_CHECK(CRids == std::vector<int>({4, 5, 6}));
// // check a rename in parent and child auto f = R"(data_TEST
#
loop_
_test.id
_test.name
1 aap
2 noot
3 mies
4 .
5 ?
)"_cf;
// for (auto r : cat1.find(cif::v2::key("id") == 1)) auto &db = f.front();
// {
// r["id"] = 10;
// break;
// }
// BOOST_CHECK(cat1.size() == 3); // query tests
// BOOST_CHECK(cat2.size() == 7);
// BOOST_CHECK(cat1.find(cif::v2::key("id") == 1).size() == 0); for (const auto &[id, name] : db["test"].rows<int, std::optional<std::string>>("id", "name"))
// BOOST_CHECK(cat1.find(cif::v2::key("id") == 10).size() == 1); {
switch (id)
{
case 1: BOOST_CHECK(name == "aap"); break;
case 2: BOOST_CHECK(name == "noot"); break;
case 3: BOOST_CHECK(name == "mies"); break;
case 4:
case 5: BOOST_CHECK(not name); break;
default:
BOOST_CHECK(false);
}
}
}
// BOOST_CHECK(cat2.find(cif::v2::key("parent_id") == 1).size() == 0); BOOST_AUTO_TEST_CASE(c3)
// BOOST_CHECK(cat2.find(cif::v2::key("parent_id2") == 1).size() == 0); {
// BOOST_CHECK(cat2.find(cif::v2::key("parent_id3") == 1).size() == 0); cif::VERBOSE = 1;
// BOOST_CHECK(cat2.find(cif::v2::key("parent_id") == 10).size() == 1);
// BOOST_CHECK(cat2.find(cif::v2::key("parent_id2") == 10).size() == 1);
// BOOST_CHECK(cat2.find(cif::v2::key("parent_id3") == 10).size() == 1);
// for (auto r : cat1.find(cif::v2::key("id") == 2)) auto f = R"(data_TEST
// { #
// r["id"] = 20; loop_
// break; _test.id
// } _test.name
1 aap
2 noot
3 mies
4 .
5 ?
)"_cf;
// BOOST_CHECK(cat1.size() == 3); auto &db = f.front();
// BOOST_CHECK(cat2.size() == 7);
// BOOST_CHECK(cat1.find(cif::v2::key("id") == 2).size() == 0); // query tests
// BOOST_CHECK(cat1.find(cif::v2::key("id") == 20).size() == 1); for (const auto &[id, name] : db["test"].find<int, std::optional<std::string>>(cif::v2::all(), "id", "name"))
{
switch (id)
{
case 1: BOOST_CHECK(name == "aap"); break;
case 2: BOOST_CHECK(name == "noot"); break;
case 3: BOOST_CHECK(name == "mies"); break;
case 4:
case 5: BOOST_CHECK(not name); break;
default:
BOOST_CHECK(false);
}
}
// BOOST_CHECK(cat2.find(cif::v2::key("parent_id") == 2).size() == 0); const auto &[id, name] = db["test"].find1<int, std::string>(cif::v2::key("id") == 1, "id", "name");
// BOOST_CHECK(cat2.find(cif::v2::key("parent_id2") == 2).size() == 0);
// BOOST_CHECK(cat2.find(cif::v2::key("parent_id3") == 2).size() == 0);
// BOOST_CHECK(cat2.find(cif::v2::key("parent_id") == 20).size() == 2);
// BOOST_CHECK(cat2.find(cif::v2::key("parent_id2") == 20).size() == 2);
// BOOST_CHECK(cat2.find(cif::v2::key("parent_id3") == 20).size() == 2);
// for (auto r : cat1.find(cif::v2::key("id") == 3)) BOOST_CHECK(id == 1);
// { BOOST_CHECK(name == "aap");
// r["id"] = 30; }
// break;
// }
// BOOST_CHECK(cat1.size() == 3); // --------------------------------------------------------------------
// BOOST_CHECK(cat2.size() == 7); // rename test
// BOOST_CHECK(cat1.find(cif::v2::key("id") == 3).size() == 0); BOOST_AUTO_TEST_CASE(r1)
// BOOST_CHECK(cat1.find(cif::v2::key("id") == 30).size() == 1); {
/*
Rationale:
// BOOST_CHECK(cat2.find(cif::v2::key("parent_id") == 3).size() == 0); The pdbx_mmcif dictionary contains inconsistent child-parent relations. E.g. atom_site is parent
// BOOST_CHECK(cat2.find(cif::v2::key("parent_id2") == 3).size() == 0); of pdbx_nonpoly_scheme which itself is a parent of pdbx_entity_nonpoly. If I want to rename a residue
// BOOST_CHECK(cat2.find(cif::v2::key("parent_id3") == 3).size() == 0); I cannot update pdbx_nonpoly_scheme since changing a parent changes children, but not vice versa.
// BOOST_CHECK(cat2.find(cif::v2::key("parent_id") == 30).size() == 1);
// BOOST_CHECK(cat2.find(cif::v2::key("parent_id2") == 30).size() == 1);
// BOOST_CHECK(cat2.find(cif::v2::key("parent_id3") == 30).size() == 1);
// // test delete But if I change the comp_id in atom_site, the pdbx_nonpoly_scheme is update, that's good, and then
pdbx_entity_nonpoly is updated and that's bad.
// cat1.erase(cif::v2::key("id") == 10); The idea is now that if we update a parent and a child that must change as well, we first check
// BOOST_CHECK(cat1.size() == 2); if there are more parents of this child that will not change. In that case we have to split the
// BOOST_CHECK(cat2.size() == 4); child into two, one with the new value and one with the old. We then of course have to split all
children of this split row that are direct children.
*/
// cat1.erase(cif::v2::key("id") == 20); const char dict[] = R"(
// BOOST_CHECK(cat1.size() == 1); data_test_dict.dic
// BOOST_CHECK(cat2.size() == 1); _datablock.id test_dict.dic
_datablock.description
;
A test dictionary
;
_dictionary.title test_dict.dic
_dictionary.datablock_id test_dict.dic
_dictionary.version 1.0
// cat1.erase(cif::v2::key("id") == 30); loop_
// BOOST_CHECK(cat1.size() == 0); _item_type_list.code
// BOOST_CHECK(cat2.size() == 0); _item_type_list.primitive_code
// } _item_type_list.construct
code char
'[][_,.;:"&<>()/\{}'`~!@#$%A-Za-z0-9*|+-]*'
// // -------------------------------------------------------------------- text char
'[][ \n\t()_,.;:"&<>/\{}'`~!@#$%?+=*A-Za-z0-9|^-]*'
// BOOST_AUTO_TEST_CASE(c1) int numb
// { '[+-]?[0-9]+'
// cif::VERBOSE = 1;
// auto f = R"(data_TEST save_cat_1
// # _category.description 'A simple test category'
// loop_ _category.id cat_1
// _test.id _category.mandatory_code no
// _test.name _category_key.name '_cat_1.id'
// 1 aap save_
// 2 noot
// 3 mies
// 4 .
// 5 ?
// )"_cf;
// auto &db = f.front();
// for (auto r : db["test"].find(cif::v2::key("id") == 1))
// {
// const auto &[id, name] = r.get<int, std::string>({"id", "name"});
// BOOST_CHECK(id == 1);
// BOOST_CHECK(name == "aap");
// }
// for (auto r : db["test"].find(cif::v2::key("id") == 4)) save__cat_1.id
// { _item.name '_cat_1.id'
// const auto &[id, name] = r.get<int, std::string>({"id", "name"}); _item.category_id cat_1
// BOOST_CHECK(id == 4); _item.mandatory_code yes
// BOOST_CHECK(name.empty()); _item_linked.child_name '_cat_2.parent_id'
// } _item_linked.parent_name '_cat_1.id'
_item_type.code int
save_
// for (auto r : db["test"].find(cif::v2::key("id") == 5)) save__cat_1.name
// { _item.name '_cat_1.name'
// const auto &[id, name] = r.get<int, std::string>({"id", "name"}); _item.category_id cat_1
// BOOST_CHECK(id == 5); _item.mandatory_code yes
// BOOST_CHECK(name.empty()); _item_type.code code
// } save_
// // optional save__cat_1.desc
_item.name '_cat_1.desc'
_item.category_id cat_1
_item.mandatory_code yes
_item_type.code text
save_
// for (auto r : db["test"]) save_cat_2
// { _category.description 'A second simple test category'
// const auto &[id, name] = r.get<int, std::optional<std::string>>({"id", "name"}); _category.id cat_2
// switch (id) _category.mandatory_code no
// { _category_key.name '_cat_2.id'
// case 1: BOOST_CHECK(name == "aap"); break; save_
// case 2: BOOST_CHECK(name == "noot"); break;
// case 3: BOOST_CHECK(name == "mies"); break;
// case 4:
// case 5: BOOST_CHECK(not name); break;
// default:
// BOOST_CHECK(false);
// }
// }
// }
// BOOST_AUTO_TEST_CASE(c2) save__cat_2.id
// { _item.name '_cat_2.id'
// cif::VERBOSE = 1; _item.category_id cat_2
_item.mandatory_code yes
_item_type.code int
save_
// auto f = R"(data_TEST save__cat_2.name
// # _item.name '_cat_2.name'
// loop_ _item.category_id cat_2
// _test.id _item.mandatory_code yes
// _test.name _item_type.code code
// 1 aap save_
// 2 noot
// 3 mies
// 4 .
// 5 ?
// )"_cf;
// auto &db = f.front(); save__cat_2.num
_item.name '_cat_2.num'
_item.category_id cat_2
_item.mandatory_code yes
_item_type.code int
save_
// // query tests save__cat_2.desc
_item.name '_cat_2.desc'
_item.category_id cat_2
_item.mandatory_code yes
_item_type.code text
save_
// for (const auto &[id, name] : db["test"].rows<int, std::optional<std::string>>("id", "name")) save_cat_3
// { _category.description 'A third simple test category'
// switch (id) _category.id cat_3
// { _category.mandatory_code no
// case 1: BOOST_CHECK(name == "aap"); break; _category_key.name '_cat_3.id'
// case 2: BOOST_CHECK(name == "noot"); break; save_
// case 3: BOOST_CHECK(name == "mies"); break;
// case 4:
// case 5: BOOST_CHECK(not name); break;
// default:
// BOOST_CHECK(false);
// }
// }
// }
// BOOST_AUTO_TEST_CASE(c3) save__cat_3.id
// { _item.name '_cat_3.id'
// cif::VERBOSE = 1; _item.category_id cat_3
_item.mandatory_code yes
_item_type.code int
save_
// auto f = R"(data_TEST save__cat_3.name
// # _item.name '_cat_3.name'
// loop_ _item.category_id cat_3
// _test.id _item.mandatory_code yes
// _test.name _item_type.code code
// 1 aap save_
// 2 noot
// 3 mies
// 4 .
// 5 ?
// )"_cf;
// auto &db = f.front();
// // query tests
// for (const auto &[id, name] : db["test"].find<int, std::optional<std::string>>(cif::All(), "id", "name"))
// {
// switch (id)
// {
// case 1: BOOST_CHECK(name == "aap"); break;
// case 2: BOOST_CHECK(name == "noot"); break;
// case 3: BOOST_CHECK(name == "mies"); break;
// case 4:
// case 5: BOOST_CHECK(not name); break;
// default:
// BOOST_CHECK(false);
// }
// }
// const auto &[id, name] = db["test"].find1<int, std::string>(cif::v2::key("id") == 1, "id", "name"); save__cat_3.num
_item.name '_cat_3.num'
_item.category_id cat_3
_item.mandatory_code yes
_item_type.code int
save_
// BOOST_CHECK(id == 1); loop_
// BOOST_CHECK(name == "aap"); _pdbx_item_linked_group_list.child_category_id
// } _pdbx_item_linked_group_list.link_group_id
_pdbx_item_linked_group_list.child_name
_pdbx_item_linked_group_list.parent_name
_pdbx_item_linked_group_list.parent_category_id
cat_1 1 '_cat_1.name' '_cat_2.name' cat_2
cat_2 1 '_cat_2.name' '_cat_3.name' cat_3
cat_2 1 '_cat_2.num' '_cat_3.num' cat_3
// // -------------------------------------------------------------------- )";
// // rename test
// BOOST_AUTO_TEST_CASE(r1) struct membuf : public std::streambuf
// { {
// /* membuf(char *text, size_t length)
// Rationale: {
this->setg(text, text, text + length);
// The pdbx_mmcif dictionary contains inconsistent child-parent relations. E.g. atom_site is parent }
// of pdbx_nonpoly_scheme which itself is a parent of pdbx_entity_nonpoly. If I want to rename a residue } buffer(const_cast<char *>(dict), sizeof(dict) - 1);
// I cannot update pdbx_nonpoly_scheme since changing a parent changes children, but not vice versa.
// But if I change the comp_id in atom_site, the pdbx_nonpoly_scheme is update, that's good, and then
// pdbx_entity_nonpoly is updated and that's bad.
// The idea is now that if we update a parent and a child that must change as well, we first check
// if there are more parents of this child that will not change. In that case we have to split the
// child into two, one with the new value and one with the old. We then of course have to split all
// children of this split row that are direct children.
// */
// const char dict[] = R"(
// data_test_dict.dic
// _datablock.id test_dict.dic
// _datablock.description
// ;
// A test dictionary
// ;
// _dictionary.title test_dict.dic
// _dictionary.datablock_id test_dict.dic
// _dictionary.version 1.0
// loop_
// _item_type_list.code
// _item_type_list.primitive_code
// _item_type_list.construct
// code char
// '[][_,.;:"&<>()/\{}'`~!@#$%A-Za-z0-9*|+-]*'
// text char
// '[][ \n\t()_,.;:"&<>/\{}'`~!@#$%?+=*A-Za-z0-9|^-]*'
// int numb
// '[+-]?[0-9]+'
// save_cat_1
// _category.description 'A simple test category'
// _category.id cat_1
// _category.mandatory_code no
// _category_key.name '_cat_1.id'
// save_
// save__cat_1.id
// _item.name '_cat_1.id'
// _item.category_id cat_1
// _item.mandatory_code yes
// _item_linked.child_name '_cat_2.parent_id'
// _item_linked.parent_name '_cat_1.id'
// _item_type.code int
// save_
// save__cat_1.name
// _item.name '_cat_1.name'
// _item.category_id cat_1
// _item.mandatory_code yes
// _item_type.code code
// save_
// save__cat_1.desc
// _item.name '_cat_1.desc'
// _item.category_id cat_1
// _item.mandatory_code yes
// _item_type.code text
// save_
// save_cat_2
// _category.description 'A second simple test category'
// _category.id cat_2
// _category.mandatory_code no
// _category_key.name '_cat_2.id'
// save_
// save__cat_2.id
// _item.name '_cat_2.id'
// _item.category_id cat_2
// _item.mandatory_code yes
// _item_type.code int
// save_
// save__cat_2.name
// _item.name '_cat_2.name'
// _item.category_id cat_2
// _item.mandatory_code yes
// _item_type.code code
// save_
// save__cat_2.num
// _item.name '_cat_2.num'
// _item.category_id cat_2
// _item.mandatory_code yes
// _item_type.code int
// save_
// save__cat_2.desc
// _item.name '_cat_2.desc'
// _item.category_id cat_2
// _item.mandatory_code yes
// _item_type.code text
// save_
// save_cat_3
// _category.description 'A third simple test category'
// _category.id cat_3
// _category.mandatory_code no
// _category_key.name '_cat_3.id'
// save_
// save__cat_3.id
// _item.name '_cat_3.id'
// _item.category_id cat_3
// _item.mandatory_code yes
// _item_type.code int
// save_
// save__cat_3.name
// _item.name '_cat_3.name'
// _item.category_id cat_3
// _item.mandatory_code yes
// _item_type.code code
// save_
// save__cat_3.num
// _item.name '_cat_3.num'
// _item.category_id cat_3
// _item.mandatory_code yes
// _item_type.code int
// save_
// loop_ std::istream is_dict(&buffer);
// _pdbx_item_linked_group_list.child_category_id
// _pdbx_item_linked_group_list.link_group_id
// _pdbx_item_linked_group_list.child_name
// _pdbx_item_linked_group_list.parent_name
// _pdbx_item_linked_group_list.parent_category_id
// cat_1 1 '_cat_1.name' '_cat_2.name' cat_2
// cat_2 1 '_cat_2.name' '_cat_3.name' cat_3
// cat_2 1 '_cat_2.num' '_cat_3.num' cat_3
// )";
// struct membuf : public std::streambuf
// {
// membuf(char *text, size_t length)
// {
// this->setg(text, text, text + length);
// }
// } buffer(const_cast<char *>(dict), sizeof(dict) - 1);
// std::istream is_dict(&buffer); auto validator = cif::v2::parse_dictionary("test", is_dict);
// cif::v2::validator validator("test", is_dict);
// cif::File f; cif::v2::file f;
// f.setValidator(&validator); f.set_validator(&validator);
// // -------------------------------------------------------------------- // --------------------------------------------------------------------
// const char data[] = R"( const char data[] = R"(
// data_test data_test
// loop_ loop_
// _cat_1.id _cat_1.id
// _cat_1.name _cat_1.name
// _cat_1.desc _cat_1.desc
// 1 aap Aap 1 aap Aap
// 2 noot Noot 2 noot Noot
// 3 mies Mies 3 mies Mies
// loop_ loop_
// _cat_2.id _cat_2.id
// _cat_2.name _cat_2.name
// _cat_2.num _cat_2.num
// _cat_2.desc _cat_2.desc
// 1 aap 1 'Een dier' 1 aap 1 'Een dier'
// 2 aap 2 'Een andere aap' 2 aap 2 'Een andere aap'
// 3 noot 1 'walnoot bijvoorbeeld' 3 noot 1 'walnoot bijvoorbeeld'
// loop_ loop_
// _cat_3.id _cat_3.id
// _cat_3.name _cat_3.name
// _cat_3.num _cat_3.num
// 1 aap 1 1 aap 1
// 2 aap 2 2 aap 2
// )"; )";
// using namespace cif::literals; using namespace cif::v2::literals;
// struct data_membuf : public std::streambuf struct data_membuf : public std::streambuf
// { {
// data_membuf(char *text, size_t length) data_membuf(char *text, size_t length)
// { {
// this->setg(text, text, text + length); this->setg(text, text, text + length);
// } }
// } data_buffer(const_cast<char *>(data), sizeof(data) - 1); } data_buffer(const_cast<char *>(data), sizeof(data) - 1);
// std::istream is_data(&data_buffer); std::istream is_data(&data_buffer);
// f.load(is_data); f.load(is_data);
// auto &cat1 = f.front()["cat_1"]; auto &cat1 = f.front()["cat_1"];
// auto &cat2 = f.front()["cat_2"]; auto &cat2 = f.front()["cat_2"];
// auto &cat3 = f.front()["cat_3"]; auto &cat3 = f.front()["cat_3"];
// cat3.update_value("name"_key == "aap" and "num"_key == 1, "name", "aapje"); cat3.update_value("name"_key == "aap" and "num"_key == 1, "name", "aapje");
// BOOST_CHECK(cat3.size() == 2); BOOST_CHECK(cat3.size() == 2);
// { {
// int id, num; int id, num;
// std::string name; std::string name;
// cif::tie(id, name, num) = cat3.front().get("id", "name", "num"); cif::v2::tie(id, name, num) = cat3.front().get("id", "name", "num");
// BOOST_CHECK(id == 1); BOOST_CHECK(id == 1);
// BOOST_CHECK(num == 1); BOOST_CHECK(num == 1);
// BOOST_CHECK(name == "aapje"); BOOST_CHECK(name == "aapje");
// cif::tie(id, name, num) = cat3.back().get("id", "name", "num"); cif::v2::tie(id, name, num) = cat3.back().get("id", "name", "num");
// BOOST_CHECK(id == 2); BOOST_CHECK(id == 2);
// BOOST_CHECK(num == 2); BOOST_CHECK(num == 2);
// BOOST_CHECK(name == "aap"); BOOST_CHECK(name == "aap");
// } }
// int i = 0; int i = 0;
// for (const auto &[id, name, num, desc] : cat2.rows<int, std::string, int, std::string>("id", "name", "num", "desc")) for (const auto &[id, name, num, desc] : cat2.rows<int, std::string, int, std::string>("id", "name", "num", "desc"))
// { {
// switch (++i) switch (++i)
// { {
// case 1: case 1:
// BOOST_CHECK(id == 1); BOOST_CHECK(id == 1);
// BOOST_CHECK(num == 1); BOOST_CHECK(num == 1);
// BOOST_CHECK(name == "aapje"); BOOST_CHECK(name == "aapje");
// BOOST_CHECK(desc == "Een dier"); BOOST_CHECK(desc == "Een dier");
// break; break;
// case 2: case 2:
// BOOST_CHECK(id == 2); BOOST_CHECK(id == 2);
// BOOST_CHECK(num == 2); BOOST_CHECK(num == 2);
// BOOST_CHECK(name == "aap"); BOOST_CHECK(name == "aap");
// BOOST_CHECK(desc == "Een andere aap"); BOOST_CHECK(desc == "Een andere aap");
// break; break;
// case 3: case 3:
// BOOST_CHECK(id == 3); BOOST_CHECK(id == 3);
// BOOST_CHECK(num == 1); BOOST_CHECK(num == 1);
// BOOST_CHECK(name == "noot"); BOOST_CHECK(name == "noot");
// BOOST_CHECK(desc == "walnoot bijvoorbeeld"); BOOST_CHECK(desc == "walnoot bijvoorbeeld");
// break; break;
// default: default:
// BOOST_FAIL("Unexpected record"); BOOST_FAIL("Unexpected record");
// } }
// } }
// BOOST_CHECK(cat1.size() == 4); BOOST_CHECK(cat1.size() == 4);
// i = 0; i = 0;
// for (const auto &[id, name, desc] : cat1.rows<int, std::string, std::string>("id", "name", "desc")) for (const auto &[id, name, desc] : cat1.rows<int, std::string, std::string>("id", "name", "desc"))
// { {
// switch (++i) switch (++i)
// { {
// case 1: case 1:
// BOOST_CHECK(id == 1); BOOST_CHECK(id == 1);
// BOOST_CHECK(name == "aapje"); BOOST_CHECK(name == "aapje");
// BOOST_CHECK(desc == "Aap"); BOOST_CHECK(desc == "Aap");
// break; break;
// case 2: case 2:
// BOOST_CHECK(id == 2); BOOST_CHECK(id == 2);
// BOOST_CHECK(name == "noot"); BOOST_CHECK(name == "noot");
// BOOST_CHECK(desc == "Noot"); BOOST_CHECK(desc == "Noot");
// break; break;
// case 3: case 3:
// BOOST_CHECK(id == 3); BOOST_CHECK(id == 3);
// BOOST_CHECK(name == "mies"); BOOST_CHECK(name == "mies");
// BOOST_CHECK(desc == "Mies"); BOOST_CHECK(desc == "Mies");
// break; break;
// case 4: case 4:
// BOOST_CHECK(id == 4); BOOST_CHECK(id == 4);
// BOOST_CHECK(name == "aap"); BOOST_CHECK(name == "aap");
// BOOST_CHECK(desc == "Aap"); BOOST_CHECK(desc == "Aap");
// break; break;
// default: default:
// BOOST_FAIL("Unexpected record"); BOOST_FAIL("Unexpected record");
// } }
// } }
// f.save(std::cout); // f.save(std::cout);
// } }
// // -------------------------------------------------------------------- // --------------------------------------------------------------------
// BOOST_AUTO_TEST_CASE(bondmap_1) // BOOST_AUTO_TEST_CASE(bondmap_1)
// { // {
...@@ -1929,195 +1930,101 @@ _cat_2.desc ...@@ -1929,195 +1930,101 @@ _cat_2.desc
// BOOST_CHECK(mmcif::BondMap::atomIDsForCompound("UN_").empty() == false); // BOOST_CHECK(mmcif::BondMap::atomIDsForCompound("UN_").empty() == false);
// } // }
// BOOST_AUTO_TEST_CASE(reading_file_1) BOOST_AUTO_TEST_CASE(reading_file_1)
// { {
// std::istringstream is("Hello, world!"); std::istringstream is("Hello, world!");
// cif::File file;
// BOOST_CHECK_THROW(file.load(is), std::runtime_error);
// }
// // 3d tests
// using namespace mmcif;
// BOOST_AUTO_TEST_CASE(t1)
// {
// // std::random_device rnd;
// // std::mt19937 gen(rnd());
// // std::uniform_real_distribution<float> dis(0, 1);
// // Quaternion q{ dis(gen), dis(gen), dis(gen), dis(gen) };
// // q = Normalize(q);
// // Quaternion q{ 0.1, 0.2, 0.3, 0.4 };
// Quaternion q{0.5, 0.5, 0.5, 0.5};
// q = Normalize(q);
// const auto &&[angle0, axis0] = QuaternionToAngleAxis(q);
// std::vector<Point> p1{
// {16.979, 13.301, 44.555},
// {18.150, 13.525, 43.680},
// {18.656, 14.966, 43.784},
// {17.890, 15.889, 44.078},
// {17.678, 13.270, 42.255},
// {16.248, 13.734, 42.347},
// {15.762, 13.216, 43.724}};
// auto p2 = p1;
// CenterPoints(p1);
// for (auto &p : p2)
// p.rotate(q);
// CenterPoints(p2);
// auto q2 = AlignPoints(p1, p2);
// const auto &&[angle, axis] = QuaternionToAngleAxis(q2);
// BOOST_TEST(std::fmod(360 + angle, 360) == std::fmod(360 - angle0, 360), tt::tolerance(0.01));
// for (auto &p : p1)
// p.rotate(q2);
// float rmsd = RMSd(p1, p2);
// BOOST_TEST(rmsd < 1e-5);
// // std::cout << "rmsd: " << RMSd(p1, p2) << std::endl;
// }
// BOOST_AUTO_TEST_CASE(t2)
// {
// Point p[] = {
// { 1, 1, 0 },
// { 2, 1, 0 },
// { 1, 2, 0 }
// };
// Point xp = mmcif::CrossProduct(p[1] - p[0], p[2] - p[0]);
// Quaternion q = ConstructFromAngleAxis(45, xp); //mmcif::Normalize(Quaternion{45 * mmcif::kPI / 180, xp.mX, xp.mY, xp.mZ});
// auto &&[angle, axis] = mmcif::QuaternionToAngleAxis(q);
// BOOST_TEST(angle == 45, tt::tolerance(0.01));
// }
// BOOST_AUTO_TEST_CASE(t3)
// {
// Point p[] = {
// { 1, 1, 0 },
// { 2, 1, 0 },
// { 1, 2, 0 }
// };
// Point xp = mmcif::CrossProduct(p[1] - p[0], p[2] - p[0]);
// Quaternion q = ConstructFromAngleAxis(45, xp); //mmcif::Normalize(Quaternion{45 * mmcif::kPI / 180, xp.mX, xp.mY, xp.mZ});
// Point v = p[1];
// v -= p[0];
// v.rotate(q);
// v += p[0];
// std::cout << v << std::endl;
// double a = mmcif::Angle(v, p[0], p[1]);
// BOOST_TEST(a == 45, tt::tolerance(0.01));
// }
// BOOST_AUTO_TEST_CASE(parser_test_1)
// {
// auto data1 = R"(
// data_QM
// _test.text ??
// )"_cf;
// auto &db1 = data1.front(); cif::v2::file file;
// auto &test1 = db1["test"]; BOOST_CHECK_THROW(file.load(is), std::runtime_error);
}
// BOOST_CHECK_EQUAL(test1.size(), 1); BOOST_AUTO_TEST_CASE(parser_test_1)
{
auto data1 = R"(
data_QM
_test.text ??
)"_cf;
// for (auto r : test1) auto &db1 = data1.front();
// { auto &test1 = db1["test"];
// const auto &[text] = r.get<std::string>({"text"});
// BOOST_CHECK_EQUAL(text, "??");
// }
// std::stringstream ss; BOOST_CHECK_EQUAL(test1.size(), 1);
// data1.save(ss);
// auto data2 = cif::File(ss); for (auto r : test1)
{
const auto &[text] = r.get<std::string>({"text"});
BOOST_CHECK_EQUAL(text, "??");
}
// auto &db2 = data2.front(); std::stringstream ss;
// auto &test2 = db2["test"]; data1.save(ss);
// BOOST_CHECK_EQUAL(test2.size(), 1); auto data2 = cif::File(ss);
// for (auto r : test2) auto &db2 = data2.front();
// { auto &test2 = db2["test"];
// const auto &[text] = r.get<std::string>({"text"});
// BOOST_CHECK_EQUAL(text, "??");
// }
// }
// BOOST_AUTO_TEST_CASE(output_test_1) BOOST_CHECK_EQUAL(test2.size(), 1);
// {
// auto data1 = R"(
// data_Q
// loop_
// _test.text
// "stop_the_crap"
// 'and stop_ this too'
// 'data_dinges'
// 'blablaglobal_bla'
// boo.data_.whatever
// )"_cf;
// auto &db1 = data1.front(); for (auto r : test2)
// auto &test1 = db1["test"]; {
const auto &[text] = r.get<std::string>({"text"});
BOOST_CHECK_EQUAL(text, "??");
}
}
// struct T { BOOST_AUTO_TEST_CASE(output_test_1)
// const char *s; {
// bool q; auto data1 = R"(
// } kS[] = { data_Q
// { "stop_the_crap", false }, loop_
// { "and stop_ this too", false }, _test.text
// { "data_dinges", false }, "stop_the_crap"
// { "blablaglobal_bla", false }, 'and stop_ this too'
// { "boo.data_.whatever", true } 'data_dinges'
// }; 'blablaglobal_bla'
boo.data_.whatever
)"_cf;
auto &db1 = data1.front();
auto &test1 = db1["test"];
struct T {
const char *s;
bool q;
} kS[] = {
{ "stop_the_crap", false },
{ "and stop_ this too", false },
{ "data_dinges", false },
{ "blablaglobal_bla", false },
{ "boo.data_.whatever", true }
};
// BOOST_CHECK_EQUAL(test1.size(), sizeof(kS) / sizeof(T)); BOOST_CHECK_EQUAL(test1.size(), sizeof(kS) / sizeof(T));
// size_t i = 0; size_t i = 0;
// for (auto r : test1) for (auto r : test1)
// { {
// const auto &[text] = r.get<std::string>({"text"}); const auto &[text] = r.get<std::string>({"text"});
// BOOST_CHECK_EQUAL(text, kS[i].s); BOOST_CHECK_EQUAL(text, kS[i].s);
// BOOST_CHECK_EQUAL(cif::isUnquotedString(kS[i].s), kS[i].q); BOOST_CHECK_EQUAL(cif::isUnquotedString(kS[i].s), kS[i].q);
// ++i; ++i;
// } }
// std::stringstream ss; std::stringstream ss;
// data1.save(ss); data1.save(ss);
// auto data2 = cif::File(ss); auto data2 = cif::File(ss);
// auto &db2 = data2.front(); auto &db2 = data2.front();
// auto &test2 = db2["test"]; auto &test2 = db2["test"];
// BOOST_CHECK_EQUAL(test2.size(), sizeof(kS) / sizeof(T)); BOOST_CHECK_EQUAL(test2.size(), sizeof(kS) / sizeof(T));
// i = 0; i = 0;
// for (auto r : test2) for (auto r : test2)
// { {
// const auto &[text] = r.get<std::string>({"text"}); const auto &[text] = r.get<std::string>({"text"});
// BOOST_CHECK_EQUAL(text, kS[i++].s); BOOST_CHECK_EQUAL(text, kS[i++].s);
// } }
// } }
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