This commit is contained in:
Maarten L. Hekkelman
2026-02-10 16:53:18 +01:00
parent abc61afe7d
commit 25a73d288d
25 changed files with 4119 additions and 4009 deletions

View File

@@ -249,28 +249,28 @@ write_version_header("${CMAKE_CURRENT_SOURCE_DIR}/src/" LIB_NAME "LibCIFPP")
# Sources
set(project_sources
${CMAKE_CURRENT_SOURCE_DIR}/src/category.cpp
# ${CMAKE_CURRENT_SOURCE_DIR}/src/condition.cpp
# ${CMAKE_CURRENT_SOURCE_DIR}/src/datablock.cpp
# ${CMAKE_CURRENT_SOURCE_DIR}/src/dictionary_parser.cpp
# ${CMAKE_CURRENT_SOURCE_DIR}/src/file.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/condition.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/datablock.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/dictionary_parser.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/file.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/item.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/parser.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/row.cpp
# ${CMAKE_CURRENT_SOURCE_DIR}/src/validate.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/validate.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/text.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/utilities.cpp
# ${CMAKE_CURRENT_SOURCE_DIR}/src/atom_type.cpp
# ${CMAKE_CURRENT_SOURCE_DIR}/src/compound.cpp
# ${CMAKE_CURRENT_SOURCE_DIR}/src/point.cpp
# ${CMAKE_CURRENT_SOURCE_DIR}/src/symmetry.cpp
# ${CMAKE_CURRENT_SOURCE_DIR}/src/model.cpp
# ${CMAKE_CURRENT_SOURCE_DIR}/src/pdb/cif2pdb.cpp
# ${CMAKE_CURRENT_SOURCE_DIR}/src/pdb/pdb2cif.cpp
# ${CMAKE_CURRENT_SOURCE_DIR}/src/pdb/pdb_record.hpp
# ${CMAKE_CURRENT_SOURCE_DIR}/src/pdb/pdb2cif_remark_3.hpp
# ${CMAKE_CURRENT_SOURCE_DIR}/src/pdb/pdb2cif_remark_3.cpp
# ${CMAKE_CURRENT_SOURCE_DIR}/src/pdb/reconstruct.cpp
# ${CMAKE_CURRENT_SOURCE_DIR}/src/pdb/validate-pdbx.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/atom_type.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/compound.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/point.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/symmetry.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/model.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/pdb/cif2pdb.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/pdb/pdb2cif.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/pdb/pdb_record.hpp
${CMAKE_CURRENT_SOURCE_DIR}/src/pdb/pdb2cif_remark_3.hpp
${CMAKE_CURRENT_SOURCE_DIR}/src/pdb/pdb2cif_remark_3.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/pdb/reconstruct.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/pdb/validate-pdbx.cpp
)
set(project_headers

View File

@@ -124,16 +124,6 @@ class multiple_results_error : public std::runtime_error
}
};
// --------------------------------------------------------------------
// These should be moved elsewhere, one day.
/// \cond
template <typename T>
inline constexpr bool is_optional_v = false;
template <typename T>
inline constexpr bool is_optional_v<std::optional<T>> = true;
/// \endcond
// --------------------------------------------------------------------
/// The class category is a sequence container for rows of data values.
@@ -154,8 +144,8 @@ class category
friend class iterator_impl_base;
using value_type = row_handle;
using reference = value_type;
using const_reference = const value_type;
using reference = row_handle;
using const_reference = const_row_handle;
using iterator = iterator_impl<>;
using const_iterator = const_iterator_impl<>;
@@ -296,7 +286,7 @@ class category
/// the category is empty.
[[nodiscard]] const_reference front() const
{
return { const_cast<category &>(*this), const_cast<row &>(*m_head) };
return { *this, *m_head };
}
/// @brief Return a reference to the last row in this category.
@@ -312,7 +302,7 @@ class category
/// the category is empty.
[[nodiscard]] const_reference back() const
{
return { const_cast<category &>(*this), const_cast<row &>(*m_tail) };
return { *this, *m_tail };
}
/// Return an iterator to the first row
@@ -376,7 +366,7 @@ class category
struct key_element_type
{
std::string name; ///< Name of the item
std::string value; ///< Value to be found
item_value value; ///< Value to be found
bool may_be_null = false; ///< If true, value should be same or empty
};
@@ -388,13 +378,10 @@ class category
/// @return The row found in the index, or an undefined row_handle
row_handle operator[](const key_type &key);
/// @brief Return a const row_handle for the row specified by \a key
/// @brief Return a const_row_handle for the row specified by \a key
/// @param key The value for the key, items specified in the dictionary should have a value
/// @return The row found in the index, or an undefined row_handle
const row_handle operator[](const key_type &key) const
{
return const_cast<category *>(this)->operator[](key);
}
const_row_handle operator[](const key_type &key) const;
// --------------------------------------------------------------------
@@ -592,7 +579,7 @@ class category
/// there are is not exactly one row matching @a cond
/// @param cond The condition to search for
/// @return Row handle to the row found
const row_handle find1(condition &&cond) const
const_row_handle find1(condition &&cond) const
{
return find1(cbegin(), std::move(cond));
}
@@ -602,7 +589,7 @@ class category
/// @param pos The position to start the search
/// @param cond The condition to search for
/// @return Row handle to the row found
const row_handle find1(const_iterator pos, condition &&cond) const
const_row_handle find1(const_iterator pos, condition &&cond) const
{
auto h = find(pos, std::move(cond));
@@ -727,7 +714,7 @@ class category
/// @brief Return a const row handle to the first row that matches @a cond
/// @param cond The condition to search for
/// @return The const handle to the row that matches or an empty row_handle
const row_handle find_first(condition &&cond) const
const_row_handle find_first(condition &&cond) const
{
return find_first(cbegin(), std::move(cond));
}
@@ -736,11 +723,11 @@ class category
/// @param pos The location to start searching
/// @param cond The condition to search for
/// @return The const handle to the row that matches or an empty row_handle
const row_handle find_first(const_iterator pos, condition &&cond) const
const_row_handle find_first(const_iterator pos, condition &&cond) const
{
auto h = find(pos, std::move(cond));
return h.empty() ? row_handle{} : *h.begin();
return h.empty() ? const_row_handle{} : *h.begin();
}
/// @brief Return the value for item @a item for the first row that matches condition @a cond
@@ -1041,7 +1028,7 @@ class category
// --------------------------------------------------------------------
using value_provider_type = std::function<std::string_view(std::string_view)>;
using value_provider_type = std::function<item_value(const item_value &)>;
/// \brief Update a single item named @a item_name in the rows that match
/// \a cond to values provided by a callback function \a value_provider
@@ -1073,7 +1060,7 @@ class category
/// That means, child categories are updated if the links are absolute
/// and unique. If they are not, the child category rows are split.
void update_value(condition &&cond, std::string_view item_name, std::string_view value)
void update_value(condition &&cond, std::string_view item_name, const item_value &value)
{
auto rs = find(std::move(cond));
std::vector<row_handle> rows;
@@ -1087,9 +1074,9 @@ class category
/// That means, child categories are updated if the links are absolute
/// and unique. If they are not, the child category rows are split.
void update_value(const std::vector<row_handle> &rows, std::string_view item_name, std::string_view value)
void update_value(const std::vector<row_handle> &rows, std::string_view item_name, const item_value &value)
{
update_value(rows, item_name, [value](std::string_view)
update_value(rows, item_name, [value](const item_value &)
{ return value; });
}
@@ -1101,7 +1088,7 @@ class category
/// @brief Return the name for item with index @a ix
/// @param ix The index number
/// @return The name of the item
[[nodiscard]] std::string_view get_item_name(uint16_t ix) const
[[nodiscard]] const std::string &get_item_name(uint16_t ix) const
{
if (ix >= m_items.size())
throw std::out_of_range("item index is out of range");

View File

@@ -152,16 +152,16 @@ namespace detail
virtual ~condition_impl() = default;
virtual condition_impl *prepare(const category &) { return this; }
[[nodiscard]] virtual bool test(row_handle) const = 0;
[[nodiscard]] virtual bool test(const_row_handle) const = 0;
virtual void str(std::ostream &) const = 0;
[[nodiscard]] virtual std::optional<row_handle> single() const { return {}; };
[[nodiscard]] virtual std::optional<const_row_handle> single() const { return {}; };
virtual bool equals([[maybe_unused]] const condition_impl *rhs) const { return false; }
};
struct all_condition_impl : public condition_impl
{
[[nodiscard]] bool test(row_handle) const override { return true; }
[[nodiscard]] bool test(const_row_handle) const override { return true; }
void str(std::ostream &os) const override { os << "*"; }
};
@@ -244,7 +244,7 @@ class condition
* @return true If there is a match
* @return false If there is no match
*/
bool operator()(row_handle r) const
bool operator()(const_row_handle r) const
{
assert(this->m_impl != nullptr);
assert(this->m_prepared);
@@ -265,12 +265,12 @@ class condition
* @brief If the prepare step found out there is only one hit
* this single hit can be returned by this method.
*
* @return std::optional<row_handle> The result will contain
* @return std::optional<const_row_handle> The result will contain
* a row reference if there is a single hit, it will be empty otherwise
*/
[[nodiscard]] std::optional<row_handle> single() const
[[nodiscard]] std::optional<const_row_handle> single() const
{
return m_impl ? m_impl->single() : std::optional<row_handle>();
return m_impl ? m_impl->single() : std::optional<const_row_handle>();
}
friend condition operator||(condition &&a, condition &&b); /**< Return a condition which is the logical OR or condition @a and @b */
@@ -327,7 +327,7 @@ namespace detail
return this;
}
[[nodiscard]] bool test(row_handle r) const override
[[nodiscard]] bool test(const_row_handle r) const override
{
return r[m_item_ix].empty();
}
@@ -354,7 +354,7 @@ namespace detail
return this;
}
[[nodiscard]] bool test(row_handle r) const override
[[nodiscard]] bool test(const_row_handle r) const override
{
return not r[m_item_ix].empty();
}
@@ -378,7 +378,7 @@ namespace detail
condition_impl *prepare(const category &c) override;
[[nodiscard]] bool test(row_handle r) const override
[[nodiscard]] bool test(const_row_handle r) const override
{
return m_single_hit.has_value() ? *m_single_hit == r : r[m_item_ix].compare(m_value, m_icase) == 0;
}
@@ -388,7 +388,7 @@ namespace detail
os << m_item_name << (m_icase ? "^ " : " ") << " == " << m_value;
}
[[nodiscard]] std::optional<row_handle> single() const override
[[nodiscard]] std::optional<const_row_handle> single() const override
{
return m_single_hit;
}
@@ -411,7 +411,7 @@ namespace detail
uint16_t m_item_ix = 0;
bool m_icase = false;
item_value m_value;
std::optional<row_handle> m_single_hit;
std::optional<const_row_handle> m_single_hit;
};
struct key_equals_or_empty_condition_impl : public condition_impl
@@ -431,7 +431,7 @@ namespace detail
return this;
}
[[nodiscard]] bool test(row_handle r) const override
[[nodiscard]] bool test(const_row_handle r) const override
{
bool result = false;
if (m_single_hit.has_value())
@@ -446,7 +446,7 @@ namespace detail
os << '(' << m_item_name << (m_icase ? "^ " : " ") << " == " << m_value << " OR " << m_item_name << " IS NULL)";
}
[[nodiscard]] std::optional<row_handle> single() const override
[[nodiscard]] std::optional<const_row_handle> single() const override
{
return m_single_hit;
}
@@ -469,7 +469,7 @@ namespace detail
uint16_t m_item_ix = 0;
item_value &m_value;
bool m_icase = false;
std::optional<row_handle> m_single_hit;
std::optional<const_row_handle> m_single_hit;
};
struct key_compare_condition_impl : public condition_impl
@@ -489,7 +489,7 @@ namespace detail
return this;
}
[[nodiscard]] bool test(row_handle r) const override
[[nodiscard]] bool test(const_row_handle r) const override
{
return m_compare(r, m_icase);
}
@@ -502,7 +502,7 @@ namespace detail
std::string m_item_name;
uint16_t m_item_ix = 0;
bool m_icase = false;
std::function<bool(row_handle, bool)> m_compare;
std::function<bool(const_row_handle, bool)> m_compare;
std::string m_str;
};
@@ -520,7 +520,7 @@ namespace detail
return this;
}
[[nodiscard]] bool test(row_handle r) const override
[[nodiscard]] bool test(const_row_handle r) const override
{
auto txt = r[m_item_ix].get<std::string>();
return std::regex_match(txt.begin(), txt.end(), mRx);
@@ -546,7 +546,7 @@ namespace detail
{
}
[[nodiscard]] bool test(row_handle r) const override
[[nodiscard]] bool test(const_row_handle r) const override
{
auto &c = r.get_category();
@@ -578,7 +578,7 @@ namespace detail
{
}
[[nodiscard]] bool test(row_handle r) const override
[[nodiscard]] bool test(const_row_handle r) const override
{
auto &c = r.get_category();
@@ -648,7 +648,7 @@ namespace detail
condition_impl *prepare(const category &c) override;
[[nodiscard]] bool test(row_handle r) const override;
[[nodiscard]] bool test(const_row_handle r) const override;
void str(std::ostream &os) const override
{
@@ -668,9 +668,9 @@ namespace detail
os << ')';
}
[[nodiscard]] std::optional<row_handle> single() const override
[[nodiscard]] std::optional<const_row_handle> single() const override
{
std::optional<row_handle> result;
std::optional<const_row_handle> result;
for (auto sub : m_sub)
{
@@ -695,7 +695,7 @@ namespace detail
static condition_impl *combine_equal(std::vector<and_condition_impl *> &subs, or_condition_impl *oc);
std::vector<condition_impl *> m_sub;
std::optional<row_handle> m_single; // Potential result of index lookup
std::optional<const_row_handle> m_single; // Potential result of index lookup
};
struct or_condition_impl : public condition_impl
@@ -731,7 +731,7 @@ namespace detail
condition_impl *prepare(const category &c) override;
[[nodiscard]] bool test(row_handle r) const override
[[nodiscard]] bool test(const_row_handle r) const override
{
bool result = false;
@@ -762,9 +762,9 @@ namespace detail
os << ')';
}
[[nodiscard]] std::optional<row_handle> single() const override
[[nodiscard]] std::optional<const_row_handle> single() const override
{
std::optional<row_handle> result;
std::optional<const_row_handle> result;
for (auto sub : m_sub)
{
@@ -807,7 +807,7 @@ namespace detail
return this;
}
[[nodiscard]] bool test(row_handle r) const override
[[nodiscard]] bool test(const_row_handle r) const override
{
return not mA->test(r);
}
@@ -863,26 +863,6 @@ inline condition operator or(condition &&a, condition &&b)
return condition(new detail::key_equals_or_empty_condition_impl(ci));
}
if (typeid(*a.m_impl) == typeid(detail::key_equals_number_condition_impl) and
typeid(*b.m_impl) == typeid(detail::key_is_empty_condition_impl))
{
auto ci = static_cast<detail::key_equals_number_condition_impl *>(a.m_impl);
auto ce = static_cast<detail::key_is_empty_condition_impl *>(b.m_impl);
if (ci->m_item_name == ce->m_item_name)
return condition(new detail::key_equals_number_or_empty_condition_impl(ci));
}
if (typeid(*b.m_impl) == typeid(detail::key_equals_number_condition_impl) and
typeid(*a.m_impl) == typeid(detail::key_is_empty_condition_impl))
{
auto ci = static_cast<detail::key_equals_number_condition_impl *>(b.m_impl);
auto ce = static_cast<detail::key_is_empty_condition_impl *>(a.m_impl);
if (ci->m_item_name == ce->m_item_name)
return condition(new detail::key_equals_number_or_empty_condition_impl(ci));
}
return condition(new detail::or_condition_impl(std::move(a), std::move(b)));
}
@@ -956,23 +936,15 @@ struct key
std::string m_item_name; ///< The item name
};
template <typename T>
concept Numeric = ((std::is_floating_point_v<T> or std::is_integral_v<T>) and not std::is_same_v<T, bool>);
/**
* @brief Operator to create an equals condition based on a key @a key and a numeric value @a v
*/
template <Numeric T>
condition operator==(const key &key, const T &v)
{
// TODO: change key_equals_etc... to use std::variant<double,int64_t> or something
return condition(new detail::key_equals_number_condition_impl(key.m_item_name, static_cast<double>(v)));
}
/**
* @brief Operator to create an equals condition based on a key @a key and a value @a value
*/
inline condition operator==(const key &key, std::string_view value)
template <typename T>
concept Numeric = ((std::is_floating_point_v<T> or std::is_integral_v<T>) and not std::is_same_v<T, bool>);
inline condition operator==(const key &key, const item_value &value)
{
if (not value.empty())
return condition(new detail::key_equals_condition_impl({ key.m_item_name, value }));
@@ -980,29 +952,10 @@ inline condition operator==(const key &key, std::string_view value)
return condition(new detail::key_is_empty_condition_impl(key.m_item_name));
}
/**
* @brief Operator to create an equals condition based on a key @a key and a value @a value
*/
template <typename T>
requires std::is_same_v<T, bool>
inline condition operator==(const key &key, T value)
{
return condition(new detail::key_equals_condition_impl({ key.m_item_name, value ? "y" : "n" }));
}
/**
* @brief Operator to create a not equals condition based on a key @a key and a value @a v
*/
template <typename T>
condition operator!=(const key &key, const T &v)
{
return condition(new detail::not_condition_impl(operator==(key, v)));
}
/**
* @brief Operator to create a not equals condition based on a key @a key and a value @a value
*/
inline condition operator!=(const key &key, std::string_view value)
inline condition operator!=(const key &key, const item_value &value)
{
return condition(new detail::not_condition_impl(operator==(key, value)));
}
@@ -1014,9 +967,9 @@ template <Numeric T>
condition operator>(const key &key, const T &v)
{
return condition(new detail::key_compare_condition_impl(
key.m_item_name, [item_name = key.m_item_name, v](row_handle r, bool icase)
key.m_item_name, [item_name = key.m_item_name, v](const_row_handle r, bool icase)
{ return r[item_name].compare(v) > 0; },
cif::format(" > {}", v)));
std::format(" > {}", v)));
}
/**
@@ -1026,7 +979,7 @@ template <Numeric T>
condition operator>=(const key &key, const T &v)
{
return condition(new detail::key_compare_condition_impl(
key.m_item_name, [item_name = key.m_item_name, v](row_handle r, bool icase)
key.m_item_name, [item_name = key.m_item_name, v](const_row_handle r, bool icase)
{ return r[item_name].compare(v) >= 0; },
std::format(" >= {}", v)));
}
@@ -1038,7 +991,7 @@ template <Numeric T>
condition operator<(const key &key, const T &v)
{
return condition(new detail::key_compare_condition_impl(
key.m_item_name, [item_name = key.m_item_name, v](row_handle r, bool icase)
key.m_item_name, [item_name = key.m_item_name, v](const_row_handle r, bool icase)
{ return r[item_name].compare(v) < 0; },
std::format(" < {}", v)));
}
@@ -1050,7 +1003,7 @@ template <Numeric T>
condition operator<=(const key &key, const T &v)
{
return condition(new detail::key_compare_condition_impl(
key.m_item_name, [item_name = key.m_item_name, v](row_handle r, bool icase)
key.m_item_name, [item_name = key.m_item_name, v](const_row_handle r, bool icase)
{ return r[item_name].compare(v) <= 0; },
std::format(" <= {}", v)));
}
@@ -1061,7 +1014,7 @@ condition operator<=(const key &key, const T &v)
inline condition operator>(const key &key, std::string_view v)
{
return condition(new detail::key_compare_condition_impl(
key.m_item_name, [item_name = key.m_item_name, v](row_handle r, bool icase)
key.m_item_name, [item_name = key.m_item_name, v](const_row_handle r, bool icase)
{ return r[item_name].compare(v, icase) > 0; },
std::format(" > {}", v)));
}
@@ -1072,7 +1025,7 @@ inline condition operator>(const key &key, std::string_view v)
inline condition operator>=(const key &key, std::string_view v)
{
return condition(new detail::key_compare_condition_impl(
key.m_item_name, [item_name = key.m_item_name, v](row_handle r, bool icase)
key.m_item_name, [item_name = key.m_item_name, v](const_row_handle r, bool icase)
{ return r[item_name].compare(v, icase) >= 0; },
std::format(" >= {}", v)));
}
@@ -1083,7 +1036,7 @@ inline condition operator>=(const key &key, std::string_view v)
inline condition operator<(const key &key, std::string_view v)
{
return condition(new detail::key_compare_condition_impl(
key.m_item_name, [item_name = key.m_item_name, v](row_handle r, bool icase)
key.m_item_name, [item_name = key.m_item_name, v](const_row_handle r, bool icase)
{ return r[item_name].compare(v, icase) < 0; },
std::format(" < {}", v)));
}
@@ -1094,7 +1047,7 @@ inline condition operator<(const key &key, std::string_view v)
inline condition operator<=(const key &key, std::string_view v)
{
return condition(new detail::key_compare_condition_impl(
key.m_item_name, [item_name = key.m_item_name, v](row_handle r, bool icase)
key.m_item_name, [item_name = key.m_item_name, v](const_row_handle r, bool icase)
{ return r[item_name].compare(v, icase) <= 0; },
std::format(" <= {}", v)));
}

View File

@@ -42,9 +42,6 @@ class file;
class parser;
class row;
class row_handle;
class item;
struct item_handle;
} // namespace cif

View File

@@ -31,10 +31,13 @@
#include "cif++/utilities.hpp"
#include <algorithm>
#include <cassert>
#include <catch2/catch_tostring.hpp>
#include <cctype>
#include <charconv>
#include <cstdint>
#include <cstdlib>
#include <cstring>
#include <iomanip>
#include <iostream>
#include <limits>
@@ -56,7 +59,8 @@
namespace cif
{
class row_handle;
class category;
class row;
// --------------------------------------------------------------------
/** @brief item is a transient class that is used to pass data into rows
@@ -97,8 +101,9 @@ enum class item_value_type
INT,
FLOAT,
TEXT,
MISSING,
EMPTY // This is the real NULL in SQL terms
INAPPLICABLE,
MISSING
};
template <typename T>
@@ -113,12 +118,21 @@ concept FloatType = std::is_floating_point_v<std::remove_cvref_t<T>>;
template <typename T>
concept StringType = (std::is_assignable_v<std::string, T> and not std::is_integral_v<T> and not std::is_floating_point_v<T>);
// --------------------------------------------------------------------
/// \cond
template <typename T>
inline constexpr bool is_optional_v = false;
template <typename T>
inline constexpr bool is_optional_v<std::optional<T>> = true;
/// \endcond
class item_value
{
public:
item_value() noexcept
{
m_data.m_type = item_value_type::EMPTY;
m_data.m_type = item_value_type::MISSING;
}
item_value(item_value_type type) noexcept
@@ -134,14 +148,17 @@ class item_value
case item_value_type::BOOLEAN: m_data.m_value = rhs.m_data.m_value.m_boolean; break;
case item_value_type::INT: m_data.m_value = rhs.m_data.m_value.m_integer; break;
case item_value_type::FLOAT: m_data.m_value = rhs.m_data.m_value.m_float; break;
case item_value_type::TEXT: m_data.m_value = rhs.m_data.sv(); break;
case item_value_type::TEXT:
m_data.m_len = rhs.m_data.m_len;
m_data.m_value = rhs.m_data.sv();
break;
default: break;
}
}
item_value(std::nullptr_t)
{
m_data.m_type = item_value_type::EMPTY;
m_data.m_type = item_value_type::MISSING;
}
template <BooleanType T>
@@ -191,8 +208,14 @@ class item_value
template <typename T>
item_value(std::optional<T> v)
: item_value(v.has_value() ? *v : nullptr)
{
if (v.has_value())
{
item_value iv{ *v };
swap(*this, iv);
}
else
m_data.m_type = item_value_type::MISSING;
}
item_value(item_value &&rhs) noexcept
@@ -208,15 +231,19 @@ class item_value
// --------------------------------------------------------------------
constexpr bool is_null() const noexcept { return m_data.m_type == item_value_type::MISSING; }
constexpr bool is_empty() const noexcept { return m_data.m_type == item_value_type::EMPTY; }
constexpr bool is_string() const noexcept { return m_data.m_type == item_value_type::TEXT; }
constexpr bool is_number() const noexcept { return is_number_int() or is_number_float(); }
constexpr bool is_number_int() const noexcept { return m_data.m_type == item_value_type::INT; }
constexpr bool is_number_float() const noexcept { return m_data.m_type == item_value_type::FLOAT; }
constexpr bool is_boolean() const noexcept { return m_data.m_type == item_value_type::BOOLEAN; }
[[nodiscard]] constexpr bool is_inapplicable() const noexcept { return m_data.m_type == item_value_type::INAPPLICABLE; }
[[nodiscard]] constexpr bool is_missing() const noexcept { return m_data.m_type == item_value_type::MISSING; }
[[nodiscard]] constexpr bool is_null() const noexcept { return is_inapplicable() or is_missing(); }
constexpr item_value_type type() const { return m_data.m_type; }
[[nodiscard]] constexpr bool is_string() const noexcept { return m_data.m_type == item_value_type::TEXT; }
[[nodiscard]] constexpr bool is_number_int() const noexcept { return m_data.m_type == item_value_type::INT; }
[[nodiscard]] constexpr bool is_number_float() const noexcept { return m_data.m_type == item_value_type::FLOAT; }
[[nodiscard]] constexpr bool is_number() const noexcept { return is_number_int() or is_number_float(); }
[[nodiscard]] constexpr bool is_boolean() const noexcept { return m_data.m_type == item_value_type::BOOLEAN; }
[[nodiscard]] constexpr item_value_type type() const { return m_data.m_type; }
explicit operator bool() const noexcept
{
@@ -227,18 +254,18 @@ class item_value
case item_value_type::INT: result = m_data.m_value.m_integer != 0; break;
case item_value_type::FLOAT: result = m_data.m_value.m_float != 0; break;
case item_value_type::TEXT: result = m_data.m_len != 0; break;
case item_value_type::MISSING:
case item_value_type::EMPTY: result = false; break;
case item_value_type::INAPPLICABLE:
case item_value_type::MISSING: result = false; break;
}
return result;
}
bool empty() const noexcept
[[nodiscard]] bool empty() const noexcept
{
switch (m_data.m_type)
{
case item_value_type::INAPPLICABLE:
case item_value_type::MISSING:
case item_value_type::EMPTY:
return true;
case item_value_type::TEXT:
@@ -252,12 +279,12 @@ class item_value
// --------------------------------------------------------------------
template <StringType T>
inline std::string get() const
[[nodiscard]] inline std::string get() const
{
switch (m_data.m_type)
{
case item_value_type::EMPTY:
case item_value_type::MISSING:
case item_value_type::INAPPLICABLE:
return "";
case item_value_type::TEXT:
@@ -284,7 +311,7 @@ class item_value
}
template <IntegralType T>
std::remove_cvref_t<T> get() const
[[nodiscard]] std::remove_cvref_t<T> get() const
{
switch (m_data.m_type)
{
@@ -301,6 +328,9 @@ class item_value
auto &&[ptr, ec] = std::from_chars(sv.data(), sv.data() + sv.length(), v);
if (ec != std::errc{})
throw std::system_error(std::make_error_code(ec));
if (ptr != sv.data() + sv.length())
throw std::invalid_argument("String value does not contain only an integer");
return v;
}
default:
@@ -309,7 +339,7 @@ class item_value
}
template <FloatType T>
std::remove_cvref_t<T> get() const
[[nodiscard]] std::remove_cvref_t<T> get() const
{
switch (m_data.m_type)
{
@@ -326,6 +356,8 @@ class item_value
auto &&[ptr, ec] = std::from_chars(sv.data(), sv.data() + sv.length(), v);
if (ec != std::errc{})
throw std::system_error(std::make_error_code(ec));
if (ptr != sv.data() + sv.length())
throw std::invalid_argument("String value does not contain only a floating point number");
return v;
}
default:
@@ -334,7 +366,7 @@ class item_value
}
template <BooleanType T>
std::remove_cvref_t<T> get() const
[[nodiscard]] std::remove_cvref_t<T> get() const
{
switch (m_data.m_type)
{
@@ -352,19 +384,28 @@ class item_value
}
template <typename T>
std::optional<T> get() const
requires is_optional_v<T>
[[nodiscard]] auto get() const
{
switch (m_data.m_type)
{
case item_value_type::INAPPLICABLE:
case item_value_type::MISSING:
case item_value_type::EMPTY:
return {};
return T{};
default:
return get<T>();
{
auto v = get<typename T::value_type>();
return T{ v };
}
}
}
[[nodiscard]] std::string str() const
{
return get<std::string>();
}
// --------------------------------------------------------------------
friend void swap(item_value &a, item_value &b) noexcept
@@ -403,31 +444,28 @@ class item_value
case item_value_type::INT: return m_data.m_value.m_integer == rhs.m_data.m_value.m_integer;
case item_value_type::FLOAT: return m_data.m_value.m_float == rhs.m_data.m_value.m_float;
case item_value_type::TEXT: return m_data.sv() == rhs.m_data.sv();
case item_value_type::MISSING:
case item_value_type::EMPTY: return true;
case item_value_type::INAPPLICABLE:
case item_value_type::MISSING: return true;
}
}
return false;
}
int compare(const item_value &b, bool ignore_case = false) const noexcept;
[[nodiscard]] int compare(const item_value &b, bool ignore_case = false) const noexcept;
friend std::ostream operator<<(std::ostream &os, const item_value &v);
friend std::ostream &operator<<(std::ostream &os, const item_value &v);
private:
union value
{
bool m_boolean;
int64_t m_integer;
int64_t m_integer{};
double m_float;
char m_local_str[8];
char *m_str;
value()
: m_integer(0)
{
}
value() = default;
value(bool v)
: m_boolean(v)
@@ -453,7 +491,7 @@ class item_value
m_str[s.length()] = 0;
}
else
memcpy(m_local_str, s.data(), s.length() + 1);
std::memcpy(m_local_str, s.data(), s.length() + 1);
}
value(item_value_type t)
@@ -470,7 +508,7 @@ class item_value
struct data
{
item_value_type m_type = item_value_type::EMPTY;
item_value_type m_type = item_value_type::MISSING;
uint32_t m_len{};
value m_value{};
@@ -481,7 +519,13 @@ class item_value
}
data() noexcept = default;
data(data &&) noexcept = default;
data(data &&rhs) noexcept
{
std::swap(m_type, rhs.m_type);
std::swap(m_len, rhs.m_len);
std::swap(m_value, rhs.m_value);
}
data(const data &) noexcept = delete;
data &operator=(data &&) noexcept = delete;
data &operator=(const data &) noexcept = delete;
@@ -515,7 +559,7 @@ class item
/// content the character '.', i.e. an inapplicable value.
item(std::string name)
: m_name(std::move(name))
, m_value(item_value_type::EMPTY)
, m_value(item_value_type::MISSING)
{
}
@@ -525,98 +569,31 @@ class item
{
}
// /// \brief constructor for an item with name \a name and as
// /// content the character '.', i.e. an inapplicable value.
// item(std::string_view name, std::nullptr_t)
// : m_name(name)
// , m_value(item_value_type::EMPTY)
// {
// }
// /// \brief constructor for an item with name \a name and as
// /// content a single character string with content \a value
// item(std::string_view name, char value)
// : m_name(name)
// , m_value(std::string_view{ &value, 1 })
// {
// }
// /// \brief constructor for an item with name \a name and as
// /// content the formatted floating point value \a value
// template <FloatType T>
// item(std::string_view name, T value)
// : m_name(name)
// , m_value(value)
// {
// }
// /// \brief constructor for an item with name \a name and as
// /// content the formatted floating point value \a value with
// /// precision \a precision
// template <FloatType T>
// item(std::string_view name, T value, int precision)
// : m_name(name)
// , m_value(value, precision)
// {
// }
// /// \brief constructor for an item with name \a name and as
// /// content the formatted integral value \a value
// template <IntegralType T>
// item(const std::string_view name, T value)
// : m_name(name)
// , m_value(value)
// {
// }
// // TODO: Perhaps introduce a real boolean type?
// /// \brief constructor for an item with name \a name and as
// /// content the formatted boolean value \a value
// template <BooleanType T>
// item(const std::string_view name, T value)
// : m_name(name)
// , m_value(value)
// {
// }
// /// \brief constructor for an item with name \a name and as
// /// content value \a value
// item(const std::string_view name, std::string_view value)
// : m_name(name)
// , m_value(value)
// {
// }
// /// \brief constructor for an item with name \a name and as
// /// content the optional value \a value
// template <typename T>
// item(const std::string_view name, const std::optional<T> &value)
// : m_name(name)
// , m_value(item_value_type::MISSING)
// {
// if (value.has_value())
// m_value = *value;
// }
// /// \brief constructor for an item with name \a name and as
// /// content the formatted floating point value \a value with
// /// precision \a precision
// template <typename T, std::enable_if_t<std::is_floating_point_v<T>, int> = 0>
// item(std::string_view name, const std::optional<T> &value, int precision)
// : m_name(name)
// , m_value(item_value_type::MISSING)
// {
// if (value.has_value())
// m_value = item_value(*value, precision);
// }
/** @cond */
item(const item &rhs) = default;
item(item &&rhs) noexcept = default;
item &operator=(const item &rhs) = default;
item &operator=(item &&rhs) noexcept = default;
item(const item &rhs)
: m_name(rhs.m_name)
, m_value(rhs.m_value)
{
}
item(item &&rhs)
{
swap(*this, rhs);
}
item &operator=(item rhs) noexcept
{
swap(*this, rhs);
return *this;
}
/** @endcond */
friend void swap(item &a, item &b) noexcept
{
std::swap(a.m_name, b.m_name);
std::swap(a.m_value, b.m_value);
}
const std::string &name() const { return m_name; } ///< Return the name of the item
const item_value &value() const & { return m_value; } ///< Return the value of the item
item_value &value() & { return m_value; } ///< Return the value of the item
@@ -627,11 +604,11 @@ class item
/// \brief empty means either null or unknown
bool empty() const { return m_value.empty(); }
/// \brief returns true if the item contains '.'
/// \brief returns true if the item contains '.' or '?'
bool is_null() const { return m_value.is_null(); }
/// \brief returns true if the item contains '?'
bool is_unknown() const { return m_value.is_empty(); }
bool is_unknown() const { return m_value.is_missing(); }
// /// \brief the length of the value string
// std::size_t length() const { return m_value.length(); }
@@ -646,7 +623,7 @@ class item
return value();
}
auto operator<=>(const item &rhs) const = default;
// auto operator<=>(const item &rhs) const = default;
private:
std::string m_name;
@@ -654,18 +631,13 @@ class item
};
// --------------------------------------------------------------------
// Transient object to access stored data
/// \brief This is item_handle, it is used to access the data stored in item_value.
/// \brief This is item_handle, it is used to access the data stored in
/// item_value's in rows
struct item_handle
{
public:
/** @cond */
// conversion helper class
template <typename T, typename = void>
struct item_value_as;
/** @endcond */
item_handle() = delete;
/**
* @brief Assign value @a value to the item referenced
@@ -674,73 +646,44 @@ struct item_handle
* @param value The value
* @return reference to this item_handle
*/
item_handle &operator=(item_value value);
[[nodiscard]] item_value_type &value();
[[nodiscard]] const item_value &value() const;
[[nodiscard]] constexpr bool is_inapplicable() const noexcept { return value().type() == item_value_type::INAPPLICABLE; }
[[nodiscard]] constexpr bool is_missing() const noexcept { return value().type() == item_value_type::MISSING; }
[[nodiscard]] constexpr bool is_null() const noexcept { return is_inapplicable() or is_missing(); }
[[nodiscard]] constexpr bool is_string() const noexcept { return value().type() == item_value_type::TEXT; }
[[nodiscard]] constexpr bool is_number_int() const noexcept { return value().type() == item_value_type::INT; }
[[nodiscard]] constexpr bool is_number_float() const noexcept { return value().type() == item_value_type::FLOAT; }
[[nodiscard]] constexpr bool is_number() const noexcept { return is_number_int() or is_number_float(); }
[[nodiscard]] constexpr bool is_boolean() const noexcept { return value().type() == item_value_type::BOOLEAN; }
[[nodiscard]] auto type() const { return value().type(); }
template <typename T>
item_handle &operator=(const T &value)
auto get() const
{
assign_value(item{ "", value }.value());
return *this;
return value().template get<T>();
}
/**
* @brief Assign value @a value to the item referenced
*
* @tparam T Type of the value
* @param value The value
* @return reference to this item_handle
*/
template <typename T>
item_handle &operator=(T &&value)
auto as() const
{
assign_value(item{ "", std::forward<T>(value) }.value());
return *this;
return value().template get<T>();
}
/**
* @brief Assign value @a value to the item referenced
*
* @tparam T Type of the value
* @param value The value
* @return reference to this item_handle
*/
template <std::size_t N>
item_handle &operator=(const char (&value)[N])
[[nodiscard]] auto str() const
{
assign_value(item{ "", std::move(value) }.value());
return *this;
return value().str();
}
/**
* @brief A method with a variable number of arguments that will be concatenated and
* assigned as a string. Use it like this:
*
* @code{.cpp}
* cif::item_handle ih;
* is.os("The result of ", 1, " * ", 42, " is of course ", 42);
* @endcode
*
* And the content will then be `The result of 1 * 42 is of course 42`.
*
* @tparam Ts Types of the parameters
* @param v The parameters to concatenate
*/
template <typename... Ts>
void os(const Ts &...v)
{
std::ostringstream ss;
((ss << v), ...);
this->operator=(ss.str());
}
/** Swap contents of this and @a b */
void swap(item_handle &b);
/** Return the contents of this item as type @tparam T */
template <typename T = std::string>
[[nodiscard]] auto as() const -> T
{
using value_type = std::remove_cv_t<std::remove_reference_t<T>>;
return item_value_as<value_type>::convert(*this);
}
/** Swap contents of @a a and @a b */
friend void swap(item_handle &a, item_handle &b) noexcept;
/** Return the contents of this item as type @tparam T or, if not
* set, use @a dv as the default value.
@@ -748,7 +691,7 @@ struct item_handle
template <typename T>
[[nodiscard]] auto value_or(const T &dv) const
{
return empty() ? dv : this->as<T>();
return empty() ? dv : this->get<T>();
}
/**
@@ -762,21 +705,25 @@ struct item_handle
* @param icase Flag indicating if we should compare character case sensitive
* @return -1, 0 or 1
*/
template <typename T>
[[nodiscard]] int compare(const T &value, bool icase = true) const noexcept
[[nodiscard]] int compare(const item_value &value, bool icase = true) const noexcept
{
return item_value_as<T>::compare(*this, value, icase);
return this->value().compare(value, icase);
}
[[nodiscard]] int compare(const item_handle &value, bool icase = true) const noexcept
{
return compare(value.value(), icase);
}
/**
* @brief Compare the value contained with the value @a value and
* return true if both are equal.
*/
template <typename T>
[[nodiscard]] bool operator==(const T &value) const noexcept
[[nodiscard]] bool operator==(const item_value &value) const noexcept
{
// TODO: icase or not icase?
return item_value_as<T>::compare(*this, value, true) == 0;
return this->value().compare(value) != 0;
}
// We may not have C++20 yet...
@@ -798,29 +745,14 @@ struct item_handle
*/
[[nodiscard]] bool empty() const
{
auto txt = text();
return txt.empty() or (txt.length() == 1 and (txt.front() == '.' or txt.front() == '?'));
return this->value().empty();
}
/** Easy way to test for an empty item */
explicit operator bool() const { return not empty(); }
/// is_null return true if the item contains '.'
[[nodiscard]] bool is_null() const
{
auto txt = text();
return txt.length() == 1 and txt.front() == '.';
}
/// is_unknown returns true if the item contains '?'
[[nodiscard]] bool is_unknown() const
{
auto txt = text();
return txt.length() == 1 and txt.front() == '?';
}
/** Return a std::string_view for the contents */
[[nodiscard]] std::string_view text() const;
[[nodiscard]] std::string_view text_() const;
/**
* @brief Construct a new item handle object
@@ -828,227 +760,148 @@ struct item_handle
* @param item Item index
* @param row Reference to the row
*/
item_handle(uint16_t item, row_handle &row)
: m_item_ix(item)
, m_row_handle(row)
item_handle(category &cat, row &row, uint16_t item_ix)
: m_category(cat)
, m_row(row)
, m_item_ix(item_ix)
{
}
/** A variable holding an empty item */
CIFPP_EXPORT static const item_handle s_null_item;
/** friend to swap two item handles */
friend void swap(item_handle a, item_handle b)
{
a.swap(b);
}
private:
item_handle() noexcept;
category &m_category;
row &m_row;
uint16_t m_item_ix;
row_handle &m_row_handle;
void assign_value(std::string_view value);
void assign_value(item_value value);
};
// --------------------------------------------------------------------
// Transient object to access stored data
/// \brief This is item_handle, it is used to access the data stored in item_value.
template <typename T>
struct item_handle::item_value_as<T, std::enable_if_t<std::is_arithmetic_v<T> and not std::is_same_v<T, bool>>>
struct const_item_handle
{
using value_type = std::remove_reference_t<std::remove_cv_t<T>>;
public:
const_item_handle() = delete;
static value_type convert(const item_handle &ref)
[[nodiscard]] const item_value &value() const;
[[nodiscard]] constexpr bool is_inapplicable() const noexcept { return value().type() == item_value_type::INAPPLICABLE; }
[[nodiscard]] constexpr bool is_missing() const noexcept { return value().type() == item_value_type::MISSING; }
[[nodiscard]] constexpr bool is_null() const noexcept { return is_inapplicable() or is_missing(); }
[[nodiscard]] constexpr bool is_string() const noexcept { return value().type() == item_value_type::TEXT; }
[[nodiscard]] constexpr bool is_number_int() const noexcept { return value().type() == item_value_type::INT; }
[[nodiscard]] constexpr bool is_number_float() const noexcept { return value().type() == item_value_type::FLOAT; }
[[nodiscard]] constexpr bool is_number() const noexcept { return is_number_int() or is_number_float(); }
[[nodiscard]] constexpr bool is_boolean() const noexcept { return value().type() == item_value_type::BOOLEAN; }
[[nodiscard]] auto type() const { return value().type(); }
template <typename T>
auto get() const
{
value_type result = {};
if (not ref.empty())
{
auto txt = ref.text();
auto b = txt.data();
auto e = txt.data() + txt.size();
std::from_chars_result r = (b + 1 < e and *b == '+' and std::isdigit(b[1])) //
? from_chars(b + 1, e, result)
: from_chars(b, e, result);
if (r.ec != std::errc{} or r.ptr != e)
{
result = {};
if (cif::VERBOSE)
{
if (r.ec == std::errc::invalid_argument)
std::cerr << "Attempt to convert " << std::quoted(txt) << " into a number\n";
else if (r.ec == std::errc::result_out_of_range)
std::cerr << "Conversion of " << std::quoted(txt) << " into a type that is too small\n";
else
std::cerr << "Not a valid number " << std::quoted(txt) << '\n';
}
}
}
return result;
return value().template get<T>();
}
static int compare(const item_handle &ref, const T &value, bool icase) noexcept
template <typename T>
auto as() const
{
int result = 0;
auto txt = ref.text();
if (ref.empty())
result = 1;
else
{
value_type v = {};
auto b = txt.data();
auto e = txt.data() + txt.size();
std::from_chars_result r = (b + 1 < e and *b == '+' and std::isdigit(b[1]))
? from_chars(b + 1, e, v)
: from_chars(b, e, v);
if (r.ec != std::errc{} or r.ptr != e)
{
if (cif::VERBOSE)
{
if (r.ec == std::errc::invalid_argument)
std::cerr << "Attempt to convert " << std::quoted(txt) << " into a number\n";
else if (r.ec == std::errc::result_out_of_range)
std::cerr << "Conversion of " << std::quoted(txt) << " into a type that is too small\n";
else
std::cerr << "Not a valid number " << std::quoted(txt) << '\n';
}
result = 1;
}
else if (std::abs(v - value) <= std::numeric_limits<value_type>::epsilon())
result = 0;
else if (v < value)
result = -1;
else if (v > value)
result = 1;
}
return result;
return value().template get<T>();
}
[[nodiscard]] auto str() const
{
return value().str();
}
/** Return the contents of this item as type @tparam T or, if not
* set, use @a dv as the default value.
*/
template <typename T>
[[nodiscard]] auto value_or(const T &dv) const
{
return empty() ? dv : this->get<T>();
}
/**
* @brief Compare the contents of this item with value @a value
* optionally ignoring character case, if @a icase is true.
* Returns 0 if both are equal, -1 if this sorts before @a value
* and 1 if this sorts after @a value
*
* @tparam T Type of the value @a value
* @param value The value to compare with
* @param icase Flag indicating if we should compare character case sensitive
* @return -1, 0 or 1
*/
[[nodiscard]] int compare(const item_value &value, bool icase = true) const noexcept
{
return this->value().compare(value, icase);
}
[[nodiscard]] int compare(const const_item_handle &value, bool icase = true) const noexcept
{
return compare(value.value(), icase);
}
/**
* @brief Compare the value contained with the value @a value and
* return true if both are equal.
*/
[[nodiscard]] bool operator==(const item_value &value) const noexcept
{
// TODO: icase or not icase?
return this->value().compare(value) != 0;
}
// We may not have C++20 yet...
/**
* @brief Compare the value contained with the value @a value and
* return true if both are not equal.
*/
template <typename T>
[[nodiscard]] bool operator!=(const T &value) const noexcept
{
return not operator==(value);
}
/**
* @brief Returns true if the content string is empty or
* only contains '.' meaning null or '?' meaning unknown
* in a mmCIF context
*/
[[nodiscard]] bool empty() const
{
return this->value().empty();
}
/** Easy way to test for an empty item */
explicit operator bool() const { return not empty(); }
/** Return a std::string_view for the contents */
[[nodiscard]] std::string_view text_() const;
/**
* @brief Construct a new item handle object
*
* @param item Item index
* @param row Reference to the row
*/
const_item_handle(const category &cat, const row &row, uint16_t item_ix)
: m_category(cat)
, m_row(row)
, m_item_ix(item_ix)
{
}
private:
const category &m_category;
const row &m_row;
uint16_t m_item_ix;
};
template <typename T>
struct item_handle::item_value_as<std::optional<T>>
{
static std::optional<T> convert(const item_handle &ref)
{
std::optional<T> result;
if (ref)
result = ref.as<T>();
return result;
}
static int compare(const item_handle &ref, std::optional<T> value, bool icase) noexcept
{
if (ref.empty() and not value)
return 0;
if (ref.empty())
return -1;
else if (not value)
return 1;
else
return ref.compare(*value, icase);
}
};
template <typename T>
struct item_handle::item_value_as<T, std::enable_if_t<std::is_same_v<T, bool>>>
{
static bool convert(const item_handle &ref)
{
bool result = false;
if (not ref.empty())
result = iequals(ref.text(), "y");
return result;
}
static int compare(const item_handle &ref, bool value, bool icase) noexcept
{
bool rv = convert(ref);
return value && rv ? 0
: (rv < value ? -1 : 1);
}
};
template <std::size_t N>
struct item_handle::item_value_as<char[N]>
{
static std::string convert(const item_handle &ref)
{
if (ref.empty())
return {};
return { ref.text().data(), ref.text().size() };
}
static int compare(const item_handle &ref, const char (&value)[N], bool icase) noexcept
{
return icase ? cif::icompare(ref.text(), value) : ref.text().compare(value);
}
};
template <typename T>
struct item_handle::item_value_as<T, std::enable_if_t<std::is_same_v<T, const char *>>>
{
static std::string convert(const item_handle &ref)
{
if (ref.empty())
return {};
return { ref.text().data(), ref.text().size() };
}
static int compare(const item_handle &ref, const char *value, bool icase) noexcept
{
return icase ? cif::icompare(ref.text(), value) : ref.text().compare(value);
}
};
template <typename T>
struct item_handle::item_value_as<T, std::enable_if_t<std::is_same_v<T, std::string_view>>>
{
static std::string convert(const item_handle &ref)
{
if (ref.empty())
return {};
return { ref.text().data(), ref.text().size() };
}
static int compare(const item_handle &ref, const std::string_view &value, bool icase) noexcept
{
return icase ? cif::icompare(ref.text(), value) : ref.text().compare(value);
}
};
template <typename T>
struct item_handle::item_value_as<T, std::enable_if_t<std::is_same_v<T, std::string>>>
{
static std::string convert(const item_handle &ref)
{
if (ref.empty())
return {};
return { ref.text().data(), ref.text().size() };
}
static int compare(const item_handle &ref, const std::string &value, bool icase) noexcept
{
return icase ? cif::icompare(ref.text(), value) : ref.text().compare(value);
}
};
/** @endcond */
} // namespace cif
namespace std
@@ -1075,4 +928,3 @@ struct tuple_element<1, ::cif::item>
};
} // namespace std

View File

@@ -91,7 +91,7 @@ class iterator_impl_base
template <bool C, typename... T2s>
iterator_impl_base(const iterator_impl_base<C, T2s...> &rhs)
: m_current(const_cast<row_handle&>(rhs.m_current))
: m_current(rhs.m_current)
, m_value(rhs.m_value)
, m_item_ix(rhs.m_item_ix)
{
@@ -99,7 +99,7 @@ class iterator_impl_base
template <bool C>
iterator_impl_base(iterator_impl_base<C, Ts...> &rhs)
: m_current(const_cast<row_handle&>(rhs.m_current))
: m_current(rhs.m_current)
, m_value(rhs.m_value)
, m_item_ix(rhs.m_item_ix)
{
@@ -108,7 +108,7 @@ class iterator_impl_base
template <bool C>
iterator_impl_base(const iterator_impl_base<C> &rhs, const std::array<uint16_t, N> &cix)
: m_current(const_cast<row_handle&>(rhs.m_current))
: m_current(rhs.m_current)
, m_item_ix(cix)
{
m_value = get(std::make_index_sequence<N>());
@@ -144,7 +144,7 @@ class iterator_impl_base
return &m_value;
}
operator const row_handle() const
operator const_row_handle() const
{
return m_current;
}
@@ -195,7 +195,7 @@ class iterator_impl_base
return m_current ? tuple_type{ m_current[m_item_ix[Is]].template as<Ts>()... } : tuple_type{};
}
row_handle m_current;
std::conditional_t<Const, const_row_handle, row_handle> m_current;
tuple_type m_value;
std::array<uint16_t, N> m_item_ix;
};
@@ -218,10 +218,11 @@ class iterator_impl_base<Const>
friend class category;
using category_type = std::conditional_t<Const, const category, category>;
using row_type = std::conditional_t<Const, const row, row>;
using iterator_category = std::forward_iterator_tag;
using value_type = std::conditional_t<Const, const row_handle, row_handle>;
using value_type = std::conditional_t<Const, const_row_handle, row_handle>;
using difference_type = std::ptrdiff_t;
using pointer = value_type *;
using reference = value_type &;
@@ -233,18 +234,18 @@ class iterator_impl_base<Const>
template <bool C>
iterator_impl_base(const iterator_impl_base<C> &rhs)
: m_current(const_cast<row_handle &>(rhs.m_current))
: m_current(rhs.m_current)
{
}
iterator_impl_base(category_type &cat, row *current)
iterator_impl_base(category_type &cat, row_type *current)
: m_current(cat, *current)
{
}
template <bool C>
iterator_impl_base(const iterator_impl_base<C> &rhs, const std::array<uint16_t, 0> &)
: m_current(const_cast<row_handle &>(rhs.m_current))
: m_current(rhs.m_current)
{
}
@@ -276,7 +277,7 @@ class iterator_impl_base<Const>
return &m_current;
}
operator const row_handle() const
operator const_row_handle() const
{
return m_current;
}
@@ -324,7 +325,7 @@ class iterator_impl_base<Const>
/** @endcond */
private:
row_handle m_current;
value_type m_current;
};
/**
@@ -368,7 +369,7 @@ class iterator_impl_base<Const, T>
template <bool C>
iterator_impl_base(iterator_impl_base<C, T> &rhs)
: m_current(const_cast<row_handle&>(rhs.m_current))
: m_current(rhs.m_current)
, m_value(rhs.m_value)
, m_item_ix(rhs.m_item_ix)
{
@@ -377,7 +378,7 @@ class iterator_impl_base<Const, T>
template <bool C>
iterator_impl_base(const iterator_impl_base<C> &rhs, const std::array<uint16_t, 1> &cix)
: m_current(const_cast<row_handle&>(rhs.m_current))
: m_current(rhs.m_current)
, m_item_ix(cix[0])
{
m_value = get();
@@ -413,7 +414,7 @@ class iterator_impl_base<Const, T>
return &m_value;
}
operator const row_handle() const
operator const_row_handle() const
{
return m_current;
}
@@ -460,7 +461,7 @@ class iterator_impl_base<Const, T>
private:
[[nodiscard]] value_type get() const
{
return m_current ? m_current[m_item_ix].template as<value_type>() : value_type{};
return m_current ? m_current[m_item_ix].template get<value_type>() : value_type{};
}
row_handle m_current;
@@ -810,6 +811,11 @@ void swap(conditional_iterator_proxy_base<Const, Ts...> &lhs, conditional_iterat
std::swap(lhs.mCix, rhs.mCix);
}
// --------------------------------------------------------------------
// template <bool Const, typename... Ts>
/** @endcond */
} // namespace cif

View File

@@ -128,7 +128,7 @@ class atom
return m_cat[{ { .name = "id", .value = m_id } }];
}
[[nodiscard]] const row_handle row() const
[[nodiscard]] const_row_handle row() const
{
return m_cat[{ { .name = "id", .value = m_id } }];
}
@@ -142,7 +142,7 @@ class atom
return result;
}
[[nodiscard]] const row_handle row_aniso() const
[[nodiscard]] const_row_handle row_aniso() const
{
row_handle result{};
auto cat = m_db.get("atom_site_anisotrop");
@@ -189,7 +189,7 @@ class atom
* @param db The datablock where the _atom_site category resides
* @param row The row containing the data for this atom
*/
atom(const datablock &db, const row_handle &row)
atom(const datablock &db, const_row_handle &row)
: atom(std::make_shared<atom_impl>(db, row["id"].as<std::string>()))
{
}
@@ -315,10 +315,10 @@ class atom
}
/// for direct access to underlying data, be careful!
[[nodiscard]] const row_handle get_row() const { return impl().row(); }
[[nodiscard]] const_row_handle get_row() const { return impl().row(); }
/// for direct access to underlying data, be careful!
[[nodiscard]] const row_handle get_row_aniso() const { return impl().row_aniso(); }
[[nodiscard]] const_row_handle get_row_aniso() const { return impl().row_aniso(); }
/// Return if the atom is actually a symmetry copy or the original one
[[nodiscard]] bool is_symmetry_copy() const { return impl().m_symop != "1_555"; }

View File

@@ -166,7 +166,13 @@ class sac_parser
SAVE_NAME,
STOP,
ITEM_NAME,
VALUE
VALUE_INAPPLICABLE,
VALUE_UNKNOWN,
VALUE_NUMERIC_INTEGER,
VALUE_NUMERIC_FLOAT,
VALUE_CHARSTRING,
VALUE_TEXTFIELD
};
static constexpr const char *get_token_name(CIFToken token)
@@ -182,7 +188,15 @@ class sac_parser
case CIFToken::SAVE_NAME: return "SAVE+name";
case CIFToken::STOP: return "STOP";
case CIFToken::ITEM_NAME: return "Tag";
case CIFToken::VALUE: return "Value";
// case CIFToken::VALUE: return "Value";
case CIFToken::VALUE_INAPPLICABLE: return "Inapplicable value";
case CIFToken::VALUE_UNKNOWN: return "'Unknown' value (=null)";
case CIFToken::VALUE_NUMERIC_INTEGER: return "Integer value";
case CIFToken::VALUE_NUMERIC_FLOAT: return "Float value";
case CIFToken::VALUE_CHARSTRING: return "Charstring value";
case CIFToken::VALUE_TEXTFIELD: return "Textfield value";
default: return "Invalid token parameter";
}
}
@@ -282,13 +296,17 @@ class sac_parser
ItemName,
TextItem,
TextItemNL,
Reserved,
Value,
TextItemBS,
TextItemBS2,
TextItemBSNL,
Reserved,
Value
Numeric_Integer,
Numeric_Float,
Numeric_Exponent1,
Numeric_Exponent2
};
std::streambuf &m_source;
@@ -302,6 +320,8 @@ class sac_parser
// token buffer
std::vector<char> m_token_buffer;
std::string_view m_token_value;
int64_t m_token_value_int;
double m_token_value_float;
/** @endcond */
};

View File

@@ -32,6 +32,7 @@
#include <cstddef>
#include <cstdint>
#include <initializer_list>
#include <stdexcept>
#include <string_view>
#include <tuple>
#include <type_traits>
@@ -88,12 +89,13 @@ namespace cif
class category;
class row_handle;
class const_row_handle;
namespace cql
{
struct connection_impl;
}
namespace detail
{
@@ -103,7 +105,7 @@ namespace detail
{
static constexpr std::size_t N = sizeof...(C);
get_row_result(const row_handle &r, std::array<uint16_t, N> &&items)
get_row_result(const const_row_handle &r, std::array<uint16_t, N> &&items)
: m_row(r)
, m_items(std::move(items))
{
@@ -127,7 +129,7 @@ namespace detail
return std::tuple<Ts...>{ m_row[m_items[Is]].template get<Ts>()... };
}
const row_handle &m_row;
const const_row_handle &m_row;
std::array<uint16_t, N> m_items;
};
@@ -179,7 +181,9 @@ class row : public std::vector<item_value>
*/
item_value *get(uint16_t ix)
{
return ix < size() ? &data()[ix] : nullptr;
if (ix >= size())
resize(ix + 1);
return &data()[ix];
}
/**
@@ -190,14 +194,14 @@ class row : public std::vector<item_value>
return ix < size() ? &data()[ix] : nullptr;
}
// private:
// private:
friend class category;
friend class category_index;
template <bool, typename...>
friend class iterator_impl_base;
void append(uint16_t ix, item_value &&iv)
void append(uint16_t ix, item_value iv)
{
if (ix >= size())
resize(ix + 1);
@@ -221,7 +225,8 @@ class row_handle
{
public:
/** @cond */
friend struct item_handle;
template <bool>
friend struct item_handle_base;
friend class category;
friend class category_index;
friend class row_initializer;
@@ -230,24 +235,24 @@ class row_handle
friend class iterator_impl_base;
row_handle() = default;
virtual ~row_handle() = default;
row_handle(const row_handle &) = default;
row_handle(row_handle &&) = default;
row_handle &operator=(const row_handle &) = default;
row_handle &operator=(row_handle &&) = default;
/** @endcond */
/// \brief constructor taking a category @a cat and a row @a r
row_handle(const category &cat, const row &r)
: m_category(const_cast<category *>(&cat))
, m_row(const_cast<row *>(&r))
row_handle(category &cat, row &r)
: m_category(&cat)
, m_row(&r)
{
}
/// \brief return the category this row belongs to
[[nodiscard]] const category &get_category() const
[[nodiscard]] category &get_category() const
{
return *m_category;
}
@@ -270,58 +275,31 @@ class row_handle
return not empty();
}
/// \brief return the count of the items
[[nodiscard]] size_t size() const { return m_row->size(); }
/// \brief return a cif::item_handle to the item in item @a item_ix
item_handle operator[](uint16_t item_ix)
{
return empty() ? item_handle::s_null_item : item_handle(item_ix, *this);
return { *m_category, *m_row, item_ix };
}
/// \brief return a const cif::item_handle to the item in item @a item_ix
const item_handle operator[](uint16_t item_ix) const
/// \brief return a cif::item_handle to the item in item @a item_ix
const_item_handle operator[](uint16_t item_ix) const
{
return empty() ? item_handle::s_null_item : item_handle(item_ix, const_cast<row_handle &>(*this));
return { *m_category, *m_row, item_ix };
}
/// \brief return a cif::item_handle to the item in the item named @a item_name
item_handle operator[](std::string_view item_name)
{
return empty() ? item_handle::s_null_item : item_handle(add_item(item_name), *this);
return { *m_category, *m_row, get_item_ix(item_name) };
}
/// \brief return a const cif::item_handle to the item in the item named @a item_name
const item_handle operator[](std::string_view item_name) const
/// \brief return a cif::item_handle to the item in the item named @a item_name
const_item_handle operator[](std::string_view item_name) const
{
return empty() ? item_handle::s_null_item : item_handle(get_item_ix(item_name), const_cast<row_handle &>(*this));
}
/// \brief Return an object that can be used in combination with cif::tie
/// to assign the values for the items @a items
template <typename... C>
[[nodiscard]] auto get(C... items) const
{
return detail::get_row_result<C...>(*this, { get_item_ix(items)... });
}
/// \brief Return a tuple of values of types @a Ts for the items @a items
template <typename... Ts, typename... C>
std::tuple<Ts...> get(C... items) const
requires(sizeof...(Ts) == sizeof...(C) and sizeof...(C) != 1)
{
return detail::get_row_result<Ts...>(*this, { get_item_ix(items)... });
}
/// \brief Get the value of item @a item cast to type @a T
template <typename T>
T get(const char *item) const
{
return operator[](get_item_ix(item)).template get<T>();
}
/// \brief Get the value of item @a item cast to type @a T
template <typename T>
[[nodiscard]] T get(std::string_view item) const
{
return operator[](get_item_ix(item)).template get<T>();
return { *m_category, *m_row, get_item_ix(item_name) };
}
/// \brief assign each of the items named in @a values to their respective value
@@ -360,39 +338,185 @@ class row_handle
void assign(uint16_t item, item_value value, bool updateLinked, bool validate = true);
/// \brief Return an object that can be used in combination with cif::tie
/// to assign the values for the items @a items
template <typename... C>
[[nodiscard]] auto get(C... items) const
{
return detail::get_row_result<C...>(*this, { get_item_ix(items)... });
}
/// \brief Return a tuple of values of types @a Ts for the items @a items
template <typename... Ts, typename... C>
std::tuple<Ts...> get(C... items) const
requires(sizeof...(Ts) == sizeof...(C) and sizeof...(C) != 1)
{
return detail::get_row_result<Ts...>(*this, { get_item_ix(items)... });
}
/// \brief Get the value of item @a item cast to type @a T
template <typename T>
[[nodiscard]] T get(std::string_view item) const
{
return operator[](get_item_ix(item)).template get<T>();
}
/// \brief compare two rows
bool operator==(const row_handle &rhs) const { return m_category == rhs.m_category and m_row == rhs.m_row; }
/// \brief compare two rows
bool operator!=(const row_handle &rhs) const { return m_category != rhs.m_category or m_row != rhs.m_row; }
private:
protected:
[[nodiscard]] uint16_t get_item_ix(std::string_view name) const;
[[nodiscard]] std::string_view get_item_name(uint16_t ix) const;
uint16_t add_item(std::string_view name);
friend cql::connection_impl;
row *get_row()
[[nodiscard]] auto get_row() const
{
return m_row;
}
[[nodiscard]] const row *get_row() const
{
return m_row;
}
// void swap(uint16_t item, row_handle &r) noexcept(false);
// {
// if (not m_category)
// throw std::runtime_error("uninitialized row");
//
// m_category->swap_item(item, *this, b);
// }
category *m_category = nullptr;
row *m_row = nullptr;
private:
uint16_t add_item(std::string_view name);
void assign(const item &i, bool updateLinked)
{
assign(i.name(), i.value(), updateLinked);
}
};
void swap(uint16_t item, row_handle &r) noexcept(false);
class const_row_handle
{
public:
/** @cond */
template <bool>
friend struct item_handle_base;
friend class category;
friend class category_index;
friend class row_initializer;
category *m_category = nullptr;
row *m_row = nullptr;
template <bool, typename...>
friend class iterator_impl_base;
const_row_handle() = default;
virtual ~const_row_handle() = default;
const_row_handle(const const_row_handle &) = default;
const_row_handle(const_row_handle &&) = default;
const_row_handle &operator=(const const_row_handle &) = default;
const_row_handle &operator=(const_row_handle &&) = default;
/** @endcond */
/// \brief constructor taking a category @a cat and a row @a r
const_row_handle(const category &cat, const row &r)
: m_category(&cat)
, m_row(&r)
{
}
/// \brief return the category this row belongs to
[[nodiscard]] const category &get_category() const
{
return *m_category;
}
/// \brief return the row ID
[[nodiscard]] int64_t row_id() const
{
return reinterpret_cast<int64_t>(m_row);
}
/// \brief Return true if the row is empty or uninitialised
[[nodiscard]] bool empty() const
{
return m_category == nullptr or m_row == nullptr;
}
/// \brief convenience method to test for empty()
explicit operator bool() const
{
return not empty();
}
/// \brief return the count of the items
[[nodiscard]] size_t size() const { return m_row->size(); }
/// \brief return a cif::item_handle to the item in item @a item_ix
const_item_handle operator[](uint16_t item_ix) const
{
return { *m_category, *m_row, item_ix };
}
/// \brief return a cif::item_handle to the item in the item named @a item_name
const_item_handle operator[](std::string_view item_name) const
{
return { *m_category, *m_row, get_item_ix(item_name) };
}
/// \brief Return an object that can be used in combination with cif::tie
/// to assign the values for the items @a items
template <typename... C>
[[nodiscard]] auto get(C... items) const
{
return detail::get_row_result<C...>(*this, { get_item_ix(items)... });
}
/// \brief Return a tuple of values of types @a Ts for the items @a items
template <typename... Ts, typename... C>
std::tuple<Ts...> get(C... items) const
requires(sizeof...(Ts) == sizeof...(C) and sizeof...(C) != 1)
{
return detail::get_row_result<Ts...>(*this, { get_item_ix(items)... });
}
/// \brief Get the value of item @a item cast to type @a T
template <typename T>
[[nodiscard]] T get(std::string_view item) const
{
return operator[](get_item_ix(item)).template get<T>();
}
/// \brief compare two rows
bool operator==(const const_row_handle &rhs) const { return m_category == rhs.m_category and m_row == rhs.m_row; }
/// \brief compare two rows
bool operator!=(const const_row_handle &rhs) const { return m_category != rhs.m_category or m_row != rhs.m_row; }
protected:
[[nodiscard]] uint16_t get_item_ix(std::string_view name) const;
[[nodiscard]] std::string_view get_item_name(uint16_t ix) const;
friend cql::connection_impl;
[[nodiscard]] auto get_row() const
{
return m_row;
}
// void swap(uint16_t item, const_row_handle &r) noexcept(false);
// {
// if (not m_category)
// throw std::runtime_error("uninitialized row");
//
// m_category->swap_item(item, *this, b);
// }
const category *m_category = nullptr;
const row *m_row = nullptr;
};
// --------------------------------------------------------------------
@@ -433,7 +557,7 @@ class row_initializer : public std::vector<item>
}
/// \brief constructor taking the values of an existing row
row_initializer(row_handle rh);
row_initializer(const_row_handle rh);
/// \brief set the value for item name @a name to @a value
void set_value(std::string name, item_value value);

View File

@@ -326,11 +326,13 @@ struct item_validator
return iequals(m_item_name, rhs.m_item_name);
}
/// @brief Validate the value in @a value for this item
/// Will throw a std::system_error exception if it fails
void operator()(std::string_view value) const;
/// @brief Validate value @a value, throws if invalid
void validate_value(const item_value &value) const;
/// @brief A more gentle version of value validation
/// @brief Validate value @a value and return potential error in @a ec
bool validate_value(const item_value &value, std::error_code &ec) const noexcept;
/// @brief Validate value @a value and return potential error in @a ec
bool validate_value(std::string_view value, std::error_code &ec) const noexcept;
};

View File

@@ -97,10 +97,11 @@ class row_comparator
int d = 0;
for (const auto &[k, f] : m_comparator)
{
std::string_view ka = rha[k].text();
std::string_view kb = rhb[k].text();
// std::string_view ka = rha[k].text();
// std::string_view kb = rhb[k].text();
d = f(ka, kb);
// d = f(ka, kb);
d = rha[k].value().compare(rhb[k].value());
if (d != 0)
break;
@@ -109,33 +110,35 @@ class row_comparator
return d;
}
int operator()(const category &cat, const category::key_type &a, const row *b) const
{
assert(b);
// int operator()(const category &cat, const category::key_type &a, const row *b) const
// {
// assert(b);
row_handle rhb(cat, *b);
// row_handle rhb(cat, *b);
int d = 0;
auto ai = a.begin();
// int d = 0;
// auto ai = a.begin();
for (const auto &[k, f] : m_comparator)
{
assert(ai != a.end());
// for (const auto &[k, f] : m_comparator)
// {
// // assert(ai != a.end());
std::string_view ka = ai->value;
std::string_view kb = rhb[k].text();
// // std::string_view ka = ai->value;
// // std::string_view kb = rhb[k].text();
if (not(ai->may_be_null and rhb[k].empty()))
d = f(ka, kb);
// // if (not(ai->may_be_null and rhb[k].empty()))
// // d = f(ka, kb);
if (d != 0)
break;
// d = rha[k].value().compare(rhb[k].value());
++ai;
}
// if (d != 0)
// break;
return d;
}
// ++ai;
// }
// return d;
// }
private:
using compareFunc = std::function<int(std::string_view, std::string_view)>;
@@ -368,34 +371,35 @@ row *category_index::find(const category &cat, row *k) const
row *category_index::find_by_value(const category &cat, const category::key_type &k) const
{
// sort the values in k first
return nullptr;
// // sort the values in k first
category::key_type k2;
for (auto &f : cat.key_item_indices())
{
auto fld = cat.get_item_name(f);
// category::key_type k2;
// for (auto &f : cat.key_item_indices())
// {
// auto fld = cat.get_item_name(f);
auto ki = std::ranges::find_if(k, [&fld](auto &i)
{ return i.name == fld; });
if (ki == k.end())
k2.emplace_back(std::string{ fld }, "");
else
k2.emplace_back(*ki);
}
// auto ki = std::ranges::find_if(k, [&fld](auto &i)
// { return i.name == fld; });
// if (ki == k.end())
// k2.emplace_back(std::string{ fld }, "");
// else
// k2.emplace_back(*ki);
// }
const entry *r = m_root;
while (r != nullptr)
{
int d = m_row_comparator(cat, k2, r->m_row);
if (d < 0)
r = r->m_left;
else if (d > 0)
r = r->m_right;
else
break;
}
// const entry *r = m_root;
// while (r != nullptr)
// {
// int d = m_row_comparator(cat, k2, r->m_row);
// if (d < 0)
// r = r->m_left;
// else if (d > 0)
// r = r->m_right;
// else
// break;
// }
return r ? r->m_row : nullptr;
// return r ? r->m_row : nullptr;
}
void category_index::insert(category &cat, row *k)
@@ -422,7 +426,7 @@ category_index::entry *category_index::insert(category &cat, entry *h, row *v)
for (auto col : cat.key_items())
{
if (rh[col])
os << col << ": " << std::quoted(rh[col].text()) << "; ";
os << col << ": " << std::quoted(rh[col].str()) << "; ";
}
throw duplicate_key_error("Duplicate Key violation, cat: " + cat.name() + " values: " + os.str());
@@ -870,7 +874,7 @@ bool category::is_valid() const
seen = true;
std::error_code ec;
iv->validate_value(vi->text(), ec);
iv->validate_value(*vi, ec);
if (ec != std::errc{})
{
@@ -990,6 +994,25 @@ row_handle category::operator[](const key_type &key)
// --------------------------------------------------------------------
const_row_handle category::operator[](const key_type &key) const
{
const_row_handle result{};
if (not empty())
{
if (m_index == nullptr)
throw std::logic_error("Category " + m_name + " does not have an index");
auto row = m_index->find_by_value(*this, key);
if (row != nullptr)
result = { *this, *row };
}
return result;
}
// --------------------------------------------------------------------
condition category::get_parents_condition(row_handle rh, const category &parentCat) const
{
if (m_validator == nullptr or m_cat_validator == nullptr)
@@ -1010,12 +1033,12 @@ condition category::get_parents_condition(row_handle rh, const category &parentC
for (std::size_t ix = 0; ix < link->m_child_keys.size(); ++ix)
{
auto childValue = rh[link->m_child_keys[ix]];
auto childValue = rh[link->m_child_keys[ix]].value();
if (childValue.empty())
continue;
cond = std::move(cond) and key(link->m_parent_keys[ix]) == childValue.text();
cond = std::move(cond) and key(link->m_parent_keys[ix]) == childValue;
}
result = std::move(result) or std::move(cond);
@@ -1055,14 +1078,14 @@ condition category::get_children_condition(row_handle rh, const category &childC
auto childKey = link->m_child_keys[ix];
auto parentKey = link->m_parent_keys[ix];
auto parentValue = rh[parentKey];
auto parentValue = rh[parentKey].value();
if (parentValue.empty())
cond = std::move(cond) and key(childKey) == null;
else if (link->m_parent_keys.size() > 1 and not mandatoryChildItems.contains(childKey))
cond = std::move(cond) and (key(childKey) == parentValue.text() or key(childKey) == null);
cond = std::move(cond) and (key(childKey) == parentValue or key(childKey) == null);
else
cond = std::move(cond) and key(childKey) == parentValue.text();
cond = std::move(cond) and key(childKey) == parentValue;
}
result = std::move(result) or std::move(cond);
@@ -1405,7 +1428,7 @@ void category::update_value(const std::vector<row_handle> &rows, std::string_vie
{
for (auto row : rows)
{
std::string value{ value_provider(row[item_name].text()) };
auto value{ value_provider(row[item_name].value()) };
std::error_code ec;
col.m_validator->validate_value(value, ec);
@@ -1417,8 +1440,8 @@ void category::update_value(const std::vector<row_handle> &rows, std::string_vie
// update and see if we need to update any child categories that depend on this value
for (auto parent : rows)
{
std::string oldValue{ parent[item_name].text() };
std::string value{ value_provider(oldValue) };
auto oldValue{ parent[item_name].value() };
auto value{ value_provider(oldValue) };
update_value(parent.get_row(), colIx, value, false, false);
@@ -1441,7 +1464,7 @@ void category::update_value(const std::vector<row_handle> &rows, std::string_vie
cond = std::move(cond) && key(ck) == oldValue;
}
else
cond = std::move(cond) && key(ck) == parent[pk].text();
cond = std::move(cond) && key(ck) == parent[pk].value();
}
auto children = childCat->find(std::move(cond));
@@ -1465,7 +1488,7 @@ void category::update_value(const std::vector<row_handle> &rows, std::string_vie
std::string pk = linked->m_parent_keys[ix];
std::string ck = linked->m_child_keys[ix];
cond_c = std::move(cond_c) && key(pk) == child[ck].text();
cond_c = std::move(cond_c) && key(pk) == child[ck].value();
}
auto parents = find(std::move(cond_c));
@@ -1486,7 +1509,7 @@ void category::update_value(const std::vector<row_handle> &rows, std::string_vie
if (pk == item_name)
check = std::move(check) && key(ck) == value;
else
check = std::move(check) && key(ck) == parent[pk].text();
check = std::move(check) && key(ck) == parent[pk].value();
}
if (childCat->contains(std::move(check))) // phew..., narrow escape
@@ -1527,11 +1550,13 @@ void category::update_value(row *row, uint16_t item, item_value value, bool upda
if (ival != nullptr and *ival == value)
return;
auto oldValue = *ival;
m_dirty = true;
// check the value
if (col.m_validator and validate)
col.m_validator->operator()(value);
col.m_validator->validate_value(value);
// If the item is part of the Key for this category, remove it from the index
// before updating
@@ -1580,11 +1605,11 @@ void category::update_value(row *row, uint16_t item, item_value value, bool upda
if (pk == iv->m_item_name)
{
childItemName = ck;
cond = std::move(cond) and key(ck) == oldStrValue;
cond = std::move(cond) and key(ck) == oldValue;
}
else
{
std::string_view pk_value = rh[pk].text();
auto pk_value = rh[pk].value();
if (pk_value.empty())
cond = std::move(cond) and key(ck) == null;
else
@@ -1616,7 +1641,7 @@ void category::update_value(row *row, uint16_t item, item_value value, bool upda
cond_n = std::move(cond_n) and key(ck) == value;
else
{
std::string_view pk_value = rh[pk].text();
auto pk_value = rh[pk].value();
if (pk_value.empty())
cond_n = std::move(cond_n) and key(ck) == null;
else
@@ -1651,7 +1676,7 @@ row *category::clone_row(const row &r)
if (not i)
continue;
result->append(ix, { i.text() });
result->append(ix, i);
}
}
catch (...)
@@ -1684,7 +1709,7 @@ row_handle category::create_copy(row_handle r)
{
auto i = r.m_row->get(ix);
if (i != nullptr)
items.emplace_back(m_items[ix].m_name, i->text());
items.emplace_back(m_items[ix].m_name, *i);
}
if (m_cat_validator and m_cat_validator->m_keys.size() == 1)
@@ -1742,7 +1767,7 @@ category::iterator category::insert_impl(const_iterator pos, row *n)
auto i = n->get(ix);
if (i != nullptr)
{
iv->operator()(i->value());
iv->validate_value(*i);
seen = true;
}
@@ -2055,11 +2080,11 @@ void category::write_cif(std::ostream &os, const std::vector<uint16_t> &order, b
if (v == nullptr)
continue;
if (v->text().find('\n') == std::string_view::npos)
if (v->str().find('\n') == std::string_view::npos)
{
std::size_t l = v->text().length();
std::size_t l = v->str().length();
if (not sac_parser::is_unquoted_string(v->text()))
if (not sac_parser::is_unquoted_string(v->str()))
l += 2;
if (l > 132)
@@ -2079,10 +2104,10 @@ void category::write_cif(std::ostream &os, const std::vector<uint16_t> &order, b
{
std::size_t w = itemWidths[cix];
std::string_view s;
std::string s;
auto iv = r->get(cix);
if (iv != nullptr)
s = iv->text();
s = iv->str();
if (s.empty())
s = "?";
@@ -2134,10 +2159,10 @@ void category::write_cif(std::ostream &os, const std::vector<uint16_t> &order, b
if (not right_aligned[cix])
continue;
std::string_view s;
std::string s;
auto iv = m_head->get(cix);
if (iv != nullptr)
s = iv->text();
s = iv->str();
if (s.empty())
s = "?";
@@ -2160,10 +2185,10 @@ void category::write_cif(std::ostream &os, const std::vector<uint16_t> &order, b
os << m_name << '.';
os << col.m_name << std::string(l - col.m_name.length() - m_name.length() - 2, ' ');
std::string_view s;
std::string s;
auto iv = m_head->get(cix);
if (iv != nullptr)
s = iv->text();
s = iv->str();
if (s.empty())
s = "?";
@@ -2262,7 +2287,7 @@ void category::write_delimited(std::ostream &os, const std::vector<uint16_t> &or
if (v == nullptr)
continue;
size_t l = get_line(v->text()).length();
size_t l = get_line(v->str()).length();
if (itemWidths[ix] < l)
itemWidths[ix] = l;
}
@@ -2324,11 +2349,11 @@ void category::write_delimited(std::ostream &os, const std::vector<uint16_t> &or
std::size_t w = itemWidths[cix];
std::string_view s;
std::string s;
auto iv = r->get(cix);
if (iv != nullptr)
s = iv->text();
s = iv->str();
if (s == "?" or s == ".")
s = "";
@@ -2383,7 +2408,7 @@ void category::write_markdown(std::ostream &os, const std::vector<uint16_t> &ord
if (v == nullptr)
continue;
size_t l = v->text().length();
size_t l = v->str().length();
if (itemWidths[ix] < l)
itemWidths[ix] = l;
}
@@ -2432,11 +2457,11 @@ void category::write_markdown(std::ostream &os, const std::vector<uint16_t> &ord
std::size_t w = itemWidths[cix];
std::string_view s;
std::string s;
auto iv = r->get(cix);
if (iv != nullptr)
s = iv->text();
s = iv->str();
if (s == "?" or s == ".")
s = "";
@@ -2509,7 +2534,7 @@ void category::write_table(std::ostream &os, const std::vector<uint16_t> &order,
if (v == nullptr)
continue;
size_t l = v->text().length();
size_t l = v->str().length();
if (itemWidths[ix] < l)
itemWidths[ix] = l;
}
@@ -2565,11 +2590,11 @@ void category::write_table(std::ostream &os, const std::vector<uint16_t> &order,
std::size_t w = itemWidths[cix];
std::string_view s;
std::string s;
auto iv = r->get(cix);
if (iv != nullptr)
s = iv->text();
s = iv->str();
if (s == "?" or s == ".")
s = "";
@@ -2664,7 +2689,7 @@ bool category::operator==(const category &rhs) const
// a.reorderByIndex();
// b.reorderByIndex();
auto rowEqual = [&](const row_handle &a, const row_handle &b)
auto rowEqual = [&](const_row_handle &a, const_row_handle &b)
{
int d = 0;
@@ -2675,7 +2700,7 @@ bool category::operator==(const category &rhs) const
std::tie(item_name, compare) = item_names[kix];
d = compare(a[item_name].text(), b[item_name].text());
d = a[item_name].compare(b[item_name]);
if (d != 0)
break;
@@ -2706,14 +2731,14 @@ bool category::operator==(const category &rhs) const
// make it an option to compare unapplicable to empty or something
auto ta = ra[item_name].text();
if (ta == "." or ta == "?")
ta = "";
auto tb = rb[item_name].text();
if (tb == "." or tb == "?")
tb = "";
// auto ta = ra[item_name].text();
// if (ta == "." or ta == "?")
// ta = "";
// auto tb = rb[item_name].text();
// if (tb == "." or tb == "?")
// tb = "";
if (compare(ta, tb) != 0)
if (ra[item_name].compare(rb[item_name]) != 0)
return false;
}

View File

@@ -567,9 +567,9 @@ compound *local_compound_factory_impl::construct_compound(const datablock &rdb,
{ "atom_id", atom_id },
{ "type_symbol", type_symbol },
{ "charge", charge },
{ "model_Cartn_x", x.has_value() ? x : xi, 3 },
{ "model_Cartn_y", y.has_value() ? y : yi, 3 },
{ "model_Cartn_z", z.has_value() ? z : zi, 3 },
{ "model_Cartn_x", x.has_value() ? x : xi/* , 3 */ },
{ "model_Cartn_y", y.has_value() ? y : yi/* , 3 */ },
{ "model_Cartn_z", z.has_value() ? z : zi/* , 3 */ },
{ "pdbx_ordinal", ord++ } });
formal_charge += charge;

View File

@@ -78,7 +78,7 @@ namespace detail
// return this;
// }
//
// bool test(row_handle r) const override
// bool test(const_row_handle r) const override
// {
// return m_single_hit == r;
// }
@@ -126,21 +126,6 @@ namespace detail
return this;
}
condition_impl *key_equals_number_condition_impl::prepare(const category &c)
{
m_item_ix = c.get_item_ix(m_item_name);
if (c.get_cat_validator() != nullptr and
c.key_item_indices().contains(m_item_ix) and
c.key_item_indices().size() == 1)
{
item v(m_item_name, m_value);
m_single_hit = c[{ { m_item_name, std::string{ v.value() }, false } }];
}
return this;
}
bool found_in_range(condition_impl *c, std::vector<and_condition_impl *>::iterator b, std::vector<and_condition_impl *>::iterator e)
{
bool result = true;
@@ -237,17 +222,6 @@ namespace detail
continue;
}
if (auto s = dynamic_cast<const key_equals_number_condition_impl *>(sub); s != nullptr)
{
if (keys.contains(s->m_item_name))
{
item v{ s->m_item_name, s->m_value };
lookup.emplace_back(s->m_item_name, std::string{ v.value() });
subs.emplace_back(sub);
}
continue;
}
if (auto s = dynamic_cast<const key_equals_or_empty_condition_impl *>(sub); s != nullptr)
{
if (keys.contains(s->m_item_name))
@@ -258,17 +232,6 @@ namespace detail
}
continue;
}
if (auto s = dynamic_cast<const key_equals_number_or_empty_condition_impl *>(sub); s != nullptr)
{
if (keys.contains(s->m_item_name))
{
item v{ s->m_item_name, s->m_value };
lookup.emplace_back(s->m_item_name, std::string{ v.value() }, true);
subs.emplace_back(sub);
}
continue;
}
}
if (lookup.size() == keys.size())
@@ -283,7 +246,7 @@ namespace detail
return this;
}
bool and_condition_impl::test(row_handle r) const
bool and_condition_impl::test(const_row_handle r) const
{
bool result = true;

View File

@@ -156,7 +156,7 @@ class dictionary_parser : public parser
match(CIFToken::ITEM_NAME);
}
while (m_lookahead == CIFToken::VALUE)
while (m_lookahead >= CIFToken::VALUE_INAPPLICABLE)
{
cat->emplace({});
auto row = cat->back();
@@ -164,7 +164,7 @@ class dictionary_parser : public parser
for (auto item_name : item_names)
{
row[item_name] = m_token_value;
match(CIFToken::VALUE);
match(m_lookahead);
}
}
@@ -184,7 +184,7 @@ class dictionary_parser : public parser
cat->emplace({});
cat->back()[item_name] = m_token_value;
match(CIFToken::VALUE);
match(m_lookahead >= CIFToken::VALUE_INAPPLICABLE ? m_lookahead : CIFToken::VALUE_CHARSTRING);
}
}
@@ -257,7 +257,7 @@ class dictionary_parser : public parser
auto vi = std::ranges::find(ivs, item_validator{ item_name });
if (vi == ivs.end())
ivs.emplace_back(item_name, iequals(mandatory, "yes"), tv, ess, defaultValue, cat_name, aliases);
ivs.push_back(item_validator{ item_name, iequals(mandatory, "yes"), tv, ess, defaultValue, cat_name, std::move(aliases) });
else
{
// need to update the itemValidator?

View File

@@ -36,38 +36,70 @@
namespace cif
{
const item_handle item_handle::s_null_item;
row_handle s_null_row_handle;
item_handle::item_handle() noexcept
: m_item_ix(std::numeric_limits<uint16_t>::max())
, m_row_handle(s_null_row_handle)
int item_value::compare(const item_value &b, bool ignore_case) const noexcept
{
}
int d = static_cast<int>(m_data.m_type) - static_cast<int>(b.m_data.m_type);
std::string_view item_handle::text() const
{
if (not m_row_handle.empty())
if (d == 0)
{
auto iv = m_row_handle.m_row->get(m_item_ix);
if (iv != nullptr)
return iv->text();
switch (m_data.m_type)
{
case cif::item_value_type::BOOLEAN:
d = static_cast<int>(m_data.m_value.m_boolean) - static_cast<int>(b.m_data.m_value.m_boolean);
break;
case cif::item_value_type::INT:
d = m_data.m_value.m_integer - b.m_data.m_value.m_integer;
break;
case cif::item_value_type::FLOAT:
{
auto dp = (m_data.m_value.m_float <=> b.m_data.m_value.m_float);
if (dp == std::partial_ordering::less)
d = -1;
else if (dp == std::partial_ordering::greater)
d = 1;
break;
}
case cif::item_value_type::TEXT:
d = m_data.sv().compare(b.m_data.sv());
break;
default:;
}
}
return {};
return d;
}
void item_handle::assign_value(std::string_view value)
{
assert(not m_row_handle.empty());
m_row_handle.assign(m_item_ix, value, true);
}
// void const_item_handle::assign_value(const item_value &value)
// {
// assert(not m_row_handle.empty());
// m_row_handle.assign(m_item_ix, value, true);
// }
void item_handle::swap(item_handle &b)
std::ostream &operator<<(std::ostream &os, const item_value &v)
{
assert(m_item_ix == b.m_item_ix);
// assert(&m_row_handle.m_category == &b.m_row_handle.m_category);
m_row_handle.swap(m_item_ix, b.m_row_handle);
switch (v.type())
{
case cif::item_value_type::BOOLEAN:
os << std::boolalpha << v.m_data.m_value.m_boolean;
break;
case cif::item_value_type::INT:
os << v.m_data.m_value.m_integer;
break;
case cif::item_value_type::FLOAT:
os << v.m_data.m_value.m_float;
break;
case cif::item_value_type::TEXT:
os << v.m_data.sv();
break;
case cif::item_value_type::MISSING:
os << '?';
break;
case cif::item_value_type::INAPPLICABLE:
os << '.';
break;
}
return os;
}
} // namespace cif

View File

@@ -237,7 +237,7 @@ atom residue::create_new_atom(atom_type inType, const std::string &inAtomID, poi
{ "auth_atom_id", inAtomID },
{ "auth_comp_id", m_compound_id },
{ "auth_seq_id", m_pdb_seq_num },
{ "occupancy", 1.0f, 2 },
{ "occupancy", 1.0f/* , 2 */ },
{ "B_iso_or_equiv", 20.0f },
{ "pdbx_PDB_model_num", m_structure->get_model_nr() },
});
@@ -955,8 +955,8 @@ cif::mm::atom sugar::add_atom(row_initializer atom_info)
atom_info.set_value({ "auth_asym_id", m_branch->get_asym_id() });
atom_info.set_value({ "auth_comp_id", m_compound_id });
atom_info.set_value({ "auth_seq_id", m_pdb_seq_num });
atom_info.set_value({ "occupancy", 1.0, 2 });
atom_info.set_value({ "B_iso_or_equiv", 30.0, 2 });
atom_info.set_value({ "occupancy", 1.0/* , 2 */ });
atom_info.set_value({ "B_iso_or_equiv", 30.0/* , 2 */ });
atom_info.set_value({ "pdbx_PDB_model_num", 1 });
auto row = atom_site.emplace(std::move(atom_info));
@@ -1859,11 +1859,7 @@ void structure::swap_atoms(atom a1, atom a2)
auto r2 = atomSites.find1(key("id") == a2.id());
for (std::string fld : std::initializer_list<std::string>{ "label_atom_id", "auth_atom_id", "type_symbol" })
{
auto l1 = r1[fld];
auto l2 = r2[fld];
l1.swap(l2);
}
swap(r1[fld].value(), r2[fld].value());
}
catch (const std::exception &ex)
{
@@ -2280,7 +2276,7 @@ std::string structure::create_non_poly(const std::string &entity_id, std::vector
atom.set_value_if_empty({ "auth_seq_id", 1 });
atom.set_value_if_empty({ "pdbx_PDB_model_num", 1 });
atom.set_value_if_empty({ "label_alt_id", "" });
atom.set_value_if_empty({ "occupancy", 1.0, 2 });
atom.set_value_if_empty({ "occupancy", 1.0/* , 2 */ });
auto row = atom_site.emplace(atom.begin(), atom.end());
@@ -2378,7 +2374,7 @@ void structure::create_water(row_initializer atom)
atom.set_value_if_empty({ "auth_comp_id", "HOH" });
atom.set_value_if_empty({ "pdbx_PDB_model_num", 1 });
atom.set_value_if_empty({ "label_alt_id", "" });
atom.set_value_if_empty({ "occupancy", 1.0, 2 });
atom.set_value_if_empty({ "occupancy", 1.0/* , 2 */ });
auto row = atom_site.emplace(atom.begin(), atom.end());
@@ -2449,7 +2445,7 @@ std::string structure::create_link(atom a1, atom a2, const std::string &link_typ
{ "ptnr2_auth_seq_id", a2.get_auth_seq_id() },
{ "ptnr2_symmetry", a2.symmetry() },
{ "pdbx_dist_value", distance(a1.get_location(), a2.get_location()), 3 },
{ "pdbx_dist_value", distance(a1.get_location(), a2.get_location())/* , 3 */ },
{ "pdbx_role", role } });
return link_id;

View File

@@ -276,6 +276,8 @@ sac_parser::CIFToken sac_parser::get_next_token()
m_token_buffer.clear();
m_token_value = {};
bool negative = false;
reserved_words_automaton dag;
while (result == CIFToken::UNKNOWN)
@@ -314,6 +316,15 @@ sac_parser::CIFToken sac_parser::get_next_token()
}
else if (dag.move(ch) == reserved_words_automaton::undefined)
state = State::Reserved;
else if (ch == '+' or ch == '-')
{
negative = true;
state = State::Numeric_Integer;
}
else if (ch >= '0' and ch <= '9')
state = State::Numeric_Integer;
else if (ch == '.')
state = State::Numeric_Float;
else
state = State::Value;
break;
@@ -350,7 +361,7 @@ sac_parser::CIFToken sac_parser::get_next_token()
if (not is_non_blank(ch))
{
retract();
result = CIFToken::VALUE;
result = CIFToken::VALUE_UNKNOWN;
}
else
state = State::Value;
@@ -396,7 +407,7 @@ sac_parser::CIFToken sac_parser::get_next_token()
{
assert(m_token_buffer.size() >= 2);
m_token_value = std::string_view(m_token_buffer.data() + 1, m_token_buffer.size() - 3);
result = CIFToken::VALUE;
result = CIFToken::VALUE_CHARSTRING;
}
else if (ch == kEOF)
error("unterminated textfield");
@@ -411,7 +422,7 @@ sac_parser::CIFToken sac_parser::get_next_token()
{
assert(m_token_buffer.size() >= 2);
m_token_value = std::string_view(m_token_buffer.data() + 1, m_token_buffer.size() - 3);
result = CIFToken::VALUE;
result = CIFToken::VALUE_TEXTFIELD;
}
else if (ch == kEOF)
error("unterminated textfield");
@@ -432,7 +443,7 @@ sac_parser::CIFToken sac_parser::get_next_token()
if (is_white(ch))
{
retract();
result = CIFToken::VALUE;
result = CIFToken::VALUE_CHARSTRING;
if (m_token_buffer.size() < 2)
error("Invalid quoted string token");
@@ -467,7 +478,7 @@ sac_parser::CIFToken sac_parser::get_next_token()
if (not is_non_blank(ch))
{
retract();
result = CIFToken::VALUE;
result = CIFToken::VALUE_CHARSTRING;
m_token_value = std::string_view(m_token_buffer.data(), m_token_buffer.size());
}
else
@@ -508,11 +519,65 @@ sac_parser::CIFToken sac_parser::get_next_token()
}
break;
case State::Numeric_Integer:
if (ch == '.')
state = State::Numeric_Float;
else if (ch == 'e' or ch == 'E')
state = State::Numeric_Exponent1;
else if (not is_non_blank(ch))
{
retract();
result = CIFToken::VALUE_NUMERIC_INTEGER;
}
else if (ch < '0' or ch > '9')
state = State::Value;
break;
case State::Numeric_Float:
if (not is_non_blank(ch))
{
retract();
if (m_token_buffer.size() == 1)
result = CIFToken::VALUE_INAPPLICABLE;
else
result = CIFToken::VALUE_NUMERIC_FLOAT;
}
else if (ch == 'e' or ch == 'E')
state = State::Numeric_Exponent1;
else if (ch < '0' or ch > '9')
state = State::Value;
break;
case State::Numeric_Exponent1:
if (ch == '+' or ch == '-' or (ch >= '0' and ch <= '9'))
state = State::Numeric_Exponent2;
else
{
if (VERBOSE > 0)
std::cerr << "parsing " << std::string_view{ m_token_buffer.data(), m_token_buffer.size() } << " Invalid floating point value, expected digit or sign character\n";
state = State::Value;
}
break;
case State::Numeric_Exponent2:
if (not is_non_blank(ch))
{
retract();
result = CIFToken::VALUE_NUMERIC_FLOAT;
}
else if (ch < '0' or ch > '9')
{
if (VERBOSE > 0)
std::cerr << "parsing " << std::string_view{ m_token_buffer.data(), m_token_buffer.size() } << " Invalid floating point value, expected exponent digit\n";
state = State::Value;
}
break;
case State::Value:
if (not is_non_blank(ch))
{
retract();
result = CIFToken::VALUE;
result = CIFToken::VALUE_CHARSTRING;
m_token_value = std::string_view(m_token_buffer.data(), m_token_buffer.size());
break;
}
@@ -525,12 +590,25 @@ sac_parser::CIFToken sac_parser::get_next_token()
}
}
if (VERBOSE >= 5)
// if (VERBOSE >= 5)
// {
// std::cerr << get_token_name(result);
// if (result != CIFToken::END_OF_FILE)
// std::cerr << " " << std::quoted(m_token_value);
// std::cerr << '\n';
// }
if (result == CIFToken::VALUE_NUMERIC_INTEGER)
{
std::cerr << get_token_name(result);
if (result != CIFToken::END_OF_FILE)
std::cerr << " " << std::quoted(m_token_value);
std::cerr << '\n';
auto [ptr, ec] = std::from_chars(m_token_buffer.data(), m_token_buffer.data() + m_token_buffer.size(), m_token_value_int);
if (ec != std::errc{})
error("Invalid integer value: " + std::make_error_code(ec).message());
}
else if (result == CIFToken::VALUE_NUMERIC_FLOAT)
{
auto [ptr, ec] = std::from_chars(m_token_buffer.data(), m_token_buffer.data() + m_token_buffer.size(), m_token_value_float);
if (ec != std::errc{})
error("Invalid integer value: " + std::make_error_code(ec).message());
}
return result;
@@ -785,7 +863,10 @@ void sac_parser::parse_global()
while (m_lookahead == CIFToken::ITEM_NAME)
{
match(CIFToken::ITEM_NAME);
match(CIFToken::VALUE);
if (m_lookahead >= CIFToken::VALUE_INAPPLICABLE)
match(m_lookahead);
else
match(CIFToken::VALUE_CHARSTRING);
}
}
@@ -824,14 +905,38 @@ void sac_parser::parse_datablock()
match(CIFToken::ITEM_NAME);
}
while (m_lookahead == CIFToken::VALUE)
while (m_lookahead >= CIFToken::VALUE_INAPPLICABLE)
{
produce_row();
for (auto item_name : item_names)
{
produce_item(cat, item_name, m_token_value);
match(CIFToken::VALUE);
switch (m_lookahead)
{
case CIFToken::VALUE_INAPPLICABLE:
produce_item(cat, item_name, nullptr);
match(m_lookahead);
break;
case CIFToken::VALUE_UNKNOWN:
produce_item(cat, item_name, std::optional<std::string>{});
match(m_lookahead);
break;
case CIFToken::VALUE_NUMERIC_INTEGER:
produce_item(cat, item_name, m_token_value_int);
match(m_lookahead);
break;
case CIFToken::VALUE_NUMERIC_FLOAT:
produce_item(cat, item_name, m_token_value_float);
match(m_lookahead);
break;
case CIFToken::VALUE_CHARSTRING:
case CIFToken::VALUE_TEXTFIELD:
produce_item(cat, item_name, m_token_value);
match(m_lookahead);
break;
default:;
match(CIFToken::VALUE_CHARSTRING);
}
}
}
@@ -853,9 +958,33 @@ void sac_parser::parse_datablock()
match(CIFToken::ITEM_NAME);
produce_item(cat, itemName, m_token_value);
switch (m_lookahead)
{
case CIFToken::VALUE_INAPPLICABLE:
produce_item(cat, itemName, nullptr);
match(CIFToken::VALUE_INAPPLICABLE);
break;
case CIFToken::VALUE_UNKNOWN:
produce_item(cat, itemName, item_value{ std::optional<std::string>{} });
match(CIFToken::VALUE_UNKNOWN);
break;
case CIFToken::VALUE_NUMERIC_INTEGER:
produce_item(cat, itemName, m_token_value_int);
match(CIFToken::VALUE_NUMERIC_INTEGER);
break;
case CIFToken::VALUE_NUMERIC_FLOAT:
produce_item(cat, itemName, m_token_value_float);
match(CIFToken::VALUE_NUMERIC_FLOAT);
break;
case CIFToken::VALUE_CHARSTRING:
case CIFToken::VALUE_TEXTFIELD:
produce_item(cat, itemName, m_token_value);
match(m_lookahead);
break;
default:
match(CIFToken::VALUE_CHARSTRING);
}
match(CIFToken::VALUE);
break;
}

View File

@@ -652,9 +652,9 @@ class FBase
mRow = r.front();
}
[[nodiscard]] std::string_view text() const
[[nodiscard]] std::string text() const
{
return mRow.empty() or mRow[mField].empty() ? "" : mRow[mField].text();
return mRow.empty() or mRow[mField].empty() ? "" : mRow[mField].str();
}
row_handle mRow;

View File

@@ -4566,7 +4566,7 @@ void PDBFileParser::ConstructEntities()
{ "id", cc },
{ "name", name },
{ "formula", formula },
{ "formula_weight", formulaWeight, 3 },
{ "formula_weight", formulaWeight/* , 3 */ },
{ "mon_nstd_flag", nstd },
{ "type", type }
});

View File

@@ -1534,7 +1534,7 @@ bool Remark3Parser::parse(const std::string &expMethod, PDBRecord *r, cif::datab
continue;
for (auto &iv : cv->m_item_validators)
r2[iv.m_item_name] = r1[iv.m_item_name].text();
r2[iv.m_item_name] = r1[iv.m_item_name].str();
}
}
else

View File

@@ -26,7 +26,8 @@
#include "cif++.hpp"
#include "cif++/compound.hpp"
#include "cif++/cql.hpp"
// #include "cif++/cql.hpp"
#include "cif++/item.hpp"
#include "cif++/point.hpp"
#include "cif++/row.hpp"
@@ -187,7 +188,7 @@ void checkEntities(datablock &db)
}
if (formula_weight > 0)
entity.assign({ { "formula_weight", formula_weight, 3 } });
entity.assign({ { "formula_weight", formula_weight/* , 3 */ } });
}
}
@@ -454,10 +455,10 @@ void checkChemCompRecords(datablock &db)
for (auto chem_comp_entry : chem_comp)
{
auto compound = cf.create(chem_comp_entry["id"].text());
auto compound = cf.create(chem_comp_entry["id"].str());
if (not compound)
std::cerr << "Unknown compound: " << chem_comp_entry["id"].text() << '\n';
std::cerr << "Unknown compound: " << chem_comp_entry["id"].str() << '\n';
else
{
std::vector<item> items;
@@ -544,7 +545,7 @@ void checkAtomRecords(datablock &db)
if (row["type_symbol"].empty())
throw std::runtime_error("Missing type symbol in atom_site record");
std::string symbol{ row["type_symbol"].text() };
std::string symbol{ row["type_symbol"].str() };
if (atom_type.count("symbol"_key == symbol) == 0)
atom_type.emplace({ { "symbol", symbol } });
@@ -617,19 +618,19 @@ void checkAtomRecords(datablock &db)
row["label_seq_id"] = std::to_string(seq_id);
if (row["label_asym_id"].empty())
row["label_asym_id"] = row["auth_asym_id"].text();
row["label_asym_id"] = row["auth_asym_id"].value();
else if (row["auth_asym_id"].empty())
row["auth_asym_id"] = row["label_asym_id"].text();
row["auth_asym_id"] = row["label_asym_id"].value();
if (row["label_comp_id"].empty())
row["label_comp_id"] = row["auth_comp_id"].text();
row["label_comp_id"] = row["auth_comp_id"].value();
else if (row["auth_comp_id"].empty())
row["auth_comp_id"] = row["label_comp_id"].text();
row["auth_comp_id"] = row["label_comp_id"].value();
if (row["label_atom_id"].empty())
row["label_atom_id"] = row["auth_atom_id"].text();
row["label_atom_id"] = row["auth_atom_id"].value();
else if (row["auth_atom_id"].empty())
row["auth_atom_id"] = row["label_atom_id"].text();
row["auth_atom_id"] = row["label_atom_id"].value();
// Rewrite the coordinates and other items that look better in a fixed format
// Be careful not to nuke invalidly formatted data here
@@ -648,14 +649,14 @@ void checkAtomRecords(datablock &db)
if (auto [ptr, ec] = cif::from_chars(s.data(), s.data() + s.length(), v); ec != std::errc{})
continue;
if (s.length() < prec + 1UL or s[s.length() - prec - 1] != '.')
/* if (s.length() < prec + 1UL or s[s.length() - prec - 1] != '.')
{
char b[12];
if (auto [ptr, ec] = std::to_chars(b, b + sizeof(b), v, std::chars_format::fixed, prec); ec == std::errc{})
row.assign(item_name, { b, static_cast<std::string::size_type>(ptr - b) }, false, false);
}
}
*/ }
}
// auto *cv = atom_site.get_cat_validator();
@@ -714,24 +715,24 @@ void checkAtomAnisotropRecords(datablock &db)
// this happens sometimes (Phenix):
if (row["type_symbol"].empty())
row["type_symbol"] = parent["type_symbol"].text();
else if (row["type_symbol"].text() != parent["type_symbol"].text())
row["type_symbol"] = parent["type_symbol"].value();
else if (row["type_symbol"].value() != parent["type_symbol"].value())
{
if (cif::VERBOSE and std::exchange(warnReplaceTypeSymbol, false))
std::clog << "Replacing type_symbol in atom_site_anisotrop record(s)\n";
row["type_symbol"] = parent["type_symbol"].text();
row["type_symbol"] = parent["type_symbol"].value();
}
if (row["pdbx_auth_alt_id"].empty() and not parent["pdbx_auth_alt_id"].empty())
row["pdbx_auth_alt_id"] = parent["pdbx_auth_alt_id"].text();
row["pdbx_auth_alt_id"] = parent["pdbx_auth_alt_id"].value();
if (row["pdbx_label_seq_id"].empty() and not parent["label_seq_id"].empty())
row["pdbx_label_seq_id"] = parent["label_seq_id"].text();
row["pdbx_label_seq_id"] = parent["label_seq_id"].value();
if (row["pdbx_label_asym_id"].empty() and not parent["label_asym_id"].empty())
row["pdbx_label_asym_id"] = parent["label_asym_id"].text();
row["pdbx_label_asym_id"] = parent["label_asym_id"].value();
if (row["pdbx_label_atom_id"].empty() and not parent["label_atom_id"].empty())
row["pdbx_label_atom_id"] = parent["label_atom_id"].text();
row["pdbx_label_atom_id"] = parent["label_atom_id"].value();
if (row["pdbx_label_comp_id"].empty() and not parent["label_comp_id"].empty())
row["pdbx_label_comp_id"] = parent["label_comp_id"].text();
row["pdbx_label_comp_id"] = parent["label_comp_id"].value();
}
if (not to_be_deleted.empty())
@@ -1130,7 +1131,7 @@ void createPdbxPolySeqScheme(datablock &db)
for (auto col : { "label_asym_id", "label_entity_id", "label_seq_id", "label_comp_id", "auth_seq_id", "auth_comp_id", "pdbx_PDB_ins_code"})
atom_site.add_item(col);
cql::connection conn(db);
/* cql::connection conn(db);
cql::transaction tx(conn);
for (auto &&[asym_id, entity_id, seq_id, comp_id, auth_seq_id, auth_comp_id, pdb_ins_code] :
tx.stream<std::string, std::string, std::optional<int>, std::string, std::string, std::string, std::optional<std::string>>(
@@ -1165,7 +1166,7 @@ void createPdbxPolySeqScheme(datablock &db)
last_asym_id = asym_id;
last_seq_id = seq_id;
}
*/
// // select distinct A.entity_id, A.id, B.mon_id, B.num, B.hetero, C.auth_seq_id, C.auth_comp_id, C.pdbx_PDB_ins_code from struct_asym A, entity_poly_seq B, atom_site C where A.entity_id = B.entity_id and C.label_asym_id = A.id and C.label_seq_id = B.num order by A.entity_id, B.num;
// // select distinct label_entity_id, label_asym_id, label_comp_id, label_seq_id, auth_asym_id, auth_seq_id, auth_comp_id from atom_site order by CAST(label_entity_id AS INT), label_asym_id, CAST(label_seq_id AS INT);
@@ -1658,14 +1659,13 @@ bool reconstruct_pdbx(file &file, const validator &validator)
for (auto row : cat)
{
std::error_code ec;
std::string_view value = row[ix].text();
if (not iv->validate_value(value, ec))
if (not iv->validate_value(row[ix].value(), ec))
{
if (cif::VERBOSE > 0)
std::clog << "Replacing value (" << std::quoted(value) << ") for item " << item_name << " in category " << cat.name() << " since it does not validate\n";
std::clog << "Replacing value (" << std::quoted(row[ix].str()) << ") for item " << item_name << " in category " << cat.name() << " since it does not validate\n";
row[ix] = "?";
row[ix] = item_value{ cif::item_value_type::INAPPLICABLE };
}
}
}

View File

@@ -55,7 +55,7 @@ condition get_parents_condition(const validator &validator, row_handle rh, const
if (childValue.empty())
continue;
cond = std::move(cond) and key(link->m_parent_keys[ix]) == childValue.text();
cond = std::move(cond) and key(link->m_parent_keys[ix]) == childValue.value();
}
result = std::move(result) or std::move(cond);
@@ -145,7 +145,7 @@ bool is_valid_pdbx_file(const file &file, const validator &validator, std::error
if (p.size() != 1)
{
if (VERBOSE > 0)
std::clog << "In atom_site record: " << r["id"].text() << '\n';
std::clog << "In atom_site record: " << r["id"].str() << '\n';
throw std::runtime_error("For each monomer in atom_site there should be exactly one pdbx_poly_seq_scheme record");
}
}

View File

@@ -40,37 +40,25 @@
namespace cif
{
item_value s_null_item;
// item_value &row_handle::operator[](uint16_t item_ix)
// {
// return empty() or item_ix >= m_row->size() ? s_null_item : m_row->operator[](item_ix);
// }
item_value &row_handle::operator[](uint16_t item_ix)
{
return empty() or item_ix >= m_row->size() ? s_null_item : m_row->operator[](item_ix);
}
// const item_value &row_handle::operator[](uint16_t item_ix) const
// {
// return empty() or item_ix >= m_row->size() ? s_null_item : m_row->operator[](item_ix);
// }
const item_value &row_handle::operator[](uint16_t item_ix) const
{
return empty() or item_ix >= m_row->size() ? s_null_item : m_row->operator[](item_ix);
}
// item_value &row_handle::operator[](std::string_view item_name)
// {
// return operator[](get_item_ix(item_name));
// }
item_value &row_handle::operator[](std::string_view item_name)
{
return operator[](get_item_ix(item_name));
}
const item_value &row_handle::operator[](std::string_view item_name) const
{
return operator[](get_item_ix(item_name));
}
// --------------------------------------------------------------------
void row_handle::assign(uint16_t item, item_value value, bool updateLinked, bool validate)
{
if (not m_category)
throw std::runtime_error("uninitialized row");
m_category->update_value(m_row, item, std::move(value), updateLinked, validate);
}
// const item_value &row_handle::operator[](std::string_view item_name) const
// {
// return operator[](get_item_ix(item_name));
// }
uint16_t row_handle::get_item_ix(std::string_view name) const
{
@@ -88,6 +76,32 @@ std::string_view row_handle::get_item_name(uint16_t ix) const
return m_category->get_item_name(ix);
}
uint16_t const_row_handle::get_item_ix(std::string_view name) const
{
if (not m_category)
throw std::runtime_error("uninitialized row");
return m_category->get_item_ix(name);
}
std::string_view const_row_handle::get_item_name(uint16_t ix) const
{
if (not m_category)
throw std::runtime_error("uninitialized row");
return m_category->get_item_name(ix);
}
// --------------------------------------------------------------------
void row_handle::assign(uint16_t item, item_value value, bool updateLinked, bool validate)
{
if (not m_category)
throw std::runtime_error("uninitialized row");
m_category->update_value(m_row, item, std::move(value), updateLinked, validate);
}
uint16_t row_handle::add_item(std::string_view name)
{
if (not m_category)
@@ -96,24 +110,16 @@ uint16_t row_handle::add_item(std::string_view name)
return m_category->add_item(name);
}
void row_handle::swap(uint16_t item, row_handle &b)
{
if (not m_category)
throw std::runtime_error("uninitialized row");
m_category->swap_item(item, *this, b);
}
// --------------------------------------------------------------------
row_initializer::row_initializer(row_handle rh)
row_initializer::row_initializer(const_row_handle rh)
{
if (not rh.m_category)
throw std::runtime_error("uninitialized row");
assert(rh.m_row);
row *r = rh.get_row();
auto r = rh.get_row();
auto &cat = *rh.m_category;
for (uint16_t ix = 0; ix < r->size(); ++ix)

View File

@@ -257,22 +257,22 @@ int type_validator::compare(std::string_view a, std::string_view b) const
// --------------------------------------------------------------------
void item_validator::operator()(std::string_view value) const
void item_validator::validate_value(const item_value &value) const
{
std::error_code ec;
if (not validate_value(value, ec))
throw std::system_error(ec, std::format("'{}' is not a valid value for {}", value, m_item_name));
throw std::system_error(ec, std::format("'{}' is not a valid value for {}", value.str(), m_item_name));
}
bool item_validator::validate_value(std::string_view value, std::error_code &ec) const noexcept
bool item_validator::validate_value(const item_value &value, std::error_code &ec) const noexcept
{
ec.clear();
if (not value.empty() and value != "?" and value != ".")
if (not value.empty())
{
if (m_type != nullptr and not m_type->m_rx->match(value))
if (m_type != nullptr and not m_type->m_rx->match(value.str()))
ec = make_error_code(validation_error::value_does_not_match_rx);
else if (not m_enums.empty() and m_enums.count(std::string{ value }) == 0)
else if (not m_enums.empty() and m_enums.count(value.str()) == 0)
ec = make_error_code(validation_error::value_is_not_in_enumeration_list);
}

File diff suppressed because it is too large Load Diff