Commit 345c4778 by Maarten L. Hekkelman

start structure-test unit test

added compare functions for Datablock and Category
parent 0ccb2f88
...@@ -385,6 +385,7 @@ if(CIFPP_BUILD_TESTS) ...@@ -385,6 +385,7 @@ if(CIFPP_BUILD_TESTS)
list(APPEND CIFPP_tests list(APPEND CIFPP_tests
# pdb2cif # pdb2cif
rename-compound rename-compound
structure
unit) unit)
foreach(CIFPP_TEST IN LISTS CIFPP_tests) foreach(CIFPP_TEST IN LISTS CIFPP_tests)
......
...@@ -28,14 +28,14 @@ ...@@ -28,14 +28,14 @@
#include <string> #include <string>
#include <regex> #include <array>
#include <functional>
#include <iostream> #include <iostream>
#include <sstream>
#include <set>
#include <list> #include <list>
#include <array>
#include <optional> #include <optional>
#include <functional> #include <regex>
#include <set>
#include <sstream>
#include "cif++/CifUtils.hpp" #include "cif++/CifUtils.hpp"
...@@ -141,17 +141,31 @@ class Item ...@@ -141,17 +141,31 @@ class Item
public: public:
Item() {} Item() {}
template<typename T, std::enable_if_t<std::is_arithmetic_v<T>, int> = 0> template <typename T, std::enable_if_t<std::is_arithmetic_v<T>, int> = 0>
Item(const std::string& name, const T& value) Item(const std::string &name, const T &value)
: mName(name), mValue(std::to_string(value)) {} : mName(name)
, mValue(std::to_string(value))
{
}
Item(const std::string& name, const std::string& value) Item(const std::string &name, const std::string &value)
: mName(name), mValue(value) {} : mName(name)
, mValue(value)
{
}
Item(const Item& rhs) : mName(rhs.mName), mValue(rhs.mValue) {} Item(const Item &rhs)
Item(Item&& rhs) noexcept : mName(std::move(rhs.mName)), mValue(std::move(rhs.mValue)) {} : mName(rhs.mName)
, mValue(rhs.mValue)
{
}
Item(Item &&rhs) noexcept
: mName(std::move(rhs.mName))
, mValue(std::move(rhs.mValue))
{
}
Item& operator=(const Item& rhs) Item &operator=(const Item &rhs)
{ {
if (this != &rhs) if (this != &rhs)
{ {
...@@ -162,7 +176,7 @@ class Item ...@@ -162,7 +176,7 @@ class Item
return *this; return *this;
} }
Item& operator=(Item&& rhs) noexcept Item &operator=(Item &&rhs) noexcept
{ {
if (this != &rhs) if (this != &rhs)
{ {
...@@ -173,10 +187,10 @@ class Item ...@@ -173,10 +187,10 @@ class Item
return *this; return *this;
} }
const std::string& name() const { return mName; } const std::string &name() const { return mName; }
const std::string& value() const { return mValue; } const std::string &value() const { return mValue; }
void value(const std::string& v) { mValue = v; } void value(const std::string &v) { mValue = v; }
// empty means either null or unknown // empty means either null or unknown
bool empty() const; bool empty() const;
...@@ -188,7 +202,7 @@ class Item ...@@ -188,7 +202,7 @@ class Item
bool is_unknown() const; bool is_unknown() const;
size_t length() const { return mValue.length(); } size_t length() const { return mValue.length(); }
const char* c_str() const { return mValue.c_str(); } const char *c_str() const { return mValue.c_str(); }
private: private:
std::string mName; std::string mName;
...@@ -207,16 +221,16 @@ class Datablock ...@@ -207,16 +221,16 @@ class Datablock
using iterator = CategoryList::iterator; using iterator = CategoryList::iterator;
using const_iterator = CategoryList::const_iterator; using const_iterator = CategoryList::const_iterator;
Datablock(const std::string& name); Datablock(const std::string &name);
~Datablock(); ~Datablock();
Datablock(const Datablock&) = delete; Datablock(const Datablock &) = delete;
Datablock& operator=(const Datablock&) = delete; Datablock &operator=(const Datablock &) = delete;
std::string getName() const { return mName; } std::string getName() const { return mName; }
void setName(const std::string& n) { mName = n; } void setName(const std::string &n) { mName = n; }
std::string firstItem(const std::string& tag) const; std::string firstItem(const std::string &tag) const;
iterator begin() { return mCategories.begin(); } iterator begin() { return mCategories.begin(); }
iterator end() { return mCategories.end(); } iterator end() { return mCategories.end(); }
...@@ -224,32 +238,36 @@ class Datablock ...@@ -224,32 +238,36 @@ class Datablock
const_iterator begin() const { return mCategories.begin(); } const_iterator begin() const { return mCategories.begin(); }
const_iterator end() const { return mCategories.end(); } const_iterator end() const { return mCategories.end(); }
Category& operator[](const std::string& name); Category &operator[](const std::string &name);
std::tuple<iterator,bool> emplace(const std::string& name); std::tuple<iterator, bool> emplace(const std::string &name);
bool isValid(); bool isValid();
void validateLinks() const; void validateLinks() const;
void setValidator(Validator* v); void setValidator(Validator *v);
// this one only looks up a Category, returns nullptr if it does not exist // this one only looks up a Category, returns nullptr if it does not exist
Category* get(const std::string& name); const Category *get(const std::string &name) const;
Category *get(const std::string &name);
void getTagOrder(std::vector<std::string>& tags) const; void getTagOrder(std::vector<std::string> &tags) const;
void write(std::ostream& os, const std::vector<std::string>& order); void write(std::ostream &os, const std::vector<std::string> &order);
void write(std::ostream& os); void write(std::ostream &os);
// convenience function, add a line to the software category // convenience function, add a line to the software category
void add_software(const std::string& name, const std::string& classification, void add_software(const std::string &name, const std::string &classification,
const std::string& versionNr, const std::string& versionDate); const std::string &versionNr, const std::string &versionDate);
private: friend bool operator==(const Datablock &lhs, const Datablock &rhs);
friend std::ostream& operator<<(std::ostream &os, const Datablock &data);
private:
std::list<Category> mCategories; std::list<Category> mCategories;
std::string mName; std::string mName;
Validator* mValidator; Validator *mValidator;
Datablock* mNext; Datablock *mNext;
}; };
// -------------------------------------------------------------------- // --------------------------------------------------------------------
...@@ -264,20 +282,19 @@ namespace detail ...@@ -264,20 +282,19 @@ namespace detail
class ItemReference class ItemReference
{ {
public: public:
// conversion helper class // conversion helper class
template<typename T, typename = void> template <typename T, typename = void>
struct item_value_as; struct item_value_as;
template<typename T, std::enable_if_t<std::is_arithmetic_v<T>, int> = 0> template <typename T, std::enable_if_t<std::is_arithmetic_v<T>, int> = 0>
ItemReference& operator=(const T& value) ItemReference &operator=(const T &value)
{ {
this->operator=(std::to_string(value)); this->operator=(std::to_string(value));
return *this; return *this;
} }
template<typename T> template <typename T>
ItemReference& operator=(const std::optional<T>& value) ItemReference &operator=(const std::optional<T> &value)
{ {
if (value) if (value)
this->operator=(*value); this->operator=(*value);
...@@ -286,27 +303,27 @@ namespace detail ...@@ -286,27 +303,27 @@ namespace detail
return *this; return *this;
} }
ItemReference& operator=(const std::string& value); ItemReference &operator=(const std::string &value);
template<typename... Ts> template <typename... Ts>
void os(const Ts& ... v) void os(const Ts &...v)
{ {
std::ostringstream ss; std::ostringstream ss;
((ss << v), ...); ((ss << v), ...);
this->operator=(ss.str()); this->operator=(ss.str());
} }
void swap(ItemReference& b); void swap(ItemReference &b);
template<typename T = std::string> template <typename T = std::string>
auto as() const auto as() const
{ {
using value_type = std::remove_cv_t<std::remove_reference_t<T>>; using value_type = std::remove_cv_t<std::remove_reference_t<T>>;
return item_value_as<value_type>::convert(*this); return item_value_as<value_type>::convert(*this);
} }
template<typename T> template <typename T>
int compare(const T& value, bool icase) const int compare(const T &value, bool icase) const
{ {
return item_value_as<T>::compare(*this, value, icase); return item_value_as<T>::compare(*this, value, icase);
} }
...@@ -321,36 +338,45 @@ namespace detail ...@@ -321,36 +338,45 @@ namespace detail
// is_unknown means the field contains '?' // is_unknown means the field contains '?'
bool is_unknown() const; bool is_unknown() const;
const char* c_str() const; const char *c_str() const;
// the following returns the defaultValue from either the parameter // the following returns the defaultValue from either the parameter
// or, if specified, the value from _item_default.value in the dictionary // or, if specified, the value from _item_default.value in the dictionary
const char* c_str(const char* defaultValue) const; const char *c_str(const char *defaultValue) const;
bool operator!=(const std::string& s) const { return s != c_str(); } bool operator!=(const std::string &s) const { return s != c_str(); }
bool operator==(const std::string& s) const { return s == c_str(); } bool operator==(const std::string &s) const { return s == c_str(); }
private: private:
friend class ::cif::Row; friend class ::cif::Row;
ItemReference(const char* name, size_t column, Row& row) ItemReference(const char *name, size_t column, Row &row)
: mName(name), mColumn(column), mRow(row) {} : mName(name)
, mColumn(column)
, mRow(row)
{
}
ItemReference(const char* name, size_t column, const Row& row) ItemReference(const char *name, size_t column, const Row &row)
: mName(name), mColumn(column), mRow(const_cast<Row&>(row)), mConst(true) {} : mName(name)
, mColumn(column)
, mRow(const_cast<Row &>(row))
, mConst(true)
{
}
const char* mName; const char *mName;
size_t mColumn; size_t mColumn;
Row& mRow; Row &mRow;
bool mConst = false; bool mConst = false;
}; };
template<typename T> template <typename T>
struct ItemReference::item_value_as<T, std::enable_if_t<std::is_floating_point_v<T>>> struct ItemReference::item_value_as<T, std::enable_if_t<std::is_floating_point_v<T>>>
{ {
using value_type = std::remove_reference_t<std::remove_cv_t<T>>; using value_type = std::remove_reference_t<std::remove_cv_t<T>>;
static value_type convert(const ItemReference& ref) static value_type convert(const ItemReference &ref)
{ {
value_type result = {}; value_type result = {};
if (not ref.empty()) if (not ref.empty())
...@@ -358,11 +384,11 @@ namespace detail ...@@ -358,11 +384,11 @@ namespace detail
return result; return result;
} }
static int compare(const ItemReference& ref, double value, bool icase) static int compare(const ItemReference &ref, double value, bool icase)
{ {
int result = 0; int result = 0;
const char* s = ref.c_str(); const char *s = ref.c_str();
if (s == nullptr or *s == 0) if (s == nullptr or *s == 0)
result = 1; result = 1;
...@@ -388,10 +414,10 @@ namespace detail ...@@ -388,10 +414,10 @@ namespace detail
} }
}; };
template<typename T> template <typename T>
struct ItemReference::item_value_as<T, std::enable_if_t<std::is_integral_v<T> and std::is_unsigned_v<T> and not std::is_same_v<T, bool>>> struct ItemReference::item_value_as<T, std::enable_if_t<std::is_integral_v<T> and std::is_unsigned_v<T> and not std::is_same_v<T, bool>>>
{ {
static T convert(const ItemReference& ref) static T convert(const ItemReference &ref)
{ {
T result = {}; T result = {};
if (not ref.empty()) if (not ref.empty())
...@@ -399,11 +425,11 @@ namespace detail ...@@ -399,11 +425,11 @@ namespace detail
return result; return result;
} }
static int compare(const ItemReference& ref, unsigned long value, bool icase) static int compare(const ItemReference &ref, unsigned long value, bool icase)
{ {
int result = 0; int result = 0;
const char* s = ref.c_str(); const char *s = ref.c_str();
if (s == nullptr or *s == 0) if (s == nullptr or *s == 0)
result = 1; result = 1;
...@@ -429,10 +455,10 @@ namespace detail ...@@ -429,10 +455,10 @@ namespace detail
} }
}; };
template<typename T> template <typename T>
struct ItemReference::item_value_as<T, std::enable_if_t<std::is_integral_v<T> and std::is_signed_v<T> and not std::is_same_v<T, bool>>> struct ItemReference::item_value_as<T, std::enable_if_t<std::is_integral_v<T> and std::is_signed_v<T> and not std::is_same_v<T, bool>>>
{ {
static T convert(const ItemReference& ref) static T convert(const ItemReference &ref)
{ {
T result = {}; T result = {};
if (not ref.empty()) if (not ref.empty())
...@@ -440,11 +466,11 @@ namespace detail ...@@ -440,11 +466,11 @@ namespace detail
return result; return result;
} }
static int compare(const ItemReference& ref, long value, bool icase) static int compare(const ItemReference &ref, long value, bool icase)
{ {
int result = 0; int result = 0;
const char* s = ref.c_str(); const char *s = ref.c_str();
if (s == nullptr or *s == 0) if (s == nullptr or *s == 0)
result = 1; result = 1;
...@@ -470,10 +496,10 @@ namespace detail ...@@ -470,10 +496,10 @@ namespace detail
} }
}; };
template<typename T> template <typename T>
struct ItemReference::item_value_as<std::optional<T>> struct ItemReference::item_value_as<std::optional<T>>
{ {
static std::optional<T> convert(const ItemReference& ref) static std::optional<T> convert(const ItemReference &ref)
{ {
std::optional<T> result; std::optional<T> result;
if (ref) if (ref)
...@@ -481,7 +507,7 @@ namespace detail ...@@ -481,7 +507,7 @@ namespace detail
return result; return result;
} }
static int compare(const ItemReference& ref, std::optional<T> value, bool icase) static int compare(const ItemReference &ref, std::optional<T> value, bool icase)
{ {
if (ref.empty() and not value) if (ref.empty() and not value)
return 0; return 0;
...@@ -495,10 +521,10 @@ namespace detail ...@@ -495,10 +521,10 @@ namespace detail
} }
}; };
template<typename T> template <typename T>
struct ItemReference::item_value_as<T, std::enable_if_t<std::is_same_v<T, bool>>> struct ItemReference::item_value_as<T, std::enable_if_t<std::is_same_v<T, bool>>>
{ {
static bool convert(const ItemReference& ref) static bool convert(const ItemReference &ref)
{ {
bool result = false; bool result = false;
if (not ref.empty()) if (not ref.empty())
...@@ -506,7 +532,7 @@ namespace detail ...@@ -506,7 +532,7 @@ namespace detail
return result; return result;
} }
static int compare(const ItemReference& ref, bool value, bool icase) static int compare(const ItemReference &ref, bool value, bool icase)
{ {
bool rv = convert(ref); bool rv = convert(ref);
return value && rv ? 0 return value && rv ? 0
...@@ -514,51 +540,52 @@ namespace detail ...@@ -514,51 +540,52 @@ namespace detail
} }
}; };
template<size_t N> template <size_t N>
struct ItemReference::item_value_as<char[N]> struct ItemReference::item_value_as<char[N]>
{ {
static int compare(const ItemReference& ref, const char (&value)[N], bool icase) static int compare(const ItemReference &ref, const char (&value)[N], bool icase)
{ {
return icase ? cif::icompare(ref.c_str(), value) : std::strcmp(ref.c_str(), value); return icase ? cif::icompare(ref.c_str(), value) : std::strcmp(ref.c_str(), value);
} }
}; };
template<> template <>
struct ItemReference::item_value_as<const char*> struct ItemReference::item_value_as<const char *>
{ {
static const char* convert(const ItemReference& ref) static const char *convert(const ItemReference &ref)
{ {
return ref.c_str(); return ref.c_str();
} }
static int compare(const ItemReference& ref, const char* value, bool icase) static int compare(const ItemReference &ref, const char *value, bool icase)
{ {
return icase ? cif::icompare(ref.c_str(), value) : std::strcmp(ref.c_str(), value); return icase ? cif::icompare(ref.c_str(), value) : std::strcmp(ref.c_str(), value);
} }
}; };
template<> template <>
struct ItemReference::item_value_as<std::string> struct ItemReference::item_value_as<std::string>
{ {
static std::string convert(const ItemReference& ref) static std::string convert(const ItemReference &ref)
{ {
return ref.c_str(); return ref.c_str();
} }
static int compare(const ItemReference& ref, const std::string& value, bool icase) static int compare(const ItemReference &ref, const std::string &value, bool icase)
{ {
return icase ? cif::icompare(ref.c_str(), value) : std::strcmp(ref.c_str(), value.c_str()); return icase ? cif::icompare(ref.c_str(), value) : std::strcmp(ref.c_str(), value.c_str());
} }
}; };
// some helper classes to help create tuple result types // some helper classes to help create tuple result types
template<typename... C> template <typename... C>
struct getRowResult struct getRowResult
{ {
static constexpr size_t N = sizeof...(C); static constexpr size_t N = sizeof...(C);
getRowResult(const Row& r, std::array<size_t, N>&& columns) getRowResult(const Row &r, std::array<size_t, N> &&columns)
: mRow(r), mColumns(std::move(columns)) : mRow(r)
, mColumns(std::move(columns))
{ {
} }
...@@ -567,30 +594,33 @@ namespace detail ...@@ -567,30 +594,33 @@ namespace detail
return mRow[mColumns[ix]]; return mRow[mColumns[ix]];
} }
template<typename... Ts, std::enable_if_t<N == sizeof...(Ts), int> = 0> template <typename... Ts, std::enable_if_t<N == sizeof...(Ts), int> = 0>
operator std::tuple<Ts...>() const operator std::tuple<Ts...>() const
{ {
return get<Ts...>(std::index_sequence_for<Ts...>{}); return get<Ts...>(std::index_sequence_for<Ts...>{});
} }
template<typename... Ts, std::size_t... Is> template <typename... Ts, std::size_t... Is>
std::tuple<Ts...> get(std::index_sequence<Is...>) const std::tuple<Ts...> get(std::index_sequence<Is...>) const
{ {
return std::tuple<Ts...>{mRow[mColumns[Is]].template as<Ts>()...}; return std::tuple<Ts...>{mRow[mColumns[Is]].template as<Ts>()...};
} }
const Row& mRow; const Row &mRow;
std::array<size_t, N> mColumns; std::array<size_t, N> mColumns;
}; };
// we want to be able to tie some variables to a RowResult, for this we use tiewraps // we want to be able to tie some variables to a RowResult, for this we use tiewraps
template<typename... Ts> template <typename... Ts>
struct tieWrap struct tieWrap
{ {
tieWrap(Ts... args) : mVal(args...) {} tieWrap(Ts... args)
: mVal(args...)
{
}
template<typename RR> template <typename RR>
void operator=(const RR&& rr) void operator=(const RR &&rr)
{ {
// getRowResult will do the conversion, but only if the types // getRowResult will do the conversion, but only if the types
// are compatible. That means the number of parameters to the get() // are compatible. That means the number of parameters to the get()
...@@ -604,12 +634,12 @@ namespace detail ...@@ -604,12 +634,12 @@ namespace detail
std::tuple<Ts...> mVal; std::tuple<Ts...> mVal;
}; };
} } // namespace detail
template<typename... Ts> template <typename... Ts>
auto tie(Ts&... v) auto tie(Ts &...v)
{ {
return detail::tieWrap<Ts&...>(std::forward<Ts&>(v)...); return detail::tieWrap<Ts &...>(std::forward<Ts &>(v)...);
} }
class Row class Row
...@@ -622,19 +652,26 @@ class Row ...@@ -622,19 +652,26 @@ class Row
friend class RowSet; friend class RowSet;
Row() Row()
: mData(nullptr) {} : mData(nullptr)
{
}
Row(ItemRow* data) Row(ItemRow *data)
: mData(data) {} : mData(data)
{
}
Row(const ItemRow* data) Row(const ItemRow *data)
: mData(const_cast<ItemRow*>(data)), mCascade(false) {} : mData(const_cast<ItemRow *>(data))
, mCascade(false)
{
}
Row(const Row& rhs); Row(const Row &rhs);
Row& operator=(const Row& rhs); Row &operator=(const Row &rhs);
Row(Row&& rhs); Row(Row &&rhs);
Row& operator=(Row&& rhs); Row &operator=(Row &&rhs);
~Row(); ~Row();
...@@ -650,26 +687,30 @@ class Row ...@@ -650,26 +687,30 @@ class Row
using iterator_category = std::forward_iterator_tag; using iterator_category = std::forward_iterator_tag;
using value_type = const Item; using value_type = const Item;
using difference_type = std::ptrdiff_t; using difference_type = std::ptrdiff_t;
using pointer = value_type*; using pointer = value_type *;
using reference = value_type&; using reference = value_type &;
const_iterator(ItemRow* data, ItemValue* ptr); const_iterator(ItemRow *data, ItemValue *ptr);
reference operator*() { return mCurrent; } reference operator*() { return mCurrent; }
pointer operator->() { return &mCurrent; } pointer operator->() { return &mCurrent; }
const_iterator& operator++(); const_iterator &operator++();
const_iterator operator++(int) { const_iterator result(*this); this->operator++(); return result; } const_iterator operator++(int)
{
const_iterator result(*this);
this->operator++();
return result;
}
bool operator==(const const_iterator& rhs) const { return mPtr == rhs.mPtr; } bool operator==(const const_iterator &rhs) const { return mPtr == rhs.mPtr; }
bool operator!=(const const_iterator& rhs) const { return mPtr != rhs.mPtr; } bool operator!=(const const_iterator &rhs) const { return mPtr != rhs.mPtr; }
private: private:
void fetch(); void fetch();
ItemRow* mData; ItemRow *mData;
ItemValue* mPtr; ItemValue *mPtr;
Item mCurrent; Item mCurrent;
}; };
...@@ -693,32 +734,32 @@ class Row ...@@ -693,32 +734,32 @@ class Row
return detail::ItemReference("<anonymous column>", column, *this); return detail::ItemReference("<anonymous column>", column, *this);
} }
const detail::ItemReference operator[](const char* itemTag) const const detail::ItemReference operator[](const char *itemTag) const
{ {
size_t column = ColumnForItemTag(itemTag); size_t column = ColumnForItemTag(itemTag);
return detail::ItemReference(itemTag, column, *this); return detail::ItemReference(itemTag, column, *this);
} }
detail::ItemReference operator[](const char* itemTag) detail::ItemReference operator[](const char *itemTag)
{ {
size_t column = ColumnForItemTag(itemTag); size_t column = ColumnForItemTag(itemTag);
return detail::ItemReference(itemTag, column, *this); return detail::ItemReference(itemTag, column, *this);
} }
const detail::ItemReference operator[](const std::string& itemTag) const const detail::ItemReference operator[](const std::string &itemTag) const
{ {
size_t column = ColumnForItemTag(itemTag.c_str()); size_t column = ColumnForItemTag(itemTag.c_str());
return detail::ItemReference(itemTag.c_str(), column, *this); return detail::ItemReference(itemTag.c_str(), column, *this);
} }
detail::ItemReference operator[](const std::string& itemTag) detail::ItemReference operator[](const std::string &itemTag)
{ {
size_t column = ColumnForItemTag(itemTag.c_str()); size_t column = ColumnForItemTag(itemTag.c_str());
return detail::ItemReference(itemTag.c_str(), column, *this); return detail::ItemReference(itemTag.c_str(), column, *this);
} }
template<typename... Ts, size_t N> template <typename... Ts, size_t N>
std::tuple<Ts...> get(char const * const (&columns)[N]) const std::tuple<Ts...> get(char const *const (&columns)[N]) const
{ {
static_assert(sizeof...(Ts) == N, "Number of columns should be equal to number of types to return"); static_assert(sizeof...(Ts) == N, "Number of columns should be equal to number of types to return");
...@@ -728,44 +769,43 @@ class Row ...@@ -728,44 +769,43 @@ class Row
return detail::getRowResult<Ts...>(*this, std::move(cix)); return detail::getRowResult<Ts...>(*this, std::move(cix));
} }
template<typename... C> template <typename... C>
auto get(C... columns) const auto get(C... columns) const
{ {
return detail::getRowResult<C...>(*this, { ColumnForItemTag(columns)... }); return detail::getRowResult<C...>(*this, {ColumnForItemTag(columns)...});
} }
void assign(const std::vector<Item>& values); void assign(const std::vector<Item> &values);
void assign(const std::string& name, const std::string& value, bool updateLinked); void assign(const std::string &name, const std::string &value, bool updateLinked);
bool operator==(const Row& rhs) const bool operator==(const Row &rhs) const
{ {
return mData == rhs.mData; return mData == rhs.mData;
} }
bool operator!=(const Row& rhs) const bool operator!=(const Row &rhs) const
{ {
return mData != rhs.mData; return mData != rhs.mData;
} }
ItemRow* data() const { return mData; } ItemRow *data() const { return mData; }
void swap(Row& rhs) void swap(Row &rhs)
{ {
std::swap(mData, rhs.mData); std::swap(mData, rhs.mData);
} }
friend std::ostream& operator<<(std::ostream& os, const Row& row); friend std::ostream &operator<<(std::ostream &os, const Row &row);
private: private:
void assign(size_t column, const std::string &value, bool updateLinked);
void assign(const Item &i, bool updateLinked);
void assign(size_t column, const std::string& value, bool updateLinked); static void swap(size_t column, ItemRow *a, ItemRow *b);
void assign(const Item& i, bool updateLinked);
static void swap(size_t column, ItemRow* a, ItemRow* b); size_t ColumnForItemTag(const char *itemTag) const;
size_t ColumnForItemTag(const char* itemTag) const; ItemRow *mData;
ItemRow* mData;
uint32_t mLineNr = 0; uint32_t mLineNr = 0;
bool mCascade = true; bool mCascade = true;
}; };
...@@ -776,45 +816,50 @@ class Row ...@@ -776,45 +816,50 @@ class Row
namespace detail namespace detail
{ {
struct ConditionImpl struct ConditionImpl
{ {
virtual ~ConditionImpl() {} virtual ~ConditionImpl() {}
virtual void prepare(const Category& c) {} virtual void prepare(const Category &c) {}
virtual bool test(const Category& c, const Row& r) const = 0; virtual bool test(const Category &c, const Row &r) const = 0;
virtual void str(std::ostream& os) const = 0; virtual void str(std::ostream &os) const = 0;
}; };
struct AllConditionImpl : public ConditionImpl struct AllConditionImpl : public ConditionImpl
{ {
virtual bool test(const Category& c, const Row& r) const { return true; } virtual bool test(const Category &c, const Row &r) const { return true; }
virtual void str(std::ostream& os) const { os << "*"; } virtual void str(std::ostream &os) const { os << "*"; }
}; };
struct OrConditionImpl; struct OrConditionImpl;
struct AndConditionImpl; struct AndConditionImpl;
struct NotConditionImpl; struct NotConditionImpl;
} } // namespace detail
class Condition class Condition
{ {
public: public:
Condition()
: mImpl(nullptr)
{
}
Condition(detail::ConditionImpl *impl)
: mImpl(impl)
{
}
Condition() : mImpl(nullptr) {} Condition(const Condition &) = delete;
Condition(detail::ConditionImpl* impl) : mImpl(impl) {}
Condition(const Condition&) = delete;
Condition(Condition&& rhs) noexcept Condition(Condition &&rhs) noexcept
: mImpl(nullptr) : mImpl(nullptr)
{ {
std::swap(mImpl, rhs.mImpl); std::swap(mImpl, rhs.mImpl);
} }
Condition& operator=(const Condition&) = delete; Condition &operator=(const Condition &) = delete;
Condition& operator=(Condition&& rhs) noexcept Condition &operator=(Condition &&rhs) noexcept
{ {
std::swap(mImpl, rhs.mImpl); std::swap(mImpl, rhs.mImpl);
return *this; return *this;
...@@ -826,14 +871,14 @@ class Condition ...@@ -826,14 +871,14 @@ class Condition
mImpl = nullptr; mImpl = nullptr;
} }
void prepare(const Category& c) void prepare(const Category &c)
{ {
if (mImpl) if (mImpl)
mImpl->prepare(c); mImpl->prepare(c);
mPrepared = true; mPrepared = true;
} }
bool operator()(const Category& c, const Row& r) const bool operator()(const Category &c, const Row &r) const
{ {
assert(mImpl); assert(mImpl);
assert(mPrepared); assert(mPrepared);
...@@ -842,27 +887,27 @@ class Condition ...@@ -842,27 +887,27 @@ class Condition
bool empty() const { return mImpl == nullptr; } bool empty() const { return mImpl == nullptr; }
friend Condition operator||(Condition&& a, Condition&& b); friend Condition operator||(Condition &&a, Condition &&b);
friend Condition operator&&(Condition&& a, Condition&& b); friend Condition operator&&(Condition &&a, Condition &&b);
friend struct detail::OrConditionImpl; friend struct detail::OrConditionImpl;
friend struct detail::AndConditionImpl; friend struct detail::AndConditionImpl;
friend struct detail::NotConditionImpl; friend struct detail::NotConditionImpl;
void swap(Condition& rhs) void swap(Condition &rhs)
{ {
std::swap(mImpl, rhs.mImpl); std::swap(mImpl, rhs.mImpl);
std::swap(mPrepared, rhs.mPrepared); std::swap(mPrepared, rhs.mPrepared);
} }
friend std::ostream& operator<<(std::ostream& os, const Condition& cond); friend std::ostream &operator<<(std::ostream &os, const Condition &cond);
private: private:
detail::ConditionImpl* mImpl; detail::ConditionImpl *mImpl;
bool mPrepared = false; bool mPrepared = false;
}; };
inline std::ostream& operator<<(std::ostream& os, const Condition& cond) inline std::ostream &operator<<(std::ostream &os, const Condition &cond)
{ {
if (cond.mImpl) if (cond.mImpl)
cond.mImpl->str(os); cond.mImpl->str(os);
...@@ -872,41 +917,47 @@ inline std::ostream& operator<<(std::ostream& os, const Condition& cond) ...@@ -872,41 +917,47 @@ inline std::ostream& operator<<(std::ostream& os, const Condition& cond)
namespace detail namespace detail
{ {
struct KeyIsEmptyConditionImpl : public ConditionImpl struct KeyIsEmptyConditionImpl : public ConditionImpl
{ {
KeyIsEmptyConditionImpl(const std::string& ItemTag) KeyIsEmptyConditionImpl(const std::string &ItemTag)
: mItemTag(ItemTag) {} : mItemTag(ItemTag)
{
}
virtual void prepare(const Category& c); virtual void prepare(const Category &c);
virtual bool test(const Category& c, const Row& r) const virtual bool test(const Category &c, const Row &r) const
{ {
return r[mItemIx].empty(); return r[mItemIx].empty();
} }
virtual void str(std::ostream& os) const virtual void str(std::ostream &os) const
{ {
os << mItemTag << " IS NULL"; os << mItemTag << " IS NULL";
} }
std::string mItemTag; std::string mItemTag;
size_t mItemIx = 0; size_t mItemIx = 0;
}; };
struct KeyCompareConditionImpl : public ConditionImpl struct KeyCompareConditionImpl : public ConditionImpl
{ {
template<typename COMP> template <typename COMP>
KeyCompareConditionImpl(const std::string& ItemTag, COMP&& comp, const std::string& s) KeyCompareConditionImpl(const std::string &ItemTag, COMP &&comp, const std::string &s)
: mItemTag(ItemTag), mComp(std::move(comp)), mStr(s) {} : mItemTag(ItemTag)
, mComp(std::move(comp))
, mStr(s)
{
}
virtual void prepare(const Category& c); virtual void prepare(const Category &c);
virtual bool test(const Category& c, const Row& r) const virtual bool test(const Category &c, const Row &r) const
{ {
return mComp(c, r, mCaseInsensitive); return mComp(c, r, mCaseInsensitive);
} }
virtual void str(std::ostream& os) const virtual void str(std::ostream &os) const
{ {
os << mItemTag << (mCaseInsensitive ? "^ " : " ") << mStr; os << mItemTag << (mCaseInsensitive ? "^ " : " ") << mStr;
} }
...@@ -914,23 +965,27 @@ struct KeyCompareConditionImpl : public ConditionImpl ...@@ -914,23 +965,27 @@ struct KeyCompareConditionImpl : public ConditionImpl
std::string mItemTag; std::string mItemTag;
size_t mItemIx = 0; size_t mItemIx = 0;
bool mCaseInsensitive = false; bool mCaseInsensitive = false;
std::function<bool(const Category&, const Row&, bool)> mComp; std::function<bool(const Category &, const Row &, bool)> mComp;
std::string mStr; std::string mStr;
}; };
struct KeyMatchesConditionImpl : public ConditionImpl struct KeyMatchesConditionImpl : public ConditionImpl
{ {
KeyMatchesConditionImpl(const std::string& ItemTag, const std::regex& rx) KeyMatchesConditionImpl(const std::string &ItemTag, const std::regex &rx)
: mItemTag(ItemTag), mItemIx(0), mRx(rx) {} : mItemTag(ItemTag)
, mItemIx(0)
, mRx(rx)
{
}
virtual void prepare(const Category& c); virtual void prepare(const Category &c);
virtual bool test(const Category& c, const Row& r) const virtual bool test(const Category &c, const Row &r) const
{ {
return std::regex_match(r[mItemIx].as<std::string>(), mRx); return std::regex_match(r[mItemIx].as<std::string>(), mRx);
} }
virtual void str(std::ostream& os) const virtual void str(std::ostream &os) const
{ {
os << mItemTag << " =~ expression"; os << mItemTag << " =~ expression";
} }
...@@ -938,43 +993,48 @@ struct KeyMatchesConditionImpl : public ConditionImpl ...@@ -938,43 +993,48 @@ struct KeyMatchesConditionImpl : public ConditionImpl
std::string mItemTag; std::string mItemTag;
size_t mItemIx; size_t mItemIx;
std::regex mRx; std::regex mRx;
}; };
template<typename T> template <typename T>
struct AnyIsConditionImpl : public ConditionImpl struct AnyIsConditionImpl : public ConditionImpl
{ {
typedef T valueType; typedef T valueType;
AnyIsConditionImpl(const valueType& value) AnyIsConditionImpl(const valueType &value)
: mValue(value) {} : mValue(value)
{
}
virtual bool test(const Category& c, const Row& r) const; virtual bool test(const Category &c, const Row &r) const;
virtual void str(std::ostream& os) const virtual void str(std::ostream &os) const
{ {
os << "<any> == " << mValue; os << "<any> == " << mValue;
} }
valueType mValue; valueType mValue;
}; };
struct AnyMatchesConditionImpl : public ConditionImpl struct AnyMatchesConditionImpl : public ConditionImpl
{ {
AnyMatchesConditionImpl(const std::regex& rx) AnyMatchesConditionImpl(const std::regex &rx)
: mRx(rx) {} : mRx(rx)
{
}
virtual bool test(const Category& c, const Row& r) const; virtual bool test(const Category &c, const Row &r) const;
virtual void str(std::ostream& os) const virtual void str(std::ostream &os) const
{ {
os << "<any> =~ expression"; os << "<any> =~ expression";
} }
std::regex mRx; std::regex mRx;
}; };
struct AndConditionImpl : public ConditionImpl struct AndConditionImpl : public ConditionImpl
{ {
AndConditionImpl(Condition&& a, Condition&& b) AndConditionImpl(Condition &&a, Condition &&b)
: mA(nullptr), mB(nullptr) : mA(nullptr)
, mB(nullptr)
{ {
std::swap(mA, a.mImpl); std::swap(mA, a.mImpl);
std::swap(mB, b.mImpl); std::swap(mB, b.mImpl);
...@@ -986,18 +1046,18 @@ struct AndConditionImpl : public ConditionImpl ...@@ -986,18 +1046,18 @@ struct AndConditionImpl : public ConditionImpl
delete mB; delete mB;
} }
virtual void prepare(const Category& c) virtual void prepare(const Category &c)
{ {
mA->prepare(c); mA->prepare(c);
mB->prepare(c); mB->prepare(c);
} }
virtual bool test(const Category& c, const Row& r) const virtual bool test(const Category &c, const Row &r) const
{ {
return mA->test(c, r) and mB->test(c, r); return mA->test(c, r) and mB->test(c, r);
} }
virtual void str(std::ostream& os) const virtual void str(std::ostream &os) const
{ {
os << '('; os << '(';
mA->str(os); mA->str(os);
...@@ -1006,14 +1066,15 @@ struct AndConditionImpl : public ConditionImpl ...@@ -1006,14 +1066,15 @@ struct AndConditionImpl : public ConditionImpl
os << ')'; os << ')';
} }
ConditionImpl* mA; ConditionImpl *mA;
ConditionImpl* mB; ConditionImpl *mB;
}; };
struct OrConditionImpl : public ConditionImpl struct OrConditionImpl : public ConditionImpl
{ {
OrConditionImpl(Condition&& a, Condition&& b) OrConditionImpl(Condition &&a, Condition &&b)
: mA(nullptr), mB(nullptr) : mA(nullptr)
, mB(nullptr)
{ {
std::swap(mA, a.mImpl); std::swap(mA, a.mImpl);
std::swap(mB, b.mImpl); std::swap(mB, b.mImpl);
...@@ -1025,18 +1086,18 @@ struct OrConditionImpl : public ConditionImpl ...@@ -1025,18 +1086,18 @@ struct OrConditionImpl : public ConditionImpl
delete mB; delete mB;
} }
virtual void prepare(const Category& c) virtual void prepare(const Category &c)
{ {
mA->prepare(c); mA->prepare(c);
mB->prepare(c); mB->prepare(c);
} }
virtual bool test(const Category& c, const Row& r) const virtual bool test(const Category &c, const Row &r) const
{ {
return mA->test(c, r) or mB->test(c, r); return mA->test(c, r) or mB->test(c, r);
} }
virtual void str(std::ostream& os) const virtual void str(std::ostream &os) const
{ {
os << '('; os << '(';
mA->str(os); mA->str(os);
...@@ -1045,13 +1106,13 @@ struct OrConditionImpl : public ConditionImpl ...@@ -1045,13 +1106,13 @@ struct OrConditionImpl : public ConditionImpl
os << ')'; os << ')';
} }
ConditionImpl* mA; ConditionImpl *mA;
ConditionImpl* mB; ConditionImpl *mB;
}; };
struct NotConditionImpl : public ConditionImpl struct NotConditionImpl : public ConditionImpl
{ {
NotConditionImpl(Condition&& a) NotConditionImpl(Condition &&a)
: mA(nullptr) : mA(nullptr)
{ {
std::swap(mA, a.mImpl); std::swap(mA, a.mImpl);
...@@ -1062,29 +1123,29 @@ struct NotConditionImpl : public ConditionImpl ...@@ -1062,29 +1123,29 @@ struct NotConditionImpl : public ConditionImpl
delete mA; delete mA;
} }
virtual void prepare(const Category& c) virtual void prepare(const Category &c)
{ {
mA->prepare(c); mA->prepare(c);
} }
virtual bool test(const Category& c, const Row& r) const virtual bool test(const Category &c, const Row &r) const
{ {
return not mA->test(c, r); return not mA->test(c, r);
} }
virtual void str(std::ostream& os) const virtual void str(std::ostream &os) const
{ {
os << "NOT ("; os << "NOT (";
mA->str(os); mA->str(os);
os << ')'; os << ')';
} }
ConditionImpl* mA; ConditionImpl *mA;
}; };
} } // namespace detail
inline Condition operator&&(Condition&& a, Condition&& b) inline Condition operator&&(Condition &&a, Condition &&b)
{ {
if (a.mImpl and b.mImpl) if (a.mImpl and b.mImpl)
return Condition(new detail::AndConditionImpl(std::move(a), std::move(b))); return Condition(new detail::AndConditionImpl(std::move(a), std::move(b)));
...@@ -1093,7 +1154,7 @@ inline Condition operator&&(Condition&& a, Condition&& b) ...@@ -1093,7 +1154,7 @@ inline Condition operator&&(Condition&& a, Condition&& b)
return Condition(std::move(b)); return Condition(std::move(b));
} }
inline Condition operator||(Condition&& a, Condition&& b) inline Condition operator||(Condition &&a, Condition &&b)
{ {
if (a.mImpl and b.mImpl) if (a.mImpl and b.mImpl)
return Condition(new detail::OrConditionImpl(std::move(a), std::move(b))); return Condition(new detail::OrConditionImpl(std::move(a), std::move(b)));
...@@ -1102,38 +1163,50 @@ inline Condition operator||(Condition&& a, Condition&& b) ...@@ -1102,38 +1163,50 @@ inline Condition operator||(Condition&& a, Condition&& b)
return Condition(std::move(b)); return Condition(std::move(b));
} }
struct Empty {}; struct Empty
{
};
struct Key struct Key
{ {
Key(const std::string& itemTag) : mItemTag(itemTag) {} Key(const std::string &itemTag)
Key(const char* itemTag) : mItemTag(itemTag) {} : mItemTag(itemTag)
{
}
Key(const char *itemTag)
: mItemTag(itemTag)
{
}
Key(const Key&) = delete; Key(const Key &) = delete;
Key& operator=(const Key&) = delete; Key &operator=(const Key &) = delete;
std::string mItemTag; std::string mItemTag;
}; };
template<typename T> template <typename T>
Condition operator==(const Key& key, const T& v) Condition operator==(const Key &key, const T &v)
{ {
std::ostringstream s; std::ostringstream s;
s << " == " << v; s << " == " << v;
return Condition(new detail::KeyCompareConditionImpl(key.mItemTag, [tag = key.mItemTag, v](const Category& c, const Row& r, bool icase) return Condition(new detail::KeyCompareConditionImpl(
{ return r[tag].template compare<T>(v, icase) == 0; }, s.str())); key.mItemTag, [tag = key.mItemTag, v](const Category &c, const Row &r, bool icase)
{ return r[tag].template compare<T>(v, icase) == 0; },
s.str()));
} }
inline Condition operator==(const Key& key, const char *value) inline Condition operator==(const Key &key, const char *value)
{ {
if (value != nullptr and *value != 0) if (value != nullptr and *value != 0)
{ {
std::ostringstream s; std::ostringstream s;
s << " == " << value; s << " == " << value;
return Condition(new detail::KeyCompareConditionImpl(key.mItemTag, [tag = key.mItemTag, value](const Category& c, const Row& r, bool icase) return Condition(new detail::KeyCompareConditionImpl(
{ return r[tag].compare(value, icase) == 0; }, s.str())); key.mItemTag, [tag = key.mItemTag, value](const Category &c, const Row &r, bool icase)
{ return r[tag].compare(value, icase) == 0; },
s.str()));
} }
else else
return Condition(new detail::KeyIsEmptyConditionImpl(key.mItemTag)); return Condition(new detail::KeyIsEmptyConditionImpl(key.mItemTag));
...@@ -1148,89 +1221,94 @@ inline Condition operator==(const Key& key, const char *value) ...@@ -1148,89 +1221,94 @@ inline Condition operator==(const Key& key, const char *value)
// { return r[tag].template compare<(v, icase) == 0; })); // { return r[tag].template compare<(v, icase) == 0; }));
// } // }
inline Condition operator==(const Key& key, const Empty&) inline Condition operator==(const Key &key, const Empty &)
{ {
return Condition(new detail::KeyIsEmptyConditionImpl(key.mItemTag)); return Condition(new detail::KeyIsEmptyConditionImpl(key.mItemTag));
} }
template<typename T> template <typename T>
Condition operator!=(const Key& key, const T& v) Condition operator!=(const Key &key, const T &v)
{ {
return Condition(new detail::NotConditionImpl(operator==(key, v))); return Condition(new detail::NotConditionImpl(operator==(key, v)));
} }
inline Condition operator!=(const Key& key, const char* v) inline Condition operator!=(const Key &key, const char *v)
{ {
std::string value(v ? v : ""); std::string value(v ? v : "");
return Condition(new detail::NotConditionImpl(operator==(key, value))); return Condition(new detail::NotConditionImpl(operator==(key, value)));
} }
template<typename T> template <typename T>
Condition operator>(const Key& key, const T& v) Condition operator>(const Key &key, const T &v)
{ {
std::ostringstream s; std::ostringstream s;
s << " > " << v; s << " > " << v;
return Condition(new detail::KeyCompareConditionImpl(key.mItemTag, [tag = key.mItemTag, v](const Category& c, const Row& r, bool icase) return Condition(new detail::KeyCompareConditionImpl(
{ return r[tag].template compare<T>(v, icase) > 0; }, s.str())); key.mItemTag, [tag = key.mItemTag, v](const Category &c, const Row &r, bool icase)
{ return r[tag].template compare<T>(v, icase) > 0; },
s.str()));
} }
template<typename T> template <typename T>
Condition operator>=(const Key& key, const T& v) Condition operator>=(const Key &key, const T &v)
{ {
std::ostringstream s; std::ostringstream s;
s << " >= " << v; s << " >= " << v;
return Condition(new detail::KeyCompareConditionImpl(key.mItemTag, [tag = key.mItemTag, v](const Category& c, const Row& r, bool icase) return Condition(new detail::KeyCompareConditionImpl(
{ return r[tag].template compare<T>(v, icase) >= 0; }, s.str())); key.mItemTag, [tag = key.mItemTag, v](const Category &c, const Row &r, bool icase)
{ return r[tag].template compare<T>(v, icase) >= 0; },
s.str()));
} }
template<typename T> template <typename T>
Condition operator<(const Key& key, const T& v) Condition operator<(const Key &key, const T &v)
{ {
std::ostringstream s; std::ostringstream s;
s << " < " << v; s << " < " << v;
return Condition(new detail::KeyCompareConditionImpl(key.mItemTag, [tag = key.mItemTag, v](const Category& c, const Row& r, bool icase) return Condition(new detail::KeyCompareConditionImpl(
{ return r[tag].template compare<T>(v, icase) < 0; }, s.str())); key.mItemTag, [tag = key.mItemTag, v](const Category &c, const Row &r, bool icase)
{ return r[tag].template compare<T>(v, icase) < 0; },
s.str()));
} }
template<typename T> template <typename T>
Condition operator<=(const Key& key, const T& v) Condition operator<=(const Key &key, const T &v)
{ {
std::ostringstream s; std::ostringstream s;
s << " <= " << v; s << " <= " << v;
return Condition(new detail::KeyCompareConditionImpl(key.mItemTag, [tag = key.mItemTag, v](const Category& c, const Row& r, bool icase) return Condition(new detail::KeyCompareConditionImpl(
{ return r[tag].template compare<T>(v, icase) <= 0; }, s.str())); key.mItemTag, [tag = key.mItemTag, v](const Category &c, const Row &r, bool icase)
{ return r[tag].template compare<T>(v, icase) <= 0; },
s.str()));
} }
template<> template <>
inline inline Condition operator==(const Key &key, const std::regex &rx)
Condition operator==(const Key& key, const std::regex& rx)
{ {
return Condition(new detail::KeyMatchesConditionImpl(key.mItemTag, rx)); return Condition(new detail::KeyMatchesConditionImpl(key.mItemTag, rx));
} }
template<> template <>
inline inline Condition operator==(const Key &key, const Empty &)
Condition operator==(const Key& key, const Empty&)
{ {
return Condition(new detail::KeyIsEmptyConditionImpl(key.mItemTag)); return Condition(new detail::KeyIsEmptyConditionImpl(key.mItemTag));
} }
struct any struct any
{ {
template<typename T> template <typename T>
Condition operator==(const T& v) const Condition operator==(const T &v) const
{ {
return Condition(new detail::AnyIsConditionImpl<T>(v)); return Condition(new detail::AnyIsConditionImpl<T>(v));
} }
}; };
template<> template <>
inline inline Condition any::operator==(const std::regex &rx) const
Condition any::operator==(const std::regex& rx) const
{ {
return Condition(new detail::AnyMatchesConditionImpl(rx)); return Condition(new detail::AnyMatchesConditionImpl(rx));
} }
...@@ -1240,7 +1318,7 @@ inline Condition All() ...@@ -1240,7 +1318,7 @@ inline Condition All()
return Condition(new detail::AllConditionImpl()); return Condition(new detail::AllConditionImpl());
} }
inline Condition Not(Condition&& cond) inline Condition Not(Condition &&cond)
{ {
return Condition(new detail::NotConditionImpl(std::move(cond))); return Condition(new detail::NotConditionImpl(std::move(cond)));
} }
...@@ -1248,69 +1326,76 @@ inline Condition Not(Condition&& cond) ...@@ -1248,69 +1326,76 @@ inline Condition Not(Condition&& 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)
{ {
return Key(std::string(text, length)); return Key(std::string(text, length));
} }
inline constexpr Empty Null = Empty();
} inline constexpr Empty Null = Empty();
} // namespace literals
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
// iterators // iterators
template<typename RowType, typename... Ts> template <typename RowType, typename... Ts>
class iterator_impl class iterator_impl
{ {
public: public:
template<typename, typename...> friend class iterator_impl; template <typename, typename...>
friend class iterator_impl;
static constexpr size_t N = sizeof...(Ts); static constexpr size_t N = sizeof...(Ts);
using iterator_category = std::forward_iterator_tag; using iterator_category = std::forward_iterator_tag;
using value_type = std::conditional_t<N == 0, RowType, std::tuple<Ts...>>; using value_type = std::conditional_t<N == 0, RowType, std::tuple<Ts...>>;
using difference_type = std::ptrdiff_t; using difference_type = std::ptrdiff_t;
using pointer = value_type*; using pointer = value_type *;
using reference = value_type&; using reference = value_type &;
friend class Category; friend class Category;
// default constructor, equal to end() // default constructor, equal to end()
iterator_impl() {} iterator_impl() {}
iterator_impl(const iterator_impl& rhs) iterator_impl(const iterator_impl &rhs)
: mCurrent(rhs.mCurrent), mValue(rhs.mValue), mCix(rhs.mCix) : mCurrent(rhs.mCurrent)
, mValue(rhs.mValue)
, mCix(rhs.mCix)
{ {
} }
iterator_impl(ItemRow* data) iterator_impl(ItemRow *data)
: mCurrent(data) : mCurrent(data)
{ {
static_assert(N == 0, "Only valid if this is a row iterator, not a row<xxx> iterator"); static_assert(N == 0, "Only valid if this is a row iterator, not a row<xxx> iterator");
} }
iterator_impl(ItemRow* data, const std::array<size_t,N>& cix) iterator_impl(ItemRow *data, const std::array<size_t, N> &cix)
: mCurrent(data), mCix(cix) {} : mCurrent(data)
, mCix(cix)
{
}
template<typename IRowType> template <typename IRowType>
iterator_impl(iterator_impl<IRowType, Ts...>& rhs) iterator_impl(iterator_impl<IRowType, Ts...> &rhs)
: mCurrent(rhs.mCurrent), mCix(rhs.mCix) : mCurrent(rhs.mCurrent)
, mCix(rhs.mCix)
{ {
if constexpr (N > 0) if constexpr (N > 0)
mValue = get(mCurrent, std::make_index_sequence<N>()); mValue = get(mCurrent, std::make_index_sequence<N>());
} }
template<typename IRowType> template <typename IRowType>
iterator_impl(const iterator_impl<IRowType>& rhs, const std::array<size_t,N>& cix) iterator_impl(const iterator_impl<IRowType> &rhs, const std::array<size_t, N> &cix)
: mCurrent(rhs.mCurrent), mCix(cix) : mCurrent(rhs.mCurrent)
, mCix(cix)
{ {
if constexpr (N > 0) if constexpr (N > 0)
mValue = get(mCurrent, std::make_index_sequence<N>()); mValue = get(mCurrent, std::make_index_sequence<N>());
} }
iterator_impl& operator=(const iterator_impl& i) iterator_impl &operator=(const iterator_impl &i)
{ {
mCurrent = i.mCurrent; mCurrent = i.mCurrent;
if constexpr (N != 0) if constexpr (N != 0)
...@@ -1344,7 +1429,7 @@ class iterator_impl ...@@ -1344,7 +1429,7 @@ class iterator_impl
return mCurrent; return mCurrent;
} }
iterator_impl& operator++() iterator_impl &operator++()
{ {
mCurrent.next(); mCurrent.next();
...@@ -1361,24 +1446,23 @@ class iterator_impl ...@@ -1361,24 +1446,23 @@ class iterator_impl
return result; return result;
} }
bool operator==(const iterator_impl& rhs) const { return mCurrent == rhs.mCurrent; } bool operator==(const iterator_impl &rhs) const { return mCurrent == rhs.mCurrent; }
bool operator!=(const iterator_impl& rhs) const { return mCurrent != rhs.mCurrent; } bool operator!=(const iterator_impl &rhs) const { return mCurrent != rhs.mCurrent; }
template<typename IRowType, typename... ITs> template <typename IRowType, typename... ITs>
bool operator==(const iterator_impl<IRowType, ITs...>& rhs) const bool operator==(const iterator_impl<IRowType, ITs...> &rhs) const
{ {
return mCurrent == rhs.mCurrent; return mCurrent == rhs.mCurrent;
} }
template<typename IRowType, typename... ITs> template <typename IRowType, typename... ITs>
bool operator!=(const iterator_impl<IRowType, ITs...>& rhs) const bool operator!=(const iterator_impl<IRowType, ITs...> &rhs) const
{ {
return mCurrent != rhs.mCurrent; return mCurrent != rhs.mCurrent;
} }
private: private:
template <std::size_t... Is>
template<std::size_t... Is>
std::tuple<Ts...> get(Row row, std::index_sequence<Is...>) const std::tuple<Ts...> get(Row row, std::index_sequence<Is...>) const
{ {
if (row) if (row)
...@@ -1394,7 +1478,7 @@ class iterator_impl ...@@ -1394,7 +1478,7 @@ class iterator_impl
// -------------------------------------------------------------------- // --------------------------------------------------------------------
// iterator proxy // iterator proxy
template<typename RowType, typename... Ts> template <typename RowType, typename... Ts>
class iterator_proxy class iterator_proxy
{ {
public: public:
...@@ -1403,14 +1487,14 @@ class iterator_proxy ...@@ -1403,14 +1487,14 @@ class iterator_proxy
using iterator = iterator_impl<RowType, Ts...>; using iterator = iterator_impl<RowType, Ts...>;
using row_iterator = iterator_impl<RowType>; using row_iterator = iterator_impl<RowType>;
iterator_proxy(Category& cat, row_iterator pos, char const* const columns[N]); iterator_proxy(Category &cat, row_iterator pos, char const *const columns[N]);
iterator_proxy(Category& cat, row_iterator pos, std::initializer_list<char const*> columns); iterator_proxy(Category &cat, row_iterator pos, std::initializer_list<char const *> columns);
iterator_proxy(iterator_proxy&& p); iterator_proxy(iterator_proxy &&p);
iterator_proxy& operator=(iterator_proxy&& p); iterator_proxy &operator=(iterator_proxy &&p);
iterator_proxy(const iterator_proxy&) = delete; iterator_proxy(const iterator_proxy &) = delete;
iterator_proxy& operator=(const iterator_proxy&) = delete; iterator_proxy &operator=(const iterator_proxy &) = delete;
iterator begin() const { return iterator(mCBegin, mCix); } iterator begin() const { return iterator(mCBegin, mCix); }
iterator end() const { return iterator(mCEnd, mCix); } iterator end() const { return iterator(mCEnd, mCix); }
...@@ -1424,9 +1508,9 @@ class iterator_proxy ...@@ -1424,9 +1508,9 @@ class iterator_proxy
RowType front() { return *begin(); } RowType front() { return *begin(); }
RowType back() { return *(std::prev(end())); } RowType back() { return *(std::prev(end())); }
Category& category() const { return *mCat;} Category &category() const { return *mCat; }
void swap(iterator_proxy& rhs) void swap(iterator_proxy &rhs)
{ {
std::swap(mCat, rhs.mCat); std::swap(mCat, rhs.mCat);
std::swap(mCBegin, rhs.mCBegin); std::swap(mCBegin, rhs.mCBegin);
...@@ -1435,15 +1519,15 @@ class iterator_proxy ...@@ -1435,15 +1519,15 @@ class iterator_proxy
} }
private: private:
Category* mCat; Category *mCat;
row_iterator mCBegin, mCEnd; row_iterator mCBegin, mCEnd;
std::array<size_t,N> mCix; std::array<size_t, N> mCix;
}; };
// -------------------------------------------------------------------- // --------------------------------------------------------------------
// conditional iterator proxy // conditional iterator proxy
template<typename RowType, typename... Ts> template <typename RowType, typename... Ts>
class conditional_iterator_proxy class conditional_iterator_proxy
{ {
public: public:
...@@ -1461,12 +1545,12 @@ class conditional_iterator_proxy ...@@ -1461,12 +1545,12 @@ class conditional_iterator_proxy
using iterator_category = std::forward_iterator_tag; using iterator_category = std::forward_iterator_tag;
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 = value_type&; using reference = value_type &;
conditional_iterator_impl(Category& cat, row_iterator pos, const Condition& cond, const std::array<size_t,N>& cix); conditional_iterator_impl(Category &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;
conditional_iterator_impl& operator=(const conditional_iterator_impl& i) = default; conditional_iterator_impl &operator=(const conditional_iterator_impl &i) = default;
virtual ~conditional_iterator_impl() = default; virtual ~conditional_iterator_impl() = default;
...@@ -1480,7 +1564,7 @@ class conditional_iterator_proxy ...@@ -1480,7 +1564,7 @@ class conditional_iterator_proxy
return &*mBegin; return &*mBegin;
} }
conditional_iterator_impl& operator++() conditional_iterator_impl &operator++()
{ {
while (mBegin != mEnd) while (mBegin != mEnd)
{ {
...@@ -1501,35 +1585,34 @@ class conditional_iterator_proxy ...@@ -1501,35 +1585,34 @@ class conditional_iterator_proxy
return result; return result;
} }
bool operator==(const conditional_iterator_impl& rhs) const { return mBegin == rhs.mBegin; } bool operator==(const conditional_iterator_impl &rhs) const { return mBegin == rhs.mBegin; }
bool operator!=(const conditional_iterator_impl& rhs) const { return mBegin != rhs.mBegin; } bool operator!=(const conditional_iterator_impl &rhs) const { return mBegin != rhs.mBegin; }
template<typename IRowType, typename... ITs> template <typename IRowType, typename... ITs>
bool operator==(const iterator_impl<IRowType, ITs...>& rhs) const { return mBegin == rhs; } bool operator==(const iterator_impl<IRowType, ITs...> &rhs) const { return mBegin == rhs; }
template<typename IRowType, typename... ITs> template <typename IRowType, typename... ITs>
bool operator!=(const iterator_impl<IRowType, ITs...>& rhs) const { return mBegin != rhs; } bool operator!=(const iterator_impl<IRowType, ITs...> &rhs) const { return mBegin != rhs; }
private: private:
Category *mCat;
Category* mCat;
base_iterator mBegin, mEnd; base_iterator mBegin, mEnd;
const Condition* mCondition; const Condition *mCondition;
}; };
using iterator = conditional_iterator_impl; using iterator = conditional_iterator_impl;
using reference = typename iterator::reference; using reference = typename iterator::reference;
conditional_iterator_proxy(Category& cat, row_iterator pos, Condition&& cond); conditional_iterator_proxy(Category &cat, row_iterator pos, Condition &&cond);
template<std::size_t TN = N, std::enable_if_t<TN != 0, bool> = true> template <std::size_t TN = N, std::enable_if_t<TN != 0, bool> = true>
conditional_iterator_proxy(Category& cat, row_iterator pos, Condition&& cond, char const* const columns[N]); conditional_iterator_proxy(Category &cat, row_iterator pos, Condition &&cond, char const *const columns[N]);
conditional_iterator_proxy(conditional_iterator_proxy&& p); conditional_iterator_proxy(conditional_iterator_proxy &&p);
conditional_iterator_proxy& operator=(conditional_iterator_proxy&& p); conditional_iterator_proxy &operator=(conditional_iterator_proxy &&p);
conditional_iterator_proxy(const conditional_iterator_proxy&) = delete; conditional_iterator_proxy(const conditional_iterator_proxy &) = delete;
conditional_iterator_proxy& operator=(const conditional_iterator_proxy&) = delete; conditional_iterator_proxy &operator=(const conditional_iterator_proxy &) = delete;
iterator begin() const; iterator begin() const;
iterator end() const; iterator end() const;
...@@ -1542,15 +1625,15 @@ class conditional_iterator_proxy ...@@ -1542,15 +1625,15 @@ class conditional_iterator_proxy
RowType front() { return *begin(); } RowType front() { return *begin(); }
Category& category() const { return *mCat;} Category &category() const { return *mCat; }
void swap(conditional_iterator_proxy& rhs); void swap(conditional_iterator_proxy &rhs);
private: private:
Category* mCat; Category *mCat;
Condition mCondition; Condition mCondition;
row_iterator mCBegin, mCEnd; row_iterator mCBegin, mCEnd;
std::array<size_t,N> mCix; std::array<size_t, N> mCix;
}; };
// -------------------------------------------------------------------- // --------------------------------------------------------------------
...@@ -1732,9 +1815,9 @@ class Category ...@@ -1732,9 +1815,9 @@ class Category
friend class Row; friend class Row;
friend class detail::ItemReference; friend class detail::ItemReference;
Category(Datablock& db, const std::string& name, Validator* Validator); Category(Datablock &db, const std::string &name, Validator *Validator);
Category(const Category&) = delete; Category(const Category &) = delete;
Category& operator=(const Category&) = delete; Category &operator=(const Category &) = delete;
~Category(); ~Category();
const std::string name() const { return mName; } const std::string name() const { return mName; }
...@@ -1759,55 +1842,55 @@ class Category ...@@ -1759,55 +1842,55 @@ class Category
Row front() { return Row(mHead); } Row front() { return Row(mHead); }
Row back() { return Row(mTail); } Row back() { return Row(mTail); }
Row operator[](Condition&& cond); Row operator[](Condition &&cond);
template<typename... Ts, size_t N> template <typename... Ts, size_t N>
iterator_proxy<Row, Ts...> rows(char const* const (&columns)[N]) iterator_proxy<Row, Ts...> rows(char const *const (&columns)[N])
{ {
static_assert(sizeof...(Ts) == N, "The number of column titles should be equal to the number of types to return"); static_assert(sizeof...(Ts) == N, "The number of column titles should be equal to the number of types to return");
return iterator_proxy<Row, Ts...>(*this, begin(), columns ); return iterator_proxy<Row, Ts...>(*this, begin(), columns);
} }
template<typename... Ts, typename... Ns> template <typename... Ts, typename... Ns>
iterator_proxy<Row, Ts...> rows(Ns... names) iterator_proxy<Row, Ts...> rows(Ns... names)
{ {
static_assert(sizeof...(Ts) == sizeof...(Ns), "The number of column titles should be equal to the number of types to return"); static_assert(sizeof...(Ts) == sizeof...(Ns), "The number of column titles should be equal to the number of types to return");
return iterator_proxy<Row, Ts...>(*this, begin(), { names... }); return iterator_proxy<Row, Ts...>(*this, begin(), {names...});
} }
conditional_iterator_proxy<Row> find(Condition&& cond) conditional_iterator_proxy<Row> find(Condition &&cond)
{ {
return find(cbegin(), std::forward<Condition>(cond)); return find(cbegin(), std::forward<Condition>(cond));
} }
conditional_iterator_proxy<Row> find(const_iterator pos, Condition&& cond) conditional_iterator_proxy<Row> find(const_iterator pos, Condition &&cond)
{ {
return { *this, pos, std::forward<Condition>(cond) }; return {*this, pos, std::forward<Condition>(cond)};
} }
template<typename... Ts, size_t N> template <typename... Ts, size_t N>
conditional_iterator_proxy<Row, Ts...> find(Condition&& cond, char const* const (&columns)[N]) conditional_iterator_proxy<Row, Ts...> find(Condition &&cond, char const *const (&columns)[N])
{ {
static_assert(sizeof...(Ts) == N, "The number of column titles should be equal to the number of types to return"); static_assert(sizeof...(Ts) == N, "The number of column titles should be equal to the number of types to return");
return find<Ts...>(cbegin(), std::forward<Condition>(cond), std::forward<char const* const[N]>(columns)); return find<Ts...>(cbegin(), std::forward<Condition>(cond), std::forward<char const *const[N]>(columns));
} }
template<typename... Ts, size_t N> template <typename... Ts, size_t N>
conditional_iterator_proxy<Row, Ts...> find(const_iterator pos, Condition&& cond, char const* const (&columns)[N]) conditional_iterator_proxy<Row, Ts...> find(const_iterator pos, Condition &&cond, char const *const (&columns)[N])
{ {
static_assert(sizeof...(Ts) == N, "The number of column titles should be equal to the number of types to return"); static_assert(sizeof...(Ts) == N, "The number of column titles should be equal to the number of types to return");
return { *this, pos, std::forward<Condition>(cond), columns }; return {*this, pos, std::forward<Condition>(cond), columns};
} }
// -------------------------------------------------------------------- // --------------------------------------------------------------------
// if you only expect a single row // if you only expect a single row
Row find1(Condition&& cond) Row find1(Condition &&cond)
{ {
return find1(cbegin(), std::forward<Condition>(cond)); return find1(cbegin(), std::forward<Condition>(cond));
} }
Row find1(const_iterator pos, Condition&& cond) Row find1(const_iterator pos, Condition &&cond)
{ {
auto h = find(pos, std::forward<Condition>(cond)); auto h = find(pos, std::forward<Condition>(cond));
...@@ -1820,19 +1903,19 @@ class Category ...@@ -1820,19 +1903,19 @@ class Category
return *h.begin(); return *h.begin();
} }
template<typename... Ts, size_t N> template <typename... Ts, size_t N>
std::tuple<Ts...> find1(Condition&& cond, char const* const (&columns)[N]) std::tuple<Ts...> find1(Condition &&cond, char const *const (&columns)[N])
{ {
static_assert(sizeof...(Ts) == N, "The number of column titles should be equal to the number of types to return"); static_assert(sizeof...(Ts) == N, "The number of column titles should be equal to the number of types to return");
return find1<Ts...>(cbegin(), std::forward<Condition>(cond), std::forward<char const* const[N]>(columns)); return find1<Ts...>(cbegin(), std::forward<Condition>(cond), std::forward<char const *const[N]>(columns));
} }
template<typename... Ts, size_t N> template <typename... Ts, size_t N>
std::tuple<Ts...> find1(const_iterator pos, Condition&& cond, char const* const (&columns)[N]) std::tuple<Ts...> find1(const_iterator pos, Condition &&cond, char const *const (&columns)[N])
{ {
static_assert(sizeof...(Ts) == N, "The number of column titles should be equal to the number of types to return"); static_assert(sizeof...(Ts) == N, "The number of column titles should be equal to the number of types to return");
auto h = find<Ts...>(pos, std::forward<Condition>(cond), std::forward<char const* const[N]>(columns)); auto h = find<Ts...>(pos, std::forward<Condition>(cond), std::forward<char const *const[N]>(columns));
if (h.empty()) if (h.empty())
throw std::runtime_error("No hits found"); throw std::runtime_error("No hits found");
...@@ -1843,25 +1926,29 @@ class Category ...@@ -1843,25 +1926,29 @@ class Category
return *h.begin(); return *h.begin();
} }
bool exists(Condition&& cond) const; bool exists(Condition &&cond) const;
RowSet orderBy(const std::string& Item) RowSet orderBy(const std::string &Item)
{ return orderBy({ Item }); } {
return orderBy({Item});
}
RowSet orderBy(std::initializer_list<std::string> Items); RowSet orderBy(std::initializer_list<std::string> Items);
std::tuple<Row,bool> emplace(Item value) { return emplace({ value }); } std::tuple<Row, bool> emplace(Item value) { return emplace({value}); }
std::tuple<Row,bool> emplace(std::initializer_list<Item> values) std::tuple<Row, bool> emplace(std::initializer_list<Item> values)
{ return emplace(values.begin(), values.end()); } {
return emplace(values.begin(), values.end());
}
std::tuple<Row,bool> emplace(Row r); std::tuple<Row, bool> emplace(Row r);
template<class Iter> template <class Iter>
std::tuple<Row,bool> emplace(Iter b, Iter e); std::tuple<Row, bool> emplace(Iter b, Iter e);
size_t erase(Condition&& cond); size_t erase(Condition &&cond);
size_t erase(Condition&& cond, std::function<void(const Row&)>&& visit); size_t erase(Condition &&cond, std::function<void(const Row &)> &&visit);
void erase(Row r); void erase(Row r);
iterator erase(iterator ri); iterator erase(iterator ri);
...@@ -1872,48 +1959,48 @@ class Category ...@@ -1872,48 +1959,48 @@ class Category
// erase without cascade, should only be used when speed is needed // erase without cascade, should only be used when speed is needed
size_t erase_nocascade(Condition&& cond) size_t erase_nocascade(Condition &&cond)
{ {
return erase_nocascade(std::forward<Condition>(cond), [](auto r){}); return erase_nocascade(std::forward<Condition>(cond), [](auto r) {});
} }
size_t erase_nocascade(Condition&& cond, std::function<void(const Row&)>&& visit) size_t erase_nocascade(Condition &&cond, std::function<void(const Row &)> &&visit)
{ {
auto savedValidator = mValidator; auto savedValidator = mValidator;
mValidator = nullptr; mValidator = nullptr;
auto result = erase(std::forward<Condition>(cond), std::forward<std::function<void(const Row&)>>(visit)); auto result = erase(std::forward<Condition>(cond), std::forward<std::function<void(const Row &)>>(visit));
mValidator = savedValidator; mValidator = savedValidator;
return result; return result;
} }
void eraseOrphans(Condition&& cond); void eraseOrphans(Condition &&cond);
/// an orphan is a row that is the child side of one or more /// an orphan is a row that is the child side of one or more
/// links and for which there is no single parent left. /// links and for which there is no single parent left.
bool isOrphan(Row r); bool isOrphan(Row r);
bool hasParent(Row r, const Category& parentCat, const ValidateLink& link) const; bool hasParent(Row r, const Category &parentCat, const ValidateLink &link) const;
bool hasChildren(Row r) const; bool hasChildren(Row r) const;
bool hasParents(Row r) const; bool hasParents(Row r) const;
RowSet getChildren(Row r, Category& childCat); RowSet getChildren(Row r, Category &childCat);
RowSet getChildren(Row r, const char* childCat); RowSet getChildren(Row r, const char *childCat);
RowSet getParents(Row r, Category& parentCat); RowSet getParents(Row r, Category &parentCat);
RowSet getParents(Row r, const char* parentCat); RowSet getParents(Row r, const char *parentCat);
RowSet getLinked(Row r, Category& cat); RowSet getLinked(Row r, Category &cat);
RowSet getLinked(Row r, const char* cat); RowSet getLinked(Row r, const char *cat);
bool isValid(); bool isValid();
void validateLinks() const; void validateLinks() const;
const Validator& getValidator() const; const Validator &getValidator() const;
const ValidateCategory* getCatValidator() const { return mCatValidator; } const ValidateCategory *getCatValidator() const { return mCatValidator; }
Datablock& db() { return mDb; } Datablock &db() { return mDb; }
void setValidator(Validator* v); void setValidator(Validator *v);
iset fields() const; iset fields() const;
iset mandatoryFields() const; iset mandatoryFields() const;
...@@ -1921,18 +2008,18 @@ class Category ...@@ -1921,18 +2008,18 @@ class Category
std::set<size_t> keyFieldsByIndex() const; std::set<size_t> keyFieldsByIndex() const;
void drop(const std::string& field); void drop(const std::string &field);
void getTagOrder(std::vector<std::string>& tags) const; void getTagOrder(std::vector<std::string> &tags) const;
// return index for known column, or the next available column index // return index for known column, or the next available column index
size_t getColumnIndex(const std::string& name) const; size_t getColumnIndex(const std::string &name) const;
bool hasColumn(const std::string& name) const; bool hasColumn(const std::string &name) const;
const std::string& getColumnName(size_t columnIndex) const; const std::string &getColumnName(size_t columnIndex) const;
std::vector<std::string> getColumnNames() const; std::vector<std::string> getColumnNames() const;
void reorderByIndex(); void reorderByIndex();
void sort(std::function<int(const Row&, const Row&)> comparator); void sort(std::function<int(const Row &, const Row &)> comparator);
// -------------------------------------------------------------------- // --------------------------------------------------------------------
/// Rename a single column in the rows that match \a cond to value \a value /// Rename a single column in the rows that match \a cond to value \a value
...@@ -1942,7 +2029,7 @@ class Category ...@@ -1942,7 +2029,7 @@ class Category
void update_value(Condition &&cond, const std::string &tag, const std::string &value) void update_value(Condition &&cond, const std::string &tag, const std::string &value)
{ {
update_value(RowSet{ *this, std::move(cond) }, tag, value); update_value(RowSet{*this, std::move(cond)}, tag, value);
} }
void update_value(RowSet &&rows, const std::string &tag, const std::string &value); void update_value(RowSet &&rows, const std::string &tag, const std::string &value);
...@@ -1954,25 +2041,30 @@ class Category ...@@ -1954,25 +2041,30 @@ class Category
std::string getUniqueID(std::function<std::string(int)> generator = cif::cifIdForNumber); std::string getUniqueID(std::function<std::string(int)> generator = cif::cifIdForNumber);
std::string getUniqueID(const std::string &prefix) std::string getUniqueID(const std::string &prefix)
{ {
return getUniqueID([prefix](int nr) { return prefix + std::to_string(nr); }); return getUniqueID([prefix](int nr)
{ return prefix + std::to_string(nr); });
} }
private: // --------------------------------------------------------------------
// for debugging
friend bool operator==(const Category &lhs, const Category &rhs);
void write(std::ostream& os); private:
void write(std::ostream& os, const std::vector<std::string>& order); void write(std::ostream &os);
void write(std::ostream& os, const std::vector<size_t>& order, bool includeEmptyColumns); void write(std::ostream &os, const std::vector<std::string> &order);
void write(std::ostream &os, const std::vector<size_t> &order, bool includeEmptyColumns);
size_t addColumn(const std::string& name); size_t addColumn(const std::string &name);
Datablock& mDb; Datablock &mDb;
std::string mName; std::string mName;
Validator* mValidator; Validator *mValidator;
const ValidateCategory* mCatValidator = nullptr; const ValidateCategory *mCatValidator = nullptr;
std::vector<ItemColumn> mColumns; std::vector<ItemColumn> mColumns;
ItemRow* mHead; ItemRow *mHead;
ItemRow* mTail; ItemRow *mTail;
class CatIndex* mIndex; class CatIndex *mIndex;
}; };
// -------------------------------------------------------------------- // --------------------------------------------------------------------
...@@ -1984,66 +2076,81 @@ class File ...@@ -1984,66 +2076,81 @@ class File
friend class Validator; friend class Validator;
File(); File();
File(std::istream& is, bool validate = false); File(std::istream &is, bool validate = false);
File(const std::string& path, bool validate = false); File(const std::string &path, bool validate = false);
File(File&& rhs); File(File &&rhs);
File(const File& rhs) = delete; File(const File &rhs) = delete;
File& operator=(const File& rhs) = delete; File &operator=(const File &rhs) = delete;
~File(); ~File();
void load(const std::string& p); void load(const std::string &p);
void save(const std::string& p); void save(const std::string &p);
void load(std::istream& is); void load(std::istream &is);
void save(std::ostream& os); void save(std::ostream &os);
/// \brief Load only the data block \a datablock from the mmCIF file /// \brief Load only the data block \a datablock from the mmCIF file
void load(std::istream& is, const std::string& datablock); void load(std::istream &is, const std::string &datablock);
void save(std::ostream& os, const std::vector<std::string>& order) { write(os, order); } void save(std::ostream &os, const std::vector<std::string> &order) { write(os, order); }
void write(std::ostream& os, const std::vector<std::string>& order); void write(std::ostream &os, const std::vector<std::string> &order);
void loadDictionary(); // load the default dictionary, that is mmcifDdl in this case void loadDictionary(); // load the default dictionary, that is mmcifDdl in this case
void loadDictionary(const char* dict); // load one of the compiled in dictionaries void loadDictionary(const char *dict); // load one of the compiled in dictionaries
void loadDictionary(std::istream& is); // load dictionary from input stream void loadDictionary(std::istream &is); // load dictionary from input stream
bool isValid(); bool isValid();
void validateLinks() const; void validateLinks() const;
Datablock& firstDatablock() const Datablock &firstDatablock() const
{
if (mHead == nullptr)
throw std::runtime_error("No datablocks in file");
return *mHead;
}
Datablock &firstDatablock()
{ {
if (mHead == nullptr) if (mHead == nullptr)
throw std::runtime_error("No datablocks in file"); throw std::runtime_error("No datablocks in file");
return *mHead; return *mHead;
} }
void append(Datablock* e); void append(Datablock *e);
Datablock* get(const std::string& name) const; Datablock *get(const std::string &name) const;
Datablock& operator[](const std::string& name); Datablock &operator[](const std::string &name);
struct iterator struct iterator
{ {
using iterator_category = std::forward_iterator_tag; using iterator_category = std::forward_iterator_tag;
using value_type = Datablock; using value_type = Datablock;
using difference_type = std::ptrdiff_t; using difference_type = std::ptrdiff_t;
using pointer = value_type*; using pointer = value_type *;
using reference = value_type&; using reference = value_type &;
iterator(Datablock* db) : mCurrent(db) {} iterator(Datablock *db)
: mCurrent(db)
{
}
reference operator*() { return *mCurrent; } reference operator*() { return *mCurrent; }
pointer operator->() { return mCurrent; } pointer operator->() { return mCurrent; }
iterator& operator++(); iterator &operator++();
iterator operator++(int) { iterator result(*this); this->operator++(); return result; } iterator operator++(int)
{
iterator result(*this);
this->operator++();
return result;
}
bool operator==(const iterator& rhs) const { return mCurrent == rhs.mCurrent; } bool operator==(const iterator &rhs) const { return mCurrent == rhs.mCurrent; }
bool operator!=(const iterator& rhs) const { return not (mCurrent == rhs.mCurrent); } bool operator!=(const iterator &rhs) const { return not(mCurrent == rhs.mCurrent); }
private: private:
Datablock* mCurrent; Datablock *mCurrent;
}; };
iterator begin() const; iterator begin() const;
...@@ -2051,15 +2158,14 @@ class File ...@@ -2051,15 +2158,14 @@ class File
bool empty() const { return mHead == nullptr; } bool empty() const { return mHead == nullptr; }
const Validator& getValidator() const; const Validator &getValidator() const;
void getTagOrder(std::vector<std::string>& tags) const; void getTagOrder(std::vector<std::string> &tags) const;
private: private:
void setValidator(Validator *v);
void setValidator(Validator* v); Datablock *mHead;
Validator *mValidator;
Datablock* mHead;
Validator* mValidator;
}; };
// -------------------------------------------------------------------- // --------------------------------------------------------------------
...@@ -2068,12 +2174,11 @@ class File ...@@ -2068,12 +2174,11 @@ class File
namespace detail namespace detail
{ {
template<typename T> template <typename T>
inline inline bool AnyIsConditionImpl<T>::test(const Category &c, const Row &r) const
bool AnyIsConditionImpl<T>::test(const Category& c, const Row& r) const {
{
bool result = false; bool result = false;
for (auto& f: c.fields()) for (auto &f : c.fields())
{ {
try try
{ {
...@@ -2083,16 +2188,18 @@ bool AnyIsConditionImpl<T>::test(const Category& c, const Row& r) const ...@@ -2083,16 +2188,18 @@ bool AnyIsConditionImpl<T>::test(const Category& c, const Row& r) const
break; break;
} }
} }
catch (...) {} catch (...)
{
}
} }
return result; return result;
} }
inline bool AnyMatchesConditionImpl::test(const Category& c, const Row& r) const inline bool AnyMatchesConditionImpl::test(const Category &c, const Row &r) const
{ {
bool result = false; bool result = false;
for (auto& f: c.fields()) for (auto &f : c.fields())
{ {
try try
{ {
...@@ -2102,30 +2209,32 @@ inline bool AnyMatchesConditionImpl::test(const Category& c, const Row& r) const ...@@ -2102,30 +2209,32 @@ inline bool AnyMatchesConditionImpl::test(const Category& c, const Row& r) const
break; break;
} }
} }
catch (...) {} catch (...)
{
}
} }
return result; return result;
} }
} } // namespace detail
// these should be here, as I learned today // these should be here, as I learned today
inline void swap(cif::Row& a, cif::Row& b) inline void swap(cif::Row &a, cif::Row &b)
{ {
a.swap(b); a.swap(b);
} }
inline void swap(cif::detail::ItemReference& a, cif::detail::ItemReference& b) inline void swap(cif::detail::ItemReference &a, cif::detail::ItemReference &b)
{ {
a.swap(b); a.swap(b);
} }
// -------------------------------------------------------------------- // --------------------------------------------------------------------
template<typename RowType, typename... Ts> template <typename RowType, typename... Ts>
iterator_proxy<RowType, Ts...>::iterator_proxy(Category& cat, row_iterator pos, char const* const columns[N]) iterator_proxy<RowType, Ts...>::iterator_proxy(Category &cat, row_iterator pos, char const *const columns[N])
: mCat(&cat) : mCat(&cat)
, mCBegin(pos) , mCBegin(pos)
, mCEnd(cat.end()) , mCEnd(cat.end())
...@@ -2134,8 +2243,8 @@ iterator_proxy<RowType, Ts...>::iterator_proxy(Category& cat, row_iterator pos, ...@@ -2134,8 +2243,8 @@ iterator_proxy<RowType, Ts...>::iterator_proxy(Category& cat, row_iterator pos,
mCix[i] = mCat->getColumnIndex(columns[i]); mCix[i] = mCat->getColumnIndex(columns[i]);
} }
template<typename RowType, typename... Ts> template <typename RowType, typename... Ts>
iterator_proxy<RowType, Ts...>::iterator_proxy(Category& cat, row_iterator pos, std::initializer_list<char const*> columns) iterator_proxy<RowType, Ts...>::iterator_proxy(Category &cat, row_iterator pos, std::initializer_list<char const *> columns)
: mCat(&cat) : mCat(&cat)
, mCBegin(pos) , mCBegin(pos)
, mCEnd(cat.end()) , mCEnd(cat.end())
...@@ -2143,30 +2252,36 @@ iterator_proxy<RowType, Ts...>::iterator_proxy(Category& cat, row_iterator pos, ...@@ -2143,30 +2252,36 @@ iterator_proxy<RowType, Ts...>::iterator_proxy(Category& cat, row_iterator pos,
// static_assert(columns.size() == N, "The list of column names should be exactly the same as the list of requested columns"); // static_assert(columns.size() == N, "The list of column names should be exactly the same as the list of requested columns");
std::size_t i = 0; std::size_t i = 0;
for (auto column: columns) for (auto column : columns)
mCix[i++] = mCat->getColumnIndex(column); mCix[i++] = mCat->getColumnIndex(column);
} }
// -------------------------------------------------------------------- // --------------------------------------------------------------------
template<typename RowType, typename... Ts> template <typename RowType, typename... Ts>
conditional_iterator_proxy<RowType, Ts...>::conditional_iterator_impl::conditional_iterator_impl( conditional_iterator_proxy<RowType, Ts...>::conditional_iterator_impl::conditional_iterator_impl(
Category& cat, row_iterator pos, const Condition& cond, const std::array<size_t,N>& cix) Category &cat, row_iterator pos, const Condition &cond, const std::array<size_t, N> &cix)
: mCat(&cat), mBegin(pos, cix), mEnd(cat.end(), cix), mCondition(&cond) : mCat(&cat)
, mBegin(pos, cix)
, mEnd(cat.end(), cix)
, mCondition(&cond)
{ {
} }
template<typename RowType, typename... Ts> template <typename RowType, typename... Ts>
conditional_iterator_proxy<RowType, Ts...>::conditional_iterator_proxy(conditional_iterator_proxy&& p) conditional_iterator_proxy<RowType, Ts...>::conditional_iterator_proxy(conditional_iterator_proxy &&p)
: mCat(nullptr), mCBegin(p.mCBegin), mCEnd(p.mCEnd), mCix(p.mCix) : mCat(nullptr)
, mCBegin(p.mCBegin)
, mCEnd(p.mCEnd)
, mCix(p.mCix)
{ {
std::swap(mCat, p.mCat); std::swap(mCat, p.mCat);
std::swap(mCix, p.mCix); std::swap(mCix, p.mCix);
mCondition.swap(p.mCondition); mCondition.swap(p.mCondition);
} }
template<typename RowType, typename... Ts> template <typename RowType, typename... Ts>
conditional_iterator_proxy<RowType, Ts...>::conditional_iterator_proxy(Category& cat, row_iterator pos, Condition&& cond) conditional_iterator_proxy<RowType, Ts...>::conditional_iterator_proxy(Category &cat, row_iterator pos, Condition &&cond)
: mCat(&cat) : mCat(&cat)
, mCondition(std::move(cond)) , mCondition(std::move(cond))
, mCBegin(pos) , mCBegin(pos)
...@@ -2178,9 +2293,9 @@ conditional_iterator_proxy<RowType, Ts...>::conditional_iterator_proxy(Category& ...@@ -2178,9 +2293,9 @@ conditional_iterator_proxy<RowType, Ts...>::conditional_iterator_proxy(Category&
++mCBegin; ++mCBegin;
} }
template<typename RowType, typename... Ts> template <typename RowType, typename... Ts>
template<std::size_t TN, std::enable_if_t<TN != 0, bool>> template <std::size_t TN, std::enable_if_t<TN != 0, bool>>
conditional_iterator_proxy<RowType, Ts...>::conditional_iterator_proxy(Category& cat, row_iterator pos, Condition&& cond, const char* const columns[N]) conditional_iterator_proxy<RowType, Ts...>::conditional_iterator_proxy(Category &cat, row_iterator pos, Condition &&cond, const char *const columns[N])
: mCat(&cat) : mCat(&cat)
, mCondition(std::move(cond)) , mCondition(std::move(cond))
, mCBegin(pos) , mCBegin(pos)
...@@ -2195,33 +2310,33 @@ conditional_iterator_proxy<RowType, Ts...>::conditional_iterator_proxy(Category& ...@@ -2195,33 +2310,33 @@ conditional_iterator_proxy<RowType, Ts...>::conditional_iterator_proxy(Category&
mCix[i] = mCat->getColumnIndex(columns[i]); mCix[i] = mCat->getColumnIndex(columns[i]);
} }
template<typename RowType, typename... Ts> template <typename RowType, typename... Ts>
conditional_iterator_proxy<RowType, Ts...>& conditional_iterator_proxy<RowType, Ts...>::operator=(conditional_iterator_proxy&& p) conditional_iterator_proxy<RowType, Ts...> &conditional_iterator_proxy<RowType, Ts...>::operator=(conditional_iterator_proxy &&p)
{ {
swap(p); swap(p);
return *this; return *this;
} }
template<typename RowType, typename... Ts> template <typename RowType, typename... Ts>
typename conditional_iterator_proxy<RowType, Ts...>::iterator conditional_iterator_proxy<RowType, Ts...>::begin() const typename conditional_iterator_proxy<RowType, Ts...>::iterator conditional_iterator_proxy<RowType, Ts...>::begin() const
{ {
return iterator(*mCat, mCBegin, mCondition, mCix); return iterator(*mCat, mCBegin, mCondition, mCix);
} }
template<typename RowType, typename... Ts> template <typename RowType, typename... Ts>
typename conditional_iterator_proxy<RowType, Ts...>::iterator conditional_iterator_proxy<RowType, Ts...>::end() const typename conditional_iterator_proxy<RowType, Ts...>::iterator conditional_iterator_proxy<RowType, Ts...>::end() const
{ {
return iterator(*mCat, mCEnd, mCondition, mCix); return iterator(*mCat, mCEnd, mCondition, mCix);
} }
template<typename RowType, typename... Ts> template <typename RowType, typename... Ts>
bool conditional_iterator_proxy<RowType, Ts...>::empty() const bool conditional_iterator_proxy<RowType, Ts...>::empty() const
{ {
return mCBegin == mCEnd; return mCBegin == mCEnd;
} }
template<typename RowType, typename... Ts> template <typename RowType, typename... Ts>
void conditional_iterator_proxy<RowType, Ts...>::swap(conditional_iterator_proxy& rhs) void conditional_iterator_proxy<RowType, Ts...>::swap(conditional_iterator_proxy &rhs)
{ {
std::swap(mCat, rhs.mCat); std::swap(mCat, rhs.mCat);
mCondition.swap(rhs.mCondition); mCondition.swap(rhs.mCondition);
...@@ -2230,5 +2345,4 @@ void conditional_iterator_proxy<RowType, Ts...>::swap(conditional_iterator_proxy ...@@ -2230,5 +2345,4 @@ void conditional_iterator_proxy<RowType, Ts...>::swap(conditional_iterator_proxy
std::swap(mCix, rhs.mCix); std::swap(mCix, rhs.mCix);
} }
} } // namespace cif
...@@ -397,6 +397,8 @@ class File : public std::enable_shared_from_this<File> ...@@ -397,6 +397,8 @@ class File : public std::enable_shared_from_this<File>
File(const File &) = delete; File(const File &) = delete;
File &operator=(const File &) = delete; File &operator=(const File &) = delete;
cif::Datablock& createDatablock(const std::string &name);
void load(const std::string &path); void load(const std::string &path);
void save(const std::string &path); void save(const std::string &path);
...@@ -482,6 +484,19 @@ class Structure ...@@ -482,6 +484,19 @@ class Structure
void changeResidue(const Residue &res, const std::string &newCompound, void changeResidue(const Residue &res, const std::string &newCompound,
const std::vector<std::tuple<std::string, std::string>> &remappedAtoms); const std::vector<std::tuple<std::string, std::string>> &remappedAtoms);
/// \brief Create a new non-polymer entity, returns new ID
/// \param data The data to use to fill the entity
/// \param mon_id The mon_id for the new nonpoly
/// \param name The name of the nonpoly
/// \return The ID of the created entity
std::string createEntityNonPoly(std::vector<cif::Item> data, const std::string &mon_id);
/// \brief Create a new NonPolymer struct_asym with atoms constructed from \a atom_data, returns asym_id
/// \param entity_id The entity ID of the new nonpoly
/// \param atoms The array of atom data fields
/// \return The newly create asym ID
std::string createNonpoly(const std::string &entity_id, const std::vector<cif::Item> &atoms);
/// To sort the atoms in order of model > asym-id > res-id > atom-id /// To sort the atoms in order of model > asym-id > res-id > atom-id
/// Will asssign new atom_id's to all atoms. Be carefull /// Will asssign new atom_id's to all atoms. Be carefull
void sortAtoms(); void sortAtoms();
......
...@@ -26,26 +26,26 @@ ...@@ -26,26 +26,26 @@
#include <cassert> #include <cassert>
#include <stack> #include <fstream>
#include <tuple> #include <numeric>
#include <regex> #include <regex>
#include <set> #include <set>
#include <stack>
#include <tuple>
#include <unordered_map> #include <unordered_map>
#include <numeric>
#include <fstream>
#include <filesystem> #include <filesystem>
#include <boost/algorithm/string.hpp> #include <boost/algorithm/string.hpp>
#include <boost/iostreams/filtering_stream.hpp>
#include <boost/iostreams/filter/bzip2.hpp> #include <boost/iostreams/filter/bzip2.hpp>
#include <boost/iostreams/filter/gzip.hpp> #include <boost/iostreams/filter/gzip.hpp>
#include <boost/iostreams/filtering_stream.hpp>
#include <boost/logic/tribool.hpp> #include <boost/logic/tribool.hpp>
#include "cif++/Cif++.hpp" #include "cif++/Cif++.hpp"
#include "cif++/CifParser.hpp" #include "cif++/CifParser.hpp"
#include "cif++/CifValidator.hpp"
#include "cif++/CifUtils.hpp" #include "cif++/CifUtils.hpp"
#include "cif++/CifValidator.hpp"
namespace ba = boost::algorithm; namespace ba = boost::algorithm;
namespace io = boost::iostreams; namespace io = boost::iostreams;
...@@ -56,7 +56,7 @@ namespace cif ...@@ -56,7 +56,7 @@ namespace cif
CIFPP_EXPORT int VERBOSE = 0; CIFPP_EXPORT int VERBOSE = 0;
static const char* kEmptyResult = ""; static const char *kEmptyResult = "";
// -------------------------------------------------------------------- // --------------------------------------------------------------------
// most internal data structures are stored as linked lists // most internal data structures are stored as linked lists
...@@ -64,26 +64,27 @@ static const char* kEmptyResult = ""; ...@@ -64,26 +64,27 @@ static const char* kEmptyResult = "";
struct ItemValue struct ItemValue
{ {
ItemValue* mNext; ItemValue *mNext;
uint32_t mColumnIndex; uint32_t mColumnIndex;
char mText[4]; char mText[4];
ItemValue(const char* v, size_t columnIndex); ItemValue(const char *v, size_t columnIndex);
~ItemValue(); ~ItemValue();
bool empty() const { return mText[0] == 0 or ((mText[0] == '.' or mText[0] == '?') and mText[1] == 0); } bool empty() const { return mText[0] == 0 or ((mText[0] == '.' or mText[0] == '?') and mText[1] == 0); }
bool null() const { return mText[0] == '.' and mText[1] == 0; } bool null() const { return mText[0] == '.' and mText[1] == 0; }
bool unknown() const { return mText[0] == '?' and mText[1] == 0; } bool unknown() const { return mText[0] == '?' and mText[1] == 0; }
void* operator new(size_t size, size_t dataSize); void *operator new(size_t size, size_t dataSize);
void operator delete(void* p); void operator delete(void *p);
void operator delete(void* p, size_t dataSize); void operator delete(void *p, size_t dataSize);
}; };
// -------------------------------------------------------------------- // --------------------------------------------------------------------
ItemValue::ItemValue(const char* value, size_t columnIndex) ItemValue::ItemValue(const char *value, size_t columnIndex)
: mNext(nullptr), mColumnIndex(uint32_t(columnIndex)) : mNext(nullptr)
, mColumnIndex(uint32_t(columnIndex))
{ {
assert(columnIndex < std::numeric_limits<uint32_t>::max()); assert(columnIndex < std::numeric_limits<uint32_t>::max());
strcpy(mText, value); strcpy(mText, value);
...@@ -101,17 +102,17 @@ ItemValue::~ItemValue() ...@@ -101,17 +102,17 @@ ItemValue::~ItemValue()
} }
} }
void* ItemValue::operator new(size_t size, size_t dataSize) void *ItemValue::operator new(size_t size, size_t dataSize)
{ {
return malloc(size - 4 + dataSize + 1); return malloc(size - 4 + dataSize + 1);
} }
void ItemValue::operator delete(void* p) void ItemValue::operator delete(void *p)
{ {
free(p); free(p);
} }
void ItemValue::operator delete(void* p, size_t dataSize) void ItemValue::operator delete(void *p, size_t dataSize)
{ {
free(p); free(p);
} }
...@@ -122,7 +123,7 @@ void ItemValue::operator delete(void* p, size_t dataSize) ...@@ -122,7 +123,7 @@ void ItemValue::operator delete(void* p, size_t dataSize)
struct ItemColumn struct ItemColumn
{ {
std::string mName; // store lower-case, for optimization std::string mName; // store lower-case, for optimization
const ValidateItem* mValidator; const ValidateItem *mValidator;
}; };
// itemRow contains the actual values for a Row in a Category // itemRow contains the actual values for a Row in a Category
...@@ -132,7 +133,7 @@ struct ItemRow ...@@ -132,7 +133,7 @@ struct ItemRow
~ItemRow(); ~ItemRow();
void drop(size_t columnIx); void drop(size_t columnIx);
const char* c_str(size_t columnIx) const; const char *c_str(size_t columnIx) const;
std::string str() const std::string str() const
{ {
...@@ -152,13 +153,13 @@ struct ItemRow ...@@ -152,13 +153,13 @@ struct ItemRow
return s.str(); return s.str();
} }
ItemRow* mNext; ItemRow *mNext;
Category* mCategory; Category *mCategory;
ItemValue* mValues; ItemValue *mValues;
uint32_t mLineNr = 0; uint32_t mLineNr = 0;
}; };
std::ostream& operator<<(std::ostream& os, const ItemRow& r) std::ostream &operator<<(std::ostream &os, const ItemRow &r)
{ {
os << r.mCategory->name() << '['; os << r.mCategory->name() << '[';
for (auto iv = r.mValues; iv != nullptr; iv = iv->mNext) for (auto iv = r.mValues; iv != nullptr; iv = iv->mNext)
...@@ -219,9 +220,9 @@ void ItemRow::drop(size_t columnIx) ...@@ -219,9 +220,9 @@ void ItemRow::drop(size_t columnIx)
#endif #endif
} }
const char* ItemRow::c_str(size_t columnIx) const const char *ItemRow::c_str(size_t columnIx) const
{ {
const char* result = kEmptyResult; const char *result = kEmptyResult;
for (auto v = mValues; v != nullptr; v = v->mNext) for (auto v = mValues; v != nullptr; v = v->mNext)
{ {
...@@ -240,8 +241,8 @@ const char* ItemRow::c_str(size_t columnIx) const ...@@ -240,8 +241,8 @@ const char* ItemRow::c_str(size_t columnIx) const
namespace detail namespace detail
{ {
ItemReference& ItemReference::operator=(const std::string& value) ItemReference &ItemReference::operator=(const std::string &value)
{ {
if (mConst) if (mConst)
throw std::logic_error("Attempt to write to a constant row"); throw std::logic_error("Attempt to write to a constant row");
...@@ -250,15 +251,15 @@ ItemReference& ItemReference::operator=(const std::string& value) ...@@ -250,15 +251,15 @@ ItemReference& ItemReference::operator=(const std::string& value)
mRow.assign(mName, value, false); mRow.assign(mName, value, false);
return *this; return *this;
} }
const char* ItemReference::c_str() const const char *ItemReference::c_str() const
{ {
const char* result = kEmptyResult; const char *result = kEmptyResult;
if (mRow.mData != nullptr /* and mRow.mData->mCategory != nullptr*/) if (mRow.mData != nullptr /* and mRow.mData->mCategory != nullptr*/)
{ {
// assert(mRow.mData->mCategory); // assert(mRow.mData->mCategory);
for (auto iv = mRow.mData->mValues; iv != nullptr; iv = iv->mNext) for (auto iv = mRow.mData->mValues; iv != nullptr; iv = iv->mNext)
{ {
...@@ -273,11 +274,11 @@ const char* ItemReference::c_str() const ...@@ -273,11 +274,11 @@ const char* ItemReference::c_str() const
} }
return result; return result;
} }
const char* ItemReference::c_str(const char* defaultValue) const const char *ItemReference::c_str(const char *defaultValue) const
{ {
const char* result = defaultValue; const char *result = defaultValue;
if (mRow.mData != nullptr and mRow.mData->mCategory != nullptr) if (mRow.mData != nullptr and mRow.mData->mCategory != nullptr)
{ {
...@@ -302,15 +303,15 @@ const char* ItemReference::c_str(const char* defaultValue) const ...@@ -302,15 +303,15 @@ const char* ItemReference::c_str(const char* defaultValue) const
} }
return result; return result;
} }
bool ItemReference::empty() const bool ItemReference::empty() const
{ {
return c_str() == kEmptyResult; return c_str() == kEmptyResult;
} }
bool ItemReference::is_null() const bool ItemReference::is_null() const
{ {
boost::tribool result; boost::tribool result;
if (mRow.mData != nullptr and mRow.mData->mCategory != nullptr) if (mRow.mData != nullptr and mRow.mData->mCategory != nullptr)
...@@ -333,26 +334,28 @@ bool ItemReference::is_null() const ...@@ -333,26 +334,28 @@ bool ItemReference::is_null() const
} }
return result ? true : false; return result ? true : false;
} }
void ItemReference::swap(ItemReference& b) void ItemReference::swap(ItemReference &b)
{ {
Row::swap(mColumn, mRow.mData, b.mRow.mData); Row::swap(mColumn, mRow.mData, b.mRow.mData);
} }
std::ostream& operator<<(std::ostream& os, ItemReference& item) std::ostream &operator<<(std::ostream &os, ItemReference &item)
{ {
os << item.c_str(); os << item.c_str();
return os; return os;
} }
} } // namespace detail
// -------------------------------------------------------------------- // --------------------------------------------------------------------
// Datablock implementation // Datablock implementation
Datablock::Datablock(const std::string& name) Datablock::Datablock(const std::string &name)
: mName(name), mValidator(nullptr), mNext(nullptr) : mName(name)
, mValidator(nullptr)
, mNext(nullptr)
{ {
} }
...@@ -361,18 +364,18 @@ Datablock::~Datablock() ...@@ -361,18 +364,18 @@ Datablock::~Datablock()
delete mNext; delete mNext;
} }
std::string Datablock::firstItem(const std::string& tag) const std::string Datablock::firstItem(const std::string &tag) const
{ {
std::string result; std::string result;
std::string catName, itemName; std::string catName, itemName;
std::tie(catName, itemName) = splitTagName(tag); std::tie(catName, itemName) = splitTagName(tag);
for (auto& cat: mCategories) for (auto &cat : mCategories)
{ {
if (iequals(cat.name(), catName)) if (iequals(cat.name(), catName))
{ {
for (auto row: cat) for (auto row : cat)
{ {
result = row[itemName].as<std::string>(); result = row[itemName].as<std::string>();
break; break;
...@@ -385,10 +388,10 @@ std::string Datablock::firstItem(const std::string& tag) const ...@@ -385,10 +388,10 @@ std::string Datablock::firstItem(const std::string& tag) const
return result; return result;
} }
auto Datablock::emplace(const std::string& name) -> std::tuple<iterator,bool> auto Datablock::emplace(const std::string &name) -> std::tuple<iterator, bool>
{ {
bool isNew = false; bool isNew = false;
iterator i = find_if(begin(), end(), [name](const Category& cat) -> bool iterator i = find_if(begin(), end(), [name](const Category &cat) -> bool
{ return iequals(cat.name(), name); }); { return iequals(cat.name(), name); });
if (i == end()) if (i == end())
...@@ -400,16 +403,24 @@ auto Datablock::emplace(const std::string& name) -> std::tuple<iterator,bool> ...@@ -400,16 +403,24 @@ auto Datablock::emplace(const std::string& name) -> std::tuple<iterator,bool>
return std::make_tuple(i, isNew); return std::make_tuple(i, isNew);
} }
Category& Datablock::operator[](const std::string& name) Category &Datablock::operator[](const std::string &name)
{ {
iterator i; iterator i;
std::tie(i, std::ignore) = emplace(name); std::tie(i, std::ignore) = emplace(name);
return *i; return *i;
} }
Category* Datablock::get(const std::string& name) Category *Datablock::get(const std::string &name)
{
auto i = find_if(begin(), end(), [name](const Category &cat) -> bool
{ return iequals(cat.name(), name); });
return i == end() ? nullptr : &*i;
}
const Category *Datablock::get(const std::string &name) const
{ {
auto i = find_if(begin(), end(), [name](const Category& cat) -> bool auto i = find_if(begin(), end(), [name](const Category &cat) -> bool
{ return iequals(cat.name(), name); }); { return iequals(cat.name(), name); });
return i == end() ? nullptr : &*i; return i == end() ? nullptr : &*i;
...@@ -421,47 +432,45 @@ bool Datablock::isValid() ...@@ -421,47 +432,45 @@ bool Datablock::isValid()
throw std::runtime_error("Validator not specified"); throw std::runtime_error("Validator not specified");
bool result = true; bool result = true;
for (auto& cat: *this) for (auto &cat : *this)
result = cat.isValid() and result; result = cat.isValid() and result;
return result; return result;
} }
void Datablock::validateLinks() const void Datablock::validateLinks() const
{ {
for (auto& cat: *this) for (auto &cat : *this)
cat.validateLinks(); cat.validateLinks();
} }
void Datablock::setValidator(Validator* v) void Datablock::setValidator(Validator *v)
{ {
mValidator = v; mValidator = v;
for (auto& cat: *this) for (auto &cat : *this)
cat.setValidator(v); cat.setValidator(v);
} }
void Datablock::add_software(const std::string& name, const std::string& classification, const std::string& versionNr, const std::string& versionDate) void Datablock::add_software(const std::string &name, const std::string &classification, const std::string &versionNr, const std::string &versionDate)
{ {
Category& cat = operator[]("software"); Category &cat = operator[]("software");
auto ordNr = cat.size() + 1; auto ordNr = cat.size() + 1;
// TODO: should we check this ordinal number??? // TODO: should we check this ordinal number???
cat.emplace({ cat.emplace({{"pdbx_ordinal", ordNr},
{ "pdbx_ordinal", ordNr }, {"name", name},
{ "name", name }, {"version", versionNr},
{ "version", versionNr }, {"date", versionDate},
{ "date", versionDate }, {"classification", classification}});
{ "classification", classification }
});
} }
void Datablock::getTagOrder(std::vector<std::string>& tags) const void Datablock::getTagOrder(std::vector<std::string> &tags) const
{ {
for (auto& cat: *this) for (auto &cat : *this)
cat.getTagOrder(tags); cat.getTagOrder(tags);
} }
void Datablock::write(std::ostream& os) void Datablock::write(std::ostream &os)
{ {
os << "data_" << mName << std::endl os << "data_" << mName << std::endl
<< "# " << std::endl; << "# " << std::endl;
...@@ -470,7 +479,7 @@ void Datablock::write(std::ostream& os) ...@@ -470,7 +479,7 @@ void Datablock::write(std::ostream& os)
// and if it exists, _AND_ we have a Validator, write out the // and if it exists, _AND_ we have a Validator, write out the
// audit_conform record. // audit_conform record.
for (auto& cat: mCategories) for (auto &cat : mCategories)
{ {
if (cat.name() == "entry") if (cat.name() == "entry")
{ {
...@@ -479,10 +488,8 @@ void Datablock::write(std::ostream& os) ...@@ -479,10 +488,8 @@ void Datablock::write(std::ostream& os)
if (mValidator != nullptr) if (mValidator != nullptr)
{ {
Category auditConform(*this, "audit_conform", nullptr); Category auditConform(*this, "audit_conform", nullptr);
auditConform.emplace({ auditConform.emplace({{"dict_name", mValidator->dictName()},
{ "dict_name", mValidator->dictName() }, {"dict_version", mValidator->dictVersion()}});
{ "dict_version", mValidator->dictVersion() }
});
auditConform.write(os); auditConform.write(os);
} }
...@@ -490,35 +497,36 @@ void Datablock::write(std::ostream& os) ...@@ -490,35 +497,36 @@ void Datablock::write(std::ostream& os)
} }
} }
for (auto& cat: mCategories) for (auto &cat : mCategories)
{ {
if (cat.name() != "entry" and cat.name() != "audit_conform") if (cat.name() != "entry" and cat.name() != "audit_conform")
cat.write(os); cat.write(os);
} }
} }
void Datablock::write(std::ostream& os, const std::vector<std::string>& order) void Datablock::write(std::ostream &os, const std::vector<std::string> &order)
{ {
os << "data_" << mName << std::endl os << "data_" << mName << std::endl
<< "# " << std::endl; << "# " << std::endl;
std::vector<std::string> catOrder; std::vector<std::string> catOrder;
for (auto& o: order) for (auto &o : order)
{ {
std::string cat, Item; std::string cat, Item;
std::tie(cat, Item) = splitTagName(o); std::tie(cat, Item) = splitTagName(o);
if (find_if(catOrder.rbegin(), catOrder.rend(), [cat](const std::string& s) -> bool { return iequals(cat, s); }) == catOrder.rend()) if (find_if(catOrder.rbegin(), catOrder.rend(), [cat](const std::string &s) -> bool
{ return iequals(cat, s); }) == catOrder.rend())
catOrder.push_back(cat); catOrder.push_back(cat);
} }
for (auto& c: catOrder) for (auto &c : catOrder)
{ {
auto cat = get(c); auto cat = get(c);
if (cat == nullptr) if (cat == nullptr)
continue; continue;
std::vector<std::string> items; std::vector<std::string> items;
for (auto& o: order) for (auto &o : order)
{ {
std::string catName, Item; std::string catName, Item;
std::tie(catName, Item) = splitTagName(o); std::tie(catName, Item) = splitTagName(o);
...@@ -531,43 +539,115 @@ void Datablock::write(std::ostream& os, const std::vector<std::string>& order) ...@@ -531,43 +539,115 @@ void Datablock::write(std::ostream& os, const std::vector<std::string>& order)
} }
// for any Category we missed in the catOrder // for any Category we missed in the catOrder
for (auto& cat: mCategories) for (auto &cat : mCategories)
{ {
if (find_if(catOrder.begin(), catOrder.end(), [&](const std::string& s) -> bool { return iequals(cat.name(), s); }) != catOrder.end()) if (find_if(catOrder.begin(), catOrder.end(), [&](const std::string &s) -> bool
{ return iequals(cat.name(), s); }) != catOrder.end())
continue; continue;
cat.write(os); cat.write(os);
} }
// // mmcif support, sort of. First write the 'entry' Category // // mmcif support, sort of. First write the 'entry' Category
// // and if it exists, _AND_ we have a Validator, write out the // // and if it exists, _AND_ we have a Validator, write out the
// // auditConform record. // // auditConform record.
// //
// for (auto& cat: mCategories) // for (auto& cat: mCategories)
// { // {
// if (cat.name() == "entry") // if (cat.name() == "entry")
// { // {
// cat.write(os); // cat.write(os);
// //
// if (mValidator != nullptr) // if (mValidator != nullptr)
// { // {
// Category auditConform(*this, "audit_conform", nullptr); // Category auditConform(*this, "audit_conform", nullptr);
// auditConform.emplace({ // auditConform.emplace({
// { "dict_name", mValidator->dictName() }, // { "dict_name", mValidator->dictName() },
// { "dict_version", mValidator->dictVersion() } // { "dict_version", mValidator->dictVersion() }
// }); // });
// auditConform.write(os); // auditConform.write(os);
// } // }
// //
// break; // break;
// } // }
// } // }
// //
// for (auto& cat: mCategories) // for (auto& cat: mCategories)
// { // {
// if (cat.name() != "entry" and cat.name() != "audit_conform") // if (cat.name() != "entry" and cat.name() != "audit_conform")
// cat.write(os); // cat.write(os);
// } // }
}
bool operator==(const cif::Datablock &dbA, const cif::Datablock &dbB)
{
std::vector<std::string> catA, catB;
for (auto &cat : dbA)
{
if (not cat.empty())
catA.push_back(cat.name());
}
sort(catA.begin(), catA.end());
for (auto &cat : dbB)
{
if (not cat.empty())
catB.push_back(cat.name());
}
sort(catB.begin(), catB.end());
// loop over categories twice, to group output
// First iteration is to list missing categories.
std::vector<std::string> missingA, missingB;
auto catA_i = catA.begin(), catB_i = catB.begin();
while (catA_i != catA.end() and catB_i != catB.end())
{
if (not iequals(*catA_i, *catB_i))
return false;
++catA_i, ++catB_i;
}
if (catA_i != catA.end() or catB_i != catB.end())
return false;
// Second loop, now compare category values
catA_i = catA.begin(), catB_i = catB.begin();
while (catA_i != catA.end() and catB_i != catB.end())
{
std::string nA = *catA_i;
ba::to_lower(nA);
std::string nB = *catB_i;
ba::to_lower(nB);
int d = nA.compare(nB);
if (d > 0)
++catB_i;
else if (d < 0)
++catA_i;
else
{
if (not (*dbA.get(*catA_i) == *dbB.get(*catB_i)))
return false;
++catA_i;
++catB_i;
}
}
return true;
}
std::ostream& operator<<(std::ostream &os, const Datablock &data)
{
// whoohoo... this sucks!
const_cast<Datablock&>(data).write(os);
return os;
} }
// -------------------------------------------------------------------- // --------------------------------------------------------------------
...@@ -576,8 +656,8 @@ void Datablock::write(std::ostream& os, const std::vector<std::string>& order) ...@@ -576,8 +656,8 @@ void Datablock::write(std::ostream& os, const std::vector<std::string>& order)
namespace detail namespace detail
{ {
void KeyCompareConditionImpl::prepare(const Category& c) void KeyCompareConditionImpl::prepare(const Category &c)
{ {
mItemIx = c.getColumnIndex(mItemTag); mItemIx = c.getColumnIndex(mItemTag);
auto cv = c.getCatValidator(); auto cv = c.getCatValidator();
...@@ -590,19 +670,19 @@ void KeyCompareConditionImpl::prepare(const Category& c) ...@@ -590,19 +670,19 @@ void KeyCompareConditionImpl::prepare(const Category& c)
mCaseInsensitive = type->mPrimitiveType == DDL_PrimitiveType::UChar; mCaseInsensitive = type->mPrimitiveType == DDL_PrimitiveType::UChar;
} }
} }
} }
void KeyIsEmptyConditionImpl::prepare(const Category& c) void KeyIsEmptyConditionImpl::prepare(const Category &c)
{ {
mItemIx = c.getColumnIndex(mItemTag); mItemIx = c.getColumnIndex(mItemTag);
} }
void KeyMatchesConditionImpl::prepare(const Category& c) void KeyMatchesConditionImpl::prepare(const Category &c)
{ {
mItemIx = c.getColumnIndex(mItemTag); mItemIx = c.getColumnIndex(mItemTag);
} }
} } // namespace detail
// -------------------------------------------------------------------- // --------------------------------------------------------------------
// //
...@@ -611,32 +691,31 @@ void KeyMatchesConditionImpl::prepare(const Category& c) ...@@ -611,32 +691,31 @@ void KeyMatchesConditionImpl::prepare(const Category& c)
class RowComparator class RowComparator
{ {
public: public:
RowComparator(Category *cat)
RowComparator(Category* cat)
: RowComparator(cat, cat->getCatValidator()->mKeys.begin(), cat->getCatValidator()->mKeys.end()) : RowComparator(cat, cat->getCatValidator()->mKeys.begin(), cat->getCatValidator()->mKeys.end())
{ {
} }
template<typename KeyIter> template <typename KeyIter>
RowComparator(Category* cat, KeyIter b, KeyIter e); RowComparator(Category *cat, KeyIter b, KeyIter e);
int operator()(const ItemRow* a, const ItemRow* b) const; int operator()(const ItemRow *a, const ItemRow *b) const;
int operator()(const Row& a, const Row& b) const int operator()(const Row &a, const Row &b) const
{ {
return operator()(a.mData, b.mData); return operator()(a.mData, b.mData);
} }
private: private:
typedef std::function<int(const char*,const char*)> compareFunc; typedef std::function<int(const char *, const char *)> compareFunc;
typedef std::tuple<size_t,compareFunc> keyComp; typedef std::tuple<size_t, compareFunc> keyComp;
std::vector<keyComp> mComp; std::vector<keyComp> mComp;
}; };
template<typename KeyIter> template <typename KeyIter>
RowComparator::RowComparator(Category* cat, KeyIter b, KeyIter e) RowComparator::RowComparator(Category *cat, KeyIter b, KeyIter e)
{ {
auto cv = cat->getCatValidator(); auto cv = cat->getCatValidator();
...@@ -660,21 +739,21 @@ RowComparator::RowComparator(Category* cat, KeyIter b, KeyIter e) ...@@ -660,21 +739,21 @@ RowComparator::RowComparator(Category* cat, KeyIter b, KeyIter e)
} }
} }
int RowComparator::operator()(const ItemRow* a, const ItemRow* b) const int RowComparator::operator()(const ItemRow *a, const ItemRow *b) const
{ {
assert(a); assert(a);
assert(b); assert(b);
int d = 0; int d = 0;
for (auto& c: mComp) for (auto &c : mComp)
{ {
size_t k; size_t k;
compareFunc f; compareFunc f;
std::tie(k, f) = c; std::tie(k, f) = c;
const char* ka = a->c_str(k); const char *ka = a->c_str(k);
const char* kb = b->c_str(k); const char *kb = b->c_str(k);
d = f(ka, kb); d = f(ka, kb);
...@@ -693,26 +772,26 @@ int RowComparator::operator()(const ItemRow* a, const ItemRow* b) const ...@@ -693,26 +772,26 @@ int RowComparator::operator()(const ItemRow* a, const ItemRow* b) const
class CatIndex class CatIndex
{ {
public: public:
CatIndex(Category* cat); CatIndex(Category *cat);
~CatIndex(); ~CatIndex();
ItemRow* find(ItemRow* k) const; ItemRow *find(ItemRow *k) const;
void insert(ItemRow* r); void insert(ItemRow *r);
void erase(ItemRow* r); void erase(ItemRow *r);
// batch create // batch create
void reconstruct(); void reconstruct();
// reorder the ItemRow's and returns new head and tail // reorder the ItemRow's and returns new head and tail
std::tuple<ItemRow*,ItemRow*> reorder() std::tuple<ItemRow *, ItemRow *> reorder()
{ {
std::tuple<ItemRow*,ItemRow*> result = std::make_tuple(nullptr, nullptr); std::tuple<ItemRow *, ItemRow *> result = std::make_tuple(nullptr, nullptr);
if (mRoot != nullptr) if (mRoot != nullptr)
{ {
entry* head = findMin(mRoot); entry *head = findMin(mRoot);
entry* tail = reorder(mRoot); entry *tail = reorder(mRoot);
tail->mRow->mNext = nullptr; tail->mRow->mNext = nullptr;
...@@ -723,14 +802,18 @@ class CatIndex ...@@ -723,14 +802,18 @@ class CatIndex
} }
size_t size() const; size_t size() const;
// bool isValid() const; // bool isValid() const;
private: private:
struct entry struct entry
{ {
entry(ItemRow* r) entry(ItemRow *r)
: mRow(r), mLeft(nullptr), mRight(nullptr), mRed(true) {} : mRow(r)
, mLeft(nullptr)
, mRight(nullptr)
, mRed(true)
{
}
~entry() ~entry()
{ {
...@@ -738,20 +821,20 @@ class CatIndex ...@@ -738,20 +821,20 @@ class CatIndex
delete mRight; delete mRight;
} }
ItemRow* mRow; ItemRow *mRow;
entry* mLeft; entry *mLeft;
entry* mRight; entry *mRight;
bool mRed; bool mRed;
}; };
entry* insert(entry* h, ItemRow* v); entry *insert(entry *h, ItemRow *v);
entry* erase(entry* h, ItemRow* k); entry *erase(entry *h, ItemRow *k);
// void validate(entry* h, bool isParentRed, uint32_t blackDepth, uint32_t& minBlack, uint32_t& maxBlack) const; // void validate(entry* h, bool isParentRed, uint32_t blackDepth, uint32_t& minBlack, uint32_t& maxBlack) const;
entry* rotateLeft(entry* h) entry *rotateLeft(entry *h)
{ {
entry* x = h->mRight; entry *x = h->mRight;
h->mRight = x->mLeft; h->mRight = x->mLeft;
x->mLeft = h; x->mLeft = h;
x->mRed = h->mRed; x->mRed = h->mRed;
...@@ -759,9 +842,9 @@ class CatIndex ...@@ -759,9 +842,9 @@ class CatIndex
return x; return x;
} }
entry* rotateRight(entry* h) entry *rotateRight(entry *h)
{ {
entry* x = h->mLeft; entry *x = h->mLeft;
h->mLeft = x->mRight; h->mLeft = x->mRight;
x->mRight = h; x->mRight = h;
x->mRed = h->mRed; x->mRed = h->mRed;
...@@ -769,7 +852,7 @@ class CatIndex ...@@ -769,7 +852,7 @@ class CatIndex
return x; return x;
} }
void flipColour(entry* h) void flipColour(entry *h)
{ {
h->mRed = not h->mRed; h->mRed = not h->mRed;
...@@ -780,12 +863,12 @@ class CatIndex ...@@ -780,12 +863,12 @@ class CatIndex
h->mRight->mRed = not h->mRight->mRed; h->mRight->mRed = not h->mRight->mRed;
} }
bool isRed(entry* h) const bool isRed(entry *h) const
{ {
return h != nullptr and h->mRed; return h != nullptr and h->mRed;
} }
entry* moveRedLeft(entry* h) entry *moveRedLeft(entry *h)
{ {
flipColour(h); flipColour(h);
...@@ -799,7 +882,7 @@ class CatIndex ...@@ -799,7 +882,7 @@ class CatIndex
return h; return h;
} }
entry* moveRedRight(entry* h) entry *moveRedRight(entry *h)
{ {
flipColour(h); flipColour(h);
...@@ -812,7 +895,7 @@ class CatIndex ...@@ -812,7 +895,7 @@ class CatIndex
return h; return h;
} }
entry* fixUp(entry* h) entry *fixUp(entry *h)
{ {
if (isRed(h->mRight)) if (isRed(h->mRight))
h = rotateLeft(h); h = rotateLeft(h);
...@@ -826,7 +909,7 @@ class CatIndex ...@@ -826,7 +909,7 @@ class CatIndex
return h; return h;
} }
entry* findMin(entry* h) entry *findMin(entry *h)
{ {
while (h->mLeft != nullptr) while (h->mLeft != nullptr)
h = h->mLeft; h = h->mLeft;
...@@ -834,7 +917,7 @@ class CatIndex ...@@ -834,7 +917,7 @@ class CatIndex
return h; return h;
} }
entry* eraseMin(entry* h) entry *eraseMin(entry *h)
{ {
if (h->mLeft == nullptr) if (h->mLeft == nullptr)
{ {
...@@ -855,7 +938,7 @@ class CatIndex ...@@ -855,7 +938,7 @@ class CatIndex
} }
// Fix mNext fields for rows in order of this index // Fix mNext fields for rows in order of this index
entry* reorder(entry* e) entry *reorder(entry *e)
{ {
auto result = e; auto result = e;
...@@ -876,13 +959,15 @@ class CatIndex ...@@ -876,13 +959,15 @@ class CatIndex
return result; return result;
} }
Category& mCat; Category &mCat;
RowComparator mComp; RowComparator mComp;
entry* mRoot; entry *mRoot;
}; };
CatIndex::CatIndex(Category* cat) CatIndex::CatIndex(Category *cat)
: mCat(*cat), mComp(cat), mRoot(nullptr) : mCat(*cat)
, mComp(cat)
, mRoot(nullptr)
{ {
} }
...@@ -891,9 +976,9 @@ CatIndex::~CatIndex() ...@@ -891,9 +976,9 @@ CatIndex::~CatIndex()
delete mRoot; delete mRoot;
} }
ItemRow* CatIndex::find(ItemRow* k) const ItemRow *CatIndex::find(ItemRow *k) const
{ {
const entry* r = mRoot; const entry *r = mRoot;
while (r != nullptr) while (r != nullptr)
{ {
int d = mComp(k, r->mRow); int d = mComp(k, r->mRow);
...@@ -908,20 +993,22 @@ ItemRow* CatIndex::find(ItemRow* k) const ...@@ -908,20 +993,22 @@ ItemRow* CatIndex::find(ItemRow* k) const
return r ? r->mRow : nullptr; return r ? r->mRow : nullptr;
} }
void CatIndex::insert(ItemRow* k) void CatIndex::insert(ItemRow *k)
{ {
mRoot = insert(mRoot, k); mRoot = insert(mRoot, k);
mRoot->mRed = false; mRoot->mRed = false;
} }
CatIndex::entry* CatIndex::insert(entry* h, ItemRow* v) CatIndex::entry *CatIndex::insert(entry *h, ItemRow *v)
{ {
if (h == nullptr) if (h == nullptr)
return new entry(v); return new entry(v);
int d = mComp(v, h->mRow); int d = mComp(v, h->mRow);
if (d < 0) h->mLeft = insert(h->mLeft, v); if (d < 0)
else if (d > 0) h->mRight = insert(h->mRight, v); h->mLeft = insert(h->mLeft, v);
else if (d > 0)
h->mRight = insert(h->mRight, v);
else else
throw std::runtime_error("Duplicate Key violation, cat: " + mCat.name() + " values: " + v->str()); throw std::runtime_error("Duplicate Key violation, cat: " + mCat.name() + " values: " + v->str());
...@@ -937,14 +1024,14 @@ CatIndex::entry* CatIndex::insert(entry* h, ItemRow* v) ...@@ -937,14 +1024,14 @@ CatIndex::entry* CatIndex::insert(entry* h, ItemRow* v)
return h; return h;
} }
void CatIndex::erase(ItemRow* k) void CatIndex::erase(ItemRow *k)
{ {
mRoot = erase(mRoot, k); mRoot = erase(mRoot, k);
if (mRoot != nullptr) if (mRoot != nullptr)
mRoot->mRed = false; mRoot->mRed = false;
} }
CatIndex::entry* CatIndex::erase(entry* h, ItemRow* k) CatIndex::entry *CatIndex::erase(entry *h, ItemRow *k)
{ {
if (mComp(k, h->mRow) < 0) if (mComp(k, h->mRow) < 0)
{ {
...@@ -990,88 +1077,88 @@ void CatIndex::reconstruct() ...@@ -990,88 +1077,88 @@ void CatIndex::reconstruct()
delete mRoot; delete mRoot;
mRoot = nullptr; mRoot = nullptr;
for (auto r: mCat) for (auto r : mCat)
insert(r.mData); insert(r.mData);
// maybe reconstruction can be done quicker by using the following commented code. // 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 std::set the red/black flag correctly in that case. // however, I've not had the time to think of a way to std::set the red/black flag correctly in that case.
// std::vector<ItemRow*> rows; // std::vector<ItemRow*> rows;
// transform(mCat.begin(), mCat.end(), backInserter(rows), // transform(mCat.begin(), mCat.end(), backInserter(rows),
// [](Row r) -> ItemRow* { assert(r.mData); return r.mData; }); // [](Row r) -> ItemRow* { assert(r.mData); return r.mData; });
// //
// assert(std::find(rows.begin(), rows.end(), nullptr) == rows.end()); // assert(std::find(rows.begin(), rows.end(), nullptr) == rows.end());
// //
// // don't use sort here, it will run out of the stack of something. // // don't use sort here, it will run out of the stack of something.
// // quicksort is notorious for using excessive recursion. // // quicksort is notorious for using excessive recursion.
// // Besides, most of the time, the data is ordered already anyway. // // Besides, most of the time, the data is ordered already anyway.
// //
// stable_sort(rows.begin(), rows.end(), [this](ItemRow* a, ItemRow* b) -> bool { return this->mComp(a, b) < 0; }); // stable_sort(rows.begin(), rows.end(), [this](ItemRow* a, ItemRow* b) -> bool { return this->mComp(a, b) < 0; });
// //
// for (size_t i = 0; i < rows.size() - 1; ++i) // for (size_t i = 0; i < rows.size() - 1; ++i)
// assert(mComp(rows[i], rows[i + 1]) < 0); // assert(mComp(rows[i], rows[i + 1]) < 0);
// //
// deque<entry*> e; // deque<entry*> e;
// transform(rows.begin(), rows.end(), back_inserter(e), // transform(rows.begin(), rows.end(), back_inserter(e),
// [](ItemRow* r) -> entry* { return new entry(r); }); // [](ItemRow* r) -> entry* { return new entry(r); });
// //
// while (e.size() > 1) // while (e.size() > 1)
// { // {
// deque<entry*> ne; // deque<entry*> ne;
// //
// while (not e.empty()) // while (not e.empty())
// { // {
// entry* a = e.front(); // entry* a = e.front();
// e.pop_front(); // e.pop_front();
// //
// if (e.empty()) // if (e.empty())
// ne.push_back(a); // ne.push_back(a);
// else // else
// { // {
// entry* b = e.front(); // entry* b = e.front();
// b->mLeft = a; // b->mLeft = a;
// //
// assert(mComp(a->mRow, b->mRow) < 0); // assert(mComp(a->mRow, b->mRow) < 0);
// //
// e.pop_front(); // e.pop_front();
// //
// if (not e.empty()) // if (not e.empty())
// { // {
// entry* c = e.front(); // entry* c = e.front();
// e.pop_front(); // e.pop_front();
// //
// assert(mComp(b->mRow, c->mRow) < 0); // assert(mComp(b->mRow, c->mRow) < 0);
// //
// b->mRight = c; // b->mRight = c;
// } // }
// //
// ne.push_back(b); // ne.push_back(b);
// //
// if (not e.empty()) // if (not e.empty())
// { // {
// ne.push_back(e.front()); // ne.push_back(e.front());
// e.pop_front(); // e.pop_front();
// } // }
// } // }
// } // }
// //
// swap (e, ne); // swap (e, ne);
// } // }
// //
// assert(e.size() == 1); // assert(e.size() == 1);
// mRoot = e.front(); // mRoot = e.front();
} }
size_t CatIndex::size() const size_t CatIndex::size() const
{ {
std::stack<entry*> s; std::stack<entry *> s;
s.push(mRoot); s.push(mRoot);
size_t result = 0; size_t result = 0;
while (not s.empty()) while (not s.empty())
{ {
entry* e = s.top(); entry *e = s.top();
s.pop(); s.pop();
if (e == nullptr) if (e == nullptr)
...@@ -1153,36 +1240,36 @@ size_t CatIndex::size() const ...@@ -1153,36 +1240,36 @@ size_t CatIndex::size() const
// -------------------------------------------------------------------- // --------------------------------------------------------------------
RowSet::RowSet(Category& cat) RowSet::RowSet(Category &cat)
: mCat(&cat) : mCat(&cat)
// , mCond(nullptr) // , mCond(nullptr)
{ {
} }
RowSet::RowSet(Category& cat, Condition&& cond) RowSet::RowSet(Category &cat, Condition &&cond)
: mCat(&cat) : mCat(&cat)
// , mCond(new Condition(std::forward<Condition>(cond))) // , mCond(new Condition(std::forward<Condition>(cond)))
{ {
cond.prepare(cat); cond.prepare(cat);
for (auto r: cat) for (auto r : cat)
{ {
if (cond(cat, r)) if (cond(cat, r))
mItems.push_back(r.mData); mItems.push_back(r.mData);
} }
} }
RowSet::RowSet(const RowSet& rhs) RowSet::RowSet(const RowSet &rhs)
: mCat(rhs.mCat) : mCat(rhs.mCat)
, mItems(rhs.mItems) , mItems(rhs.mItems)
// , mCond(nullptr) // , mCond(nullptr)
{ {
} }
RowSet::RowSet(RowSet&& rhs) RowSet::RowSet(RowSet &&rhs)
: mCat(rhs.mCat) : mCat(rhs.mCat)
, mItems(std::move(rhs.mItems)) , mItems(std::move(rhs.mItems))
// , mCond(rhs.mCond) // , mCond(rhs.mCond)
{ {
// rhs.mCond = nullptr; // rhs.mCond = nullptr;
} }
...@@ -1192,7 +1279,7 @@ RowSet::~RowSet() ...@@ -1192,7 +1279,7 @@ RowSet::~RowSet()
// delete mCond; // delete mCond;
} }
RowSet& RowSet::operator=(const RowSet& rhs) RowSet &RowSet::operator=(const RowSet &rhs)
{ {
if (this != &rhs) if (this != &rhs)
{ {
...@@ -1203,7 +1290,7 @@ RowSet& RowSet::operator=(const RowSet& rhs) ...@@ -1203,7 +1290,7 @@ RowSet& RowSet::operator=(const RowSet& rhs)
return *this; return *this;
} }
RowSet& RowSet::operator=(RowSet&& rhs) RowSet &RowSet::operator=(RowSet &&rhs)
{ {
if (this != &rhs) if (this != &rhs)
{ {
...@@ -1214,7 +1301,7 @@ RowSet& RowSet::operator=(RowSet&& rhs) ...@@ -1214,7 +1301,7 @@ RowSet& RowSet::operator=(RowSet&& rhs)
return *this; return *this;
} }
RowSet& RowSet::orderBy(std::initializer_list<std::string> items) RowSet &RowSet::orderBy(std::initializer_list<std::string> items)
{ {
RowComparator c(mCat, items.begin(), items.end()); RowComparator c(mCat, items.begin(), items.end());
...@@ -1225,9 +1312,13 @@ RowSet& RowSet::orderBy(std::initializer_list<std::string> items) ...@@ -1225,9 +1312,13 @@ RowSet& RowSet::orderBy(std::initializer_list<std::string> items)
// -------------------------------------------------------------------- // --------------------------------------------------------------------
Category::Category(Datablock& db, const std::string& name, Validator* Validator) Category::Category(Datablock &db, const std::string &name, Validator *Validator)
: mDb(db), mName(name), mValidator(Validator) : mDb(db)
, mHead(nullptr), mTail(nullptr), mIndex(nullptr) , mName(name)
, mValidator(Validator)
, mHead(nullptr)
, mTail(nullptr)
, mIndex(nullptr)
{ {
if (mName.empty()) if (mName.empty())
throw ValidationError("invalid empty name for Category"); throw ValidationError("invalid empty name for Category");
...@@ -1239,10 +1330,10 @@ Category::Category(Datablock& db, const std::string& name, Validator* Validator) ...@@ -1239,10 +1330,10 @@ Category::Category(Datablock& db, const std::string& name, Validator* Validator)
{ {
// make sure all required columns are added // make sure all required columns are added
for (auto& k: mCatValidator->mKeys) for (auto &k : mCatValidator->mKeys)
addColumn(k); addColumn(k);
for (auto& k: mCatValidator->mMandatoryFields) for (auto &k : mCatValidator->mMandatoryFields)
addColumn(k); addColumn(k);
mIndex = new CatIndex(this); mIndex = new CatIndex(this);
...@@ -1256,7 +1347,7 @@ Category::~Category() ...@@ -1256,7 +1347,7 @@ Category::~Category()
delete mIndex; delete mIndex;
} }
void Category::setValidator(Validator* v) void Category::setValidator(Validator *v)
{ {
mValidator = v; mValidator = v;
...@@ -1273,22 +1364,22 @@ void Category::setValidator(Validator* v) ...@@ -1273,22 +1364,22 @@ void Category::setValidator(Validator* v)
{ {
mIndex = new CatIndex(this); mIndex = new CatIndex(this);
mIndex->reconstruct(); mIndex->reconstruct();
//#if DEBUG //#if DEBUG
// assert(mIndex->size() == size()); // assert(mIndex->size() == size());
// mIndex->validate(); // mIndex->validate();
//#endif //#endif
} }
} }
else else
mCatValidator = nullptr; mCatValidator = nullptr;
} }
bool Category::hasColumn(const std::string& name) const bool Category::hasColumn(const std::string &name) const
{ {
return getColumnIndex(name) < mColumns.size(); return getColumnIndex(name) < mColumns.size();
} }
size_t Category::getColumnIndex(const std::string& name) const size_t Category::getColumnIndex(const std::string &name) const
{ {
size_t result; size_t result;
...@@ -1308,7 +1399,7 @@ size_t Category::getColumnIndex(const std::string& name) const ...@@ -1308,7 +1399,7 @@ size_t Category::getColumnIndex(const std::string& name) const
return result; return result;
} }
const std::string& Category::getColumnName(size_t columnIx) const const std::string &Category::getColumnName(size_t columnIx) const
{ {
return mColumns.at(columnIx).mName; return mColumns.at(columnIx).mName;
} }
...@@ -1316,18 +1407,18 @@ const std::string& Category::getColumnName(size_t columnIx) const ...@@ -1316,18 +1407,18 @@ const std::string& Category::getColumnName(size_t columnIx) const
std::vector<std::string> Category::getColumnNames() const std::vector<std::string> Category::getColumnNames() const
{ {
std::vector<std::string> result; std::vector<std::string> result;
for (auto& c: mColumns) for (auto &c : mColumns)
result.push_back(c.mName); result.push_back(c.mName);
return result; return result;
} }
size_t Category::addColumn(const std::string& name) size_t Category::addColumn(const std::string &name)
{ {
size_t result = getColumnIndex(name); size_t result = getColumnIndex(name);
if (result == mColumns.size()) if (result == mColumns.size())
{ {
const ValidateItem* itemValidator = nullptr; const ValidateItem *itemValidator = nullptr;
if (mCatValidator != nullptr) if (mCatValidator != nullptr)
{ {
...@@ -1363,17 +1454,17 @@ void Category::reorderByIndex() ...@@ -1363,17 +1454,17 @@ void Category::reorderByIndex()
std::tie(mHead, mTail) = mIndex->reorder(); std::tie(mHead, mTail) = mIndex->reorder();
} }
void Category::sort(std::function<int(const Row&, const Row&)> comparator) void Category::sort(std::function<int(const Row &, const Row &)> comparator)
{ {
if (mHead == nullptr) if (mHead == nullptr)
return; return;
std::vector<ItemRow*> rows; std::vector<ItemRow *> rows;
for (auto itemRow = mHead; itemRow != nullptr; itemRow = itemRow->mNext) for (auto itemRow = mHead; itemRow != nullptr; itemRow = itemRow->mNext)
rows.push_back(itemRow); rows.push_back(itemRow);
std::stable_sort(rows.begin(), rows.end(), std::stable_sort(rows.begin(), rows.end(),
[&comparator](ItemRow* ia, ItemRow* ib) [&comparator](ItemRow *ia, ItemRow *ib)
{ {
Row ra(ia); Row ra(ia);
Row rb(ib); Row rb(ib);
...@@ -1428,11 +1519,12 @@ bool Category::empty() const ...@@ -1428,11 +1519,12 @@ bool Category::empty() const
return mHead == nullptr or mHead->mValues == nullptr; return mHead == nullptr or mHead->mValues == nullptr;
} }
void Category::drop(const std::string& field) void Category::drop(const std::string &field)
{ {
using namespace std::placeholders; using namespace std::placeholders;
auto ci = find_if(mColumns.begin(), mColumns.end(), auto ci = find_if(mColumns.begin(), mColumns.end(),
[field](ItemColumn& c) -> bool { return iequals(c.mName, field); }); [field](ItemColumn &c) -> bool
{ return iequals(c.mName, field); });
if (ci != mColumns.end()) if (ci != mColumns.end())
{ {
...@@ -1445,13 +1537,13 @@ void Category::drop(const std::string& field) ...@@ -1445,13 +1537,13 @@ void Category::drop(const std::string& field)
} }
} }
Row Category::operator[](Condition&& cond) Row Category::operator[](Condition &&cond)
{ {
Row result; Row result;
cond.prepare(*this); cond.prepare(*this);
for (auto r: *this) for (auto r : *this)
{ {
if (cond(*this, r)) if (cond(*this, r))
{ {
...@@ -1478,13 +1570,13 @@ Row Category::operator[](Condition&& cond) ...@@ -1478,13 +1570,13 @@ Row Category::operator[](Condition&& cond)
// return result; // return result;
// } // }
bool Category::exists(Condition&& cond) const bool Category::exists(Condition &&cond) const
{ {
bool result = false; bool result = false;
cond.prepare(*this); cond.prepare(*this);
for (auto r: *this) for (auto r : *this)
{ {
if (cond(*this, r)) if (cond(*this, r))
{ {
...@@ -1516,8 +1608,8 @@ void Category::clear() ...@@ -1516,8 +1608,8 @@ void Category::clear()
} }
} }
template<class Iter> template <class Iter>
std::tuple<Row,bool> Category::emplace(Iter b, Iter e) std::tuple<Row, bool> Category::emplace(Iter b, Iter e)
{ {
// First, make sure all mandatory fields are supplied // First, make sure all mandatory fields are supplied
Row result; Row result;
...@@ -1525,7 +1617,7 @@ std::tuple<Row,bool> Category::emplace(Iter b, Iter e) ...@@ -1525,7 +1617,7 @@ std::tuple<Row,bool> Category::emplace(Iter b, Iter e)
if (mCatValidator != nullptr and b != e) if (mCatValidator != nullptr and b != e)
{ {
for (auto& col: mColumns) for (auto &col : mColumns)
{ {
auto iv = mCatValidator->getValidatorForItem(col.mName); auto iv = mCatValidator->getValidatorForItem(col.mName);
...@@ -1601,15 +1693,15 @@ std::tuple<Row,bool> Category::emplace(Iter b, Iter e) ...@@ -1601,15 +1693,15 @@ std::tuple<Row,bool> Category::emplace(Iter b, Iter e)
mIndex->insert(nr); mIndex->insert(nr);
} }
return { result, isNew }; return {result, isNew};
} }
std::tuple<Row,bool> Category::emplace(Row r) std::tuple<Row, bool> Category::emplace(Row r)
{ {
return emplace(r.begin(), r.end()); return emplace(r.begin(), r.end());
} }
size_t Category::erase(Condition&& cond) size_t Category::erase(Condition &&cond)
{ {
size_t result = 0; size_t result = 0;
...@@ -1630,7 +1722,7 @@ size_t Category::erase(Condition&& cond) ...@@ -1630,7 +1722,7 @@ size_t Category::erase(Condition&& cond)
return result; return result;
} }
size_t Category::erase(Condition&& cond, std::function<void(const Row&)>&& verbose) size_t Category::erase(Condition &&cond, std::function<void(const Row &)> &&verbose)
{ {
size_t result = 0; size_t result = 0;
...@@ -1652,13 +1744,13 @@ size_t Category::erase(Condition&& cond, std::function<void(const Row&)>&& verbo ...@@ -1652,13 +1744,13 @@ size_t Category::erase(Condition&& cond, std::function<void(const Row&)>&& verbo
return result; return result;
} }
void Category::eraseOrphans(Condition&& cond) void Category::eraseOrphans(Condition &&cond)
{ {
std::vector<ItemRow*> remove; std::vector<ItemRow *> remove;
cond.prepare(*this); cond.prepare(*this);
for (auto r: *this) for (auto r : *this)
{ {
if (cond(*this, r) and isOrphan(r)) if (cond(*this, r) and isOrphan(r))
{ {
...@@ -1671,7 +1763,7 @@ void Category::eraseOrphans(Condition&& cond) ...@@ -1671,7 +1763,7 @@ void Category::eraseOrphans(Condition&& cond)
} }
} }
for (auto r: remove) for (auto r : remove)
erase(iterator(r)); erase(iterator(r));
} }
...@@ -1723,7 +1815,7 @@ auto Category::erase(iterator pos) -> iterator ...@@ -1723,7 +1815,7 @@ auto Category::erase(iterator pos) -> iterator
if (mValidator != nullptr) if (mValidator != nullptr)
{ {
for (auto& link: mValidator->getLinksForParent(mName)) for (auto &link : mValidator->getLinksForParent(mName))
{ {
auto childCat = mDb.get(link->mChildCategory); auto childCat = mDb.get(link->mChildCategory);
if (childCat == nullptr) if (childCat == nullptr)
...@@ -1733,7 +1825,7 @@ auto Category::erase(iterator pos) -> iterator ...@@ -1733,7 +1825,7 @@ auto Category::erase(iterator pos) -> iterator
for (size_t ix = 0; ix < link->mParentKeys.size(); ++ix) for (size_t ix = 0; ix < link->mParentKeys.size(); ++ix)
{ {
const char* value = r[link->mParentKeys[ix]].c_str(); const char *value = r[link->mParentKeys[ix]].c_str();
cond = std::move(cond) && (Key(link->mChildKeys[ix]) == value); cond = std::move(cond) && (Key(link->mChildKeys[ix]) == value);
} }
...@@ -1780,15 +1872,15 @@ Row Category::copyRow(const Row &row) ...@@ -1780,15 +1872,15 @@ Row Category::copyRow(const Row &row)
} }
} }
auto &&[ result, inserted ] = emplace(items.begin(), items.end()); auto &&[result, inserted] = emplace(items.begin(), items.end());
// assert(inserted); // assert(inserted);
return result; return result;
} }
void Category::getTagOrder(std::vector<std::string>& tags) const void Category::getTagOrder(std::vector<std::string> &tags) const
{ {
for (auto& c: mColumns) for (auto &c : mColumns)
tags.push_back("_" + mName + "." + c.mName); tags.push_back("_" + mName + "." + c.mName);
} }
...@@ -1822,7 +1914,7 @@ Category::const_iterator Category::end() const ...@@ -1822,7 +1914,7 @@ Category::const_iterator Category::end() const
return const_iterator(); return const_iterator();
} }
bool Category::hasParent(Row r, const Category& parentCat, const ValidateLink& link) const bool Category::hasParent(Row r, const Category &parentCat, const ValidateLink &link) const
{ {
assert(mValidator != nullptr); assert(mValidator != nullptr);
assert(mCatValidator != nullptr); assert(mCatValidator != nullptr);
...@@ -1832,7 +1924,7 @@ bool Category::hasParent(Row r, const Category& parentCat, const ValidateLink& l ...@@ -1832,7 +1924,7 @@ bool Category::hasParent(Row r, const Category& parentCat, const ValidateLink& l
Condition cond; Condition cond;
for (size_t ix = 0; ix < link.mChildKeys.size(); ++ix) for (size_t ix = 0; ix < link.mChildKeys.size(); ++ix)
{ {
auto& name = link.mChildKeys[ix]; auto &name = link.mChildKeys[ix];
auto field = r[name]; auto field = r[name];
if (field.empty()) if (field.empty())
{ {
...@@ -1841,7 +1933,7 @@ bool Category::hasParent(Row r, const Category& parentCat, const ValidateLink& l ...@@ -1841,7 +1933,7 @@ bool Category::hasParent(Row r, const Category& parentCat, const ValidateLink& l
} }
else else
{ {
const char* value = field.c_str(); const char *value = field.c_str();
cond = std::move(cond) and (Key(link.mParentKeys[ix]) == value); cond = std::move(cond) and (Key(link.mParentKeys[ix]) == value);
} }
} }
...@@ -1866,7 +1958,7 @@ bool Category::isOrphan(Row r) ...@@ -1866,7 +1958,7 @@ bool Category::isOrphan(Row r)
return false; return false;
bool isOrphan = true; bool isOrphan = true;
for (auto& link: mValidator->getLinksForChild(mName)) for (auto &link : mValidator->getLinksForChild(mName))
{ {
auto parentCat = mDb.get(link->mParentCategory); auto parentCat = mDb.get(link->mParentCategory);
if (parentCat == nullptr) if (parentCat == nullptr)
...@@ -1875,7 +1967,7 @@ bool Category::isOrphan(Row r) ...@@ -1875,7 +1967,7 @@ bool Category::isOrphan(Row r)
Condition cond; Condition cond;
for (size_t ix = 0; ix < link->mChildKeys.size(); ++ix) for (size_t ix = 0; ix < link->mChildKeys.size(); ++ix)
{ {
const char* value = r[link->mChildKeys[ix]].c_str(); const char *value = r[link->mChildKeys[ix]].c_str();
cond = std::move(cond) && (Key(link->mParentKeys[ix]) == value); cond = std::move(cond) && (Key(link->mParentKeys[ix]) == value);
} }
...@@ -1902,7 +1994,7 @@ bool Category::hasChildren(Row r) const ...@@ -1902,7 +1994,7 @@ bool Category::hasChildren(Row r) const
bool result = false; bool result = false;
for (auto& link: mValidator->getLinksForParent(mName)) for (auto &link : mValidator->getLinksForParent(mName))
{ {
auto childCat = mDb.get(link->mChildCategory); auto childCat = mDb.get(link->mChildCategory);
if (childCat == nullptr) if (childCat == nullptr)
...@@ -1912,7 +2004,7 @@ bool Category::hasChildren(Row r) const ...@@ -1912,7 +2004,7 @@ bool Category::hasChildren(Row r) const
for (size_t ix = 0; ix < link->mParentKeys.size(); ++ix) for (size_t ix = 0; ix < link->mParentKeys.size(); ++ix)
{ {
const char* value = r[link->mParentKeys[ix]].c_str(); const char *value = r[link->mParentKeys[ix]].c_str();
cond = std::move(cond) && (Key(link->mChildKeys[ix]) == value); cond = std::move(cond) && (Key(link->mChildKeys[ix]) == value);
} }
...@@ -1933,7 +2025,7 @@ bool Category::hasParents(Row r) const ...@@ -1933,7 +2025,7 @@ bool Category::hasParents(Row r) const
bool result = false; bool result = false;
for (auto& link: mValidator->getLinksForChild(mName)) for (auto &link : mValidator->getLinksForChild(mName))
{ {
auto parentCat = mDb.get(link->mParentCategory); auto parentCat = mDb.get(link->mParentCategory);
if (parentCat == nullptr) if (parentCat == nullptr)
...@@ -1943,7 +2035,7 @@ bool Category::hasParents(Row r) const ...@@ -1943,7 +2035,7 @@ bool Category::hasParents(Row r) const
for (size_t ix = 0; ix < link->mChildKeys.size(); ++ix) for (size_t ix = 0; ix < link->mChildKeys.size(); ++ix)
{ {
const char* value = r[link->mChildKeys[ix]].c_str(); const char *value = r[link->mChildKeys[ix]].c_str();
cond = std::move(cond) && (Key(link->mParentKeys[ix]) == value); cond = std::move(cond) && (Key(link->mParentKeys[ix]) == value);
} }
...@@ -1957,19 +2049,19 @@ bool Category::hasParents(Row r) const ...@@ -1957,19 +2049,19 @@ bool Category::hasParents(Row r) const
return result; return result;
} }
RowSet Category::getChildren(Row r, const char* childCat) RowSet Category::getChildren(Row r, const char *childCat)
{ {
return getChildren(r, mDb[childCat]); return getChildren(r, mDb[childCat]);
} }
RowSet Category::getChildren(Row r, Category& childCat) RowSet Category::getChildren(Row r, Category &childCat)
{ {
assert(mValidator != nullptr); assert(mValidator != nullptr);
assert(mCatValidator != nullptr); assert(mCatValidator != nullptr);
RowSet result(childCat); RowSet result(childCat);
for (auto& link: mValidator->getLinksForParent(mName)) for (auto &link : mValidator->getLinksForParent(mName))
{ {
if (link->mChildCategory != childCat.mName) if (link->mChildCategory != childCat.mName)
continue; continue;
...@@ -1978,7 +2070,7 @@ RowSet Category::getChildren(Row r, Category& childCat) ...@@ -1978,7 +2070,7 @@ RowSet Category::getChildren(Row r, Category& childCat)
for (size_t ix = 0; ix < link->mParentKeys.size(); ++ix) for (size_t ix = 0; ix < link->mParentKeys.size(); ++ix)
{ {
const char* value = r[link->mParentKeys[ix]].c_str(); const char *value = r[link->mParentKeys[ix]].c_str();
cond = std::move(cond) && (Key(link->mChildKeys[ix]) == value); cond = std::move(cond) && (Key(link->mChildKeys[ix]) == value);
} }
...@@ -1993,19 +2085,19 @@ RowSet Category::getChildren(Row r, Category& childCat) ...@@ -1993,19 +2085,19 @@ RowSet Category::getChildren(Row r, Category& childCat)
return result; return result;
} }
RowSet Category::getParents(Row r, const char* parentCat) RowSet Category::getParents(Row r, const char *parentCat)
{ {
return getParents(r, mDb[parentCat]); return getParents(r, mDb[parentCat]);
} }
RowSet Category::getParents(Row r, Category& parentCat) RowSet Category::getParents(Row r, Category &parentCat)
{ {
assert(mValidator != nullptr); assert(mValidator != nullptr);
assert(mCatValidator != nullptr); assert(mCatValidator != nullptr);
RowSet result(parentCat); RowSet result(parentCat);
for (auto& link: mValidator->getLinksForChild(mName)) for (auto &link : mValidator->getLinksForChild(mName))
{ {
if (link->mParentCategory != parentCat.mName) if (link->mParentCategory != parentCat.mName)
continue; continue;
...@@ -2014,7 +2106,7 @@ RowSet Category::getParents(Row r, Category& parentCat) ...@@ -2014,7 +2106,7 @@ RowSet Category::getParents(Row r, Category& parentCat)
for (size_t ix = 0; ix < link->mChildKeys.size(); ++ix) for (size_t ix = 0; ix < link->mChildKeys.size(); ++ix)
{ {
const char* value = r[link->mChildKeys[ix]].c_str(); const char *value = r[link->mChildKeys[ix]].c_str();
cond = std::move(cond) && (Key(link->mParentKeys[ix]) == value); cond = std::move(cond) && (Key(link->mParentKeys[ix]) == value);
} }
...@@ -2029,12 +2121,12 @@ RowSet Category::getParents(Row r, Category& parentCat) ...@@ -2029,12 +2121,12 @@ RowSet Category::getParents(Row r, Category& parentCat)
return result; return result;
} }
RowSet Category::getLinked(Row r, const char* cat) RowSet Category::getLinked(Row r, const char *cat)
{ {
return getLinked(r, mDb[cat]); return getLinked(r, mDb[cat]);
} }
RowSet Category::getLinked(Row r, Category& cat) RowSet Category::getLinked(Row r, Category &cat)
{ {
RowSet result = getChildren(r, cat); RowSet result = getChildren(r, cat);
if (result.empty()) if (result.empty())
...@@ -2064,7 +2156,7 @@ bool Category::isValid() ...@@ -2064,7 +2156,7 @@ bool Category::isValid()
auto mandatory = mCatValidator->mMandatoryFields; auto mandatory = mCatValidator->mMandatoryFields;
for (auto& col: mColumns) for (auto &col : mColumns)
{ {
auto iv = mCatValidator->getValidatorForItem(col.mName); auto iv = mCatValidator->getValidatorForItem(col.mName);
if (iv == nullptr) if (iv == nullptr)
...@@ -2084,18 +2176,18 @@ bool Category::isValid() ...@@ -2084,18 +2176,18 @@ bool Category::isValid()
result = false; result = false;
} }
//#if not defined(NDEBUG) //#if not defined(NDEBUG)
// // check index? // // check index?
// if (mIndex) // if (mIndex)
// { // {
// mIndex->validate(); // mIndex->validate();
// for (auto r: *this) // for (auto r: *this)
// { // {
// if (mIndex->find(r.mData) != r.mData) // if (mIndex->find(r.mData) != r.mData)
// mValidator->reportError("Key not found in index for Category " + mName); // mValidator->reportError("Key not found in index for Category " + mName);
// } // }
// } // }
//#endif //#endif
// validate all values // validate all values
mandatory = mCatValidator->mMandatoryFields; mandatory = mCatValidator->mMandatoryFields;
...@@ -2123,7 +2215,7 @@ bool Category::isValid() ...@@ -2123,7 +2215,7 @@ bool Category::isValid()
{ {
(*iv)(vi->mText); (*iv)(vi->mText);
} }
catch(const std::exception& e) catch (const std::exception &e)
{ {
mValidator->reportError("Error validating " + mColumns[cix].mName + ": " + e.what(), false); mValidator->reportError("Error validating " + mColumns[cix].mName + ": " + e.what(), false);
continue; continue;
...@@ -2147,16 +2239,16 @@ bool Category::isValid() ...@@ -2147,16 +2239,16 @@ bool Category::isValid()
void Category::validateLinks() const void Category::validateLinks() const
{ {
auto& validator = getValidator(); auto &validator = getValidator();
for (auto linkValidator: validator.getLinksForChild(mName)) for (auto linkValidator : validator.getLinksForChild(mName))
{ {
auto parent = mDb.get(linkValidator->mParentCategory); auto parent = mDb.get(linkValidator->mParentCategory);
if (parent == nullptr) if (parent == nullptr)
continue; continue;
size_t missing = 0; size_t missing = 0;
for (auto r: *this) for (auto r : *this)
if (not hasParent(r, *parent, *linkValidator)) if (not hasParent(r, *parent, *linkValidator))
++missing; ++missing;
...@@ -2168,7 +2260,7 @@ void Category::validateLinks() const ...@@ -2168,7 +2260,7 @@ void Category::validateLinks() const
} }
} }
const Validator& Category::getValidator() const const Validator &Category::getValidator() const
{ {
if (mValidator == nullptr) if (mValidator == nullptr)
throw std::runtime_error("no Validator defined yet"); throw std::runtime_error("no Validator defined yet");
...@@ -2184,7 +2276,7 @@ iset Category::fields() const ...@@ -2184,7 +2276,7 @@ iset Category::fields() const
mValidator->reportError("undefined Category", true); mValidator->reportError("undefined Category", true);
iset result; iset result;
for (auto& iv: mCatValidator->mItemValidators) for (auto &iv : mCatValidator->mItemValidators)
result.insert(iv.mTag); result.insert(iv.mTag);
return result; return result;
} }
...@@ -2208,7 +2300,7 @@ iset Category::keyFields() const ...@@ -2208,7 +2300,7 @@ iset Category::keyFields() const
if (mCatValidator == nullptr) if (mCatValidator == nullptr)
mValidator->reportError("undefined Category", true); mValidator->reportError("undefined Category", true);
return iset{ mCatValidator->mKeys.begin(), mCatValidator->mKeys.end() }; return iset{mCatValidator->mKeys.begin(), mCatValidator->mKeys.end()};
} }
std::set<size_t> Category::keyFieldsByIndex() const std::set<size_t> Category::keyFieldsByIndex() const
...@@ -2220,12 +2312,105 @@ std::set<size_t> Category::keyFieldsByIndex() const ...@@ -2220,12 +2312,105 @@ std::set<size_t> Category::keyFieldsByIndex() const
mValidator->reportError("undefined Category", true); mValidator->reportError("undefined Category", true);
std::set<size_t> result; std::set<size_t> result;
for (auto& k: mCatValidator->mKeys) for (auto &k : mCatValidator->mKeys)
result.insert(getColumnIndex(k)); result.insert(getColumnIndex(k));
return result; return result;
} }
bool operator==(const Category &a, const Category &b)
{
using namespace std::placeholders;
// set<std::string> tagsA(a.fields()), tagsB(b.fields());
//
// if (tagsA != tagsB)
// std::cout << "Unequal number of fields" << std::endl;
auto& validator = a.getValidator();
auto catValidator = validator.getValidatorForCategory(a.name());
if (catValidator == nullptr)
throw std::runtime_error("missing cat validator");
typedef std::function<int(const char*,const char*)> compType;
std::vector<std::tuple<std::string,compType>> tags;
auto keys = catValidator->mKeys;
std::vector<size_t> keyIx;
for (auto& tag: a.fields())
{
auto iv = catValidator->getValidatorForItem(tag);
if (iv == nullptr)
throw std::runtime_error("missing item validator");
auto tv = iv->mType;
if (tv == nullptr)
throw std::runtime_error("missing type validator");
tags.push_back(std::make_tuple(tag, std::bind(&cif::ValidateType::compare, tv, std::placeholders::_1, std::placeholders::_2)));
auto pred = [tag](const std::string& s) -> bool { return cif::iequals(tag, s) == 0; };
if (find_if(keys.begin(), keys.end(), pred) == keys.end())
keyIx.push_back(tags.size() - 1);
}
// a.reorderByIndex();
// b.reorderByIndex();
auto rowEqual = [&](const cif::Row& a, const cif::Row& b)
{
int d = 0;
for (auto kix: keyIx)
{
std::string tag;
compType compare;
std::tie(tag, compare) = tags[kix];
d = compare(a[tag].c_str(), b[tag].c_str());
if (d != 0)
break;
}
return d == 0;
};
auto ai = a.begin(), bi = b.begin();
while (ai != a.end() or bi != b.end())
{
if (ai == a.end() or bi == b.end())
return false;
cif::Row ra = *ai, rb = *bi;
if (not rowEqual(ra, rb))
return false;
std::vector<std::string> missingA, missingB, different;
for (auto& tt: tags)
{
std::string tag;
compType compare;
std::tie(tag, compare) = tt;
// make it an option to compare unapplicable to empty or something
const char* ta = ra[tag].c_str(); if (strcmp(ta, ".") == 0) ta = "";
const char* tb = rb[tag].c_str(); if (strcmp(tb, ".") == 0) tb = "";
if (compare(ta, tb) != 0)
return false;
}
++ai;
++bi;
}
return true;
}
// auto Category::iterator::operator++() -> iterator& // auto Category::iterator::operator++() -> iterator&
// { // {
// mCurrent = Row(mCurrent.data()->mNext); // mCurrent = Row(mCurrent.data()->mNext);
...@@ -2241,8 +2426,8 @@ std::set<size_t> Category::keyFieldsByIndex() const ...@@ -2241,8 +2426,8 @@ std::set<size_t> Category::keyFieldsByIndex() const
namespace detail namespace detail
{ {
size_t writeValue(std::ostream& os, std::string value, size_t offset, size_t width) size_t writeValue(std::ostream &os, std::string value, size_t offset, size_t width)
{ {
if (value.find('\n') != std::string::npos or width == 0 or value.length() >= 132) // write as text field if (value.find('\n') != std::string::npos or width == 0 or value.length() >= 132) // write as text field
{ {
ba::replace_all(value, "\n;", "\n\\;"); ba::replace_all(value, "\n;", "\n\\;");
...@@ -2273,7 +2458,7 @@ size_t writeValue(std::ostream& os, std::string value, size_t offset, size_t wid ...@@ -2273,7 +2458,7 @@ size_t writeValue(std::ostream& os, std::string value, size_t offset, size_t wid
else else
{ {
bool done = false; bool done = false;
for (char q: { '\'', '"'}) for (char q : {'\'', '"'})
{ {
auto p = value.find(q); // see if we can use the quote character auto p = value.find(q); // see if we can use the quote character
while (p != std::string::npos and isNonBlank(value[p + 1]) and value[p + 1] != q) while (p != std::string::npos and isNonBlank(value[p + 1]) and value[p + 1] != q)
...@@ -2310,11 +2495,11 @@ size_t writeValue(std::ostream& os, std::string value, size_t offset, size_t wid ...@@ -2310,11 +2495,11 @@ size_t writeValue(std::ostream& os, std::string value, size_t offset, size_t wid
} }
return offset; return offset;
} }
} } // namespace detail
void Category::write(std::ostream& os, const std::vector<size_t>& order, bool includeEmptyColumns) void Category::write(std::ostream &os, const std::vector<size_t> &order, bool includeEmptyColumns)
{ {
if (empty()) if (empty())
return; return;
...@@ -2328,9 +2513,9 @@ void Category::write(std::ostream& os, const std::vector<size_t>& order, bool in ...@@ -2328,9 +2513,9 @@ void Category::write(std::ostream& os, const std::vector<size_t>& order, bool in
std::vector<size_t> columnWidths; std::vector<size_t> columnWidths;
for (auto cix: order) for (auto cix : order)
{ {
auto& col = mColumns[cix]; auto &col = mColumns[cix];
os << '_' << mName << '.' << col.mName << ' ' << std::endl; os << '_' << mName << '.' << col.mName << ' ' << std::endl;
columnWidths.push_back(2); columnWidths.push_back(2);
} }
...@@ -2359,7 +2544,7 @@ void Category::write(std::ostream& os, const std::vector<size_t>& order, bool in ...@@ -2359,7 +2544,7 @@ void Category::write(std::ostream& os, const std::vector<size_t>& order, bool in
{ {
size_t offset = 0; size_t offset = 0;
for (size_t cix: order) for (size_t cix : order)
{ {
size_t w = columnWidths[cix]; size_t w = columnWidths[cix];
...@@ -2406,7 +2591,7 @@ void Category::write(std::ostream& os, const std::vector<size_t>& order, bool in ...@@ -2406,7 +2591,7 @@ void Category::write(std::ostream& os, const std::vector<size_t>& order, bool in
// first find the indent level // first find the indent level
size_t l = 0; size_t l = 0;
for (auto& col: mColumns) for (auto &col : mColumns)
{ {
std::string tag = '_' + mName + '.' + col.mName; std::string tag = '_' + mName + '.' + col.mName;
...@@ -2416,9 +2601,9 @@ void Category::write(std::ostream& os, const std::vector<size_t>& order, bool in ...@@ -2416,9 +2601,9 @@ void Category::write(std::ostream& os, const std::vector<size_t>& order, bool in
l += 3; l += 3;
for (size_t cix: order) for (size_t cix : order)
{ {
auto& col = mColumns[cix]; auto &col = mColumns[cix];
os << '_' << mName << '.' << col.mName << std::string(l - col.mName.length() - mName.length() - 2, ' '); os << '_' << mName << '.' << col.mName << std::string(l - col.mName.length() - mName.length() - 2, ' ');
...@@ -2450,23 +2635,23 @@ void Category::write(std::ostream& os, const std::vector<size_t>& order, bool in ...@@ -2450,23 +2635,23 @@ void Category::write(std::ostream& os, const std::vector<size_t>& order, bool in
os << "# " << std::endl; os << "# " << std::endl;
} }
void Category::write(std::ostream& os) void Category::write(std::ostream &os)
{ {
std::vector<size_t> order(mColumns.size()); std::vector<size_t> order(mColumns.size());
iota(order.begin(), order.end(), 0); iota(order.begin(), order.end(), 0);
write(os, order, false); write(os, order, false);
} }
void Category::write(std::ostream& os, const std::vector<std::string>& columns) void Category::write(std::ostream &os, const std::vector<std::string> &columns)
{ {
// make sure all columns are present // make sure all columns are present
for (auto& c: columns) for (auto &c : columns)
addColumn(c); addColumn(c);
std::vector<size_t> order; std::vector<size_t> order;
order.reserve(mColumns.size()); order.reserve(mColumns.size());
for (auto& c: columns) for (auto &c : columns)
order.push_back(getColumnIndex(c)); order.push_back(getColumnIndex(c));
for (size_t i = 0; i < mColumns.size(); ++i) for (size_t i = 0; i < mColumns.size(); ++i)
...@@ -2489,7 +2674,7 @@ void Category::update_value(RowSet &&rows, const std::string &tag, const std::st ...@@ -2489,7 +2674,7 @@ void Category::update_value(RowSet &&rows, const std::string &tag, const std::st
if (colIx >= mColumns.size()) if (colIx >= mColumns.size())
throw std::runtime_error("Invalid column " + value + " for " + mName); throw std::runtime_error("Invalid column " + value + " for " + mName);
auto& col = mColumns[colIx]; auto &col = mColumns[colIx];
// check the value // check the value
if (col.mValidator) if (col.mValidator)
...@@ -2497,7 +2682,7 @@ void Category::update_value(RowSet &&rows, const std::string &tag, const std::st ...@@ -2497,7 +2682,7 @@ void Category::update_value(RowSet &&rows, const std::string &tag, const std::st
// first some sanity checks, what was the old value and is it the same for all rows? // first some sanity checks, what was the old value and is it the same for all rows?
std::string oldValue = rows.front()[tag].c_str(); std::string oldValue = rows.front()[tag].c_str();
for (auto &row: rows) for (auto &row : rows)
{ {
if (oldValue != row[tag].c_str()) if (oldValue != row[tag].c_str())
throw std::runtime_error("Inconsistent old values in update_value"); throw std::runtime_error("Inconsistent old values in update_value");
...@@ -2507,16 +2692,16 @@ void Category::update_value(RowSet &&rows, const std::string &tag, const std::st ...@@ -2507,16 +2692,16 @@ void Category::update_value(RowSet &&rows, const std::string &tag, const std::st
return; return;
// update rows, but do not cascade // update rows, but do not cascade
for (auto &row: rows) for (auto &row : rows)
row.assign(colIx, value, true); row.assign(colIx, value, true);
// 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& validator = getValidator(); auto &validator = getValidator();
auto& db = mDb; auto &db = mDb;
for (auto parent: rows) for (auto parent : rows)
{ {
for (auto linked: validator.getLinksForParent(mName)) for (auto linked : validator.getLinksForParent(mName))
{ {
auto childCat = db.get(linked->mChildCategory); auto childCat = db.get(linked->mChildCategory);
if (childCat == nullptr) if (childCat == nullptr)
...@@ -2544,7 +2729,7 @@ void Category::update_value(RowSet &&rows, const std::string &tag, const std::st ...@@ -2544,7 +2729,7 @@ void Category::update_value(RowSet &&rows, const std::string &tag, const std::st
cond = std::move(cond) && Key(ck) == parent[pk].c_str(); cond = std::move(cond) && Key(ck) == parent[pk].c_str();
} }
auto children = RowSet{ *childCat, std::move(cond) }; auto children = RowSet{*childCat, std::move(cond)};
if (children.empty()) if (children.empty())
continue; continue;
...@@ -2554,7 +2739,7 @@ void Category::update_value(RowSet &&rows, const std::string &tag, const std::st ...@@ -2554,7 +2739,7 @@ void Category::update_value(RowSet &&rows, const std::string &tag, const std::st
RowSet process(*childCat); RowSet process(*childCat);
for (auto child: children) for (auto child : children)
{ {
Condition cond_c; Condition cond_c;
...@@ -2619,13 +2804,13 @@ void Category::update_value(RowSet &&rows, const std::string &tag, const std::st ...@@ -2619,13 +2804,13 @@ void Category::update_value(RowSet &&rows, const std::string &tag, const std::st
// -------------------------------------------------------------------- // --------------------------------------------------------------------
Row::Row(const Row& rhs) Row::Row(const Row &rhs)
: mData(rhs.mData) : mData(rhs.mData)
, mCascade(rhs.mCascade) , mCascade(rhs.mCascade)
{ {
} }
Row::Row(Row&& rhs) Row::Row(Row &&rhs)
: mData(rhs.mData) : mData(rhs.mData)
, mCascade(rhs.mCascade) , mCascade(rhs.mCascade)
{ {
...@@ -2634,7 +2819,6 @@ Row::Row(Row&& rhs) ...@@ -2634,7 +2819,6 @@ Row::Row(Row&& rhs)
Row::~Row() Row::~Row()
{ {
} }
void Row::next() void Row::next()
...@@ -2643,30 +2827,31 @@ void Row::next() ...@@ -2643,30 +2827,31 @@ void Row::next()
mData = mData->mNext; mData = mData->mNext;
} }
Row& Row::operator=(Row&& rhs) Row &Row::operator=(Row &&rhs)
{ {
mData = rhs.mData; rhs.mData = nullptr; mData = rhs.mData;
rhs.mData = nullptr;
mCascade = rhs.mCascade; mCascade = rhs.mCascade;
return *this; return *this;
} }
Row& Row::operator=(const Row& rhs) Row &Row::operator=(const Row &rhs)
{ {
mData = rhs.mData; mData = rhs.mData;
mCascade = rhs.mCascade; mCascade = rhs.mCascade;
return *this; return *this;
} }
void Row::assign(const std::vector<Item>& values) void Row::assign(const std::vector<Item> &values)
{ {
auto cat = mData->mCategory; auto cat = mData->mCategory;
std::map<std::string,std::tuple<size_t,std::string,std::string>> changed; std::map<std::string, std::tuple<size_t, std::string, std::string>> changed;
for (auto& value: values) for (auto &value : values)
{ {
auto columnIx = cat->addColumn(value.name()); auto columnIx = cat->addColumn(value.name());
auto& col = cat->mColumns[columnIx]; auto &col = cat->mColumns[columnIx];
std::string tag = col.mValidator ? col.mValidator->mTag : std::to_string(columnIx); std::string tag = col.mValidator ? col.mValidator->mTag : std::to_string(columnIx);
changed[tag] = std::make_tuple(columnIx, operator[](columnIx).c_str(), value.value()); changed[tag] = std::make_tuple(columnIx, operator[](columnIx).c_str(), value.value());
...@@ -2678,10 +2863,10 @@ void Row::assign(const std::vector<Item>& values) ...@@ -2678,10 +2863,10 @@ void Row::assign(const std::vector<Item>& values)
// auto iv = col.mValidator; // auto iv = col.mValidator;
if (mCascade) if (mCascade)
{ {
auto& validator = cat->getValidator(); auto &validator = cat->getValidator();
auto& db = cat->db(); auto &db = cat->db();
for (auto linked: validator.getLinksForParent(cat->mName)) for (auto linked : validator.getLinksForParent(cat->mName))
{ {
auto childCat = db.get(linked->mChildCategory); auto childCat = db.get(linked->mChildCategory);
if (childCat == nullptr) if (childCat == nullptr)
...@@ -2708,46 +2893,46 @@ void Row::assign(const std::vector<Item>& values) ...@@ -2708,46 +2893,46 @@ void Row::assign(const std::vector<Item>& values)
} }
else else
{ {
const char* value = (*this)[pk].c_str(); const char *value = (*this)[pk].c_str();
cond = std::move(cond) && (Key(ck) == value); cond = std::move(cond) && (Key(ck) == value);
} }
} }
auto rows = childCat->find(std::move(cond)); auto rows = childCat->find(std::move(cond));
for (auto& cr: rows) for (auto &cr : rows)
cr.assign(newValues); cr.assign(newValues);
} }
} }
} }
void Row::assign(const Item& value, bool skipUpdateLinked) void Row::assign(const Item &value, bool skipUpdateLinked)
{ {
assign(value.name(), value.value(), skipUpdateLinked); assign(value.name(), value.value(), skipUpdateLinked);
} }
void Row::assign(const std::string& name, const std::string& value, bool skipUpdateLinked) void Row::assign(const std::string &name, const std::string &value, bool skipUpdateLinked)
{ {
try try
{ {
auto cat = mData->mCategory; auto cat = mData->mCategory;
assign(cat->addColumn(name), value, skipUpdateLinked); assign(cat->addColumn(name), value, skipUpdateLinked);
} }
catch (const std::exception& ex) catch (const std::exception &ex)
{ {
std::cerr << "Could not assign value '" << value << "' to column _" << mData->mCategory->name() << '.' << name << std::endl; std::cerr << "Could not assign value '" << value << "' to column _" << mData->mCategory->name() << '.' << name << std::endl;
throw; throw;
} }
} }
void Row::assign(size_t column, const std::string& value, bool skipUpdateLinked) void Row::assign(size_t column, const std::string &value, bool skipUpdateLinked)
{ {
if (mData == nullptr) if (mData == nullptr)
throw std::logic_error("invalid Row, no data assigning value '" + value + "' to column with index " + std::to_string(column)); throw std::logic_error("invalid Row, no data assigning value '" + value + "' to column with index " + std::to_string(column));
auto cat = mData->mCategory; auto cat = mData->mCategory;
auto& col = cat->mColumns[column]; auto &col = cat->mColumns[column];
const char* oldValue = nullptr; const char *oldValue = nullptr;
for (auto iv = mData->mValues; iv != nullptr; iv = iv->mNext) for (auto iv = mData->mValues; iv != nullptr; iv = iv->mNext)
{ {
assert(iv != iv->mNext and (iv->mNext == nullptr or iv != iv->mNext->mNext)); assert(iv != iv->mNext and (iv->mNext == nullptr or iv != iv->mNext->mNext));
...@@ -2810,7 +2995,7 @@ void Row::assign(size_t column, const std::string& value, bool skipUpdateLinked) ...@@ -2810,7 +2995,7 @@ void Row::assign(size_t column, const std::string& value, bool skipUpdateLinked)
if (not value.empty()) if (not value.empty())
{ {
auto nv = new(value.length()) ItemValue(value.c_str(), column); auto nv = new (value.length()) ItemValue(value.c_str(), column);
if (mData->mValues == nullptr) if (mData->mValues == nullptr)
mData->mValues = nv; mData->mValues = nv;
...@@ -2830,10 +3015,10 @@ void Row::assign(size_t column, const std::string& value, bool skipUpdateLinked) ...@@ -2830,10 +3015,10 @@ void Row::assign(size_t column, const std::string& value, bool skipUpdateLinked)
auto iv = col.mValidator; auto iv = col.mValidator;
if (not skipUpdateLinked and iv != nullptr and mCascade) if (not skipUpdateLinked and iv != nullptr and mCascade)
{ {
auto& validator = cat->getValidator(); auto &validator = cat->getValidator();
auto& db = cat->db(); auto &db = cat->db();
for (auto linked: validator.getLinksForParent(cat->mName)) for (auto linked : validator.getLinksForParent(cat->mName))
{ {
auto childCat = db.get(linked->mChildCategory); auto childCat = db.get(linked->mChildCategory);
if (childCat == nullptr) if (childCat == nullptr)
...@@ -2859,7 +3044,7 @@ void Row::assign(size_t column, const std::string& value, bool skipUpdateLinked) ...@@ -2859,7 +3044,7 @@ void Row::assign(size_t column, const std::string& value, bool skipUpdateLinked)
} }
else else
{ {
const char* pk_value = (*this)[pk].c_str(); const char *pk_value = (*this)[pk].c_str();
if (*pk_value == 0) if (*pk_value == 0)
cond = std::move(cond) && Key(ck) == Empty(); cond = std::move(cond) && Key(ck) == Empty();
else else
...@@ -2893,7 +3078,7 @@ void Row::assign(size_t column, const std::string& value, bool skipUpdateLinked) ...@@ -2893,7 +3078,7 @@ void Row::assign(size_t column, const std::string& value, bool skipUpdateLinked)
cond_n = std::move(cond_n) && Key(ck) == value; cond_n = std::move(cond_n) && Key(ck) == value;
else else
{ {
const char* pk_value = (*this)[pk].c_str(); const char *pk_value = (*this)[pk].c_str();
if (*pk_value == 0) if (*pk_value == 0)
cond_n = std::move(cond_n) && Key(ck) == Empty(); cond_n = std::move(cond_n) && Key(ck) == Empty();
else else
...@@ -2910,13 +3095,13 @@ void Row::assign(size_t column, const std::string& value, bool skipUpdateLinked) ...@@ -2910,13 +3095,13 @@ void Row::assign(size_t column, const std::string& value, bool skipUpdateLinked)
continue; continue;
} }
for (auto& cr: rows) for (auto &cr : rows)
cr.assign(childTag, value, false); cr.assign(childTag, value, false);
} }
} }
} }
void Row::swap(size_t cix, ItemRow* a, ItemRow* b) void Row::swap(size_t cix, ItemRow *a, ItemRow *b)
{ {
if (a == nullptr or b == nullptr) if (a == nullptr or b == nullptr)
throw std::logic_error("invalid Rows in swap"); throw std::logic_error("invalid Rows in swap");
...@@ -2939,10 +3124,10 @@ void Row::swap(size_t cix, ItemRow* a, ItemRow* b) ...@@ -2939,10 +3124,10 @@ void Row::swap(size_t cix, ItemRow* a, ItemRow* b)
cat->mIndex->erase(b); cat->mIndex->erase(b);
} }
ItemValue* ap = nullptr; // parent of ai ItemValue *ap = nullptr; // parent of ai
ItemValue* ai = nullptr; ItemValue *ai = nullptr;
ItemValue* bp = nullptr; // parent of bi ItemValue *bp = nullptr; // parent of bi
ItemValue* bi = nullptr; ItemValue *bi = nullptr;
if (a->mValues == nullptr) if (a->mValues == nullptr)
; ;
...@@ -2964,7 +3149,6 @@ void Row::swap(size_t cix, ItemRow* a, ItemRow* b) ...@@ -2964,7 +3149,6 @@ void Row::swap(size_t cix, ItemRow* a, ItemRow* b)
} }
} }
if (b->mValues == nullptr) if (b->mValues == nullptr)
; ;
else if (b->mValues->mColumnIndex == cix) else if (b->mValues->mColumnIndex == cix)
...@@ -3018,10 +3202,10 @@ void Row::swap(size_t cix, ItemRow* a, ItemRow* b) ...@@ -3018,10 +3202,10 @@ void Row::swap(size_t cix, ItemRow* a, ItemRow* b)
auto parentColName = cat->getColumnName(cix); auto parentColName = cat->getColumnName(cix);
// see if we need to update any child categories that depend on these values // see if we need to update any child categories that depend on these values
auto& validator = cat->getValidator(); auto &validator = cat->getValidator();
auto parentCatValidator = cat->getCatValidator(); auto parentCatValidator = cat->getCatValidator();
for (auto& link: validator.getLinksForParent(cat->mName)) for (auto &link : validator.getLinksForParent(cat->mName))
{ {
if (find(link->mParentKeys.begin(), link->mParentKeys.end(), parentColName) == link->mParentKeys.end()) if (find(link->mParentKeys.begin(), link->mParentKeys.end(), parentColName) == link->mParentKeys.end())
continue; continue;
...@@ -3057,13 +3241,13 @@ void Row::swap(size_t cix, ItemRow* a, ItemRow* b) ...@@ -3057,13 +3241,13 @@ void Row::swap(size_t cix, ItemRow* a, ItemRow* b)
if (pcix == cix) if (pcix == cix)
{ {
linkChildColName = childColName; linkChildColName = childColName;
if (not (i == nullptr or strcmp(i->mText, ".") == 0 or strcmp(i->mText, "?") == 0)) if (not(i == nullptr or strcmp(i->mText, ".") == 0 or strcmp(i->mText, "?") == 0))
childValue = i->mText; childValue = i->mText;
} }
else else
{ {
std::string ps = r->c_str(pcix); std::string ps = r->c_str(pcix);
if (not (ps.empty() or ps == "." or ps == "?")) if (not(ps.empty() or ps == "." or ps == "?"))
childValue = ps; childValue = ps;
} }
...@@ -3098,14 +3282,14 @@ void Row::swap(size_t cix, ItemRow* a, ItemRow* b) ...@@ -3098,14 +3282,14 @@ void Row::swap(size_t cix, ItemRow* a, ItemRow* b)
{ {
auto i = ab == 0 ? bi : ai; auto i = ab == 0 ? bi : ai;
for (auto r: rs[ab]) for (auto r : rs[ab])
{ {
// now due to the way links are defined, we might have found a row // now due to the way links are defined, we might have found a row
// that contains an empty value for all child columns... // that contains an empty value for all child columns...
// Now, that's not a real hit, is it? // Now, that's not a real hit, is it?
size_t n = 0; size_t n = 0;
for (auto c: link->mChildKeys) for (auto c : link->mChildKeys)
if (r[c].empty()) if (r[c].empty())
++n; ++n;
...@@ -3126,7 +3310,7 @@ void Row::swap(size_t cix, ItemRow* a, ItemRow* b) ...@@ -3126,7 +3310,7 @@ void Row::swap(size_t cix, ItemRow* a, ItemRow* b)
} }
} }
size_t Row::ColumnForItemTag(const char* itemTag) const size_t Row::ColumnForItemTag(const char *itemTag) const
{ {
size_t result = 0; size_t result = 0;
if (mData != nullptr) if (mData != nullptr)
...@@ -3163,14 +3347,15 @@ void Row::lineNr(uint32_t l) ...@@ -3163,14 +3347,15 @@ void Row::lineNr(uint32_t l)
mData->mLineNr = l; mData->mLineNr = l;
} }
Row::const_iterator::const_iterator(ItemRow* data, ItemValue* ptr) Row::const_iterator::const_iterator(ItemRow *data, ItemValue *ptr)
: mData(data), mPtr(ptr) : mData(data)
, mPtr(ptr)
{ {
if (mPtr != nullptr) if (mPtr != nullptr)
fetch(); fetch();
} }
Row::const_iterator& Row::const_iterator::operator++() Row::const_iterator &Row::const_iterator::operator++()
{ {
if (mPtr != nullptr) if (mPtr != nullptr)
mPtr = mPtr->mNext; mPtr = mPtr->mNext;
...@@ -3188,7 +3373,7 @@ void Row::const_iterator::fetch() ...@@ -3188,7 +3373,7 @@ void Row::const_iterator::fetch()
mPtr->mText); mPtr->mText);
} }
std::ostream& operator<<(std::ostream& os, const Row& row) std::ostream &operator<<(std::ostream &os, const Row &row)
{ {
auto category = row.mData->mCategory; auto category = row.mData->mCategory;
std::string catName = category->name(); std::string catName = category->name();
...@@ -3209,28 +3394,29 @@ File::File() ...@@ -3209,28 +3394,29 @@ File::File()
{ {
} }
File::File(std::istream& is, bool validate) File::File(std::istream &is, bool validate)
: File() : File()
{ {
load(is); load(is);
} }
File::File(const std::string& path, bool validate) File::File(const std::string &path, bool validate)
: File() : File()
{ {
try try
{ {
load(path); load(path);
} }
catch (const std::exception& ex) catch (const std::exception &ex)
{ {
std::cerr << "Error while loading file " << path << std::endl; std::cerr << "Error while loading file " << path << std::endl;
throw; throw;
} }
} }
File::File(File&& rhs) File::File(File &&rhs)
: mHead(nullptr), mValidator(nullptr) : mHead(nullptr)
, mValidator(nullptr)
{ {
std::swap(mHead, rhs.mHead); std::swap(mHead, rhs.mHead);
std::swap(mValidator, rhs.mValidator); std::swap(mValidator, rhs.mValidator);
...@@ -3242,7 +3428,7 @@ File::~File() ...@@ -3242,7 +3428,7 @@ File::~File()
delete mValidator; delete mValidator;
} }
void File::append(Datablock* e) void File::append(Datablock *e)
{ {
e->setValidator(mValidator); e->setValidator(mValidator);
...@@ -3267,7 +3453,7 @@ void File::append(Datablock* e) ...@@ -3267,7 +3453,7 @@ void File::append(Datablock* e)
} }
} }
void File::load(const std::string& p) void File::load(const std::string &p)
{ {
fs::path path(p); fs::path path(p);
...@@ -3295,14 +3481,14 @@ void File::load(const std::string& p) ...@@ -3295,14 +3481,14 @@ void File::load(const std::string& p)
{ {
load(in); load(in);
} }
catch (const std::exception& ex) catch (const std::exception &ex)
{ {
std::cerr << "Error loading file " << path << std::endl; std::cerr << "Error loading file " << path << std::endl;
throw; throw;
} }
} }
void File::save(const std::string& p) void File::save(const std::string &p)
{ {
fs::path path(p); fs::path path(p);
...@@ -3324,9 +3510,9 @@ void File::save(const std::string& p) ...@@ -3324,9 +3510,9 @@ void File::save(const std::string& p)
save(out); save(out);
} }
void File::load(std::istream& is) void File::load(std::istream &is)
{ {
Validator* saved = mValidator; Validator *saved = mValidator;
setValidator(nullptr); setValidator(nullptr);
Parser p(is, *this); Parser p(is, *this);
...@@ -3339,9 +3525,9 @@ void File::load(std::istream& is) ...@@ -3339,9 +3525,9 @@ void File::load(std::istream& is)
} }
} }
void File::load(std::istream& is, const std::string& datablock) void File::load(std::istream &is, const std::string &datablock)
{ {
Validator* saved = mValidator; Validator *saved = mValidator;
setValidator(nullptr); setValidator(nullptr);
Parser p(is, *this); Parser p(is, *this);
...@@ -3354,9 +3540,9 @@ void File::load(std::istream& is, const std::string& datablock) ...@@ -3354,9 +3540,9 @@ void File::load(std::istream& is, const std::string& datablock)
} }
} }
void File::save(std::ostream& os) void File::save(std::ostream &os)
{ {
Datablock* e = mHead; Datablock *e = mHead;
while (e != nullptr) while (e != nullptr)
{ {
e->write(os); e->write(os);
...@@ -3364,9 +3550,9 @@ void File::save(std::ostream& os) ...@@ -3364,9 +3550,9 @@ void File::save(std::ostream& os)
} }
} }
void File::write(std::ostream& os, const std::vector<std::string>& order) void File::write(std::ostream &os, const std::vector<std::string> &order)
{ {
Datablock* e = mHead; Datablock *e = mHead;
while (e != nullptr) while (e != nullptr)
{ {
e->write(os, order); e->write(os, order);
...@@ -3374,17 +3560,17 @@ void File::write(std::ostream& os, const std::vector<std::string>& order) ...@@ -3374,17 +3560,17 @@ void File::write(std::ostream& os, const std::vector<std::string>& order)
} }
} }
Datablock* File::get(const std::string& name) const Datablock *File::get(const std::string &name) const
{ {
const Datablock* result = mHead; const Datablock *result = mHead;
while (result != nullptr and not iequals(result->mName, name)) while (result != nullptr and not iequals(result->mName, name))
result = result->mNext; result = result->mNext;
return const_cast<Datablock*>(result); return const_cast<Datablock *>(result);
} }
Datablock& File::operator[](const std::string& name) Datablock &File::operator[](const std::string &name)
{ {
Datablock* result = mHead; Datablock *result = mHead;
while (result != nullptr and not iequals(result->mName, name)) while (result != nullptr and not iequals(result->mName, name))
result = result->mNext; result = result->mNext;
if (result == nullptr) if (result == nullptr)
...@@ -3414,7 +3600,7 @@ void File::validateLinks() const ...@@ -3414,7 +3600,7 @@ void File::validateLinks() const
d->validateLinks(); d->validateLinks();
} }
const Validator& File::getValidator() const const Validator &File::getValidator() const
{ {
if (mValidator == nullptr) if (mValidator == nullptr)
throw std::runtime_error("no Validator defined yet"); throw std::runtime_error("no Validator defined yet");
...@@ -3426,7 +3612,7 @@ void File::loadDictionary() ...@@ -3426,7 +3612,7 @@ void File::loadDictionary()
loadDictionary("mmcif_ddl"); loadDictionary("mmcif_ddl");
} }
void File::loadDictionary(const char* dict) void File::loadDictionary(const char *dict)
{ {
fs::path dict_name(dict); fs::path dict_name(dict);
...@@ -3449,7 +3635,7 @@ void File::loadDictionary(const char* dict) ...@@ -3449,7 +3635,7 @@ void File::loadDictionary(const char* dict)
#if defined(CACHE_DIR) and defined(DATA_DIR) #if defined(CACHE_DIR) and defined(DATA_DIR)
if (not fs::exists(p)) if (not fs::exists(p))
{ {
for (const char* dir: { CACHE_DIR, DATA_DIR }) for (const char *dir : {CACHE_DIR, DATA_DIR})
{ {
auto p2 = fs::path(dir) / p; auto p2 = fs::path(dir) / p;
if (fs::exists(p2)) if (fs::exists(p2))
...@@ -3478,7 +3664,7 @@ void File::loadDictionary(const char* dict) ...@@ -3478,7 +3664,7 @@ void File::loadDictionary(const char* dict)
} }
} }
void File::loadDictionary(std::istream& is) void File::loadDictionary(std::istream &is)
{ {
std::unique_ptr<Validator> v(new Validator()); std::unique_ptr<Validator> v(new Validator());
...@@ -3488,7 +3674,7 @@ void File::loadDictionary(std::istream& is) ...@@ -3488,7 +3674,7 @@ void File::loadDictionary(std::istream& is)
setValidator(v.release()); setValidator(v.release());
} }
void File::setValidator(Validator* v) void File::setValidator(Validator *v)
{ {
mValidator = v; mValidator = v;
...@@ -3496,13 +3682,13 @@ void File::setValidator(Validator* v) ...@@ -3496,13 +3682,13 @@ void File::setValidator(Validator* v)
d->setValidator(mValidator); d->setValidator(mValidator);
} }
void File::getTagOrder(std::vector<std::string>& tags) const void File::getTagOrder(std::vector<std::string> &tags) const
{ {
for (auto d = mHead; d != nullptr; d = d->mNext) for (auto d = mHead; d != nullptr; d = d->mNext)
d->getTagOrder(tags); d->getTagOrder(tags);
} }
auto File::iterator::operator++() -> iterator& auto File::iterator::operator++() -> iterator &
{ {
mCurrent = mCurrent->mNext; mCurrent = mCurrent->mNext;
return *this; return *this;
...@@ -3518,4 +3704,4 @@ auto File::end() const -> iterator ...@@ -3518,4 +3704,4 @@ auto File::end() const -> iterator
return iterator(nullptr); return iterator(nullptr);
} }
} } // namespace cif
...@@ -1740,6 +1740,14 @@ File::~File() ...@@ -1740,6 +1740,14 @@ File::~File()
delete mImpl; delete mImpl;
} }
cif::Datablock& File::createDatablock(const std::string &name)
{
auto db = new cif::Datablock(name);
mImpl->mData.append(db);
mImpl->mDb = db;
}
void File::load(const std::string &p) void File::load(const std::string &p)
{ {
mImpl->load(p); mImpl->load(p);
...@@ -1778,8 +1786,11 @@ Structure::Structure(File &f, size_t modelNr, StructureOpenOptions options) ...@@ -1778,8 +1786,11 @@ Structure::Structure(File &f, size_t modelNr, StructureOpenOptions options)
: mFile(f) : mFile(f)
, mModelNr(modelNr) , mModelNr(modelNr)
{ {
auto &db = *mFile.impl().mDb; auto db = mFile.impl().mDb;
auto &atomCat = db["atom_site"]; if (db == nullptr)
throw std::logic_error("Empty file!");
auto &atomCat = (*db)["atom_site"];
for (auto &a : atomCat) for (auto &a : atomCat)
{ {
...@@ -2400,6 +2411,31 @@ void Structure::changeResidue(const Residue &res, const std::string &newCompound ...@@ -2400,6 +2411,31 @@ void Structure::changeResidue(const Residue &res, const std::string &newCompound
} }
} }
std::string Structure::createEntityNonPoly(std::vector<cif::Item> data, const std::string &mon_id)
{
auto &db = mFile.data();
auto &entity = db["entity"];
std::string entity_id = entity.getUniqueID("");
// remove any ID fields
data.erase(std::remove_if(data.begin(), data.end(), [](cif::Item &item) { return item.name() == "id"; }),
data.end());
// add our new ID
data.emplace_back("id", entity_id);
data.emplace_back("type", "non-polymer");
entity.emplace(data.begin(), data.end());
return entity_id;
}
std::string Structure::createNonpoly(const std::string &entity_id, const std::vector<cif::Item> &atoms)
{
return {};
}
void Structure::cleanupEmptyCategories() void Structure::cleanupEmptyCategories()
{ {
using namespace cif::literals; using namespace cif::literals;
......
/*-
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2021 NKI/AVL, Netherlands Cancer Institute
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#define BOOST_TEST_MODULE Structure_Test
#include <boost/test/included/unit_test.hpp>
#include <stdexcept>
#include "cif++/Cif++.hpp"
#include "cif++/Structure.hpp"
// --------------------------------------------------------------------
cif::File operator""_cf(const char* text, size_t length)
{
struct membuf : public std::streambuf
{
membuf(char* text, size_t length)
{
this->setg(text, text, text + length);
}
} buffer(const_cast<char*>(text), length);
std::istream is(&buffer);
return cif::File(is);
}
// --------------------------------------------------------------------
BOOST_AUTO_TEST_CASE(create_nonpoly_1)
{
cif::VERBOSE = 1;
// do this now, avoids the need for installing
cif::addFileResource("mmcif_pdbx_v50.dic", "../rsrc/mmcif_pdbx_v50.dic");
auto expected = R"(
data_TEST
loop_
_entity.id
_entity.type
_entity.src_method
_entity.pdbx_description
_entity.formula_weight
1 non-polymer syn 'PROTOPORPHYRIN IX CONTAINING FE' 616.487
)"_cf;
expected.loadDictionary("mmcif_pdbx_v50.dic");
mmcif::File file;
file.file().loadDictionary("mmcif_pdbx_v50.dic");
file.createDatablock("TEST"); // create a datablock
mmcif::Structure structure(file);
structure.createEntityNonPoly({
{ "src_method", "syn" },
{ "pdbx_description", "PROTOPORPHYRIN IX CONTAINING FE" },
{ "formula_weight", 616.487 }
}, "HEM" );
BOOST_TEST(expected.firstDatablock() == structure.getFile().data());
std::cout << expected.firstDatablock() << std::endl
<< std::endl
<< structure.getFile().data() << std::endl;
// // using namespace mmcif;
// auto f = R"(data_TEST
// #
// loop_
// _test.id
// _test.name
// 1 aap
// 2 noot
// 3 mies
// )"_cf;
// auto& db = f.firstDatablock();
// BOOST_CHECK(db.getName() == "TEST");
// auto& test = db["test"];
// BOOST_CHECK(test.size() == 3);
// // wrong! the next lines will crash. And that's OK, don't do that
// // for (auto r: test)
// // test.erase(r);
// // BOOST_CHECK(test.empty());
// // test.purge();
// auto n = test.erase(cif::Key("id") == 1, [](const cif::Row& r) {
// BOOST_CHECK_EQUAL(r["id"].as<int>(), 1);
// BOOST_CHECK_EQUAL(r["name"].as<std::string>(), "aap");
// });
// BOOST_CHECK_EQUAL(n, 1);
}
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