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"
...@@ -117,7 +117,7 @@ CIFPP_EXPORT extern int VERBOSE; ...@@ -117,7 +117,7 @@ CIFPP_EXPORT extern int VERBOSE;
class File; class File;
class Datablock; class Datablock;
class Category; class Category;
class Row; // a flyweight class that references data in categories class Row; // a flyweight class that references data in categories
class RowSet; class RowSet;
class Item; class Item;
class Validator; class Validator;
...@@ -141,43 +141,57 @@ class Item ...@@ -141,43 +141,57 @@ 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)
{ {
mName = rhs.mName; mName = rhs.mName;
mValue = rhs.mValue; mValue = rhs.mValue;
} }
return *this; return *this;
} }
Item& operator=(Item&& rhs) noexcept Item &operator=(Item &&rhs) noexcept
{ {
if (this != &rhs) if (this != &rhs)
{ {
mName = std::move(rhs.mName); mName = std::move(rhs.mName);
mValue = std::move(rhs.mValue); mValue = std::move(rhs.mValue);
} }
return *this; return *this;
} }
const std::string& name() const { return mName; }
const std::string& value() const { return mValue; }
void value(const std::string& v) { mValue = v; } const std::string &name() const { return mName; }
const std::string &value() const { return mValue; }
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;
...@@ -187,12 +201,12 @@ class Item ...@@ -187,12 +201,12 @@ class Item
// is_unknown means the field contains '?' // is_unknown means the field contains '?'
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;
std::string mValue; std::string mValue;
}; };
// -------------------------------------------------------------------- // --------------------------------------------------------------------
...@@ -202,54 +216,58 @@ class Datablock ...@@ -202,54 +216,58 @@ class Datablock
{ {
public: public:
friend class File; friend class File;
using CategoryList = std::list<Category>; using CategoryList = std::list<Category>;
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;
iterator begin() { return mCategories.begin(); } std::string firstItem(const std::string &tag) const;
iterator end() { return mCategories.end(); }
const_iterator begin() const { return mCategories.begin(); } iterator begin() { return mCategories.begin(); }
const_iterator end() const { return mCategories.end(); } iterator end() { return mCategories.end(); }
Category& operator[](const std::string& name); const_iterator begin() const { return mCategories.begin(); }
const_iterator end() const { return mCategories.end(); }
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 write(std::ostream &os, const std::vector<std::string> &order);
void write(std::ostream &os);
void getTagOrder(std::vector<std::string>& tags) const;
void write(std::ostream& os, const std::vector<std::string>& order);
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);
std::list<Category> mCategories; friend std::ostream& operator<<(std::ostream &os, const Datablock &data);
std::string mName;
Validator* mValidator; private:
Datablock* mNext; std::list<Category> mCategories;
std::string mName;
Validator *mValidator;
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,34 +303,34 @@ namespace detail ...@@ -286,34 +303,34 @@ 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);
} }
// empty means either null or unknown // empty means either null or unknown
bool empty() const; bool empty() const;
explicit operator bool() const { return not empty(); } explicit operator bool() const { return not empty(); }
// is_null means the field contains '.' // is_null means the field contains '.'
bool is_null() const; bool is_null() const;
...@@ -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;
...@@ -383,15 +409,15 @@ namespace detail ...@@ -383,15 +409,15 @@ namespace detail
result = 1; result = 1;
} }
} }
return result; return result;
} }
}; };
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;
...@@ -424,15 +450,15 @@ namespace detail ...@@ -424,15 +450,15 @@ namespace detail
result = 1; result = 1;
} }
} }
return result; return result;
} }
}; };
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;
...@@ -465,15 +491,15 @@ namespace detail ...@@ -465,15 +491,15 @@ namespace detail
result = 1; result = 1;
} }
} }
return result; return result;
} }
}; };
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,11 +507,11 @@ namespace detail ...@@ -481,11 +507,11 @@ 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;
if (ref.empty()) if (ref.empty())
return -1; return -1;
else if (not value) else if (not value)
...@@ -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,110 +532,114 @@ namespace detail ...@@ -506,110 +532,114 @@ 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
: (rv < value ? -1 : 1); : (rv < value ? -1 : 1);
} }
}; };
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))
{ {
} }
const ItemReference operator[](size_t ix) const const ItemReference operator[](size_t ix) const
{ {
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()
// of the row should be equal to the number of items in the tuple // of the row should be equal to the number of items in the tuple
// you are trying to tie. // you are trying to tie.
using RType = std::tuple<typename std::remove_reference<Ts>::type...>; using RType = std::tuple<typename std::remove_reference<Ts>::type...>;
mVal = static_cast<RType>(rr); mVal = static_cast<RType>(rr);
} }
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();
...@@ -643,49 +680,53 @@ class Row ...@@ -643,49 +680,53 @@ class Row
mCascade = cascade; mCascade = cascade;
} }
void next(); ///< make this row point to the next ItemRow void next(); ///< make this row point to the next ItemRow
struct const_iterator struct const_iterator
{ {
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++(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; }
private:
const_iterator &operator++();
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; }
private:
void fetch(); void fetch();
ItemRow* mData; ItemRow *mData;
ItemValue* mPtr; ItemValue *mPtr;
Item mCurrent; Item mCurrent;
}; };
// checks for an initialized Row: // checks for an initialized Row:
explicit operator bool() const { return mData != nullptr; } explicit operator bool() const { return mData != nullptr; }
// for debugging // for debugging
uint32_t lineNr() const; uint32_t lineNr() const;
void lineNr(uint32_t l); void lineNr(uint32_t l);
bool empty() const; bool empty() const;
const_iterator begin() const; const_iterator begin() const;
const_iterator end() const; const_iterator end() const;
// TODO: implement real const version? // TODO: implement real const version?
friend class detail::ItemReference; friend class detail::ItemReference;
const detail::ItemReference operator[](size_t column) const const detail::ItemReference operator[](size_t column) const
...@@ -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,46 +769,45 @@ class Row ...@@ -728,46 +769,45 @@ 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::string& name, const std::string& value, bool updateLinked);
bool operator==(const Row& rhs) const void assign(const std::vector<Item> &values);
void assign(const std::string &name, const std::string &value, bool updateLinked);
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 bool test(const Category& c, const Row& r) const = 0;
virtual void str(std::ostream& os) const = 0;
};
struct AllConditionImpl : public ConditionImpl 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 { return true; } virtual void str(std::ostream &os) const = 0;
virtual void str(std::ostream& os) const { os << "*"; } };
};
struct OrConditionImpl; struct AllConditionImpl : public ConditionImpl
struct AndConditionImpl; {
struct NotConditionImpl; virtual bool test(const Category &c, const Row &r) const { return true; }
virtual void str(std::ostream &os) const { os << "*"; }
};
} struct OrConditionImpl;
struct AndConditionImpl;
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;
...@@ -825,44 +870,44 @@ class Condition ...@@ -825,44 +870,44 @@ class Condition
delete mImpl; delete mImpl;
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);
return mImpl ? mImpl->test(c, r) : false; return mImpl ? mImpl->test(c, r) : false;
} }
bool empty() const { return mImpl == nullptr; }
friend Condition operator||(Condition&& a, Condition&& b); 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 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,219 +917,235 @@ inline std::ostream& operator<<(std::ostream& os, const Condition& cond) ...@@ -872,219 +917,235 @@ 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)
: mItemTag(ItemTag) {}
virtual void prepare(const Category& c);
virtual bool test(const Category& c, const Row& r) const
{ {
return r[mItemIx].empty(); KeyIsEmptyConditionImpl(const std::string &ItemTag)
} : mItemTag(ItemTag)
{
}
virtual void str(std::ostream& os) const virtual void prepare(const Category &c);
{
os << mItemTag << " IS NULL";
}
std::string mItemTag; virtual bool test(const Category &c, const Row &r) const
size_t mItemIx = 0; {
}; return r[mItemIx].empty();
}
struct KeyCompareConditionImpl : public ConditionImpl virtual void str(std::ostream &os) const
{ {
template<typename COMP> os << mItemTag << " IS NULL";
KeyCompareConditionImpl(const std::string& ItemTag, COMP&& comp, const std::string& s) }
: mItemTag(ItemTag), mComp(std::move(comp)), mStr(s) {}
virtual void prepare(const Category& c);
virtual bool test(const Category& c, const Row& r) const
{
return mComp(c, r, mCaseInsensitive);
}
virtual void str(std::ostream& os) const
{
os << mItemTag << (mCaseInsensitive ? "^ " : " ") << mStr;
}
std::string mItemTag; std::string mItemTag;
size_t mItemIx = 0; size_t mItemIx = 0;
bool mCaseInsensitive = false; };
std::function<bool(const Category&, const Row&, bool)> mComp;
std::string mStr;
};
struct KeyMatchesConditionImpl : public ConditionImpl struct KeyCompareConditionImpl : public ConditionImpl
{
KeyMatchesConditionImpl(const std::string& ItemTag, const std::regex& rx)
: mItemTag(ItemTag), mItemIx(0), mRx(rx) {}
virtual void prepare(const Category& c);
virtual bool test(const Category& c, const Row& r) const
{ {
return std::regex_match(r[mItemIx].as<std::string>(), mRx); template <typename COMP>
} KeyCompareConditionImpl(const std::string &ItemTag, COMP &&comp, const std::string &s)
: mItemTag(ItemTag)
, mComp(std::move(comp))
, mStr(s)
{
}
virtual void str(std::ostream& os) const virtual void prepare(const Category &c);
{
os << mItemTag << " =~ expression";
}
std::string mItemTag;
size_t mItemIx;
std::regex mRx;
};
template<typename T> virtual bool test(const Category &c, const Row &r) const
struct AnyIsConditionImpl : public ConditionImpl {
{ return mComp(c, r, mCaseInsensitive);
typedef T valueType; }
AnyIsConditionImpl(const valueType& value)
: mValue(value) {}
virtual bool test(const Category& c, const Row& r) const;
virtual void str(std::ostream& os) const
{
os << "<any> == " << mValue;
}
valueType mValue; virtual void str(std::ostream &os) const
}; {
os << mItemTag << (mCaseInsensitive ? "^ " : " ") << mStr;
}
struct AnyMatchesConditionImpl : public ConditionImpl std::string mItemTag;
{ size_t mItemIx = 0;
AnyMatchesConditionImpl(const std::regex& rx) bool mCaseInsensitive = false;
: mRx(rx) {} std::function<bool(const Category &, const Row &, bool)> mComp;
std::string mStr;
virtual bool test(const Category& c, const Row& r) const; };
virtual void str(std::ostream& os) const
struct KeyMatchesConditionImpl : public ConditionImpl
{ {
os << "<any> =~ expression"; KeyMatchesConditionImpl(const std::string &ItemTag, const std::regex &rx)
} : mItemTag(ItemTag)
, mItemIx(0)
, mRx(rx)
{
}
std::regex mRx; virtual void prepare(const Category &c);
};
struct AndConditionImpl : public ConditionImpl virtual bool test(const Category &c, const Row &r) const
{ {
AndConditionImpl(Condition&& a, Condition&& b) return std::regex_match(r[mItemIx].as<std::string>(), mRx);
: mA(nullptr), mB(nullptr) }
{
std::swap(mA, a.mImpl);
std::swap(mB, b.mImpl);
}
~AndConditionImpl()
{
delete mA;
delete mB;
}
virtual void prepare(const Category& c)
{
mA->prepare(c);
mB->prepare(c);
}
virtual bool test(const Category& c, const Row& r) const
{
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 << mItemTag << " =~ expression";
mA->str(os); }
os << ") AND (";
mB->str(os);
os << ')';
}
ConditionImpl* mA; std::string mItemTag;
ConditionImpl* mB; size_t mItemIx;
}; std::regex mRx;
};
struct OrConditionImpl : public ConditionImpl template <typename T>
{ struct AnyIsConditionImpl : public ConditionImpl
OrConditionImpl(Condition&& a, Condition&& b)
: mA(nullptr), mB(nullptr)
{
std::swap(mA, a.mImpl);
std::swap(mB, b.mImpl);
}
~OrConditionImpl()
{
delete mA;
delete mB;
}
virtual void prepare(const Category& c)
{
mA->prepare(c);
mB->prepare(c);
}
virtual bool test(const Category& c, const Row& r) const
{ {
return mA->test(c, r) or mB->test(c, r); typedef T valueType;
}
virtual void str(std::ostream& os) const AnyIsConditionImpl(const valueType &value)
{ : mValue(value)
os << '('; {
mA->str(os); }
os << ") OR (";
mB->str(os);
os << ')';
}
ConditionImpl* mA; virtual bool test(const Category &c, const Row &r) const;
ConditionImpl* mB; virtual void str(std::ostream &os) const
}; {
os << "<any> == " << mValue;
}
struct NotConditionImpl : public ConditionImpl valueType mValue;
{ };
NotConditionImpl(Condition&& a)
: mA(nullptr) struct AnyMatchesConditionImpl : public ConditionImpl
{
std::swap(mA, a.mImpl);
}
~NotConditionImpl()
{ {
delete mA; AnyMatchesConditionImpl(const std::regex &rx)
} : mRx(rx)
{
virtual void prepare(const Category& c) }
virtual bool test(const Category &c, const Row &r) const;
virtual void str(std::ostream &os) const
{
os << "<any> =~ expression";
}
std::regex mRx;
};
struct AndConditionImpl : public ConditionImpl
{ {
mA->prepare(c); AndConditionImpl(Condition &&a, Condition &&b)
} : mA(nullptr)
, mB(nullptr)
virtual bool test(const Category& c, const Row& r) const {
std::swap(mA, a.mImpl);
std::swap(mB, b.mImpl);
}
~AndConditionImpl()
{
delete mA;
delete mB;
}
virtual void prepare(const Category &c)
{
mA->prepare(c);
mB->prepare(c);
}
virtual bool test(const Category &c, const Row &r) const
{
return mA->test(c, r) and mB->test(c, r);
}
virtual void str(std::ostream &os) const
{
os << '(';
mA->str(os);
os << ") AND (";
mB->str(os);
os << ')';
}
ConditionImpl *mA;
ConditionImpl *mB;
};
struct OrConditionImpl : public ConditionImpl
{ {
return not mA->test(c, r); OrConditionImpl(Condition &&a, Condition &&b)
} : mA(nullptr)
, mB(nullptr)
{
std::swap(mA, a.mImpl);
std::swap(mB, b.mImpl);
}
~OrConditionImpl()
{
delete mA;
delete mB;
}
virtual void prepare(const Category &c)
{
mA->prepare(c);
mB->prepare(c);
}
virtual void str(std::ostream& os) const virtual bool test(const Category &c, const Row &r) const
{
return mA->test(c, r) or mB->test(c, r);
}
virtual void str(std::ostream &os) const
{
os << '(';
mA->str(os);
os << ") OR (";
mB->str(os);
os << ')';
}
ConditionImpl *mA;
ConditionImpl *mB;
};
struct NotConditionImpl : public ConditionImpl
{ {
os << "NOT ("; NotConditionImpl(Condition &&a)
mA->str(os); : mA(nullptr)
os << ')'; {
} std::swap(mA, a.mImpl);
}
ConditionImpl* mA; ~NotConditionImpl()
}; {
delete mA;
}
} virtual void prepare(const Category &c)
{
mA->prepare(c);
}
virtual bool test(const Category &c, const Row &r) const
{
return not mA->test(c, r);
}
virtual void str(std::ostream &os) const
{
os << "NOT (";
mA->str(os);
os << ')';
}
ConditionImpl *mA;
};
inline Condition operator&&(Condition&& a, Condition&& b) } // namespace detail
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 &operator=(const Key &) = delete;
Key(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,169 +1221,181 @@ inline Condition operator==(const Key& key, const char *value) ...@@ -1148,169 +1221,181 @@ 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));
} }
inline Condition All() 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)));
} }
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)
...@@ -1343,8 +1428,8 @@ class iterator_impl ...@@ -1343,8 +1428,8 @@ class iterator_impl
{ {
return mCurrent; return mCurrent;
} }
iterator_impl& operator++() iterator_impl &operator++()
{ {
mCurrent.next(); mCurrent.next();
...@@ -1359,26 +1444,25 @@ class iterator_impl ...@@ -1359,26 +1444,25 @@ class iterator_impl
iterator_impl result(*this); iterator_impl result(*this);
this->operator++(); this->operator++();
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,30 +1487,30 @@ class iterator_proxy ...@@ -1403,30 +1487,30 @@ 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); }
bool empty() const { return mCBegin == mCEnd; } bool empty() const { return mCBegin == mCEnd; }
explicit operator bool() const { return not empty(); } explicit operator bool() const { return not empty(); }
size_t size() const { return std::distance(begin(), end()); } size_t size() const { return std::distance(begin(), end()); }
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;
...@@ -1479,14 +1563,14 @@ class conditional_iterator_proxy ...@@ -1479,14 +1563,14 @@ class conditional_iterator_proxy
{ {
return &*mBegin; return &*mBegin;
} }
conditional_iterator_impl& operator++() conditional_iterator_impl &operator++()
{ {
while (mBegin != mEnd) while (mBegin != mEnd)
{ {
if (++mBegin == mEnd) if (++mBegin == mEnd)
break; break;
if ((*mCondition)(*mCat, mBegin.row())) if ((*mCondition)(*mCat, mBegin.row()))
break; break;
} }
...@@ -1499,58 +1583,57 @@ class conditional_iterator_proxy ...@@ -1499,58 +1583,57 @@ class conditional_iterator_proxy
conditional_iterator_impl result(*this); conditional_iterator_impl result(*this);
this->operator++(); this->operator++();
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;
bool empty() const; bool empty() const;
explicit operator bool() const { return not empty(); } explicit operator bool() const { return not empty(); }
size_t size() const { return std::distance(begin(), end()); } size_t size() const { return std::distance(begin(), end()); }
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;
}; };
// -------------------------------------------------------------------- // --------------------------------------------------------------------
...@@ -1723,7 +1806,7 @@ class RowSet ...@@ -1723,7 +1806,7 @@ class RowSet
}; };
// -------------------------------------------------------------------- // --------------------------------------------------------------------
// class Category acts as an STL container for Row objects // class Category acts as an STL container for Row objects
class Category class Category
{ {
...@@ -1732,16 +1815,16 @@ class Category ...@@ -1732,16 +1815,16 @@ 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; }
using iterator = iterator_impl<Row>; using iterator = iterator_impl<Row>;
using const_iterator = iterator_impl<const Row>; using const_iterator = iterator_impl<const Row>;
iterator begin(); iterator begin();
iterator end(); iterator end();
...@@ -1753,115 +1836,119 @@ class Category ...@@ -1753,115 +1836,119 @@ class Category
bool empty() const; bool empty() const;
size_t size() const; size_t size() const;
void clear(); void clear();
Row front() { return Row(mHead); }
Row back() { return Row(mTail); }
Row operator[](Condition&& cond);
template<typename... Ts, size_t N> Row front() { return Row(mHead); }
iterator_proxy<Row, Ts...> rows(char const* const (&columns)[N]) Row back() { return Row(mTail); }
Row operator[](Condition &&cond);
template <typename... Ts, size_t 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));
if (h.empty()) if (h.empty())
throw std::runtime_error("No hits found"); throw std::runtime_error("No hits found");
if (h.size() != 1) if (h.size() != 1)
throw std::runtime_error("Hit not unique"); throw std::runtime_error("Hit not unique");
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");
if (h.size() != 1) if (h.size() != 1)
throw std::runtime_error("Hit not unique"); throw std::runtime_error("Hit not unique");
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(std::initializer_list<Item> values) std::tuple<Row, bool> emplace(Item value) { return emplace({value}); }
{ return emplace(values.begin(), values.end()); }
std::tuple<Row,bool> emplace(Row r); std::tuple<Row, bool> emplace(std::initializer_list<Item> values)
{
template<class Iter> return emplace(values.begin(), values.end());
std::tuple<Row,bool> emplace(Iter b, Iter e); }
std::tuple<Row, bool> emplace(Row r);
size_t erase(Condition&& cond); template <class Iter>
size_t erase(Condition&& cond, std::function<void(const Row&)>&& visit); std::tuple<Row, bool> emplace(Iter b, Iter e);
size_t erase(Condition &&cond);
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,67 +1959,67 @@ class Category ...@@ -1872,67 +1959,67 @@ 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;
iset keyFields() const; iset keyFields() const;
std::set<size_t> keyFieldsByIndex() const; std::set<size_t> keyFieldsByIndex() const;
void drop(const std::string& field);
void getTagOrder(std::vector<std::string>& tags) const; void drop(const std::string &field);
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
void write(std::ostream& os); friend bool operator==(const Category &lhs, const Category &rhs);
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); private:
void write(std::ostream &os);
Datablock& mDb; void write(std::ostream &os, const std::vector<std::string> &order);
std::string mName; void write(std::ostream &os, const std::vector<size_t> &order, bool includeEmptyColumns);
Validator* mValidator;
const ValidateCategory* mCatValidator = nullptr; size_t addColumn(const std::string &name);
std::vector<ItemColumn> mColumns;
ItemRow* mHead; Datablock &mDb;
ItemRow* mTail; std::string mName;
class CatIndex* mIndex; Validator *mValidator;
const ValidateCategory *mCatValidator = nullptr;
std::vector<ItemColumn> mColumns;
ItemRow *mHead;
ItemRow *mTail;
class CatIndex *mIndex;
}; };
// -------------------------------------------------------------------- // --------------------------------------------------------------------
...@@ -1984,82 +2076,96 @@ class File ...@@ -1984,82 +2076,96 @@ 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) 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); Datablock &firstDatablock()
{
Datablock* get(const std::string& name) const; if (mHead == nullptr)
Datablock& operator[](const std::string& name); throw std::runtime_error("No datablocks in file");
return *mHead;
}
void append(Datablock *e);
struct iterator Datablock *get(const std::string &name) const;
Datablock &operator[](const std::string &name);
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; } {
pointer operator->() { return mCurrent; } }
iterator& operator++(); reference operator*() { return *mCurrent; }
iterator operator++(int) { iterator result(*this); this->operator++(); return result; } pointer operator->() { return mCurrent; }
bool operator==(const iterator& rhs) const { return mCurrent == rhs.mCurrent; } iterator &operator++();
bool operator!=(const iterator& rhs) const { return not (mCurrent == rhs.mCurrent); } 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 not(mCurrent == rhs.mCurrent); }
private: private:
Datablock* mCurrent; Datablock *mCurrent;
}; };
iterator begin() const; iterator begin() const;
iterator end() const; iterator end() const;
bool empty() const { return mHead == nullptr; }
const Validator& getValidator() const; bool empty() const { return mHead == nullptr; }
void getTagOrder(std::vector<std::string>& tags) const;
private:
void setValidator(Validator* v); const Validator &getValidator() const;
void getTagOrder(std::vector<std::string> &tags) const;
Datablock* mHead; private:
Validator* mValidator; void setValidator(Validator *v);
Datablock *mHead;
Validator *mValidator;
}; };
// -------------------------------------------------------------------- // --------------------------------------------------------------------
...@@ -2068,64 +2174,67 @@ class File ...@@ -2068,64 +2174,67 @@ 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;
for (auto& f: c.fields())
{ {
try bool result = false;
for (auto &f : c.fields())
{ {
if (r[f].as<valueType>() == mValue) try
{
if (r[f].as<valueType>() == mValue)
{
result = true;
break;
}
}
catch (...)
{ {
result = true;
break;
} }
} }
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;
for (auto& f: c.fields())
{ {
try bool result = false;
for (auto &f : c.fields())
{ {
if (std::regex_match(r[f].as<std::string>(), mRx)) try
{
if (std::regex_match(r[f].as<std::string>(), mRx))
{
result = true;
break;
}
}
catch (...)
{ {
result = true;
break;
} }
} }
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
...@@ -199,15 +199,15 @@ class Residue ...@@ -199,15 +199,15 @@ class Residue
// constructor for waters // constructor for waters
Residue(const Structure &structure, const std::string &compoundID, Residue(const Structure &structure, const std::string &compoundID,
const std::string &asymID, const std::string &authSeqID); const std::string &asymID, const std::string &authSeqID);
// constructor for a residue without a sequence number // constructor for a residue without a sequence number
Residue(const Structure &structure, const std::string &compoundID, Residue(const Structure &structure, const std::string &compoundID,
const std::string &asymID); const std::string &asymID);
// constructor for a residue with a sequence number // constructor for a residue with a sequence number
Residue(const Structure &structure, const std::string &compoundID, Residue(const Structure &structure, const std::string &compoundID,
const std::string &asymID, int seqID, const std::string &authSeqID); const std::string &asymID, int seqID, const std::string &authSeqID);
Residue(const Residue &rhs) = delete; Residue(const Residue &rhs) = delete;
Residue &operator=(const Residue &rhs) = delete; Residue &operator=(const Residue &rhs) = delete;
...@@ -297,7 +297,7 @@ class Monomer : public Residue ...@@ -297,7 +297,7 @@ class Monomer : public Residue
Monomer &operator=(Monomer &&rhs); Monomer &operator=(Monomer &&rhs);
Monomer(const Polymer &polymer, size_t index, int seqID, const std::string &authSeqID, Monomer(const Polymer &polymer, size_t index, int seqID, const std::string &authSeqID,
const std::string &compoundID); const std::string &compoundID);
bool is_first_in_chain() const; bool is_first_in_chain() const;
bool is_last_in_chain() const; bool is_last_in_chain() const;
...@@ -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);
...@@ -450,7 +452,7 @@ class Structure ...@@ -450,7 +452,7 @@ class Structure
// Atom getAtomByLocation(Point pt, float maxDistance) const; // Atom getAtomByLocation(Point pt, float maxDistance) const;
Atom getAtomByLabel(const std::string &atomID, const std::string &asymID, Atom getAtomByLabel(const std::string &atomID, const std::string &asymID,
const std::string &compID, int seqID, const std::string &altID = ""); const std::string &compID, int seqID, const std::string &altID = "");
/// \brief Get a residue, if \a seqID is zero, the non-polymers are searched /// \brief Get a residue, if \a seqID is zero, the non-polymers are searched
const Residue &getResidue(const std::string &asymID, const std::string &compID, int seqID = 0) const; const Residue &getResidue(const std::string &asymID, const std::string &compID, int seqID = 0) const;
...@@ -458,7 +460,7 @@ class Structure ...@@ -458,7 +460,7 @@ class Structure
// map between auth and label locations // map between auth and label locations
std::tuple<std::string, int, std::string> MapAuthToLabel(const std::string &asymID, std::tuple<std::string, int, std::string> MapAuthToLabel(const std::string &asymID,
const std::string &seqID, const std::string &compID, const std::string &insCode = ""); const std::string &seqID, const std::string &compID, const std::string &insCode = "");
std::tuple<std::string, std::string, std::string, std::string> MapLabelToAuth( std::tuple<std::string, std::string, std::string, std::string> MapLabelToAuth(
const std::string &asymID, int seqID, const std::string &compID); const std::string &asymID, int seqID, const std::string &compID);
...@@ -480,7 +482,20 @@ class Structure ...@@ -480,7 +482,20 @@ class Structure
void swapAtoms(Atom &a1, Atom &a2); // swap the labels for these atoms void swapAtoms(Atom &a1, Atom &a2); // swap the labels for these atoms
void moveAtom(Atom &a, Point p); // move atom to a new location void moveAtom(Atom &a, Point p); // move atom to a new location
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
......
...@@ -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,34 +56,35 @@ namespace cif ...@@ -56,34 +56,35 @@ 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
// Item values are stored in a simple struct. They should be const anyway // Item values are stored in a simple struct. They should be const anyway
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 null() 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); bool empty() const { return mText[0] == 0 or ((mText[0] == '.' or mText[0] == '?') and mText[1] == 0); }
void operator delete(void* p); bool null() const { return mText[0] == '.' and mText[1] == 0; }
void operator delete(void* p, size_t dataSize); bool unknown() const { return mText[0] == '?' and mText[1] == 0; }
void *operator new(size_t size, size_t dataSize);
void operator delete(void *p);
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);
} }
...@@ -121,8 +122,8 @@ void ItemValue::operator delete(void* p, size_t dataSize) ...@@ -121,8 +122,8 @@ 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,8 +133,8 @@ struct ItemRow ...@@ -132,8 +133,8 @@ 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
{ {
std::stringstream s; std::stringstream s;
...@@ -144,21 +145,21 @@ struct ItemRow ...@@ -144,21 +145,21 @@ struct ItemRow
s << mCategory->getColumnName(v->mColumnIndex) s << mCategory->getColumnName(v->mColumnIndex)
<< ':' << ':'
<< v->mText; << v->mText;
if (v->mNext != nullptr) if (v->mNext != nullptr)
s << ", "; s << ", ";
} }
s << '}'; s << '}';
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)
...@@ -168,7 +169,7 @@ std::ostream& operator<<(std::ostream& os, const ItemRow& r) ...@@ -168,7 +169,7 @@ std::ostream& operator<<(std::ostream& os, const ItemRow& r)
os << ','; os << ',';
} }
os << ']'; os << ']';
return os; return os;
} }
...@@ -219,10 +220,10 @@ void ItemRow::drop(size_t columnIx) ...@@ -219,10 +220,10 @@ 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)
{ {
if (v->mColumnIndex == columnIx) if (v->mColumnIndex == columnIx)
...@@ -240,119 +241,121 @@ const char* ItemRow::c_str(size_t columnIx) const ...@@ -240,119 +241,121 @@ 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");
if (mRow.mData == nullptr) if (mRow.mData == nullptr)
throw std::logic_error("Attempt to write to an uninitialized row"); throw std::logic_error("Attempt to write to an uninitialized row");
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;
if (mRow.mData != nullptr /* and mRow.mData->mCategory != nullptr*/)
{ {
// assert(mRow.mData->mCategory); const char *result = kEmptyResult;
for (auto iv = mRow.mData->mValues; iv != nullptr; iv = iv->mNext) if (mRow.mData != nullptr /* and mRow.mData->mCategory != nullptr*/)
{ {
if (iv->mColumnIndex == mColumn) // assert(mRow.mData->mCategory);
for (auto iv = mRow.mData->mValues; iv != nullptr; iv = iv->mNext)
{ {
if (iv->mText[0] != '.' or iv->mText[1] != 0) if (iv->mColumnIndex == mColumn)
result = iv->mText; {
if (iv->mText[0] != '.' or iv->mText[1] != 0)
break; result = iv->mText;
break;
}
} }
} }
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;
if (mRow.mData != nullptr and mRow.mData->mCategory != nullptr)
{ {
for (auto iv = mRow.mData->mValues; iv != nullptr; iv = iv->mNext) const char *result = defaultValue;
if (mRow.mData != nullptr and mRow.mData->mCategory != nullptr)
{ {
if (iv->mColumnIndex == mColumn) for (auto iv = mRow.mData->mValues; iv != nullptr; iv = iv->mNext)
{ {
// only really non-empty values if (iv->mColumnIndex == mColumn)
if (iv->mText[0] != 0 and ((iv->mText[0] != '.' and iv->mText[0] != '?') or iv->mText[1] != 0)) {
result = iv->mText; // only really non-empty values
if (iv->mText[0] != 0 and ((iv->mText[0] != '.' and iv->mText[0] != '?') or iv->mText[1] != 0))
result = iv->mText;
break; break;
}
} }
}
if (result == defaultValue and mColumn < mRow.mData->mCategory->mColumns.size()) // not found, perhaps the category has a default defined? if (result == defaultValue and mColumn < mRow.mData->mCategory->mColumns.size()) // not found, perhaps the category has a default defined?
{ {
auto iv = mRow.mData->mCategory->mColumns[mColumn].mValidator; auto iv = mRow.mData->mCategory->mColumns[mColumn].mValidator;
if (iv != nullptr and not iv->mDefault.empty()) if (iv != nullptr and not iv->mDefault.empty())
result = iv->mDefault.c_str(); result = iv->mDefault.c_str();
}
} }
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;
if (mRow.mData != nullptr and mRow.mData->mCategory != nullptr)
{ {
for (auto iv = mRow.mData->mValues; iv != nullptr; iv = iv->mNext) boost::tribool result;
if (mRow.mData != nullptr and mRow.mData->mCategory != nullptr)
{ {
if (iv->mColumnIndex == mColumn) for (auto iv = mRow.mData->mValues; iv != nullptr; iv = iv->mNext)
{ {
result = iv->mText[0] == '.' and iv->mText[1] == 0; if (iv->mColumnIndex == mColumn)
break; {
result = iv->mText[0] == '.' and iv->mText[1] == 0;
break;
}
} }
}
if (result == boost::indeterminate and mColumn < mRow.mData->mCategory->mColumns.size()) // not found, perhaps the category has a default defined? if (result == boost::indeterminate and mColumn < mRow.mData->mCategory->mColumns.size()) // not found, perhaps the category has a default defined?
{ {
auto iv = mRow.mData->mCategory->mColumns[mColumn].mValidator; auto iv = mRow.mData->mCategory->mColumns[mColumn].mValidator;
if (iv != nullptr) if (iv != nullptr)
result = iv->mDefaultIsNull; result = iv->mDefaultIsNull;
}
} }
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,33 +388,41 @@ std::string Datablock::firstItem(const std::string& tag) const ...@@ -385,33 +388,41 @@ 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())
{ {
isNew = true; isNew = true;
i = mCategories.emplace(end(), *this, name, mValidator); i = mCategories.emplace(end(), *this, name, mValidator);
} }
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 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;
}
const Category *Datablock::get(const std::string &name) const
{
auto i = find_if(begin(), end(), [name](const Category &cat) -> bool
{ return iequals(cat.name(), name); });
return i == end() ? nullptr : &*i; return i == end() ? nullptr : &*i;
} }
...@@ -421,188 +432,257 @@ bool Datablock::isValid() ...@@ -421,188 +432,257 @@ 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;
// 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
// audit_conform record. // audit_conform 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);
} }
} }
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);
if (catName == c) if (catName == c)
items.push_back(Item); items.push_back(Item);
} }
cat->write(os, items); cat->write(os, items);
} }
// 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
// // and if it exists, _AND_ we have a Validator, write out the
// // auditConform record.
//
// for (auto& cat: mCategories)
// {
// if (cat.name() == "entry")
// {
// cat.write(os);
//
// if (mValidator != nullptr)
// {
// Category auditConform(*this, "audit_conform", nullptr);
// auditConform.emplace({
// { "dict_name", mValidator->dictName() },
// { "dict_version", mValidator->dictVersion() }
// });
// auditConform.write(os);
// }
//
// break;
// }
// }
//
// for (auto& cat: mCategories)
// {
// if (cat.name() != "entry" and cat.name() != "audit_conform")
// cat.write(os);
// }
}
// -------------------------------------------------------------------- // // mmcif support, sort of. First write the 'entry' Category
// // // and if it exists, _AND_ we have a Validator, write out the
// // auditConform record.
//
// for (auto& cat: mCategories)
// {
// if (cat.name() == "entry")
// {
// cat.write(os);
//
// if (mValidator != nullptr)
// {
// Category auditConform(*this, "audit_conform", nullptr);
// auditConform.emplace({
// { "dict_name", mValidator->dictName() },
// { "dict_version", mValidator->dictVersion() }
// });
// auditConform.write(os);
// }
//
// break;
// }
// }
//
// for (auto& cat: mCategories)
// {
// if (cat.name() != "entry" and cat.name() != "audit_conform")
// cat.write(os);
// }
}
namespace detail bool operator==(const cif::Datablock &dbA, const cif::Datablock &dbB)
{ {
std::vector<std::string> catA, catB;
void KeyCompareConditionImpl::prepare(const Category& c) for (auto &cat : dbA)
{ {
mItemIx = c.getColumnIndex(mItemTag); 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();
auto cv = c.getCatValidator(); while (catA_i != catA.end() and catB_i != catB.end())
if (cv)
{ {
auto iv = cv->getValidatorForItem(mItemTag); std::string nA = *catA_i;
if (iv != nullptr and iv->mType != nullptr) 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
{ {
auto type = iv->mType; if (not (*dbA.get(*catA_i) == *dbB.get(*catB_i)))
mCaseInsensitive = type->mPrimitiveType == DDL_PrimitiveType::UChar; return false;
++catA_i;
++catB_i;
} }
} }
return true;
} }
void KeyIsEmptyConditionImpl::prepare(const Category& c) std::ostream& operator<<(std::ostream &os, const Datablock &data)
{ {
mItemIx = c.getColumnIndex(mItemTag); // whoohoo... this sucks!
const_cast<Datablock&>(data).write(os);
return os;
} }
void KeyMatchesConditionImpl::prepare(const Category& c) // --------------------------------------------------------------------
//
namespace detail
{ {
mItemIx = c.getColumnIndex(mItemTag);
}
} void KeyCompareConditionImpl::prepare(const Category &c)
{
mItemIx = c.getColumnIndex(mItemTag);
auto cv = c.getCatValidator();
if (cv)
{
auto iv = cv->getValidatorForItem(mItemTag);
if (iv != nullptr and iv->mType != nullptr)
{
auto type = iv->mType;
mCaseInsensitive = type->mPrimitiveType == DDL_PrimitiveType::UChar;
}
}
}
void KeyIsEmptyConditionImpl::prepare(const Category &c)
{
mItemIx = c.getColumnIndex(mItemTag);
}
void KeyMatchesConditionImpl::prepare(const Category &c)
{
mItemIx = c.getColumnIndex(mItemTag);
}
} // namespace detail
// -------------------------------------------------------------------- // --------------------------------------------------------------------
// //
...@@ -611,77 +691,76 @@ void KeyMatchesConditionImpl::prepare(const Category& c) ...@@ -611,77 +691,76 @@ 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>
RowComparator(Category* cat, KeyIter b, KeyIter e);
int operator()(const ItemRow* a, const ItemRow* b) const;
int operator()(const Row& a, const Row& b) const template <typename KeyIter>
RowComparator(Category *cat, KeyIter b, KeyIter e);
int operator()(const ItemRow *a, const ItemRow *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();
for (auto ki = b; ki != e; ++ki) for (auto ki = b; ki != e; ++ki)
{ {
std::string k = *ki; std::string k = *ki;
size_t ix = cat->getColumnIndex(k); size_t ix = cat->getColumnIndex(k);
auto iv = cv->getValidatorForItem(k); auto iv = cv->getValidatorForItem(k);
if (iv == nullptr) if (iv == nullptr)
throw std::runtime_error("Incomplete dictionary, no Item Validator for Key " + k); throw std::runtime_error("Incomplete dictionary, no Item Validator for Key " + k);
auto tv = iv->mType; auto tv = iv->mType;
if (tv == nullptr) if (tv == nullptr)
throw std::runtime_error("Incomplete dictionary, no type Validator for Item " + k); throw std::runtime_error("Incomplete dictionary, no type Validator for Item " + k);
using namespace std::placeholders; using namespace std::placeholders;
mComp.emplace_back(ix, std::bind(&ValidateType::compare, tv, _1, _2)); mComp.emplace_back(ix, std::bind(&ValidateType::compare, tv, _1, _2));
} }
} }
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);
if (d != 0) if (d != 0)
break; break;
} }
return d; return d;
} }
...@@ -693,148 +772,152 @@ int RowComparator::operator()(const ItemRow* a, const ItemRow* b) const ...@@ -693,148 +772,152 @@ 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;
void insert(ItemRow* r); ItemRow *find(ItemRow *k) const;
void erase(ItemRow* r);
void insert(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;
result = std::make_tuple(head->mRow, tail->mRow); result = std::make_tuple(head->mRow, tail->mRow);
} }
return result; return result;
} }
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()
{ {
delete mLeft; delete mLeft;
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;
h->mRed = true; h->mRed = true;
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;
h->mRed = true; h->mRed = true;
return x; return x;
} }
void flipColour(entry* h) void flipColour(entry *h)
{ {
h->mRed = not h->mRed; h->mRed = not h->mRed;
if (h->mLeft != nullptr) if (h->mLeft != nullptr)
h->mLeft->mRed = not h->mLeft->mRed; h->mLeft->mRed = not h->mLeft->mRed;
if (h->mRight != nullptr) if (h->mRight != nullptr)
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);
if (h->mRight != nullptr and isRed(h->mRight->mLeft)) if (h->mRight != nullptr and isRed(h->mRight->mLeft))
{ {
h->mRight = rotateRight(h->mRight); h->mRight = rotateRight(h->mRight);
h = rotateLeft(h); h = rotateLeft(h);
flipColour(h); flipColour(h);
} }
return h; return h;
} }
entry* moveRedRight(entry* h) entry *moveRedRight(entry *h)
{ {
flipColour(h); flipColour(h);
if (h->mLeft != nullptr and isRed(h->mLeft->mLeft)) if (h->mLeft != nullptr and isRed(h->mLeft->mLeft))
{ {
h = rotateRight(h); h = rotateRight(h);
flipColour(h); flipColour(h);
} }
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);
if (isRed(h->mLeft) and isRed(h->mLeft->mLeft)) if (isRed(h->mLeft) and isRed(h->mLeft->mLeft))
h = rotateRight(h); h = rotateRight(h);
if (isRed(h->mLeft) and isRed(h->mRight)) if (isRed(h->mLeft) and isRed(h->mRight))
flipColour(h); flipColour(h);
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;
return h; return h;
} }
entry* eraseMin(entry* h) entry *eraseMin(entry *h)
{ {
if (h->mLeft == nullptr) if (h->mLeft == nullptr)
{ {
...@@ -845,44 +928,46 @@ class CatIndex ...@@ -845,44 +928,46 @@ class CatIndex
{ {
if (not isRed(h->mLeft) and not isRed(h->mLeft->mLeft)) if (not isRed(h->mLeft) and not isRed(h->mLeft->mLeft))
h = moveRedLeft(h); h = moveRedLeft(h);
h->mLeft = eraseMin(h->mLeft); h->mLeft = eraseMin(h->mLeft);
h = fixUp(h); h = fixUp(h);
} }
return h; return h;
} }
// 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;
if (e->mLeft != nullptr) if (e->mLeft != nullptr)
{ {
auto l = reorder(e->mLeft); auto l = reorder(e->mLeft);
l->mRow->mNext = e->mRow; l->mRow->mNext = e->mRow;
} }
if (e->mRight != nullptr) if (e->mRight != nullptr)
{ {
auto mr = findMin(e->mRight); auto mr = findMin(e->mRight);
e->mRow->mNext = mr->mRow; e->mRow->mNext = mr->mRow;
result = reorder(e->mRight); result = reorder(e->mRight);
} }
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);
...@@ -904,24 +989,26 @@ ItemRow* CatIndex::find(ItemRow* k) const ...@@ -904,24 +989,26 @@ ItemRow* CatIndex::find(ItemRow* k) const
else else
break; break;
} }
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());
...@@ -930,21 +1017,21 @@ CatIndex::entry* CatIndex::insert(entry* h, ItemRow* v) ...@@ -930,21 +1017,21 @@ CatIndex::entry* CatIndex::insert(entry* h, ItemRow* v)
if (isRed(h->mLeft) and isRed(h->mLeft->mLeft)) if (isRed(h->mLeft) and isRed(h->mLeft->mLeft))
h = rotateRight(h); h = rotateRight(h);
if (isRed(h->mLeft) and isRed(h->mRight)) if (isRed(h->mLeft) and isRed(h->mRight))
flipColour(h); flipColour(h);
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)
{ {
...@@ -960,18 +1047,18 @@ CatIndex::entry* CatIndex::erase(entry* h, ItemRow* k) ...@@ -960,18 +1047,18 @@ CatIndex::entry* CatIndex::erase(entry* h, ItemRow* k)
{ {
if (isRed(h->mLeft)) if (isRed(h->mLeft))
h = rotateRight(h); h = rotateRight(h);
if (mComp(k, h->mRow) == 0 and h->mRight == nullptr) if (mComp(k, h->mRow) == 0 and h->mRight == nullptr)
{ {
delete h; delete h;
return nullptr; return nullptr;
} }
if (h->mRight != nullptr) if (h->mRight != nullptr)
{ {
if (not isRed(h->mRight) and not isRed(h->mRight->mLeft)) if (not isRed(h->mRight) and not isRed(h->mRight->mLeft))
h = moveRedRight(h); h = moveRedRight(h);
if (mComp(k, h->mRow) == 0) if (mComp(k, h->mRow) == 0)
{ {
h->mRow = findMin(h->mRight)->mRow; h->mRow = findMin(h->mRight)->mRow;
...@@ -981,7 +1068,7 @@ CatIndex::entry* CatIndex::erase(entry* h, ItemRow* k) ...@@ -981,7 +1068,7 @@ CatIndex::entry* CatIndex::erase(entry* h, ItemRow* k)
h->mRight = erase(h->mRight, k); h->mRight = erase(h->mRight, k);
} }
} }
return fixUp(h); return fixUp(h);
} }
...@@ -989,133 +1076,133 @@ void CatIndex::reconstruct() ...@@ -989,133 +1076,133 @@ 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)
continue; continue;
++result; ++result;
s.push(e->mLeft); s.push(e->mLeft);
s.push(e->mRight); s.push(e->mRight);
} }
return result; return result;
} }
//bool CatIndex::isValid() const //bool CatIndex::isValid() const
//{ //{
// bool result = true; // bool result = true;
// //
// if (mRoot != nullptr) // if (mRoot != nullptr)
// { // {
// uint32_t minBlack = numeric_limits<uint32_t>::max(); // uint32_t minBlack = numeric_limits<uint32_t>::max();
// uint32_t maxBlack = 0; // uint32_t maxBlack = 0;
// //
// assert(not mRoot->mRed); // assert(not mRoot->mRed);
// //
// result = isValid(mRoot, false, 0, minBlack, maxBlack); // result = isValid(mRoot, false, 0, minBlack, maxBlack);
// assert(minBlack == maxBlack); // assert(minBlack == maxBlack);
// } // }
// //
// return result; // return result;
//} //}
// //
//bool CatIndex::validate(entry* h, bool isParentRed, uint32_t blackDepth, uint32_t& minBlack, uint32_t& maxBlack) const //bool CatIndex::validate(entry* h, bool isParentRed, uint32_t blackDepth, uint32_t& minBlack, uint32_t& maxBlack) const
//{ //{
// bool result = true; // bool result = true;
// //
// if (h->mRed) // if (h->mRed)
// assert(not isParentRed); // assert(not isParentRed);
// else // else
// ++blackDepth; // ++blackDepth;
// //
// if (isParentRed) // if (isParentRed)
// assert(not h->mRed); // assert(not h->mRed);
// //
// if (h->mLeft != nullptr and h->mRight != nullptr) // if (h->mLeft != nullptr and h->mRight != nullptr)
// { // {
// if (isRed(h->mLeft)) // if (isRed(h->mLeft))
...@@ -1123,7 +1210,7 @@ size_t CatIndex::size() const ...@@ -1123,7 +1210,7 @@ size_t CatIndex::size() const
// if (isRed(h->mRight)) // if (isRed(h->mRight))
// assert(not isRed(h->mLeft)); // assert(not isRed(h->mLeft));
// } // }
// //
// if (h->mLeft != nullptr) // if (h->mLeft != nullptr)
// { // {
// assert(mComp(h->mLeft->mRow, h->mRow) < 0); // assert(mComp(h->mLeft->mRow, h->mRow) < 0);
...@@ -1136,7 +1223,7 @@ size_t CatIndex::size() const ...@@ -1136,7 +1223,7 @@ size_t CatIndex::size() const
// if (maxBlack < blackDepth) // if (maxBlack < blackDepth)
// maxBlack = blackDepth; // maxBlack = blackDepth;
// } // }
// //
// if (h->mRight != nullptr) // if (h->mRight != nullptr)
// { // {
// assert(mComp(h->mRight->mRow, h->mRow) > 0); // assert(mComp(h->mRight->mRow, h->mRow) > 0);
...@@ -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,59 +1279,63 @@ RowSet::~RowSet() ...@@ -1192,59 +1279,63 @@ RowSet::~RowSet()
// delete mCond; // delete mCond;
} }
RowSet& RowSet::operator=(const RowSet& rhs) RowSet &RowSet::operator=(const RowSet &rhs)
{ {
if (this != &rhs) if (this != &rhs)
{ {
mItems = rhs.mItems; mItems = rhs.mItems;
mCat = rhs.mCat; mCat = rhs.mCat;
} }
return *this; return *this;
} }
RowSet& RowSet::operator=(RowSet&& rhs) RowSet &RowSet::operator=(RowSet &&rhs)
{ {
if (this != &rhs) if (this != &rhs)
{ {
std::swap(mItems, rhs.mItems); std::swap(mItems, rhs.mItems);
mCat = rhs.mCat; mCat = rhs.mCat;
} }
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());
stable_sort(mItems.begin(), mItems.end(), c); stable_sort(mItems.begin(), mItems.end(), c);
return *this; return *this;
} }
// -------------------------------------------------------------------- // --------------------------------------------------------------------
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");
if (mValidator != nullptr) if (mValidator != nullptr)
{ {
mCatValidator = mValidator->getValidatorForCategory(mName); mCatValidator = mValidator->getValidatorForCategory(mName);
if (mCatValidator != nullptr) if (mCatValidator != nullptr)
{ {
// 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,16 +1347,16 @@ Category::~Category() ...@@ -1256,16 +1347,16 @@ Category::~Category()
delete mIndex; delete mIndex;
} }
void Category::setValidator(Validator* v) void Category::setValidator(Validator *v)
{ {
mValidator = v; mValidator = v;
if (mIndex != nullptr) if (mIndex != nullptr)
{ {
delete mIndex; delete mIndex;
mIndex = nullptr; mIndex = nullptr;
} }
if (mValidator != nullptr) if (mValidator != nullptr)
{ {
mCatValidator = mValidator->getValidatorForCategory(mName); mCatValidator = mValidator->getValidatorForCategory(mName);
...@@ -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;
...@@ -1297,18 +1388,18 @@ size_t Category::getColumnIndex(const std::string& name) const ...@@ -1297,18 +1388,18 @@ size_t Category::getColumnIndex(const std::string& name) const
if (iequals(name, mColumns[result].mName)) if (iequals(name, mColumns[result].mName))
break; break;
} }
if (VERBOSE and result == mColumns.size() and mCatValidator != nullptr) // validate the name, if it is known at all (since it was not found) if (VERBOSE and result == mColumns.size() and mCatValidator != nullptr) // validate the name, if it is known at all (since it was not found)
{ {
auto iv = mCatValidator->getValidatorForItem(name); auto iv = mCatValidator->getValidatorForItem(name);
if (iv == nullptr) if (iv == nullptr)
std::cerr << "Invalid name used '" + name + "' is not a known column in " + mName << std::endl; std::cerr << "Invalid name used '" + name + "' is not a known column in " + mName << std::endl;
} }
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,29 +1407,29 @@ const std::string& Category::getColumnName(size_t columnIx) const ...@@ -1316,29 +1407,29 @@ 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)
{ {
itemValidator = mCatValidator->getValidatorForItem(name); itemValidator = mCatValidator->getValidatorForItem(name);
if (itemValidator == nullptr) if (itemValidator == nullptr)
mValidator->reportError("tag " + name + " not allowed in Category " + mName, false); mValidator->reportError("tag " + name + " not allowed in Category " + mName, false);
} }
mColumns.push_back({name, itemValidator}); mColumns.push_back({name, itemValidator});
} }
return result; return result;
} }
...@@ -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);
...@@ -1416,10 +1507,10 @@ std::string Category::getUniqueID(std::function<std::string(int)> generator) ...@@ -1416,10 +1507,10 @@ std::string Category::getUniqueID(std::function<std::string(int)> generator)
size_t Category::size() const size_t Category::size() const
{ {
size_t result = 0; size_t result = 0;
for (auto pi = mHead; pi != nullptr; pi = pi->mNext) for (auto pi = mHead; pi != nullptr; pi = pi->mNext)
++result; ++result;
return result; return result;
} }
...@@ -1428,30 +1519,31 @@ bool Category::empty() const ...@@ -1428,30 +1519,31 @@ 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())
{ {
size_t columnIx = ci - mColumns.begin(); size_t columnIx = ci - mColumns.begin();
for (auto pi = mHead; pi != nullptr; pi = pi->mNext) for (auto pi = mHead; pi != nullptr; pi = pi->mNext)
pi->drop(columnIx); pi->drop(columnIx);
mColumns.erase(ci); mColumns.erase(ci);
} }
} }
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))
{ {
...@@ -1461,15 +1553,15 @@ Row Category::operator[](Condition&& cond) ...@@ -1461,15 +1553,15 @@ Row Category::operator[](Condition&& cond)
} }
return result; return result;
} }
// RowSet Category::find(Condition&& cond) // RowSet Category::find(Condition&& cond)
// { // {
// // return RowSet(*this, std::forward<Condition>(cond)); // // return RowSet(*this, std::forward<Condition>(cond));
// RowSet result(*this); // RowSet result(*this);
// 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))
{ {
...@@ -1500,7 +1592,7 @@ RowSet Category::orderBy(std::initializer_list<std::string> items) ...@@ -1500,7 +1592,7 @@ RowSet Category::orderBy(std::initializer_list<std::string> items)
{ {
RowSet result(*this); RowSet result(*this);
result.insert(result.begin(), begin(), end()); result.insert(result.begin(), begin(), end());
return result.orderBy(items); return result.orderBy(items);
} }
...@@ -1508,7 +1600,7 @@ void Category::clear() ...@@ -1508,7 +1600,7 @@ void Category::clear()
{ {
delete mHead; delete mHead;
mHead = mTail = nullptr; mHead = mTail = nullptr;
if (mIndex != nullptr) if (mIndex != nullptr)
{ {
delete mIndex; delete mIndex;
...@@ -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,15 +1617,15 @@ std::tuple<Row,bool> Category::emplace(Iter b, Iter e) ...@@ -1525,15 +1617,15 @@ 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);
if (iv == nullptr) if (iv == nullptr)
continue; continue;
bool seen = false; bool seen = false;
for (auto v = b; v != e; ++v) for (auto v = b; v != e; ++v)
{ {
if (iequals(v->name(), col.mName)) if (iequals(v->name(), col.mName))
...@@ -1542,23 +1634,23 @@ std::tuple<Row,bool> Category::emplace(Iter b, Iter e) ...@@ -1542,23 +1634,23 @@ std::tuple<Row,bool> Category::emplace(Iter b, Iter e)
break; break;
} }
} }
if (not seen and iv->mMandatory) if (not seen and iv->mMandatory)
throw std::runtime_error("missing mandatory field " + col.mName + " for Category " + mName); throw std::runtime_error("missing mandatory field " + col.mName + " for Category " + mName);
} }
if (mIndex != nullptr) if (mIndex != nullptr)
{ {
std::unique_ptr<ItemRow> nr(new ItemRow{nullptr, this, nullptr}); std::unique_ptr<ItemRow> nr(new ItemRow{nullptr, this, nullptr});
Row r(nr.get()); Row r(nr.get());
auto keys = keyFields(); auto keys = keyFields();
for (auto v = b; v != e; ++v) for (auto v = b; v != e; ++v)
{ {
if (keys.count(v->name())) if (keys.count(v->name()))
r.assign(v->name(), v->value(), true); r.assign(v->name(), v->value(), true);
} }
auto test = mIndex->find(nr.get()); auto test = mIndex->find(nr.get());
if (test != nullptr) if (test != nullptr)
{ {
...@@ -1578,7 +1670,7 @@ std::tuple<Row,bool> Category::emplace(Iter b, Iter e) ...@@ -1578,7 +1670,7 @@ std::tuple<Row,bool> Category::emplace(Iter b, Iter e)
for (auto v = b; v != e; ++v) for (auto v = b; v != e; ++v)
r.assign(*v, true); r.assign(*v, true);
// if (isOrphan(r)) // if (isOrphan(r))
// throw std::runtime_error("Cannot insert row in category " + mName + " since it would be an orphan"); // throw std::runtime_error("Cannot insert row in category " + mName + " since it would be an orphan");
...@@ -1600,16 +1692,16 @@ std::tuple<Row,bool> Category::emplace(Iter b, Iter e) ...@@ -1600,16 +1692,16 @@ std::tuple<Row,bool> Category::emplace(Iter b, Iter e)
if (mIndex != nullptr) if (mIndex != nullptr)
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;
...@@ -1627,10 +1719,10 @@ size_t Category::erase(Condition&& cond) ...@@ -1627,10 +1719,10 @@ size_t Category::erase(Condition&& cond)
++ri; ++ri;
} }
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,26 +1744,26 @@ size_t Category::erase(Condition&& cond, std::function<void(const Row&)>&& verbo ...@@ -1652,26 +1744,26 @@ 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))
{ {
if (VERBOSE > 1) if (VERBOSE > 1)
std::cerr << "Removing orphaned record: " << std::endl std::cerr << "Removing orphaned record: " << std::endl
<< r << std::endl << r << std::endl
<< std::endl; << std::endl;
remove.push_back(r.mData); remove.push_back(r.mData);
} }
} }
for (auto r: remove) for (auto r : remove)
erase(iterator(r)); erase(iterator(r));
} }
...@@ -1694,7 +1786,7 @@ auto Category::erase(iterator pos) -> iterator ...@@ -1694,7 +1786,7 @@ auto Category::erase(iterator pos) -> iterator
if (mIndex != nullptr) if (mIndex != nullptr)
mIndex->erase(r.mData); mIndex->erase(r.mData);
if (r == mHead) if (r == mHead)
{ {
mHead = mHead->mNext; mHead = mHead->mNext;
...@@ -1716,25 +1808,25 @@ auto Category::erase(iterator pos) -> iterator ...@@ -1716,25 +1808,25 @@ auto Category::erase(iterator pos) -> iterator
// links are created based on the _pdbx_item_linked_group_list entries // links are created based on the _pdbx_item_linked_group_list entries
// in mmcif_pdbx_v50.dic dictionary. // in mmcif_pdbx_v50.dic dictionary.
// //
// For each link group in _pdbx_item_linked_group_list // For each link group in _pdbx_item_linked_group_list
// a std::set of keys from one category is mapped to another. // a std::set of keys from one category is mapped to another.
// If all values in a child are the same as the specified parent ones // If all values in a child are the same as the specified parent ones
// the child is removed as well, recursively of course. // the child is removed as well, recursively of course.
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)
continue; continue;
Condition cond; Condition cond;
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,16 +1958,16 @@ bool Category::isOrphan(Row r) ...@@ -1866,16 +1958,16 @@ 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)
continue; continue;
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,18 +1994,18 @@ bool Category::hasChildren(Row r) const ...@@ -1902,18 +1994,18 @@ 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)
continue; continue;
Condition cond; Condition cond;
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,18 +2025,18 @@ bool Category::hasParents(Row r) const ...@@ -1933,18 +2025,18 @@ 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)
continue; continue;
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);
} }
...@@ -1957,29 +2049,29 @@ bool Category::hasParents(Row r) const ...@@ -1957,29 +2049,29 @@ 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;
Condition cond; Condition cond;
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,29 +2085,29 @@ RowSet Category::getChildren(Row r, Category& childCat) ...@@ -1993,29 +2085,29 @@ 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;
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);
} }
...@@ -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())
...@@ -2045,7 +2137,7 @@ RowSet Category::getLinked(Row r, Category& cat) ...@@ -2045,7 +2137,7 @@ RowSet Category::getLinked(Row r, Category& cat)
bool Category::isValid() bool Category::isValid()
{ {
bool result = true; bool result = true;
if (mValidator == nullptr) if (mValidator == nullptr)
throw std::runtime_error("no Validator specified"); throw std::runtime_error("no Validator specified");
...@@ -2055,16 +2147,16 @@ bool Category::isValid() ...@@ -2055,16 +2147,16 @@ bool Category::isValid()
std::cerr << "Skipping validation of empty Category " << mName << std::endl; std::cerr << "Skipping validation of empty Category " << mName << std::endl;
return true; return true;
} }
if (mCatValidator == nullptr) if (mCatValidator == nullptr)
{ {
mValidator->reportError("undefined Category " + mName, false); mValidator->reportError("undefined Category " + mName, false);
return false; return false;
} }
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)
...@@ -2072,48 +2164,48 @@ bool Category::isValid() ...@@ -2072,48 +2164,48 @@ bool Category::isValid()
mValidator->reportError("Field " + col.mName + " is not valid in Category " + mName, false); mValidator->reportError("Field " + col.mName + " is not valid in Category " + mName, false);
result = false; result = false;
} }
col.mValidator = iv; col.mValidator = iv;
mandatory.erase(col.mName); mandatory.erase(col.mName);
} }
if (not mandatory.empty()) if (not mandatory.empty())
{ {
mValidator->reportError("In Category " + mName + " the following mandatory fields are missing: " + ba::join(mandatory, ", "), false); mValidator->reportError("In Category " + mName + " the following mandatory fields are missing: " + ba::join(mandatory, ", "), false);
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;
for (auto ri = mHead; ri != nullptr; ri = ri->mNext) for (auto ri = mHead; ri != nullptr; ri = ri->mNext)
{ {
for (size_t cix = 0; cix < mColumns.size(); ++cix) for (size_t cix = 0; cix < mColumns.size(); ++cix)
{ {
bool seen = false; bool seen = false;
auto iv = mColumns[cix].mValidator; auto iv = mColumns[cix].mValidator;
if (iv == nullptr) if (iv == nullptr)
{ {
mValidator->reportError("invalid field " + mColumns[cix].mName + " for Category " + mName, false); mValidator->reportError("invalid field " + mColumns[cix].mName + " for Category " + mName, false);
result = false; result = false;
continue; continue;
} }
for (auto vi = ri->mValues; vi != nullptr; vi = vi->mNext) for (auto vi = ri->mValues; vi != nullptr; vi = vi->mNext)
{ {
if (vi->mColumnIndex == cix) if (vi->mColumnIndex == cix)
...@@ -2123,17 +2215,17 @@ bool Category::isValid() ...@@ -2123,17 +2215,17 @@ 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;
} }
} }
} }
if (seen or ri != mHead) if (seen or ri != mHead)
continue; continue;
if (iv != nullptr and iv->mMandatory) if (iv != nullptr and iv->mMandatory)
{ {
mValidator->reportError("missing mandatory field " + mColumns[cix].mName + " for Category " + mName, false); mValidator->reportError("missing mandatory field " + mColumns[cix].mName + " for Category " + mName, false);
...@@ -2141,34 +2233,34 @@ bool Category::isValid() ...@@ -2141,34 +2233,34 @@ bool Category::isValid()
} }
} }
} }
return result; return result;
} }
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;
if (missing) if (missing)
{ {
std::cerr << "Links for " << linkValidator->mLinkGroupLabel << " are incomplete" << std::endl std::cerr << "Links for " << linkValidator->mLinkGroupLabel << " are incomplete" << std::endl
<< " There are " << missing << " items in " << mName << " that don't have matching parent items in " << parent->mName << std::endl; << " There are " << missing << " items in " << mName << " that don't have matching parent items in " << parent->mName << std::endl;
} }
} }
} }
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");
...@@ -2179,12 +2271,12 @@ iset Category::fields() const ...@@ -2179,12 +2271,12 @@ iset Category::fields() const
{ {
if (mValidator == nullptr) if (mValidator == nullptr)
throw std::runtime_error("No Validator specified"); throw std::runtime_error("No Validator specified");
if (mCatValidator == nullptr) if (mCatValidator == nullptr)
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;
} }
...@@ -2193,10 +2285,10 @@ iset Category::mandatoryFields() const ...@@ -2193,10 +2285,10 @@ iset Category::mandatoryFields() const
{ {
if (mValidator == nullptr) if (mValidator == nullptr)
throw std::runtime_error("No Validator specified"); throw std::runtime_error("No Validator specified");
if (mCatValidator == nullptr) if (mCatValidator == nullptr)
mValidator->reportError("undefined Category", true); mValidator->reportError("undefined Category", true);
return mCatValidator->mMandatoryFields; return mCatValidator->mMandatoryFields;
} }
...@@ -2204,28 +2296,121 @@ iset Category::keyFields() const ...@@ -2204,28 +2296,121 @@ iset Category::keyFields() const
{ {
if (mValidator == nullptr) if (mValidator == nullptr)
throw std::runtime_error("No Validator specified"); throw std::runtime_error("No Validator specified");
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
{ {
if (mValidator == nullptr) if (mValidator == nullptr)
throw std::runtime_error("No Validator specified"); throw std::runtime_error("No Validator specified");
if (mCatValidator == nullptr) if (mCatValidator == nullptr)
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,52 +2426,27 @@ std::set<size_t> Category::keyFieldsByIndex() const ...@@ -2241,52 +2426,27 @@ 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
{
ba::replace_all(value, "\n;", "\n\\;");
if (offset > 0)
os << std::endl;
os << ';' << value;
if (not ba::ends_with(value, "\n"))
os << std::endl;
os << ';' << std::endl;
offset = 0;
}
else if (isUnquotedString(value.c_str()))
{ {
os << value; if (value.find('\n') != std::string::npos or width == 0 or value.length() >= 132) // write as text field
if (value.length() < width)
{
os << std::string(width - value.length(), ' ');
offset += width;
}
else
{ {
os << ' '; ba::replace_all(value, "\n;", "\n\\;");
offset += value.length() + 1;
if (offset > 0)
os << std::endl;
os << ';' << value;
if (not ba::ends_with(value, "\n"))
os << std::endl;
os << ';' << std::endl;
offset = 0;
} }
} else if (isUnquotedString(value.c_str()))
else
{
bool done = false;
for (char q: { '\'', '"'})
{ {
auto p = value.find(q); // see if we can use the quote character os << value;
while (p != std::string::npos and isNonBlank(value[p + 1]) and value[p + 1] != q)
p = value.find(q, p + 1);
if (p != std::string::npos)
continue;
os << q << value << q;
if (value.length() + 2 < width) if (value.length() < width)
{ {
os << std::string(width - value.length() - 2, ' '); os << std::string(width - value.length(), ' ');
offset += width; offset += width;
} }
else else
...@@ -2294,47 +2454,72 @@ size_t writeValue(std::ostream& os, std::string value, size_t offset, size_t wid ...@@ -2294,47 +2454,72 @@ size_t writeValue(std::ostream& os, std::string value, size_t offset, size_t wid
os << ' '; os << ' ';
offset += value.length() + 1; offset += value.length() + 1;
} }
done = true;
break;
} }
else
if (not done)
{ {
if (offset > 0) bool done = false;
os << std::endl; for (char q : {'\'', '"'})
os << ';' << value << std::endl {
<< ';' << std::endl; auto p = value.find(q); // see if we can use the quote character
offset = 0; while (p != std::string::npos and isNonBlank(value[p + 1]) and value[p + 1] != q)
p = value.find(q, p + 1);
if (p != std::string::npos)
continue;
os << q << value << q;
if (value.length() + 2 < width)
{
os << std::string(width - value.length() - 2, ' ');
offset += width;
}
else
{
os << ' ';
offset += value.length() + 1;
}
done = true;
break;
}
if (not done)
{
if (offset > 0)
os << std::endl;
os << ';' << value << std::endl
<< ';' << std::endl;
offset = 0;
}
} }
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;
// If the first Row has a next, we need a loop_ // If the first Row has a next, we need a loop_
bool needLoop = (mHead->mNext != nullptr); bool needLoop = (mHead->mNext != nullptr);
if (needLoop) if (needLoop)
{ {
os << "loop_" << std::endl; os << "loop_" << std::endl;
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);
} }
for (auto Row = mHead; Row != nullptr; Row = Row->mNext) for (auto Row = mHead; Row != nullptr; Row = Row->mNext)
{ {
for (auto v = Row->mValues; v != nullptr; v = v->mNext) for (auto v = Row->mValues; v != nullptr; v = v->mNext)
...@@ -2342,7 +2527,7 @@ void Category::write(std::ostream& os, const std::vector<size_t>& order, bool in ...@@ -2342,7 +2527,7 @@ void Category::write(std::ostream& os, const std::vector<size_t>& order, bool in
if (strchr(v->mText, '\n') == nullptr) if (strchr(v->mText, '\n') == nullptr)
{ {
size_t l = strlen(v->mText); size_t l = strlen(v->mText);
if (not isUnquotedString(v->mText)) if (not isUnquotedString(v->mText))
l += 2; l += 2;
...@@ -2354,15 +2539,15 @@ void Category::write(std::ostream& os, const std::vector<size_t>& order, bool in ...@@ -2354,15 +2539,15 @@ void Category::write(std::ostream& os, const std::vector<size_t>& order, bool in
} }
} }
} }
for (auto Row = mHead; Row != nullptr; Row = Row->mNext) // loop over rows for (auto Row = mHead; Row != nullptr; Row = Row->mNext) // loop over rows
{ {
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];
std::string s; std::string s;
for (auto iv = Row->mValues; iv != nullptr; iv = iv->mNext) for (auto iv = Row->mValues; iv != nullptr; iv = iv->mNext)
{ {
...@@ -2372,10 +2557,10 @@ void Category::write(std::ostream& os, const std::vector<size_t>& order, bool in ...@@ -2372,10 +2557,10 @@ void Category::write(std::ostream& os, const std::vector<size_t>& order, bool in
break; break;
} }
} }
if (s.empty()) if (s.empty())
s = "?"; s = "?";
size_t l = s.length(); size_t l = s.length();
if (not isUnquotedString(s.c_str())) if (not isUnquotedString(s.c_str()))
l += 2; l += 2;
...@@ -2387,41 +2572,41 @@ void Category::write(std::ostream& os, const std::vector<size_t>& order, bool in ...@@ -2387,41 +2572,41 @@ void Category::write(std::ostream& os, const std::vector<size_t>& order, bool in
os << std::endl; os << std::endl;
offset = 0; offset = 0;
} }
offset = detail::writeValue(os, s, offset, w); offset = detail::writeValue(os, s, offset, w);
if (offset >= 132) if (offset >= 132)
{ {
os << std::endl; os << std::endl;
offset = 0; offset = 0;
} }
} }
if (offset > 0) if (offset > 0)
os << std::endl; os << std::endl;
} }
} }
else else
{ {
// 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;
if (l < tag.length()) if (l < tag.length())
l = tag.length(); l = tag.length();
} }
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, ' ');
std::string s; std::string s;
for (auto iv = mHead->mValues; iv != nullptr; iv = iv->mNext) for (auto iv = mHead->mValues; iv != nullptr; iv = iv->mNext)
{ {
...@@ -2431,10 +2616,10 @@ void Category::write(std::ostream& os, const std::vector<size_t>& order, bool in ...@@ -2431,10 +2616,10 @@ void Category::write(std::ostream& os, const std::vector<size_t>& order, bool in
break; break;
} }
} }
if (s.empty()) if (s.empty())
s = "?"; s = "?";
size_t offset = l; size_t offset = l;
if (s.length() + l >= kMaxLineLength) if (s.length() + l >= kMaxLineLength)
{ {
...@@ -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,26 +2682,26 @@ void Category::update_value(RowSet &&rows, const std::string &tag, const std::st ...@@ -2497,26 +2682,26 @@ 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");
} }
if (oldValue == value) // no need to do anything if (oldValue == value) // no need to do anything
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)
...@@ -2527,7 +2712,7 @@ void Category::update_value(RowSet &&rows, const std::string &tag, const std::st ...@@ -2527,7 +2712,7 @@ void Category::update_value(RowSet &&rows, const std::string &tag, const std::st
Condition cond; Condition cond;
std::string childTag; std::string childTag;
for (size_t ix = 0; ix < linked->mParentKeys.size(); ++ix) for (size_t ix = 0; ix < linked->mParentKeys.size(); ++ix)
{ {
std::string pk = linked->mParentKeys[ix]; std::string pk = linked->mParentKeys[ix];
...@@ -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,10 +2739,10 @@ void Category::update_value(RowSet &&rows, const std::string &tag, const std::st ...@@ -2554,10 +2739,10 @@ 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;
for (size_t ix = 0; ix < linked->mParentKeys.size(); ++ix) for (size_t ix = 0; ix < linked->mParentKeys.size(); ++ix)
{ {
std::string pk = linked->mParentKeys[ix]; std::string pk = linked->mParentKeys[ix];
...@@ -2577,7 +2762,7 @@ void Category::update_value(RowSet &&rows, const std::string &tag, const std::st ...@@ -2577,7 +2762,7 @@ void Category::update_value(RowSet &&rows, const std::string &tag, const std::st
// oops, we need to split this child, unless a row already exists for the new value // oops, we need to split this child, unless a row already exists for the new value
Condition check; Condition check;
for (size_t ix = 0; ix < linked->mParentKeys.size(); ++ix) for (size_t ix = 0; ix < linked->mParentKeys.size(); ++ix)
{ {
std::string pk = linked->mParentKeys[ix]; std::string pk = linked->mParentKeys[ix];
...@@ -2591,7 +2776,7 @@ void Category::update_value(RowSet &&rows, const std::string &tag, const std::st ...@@ -2591,7 +2776,7 @@ void Category::update_value(RowSet &&rows, const std::string &tag, const std::st
check = std::move(check) && Key(ck) == parent[pk].c_str(); check = std::move(check) && Key(ck) == parent[pk].c_str();
} }
if (childCat->exists(std::move(check))) // phew..., narrow escape if (childCat->exists(std::move(check))) // phew..., narrow escape
continue; continue;
// create the actual copy, if we can... // create the actual copy, if we can...
...@@ -2605,7 +2790,7 @@ void Category::update_value(RowSet &&rows, const std::string &tag, const std::st ...@@ -2605,7 +2790,7 @@ void Category::update_value(RowSet &&rows, const std::string &tag, const std::st
} }
} }
// cannot update this... // cannot update this...
if (cif::VERBOSE) if (cif::VERBOSE)
std::cerr << "Cannot update child " << childCat->mName << "." << childTag << " with value " << value << std::endl; std::cerr << "Cannot update child " << childCat->mName << "." << childTag << " with value " << value << std::endl;
} }
...@@ -2613,19 +2798,19 @@ void Category::update_value(RowSet &&rows, const std::string &tag, const std::st ...@@ -2613,19 +2798,19 @@ void Category::update_value(RowSet &&rows, const std::string &tag, const std::st
// finally, update the children // finally, update the children
if (not process.empty()) if (not process.empty())
childCat->update_value(std::move(process), childTag, value); childCat->update_value(std::move(process), childTag, value);
} }
} }
} }
// -------------------------------------------------------------------- // --------------------------------------------------------------------
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)
...@@ -2694,7 +2879,7 @@ void Row::assign(const std::vector<Item>& values) ...@@ -2694,7 +2879,7 @@ void Row::assign(const std::vector<Item>& values)
std::string childTag; std::string childTag;
std::vector<Item> newValues; std::vector<Item> newValues;
for (size_t ix = 0; ix < linked->mParentKeys.size(); ++ix) for (size_t ix = 0; ix < linked->mParentKeys.size(); ++ix)
{ {
std::string pk = linked->mParentKeys[ix]; std::string pk = linked->mParentKeys[ix];
...@@ -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));
...@@ -2758,8 +2943,8 @@ void Row::assign(size_t column, const std::string& value, bool skipUpdateLinked) ...@@ -2758,8 +2943,8 @@ void Row::assign(size_t column, const std::string& value, bool skipUpdateLinked)
break; break;
} }
} }
if (oldValue != nullptr and value == oldValue) // no need to update if (oldValue != nullptr and value == oldValue) // no need to update
return; return;
std::string oldStrValue = oldValue ? oldValue : ""; std::string oldStrValue = oldValue ? oldValue : "";
...@@ -2770,10 +2955,10 @@ void Row::assign(size_t column, const std::string& value, bool skipUpdateLinked) ...@@ -2770,10 +2955,10 @@ void Row::assign(size_t column, const std::string& value, bool skipUpdateLinked)
// If the field is part of the Key for this Category, remove it from the index // If the field is part of the Key for this Category, remove it from the index
// before updating // before updating
bool reinsert = false; bool reinsert = false;
if (not skipUpdateLinked and // an update of an Item's value if (not skipUpdateLinked and // an update of an Item's value
cat->mIndex != nullptr and cat->keyFieldsByIndex().count(column)) cat->mIndex != nullptr and cat->keyFieldsByIndex().count(column))
{ {
reinsert = cat->mIndex->find(mData); reinsert = cat->mIndex->find(mData);
...@@ -2784,7 +2969,7 @@ void Row::assign(size_t column, const std::string& value, bool skipUpdateLinked) ...@@ -2784,7 +2969,7 @@ void Row::assign(size_t column, const std::string& value, bool skipUpdateLinked)
// first remove old value with cix // first remove old value with cix
if (mData->mValues == nullptr) if (mData->mValues == nullptr)
; // nothing to do ; // nothing to do
else if (mData->mValues->mColumnIndex == column) else if (mData->mValues->mColumnIndex == column)
{ {
auto iv = mData->mValues; auto iv = mData->mValues;
...@@ -2802,7 +2987,7 @@ void Row::assign(size_t column, const std::string& value, bool skipUpdateLinked) ...@@ -2802,7 +2987,7 @@ void Row::assign(size_t column, const std::string& value, bool skipUpdateLinked)
iv->mNext = nv->mNext; iv->mNext = nv->mNext;
nv->mNext = nullptr; nv->mNext = nullptr;
delete nv; delete nv;
break; break;
} }
} }
...@@ -2810,8 +2995,8 @@ void Row::assign(size_t column, const std::string& value, bool skipUpdateLinked) ...@@ -2810,8 +2995,8 @@ 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;
else else
...@@ -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)
...@@ -2844,7 +3029,7 @@ void Row::assign(size_t column, const std::string& value, bool skipUpdateLinked) ...@@ -2844,7 +3029,7 @@ void Row::assign(size_t column, const std::string& value, bool skipUpdateLinked)
Condition cond; Condition cond;
std::string childTag; std::string childTag;
for (size_t ix = 0; ix < linked->mParentKeys.size(); ++ix) for (size_t ix = 0; ix < linked->mParentKeys.size(); ++ix)
{ {
std::string pk = linked->mParentKeys[ix]; std::string pk = linked->mParentKeys[ix];
...@@ -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
...@@ -2881,7 +3066,7 @@ void Row::assign(size_t column, const std::string& value, bool skipUpdateLinked) ...@@ -2881,7 +3066,7 @@ void Row::assign(size_t column, const std::string& value, bool skipUpdateLinked)
// we then skip this renam // we then skip this renam
Condition cond_n; Condition cond_n;
for (size_t ix = 0; ix < linked->mParentKeys.size(); ++ix) for (size_t ix = 0; ix < linked->mParentKeys.size(); ++ix)
{ {
std::string pk = linked->mParentKeys[ix]; std::string pk = linked->mParentKeys[ix];
...@@ -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
...@@ -2906,17 +3091,17 @@ void Row::assign(size_t column, const std::string& value, bool skipUpdateLinked) ...@@ -2906,17 +3091,17 @@ void Row::assign(size_t column, const std::string& value, bool skipUpdateLinked)
{ {
if (cif::VERBOSE) if (cif::VERBOSE)
std::cerr << "Will not rename in child category since there are already rows that link to the parent" << std::endl; std::cerr << "Will not rename in child category since there are already rows that link to the parent" << std::endl;
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");
...@@ -2924,14 +3109,14 @@ void Row::swap(size_t cix, ItemRow* a, ItemRow* b) ...@@ -2924,14 +3109,14 @@ void Row::swap(size_t cix, ItemRow* a, ItemRow* b)
assert(a->mCategory == b->mCategory); assert(a->mCategory == b->mCategory);
if (a->mCategory != b->mCategory) if (a->mCategory != b->mCategory)
throw std::logic_error("Categories not same in swap"); throw std::logic_error("Categories not same in swap");
auto cat = a->mCategory; auto cat = a->mCategory;
// If the field is part of the Key for this Category, remove it from the index // If the field is part of the Key for this Category, remove it from the index
// before updating // before updating
bool reinsert = false; bool reinsert = false;
if (cat->mIndex != nullptr and cat->keyFieldsByIndex().count(cix)) if (cat->mIndex != nullptr and cat->keyFieldsByIndex().count(cix))
{ {
reinsert = true; reinsert = true;
...@@ -2939,11 +3124,11 @@ void Row::swap(size_t cix, ItemRow* a, ItemRow* b) ...@@ -2939,11 +3124,11 @@ 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)
; ;
else if (a->mValues->mColumnIndex == cix) else if (a->mValues->mColumnIndex == cix)
...@@ -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,18 +3202,18 @@ void Row::swap(size_t cix, ItemRow* a, ItemRow* b) ...@@ -3018,18 +3202,18 @@ 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;
auto childCat = cat->db().get(link->mChildCategory); auto childCat = cat->db().get(link->mChildCategory);
if (childCat == nullptr or childCat->empty()) if (childCat == nullptr or childCat->empty())
continue; continue;
auto childCatValidator = childCat->getCatValidator(); auto childCatValidator = childCat->getCatValidator();
if (childCatValidator == nullptr) if (childCatValidator == nullptr)
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;
} }
...@@ -3090,7 +3274,7 @@ void Row::swap(size_t cix, ItemRow* a, ItemRow* b) ...@@ -3090,7 +3274,7 @@ void Row::swap(size_t cix, ItemRow* a, ItemRow* b)
// if (VERBOSE > 1) // if (VERBOSE > 1)
// std::cerr << "Fixing link from " << cat->mName << " to " << childCat->mName << " with " << std::endl // std::cerr << "Fixing link from " << cat->mName << " to " << childCat->mName << " with " << std::endl
// << cond[ab] << std::endl; // << cond[ab] << std::endl;
rs.push_back(childCat->find(std::move(cond[ab]))); rs.push_back(childCat->find(std::move(cond[ab])));
} }
...@@ -3098,17 +3282,17 @@ void Row::swap(size_t cix, ItemRow* a, ItemRow* b) ...@@ -3098,17 +3282,17 @@ 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;
if (n == link->mChildKeys.size()) if (n == link->mChildKeys.size())
{ {
if (VERBOSE > 1) if (VERBOSE > 1)
...@@ -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,21 +3347,22 @@ void Row::lineNr(uint32_t l) ...@@ -3163,21 +3347,22 @@ 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;
if (mPtr != nullptr) if (mPtr != nullptr)
fetch(); fetch();
return *this; return *this;
} }
...@@ -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,10 +3428,10 @@ File::~File() ...@@ -3242,10 +3428,10 @@ File::~File()
delete mValidator; delete mValidator;
} }
void File::append(Datablock* e) void File::append(Datablock *e)
{ {
e->setValidator(mValidator); e->setValidator(mValidator);
if (mHead == nullptr) if (mHead == nullptr)
mHead = e; mHead = e;
else else
...@@ -3261,23 +3447,23 @@ void File::append(Datablock* e) ...@@ -3261,23 +3447,23 @@ void File::append(Datablock* e)
ie->mNext = e; ie->mNext = e;
break; break;
} }
ie = ie->mNext; ie = ie->mNext;
} }
} }
} }
void File::load(const std::string& p) void File::load(const std::string &p)
{ {
fs::path path(p); fs::path path(p);
std::ifstream inFile(p, std::ios_base::in | std::ios_base::binary); std::ifstream inFile(p, std::ios_base::in | std::ios_base::binary);
if (not inFile.is_open()) if (not inFile.is_open())
throw std::runtime_error("Could not open file: " + path.string()); throw std::runtime_error("Could not open file: " + path.string());
io::filtering_stream<io::input> in; io::filtering_stream<io::input> in;
std::string ext; std::string ext;
if (path.extension() == ".bz2") if (path.extension() == ".bz2")
{ {
in.push(io::bzip2_decompressor()); in.push(io::bzip2_decompressor());
...@@ -3288,27 +3474,27 @@ void File::load(const std::string& p) ...@@ -3288,27 +3474,27 @@ void File::load(const std::string& p)
in.push(io::gzip_decompressor()); in.push(io::gzip_decompressor());
ext = path.stem().extension().string(); ext = path.stem().extension().string();
} }
in.push(inFile); in.push(inFile);
try try
{ {
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);
std::ofstream outFile(p, std::ios_base::out | std::ios_base::binary); std::ofstream outFile(p, std::ios_base::out | std::ios_base::binary);
io::filtering_stream<io::output> out; io::filtering_stream<io::output> out;
if (path.extension() == ".gz") if (path.extension() == ".gz")
{ {
out.push(io::gzip_compressor()); out.push(io::gzip_compressor());
...@@ -3319,19 +3505,19 @@ void File::save(const std::string& p) ...@@ -3319,19 +3505,19 @@ void File::save(const std::string& p)
out.push(io::bzip2_compressor()); out.push(io::bzip2_compressor());
path = path.stem(); path = path.stem();
} }
out.push(outFile); out.push(outFile);
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);
p.parseFile(); p.parseFile();
if (saved != nullptr) if (saved != nullptr)
{ {
setValidator(saved); setValidator(saved);
...@@ -3339,14 +3525,14 @@ void File::load(std::istream& is) ...@@ -3339,14 +3525,14 @@ 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);
p.parseSingleDatablock(datablock); p.parseSingleDatablock(datablock);
if (saved != nullptr) if (saved != nullptr)
{ {
setValidator(saved); setValidator(saved);
...@@ -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)
...@@ -3398,7 +3584,7 @@ bool File::isValid() ...@@ -3398,7 +3584,7 @@ bool File::isValid()
{ {
if (VERBOSE) if (VERBOSE)
std::cerr << "No dictionary loaded explicitly, loading default" << std::endl; std::cerr << "No dictionary loaded explicitly, loading default" << std::endl;
loadDictionary(); loadDictionary();
} }
...@@ -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);
...@@ -3437,7 +3623,7 @@ void File::loadDictionary(const char* dict) ...@@ -3437,7 +3623,7 @@ void File::loadDictionary(const char* dict)
if (data) if (data)
loadDictionary(*data); loadDictionary(*data);
else else
{ {
// might be a compressed dictionary on disk // might be a compressed dictionary on disk
fs::path p = dict; fs::path p = 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
...@@ -795,7 +795,7 @@ std::ostream &operator<<(std::ostream &os, const Atom &atom) ...@@ -795,7 +795,7 @@ std::ostream &operator<<(std::ostream &os, const Atom &atom)
// First constructor used to be for waters only, but now accepts sugars as well. // First constructor used to be for waters only, but now accepts sugars as well.
Residue::Residue(const Structure &structure, const std::string &compoundID, Residue::Residue(const Structure &structure, const std::string &compoundID,
const std::string &asymID, const std::string &authSeqID) const std::string &asymID, const std::string &authSeqID)
: mStructure(&structure) : mStructure(&structure)
, mCompoundID(compoundID) , mCompoundID(compoundID)
, mAsymID(asymID) , mAsymID(asymID)
...@@ -804,7 +804,7 @@ Residue::Residue(const Structure &structure, const std::string &compoundID, ...@@ -804,7 +804,7 @@ Residue::Residue(const Structure &structure, const std::string &compoundID,
for (auto &a : mStructure->atoms()) for (auto &a : mStructure->atoms())
{ {
if (a.labelAsymID() != mAsymID or if (a.labelAsymID() != mAsymID or
a.labelCompID() != mCompoundID) a.labelCompID() != mCompoundID)
continue; continue;
if (compoundID == "HOH") if (compoundID == "HOH")
...@@ -830,7 +830,7 @@ Residue::Residue(const Structure &structure, const std::string &compoundID, cons ...@@ -830,7 +830,7 @@ Residue::Residue(const Structure &structure, const std::string &compoundID, cons
} }
Residue::Residue(const Structure &structure, const std::string &compoundID, Residue::Residue(const Structure &structure, const std::string &compoundID,
const std::string &asymID, int seqID, const std::string &authSeqID) const std::string &asymID, int seqID, const std::string &authSeqID)
: mStructure(&structure) : mStructure(&structure)
, mCompoundID(compoundID) , mCompoundID(compoundID)
, mAsymID(asymID) , mAsymID(asymID)
...@@ -845,7 +845,7 @@ Residue::Residue(const Structure &structure, const std::string &compoundID, ...@@ -845,7 +845,7 @@ Residue::Residue(const Structure &structure, const std::string &compoundID,
continue; continue;
if (a.labelAsymID() != mAsymID or if (a.labelAsymID() != mAsymID or
a.labelCompID() != mCompoundID) a.labelCompID() != mCompoundID)
continue; continue;
mAtoms.push_back(a); mAtoms.push_back(a);
...@@ -968,7 +968,7 @@ std::string Residue::unique_alt_id() const ...@@ -968,7 +968,7 @@ std::string Residue::unique_alt_id() const
throw std::runtime_error("Invalid Residue object"); throw std::runtime_error("Invalid Residue object");
auto firstAlt = std::find_if(mAtoms.begin(), mAtoms.end(), [](auto &a) auto firstAlt = std::find_if(mAtoms.begin(), mAtoms.end(), [](auto &a)
{ return not a.labelAltID().empty(); }); { return not a.labelAltID().empty(); });
return firstAlt != mAtoms.end() ? firstAlt->labelAltID() : ""; return firstAlt != mAtoms.end() ? firstAlt->labelAltID() : "";
} }
...@@ -1104,7 +1104,7 @@ std::tuple<Point, float> Residue::centerAndRadius() const ...@@ -1104,7 +1104,7 @@ std::tuple<Point, float> Residue::centerAndRadius() const
bool Residue::hasAlternateAtoms() const bool Residue::hasAlternateAtoms() const
{ {
return std::find_if(mAtoms.begin(), mAtoms.end(), [](const Atom &atom) return std::find_if(mAtoms.begin(), mAtoms.end(), [](const Atom &atom)
{ return atom.isAlternate(); }) != mAtoms.end(); { return atom.isAlternate(); }) != mAtoms.end();
} }
std::set<std::string> Residue::getAtomIDs() const std::set<std::string> Residue::getAtomIDs() const
...@@ -1476,7 +1476,7 @@ float Monomer::chiralVolume() const ...@@ -1476,7 +1476,7 @@ float Monomer::chiralVolume() const
auto atom3 = atomByID("CD2"); auto atom3 = atomByID("CD2");
result = DotProduct(atom1.location() - centre.location(), result = DotProduct(atom1.location() - centre.location(),
CrossProduct(atom2.location() - centre.location(), atom3.location() - centre.location())); CrossProduct(atom2.location() - centre.location(), atom3.location() - centre.location()));
} }
else if (mCompoundID == "VAL") else if (mCompoundID == "VAL")
{ {
...@@ -1486,7 +1486,7 @@ float Monomer::chiralVolume() const ...@@ -1486,7 +1486,7 @@ float Monomer::chiralVolume() const
auto atom3 = atomByID("CG2"); auto atom3 = atomByID("CG2");
result = DotProduct(atom1.location() - centre.location(), result = DotProduct(atom1.location() - centre.location(),
CrossProduct(atom2.location() - centre.location(), atom3.location() - centre.location())); CrossProduct(atom2.location() - centre.location(), atom3.location() - centre.location()));
} }
return result; return result;
...@@ -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)
{ {
...@@ -1864,13 +1875,13 @@ void Structure::updateAtomIndex() ...@@ -1864,13 +1875,13 @@ void Structure::updateAtomIndex()
iota(mAtomIndex.begin(), mAtomIndex.end(), 0); iota(mAtomIndex.begin(), mAtomIndex.end(), 0);
sort(mAtomIndex.begin(), mAtomIndex.end(), [this](size_t a, size_t b) sort(mAtomIndex.begin(), mAtomIndex.end(), [this](size_t a, size_t b)
{ return mAtoms[a].id() < mAtoms[b].id(); }); { return mAtoms[a].id() < mAtoms[b].id(); });
} }
void Structure::sortAtoms() void Structure::sortAtoms()
{ {
sort(mAtoms.begin(), mAtoms.end(), [](auto &a, auto &b) sort(mAtoms.begin(), mAtoms.end(), [](auto &a, auto &b)
{ return a.compare(b) < 0; }); { return a.compare(b) < 0; });
int id = 1; int id = 1;
for (auto &atom : mAtoms) for (auto &atom : mAtoms)
...@@ -1914,8 +1925,8 @@ AtomView Structure::waters() const ...@@ -1914,8 +1925,8 @@ AtomView Structure::waters() const
Atom Structure::getAtomByID(std::string id) const Atom Structure::getAtomByID(std::string id) const
{ {
auto i = std::lower_bound(mAtomIndex.begin(), mAtomIndex.end(), auto i = std::lower_bound(mAtomIndex.begin(), mAtomIndex.end(),
id, [this](auto &a, auto &b) id, [this](auto &a, auto &b)
{ return mAtoms[a].id() < b; }); { return mAtoms[a].id() < b; });
// auto i = find_if(mAtoms.begin(), mAtoms.end(), // auto i = find_if(mAtoms.begin(), mAtoms.end(),
// [&id](auto& a) { return a.id() == id; }); // [&id](auto& a) { return a.id() == id; });
...@@ -1931,10 +1942,10 @@ Atom Structure::getAtomByLabel(const std::string &atomID, const std::string &asy ...@@ -1931,10 +1942,10 @@ Atom Structure::getAtomByLabel(const std::string &atomID, const std::string &asy
for (auto &a : mAtoms) for (auto &a : mAtoms)
{ {
if (a.labelAtomID() == atomID and if (a.labelAtomID() == atomID and
a.labelAsymID() == asymID and a.labelAsymID() == asymID and
a.labelCompID() == compID and a.labelCompID() == compID and
a.labelSeqID() == seqID and a.labelSeqID() == seqID and
a.labelAltID() == altID) a.labelAltID() == altID)
{ {
return a; return a;
} }
...@@ -2013,12 +2024,12 @@ std::tuple<char, int, char> Structure::MapLabelToAuth( ...@@ -2013,12 +2024,12 @@ std::tuple<char, int, char> Structure::MapLabelToAuth(
try try
{ {
result = std::make_tuple(auth_asym_id.front(), std::stoi(auth_seq_num), result = std::make_tuple(auth_asym_id.front(), std::stoi(auth_seq_num),
pdb_ins_code.empty() ? ' ' : pdb_ins_code.front()); pdb_ins_code.empty() ? ' ' : pdb_ins_code.front());
} }
catch (const std::exception &ex) catch (const std::exception &ex)
{ {
result = std::make_tuple(auth_asym_id.front(), 0, result = std::make_tuple(auth_asym_id.front(), 0,
pdb_ins_code.empty() ? ' ' : pdb_ins_code.front()); pdb_ins_code.empty() ? ' ' : pdb_ins_code.front());
} }
found = true; found = true;
...@@ -2037,7 +2048,7 @@ std::tuple<char, int, char> Structure::MapLabelToAuth( ...@@ -2037,7 +2048,7 @@ std::tuple<char, int, char> Structure::MapLabelToAuth(
r.front().get("pdb_strand_id", "pdb_seq_num", "pdb_ins_code"); r.front().get("pdb_strand_id", "pdb_seq_num", "pdb_ins_code");
result = std::make_tuple(pdb_strand_id.front(), pdb_seq_num, result = std::make_tuple(pdb_strand_id.front(), pdb_seq_num,
pdb_ins_code.empty() ? ' ' : pdb_ins_code.front()); pdb_ins_code.empty() ? ' ' : pdb_ins_code.front());
found = true; found = true;
} }
...@@ -2089,7 +2100,7 @@ std::tuple<std::string, int, std::string, std::string> Structure::MapLabelToPDB( ...@@ -2089,7 +2100,7 @@ std::tuple<std::string, int, std::string, std::string> Structure::MapLabelToPDB(
} }
std::tuple<std::string, int, std::string> Structure::MapPDBToLabel(const std::string &asymID, int seqID, std::tuple<std::string, int, std::string> Structure::MapPDBToLabel(const std::string &asymID, int seqID,
const std::string &compID, const std::string &iCode) const const std::string &compID, const std::string &iCode) const
{ {
auto &db = datablock(); auto &db = datablock();
...@@ -2161,10 +2172,10 @@ void Structure::insertCompound(const std::string &compoundID, bool isEntity) ...@@ -2161,10 +2172,10 @@ void Structure::insertCompound(const std::string &compoundID, bool isEntity)
if (r.empty()) if (r.empty())
{ {
chemComp.emplace({{"id", compoundID}, chemComp.emplace({{"id", compoundID},
{"name", compound->name()}, {"name", compound->name()},
{"formula", compound->formula()}, {"formula", compound->formula()},
{"formula_weight", compound->formulaWeight()}, {"formula_weight", compound->formulaWeight()},
{"type", compound->type()}}); {"type", compound->type()}});
} }
if (isEntity) if (isEntity)
...@@ -2176,13 +2187,13 @@ void Structure::insertCompound(const std::string &compoundID, bool isEntity) ...@@ -2176,13 +2187,13 @@ void Structure::insertCompound(const std::string &compoundID, bool isEntity)
std::string entityID = std::to_string(entity.size() + 1); std::string entityID = std::to_string(entity.size() + 1);
entity.emplace({{"id", entityID}, entity.emplace({{"id", entityID},
{"type", "non-polymer"}, {"type", "non-polymer"},
{"pdbx_description", compound->name()}, {"pdbx_description", compound->name()},
{"formula_weight", compound->formulaWeight()}}); {"formula_weight", compound->formulaWeight()}});
pdbxEntityNonpoly.emplace({{"entity_id", entityID}, pdbxEntityNonpoly.emplace({{"entity_id", entityID},
{"name", compound->name()}, {"name", compound->name()},
{"comp_id", compoundID}}); {"comp_id", compoundID}});
} }
} }
} }
...@@ -2305,7 +2316,7 @@ void Structure::moveAtom(Atom &a, Point p) ...@@ -2305,7 +2316,7 @@ void Structure::moveAtom(Atom &a, Point p)
} }
void Structure::changeResidue(const Residue &res, const std::string &newCompound, void Structure::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)
{ {
using namespace cif::literals; using namespace cif::literals;
...@@ -2333,15 +2344,15 @@ void Structure::changeResidue(const Residue &res, const std::string &newCompound ...@@ -2333,15 +2344,15 @@ void Structure::changeResidue(const Residue &res, const std::string &newCompound
{ {
entityID = entity.getUniqueID(""); entityID = entity.getUniqueID("");
entity.emplace({{"id", entityID}, entity.emplace({{"id", entityID},
{"type", "non-polymer"}, {"type", "non-polymer"},
{"pdbx_description", compound->name()}, {"pdbx_description", compound->name()},
{"formula_weight", compound->formulaWeight()}}); {"formula_weight", compound->formulaWeight()}});
} }
auto &pdbxEntityNonpoly = db["pdbx_entity_nonpoly"]; auto &pdbxEntityNonpoly = db["pdbx_entity_nonpoly"];
pdbxEntityNonpoly.emplace({{"entity_id", entityID}, pdbxEntityNonpoly.emplace({{"entity_id", entityID},
{"name", compound->name()}, {"name", compound->name()},
{"comp_id", newCompound}}); {"comp_id", newCompound}});
auto &pdbxNonPolyScheme = db["pdbx_nonpoly_scheme"]; auto &pdbxNonPolyScheme = db["pdbx_nonpoly_scheme"];
for (auto &nps : pdbxNonPolyScheme.find("asym_id"_key == asymID)) for (auto &nps : pdbxNonPolyScheme.find("asym_id"_key == asymID))
...@@ -2356,10 +2367,10 @@ void Structure::changeResidue(const Residue &res, const std::string &newCompound ...@@ -2356,10 +2367,10 @@ void Structure::changeResidue(const Residue &res, const std::string &newCompound
if (not chemComp.exists(cif::Key("id") == newCompound)) if (not chemComp.exists(cif::Key("id") == newCompound))
{ {
chemComp.emplace({{"id", newCompound}, chemComp.emplace({{"id", newCompound},
{"name", compound->name()}, {"name", compound->name()},
{"formula", compound->formula()}, {"formula", compound->formula()},
{"formula_weight", compound->formulaWeight()}, {"formula_weight", compound->formulaWeight()},
{"type", compound->type()}}); {"type", compound->type()}});
} }
// update the struct_asym for the new entity // update the struct_asym for the new entity
...@@ -2377,7 +2388,7 @@ void Structure::changeResidue(const Residue &res, const std::string &newCompound ...@@ -2377,7 +2388,7 @@ void Structure::changeResidue(const Residue &res, const std::string &newCompound
tie(a1, a2) = a; tie(a1, a2) = a;
auto i = find_if(atoms.begin(), atoms.end(), [&](const Atom &a) auto i = find_if(atoms.begin(), atoms.end(), [&](const Atom &a)
{ return a.labelAtomID() == a1; }); { return a.labelAtomID() == a1; });
if (i == atoms.end()) if (i == atoms.end())
{ {
std::cerr << "Missing atom for atom ID " << a1 << std::endl; std::cerr << "Missing atom for atom ID " << a1 << std::endl;
...@@ -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);
}
...@@ -1664,4 +1664,4 @@ BOOST_AUTO_TEST_CASE(bondmap_2) ...@@ -1664,4 +1664,4 @@ BOOST_AUTO_TEST_CASE(bondmap_2)
mmcif::CompoundFactory::instance().pushDictionary("./UN_.cif"); mmcif::CompoundFactory::instance().pushDictionary("./UN_.cif");
BOOST_CHECK(mmcif::BondMap::atomIDsForCompound("UN_").empty() == false); BOOST_CHECK(mmcif::BondMap::atomIDsForCompound("UN_").empty() == false);
} }
\ No newline at end of file
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