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)
list(APPEND CIFPP_tests
# pdb2cif
rename-compound
structure
unit)
foreach(CIFPP_TEST IN LISTS CIFPP_tests)
......
......@@ -28,14 +28,14 @@
#include <string>
#include <regex>
#include <array>
#include <functional>
#include <iostream>
#include <sstream>
#include <set>
#include <list>
#include <array>
#include <optional>
#include <functional>
#include <regex>
#include <set>
#include <sstream>
#include "cif++/CifUtils.hpp"
......@@ -117,7 +117,7 @@ CIFPP_EXPORT extern int VERBOSE;
class File;
class Datablock;
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 Item;
class Validator;
......@@ -141,43 +141,57 @@ class Item
public:
Item() {}
template<typename T, std::enable_if_t<std::is_arithmetic_v<T>, int> = 0>
Item(const std::string& name, const T& value)
: mName(name), mValue(std::to_string(value)) {}
template <typename T, std::enable_if_t<std::is_arithmetic_v<T>, int> = 0>
Item(const std::string &name, const T &value)
: mName(name)
, mValue(std::to_string(value))
{
}
Item(const std::string& name, const std::string& value)
: mName(name), mValue(value) {}
Item(const std::string &name, const std::string &value)
: mName(name)
, mValue(value)
{
}
Item(const Item& rhs) : mName(rhs.mName), mValue(rhs.mValue) {}
Item(Item&& rhs) noexcept : mName(std::move(rhs.mName)), mValue(std::move(rhs.mValue)) {}
Item(const Item &rhs)
: 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)
{
mName = rhs.mName;
mValue = rhs.mValue;
}
return *this;
}
Item& operator=(Item&& rhs) noexcept
Item &operator=(Item &&rhs) noexcept
{
if (this != &rhs)
{
mName = std::move(rhs.mName);
mValue = std::move(rhs.mValue);
}
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
bool empty() const;
......@@ -187,12 +201,12 @@ class Item
// is_unknown means the field contains '?'
bool is_unknown() const;
size_t length() const { return mValue.length(); }
const char* c_str() const { return mValue.c_str(); }
size_t length() const { return mValue.length(); }
const char *c_str() const { return mValue.c_str(); }
private:
std::string mName;
std::string mValue;
std::string mName;
std::string mValue;
};
// --------------------------------------------------------------------
......@@ -202,54 +216,58 @@ class Datablock
{
public:
friend class File;
using CategoryList = std::list<Category>;
using iterator = CategoryList::iterator;
using const_iterator = CategoryList::const_iterator;
Datablock(const std::string& name);
Datablock(const std::string &name);
~Datablock();
Datablock(const Datablock&) = delete;
Datablock& operator=(const Datablock&) = delete;
Datablock(const Datablock &) = delete;
Datablock &operator=(const Datablock &) = delete;
std::string getName() const { return mName; }
void setName(const std::string& n) { mName = n; }
std::string firstItem(const std::string& tag) const;
std::string getName() const { return mName; }
void setName(const std::string &n) { mName = n; }
iterator begin() { return mCategories.begin(); }
iterator end() { return mCategories.end(); }
std::string firstItem(const std::string &tag) const;
const_iterator begin() const { return mCategories.begin(); }
const_iterator end() const { return mCategories.end(); }
iterator begin() { return mCategories.begin(); }
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();
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
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
void add_software(const std::string& name, const std::string& classification,
const std::string& versionNr, const std::string& versionDate);
void add_software(const std::string &name, const std::string &classification,
const std::string &versionNr, const std::string &versionDate);
private:
friend bool operator==(const Datablock &lhs, const Datablock &rhs);
std::list<Category> mCategories;
std::string mName;
Validator* mValidator;
Datablock* mNext;
friend std::ostream& operator<<(std::ostream &os, const Datablock &data);
private:
std::list<Category> mCategories;
std::string mName;
Validator *mValidator;
Datablock *mNext;
};
// --------------------------------------------------------------------
......@@ -264,20 +282,19 @@ namespace detail
class ItemReference
{
public:
// conversion helper class
template<typename T, typename = void>
template <typename T, typename = void>
struct item_value_as;
template<typename T, std::enable_if_t<std::is_arithmetic_v<T>, int> = 0>
ItemReference& operator=(const T& value)
template <typename T, std::enable_if_t<std::is_arithmetic_v<T>, int> = 0>
ItemReference &operator=(const T &value)
{
this->operator=(std::to_string(value));
return *this;
}
template<typename T>
ItemReference& operator=(const std::optional<T>& value)
template <typename T>
ItemReference &operator=(const std::optional<T> &value)
{
if (value)
this->operator=(*value);
......@@ -286,34 +303,34 @@ namespace detail
return *this;
}
ItemReference& operator=(const std::string& value);
ItemReference &operator=(const std::string &value);
template<typename... Ts>
void os(const Ts& ... v)
template <typename... Ts>
void os(const Ts &...v)
{
std::ostringstream ss;
((ss << v), ...);
this->operator=(ss.str());
}
void swap(ItemReference& b);
template<typename T = std::string>
void swap(ItemReference &b);
template <typename T = std::string>
auto as() const
{
using value_type = std::remove_cv_t<std::remove_reference_t<T>>;
return item_value_as<value_type>::convert(*this);
}
template<typename T>
int compare(const T& value, bool icase) const
template <typename T>
int compare(const T &value, bool icase) const
{
return item_value_as<T>::compare(*this, value, icase);
}
// empty means either null or unknown
bool empty() const;
explicit operator bool() const { return not empty(); }
explicit operator bool() const { return not empty(); }
// is_null means the field contains '.'
bool is_null() const;
......@@ -321,36 +338,45 @@ namespace detail
// is_unknown means the field contains '?'
bool is_unknown() const;
const char* c_str() const;
const char *c_str() const;
// the following returns the defaultValue from either the parameter
// or, if specified, the value from _item_default.value in the dictionary
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(); }
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(); }
private:
private:
friend class ::cif::Row;
ItemReference(const char* name, size_t column, Row& row)
: mName(name), mColumn(column), mRow(row) {}
ItemReference(const char *name, size_t column, Row &row)
: mName(name)
, mColumn(column)
, mRow(row)
{
}
ItemReference(const char* name, size_t column, const Row& row)
: mName(name), mColumn(column), mRow(const_cast<Row&>(row)), mConst(true) {}
ItemReference(const char *name, size_t column, const Row &row)
: mName(name)
, mColumn(column)
, mRow(const_cast<Row &>(row))
, mConst(true)
{
}
const char* mName;
size_t mColumn;
Row& mRow;
bool mConst = false;
const char *mName;
size_t mColumn;
Row &mRow;
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>>>
{
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 = {};
if (not ref.empty())
......@@ -358,11 +384,11 @@ namespace detail
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;
const char* s = ref.c_str();
const char *s = ref.c_str();
if (s == nullptr or *s == 0)
result = 1;
......@@ -383,15 +409,15 @@ namespace detail
result = 1;
}
}
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>>>
{
static T convert(const ItemReference& ref)
static T convert(const ItemReference &ref)
{
T result = {};
if (not ref.empty())
......@@ -399,11 +425,11 @@ namespace detail
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;
const char* s = ref.c_str();
const char *s = ref.c_str();
if (s == nullptr or *s == 0)
result = 1;
......@@ -424,15 +450,15 @@ namespace detail
result = 1;
}
}
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>>>
{
static T convert(const ItemReference& ref)
static T convert(const ItemReference &ref)
{
T result = {};
if (not ref.empty())
......@@ -440,11 +466,11 @@ namespace detail
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;
const char* s = ref.c_str();
const char *s = ref.c_str();
if (s == nullptr or *s == 0)
result = 1;
......@@ -465,15 +491,15 @@ namespace detail
result = 1;
}
}
return result;
}
};
template<typename T>
template <typename 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;
if (ref)
......@@ -481,11 +507,11 @@ namespace detail
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)
return 0;
if (ref.empty())
return -1;
else if (not value)
......@@ -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>>>
{
static bool convert(const ItemReference& ref)
static bool convert(const ItemReference &ref)
{
bool result = false;
if (not ref.empty())
......@@ -506,110 +532,114 @@ namespace detail
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);
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]>
{
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);
}
};
template<>
struct ItemReference::item_value_as<const char*>
template <>
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();
}
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);
}
};
template<>
template <>
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();
}
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());
}
};
// some helper classes to help create tuple result types
template<typename... C>
template <typename... C>
struct getRowResult
{
static constexpr size_t N = sizeof...(C);
getRowResult(const Row& r, std::array<size_t, N>&& columns)
: mRow(r), mColumns(std::move(columns))
getRowResult(const Row &r, std::array<size_t, N> &&columns)
: mRow(r)
, mColumns(std::move(columns))
{
}
const ItemReference operator[](size_t ix) const
{
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
{
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
{
return std::tuple<Ts...>{mRow[mColumns[Is]].template as<Ts>()...};
}
const Row& mRow;
const Row &mRow;
std::array<size_t, N> mColumns;
};
// 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
{
tieWrap(Ts... args) : mVal(args...) {}
tieWrap(Ts... args)
: mVal(args...)
{
}
template<typename RR>
void operator=(const RR&& rr)
template <typename RR>
void operator=(const RR &&rr)
{
// getRowResult will do the conversion, but only if the types
// 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
// 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);
}
std::tuple<Ts...> mVal;
std::tuple<Ts...> mVal;
};
}
} // namespace detail
template<typename... Ts>
auto tie(Ts&... v)
template <typename... Ts>
auto tie(Ts &...v)
{
return detail::tieWrap<Ts&...>(std::forward<Ts&>(v)...);
return detail::tieWrap<Ts &...>(std::forward<Ts &>(v)...);
}
class Row
......@@ -622,19 +652,26 @@ class Row
friend class RowSet;
Row()
: mData(nullptr) {}
: mData(nullptr)
{
}
Row(ItemRow* data)
: mData(data) {}
Row(ItemRow *data)
: mData(data)
{
}
Row(const ItemRow* data)
: mData(const_cast<ItemRow*>(data)), mCascade(false) {}
Row(const ItemRow *data)
: mData(const_cast<ItemRow *>(data))
, mCascade(false)
{
}
Row(const Row& rhs);
Row& operator=(const Row& rhs);
Row(const Row &rhs);
Row &operator=(const Row &rhs);
Row(Row&& rhs);
Row& operator=(Row&& rhs);
Row(Row &&rhs);
Row &operator=(Row &&rhs);
~Row();
......@@ -643,49 +680,53 @@ class Row
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
{
using iterator_category = std::forward_iterator_tag;
using value_type = const Item;
using difference_type = std::ptrdiff_t;
using pointer = value_type*;
using reference = value_type&;
const_iterator(ItemRow* data, ItemValue* ptr);
reference 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:
using pointer = value_type *;
using reference = value_type &;
const_iterator(ItemRow *data, ItemValue *ptr);
reference 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:
void fetch();
ItemRow* mData;
ItemValue* mPtr;
Item mCurrent;
ItemRow *mData;
ItemValue *mPtr;
Item mCurrent;
};
// checks for an initialized Row:
explicit operator bool() const { return mData != nullptr; }
explicit operator bool() const { return mData != nullptr; }
// for debugging
uint32_t lineNr() const;
void lineNr(uint32_t l);
bool empty() const;
const_iterator begin() const;
const_iterator end() const;
// TODO: implement real const version?
friend class detail::ItemReference;
const detail::ItemReference operator[](size_t column) const
......@@ -693,32 +734,32 @@ class Row
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);
return detail::ItemReference(itemTag, column, *this);
}
detail::ItemReference operator[](const char* itemTag)
detail::ItemReference operator[](const char *itemTag)
{
size_t column = ColumnForItemTag(itemTag);
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());
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());
return detail::ItemReference(itemTag.c_str(), column, *this);
}
template<typename... Ts, size_t N>
std::tuple<Ts...> get(char const * const (&columns)[N]) const
template <typename... Ts, size_t N>
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");
......@@ -728,46 +769,45 @@ class Row
return detail::getRowResult<Ts...>(*this, std::move(cix));
}
template<typename... C>
template <typename... C>
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;
}
bool operator!=(const Row& rhs) const
bool operator!=(const Row &rhs) const
{
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);
}
friend std::ostream& operator<<(std::ostream& os, const Row& row);
friend std::ostream &operator<<(std::ostream &os, const Row &row);
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);
void assign(const Item& i, bool updateLinked);
static void swap(size_t column, ItemRow* a, ItemRow* b);
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;
uint32_t mLineNr = 0;
bool mCascade = true;
ItemRow *mData;
uint32_t mLineNr = 0;
bool mCascade = true;
};
// --------------------------------------------------------------------
......@@ -776,45 +816,50 @@ class Row
namespace detail
{
struct 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 ConditionImpl
{
virtual ~ConditionImpl() {}
struct AllConditionImpl : public ConditionImpl
{
virtual bool test(const Category& c, const Row& r) const { return true; }
virtual void str(std::ostream& os) const { os << "*"; }
};
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 OrConditionImpl;
struct AndConditionImpl;
struct NotConditionImpl;
struct AllConditionImpl : public ConditionImpl
{
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
{
public:
Condition()
: mImpl(nullptr)
{
}
Condition(detail::ConditionImpl *impl)
: mImpl(impl)
{
}
Condition() : mImpl(nullptr) {}
Condition(detail::ConditionImpl* impl) : mImpl(impl) {}
Condition(const Condition&) = delete;
Condition(const Condition &) = delete;
Condition(Condition&& rhs) noexcept
Condition(Condition &&rhs) noexcept
: mImpl(nullptr)
{
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);
return *this;
......@@ -825,44 +870,44 @@ class Condition
delete mImpl;
mImpl = nullptr;
}
void prepare(const Category& c)
void prepare(const Category &c)
{
if (mImpl)
mImpl->prepare(c);
mPrepared = true;
}
bool operator()(const Category& c, const Row& r) const
bool operator()(const Category &c, const Row &r) const
{
assert(mImpl);
assert(mPrepared);
return mImpl ? mImpl->test(c, r) : false;
}
bool empty() const { return mImpl == nullptr; }
friend Condition operator||(Condition&& a, Condition&& b);
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 struct detail::OrConditionImpl;
friend struct detail::AndConditionImpl;
friend struct detail::NotConditionImpl;
void swap(Condition& rhs)
void swap(Condition &rhs)
{
std::swap(mImpl, rhs.mImpl);
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:
detail::ConditionImpl* mImpl;
detail::ConditionImpl *mImpl;
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)
cond.mImpl->str(os);
......@@ -872,219 +917,235 @@ inline std::ostream& operator<<(std::ostream& os, const Condition& cond)
namespace detail
{
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
struct KeyIsEmptyConditionImpl : public ConditionImpl
{
return r[mItemIx].empty();
}
KeyIsEmptyConditionImpl(const std::string &ItemTag)
: mItemTag(ItemTag)
{
}
virtual void str(std::ostream& os) const
{
os << mItemTag << " IS NULL";
}
virtual void prepare(const Category &c);
std::string mItemTag;
size_t mItemIx = 0;
};
virtual bool test(const Category &c, const Row &r) const
{
return r[mItemIx].empty();
}
struct KeyCompareConditionImpl : public ConditionImpl
{
template<typename COMP>
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;
}
virtual void str(std::ostream &os) const
{
os << mItemTag << " IS NULL";
}
std::string mItemTag;
size_t mItemIx = 0;
bool mCaseInsensitive = false;
std::function<bool(const Category&, const Row&, bool)> mComp;
std::string mStr;
};
std::string mItemTag;
size_t mItemIx = 0;
};
struct KeyMatchesConditionImpl : 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
struct KeyCompareConditionImpl : public ConditionImpl
{
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
{
os << mItemTag << " =~ expression";
}
std::string mItemTag;
size_t mItemIx;
std::regex mRx;
};
virtual void prepare(const Category &c);
template<typename T>
struct AnyIsConditionImpl : public ConditionImpl
{
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;
}
virtual bool test(const Category &c, const Row &r) const
{
return mComp(c, r, mCaseInsensitive);
}
valueType mValue;
};
virtual void str(std::ostream &os) const
{
os << mItemTag << (mCaseInsensitive ? "^ " : " ") << mStr;
}
struct AnyMatchesConditionImpl : public ConditionImpl
{
AnyMatchesConditionImpl(const std::regex& rx)
: mRx(rx) {}
virtual bool test(const Category& c, const Row& r) const;
virtual void str(std::ostream& os) const
std::string mItemTag;
size_t mItemIx = 0;
bool mCaseInsensitive = false;
std::function<bool(const Category &, const Row &, bool)> mComp;
std::string mStr;
};
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
{
AndConditionImpl(Condition&& a, Condition&& b)
: 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 bool test(const Category &c, const Row &r) const
{
return std::regex_match(r[mItemIx].as<std::string>(), mRx);
}
virtual void str(std::ostream& os) const
{
os << '(';
mA->str(os);
os << ") AND (";
mB->str(os);
os << ')';
}
virtual void str(std::ostream &os) const
{
os << mItemTag << " =~ expression";
}
ConditionImpl* mA;
ConditionImpl* mB;
};
std::string mItemTag;
size_t mItemIx;
std::regex mRx;
};
struct OrConditionImpl : 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
template <typename T>
struct AnyIsConditionImpl : public ConditionImpl
{
return mA->test(c, r) or mB->test(c, r);
}
typedef T valueType;
virtual void str(std::ostream& os) const
{
os << '(';
mA->str(os);
os << ") OR (";
mB->str(os);
os << ')';
}
AnyIsConditionImpl(const valueType &value)
: mValue(value)
{
}
ConditionImpl* mA;
ConditionImpl* mB;
};
virtual bool test(const Category &c, const Row &r) const;
virtual void str(std::ostream &os) const
{
os << "<any> == " << mValue;
}
struct NotConditionImpl : public ConditionImpl
{
NotConditionImpl(Condition&& a)
: mA(nullptr)
{
std::swap(mA, a.mImpl);
}
~NotConditionImpl()
valueType mValue;
};
struct AnyMatchesConditionImpl : public ConditionImpl
{
delete mA;
}
virtual void prepare(const Category& c)
AnyMatchesConditionImpl(const std::regex &rx)
: mRx(rx)
{
}
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);
}
virtual bool test(const Category& c, const Row& r) const
AndConditionImpl(Condition &&a, Condition &&b)
: 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
{
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 (";
mA->str(os);
os << ')';
}
NotConditionImpl(Condition &&a)
: mA(nullptr)
{
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)
return Condition(new detail::AndConditionImpl(std::move(a), std::move(b)));
......@@ -1093,7 +1154,7 @@ inline Condition operator&&(Condition&& a, Condition&& 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)
return Condition(new detail::OrConditionImpl(std::move(a), std::move(b)));
......@@ -1102,38 +1163,50 @@ inline Condition operator||(Condition&& a, Condition&& b)
return Condition(std::move(b));
}
struct Empty {};
struct Empty
{
};
struct Key
{
Key(const std::string& itemTag) : mItemTag(itemTag) {}
Key(const char* itemTag) : mItemTag(itemTag) {}
Key(const std::string &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;
};
template<typename T>
Condition operator==(const Key& key, const T& v)
template <typename T>
Condition operator==(const Key &key, const T &v)
{
std::ostringstream s;
s << " == " << v;
return Condition(new detail::KeyCompareConditionImpl(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()));
return Condition(new detail::KeyCompareConditionImpl(
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)
{
std::ostringstream s;
s << " == " << value;
return Condition(new detail::KeyCompareConditionImpl(key.mItemTag, [tag = key.mItemTag, value](const Category& c, const Row& r, bool icase)
{ return r[tag].compare(value, icase) == 0; }, s.str()));
return Condition(new detail::KeyCompareConditionImpl(
key.mItemTag, [tag = key.mItemTag, value](const Category &c, const Row &r, bool icase)
{ return r[tag].compare(value, icase) == 0; },
s.str()));
}
else
return Condition(new detail::KeyIsEmptyConditionImpl(key.mItemTag));
......@@ -1148,169 +1221,181 @@ inline Condition operator==(const Key& key, const char *value)
// { 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));
}
template<typename T>
Condition operator!=(const Key& key, const T& v)
template <typename T>
Condition operator!=(const Key &key, const T &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 : "");
return Condition(new detail::NotConditionImpl(operator==(key, value)));
}
template<typename T>
Condition operator>(const Key& key, const T& v)
template <typename T>
Condition operator>(const Key &key, const T &v)
{
std::ostringstream s;
s << " > " << v;
return Condition(new detail::KeyCompareConditionImpl(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()));
return Condition(new detail::KeyCompareConditionImpl(
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>
Condition operator>=(const Key& key, const T& v)
template <typename T>
Condition operator>=(const Key &key, const T &v)
{
std::ostringstream s;
s << " >= " << v;
return Condition(new detail::KeyCompareConditionImpl(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()));
return Condition(new detail::KeyCompareConditionImpl(
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>
Condition operator<(const Key& key, const T& v)
template <typename T>
Condition operator<(const Key &key, const T &v)
{
std::ostringstream s;
s << " < " << v;
return Condition(new detail::KeyCompareConditionImpl(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()));
return Condition(new detail::KeyCompareConditionImpl(
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>
Condition operator<=(const Key& key, const T& v)
template <typename T>
Condition operator<=(const Key &key, const T &v)
{
std::ostringstream s;
s << " <= " << v;
return Condition(new detail::KeyCompareConditionImpl(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()));
return Condition(new detail::KeyCompareConditionImpl(
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<>
inline
Condition operator==(const Key& key, const std::regex& rx)
template <>
inline Condition operator==(const Key &key, const std::regex &rx)
{
return Condition(new detail::KeyMatchesConditionImpl(key.mItemTag, rx));
}
template<>
inline
Condition operator==(const Key& key, const Empty&)
template <>
inline Condition operator==(const Key &key, const Empty &)
{
return Condition(new detail::KeyIsEmptyConditionImpl(key.mItemTag));
}
struct any
{
template<typename T>
Condition operator==(const T& v) const
template <typename T>
Condition operator==(const T &v) const
{
return Condition(new detail::AnyIsConditionImpl<T>(v));
}
};
template<>
inline
Condition any::operator==(const std::regex& rx) const
template <>
inline Condition any::operator==(const std::regex &rx) const
{
return Condition(new detail::AnyMatchesConditionImpl(rx));
}
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
{
inline Key operator""_key(const char* text, size_t length)
{
return Key(std::string(text, length));
}
inline constexpr Empty Null = Empty();
inline Key operator""_key(const char *text, size_t length)
{
return Key(std::string(text, length));
}
}
inline constexpr Empty Null = Empty();
} // namespace literals
// -----------------------------------------------------------------------
// iterators
template<typename RowType, typename... Ts>
template <typename RowType, typename... Ts>
class iterator_impl
{
public:
template<typename, typename...> friend class iterator_impl;
template <typename, typename...>
friend class iterator_impl;
static constexpr size_t N = sizeof...(Ts);
using iterator_category = std::forward_iterator_tag;
using value_type = std::conditional_t<N == 0, RowType, std::tuple<Ts...>>;
using difference_type = std::ptrdiff_t;
using pointer = value_type*;
using reference = value_type&;
using pointer = value_type *;
using reference = value_type &;
friend class Category;
// default constructor, equal to end()
iterator_impl() {}
iterator_impl(const iterator_impl& rhs)
: mCurrent(rhs.mCurrent), mValue(rhs.mValue), mCix(rhs.mCix)
iterator_impl(const iterator_impl &rhs)
: mCurrent(rhs.mCurrent)
, mValue(rhs.mValue)
, mCix(rhs.mCix)
{
}
iterator_impl(ItemRow* data)
iterator_impl(ItemRow *data)
: mCurrent(data)
{
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)
: mCurrent(data), mCix(cix) {}
iterator_impl(ItemRow *data, const std::array<size_t, N> &cix)
: mCurrent(data)
, mCix(cix)
{
}
template<typename IRowType>
iterator_impl(iterator_impl<IRowType, Ts...>& rhs)
: mCurrent(rhs.mCurrent), mCix(rhs.mCix)
template <typename IRowType>
iterator_impl(iterator_impl<IRowType, Ts...> &rhs)
: mCurrent(rhs.mCurrent)
, mCix(rhs.mCix)
{
if constexpr (N > 0)
mValue = get(mCurrent, std::make_index_sequence<N>());
}
template<typename IRowType>
iterator_impl(const iterator_impl<IRowType>& rhs, const std::array<size_t,N>& cix)
: mCurrent(rhs.mCurrent), mCix(cix)
template <typename IRowType>
iterator_impl(const iterator_impl<IRowType> &rhs, const std::array<size_t, N> &cix)
: mCurrent(rhs.mCurrent)
, mCix(cix)
{
if constexpr (N > 0)
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;
if constexpr (N != 0)
......@@ -1343,8 +1428,8 @@ class iterator_impl
{
return mCurrent;
}
iterator_impl& operator++()
iterator_impl &operator++()
{
mCurrent.next();
......@@ -1359,26 +1444,25 @@ class iterator_impl
iterator_impl result(*this);
this->operator++();
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>
bool operator==(const iterator_impl<IRowType, ITs...>& rhs) const
template <typename IRowType, typename... ITs>
bool operator==(const iterator_impl<IRowType, ITs...> &rhs) const
{
return mCurrent == rhs.mCurrent;
}
template<typename IRowType, typename... ITs>
bool operator!=(const iterator_impl<IRowType, ITs...>& rhs) const
template <typename IRowType, typename... ITs>
bool operator!=(const iterator_impl<IRowType, ITs...> &rhs) const
{
return mCurrent != rhs.mCurrent;
}
private:
template<std::size_t... Is>
template <std::size_t... Is>
std::tuple<Ts...> get(Row row, std::index_sequence<Is...>) const
{
if (row)
......@@ -1394,7 +1478,7 @@ class iterator_impl
// --------------------------------------------------------------------
// iterator proxy
template<typename RowType, typename... Ts>
template <typename RowType, typename... Ts>
class iterator_proxy
{
public:
......@@ -1403,30 +1487,30 @@ class iterator_proxy
using iterator = iterator_impl<RowType, Ts...>;
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, std::initializer_list<char const*> columns);
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(iterator_proxy&& p);
iterator_proxy& operator=(iterator_proxy&& p);
iterator_proxy(iterator_proxy &&p);
iterator_proxy &operator=(iterator_proxy &&p);
iterator_proxy(const iterator_proxy&) = delete;
iterator_proxy& operator=(const iterator_proxy&) = delete;
iterator_proxy(const iterator_proxy &) = delete;
iterator_proxy &operator=(const iterator_proxy &) = delete;
iterator begin() const { return iterator(mCBegin, mCix); }
iterator end() const { return iterator(mCEnd, mCix); }
iterator begin() const { return iterator(mCBegin, 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 back() { return *(std::prev(end())); }
RowType front() { return *begin(); }
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(mCBegin, rhs.mCBegin);
......@@ -1435,15 +1519,15 @@ class iterator_proxy
}
private:
Category* mCat;
Category *mCat;
row_iterator mCBegin, mCEnd;
std::array<size_t,N> mCix;
std::array<size_t, N> mCix;
};
// --------------------------------------------------------------------
// conditional iterator proxy
template<typename RowType, typename... Ts>
template <typename RowType, typename... Ts>
class conditional_iterator_proxy
{
public:
......@@ -1461,12 +1545,12 @@ class conditional_iterator_proxy
using iterator_category = std::forward_iterator_tag;
using value_type = conditional_iterator_proxy::value_type;
using difference_type = std::ptrdiff_t;
using pointer = value_type*;
using reference = value_type&;
using pointer = 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(const conditional_iterator_impl& i) = default;
conditional_iterator_impl& operator=(const conditional_iterator_impl& i) = default;
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 &operator=(const conditional_iterator_impl &i) = default;
virtual ~conditional_iterator_impl() = default;
......@@ -1479,14 +1563,14 @@ class conditional_iterator_proxy
{
return &*mBegin;
}
conditional_iterator_impl& operator++()
conditional_iterator_impl &operator++()
{
while (mBegin != mEnd)
{
if (++mBegin == mEnd)
break;
if ((*mCondition)(*mCat, mBegin.row()))
break;
}
......@@ -1499,58 +1583,57 @@ class conditional_iterator_proxy
conditional_iterator_impl result(*this);
this->operator++();
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>
bool operator==(const iterator_impl<IRowType, ITs...>& rhs) const { return mBegin == rhs; }
template <typename IRowType, typename... ITs>
bool operator==(const iterator_impl<IRowType, ITs...> &rhs) const { return mBegin == rhs; }
template<typename IRowType, typename... ITs>
bool operator!=(const iterator_impl<IRowType, ITs...>& rhs) const { return mBegin != rhs; }
template <typename IRowType, typename... ITs>
bool operator!=(const iterator_impl<IRowType, ITs...> &rhs) const { return mBegin != rhs; }
private:
Category* mCat;
Category *mCat;
base_iterator mBegin, mEnd;
const Condition* mCondition;
const Condition *mCondition;
};
using iterator = conditional_iterator_impl;
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>
conditional_iterator_proxy(Category& cat, row_iterator pos, Condition&& cond, char const* const columns[N]);
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(conditional_iterator_proxy&& p);
conditional_iterator_proxy& operator=(conditional_iterator_proxy&& p);
conditional_iterator_proxy(conditional_iterator_proxy &&p);
conditional_iterator_proxy &operator=(conditional_iterator_proxy &&p);
conditional_iterator_proxy(const conditional_iterator_proxy&) = delete;
conditional_iterator_proxy& operator=(const conditional_iterator_proxy&) = delete;
conditional_iterator_proxy(const conditional_iterator_proxy &) = delete;
conditional_iterator_proxy &operator=(const conditional_iterator_proxy &) = delete;
iterator begin() const;
iterator end() 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:
Category* mCat;
Category *mCat;
Condition mCondition;
row_iterator mCBegin, mCEnd;
std::array<size_t,N> mCix;
std::array<size_t, N> mCix;
};
// --------------------------------------------------------------------
......@@ -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
{
......@@ -1732,16 +1815,16 @@ class Category
friend class Row;
friend class detail::ItemReference;
Category(Datablock& db, const std::string& name, Validator* Validator);
Category(const Category&) = delete;
Category& operator=(const Category&) = delete;
Category(Datablock &db, const std::string &name, Validator *Validator);
Category(const Category &) = delete;
Category &operator=(const Category &) = delete;
~Category();
const std::string name() const { return mName; }
const std::string name() const { return mName; }
using iterator = iterator_impl<Row>;
using const_iterator = iterator_impl<const Row>;
iterator begin();
iterator end();
......@@ -1753,115 +1836,119 @@ class Category
bool empty() const;
size_t size() const;
void clear();
Row front() { return Row(mHead); }
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])
Row front() { return Row(mHead); }
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");
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)
{
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));
}
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>
conditional_iterator_proxy<Row, Ts...> find(Condition&& cond, char const* const (&columns)[N])
template <typename... Ts, size_t 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");
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>
conditional_iterator_proxy<Row, Ts...> find(const_iterator pos, Condition&& cond, char const* const (&columns)[N])
template <typename... Ts, size_t 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");
return { *this, pos, std::forward<Condition>(cond), columns };
return {*this, pos, std::forward<Condition>(cond), columns};
}
// --------------------------------------------------------------------
// if you only expect a single row
Row find1(Condition&& cond)
Row find1(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));
if (h.empty())
throw std::runtime_error("No hits found");
if (h.size() != 1)
throw std::runtime_error("Hit not unique");
return *h.begin();
}
template<typename... Ts, size_t N>
std::tuple<Ts...> find1(Condition&& cond, char const* const (&columns)[N])
template <typename... Ts, size_t 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");
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>
std::tuple<Ts...> find1(const_iterator pos, Condition&& cond, char const* const (&columns)[N])
template <typename... Ts, size_t 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");
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())
throw std::runtime_error("No hits found");
if (h.size() != 1)
throw std::runtime_error("Hit not unique");
return *h.begin();
}
bool exists(Condition&& cond) const;
RowSet orderBy(const std::string& Item)
{ return orderBy({ Item }); }
bool exists(Condition &&cond) const;
RowSet orderBy(const std::string &Item)
{
return orderBy({Item});
}
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)
{ return emplace(values.begin(), values.end()); }
std::tuple<Row, bool> emplace(Item value) { return emplace({value}); }
std::tuple<Row,bool> emplace(Row r);
template<class Iter>
std::tuple<Row,bool> emplace(Iter b, Iter e);
std::tuple<Row, bool> emplace(std::initializer_list<Item> values)
{
return emplace(values.begin(), values.end());
}
std::tuple<Row, bool> emplace(Row r);
size_t erase(Condition&& cond);
size_t erase(Condition&& cond, std::function<void(const Row&)>&& visit);
template <class Iter>
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);
iterator erase(iterator ri);
......@@ -1872,67 +1959,67 @@ class Category
// 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;
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;
return result;
}
void eraseOrphans(Condition&& cond);
void eraseOrphans(Condition &&cond);
/// an orphan is a row that is the child side of one or more
/// links and for which there is no single parent left.
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 hasParents(Row r) const;
RowSet getChildren(Row r, Category& childCat);
RowSet getChildren(Row r, const char* childCat);
RowSet getChildren(Row r, Category &childCat);
RowSet getChildren(Row r, const char *childCat);
RowSet getParents(Row r, Category& parentCat);
RowSet getParents(Row r, const char* parentCat);
RowSet getParents(Row r, Category &parentCat);
RowSet getParents(Row r, const char *parentCat);
RowSet getLinked(Row r, Category& cat);
RowSet getLinked(Row r, const char* cat);
RowSet getLinked(Row r, Category &cat);
RowSet getLinked(Row r, const char *cat);
bool isValid();
void validateLinks() const;
const Validator& getValidator() const;
const ValidateCategory* getCatValidator() const { return mCatValidator; }
const Validator &getValidator() const;
const ValidateCategory *getCatValidator() const { return mCatValidator; }
Datablock& db() { return mDb; }
void setValidator(Validator* v);
Datablock &db() { return mDb; }
void setValidator(Validator *v);
iset fields() const;
iset mandatoryFields() const;
iset keyFields() 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
size_t getColumnIndex(const std::string& name) const;
bool hasColumn(const std::string& name) const;
const std::string& getColumnName(size_t columnIndex) const;
size_t getColumnIndex(const std::string &name) const;
bool hasColumn(const std::string &name) const;
const std::string &getColumnName(size_t columnIndex) const;
std::vector<std::string> getColumnNames() const;
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
......@@ -1942,7 +2029,7 @@ class Category
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);
......@@ -1954,25 +2041,30 @@ class Category
std::string getUniqueID(std::function<std::string(int)> generator = cif::cifIdForNumber);
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);
void write(std::ostream& os, const std::vector<std::string>& order);
void write(std::ostream& os, const std::vector<size_t>& order, bool includeEmptyColumns);
friend bool operator==(const Category &lhs, const Category &rhs);
size_t addColumn(const std::string& name);
Datablock& mDb;
std::string mName;
Validator* mValidator;
const ValidateCategory* mCatValidator = nullptr;
std::vector<ItemColumn> mColumns;
ItemRow* mHead;
ItemRow* mTail;
class CatIndex* mIndex;
private:
void write(std::ostream &os);
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);
Datablock &mDb;
std::string mName;
Validator *mValidator;
const ValidateCategory *mCatValidator = nullptr;
std::vector<ItemColumn> mColumns;
ItemRow *mHead;
ItemRow *mTail;
class CatIndex *mIndex;
};
// --------------------------------------------------------------------
......@@ -1984,82 +2076,96 @@ class File
friend class Validator;
File();
File(std::istream& is, bool validate = false);
File(const std::string& path, bool validate = false);
File(File&& rhs);
File(const File& rhs) = delete;
File& operator=(const File& rhs) = delete;
File(std::istream &is, bool validate = false);
File(const std::string &path, bool validate = false);
File(File &&rhs);
File(const File &rhs) = delete;
File &operator=(const File &rhs) = delete;
~File();
void load(const std::string& p);
void save(const std::string& p);
void load(const std::string &p);
void save(const std::string &p);
void load(std::istream& is);
void save(std::ostream& os);
void load(std::istream &is);
void save(std::ostream &os);
/// \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 write(std::ostream& os, const std::vector<std::string>& 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 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(std::istream& is); // load dictionary from input stream
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(std::istream &is); // load dictionary from input stream
bool isValid();
void validateLinks() const;
Datablock& firstDatablock()
const Datablock &firstDatablock() const
{
if (mHead == nullptr)
throw std::runtime_error("No datablocks in file");
return *mHead;
}
void append(Datablock* e);
Datablock* get(const std::string& name) const;
Datablock& operator[](const std::string& name);
Datablock &firstDatablock()
{
if (mHead == nullptr)
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 value_type = Datablock;
using difference_type = std::ptrdiff_t;
using pointer = value_type*;
using reference = value_type&;
iterator(Datablock* db) : mCurrent(db) {}
reference operator*() { return *mCurrent; }
pointer operator->() { return mCurrent; }
iterator& operator++();
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); }
using pointer = value_type *;
using reference = value_type &;
iterator(Datablock *db)
: mCurrent(db)
{
}
reference operator*() { return *mCurrent; }
pointer operator->() { return mCurrent; }
iterator &operator++();
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:
Datablock* mCurrent;
Datablock *mCurrent;
};
iterator begin() const;
iterator end() const;
bool empty() const { return mHead == nullptr; }
const Validator& getValidator() const;
void getTagOrder(std::vector<std::string>& tags) const;
private:
bool empty() const { return mHead == nullptr; }
void setValidator(Validator* v);
const Validator &getValidator() const;
void getTagOrder(std::vector<std::string> &tags) const;
Datablock* mHead;
Validator* mValidator;
private:
void setValidator(Validator *v);
Datablock *mHead;
Validator *mValidator;
};
// --------------------------------------------------------------------
......@@ -2068,64 +2174,67 @@ class File
namespace detail
{
template<typename T>
inline
bool AnyIsConditionImpl<T>::test(const Category& c, const Row& r) const
{
bool result = false;
for (auto& f: c.fields())
template <typename T>
inline bool AnyIsConditionImpl<T>::test(const Category &c, const Row &r) const
{
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
{
bool result = false;
for (auto& f: c.fields())
inline bool AnyMatchesConditionImpl::test(const Category &c, const Row &r) const
{
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
inline void swap(cif::Row& a, cif::Row& b)
inline void swap(cif::Row &a, cif::Row &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);
}
// --------------------------------------------------------------------
template<typename RowType, typename... Ts>
iterator_proxy<RowType, Ts...>::iterator_proxy(Category& cat, row_iterator pos, char const* const columns[N])
template <typename RowType, typename... Ts>
iterator_proxy<RowType, Ts...>::iterator_proxy(Category &cat, row_iterator pos, char const *const columns[N])
: mCat(&cat)
, mCBegin(pos)
, mCEnd(cat.end())
......@@ -2134,8 +2243,8 @@ iterator_proxy<RowType, Ts...>::iterator_proxy(Category& cat, row_iterator pos,
mCix[i] = mCat->getColumnIndex(columns[i]);
}
template<typename RowType, typename... Ts>
iterator_proxy<RowType, Ts...>::iterator_proxy(Category& cat, row_iterator pos, std::initializer_list<char const*> columns)
template <typename RowType, typename... Ts>
iterator_proxy<RowType, Ts...>::iterator_proxy(Category &cat, row_iterator pos, std::initializer_list<char const *> columns)
: mCat(&cat)
, mCBegin(pos)
, mCEnd(cat.end())
......@@ -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");
std::size_t i = 0;
for (auto column: columns)
for (auto column : columns)
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(
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)
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)
{
}
template<typename RowType, typename... Ts>
conditional_iterator_proxy<RowType, Ts...>::conditional_iterator_proxy(conditional_iterator_proxy&& p)
: mCat(nullptr), mCBegin(p.mCBegin), mCEnd(p.mCEnd), mCix(p.mCix)
template <typename RowType, typename... Ts>
conditional_iterator_proxy<RowType, Ts...>::conditional_iterator_proxy(conditional_iterator_proxy &&p)
: mCat(nullptr)
, mCBegin(p.mCBegin)
, mCEnd(p.mCEnd)
, mCix(p.mCix)
{
std::swap(mCat, p.mCat);
std::swap(mCix, p.mCix);
mCondition.swap(p.mCondition);
}
template<typename RowType, typename... Ts>
conditional_iterator_proxy<RowType, Ts...>::conditional_iterator_proxy(Category& cat, row_iterator pos, Condition&& cond)
template <typename RowType, typename... Ts>
conditional_iterator_proxy<RowType, Ts...>::conditional_iterator_proxy(Category &cat, row_iterator pos, Condition &&cond)
: mCat(&cat)
, mCondition(std::move(cond))
, mCBegin(pos)
......@@ -2178,9 +2293,9 @@ conditional_iterator_proxy<RowType, Ts...>::conditional_iterator_proxy(Category&
++mCBegin;
}
template<typename RowType, typename... Ts>
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])
template <typename RowType, typename... Ts>
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])
: mCat(&cat)
, mCondition(std::move(cond))
, mCBegin(pos)
......@@ -2195,33 +2310,33 @@ conditional_iterator_proxy<RowType, Ts...>::conditional_iterator_proxy(Category&
mCix[i] = mCat->getColumnIndex(columns[i]);
}
template<typename RowType, typename... Ts>
conditional_iterator_proxy<RowType, Ts...>& conditional_iterator_proxy<RowType, Ts...>::operator=(conditional_iterator_proxy&& p)
template <typename RowType, typename... Ts>
conditional_iterator_proxy<RowType, Ts...> &conditional_iterator_proxy<RowType, Ts...>::operator=(conditional_iterator_proxy &&p)
{
swap(p);
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
{
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
{
return iterator(*mCat, mCEnd, mCondition, mCix);
}
template<typename RowType, typename... Ts>
template <typename RowType, typename... Ts>
bool conditional_iterator_proxy<RowType, Ts...>::empty() const
{
return mCBegin == mCEnd;
}
template<typename RowType, typename... Ts>
void conditional_iterator_proxy<RowType, Ts...>::swap(conditional_iterator_proxy& rhs)
template <typename RowType, typename... Ts>
void conditional_iterator_proxy<RowType, Ts...>::swap(conditional_iterator_proxy &rhs)
{
std::swap(mCat, rhs.mCat);
mCondition.swap(rhs.mCondition);
......@@ -2230,5 +2345,4 @@ void conditional_iterator_proxy<RowType, Ts...>::swap(conditional_iterator_proxy
std::swap(mCix, rhs.mCix);
}
}
} // namespace cif
......@@ -199,15 +199,15 @@ class Residue
// constructor for waters
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
Residue(const Structure &structure, const std::string &compoundID,
const std::string &asymID);
const std::string &asymID);
// constructor for a residue with a sequence number
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 &operator=(const Residue &rhs) = delete;
......@@ -297,7 +297,7 @@ class Monomer : public Residue
Monomer &operator=(Monomer &&rhs);
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_last_in_chain() const;
......@@ -397,6 +397,8 @@ class File : public std::enable_shared_from_this<File>
File(const File &) = delete;
File &operator=(const File &) = delete;
cif::Datablock& createDatablock(const std::string &name);
void load(const std::string &path);
void save(const std::string &path);
......@@ -450,7 +452,7 @@ class Structure
// Atom getAtomByLocation(Point pt, float maxDistance) const;
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
const Residue &getResidue(const std::string &asymID, const std::string &compID, int seqID = 0) const;
......@@ -458,7 +460,7 @@ class Structure
// map between auth and label locations
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(
const std::string &asymID, int seqID, const std::string &compID);
......@@ -480,7 +482,20 @@ class Structure
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 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
/// Will asssign new atom_id's to all atoms. Be carefull
......
......@@ -26,26 +26,26 @@
#include <cassert>
#include <stack>
#include <tuple>
#include <fstream>
#include <numeric>
#include <regex>
#include <set>
#include <stack>
#include <tuple>
#include <unordered_map>
#include <numeric>
#include <fstream>
#include <filesystem>
#include <boost/algorithm/string.hpp>
#include <boost/iostreams/filtering_stream.hpp>
#include <boost/iostreams/filter/bzip2.hpp>
#include <boost/iostreams/filter/gzip.hpp>
#include <boost/iostreams/filtering_stream.hpp>
#include <boost/logic/tribool.hpp>
#include "cif++/Cif++.hpp"
#include "cif++/CifParser.hpp"
#include "cif++/CifValidator.hpp"
#include "cif++/CifUtils.hpp"
#include "cif++/CifValidator.hpp"
namespace ba = boost::algorithm;
namespace io = boost::iostreams;
......@@ -56,34 +56,35 @@ namespace cif
CIFPP_EXPORT int VERBOSE = 0;
static const char* kEmptyResult = "";
static const char *kEmptyResult = "";
// --------------------------------------------------------------------
// most internal data structures are stored as linked lists
// Item values are stored in a simple struct. They should be const anyway
struct ItemValue
{
ItemValue* mNext;
uint32_t mColumnIndex;
char mText[4];
ItemValue(const char* v, size_t columnIndex);
ItemValue *mNext;
uint32_t mColumnIndex;
char mText[4];
ItemValue(const char *v, size_t columnIndex);
~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);
void operator delete(void* p);
void operator delete(void* p, size_t dataSize);
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);
void operator delete(void *p);
void operator delete(void *p, size_t dataSize);
};
// --------------------------------------------------------------------
ItemValue::ItemValue(const char* value, size_t columnIndex)
: mNext(nullptr), mColumnIndex(uint32_t(columnIndex))
ItemValue::ItemValue(const char *value, size_t columnIndex)
: mNext(nullptr)
, mColumnIndex(uint32_t(columnIndex))
{
assert(columnIndex < std::numeric_limits<uint32_t>::max());
strcpy(mText, value);
......@@ -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);
}
void ItemValue::operator delete(void* p)
void ItemValue::operator delete(void *p)
{
free(p);
}
void ItemValue::operator delete(void* p, size_t dataSize)
void ItemValue::operator delete(void *p, size_t dataSize)
{
free(p);
}
......@@ -121,8 +122,8 @@ void ItemValue::operator delete(void* p, size_t dataSize)
struct ItemColumn
{
std::string mName; // store lower-case, for optimization
const ValidateItem* mValidator;
std::string mName; // store lower-case, for optimization
const ValidateItem *mValidator;
};
// itemRow contains the actual values for a Row in a Category
......@@ -132,8 +133,8 @@ struct ItemRow
~ItemRow();
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::stringstream s;
......@@ -144,21 +145,21 @@ struct ItemRow
s << mCategory->getColumnName(v->mColumnIndex)
<< ':'
<< v->mText;
if (v->mNext != nullptr)
s << ", ";
if (v->mNext != nullptr)
s << ", ";
}
s << '}';
return s.str();
}
ItemRow* mNext;
Category* mCategory;
ItemValue* mValues;
uint32_t mLineNr = 0;
ItemRow *mNext;
Category *mCategory;
ItemValue *mValues;
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() << '[';
for (auto iv = r.mValues; iv != nullptr; iv = iv->mNext)
......@@ -168,7 +169,7 @@ std::ostream& operator<<(std::ostream& os, const ItemRow& r)
os << ',';
}
os << ']';
return os;
}
......@@ -219,10 +220,10 @@ void ItemRow::drop(size_t columnIx)
#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)
{
if (v->mColumnIndex == columnIx)
......@@ -240,119 +241,121 @@ const char* ItemRow::c_str(size_t columnIx) const
namespace detail
{
ItemReference& ItemReference::operator=(const std::string& value)
{
if (mConst)
throw std::logic_error("Attempt to write to a constant row");
ItemReference &ItemReference::operator=(const std::string &value)
{
if (mConst)
throw std::logic_error("Attempt to write to a constant row");
if (mRow.mData == nullptr)
throw std::logic_error("Attempt to write to an uninitialized row");
if (mRow.mData == nullptr)
throw std::logic_error("Attempt to write to an uninitialized row");
mRow.assign(mName, value, false);
return *this;
}
mRow.assign(mName, value, false);
return *this;
}
const char* ItemReference::c_str() const
{
const char* result = kEmptyResult;
if (mRow.mData != nullptr /* and mRow.mData->mCategory != nullptr*/)
const char *ItemReference::c_str() const
{
// assert(mRow.mData->mCategory);
for (auto iv = mRow.mData->mValues; iv != nullptr; iv = iv->mNext)
const char *result = kEmptyResult;
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)
result = iv->mText;
break;
if (iv->mColumnIndex == mColumn)
{
if (iv->mText[0] != '.' or iv->mText[1] != 0)
result = iv->mText;
break;
}
}
}
return result;
}
return result;
}
const char* ItemReference::c_str(const char* defaultValue) const
{
const char* result = defaultValue;
if (mRow.mData != nullptr and mRow.mData->mCategory != nullptr)
const char *ItemReference::c_str(const char *defaultValue) const
{
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->mText[0] != 0 and ((iv->mText[0] != '.' and iv->mText[0] != '?') or iv->mText[1] != 0))
result = iv->mText;
if (iv->mColumnIndex == mColumn)
{
// 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?
{
auto iv = mRow.mData->mCategory->mColumns[mColumn].mValidator;
if (iv != nullptr and not iv->mDefault.empty())
result = iv->mDefault.c_str();
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;
if (iv != nullptr and not iv->mDefault.empty())
result = iv->mDefault.c_str();
}
}
return result;
}
return result;
}
bool ItemReference::empty() const
{
return c_str() == kEmptyResult;
}
bool ItemReference::empty() const
{
return c_str() == kEmptyResult;
}
bool ItemReference::is_null() const
{
boost::tribool result;
if (mRow.mData != nullptr and mRow.mData->mCategory != nullptr)
bool ItemReference::is_null() const
{
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;
break;
if (iv->mColumnIndex == mColumn)
{
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?
{
auto iv = mRow.mData->mCategory->mColumns[mColumn].mValidator;
if (iv != nullptr)
result = iv->mDefaultIsNull;
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;
if (iv != nullptr)
result = iv->mDefaultIsNull;
}
}
return result ? true : false;
}
return result ? true : false;
}
void ItemReference::swap(ItemReference& b)
{
Row::swap(mColumn, mRow.mData, b.mRow.mData);
}
void ItemReference::swap(ItemReference &b)
{
Row::swap(mColumn, mRow.mData, b.mRow.mData);
}
std::ostream& operator<<(std::ostream& os, ItemReference& item)
{
os << item.c_str();
return os;
}
std::ostream &operator<<(std::ostream &os, ItemReference &item)
{
os << item.c_str();
return os;
}
}
} // namespace detail
// --------------------------------------------------------------------
// Datablock implementation
Datablock::Datablock(const std::string& name)
: mName(name), mValidator(nullptr), mNext(nullptr)
Datablock::Datablock(const std::string &name)
: mName(name)
, mValidator(nullptr)
, mNext(nullptr)
{
}
......@@ -361,18 +364,18 @@ Datablock::~Datablock()
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 catName, itemName;
std::tie(catName, itemName) = splitTagName(tag);
for (auto& cat: mCategories)
for (auto &cat : mCategories)
{
if (iequals(cat.name(), catName))
{
for (auto row: cat)
for (auto row : cat)
{
result = row[itemName].as<std::string>();
break;
......@@ -385,33 +388,41 @@ std::string Datablock::firstItem(const std::string& tag) const
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;
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); });
if (i == end())
{
isNew = true;
i = mCategories.emplace(end(), *this, name, mValidator);
}
return std::make_tuple(i, isNew);
}
Category& Datablock::operator[](const std::string& name)
Category &Datablock::operator[](const std::string &name)
{
iterator i;
std::tie(i, std::ignore) = emplace(name);
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 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;
}
......@@ -421,188 +432,257 @@ bool Datablock::isValid()
throw std::runtime_error("Validator not specified");
bool result = true;
for (auto& cat: *this)
for (auto &cat : *this)
result = cat.isValid() and result;
return result;
}
void Datablock::validateLinks() const
{
for (auto& cat: *this)
for (auto &cat : *this)
cat.validateLinks();
}
void Datablock::setValidator(Validator* v)
void Datablock::setValidator(Validator *v)
{
mValidator = v;
for (auto& cat: *this)
for (auto &cat : *this)
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;
// TODO: should we check this ordinal number???
cat.emplace({
{ "pdbx_ordinal", ordNr },
{ "name", name },
{ "version", versionNr },
{ "date", versionDate },
{ "classification", classification }
});
cat.emplace({{"pdbx_ordinal", ordNr},
{"name", name},
{"version", versionNr},
{"date", versionDate},
{"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);
}
void Datablock::write(std::ostream& os)
void Datablock::write(std::ostream &os)
{
os << "data_" << mName << std::endl
<< "# " << std::endl;
// mmcif support, sort of. First write the 'entry' Category
// and if it exists, _AND_ we have a Validator, write out the
// audit_conform record.
for (auto& cat: mCategories)
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.emplace({{"dict_name", mValidator->dictName()},
{"dict_version", mValidator->dictVersion()}});
auditConform.write(os);
}
break;
}
}
for (auto& cat: mCategories)
for (auto &cat : mCategories)
{
if (cat.name() != "entry" and cat.name() != "audit_conform")
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
<< "# " << std::endl;
std::vector<std::string> catOrder;
for (auto& o: order)
for (auto &o : order)
{
std::string cat, Item;
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);
}
for (auto& c: catOrder)
for (auto &c : catOrder)
{
auto cat = get(c);
if (cat == nullptr)
continue;
std::vector<std::string> items;
for (auto& o: order)
for (auto &o : order)
{
std::string catName, Item;
std::tie(catName, Item) = splitTagName(o);
if (catName == c)
items.push_back(Item);
}
cat->write(os, items);
}
// 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;
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)
{
mItemIx = c.getColumnIndex(mItemTag);
for (auto &cat : dbA)
{
if (not cat.empty())
catA.push_back(cat.name());
}
sort(catA.begin(), catA.end());
for (auto &cat : dbB)
{
if (not cat.empty())
catB.push_back(cat.name());
}
sort(catB.begin(), catB.end());
// loop over categories twice, to group output
// First iteration is to list missing categories.
std::vector<std::string> missingA, missingB;
auto catA_i = catA.begin(), catB_i = catB.begin();
while (catA_i != catA.end() and catB_i != catB.end())
{
if (not iequals(*catA_i, *catB_i))
return false;
++catA_i, ++catB_i;
}
if (catA_i != catA.end() or catB_i != catB.end())
return false;
// Second loop, now compare category values
catA_i = catA.begin(), catB_i = catB.begin();
auto cv = c.getCatValidator();
if (cv)
while (catA_i != catA.end() and catB_i != catB.end())
{
auto iv = cv->getValidatorForItem(mItemTag);
if (iv != nullptr and iv->mType != nullptr)
std::string nA = *catA_i;
ba::to_lower(nA);
std::string nB = *catB_i;
ba::to_lower(nB);
int d = nA.compare(nB);
if (d > 0)
++catB_i;
else if (d < 0)
++catA_i;
else
{
auto type = iv->mType;
mCaseInsensitive = type->mPrimitiveType == DDL_PrimitiveType::UChar;
if (not (*dbA.get(*catA_i) == *dbB.get(*catB_i)))
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)
class RowComparator
{
public:
RowComparator(Category* cat)
RowComparator(Category *cat)
: 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);
}
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>
RowComparator::RowComparator(Category* cat, KeyIter b, KeyIter e)
template <typename KeyIter>
RowComparator::RowComparator(Category *cat, KeyIter b, KeyIter e)
{
auto cv = cat->getCatValidator();
for (auto ki = b; ki != e; ++ki)
{
std::string k = *ki;
size_t ix = cat->getColumnIndex(k);
auto iv = cv->getValidatorForItem(k);
if (iv == nullptr)
throw std::runtime_error("Incomplete dictionary, no Item Validator for Key " + k);
auto tv = iv->mType;
if (tv == nullptr)
throw std::runtime_error("Incomplete dictionary, no type Validator for Item " + k);
using namespace std::placeholders;
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(b);
int d = 0;
for (auto& c: mComp)
for (auto &c : mComp)
{
size_t k;
compareFunc f;
std::tie(k, f) = c;
const char* ka = a->c_str(k);
const char* kb = b->c_str(k);
const char *ka = a->c_str(k);
const char *kb = b->c_str(k);
d = f(ka, kb);
if (d != 0)
break;
}
return d;
}
......@@ -693,148 +772,152 @@ int RowComparator::operator()(const ItemRow* a, const ItemRow* b) const
class CatIndex
{
public:
CatIndex(Category* cat);
CatIndex(Category *cat);
~CatIndex();
ItemRow* find(ItemRow* k) const;
void insert(ItemRow* r);
void erase(ItemRow* r);
ItemRow *find(ItemRow *k) const;
void insert(ItemRow *r);
void erase(ItemRow *r);
// batch create
void reconstruct();
// 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)
{
entry* head = findMin(mRoot);
entry* tail = reorder(mRoot);
entry *head = findMin(mRoot);
entry *tail = reorder(mRoot);
tail->mRow->mNext = nullptr;
result = std::make_tuple(head->mRow, tail->mRow);
}
return result;
}
size_t size() const;
// bool isValid() const;
private:
// bool isValid() const;
private:
struct entry
{
entry(ItemRow* r)
: mRow(r), mLeft(nullptr), mRight(nullptr), mRed(true) {}
entry(ItemRow *r)
: mRow(r)
, mLeft(nullptr)
, mRight(nullptr)
, mRed(true)
{
}
~entry()
{
delete mLeft;
delete mRight;
}
ItemRow* mRow;
entry* mLeft;
entry* mRight;
bool mRed;
ItemRow *mRow;
entry *mLeft;
entry *mRight;
bool mRed;
};
entry* insert(entry* h, ItemRow* v);
entry* erase(entry* h, ItemRow* k);
entry *insert(entry *h, ItemRow *v);
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;
x->mLeft = h;
x->mRed = h->mRed;
h->mRed = true;
return x;
}
entry* rotateRight(entry* h)
entry *rotateRight(entry *h)
{
entry* x = h->mLeft;
entry *x = h->mLeft;
h->mLeft = x->mRight;
x->mRight = h;
x->mRed = h->mRed;
h->mRed = true;
return x;
}
void flipColour(entry* h)
void flipColour(entry *h)
{
h->mRed = not h->mRed;
if (h->mLeft != nullptr)
h->mLeft->mRed = not h->mLeft->mRed;
if (h->mRight != nullptr)
h->mRight->mRed = not h->mRight->mRed;
}
bool isRed(entry* h) const
bool isRed(entry *h) const
{
return h != nullptr and h->mRed;
}
entry* moveRedLeft(entry* h)
entry *moveRedLeft(entry *h)
{
flipColour(h);
if (h->mRight != nullptr and isRed(h->mRight->mLeft))
{
h->mRight = rotateRight(h->mRight);
h = rotateLeft(h);
flipColour(h);
}
return h;
}
entry* moveRedRight(entry* h)
entry *moveRedRight(entry *h)
{
flipColour(h);
if (h->mLeft != nullptr and isRed(h->mLeft->mLeft))
{
h = rotateRight(h);
flipColour(h);
}
return h;
}
entry* fixUp(entry* h)
entry *fixUp(entry *h)
{
if (isRed(h->mRight))
h = rotateLeft(h);
if (isRed(h->mLeft) and isRed(h->mLeft->mLeft))
h = rotateRight(h);
if (isRed(h->mLeft) and isRed(h->mRight))
flipColour(h);
return h;
}
entry* findMin(entry* h)
entry *findMin(entry *h)
{
while (h->mLeft != nullptr)
h = h->mLeft;
return h;
}
entry* eraseMin(entry* h)
entry *eraseMin(entry *h)
{
if (h->mLeft == nullptr)
{
......@@ -845,44 +928,46 @@ class CatIndex
{
if (not isRed(h->mLeft) and not isRed(h->mLeft->mLeft))
h = moveRedLeft(h);
h->mLeft = eraseMin(h->mLeft);
h = fixUp(h);
}
return h;
}
// Fix mNext fields for rows in order of this index
entry* reorder(entry* e)
entry *reorder(entry *e)
{
auto result = e;
if (e->mLeft != nullptr)
{
auto l = reorder(e->mLeft);
l->mRow->mNext = e->mRow;
}
if (e->mRight != nullptr)
{
auto mr = findMin(e->mRight);
e->mRow->mNext = mr->mRow;
result = reorder(e->mRight);
}
return result;
}
Category& mCat;
RowComparator mComp;
entry* mRoot;
Category &mCat;
RowComparator mComp;
entry *mRoot;
};
CatIndex::CatIndex(Category* cat)
: mCat(*cat), mComp(cat), mRoot(nullptr)
CatIndex::CatIndex(Category *cat)
: mCat(*cat)
, mComp(cat)
, mRoot(nullptr)
{
}
......@@ -891,9 +976,9 @@ CatIndex::~CatIndex()
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)
{
int d = mComp(k, r->mRow);
......@@ -904,24 +989,26 @@ ItemRow* CatIndex::find(ItemRow* k) const
else
break;
}
return r ? r->mRow : nullptr;
}
void CatIndex::insert(ItemRow* k)
void CatIndex::insert(ItemRow *k)
{
mRoot = insert(mRoot, k);
mRoot->mRed = false;
}
CatIndex::entry* CatIndex::insert(entry* h, ItemRow* v)
CatIndex::entry *CatIndex::insert(entry *h, ItemRow *v)
{
if (h == nullptr)
return new entry(v);
int d = mComp(v, h->mRow);
if (d < 0) h->mLeft = insert(h->mLeft, v);
else if (d > 0) h->mRight = insert(h->mRight, v);
if (d < 0)
h->mLeft = insert(h->mLeft, v);
else if (d > 0)
h->mRight = insert(h->mRight, v);
else
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)
if (isRed(h->mLeft) and isRed(h->mLeft->mLeft))
h = rotateRight(h);
if (isRed(h->mLeft) and isRed(h->mRight))
if (isRed(h->mLeft) and isRed(h->mRight))
flipColour(h);
return h;
}
void CatIndex::erase(ItemRow* k)
void CatIndex::erase(ItemRow *k)
{
mRoot = erase(mRoot, k);
if (mRoot != nullptr)
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)
{
......@@ -960,18 +1047,18 @@ CatIndex::entry* CatIndex::erase(entry* h, ItemRow* k)
{
if (isRed(h->mLeft))
h = rotateRight(h);
if (mComp(k, h->mRow) == 0 and h->mRight == nullptr)
{
delete h;
return nullptr;
}
if (h->mRight != nullptr)
{
if (not isRed(h->mRight) and not isRed(h->mRight->mLeft))
h = moveRedRight(h);
if (mComp(k, h->mRow) == 0)
{
h->mRow = findMin(h->mRight)->mRow;
......@@ -981,7 +1068,7 @@ CatIndex::entry* CatIndex::erase(entry* h, ItemRow* k)
h->mRight = erase(h->mRight, k);
}
}
return fixUp(h);
}
......@@ -989,133 +1076,133 @@ void CatIndex::reconstruct()
{
delete mRoot;
mRoot = nullptr;
for (auto r: mCat)
for (auto r : mCat)
insert(r.mData);
// 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.
// std::vector<ItemRow*> rows;
// transform(mCat.begin(), mCat.end(), backInserter(rows),
// [](Row r) -> ItemRow* { assert(r.mData); return r.mData; });
//
// assert(std::find(rows.begin(), rows.end(), nullptr) == rows.end());
//
// // don't use sort here, it will run out of the stack of something.
// // quicksort is notorious for using excessive recursion.
// // Besides, most of the time, the data is ordered already anyway.
//
// stable_sort(rows.begin(), rows.end(), [this](ItemRow* a, ItemRow* b) -> bool { return this->mComp(a, b) < 0; });
//
// for (size_t i = 0; i < rows.size() - 1; ++i)
// assert(mComp(rows[i], rows[i + 1]) < 0);
//
// deque<entry*> e;
// transform(rows.begin(), rows.end(), back_inserter(e),
// [](ItemRow* r) -> entry* { return new entry(r); });
//
// while (e.size() > 1)
// {
// deque<entry*> ne;
//
// while (not e.empty())
// {
// entry* a = e.front();
// e.pop_front();
//
// if (e.empty())
// ne.push_back(a);
// else
// {
// entry* b = e.front();
// b->mLeft = a;
//
// assert(mComp(a->mRow, b->mRow) < 0);
//
// e.pop_front();
//
// if (not e.empty())
// {
// entry* c = e.front();
// e.pop_front();
//
// assert(mComp(b->mRow, c->mRow) < 0);
//
// b->mRight = c;
// }
//
// ne.push_back(b);
//
// if (not e.empty())
// {
// ne.push_back(e.front());
// e.pop_front();
// }
// }
// }
//
// swap (e, ne);
// }
//
// assert(e.size() == 1);
// mRoot = e.front();
// 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.
// std::vector<ItemRow*> rows;
// transform(mCat.begin(), mCat.end(), backInserter(rows),
// [](Row r) -> ItemRow* { assert(r.mData); return r.mData; });
//
// assert(std::find(rows.begin(), rows.end(), nullptr) == rows.end());
//
// // don't use sort here, it will run out of the stack of something.
// // quicksort is notorious for using excessive recursion.
// // Besides, most of the time, the data is ordered already anyway.
//
// stable_sort(rows.begin(), rows.end(), [this](ItemRow* a, ItemRow* b) -> bool { return this->mComp(a, b) < 0; });
//
// for (size_t i = 0; i < rows.size() - 1; ++i)
// assert(mComp(rows[i], rows[i + 1]) < 0);
//
// deque<entry*> e;
// transform(rows.begin(), rows.end(), back_inserter(e),
// [](ItemRow* r) -> entry* { return new entry(r); });
//
// while (e.size() > 1)
// {
// deque<entry*> ne;
//
// while (not e.empty())
// {
// entry* a = e.front();
// e.pop_front();
//
// if (e.empty())
// ne.push_back(a);
// else
// {
// entry* b = e.front();
// b->mLeft = a;
//
// assert(mComp(a->mRow, b->mRow) < 0);
//
// e.pop_front();
//
// if (not e.empty())
// {
// entry* c = e.front();
// e.pop_front();
//
// assert(mComp(b->mRow, c->mRow) < 0);
//
// b->mRight = c;
// }
//
// ne.push_back(b);
//
// if (not e.empty())
// {
// ne.push_back(e.front());
// e.pop_front();
// }
// }
// }
//
// swap (e, ne);
// }
//
// assert(e.size() == 1);
// mRoot = e.front();
}
size_t CatIndex::size() const
{
std::stack<entry*> s;
std::stack<entry *> s;
s.push(mRoot);
size_t result = 0;
while (not s.empty())
{
entry* e = s.top();
entry *e = s.top();
s.pop();
if (e == nullptr)
continue;
++result;
s.push(e->mLeft);
s.push(e->mRight);
}
return result;
}
//bool CatIndex::isValid() const
//{
// bool result = true;
//
//
// if (mRoot != nullptr)
// {
// uint32_t minBlack = numeric_limits<uint32_t>::max();
// uint32_t maxBlack = 0;
//
//
// assert(not mRoot->mRed);
//
//
// result = isValid(mRoot, false, 0, minBlack, maxBlack);
// assert(minBlack == maxBlack);
// }
//
//
// return result;
//}
//
//bool CatIndex::validate(entry* h, bool isParentRed, uint32_t blackDepth, uint32_t& minBlack, uint32_t& maxBlack) const
//{
// bool result = true;
//
//
// if (h->mRed)
// assert(not isParentRed);
// else
// ++blackDepth;
//
//
// if (isParentRed)
// assert(not h->mRed);
//
//
// if (h->mLeft != nullptr and h->mRight != nullptr)
// {
// if (isRed(h->mLeft))
......@@ -1123,7 +1210,7 @@ size_t CatIndex::size() const
// if (isRed(h->mRight))
// assert(not isRed(h->mLeft));
// }
//
//
// if (h->mLeft != nullptr)
// {
// assert(mComp(h->mLeft->mRow, h->mRow) < 0);
......@@ -1136,7 +1223,7 @@ size_t CatIndex::size() const
// if (maxBlack < blackDepth)
// maxBlack = blackDepth;
// }
//
//
// if (h->mRight != nullptr)
// {
// assert(mComp(h->mRight->mRow, h->mRow) > 0);
......@@ -1153,36 +1240,36 @@ size_t CatIndex::size() const
// --------------------------------------------------------------------
RowSet::RowSet(Category& cat)
RowSet::RowSet(Category &cat)
: mCat(&cat)
// , mCond(nullptr)
// , mCond(nullptr)
{
}
RowSet::RowSet(Category& cat, Condition&& cond)
RowSet::RowSet(Category &cat, Condition &&cond)
: mCat(&cat)
// , mCond(new Condition(std::forward<Condition>(cond)))
// , mCond(new Condition(std::forward<Condition>(cond)))
{
cond.prepare(cat);
for (auto r: cat)
for (auto r : cat)
{
if (cond(cat, r))
mItems.push_back(r.mData);
}
}
RowSet::RowSet(const RowSet& rhs)
RowSet::RowSet(const RowSet &rhs)
: mCat(rhs.mCat)
, mItems(rhs.mItems)
// , mCond(nullptr)
// , mCond(nullptr)
{
}
RowSet::RowSet(RowSet&& rhs)
RowSet::RowSet(RowSet &&rhs)
: mCat(rhs.mCat)
, mItems(std::move(rhs.mItems))
// , mCond(rhs.mCond)
// , mCond(rhs.mCond)
{
// rhs.mCond = nullptr;
}
......@@ -1192,59 +1279,63 @@ RowSet::~RowSet()
// delete mCond;
}
RowSet& RowSet::operator=(const RowSet& rhs)
RowSet &RowSet::operator=(const RowSet &rhs)
{
if (this != &rhs)
{
mItems = rhs.mItems;
mCat = rhs.mCat;
}
return *this;
}
RowSet& RowSet::operator=(RowSet&& rhs)
RowSet &RowSet::operator=(RowSet &&rhs)
{
if (this != &rhs)
{
std::swap(mItems, rhs.mItems);
mCat = rhs.mCat;
}
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());
stable_sort(mItems.begin(), mItems.end(), c);
return *this;
}
// --------------------------------------------------------------------
Category::Category(Datablock& db, const std::string& name, Validator* Validator)
: mDb(db), mName(name), mValidator(Validator)
, mHead(nullptr), mTail(nullptr), mIndex(nullptr)
Category::Category(Datablock &db, const std::string &name, Validator *Validator)
: mDb(db)
, mName(name)
, mValidator(Validator)
, mHead(nullptr)
, mTail(nullptr)
, mIndex(nullptr)
{
if (mName.empty())
throw ValidationError("invalid empty name for Category");
if (mValidator != nullptr)
{
mCatValidator = mValidator->getValidatorForCategory(mName);
if (mCatValidator != nullptr)
{
// make sure all required columns are added
for (auto& k: mCatValidator->mKeys)
for (auto &k : mCatValidator->mKeys)
addColumn(k);
for (auto& k: mCatValidator->mMandatoryFields)
for (auto &k : mCatValidator->mMandatoryFields)
addColumn(k);
mIndex = new CatIndex(this);
}
}
......@@ -1256,16 +1347,16 @@ Category::~Category()
delete mIndex;
}
void Category::setValidator(Validator* v)
void Category::setValidator(Validator *v)
{
mValidator = v;
if (mIndex != nullptr)
{
delete mIndex;
mIndex = nullptr;
}
if (mValidator != nullptr)
{
mCatValidator = mValidator->getValidatorForCategory(mName);
......@@ -1273,22 +1364,22 @@ void Category::setValidator(Validator* v)
{
mIndex = new CatIndex(this);
mIndex->reconstruct();
//#if DEBUG
// assert(mIndex->size() == size());
// mIndex->validate();
//#endif
//#if DEBUG
// assert(mIndex->size() == size());
// mIndex->validate();
//#endif
}
}
else
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;
......@@ -1297,18 +1388,18 @@ size_t Category::getColumnIndex(const std::string& name) const
if (iequals(name, mColumns[result].mName))
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);
if (iv == nullptr)
std::cerr << "Invalid name used '" + name + "' is not a known column in " + mName << std::endl;
}
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;
}
......@@ -1316,29 +1407,29 @@ const std::string& Category::getColumnName(size_t columnIx) const
std::vector<std::string> Category::getColumnNames() const
{
std::vector<std::string> result;
for (auto& c: mColumns)
for (auto &c : mColumns)
result.push_back(c.mName);
return result;
}
size_t Category::addColumn(const std::string& name)
size_t Category::addColumn(const std::string &name)
{
size_t result = getColumnIndex(name);
if (result == mColumns.size())
{
const ValidateItem* itemValidator = nullptr;
const ValidateItem *itemValidator = nullptr;
if (mCatValidator != nullptr)
{
itemValidator = mCatValidator->getValidatorForItem(name);
if (itemValidator == nullptr)
mValidator->reportError("tag " + name + " not allowed in Category " + mName, false);
}
mColumns.push_back({name, itemValidator});
}
return result;
}
......@@ -1363,17 +1454,17 @@ void Category::reorderByIndex()
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)
return;
std::vector<ItemRow*> rows;
std::vector<ItemRow *> rows;
for (auto itemRow = mHead; itemRow != nullptr; itemRow = itemRow->mNext)
rows.push_back(itemRow);
std::stable_sort(rows.begin(), rows.end(),
[&comparator](ItemRow* ia, ItemRow* ib)
[&comparator](ItemRow *ia, ItemRow *ib)
{
Row ra(ia);
Row rb(ib);
......@@ -1416,10 +1507,10 @@ std::string Category::getUniqueID(std::function<std::string(int)> generator)
size_t Category::size() const
{
size_t result = 0;
for (auto pi = mHead; pi != nullptr; pi = pi->mNext)
++result;
return result;
}
......@@ -1428,30 +1519,31 @@ bool Category::empty() const
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;
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())
{
size_t columnIx = ci - mColumns.begin();
for (auto pi = mHead; pi != nullptr; pi = pi->mNext)
pi->drop(columnIx);
mColumns.erase(ci);
}
}
Row Category::operator[](Condition&& cond)
Row Category::operator[](Condition &&cond)
{
Row result;
cond.prepare(*this);
for (auto r: *this)
for (auto r : *this)
{
if (cond(*this, r))
{
......@@ -1461,15 +1553,15 @@ Row Category::operator[](Condition&& cond)
}
return result;
}
}
// RowSet Category::find(Condition&& cond)
// {
// // return RowSet(*this, std::forward<Condition>(cond));
// RowSet result(*this);
// cond.prepare(*this);
// for (auto r: *this)
// {
// if (cond(*this, r))
......@@ -1478,13 +1570,13 @@ Row Category::operator[](Condition&& cond)
// return result;
// }
bool Category::exists(Condition&& cond) const
bool Category::exists(Condition &&cond) const
{
bool result = false;
cond.prepare(*this);
for (auto r: *this)
for (auto r : *this)
{
if (cond(*this, r))
{
......@@ -1500,7 +1592,7 @@ RowSet Category::orderBy(std::initializer_list<std::string> items)
{
RowSet result(*this);
result.insert(result.begin(), begin(), end());
return result.orderBy(items);
}
......@@ -1508,7 +1600,7 @@ void Category::clear()
{
delete mHead;
mHead = mTail = nullptr;
if (mIndex != nullptr)
{
delete mIndex;
......@@ -1516,8 +1608,8 @@ void Category::clear()
}
}
template<class Iter>
std::tuple<Row,bool> Category::emplace(Iter b, Iter e)
template <class Iter>
std::tuple<Row, bool> Category::emplace(Iter b, Iter e)
{
// First, make sure all mandatory fields are supplied
Row result;
......@@ -1525,15 +1617,15 @@ std::tuple<Row,bool> Category::emplace(Iter b, Iter e)
if (mCatValidator != nullptr and b != e)
{
for (auto& col: mColumns)
for (auto &col : mColumns)
{
auto iv = mCatValidator->getValidatorForItem(col.mName);
if (iv == nullptr)
continue;
bool seen = false;
for (auto v = b; v != e; ++v)
{
if (iequals(v->name(), col.mName))
......@@ -1542,23 +1634,23 @@ std::tuple<Row,bool> Category::emplace(Iter b, Iter e)
break;
}
}
if (not seen and iv->mMandatory)
throw std::runtime_error("missing mandatory field " + col.mName + " for Category " + mName);
}
if (mIndex != nullptr)
{
std::unique_ptr<ItemRow> nr(new ItemRow{nullptr, this, nullptr});
Row r(nr.get());
auto keys = keyFields();
auto keys = keyFields();
for (auto v = b; v != e; ++v)
{
if (keys.count(v->name()))
r.assign(v->name(), v->value(), true);
}
auto test = mIndex->find(nr.get());
if (test != nullptr)
{
......@@ -1578,7 +1670,7 @@ std::tuple<Row,bool> Category::emplace(Iter b, Iter e)
for (auto v = b; v != e; ++v)
r.assign(*v, true);
// if (isOrphan(r))
// 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)
if (mIndex != nullptr)
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());
}
size_t Category::erase(Condition&& cond)
size_t Category::erase(Condition &&cond)
{
size_t result = 0;
......@@ -1627,10 +1719,10 @@ size_t Category::erase(Condition&& cond)
++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;
......@@ -1652,26 +1744,26 @@ size_t Category::erase(Condition&& cond, std::function<void(const Row&)>&& verbo
return result;
}
void Category::eraseOrphans(Condition&& cond)
void Category::eraseOrphans(Condition &&cond)
{
std::vector<ItemRow*> remove;
std::vector<ItemRow *> remove;
cond.prepare(*this);
for (auto r: *this)
for (auto r : *this)
{
if (cond(*this, r) and isOrphan(r))
{
if (VERBOSE > 1)
std::cerr << "Removing orphaned record: " << std::endl
<< r << std::endl
<< std::endl;
<< r << std::endl
<< std::endl;
remove.push_back(r.mData);
}
}
for (auto r: remove)
for (auto r : remove)
erase(iterator(r));
}
......@@ -1694,7 +1786,7 @@ auto Category::erase(iterator pos) -> iterator
if (mIndex != nullptr)
mIndex->erase(r.mData);
if (r == mHead)
{
mHead = mHead->mNext;
......@@ -1716,25 +1808,25 @@ auto Category::erase(iterator pos) -> iterator
// links are created based on the _pdbx_item_linked_group_list entries
// 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.
// If all values in a child are the same as the specified parent ones
// the child is removed as well, recursively of course.
if (mValidator != nullptr)
{
for (auto& link: mValidator->getLinksForParent(mName))
for (auto &link : mValidator->getLinksForParent(mName))
{
auto childCat = mDb.get(link->mChildCategory);
if (childCat == nullptr)
continue;
Condition cond;
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);
}
......@@ -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);
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);
}
......@@ -1822,7 +1914,7 @@ Category::const_iterator Category::end() const
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(mCatValidator != nullptr);
......@@ -1832,7 +1924,7 @@ bool Category::hasParent(Row r, const Category& parentCat, const ValidateLink& l
Condition cond;
for (size_t ix = 0; ix < link.mChildKeys.size(); ++ix)
{
auto& name = link.mChildKeys[ix];
auto &name = link.mChildKeys[ix];
auto field = r[name];
if (field.empty())
{
......@@ -1841,7 +1933,7 @@ bool Category::hasParent(Row r, const Category& parentCat, const ValidateLink& l
}
else
{
const char* value = field.c_str();
const char *value = field.c_str();
cond = std::move(cond) and (Key(link.mParentKeys[ix]) == value);
}
}
......@@ -1866,16 +1958,16 @@ bool Category::isOrphan(Row r)
return false;
bool isOrphan = true;
for (auto& link: mValidator->getLinksForChild(mName))
for (auto &link : mValidator->getLinksForChild(mName))
{
auto parentCat = mDb.get(link->mParentCategory);
if (parentCat == nullptr)
continue;
Condition cond;
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);
}
......@@ -1902,18 +1994,18 @@ bool Category::hasChildren(Row r) const
bool result = false;
for (auto& link: mValidator->getLinksForParent(mName))
for (auto &link : mValidator->getLinksForParent(mName))
{
auto childCat = mDb.get(link->mChildCategory);
if (childCat == nullptr)
continue;
Condition cond;
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);
}
......@@ -1933,18 +2025,18 @@ bool Category::hasParents(Row r) const
bool result = false;
for (auto& link: mValidator->getLinksForChild(mName))
for (auto &link : mValidator->getLinksForChild(mName))
{
auto parentCat = mDb.get(link->mParentCategory);
if (parentCat == nullptr)
continue;
Condition cond;
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);
}
......@@ -1957,29 +2049,29 @@ bool Category::hasParents(Row r) const
return result;
}
RowSet Category::getChildren(Row r, const char* childCat)
RowSet Category::getChildren(Row r, const char *childCat)
{
return getChildren(r, mDb[childCat]);
}
RowSet Category::getChildren(Row r, Category& childCat)
RowSet Category::getChildren(Row r, Category &childCat)
{
assert(mValidator != nullptr);
assert(mCatValidator != nullptr);
RowSet result(childCat);
for (auto& link: mValidator->getLinksForParent(mName))
for (auto &link : mValidator->getLinksForParent(mName))
{
if (link->mChildCategory != childCat.mName)
continue;
Condition cond;
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);
}
......@@ -1993,29 +2085,29 @@ RowSet Category::getChildren(Row r, Category& childCat)
return result;
}
RowSet Category::getParents(Row r, const char* parentCat)
RowSet Category::getParents(Row r, const char *parentCat)
{
return getParents(r, mDb[parentCat]);
}
RowSet Category::getParents(Row r, Category& parentCat)
RowSet Category::getParents(Row r, Category &parentCat)
{
assert(mValidator != nullptr);
assert(mCatValidator != nullptr);
RowSet result(parentCat);
for (auto& link: mValidator->getLinksForChild(mName))
for (auto &link : mValidator->getLinksForChild(mName))
{
if (link->mParentCategory != parentCat.mName)
continue;
Condition cond;
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);
}
......@@ -2029,12 +2121,12 @@ RowSet Category::getParents(Row r, Category& parentCat)
return result;
}
RowSet Category::getLinked(Row r, const char* cat)
RowSet Category::getLinked(Row r, const char *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);
if (result.empty())
......@@ -2045,7 +2137,7 @@ RowSet Category::getLinked(Row r, Category& cat)
bool Category::isValid()
{
bool result = true;
if (mValidator == nullptr)
throw std::runtime_error("no Validator specified");
......@@ -2055,16 +2147,16 @@ bool Category::isValid()
std::cerr << "Skipping validation of empty Category " << mName << std::endl;
return true;
}
if (mCatValidator == nullptr)
{
mValidator->reportError("undefined Category " + mName, false);
return false;
}
auto mandatory = mCatValidator->mMandatoryFields;
for (auto& col: mColumns)
for (auto &col : mColumns)
{
auto iv = mCatValidator->getValidatorForItem(col.mName);
if (iv == nullptr)
......@@ -2072,48 +2164,48 @@ bool Category::isValid()
mValidator->reportError("Field " + col.mName + " is not valid in Category " + mName, false);
result = false;
}
col.mValidator = iv;
mandatory.erase(col.mName);
}
if (not mandatory.empty())
{
mValidator->reportError("In Category " + mName + " the following mandatory fields are missing: " + ba::join(mandatory, ", "), false);
result = false;
}
//#if not defined(NDEBUG)
// // check index?
// if (mIndex)
// {
// mIndex->validate();
// for (auto r: *this)
// {
// if (mIndex->find(r.mData) != r.mData)
// mValidator->reportError("Key not found in index for Category " + mName);
// }
// }
//#endif
//#if not defined(NDEBUG)
// // check index?
// if (mIndex)
// {
// mIndex->validate();
// for (auto r: *this)
// {
// if (mIndex->find(r.mData) != r.mData)
// mValidator->reportError("Key not found in index for Category " + mName);
// }
// }
//#endif
// validate all values
mandatory = mCatValidator->mMandatoryFields;
for (auto ri = mHead; ri != nullptr; ri = ri->mNext)
{
for (size_t cix = 0; cix < mColumns.size(); ++cix)
{
bool seen = false;
auto iv = mColumns[cix].mValidator;
if (iv == nullptr)
{
mValidator->reportError("invalid field " + mColumns[cix].mName + " for Category " + mName, false);
result = false;
continue;
}
for (auto vi = ri->mValues; vi != nullptr; vi = vi->mNext)
{
if (vi->mColumnIndex == cix)
......@@ -2123,17 +2215,17 @@ bool Category::isValid()
{
(*iv)(vi->mText);
}
catch(const std::exception& e)
catch (const std::exception &e)
{
mValidator->reportError("Error validating " + mColumns[cix].mName + ": " + e.what(), false);
continue;
}
}
}
if (seen or ri != mHead)
continue;
if (iv != nullptr and iv->mMandatory)
{
mValidator->reportError("missing mandatory field " + mColumns[cix].mName + " for Category " + mName, false);
......@@ -2141,34 +2233,34 @@ bool Category::isValid()
}
}
}
return result;
}
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);
if (parent == nullptr)
continue;
size_t missing = 0;
for (auto r: *this)
for (auto r : *this)
if (not hasParent(r, *parent, *linkValidator))
++missing;
if (missing)
{
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)
throw std::runtime_error("no Validator defined yet");
......@@ -2179,12 +2271,12 @@ iset Category::fields() const
{
if (mValidator == nullptr)
throw std::runtime_error("No Validator specified");
if (mCatValidator == nullptr)
mValidator->reportError("undefined Category", true);
iset result;
for (auto& iv: mCatValidator->mItemValidators)
for (auto &iv : mCatValidator->mItemValidators)
result.insert(iv.mTag);
return result;
}
......@@ -2193,10 +2285,10 @@ iset Category::mandatoryFields() const
{
if (mValidator == nullptr)
throw std::runtime_error("No Validator specified");
if (mCatValidator == nullptr)
mValidator->reportError("undefined Category", true);
return mCatValidator->mMandatoryFields;
}
......@@ -2204,28 +2296,121 @@ iset Category::keyFields() const
{
if (mValidator == nullptr)
throw std::runtime_error("No Validator specified");
if (mCatValidator == nullptr)
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
{
if (mValidator == nullptr)
throw std::runtime_error("No Validator specified");
if (mCatValidator == nullptr)
mValidator->reportError("undefined Category", true);
std::set<size_t> result;
for (auto& k: mCatValidator->mKeys)
for (auto &k : mCatValidator->mKeys)
result.insert(getColumnIndex(k));
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&
// {
// mCurrent = Row(mCurrent.data()->mNext);
......@@ -2241,52 +2426,27 @@ std::set<size_t> Category::keyFieldsByIndex() const
namespace detail
{
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()))
size_t writeValue(std::ostream &os, std::string value, size_t offset, size_t width)
{
os << value;
if (value.length() < width)
{
os << std::string(width - value.length(), ' ');
offset += width;
}
else
if (value.find('\n') != std::string::npos or width == 0 or value.length() >= 132) // write as text field
{
os << ' ';
offset += value.length() + 1;
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
{
bool done = false;
for (char q: { '\'', '"'})
else if (isUnquotedString(value.c_str()))
{
auto p = value.find(q); // see if we can use the quote character
while (p != std::string::npos and isNonBlank(value[p + 1]) and value[p + 1] != q)
p = value.find(q, p + 1);
if (p != std::string::npos)
continue;
os << q << value << q;
os << value;
if (value.length() + 2 < width)
if (value.length() < width)
{
os << std::string(width - value.length() - 2, ' ');
os << std::string(width - value.length(), ' ');
offset += width;
}
else
......@@ -2294,47 +2454,72 @@ size_t writeValue(std::ostream& os, std::string value, size_t offset, size_t wid
os << ' ';
offset += value.length() + 1;
}
done = true;
break;
}
if (not done)
else
{
if (offset > 0)
os << std::endl;
os << ';' << value << std::endl
<< ';' << std::endl;
offset = 0;
bool done = false;
for (char q : {'\'', '"'})
{
auto p = value.find(q); // see if we can use the quote character
while (p != std::string::npos and isNonBlank(value[p + 1]) and value[p + 1] != q)
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())
return;
// If the first Row has a next, we need a loop_
bool needLoop = (mHead->mNext != nullptr);
if (needLoop)
{
os << "loop_" << std::endl;
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;
columnWidths.push_back(2);
}
for (auto Row = mHead; Row != nullptr; Row = Row->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
if (strchr(v->mText, '\n') == nullptr)
{
size_t l = strlen(v->mText);
if (not isUnquotedString(v->mText))
l += 2;
......@@ -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;
for (size_t cix: order)
for (size_t cix : order)
{
size_t w = columnWidths[cix];
std::string s;
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
break;
}
}
if (s.empty())
s = "?";
size_t l = s.length();
if (not isUnquotedString(s.c_str()))
l += 2;
......@@ -2387,41 +2572,41 @@ void Category::write(std::ostream& os, const std::vector<size_t>& order, bool in
os << std::endl;
offset = 0;
}
offset = detail::writeValue(os, s, offset, w);
if (offset >= 132)
{
os << std::endl;
offset = 0;
}
}
if (offset > 0)
os << std::endl;
}
}
else
{
// first find the indent level
// first find the indent level
size_t l = 0;
for (auto& col: mColumns)
for (auto &col : mColumns)
{
std::string tag = '_' + mName + '.' + col.mName;
if (l < tag.length())
l = tag.length();
}
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, ' ');
std::string s;
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
break;
}
}
if (s.empty())
s = "?";
size_t offset = l;
if (s.length() + l >= kMaxLineLength)
{
......@@ -2450,23 +2635,23 @@ void Category::write(std::ostream& os, const std::vector<size_t>& order, bool in
os << "# " << std::endl;
}
void Category::write(std::ostream& os)
void Category::write(std::ostream &os)
{
std::vector<size_t> order(mColumns.size());
iota(order.begin(), order.end(), 0);
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
for (auto& c: columns)
for (auto &c : columns)
addColumn(c);
std::vector<size_t> order;
order.reserve(mColumns.size());
for (auto& c: columns)
for (auto &c : columns)
order.push_back(getColumnIndex(c));
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
if (colIx >= mColumns.size())
throw std::runtime_error("Invalid column " + value + " for " + mName);
auto& col = mColumns[colIx];
auto &col = mColumns[colIx];
// check the value
if (col.mValidator)
......@@ -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?
std::string oldValue = rows.front()[tag].c_str();
for (auto &row: rows)
for (auto &row : rows)
{
if (oldValue != row[tag].c_str())
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;
// update rows, but do not cascade
for (auto &row: rows)
for (auto &row : rows)
row.assign(colIx, value, true);
// see if we need to update any child categories that depend on this value
auto& validator = getValidator();
auto& db = mDb;
auto &validator = getValidator();
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);
if (childCat == nullptr)
......@@ -2527,7 +2712,7 @@ void Category::update_value(RowSet &&rows, const std::string &tag, const std::st
Condition cond;
std::string childTag;
for (size_t ix = 0; ix < linked->mParentKeys.size(); ++ix)
{
std::string pk = linked->mParentKeys[ix];
......@@ -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();
}
auto children = RowSet{ *childCat, std::move(cond) };
auto children = RowSet{*childCat, std::move(cond)};
if (children.empty())
continue;
......@@ -2554,10 +2739,10 @@ void Category::update_value(RowSet &&rows, const std::string &tag, const std::st
RowSet process(*childCat);
for (auto child: children)
for (auto child : children)
{
Condition cond_c;
for (size_t ix = 0; ix < linked->mParentKeys.size(); ++ix)
{
std::string pk = linked->mParentKeys[ix];
......@@ -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
Condition check;
for (size_t ix = 0; ix < linked->mParentKeys.size(); ++ix)
{
std::string pk = linked->mParentKeys[ix];
......@@ -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();
}
if (childCat->exists(std::move(check))) // phew..., narrow escape
if (childCat->exists(std::move(check))) // phew..., narrow escape
continue;
// create the actual copy, if we can...
......@@ -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)
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
// finally, update the children
if (not process.empty())
childCat->update_value(std::move(process), childTag, value);
}
}
}
}
// --------------------------------------------------------------------
Row::Row(const Row& rhs)
Row::Row(const Row &rhs)
: mData(rhs.mData)
, mCascade(rhs.mCascade)
{
}
Row::Row(Row&& rhs)
Row::Row(Row &&rhs)
: mData(rhs.mData)
, mCascade(rhs.mCascade)
{
......@@ -2634,7 +2819,6 @@ Row::Row(Row&& rhs)
Row::~Row()
{
}
void Row::next()
......@@ -2643,30 +2827,31 @@ void Row::next()
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;
return *this;
}
Row& Row::operator=(const Row& rhs)
Row &Row::operator=(const Row &rhs)
{
mData = rhs.mData;
mCascade = rhs.mCascade;
return *this;
}
void Row::assign(const std::vector<Item>& values)
void Row::assign(const std::vector<Item> &values)
{
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& col = cat->mColumns[columnIx];
auto &col = cat->mColumns[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());
......@@ -2678,10 +2863,10 @@ void Row::assign(const std::vector<Item>& values)
// auto iv = col.mValidator;
if (mCascade)
{
auto& validator = cat->getValidator();
auto& db = cat->db();
auto &validator = cat->getValidator();
auto &db = cat->db();
for (auto linked: validator.getLinksForParent(cat->mName))
for (auto linked : validator.getLinksForParent(cat->mName))
{
auto childCat = db.get(linked->mChildCategory);
if (childCat == nullptr)
......@@ -2694,7 +2879,7 @@ void Row::assign(const std::vector<Item>& values)
std::string childTag;
std::vector<Item> newValues;
for (size_t ix = 0; ix < linked->mParentKeys.size(); ++ix)
{
std::string pk = linked->mParentKeys[ix];
......@@ -2708,46 +2893,46 @@ void Row::assign(const std::vector<Item>& values)
}
else
{
const char* value = (*this)[pk].c_str();
const char *value = (*this)[pk].c_str();
cond = std::move(cond) && (Key(ck) == value);
}
}
auto rows = childCat->find(std::move(cond));
for (auto& cr: rows)
for (auto &cr : rows)
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);
}
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
{
auto cat = mData->mCategory;
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;
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)
throw std::logic_error("invalid Row, no data assigning value '" + value + "' to column with index " + std::to_string(column));
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)
{
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)
break;
}
}
if (oldValue != nullptr and value == oldValue) // no need to update
if (oldValue != nullptr and value == oldValue) // no need to update
return;
std::string oldStrValue = oldValue ? oldValue : "";
......@@ -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
// before updating
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))
{
reinsert = cat->mIndex->find(mData);
......@@ -2784,7 +2969,7 @@ void Row::assign(size_t column, const std::string& value, bool skipUpdateLinked)
// first remove old value with cix
if (mData->mValues == nullptr)
; // nothing to do
; // nothing to do
else if (mData->mValues->mColumnIndex == column)
{
auto iv = mData->mValues;
......@@ -2802,7 +2987,7 @@ void Row::assign(size_t column, const std::string& value, bool skipUpdateLinked)
iv->mNext = nv->mNext;
nv->mNext = nullptr;
delete nv;
break;
}
}
......@@ -2810,8 +2995,8 @@ void Row::assign(size_t column, const std::string& value, bool skipUpdateLinked)
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)
mData->mValues = nv;
else
......@@ -2830,10 +3015,10 @@ void Row::assign(size_t column, const std::string& value, bool skipUpdateLinked)
auto iv = col.mValidator;
if (not skipUpdateLinked and iv != nullptr and mCascade)
{
auto& validator = cat->getValidator();
auto& db = cat->db();
auto &validator = cat->getValidator();
auto &db = cat->db();
for (auto linked: validator.getLinksForParent(cat->mName))
for (auto linked : validator.getLinksForParent(cat->mName))
{
auto childCat = db.get(linked->mChildCategory);
if (childCat == nullptr)
......@@ -2844,7 +3029,7 @@ void Row::assign(size_t column, const std::string& value, bool skipUpdateLinked)
Condition cond;
std::string childTag;
for (size_t ix = 0; ix < linked->mParentKeys.size(); ++ix)
{
std::string pk = linked->mParentKeys[ix];
......@@ -2859,7 +3044,7 @@ void Row::assign(size_t column, const std::string& value, bool skipUpdateLinked)
}
else
{
const char* pk_value = (*this)[pk].c_str();
const char *pk_value = (*this)[pk].c_str();
if (*pk_value == 0)
cond = std::move(cond) && Key(ck) == Empty();
else
......@@ -2881,7 +3066,7 @@ void Row::assign(size_t column, const std::string& value, bool skipUpdateLinked)
// we then skip this renam
Condition cond_n;
for (size_t ix = 0; ix < linked->mParentKeys.size(); ++ix)
{
std::string pk = linked->mParentKeys[ix];
......@@ -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;
else
{
const char* pk_value = (*this)[pk].c_str();
const char *pk_value = (*this)[pk].c_str();
if (*pk_value == 0)
cond_n = std::move(cond_n) && Key(ck) == Empty();
else
......@@ -2906,17 +3091,17 @@ void Row::assign(size_t column, const std::string& value, bool skipUpdateLinked)
{
if (cif::VERBOSE)
std::cerr << "Will not rename in child category since there are already rows that link to the parent" << std::endl;
continue;
}
for (auto& cr: rows)
for (auto &cr : rows)
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)
throw std::logic_error("invalid Rows in swap");
......@@ -2924,14 +3109,14 @@ void Row::swap(size_t cix, ItemRow* a, ItemRow* b)
assert(a->mCategory == b->mCategory);
if (a->mCategory != b->mCategory)
throw std::logic_error("Categories not same in swap");
auto cat = a->mCategory;
// If the field is part of the Key for this Category, remove it from the index
// before updating
bool reinsert = false;
if (cat->mIndex != nullptr and cat->keyFieldsByIndex().count(cix))
{
reinsert = true;
......@@ -2939,11 +3124,11 @@ void Row::swap(size_t cix, ItemRow* a, ItemRow* b)
cat->mIndex->erase(b);
}
ItemValue* ap = nullptr; // parent of ai
ItemValue* ai = nullptr;
ItemValue* bp = nullptr; // parent of bi
ItemValue* bi = nullptr;
ItemValue *ap = nullptr; // parent of ai
ItemValue *ai = nullptr;
ItemValue *bp = nullptr; // parent of bi
ItemValue *bi = nullptr;
if (a->mValues == nullptr)
;
else if (a->mValues->mColumnIndex == cix)
......@@ -2964,7 +3149,6 @@ void Row::swap(size_t cix, ItemRow* a, ItemRow* b)
}
}
if (b->mValues == nullptr)
;
else if (b->mValues->mColumnIndex == cix)
......@@ -3018,18 +3202,18 @@ void Row::swap(size_t cix, ItemRow* a, ItemRow* b)
auto parentColName = cat->getColumnName(cix);
// 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();
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())
continue;
auto childCat = cat->db().get(link->mChildCategory);
if (childCat == nullptr or childCat->empty())
continue;
auto childCatValidator = childCat->getCatValidator();
if (childCatValidator == nullptr)
continue;
......@@ -3057,13 +3241,13 @@ void Row::swap(size_t cix, ItemRow* a, ItemRow* b)
if (pcix == cix)
{
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;
}
else
{
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;
}
......@@ -3090,7 +3274,7 @@ void Row::swap(size_t cix, ItemRow* a, ItemRow* b)
// if (VERBOSE > 1)
// std::cerr << "Fixing link from " << cat->mName << " to " << childCat->mName << " with " << std::endl
// << cond[ab] << std::endl;
rs.push_back(childCat->find(std::move(cond[ab])));
}
......@@ -3098,17 +3282,17 @@ void Row::swap(size_t cix, ItemRow* a, ItemRow* b)
{
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
// that contains an empty value for all child columns...
// Now, that's not a real hit, is it?
size_t n = 0;
for (auto c: link->mChildKeys)
for (auto c : link->mChildKeys)
if (r[c].empty())
++n;
if (n == link->mChildKeys.size())
{
if (VERBOSE > 1)
......@@ -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;
if (mData != nullptr)
......@@ -3163,21 +3347,22 @@ void Row::lineNr(uint32_t l)
mData->mLineNr = l;
}
Row::const_iterator::const_iterator(ItemRow* data, ItemValue* ptr)
: mData(data), mPtr(ptr)
Row::const_iterator::const_iterator(ItemRow *data, ItemValue *ptr)
: mData(data)
, mPtr(ptr)
{
if (mPtr != nullptr)
fetch();
}
Row::const_iterator& Row::const_iterator::operator++()
Row::const_iterator &Row::const_iterator::operator++()
{
if (mPtr != nullptr)
mPtr = mPtr->mNext;
if (mPtr != nullptr)
fetch();
return *this;
}
......@@ -3188,7 +3373,7 @@ void Row::const_iterator::fetch()
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;
std::string catName = category->name();
......@@ -3209,28 +3394,29 @@ File::File()
{
}
File::File(std::istream& is, bool validate)
File::File(std::istream &is, bool validate)
: File()
{
load(is);
}
File::File(const std::string& path, bool validate)
File::File(const std::string &path, bool validate)
: File()
{
try
{
load(path);
}
catch (const std::exception& ex)
catch (const std::exception &ex)
{
std::cerr << "Error while loading file " << path << std::endl;
throw;
}
}
File::File(File&& rhs)
: mHead(nullptr), mValidator(nullptr)
File::File(File &&rhs)
: mHead(nullptr)
, mValidator(nullptr)
{
std::swap(mHead, rhs.mHead);
std::swap(mValidator, rhs.mValidator);
......@@ -3242,10 +3428,10 @@ File::~File()
delete mValidator;
}
void File::append(Datablock* e)
void File::append(Datablock *e)
{
e->setValidator(mValidator);
if (mHead == nullptr)
mHead = e;
else
......@@ -3261,23 +3447,23 @@ void File::append(Datablock* e)
ie->mNext = e;
break;
}
ie = ie->mNext;
}
}
}
void File::load(const std::string& p)
void File::load(const std::string &p)
{
fs::path path(p);
std::ifstream inFile(p, std::ios_base::in | std::ios_base::binary);
if (not inFile.is_open())
throw std::runtime_error("Could not open file: " + path.string());
io::filtering_stream<io::input> in;
std::string ext;
if (path.extension() == ".bz2")
{
in.push(io::bzip2_decompressor());
......@@ -3288,27 +3474,27 @@ void File::load(const std::string& p)
in.push(io::gzip_decompressor());
ext = path.stem().extension().string();
}
in.push(inFile);
try
{
load(in);
}
catch (const std::exception& ex)
catch (const std::exception &ex)
{
std::cerr << "Error loading file " << path << std::endl;
throw;
}
}
void File::save(const std::string& p)
void File::save(const std::string &p)
{
fs::path path(p);
std::ofstream outFile(p, std::ios_base::out | std::ios_base::binary);
io::filtering_stream<io::output> out;
if (path.extension() == ".gz")
{
out.push(io::gzip_compressor());
......@@ -3319,19 +3505,19 @@ void File::save(const std::string& p)
out.push(io::bzip2_compressor());
path = path.stem();
}
out.push(outFile);
save(out);
}
void File::load(std::istream& is)
void File::load(std::istream &is)
{
Validator* saved = mValidator;
Validator *saved = mValidator;
setValidator(nullptr);
Parser p(is, *this);
p.parseFile();
if (saved != nullptr)
{
setValidator(saved);
......@@ -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);
Parser p(is, *this);
p.parseSingleDatablock(datablock);
if (saved != nullptr)
{
setValidator(saved);
......@@ -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)
{
e->write(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)
{
e->write(os, 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))
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))
result = result->mNext;
if (result == nullptr)
......@@ -3398,7 +3584,7 @@ bool File::isValid()
{
if (VERBOSE)
std::cerr << "No dictionary loaded explicitly, loading default" << std::endl;
loadDictionary();
}
......@@ -3414,7 +3600,7 @@ void File::validateLinks() const
d->validateLinks();
}
const Validator& File::getValidator() const
const Validator &File::getValidator() const
{
if (mValidator == nullptr)
throw std::runtime_error("no Validator defined yet");
......@@ -3426,7 +3612,7 @@ void File::loadDictionary()
loadDictionary("mmcif_ddl");
}
void File::loadDictionary(const char* dict)
void File::loadDictionary(const char *dict)
{
fs::path dict_name(dict);
......@@ -3437,7 +3623,7 @@ void File::loadDictionary(const char* dict)
if (data)
loadDictionary(*data);
else
else
{
// might be a compressed dictionary on disk
fs::path p = dict;
......@@ -3449,7 +3635,7 @@ void File::loadDictionary(const char* dict)
#if defined(CACHE_DIR) and defined(DATA_DIR)
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;
if (fs::exists(p2))
......@@ -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());
......@@ -3488,7 +3674,7 @@ void File::loadDictionary(std::istream& is)
setValidator(v.release());
}
void File::setValidator(Validator* v)
void File::setValidator(Validator *v)
{
mValidator = v;
......@@ -3496,13 +3682,13 @@ void File::setValidator(Validator* v)
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)
d->getTagOrder(tags);
}
auto File::iterator::operator++() -> iterator&
auto File::iterator::operator++() -> iterator &
{
mCurrent = mCurrent->mNext;
return *this;
......@@ -3518,4 +3704,4 @@ auto File::end() const -> iterator
return iterator(nullptr);
}
}
} // namespace cif
......@@ -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.
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)
, mCompoundID(compoundID)
, mAsymID(asymID)
......@@ -804,7 +804,7 @@ Residue::Residue(const Structure &structure, const std::string &compoundID,
for (auto &a : mStructure->atoms())
{
if (a.labelAsymID() != mAsymID or
a.labelCompID() != mCompoundID)
a.labelCompID() != mCompoundID)
continue;
if (compoundID == "HOH")
......@@ -830,7 +830,7 @@ Residue::Residue(const Structure &structure, const std::string &compoundID, cons
}
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)
, mCompoundID(compoundID)
, mAsymID(asymID)
......@@ -845,7 +845,7 @@ Residue::Residue(const Structure &structure, const std::string &compoundID,
continue;
if (a.labelAsymID() != mAsymID or
a.labelCompID() != mCompoundID)
a.labelCompID() != mCompoundID)
continue;
mAtoms.push_back(a);
......@@ -968,7 +968,7 @@ std::string Residue::unique_alt_id() const
throw std::runtime_error("Invalid Residue object");
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() : "";
}
......@@ -1104,7 +1104,7 @@ std::tuple<Point, float> Residue::centerAndRadius() const
bool Residue::hasAlternateAtoms() const
{
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
......@@ -1476,7 +1476,7 @@ float Monomer::chiralVolume() const
auto atom3 = atomByID("CD2");
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")
{
......@@ -1486,7 +1486,7 @@ float Monomer::chiralVolume() const
auto atom3 = atomByID("CG2");
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;
......@@ -1740,6 +1740,14 @@ File::~File()
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)
{
mImpl->load(p);
......@@ -1778,8 +1786,11 @@ Structure::Structure(File &f, size_t modelNr, StructureOpenOptions options)
: mFile(f)
, mModelNr(modelNr)
{
auto &db = *mFile.impl().mDb;
auto &atomCat = db["atom_site"];
auto db = mFile.impl().mDb;
if (db == nullptr)
throw std::logic_error("Empty file!");
auto &atomCat = (*db)["atom_site"];
for (auto &a : atomCat)
{
......@@ -1864,13 +1875,13 @@ void Structure::updateAtomIndex()
iota(mAtomIndex.begin(), mAtomIndex.end(), 0);
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()
{
sort(mAtoms.begin(), mAtoms.end(), [](auto &a, auto &b)
{ return a.compare(b) < 0; });
{ return a.compare(b) < 0; });
int id = 1;
for (auto &atom : mAtoms)
......@@ -1914,8 +1925,8 @@ AtomView Structure::waters() const
Atom Structure::getAtomByID(std::string id) const
{
auto i = std::lower_bound(mAtomIndex.begin(), mAtomIndex.end(),
id, [this](auto &a, auto &b)
{ return mAtoms[a].id() < b; });
id, [this](auto &a, auto &b)
{ return mAtoms[a].id() < b; });
// auto i = find_if(mAtoms.begin(), mAtoms.end(),
// [&id](auto& a) { return a.id() == id; });
......@@ -1931,10 +1942,10 @@ Atom Structure::getAtomByLabel(const std::string &atomID, const std::string &asy
for (auto &a : mAtoms)
{
if (a.labelAtomID() == atomID and
a.labelAsymID() == asymID and
a.labelCompID() == compID and
a.labelSeqID() == seqID and
a.labelAltID() == altID)
a.labelAsymID() == asymID and
a.labelCompID() == compID and
a.labelSeqID() == seqID and
a.labelAltID() == altID)
{
return a;
}
......@@ -2013,12 +2024,12 @@ std::tuple<char, int, char> Structure::MapLabelToAuth(
try
{
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)
{
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;
......@@ -2037,7 +2048,7 @@ std::tuple<char, int, char> Structure::MapLabelToAuth(
r.front().get("pdb_strand_id", "pdb_seq_num", "pdb_ins_code");
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;
}
......@@ -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,
const std::string &compID, const std::string &iCode) const
const std::string &compID, const std::string &iCode) const
{
auto &db = datablock();
......@@ -2161,10 +2172,10 @@ void Structure::insertCompound(const std::string &compoundID, bool isEntity)
if (r.empty())
{
chemComp.emplace({{"id", compoundID},
{"name", compound->name()},
{"formula", compound->formula()},
{"formula_weight", compound->formulaWeight()},
{"type", compound->type()}});
{"name", compound->name()},
{"formula", compound->formula()},
{"formula_weight", compound->formulaWeight()},
{"type", compound->type()}});
}
if (isEntity)
......@@ -2176,13 +2187,13 @@ void Structure::insertCompound(const std::string &compoundID, bool isEntity)
std::string entityID = std::to_string(entity.size() + 1);
entity.emplace({{"id", entityID},
{"type", "non-polymer"},
{"pdbx_description", compound->name()},
{"formula_weight", compound->formulaWeight()}});
{"type", "non-polymer"},
{"pdbx_description", compound->name()},
{"formula_weight", compound->formulaWeight()}});
pdbxEntityNonpoly.emplace({{"entity_id", entityID},
{"name", compound->name()},
{"comp_id", compoundID}});
{"name", compound->name()},
{"comp_id", compoundID}});
}
}
}
......@@ -2305,7 +2316,7 @@ void Structure::moveAtom(Atom &a, Point p)
}
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;
......@@ -2333,15 +2344,15 @@ void Structure::changeResidue(const Residue &res, const std::string &newCompound
{
entityID = entity.getUniqueID("");
entity.emplace({{"id", entityID},
{"type", "non-polymer"},
{"pdbx_description", compound->name()},
{"formula_weight", compound->formulaWeight()}});
{"type", "non-polymer"},
{"pdbx_description", compound->name()},
{"formula_weight", compound->formulaWeight()}});
}
auto &pdbxEntityNonpoly = db["pdbx_entity_nonpoly"];
pdbxEntityNonpoly.emplace({{"entity_id", entityID},
{"name", compound->name()},
{"comp_id", newCompound}});
{"name", compound->name()},
{"comp_id", newCompound}});
auto &pdbxNonPolyScheme = db["pdbx_nonpoly_scheme"];
for (auto &nps : pdbxNonPolyScheme.find("asym_id"_key == asymID))
......@@ -2356,10 +2367,10 @@ void Structure::changeResidue(const Residue &res, const std::string &newCompound
if (not chemComp.exists(cif::Key("id") == newCompound))
{
chemComp.emplace({{"id", newCompound},
{"name", compound->name()},
{"formula", compound->formula()},
{"formula_weight", compound->formulaWeight()},
{"type", compound->type()}});
{"name", compound->name()},
{"formula", compound->formula()},
{"formula_weight", compound->formulaWeight()},
{"type", compound->type()}});
}
// update the struct_asym for the new entity
......@@ -2377,7 +2388,7 @@ void Structure::changeResidue(const Residue &res, const std::string &newCompound
tie(a1, a2) = a;
auto i = find_if(atoms.begin(), atoms.end(), [&](const Atom &a)
{ return a.labelAtomID() == a1; });
{ return a.labelAtomID() == a1; });
if (i == atoms.end())
{
std::cerr << "Missing atom for atom ID " << a1 << std::endl;
......@@ -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()
{
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)
mmcif::CompoundFactory::instance().pushDictionary("./UN_.cif");
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