Commit 93375a50 by Maarten L. Hekkelman Committed by GitHub

Develop (#54)

* - renamed exists to contains
- fix compare for ints where item is empty

* - checking and optionally dropping ndb_poly_seq_scheme
- fix in iterator_proxy

* formatting data in reconstruction

* Version bump

* Attempt to get code compiling on macOS

* attempt 2 to build on macOS

* Added remove column

* Added rename_column
Added item_alias
Rename columns in reconstruct

* macOS...

* Fixed serious bug in emplace of both datablock and file.

* renaming field and column to item

* replace tag with item or item_name

* Fix validate pdbx

* version bump

* atom_site_anisotrop check

* - changed compound::is_known_peptide/is_know_base
- Add audit_conform only if file is really valid
- Added reconstruction code for PDBx

* pdb2cif work

* gcc diagnostics and clipper

* Fixing pdb2cif, and sequence checking

* work around bug in old gcc

* fix reconstruct sequence

* formatting

* some small optimisations

* Fix url in compound message

* Fix operator= for item_handle

* Fix operator= for item_handle

* new update_value in category

* test builds faster now

* Use Catch2 version 3 if installed

* catch22
parent 22537c0e
...@@ -11,4 +11,5 @@ Testing/ ...@@ -11,4 +11,5 @@ Testing/
include/cif++/exports.hpp include/cif++/exports.hpp
docs/api docs/api
docs/conf.py docs/conf.py
build_ci/ build_ci/
\ No newline at end of file data/components.cif
...@@ -27,7 +27,7 @@ cmake_minimum_required(VERSION 3.16) ...@@ -27,7 +27,7 @@ cmake_minimum_required(VERSION 3.16)
# set the project name # set the project name
project( project(
libcifpp libcifpp
VERSION 6.1.0 VERSION 7.0.0
LANGUAGES CXX) LANGUAGES CXX)
list(PREPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake") list(PREPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
...@@ -546,55 +546,7 @@ if(NOT PROJECT_IS_TOP_LEVEL) ...@@ -546,55 +546,7 @@ if(NOT PROJECT_IS_TOP_LEVEL)
endif() endif()
if(BUILD_TESTING) if(BUILD_TESTING)
# We're using the older version 2 of Catch2 add_subdirectory(test)
FetchContent_Declare(
Catch2
GIT_REPOSITORY https://github.com/catchorg/Catch2.git
GIT_TAG v2.13.9)
FetchContent_MakeAvailable(Catch2)
list(
APPEND
CIFPP_tests
unit-v2
unit-3d
format
model
rename-compound
sugar
spinner
validate-pdbx)
foreach(CIFPP_TEST IN LISTS CIFPP_tests)
set(CIFPP_TEST "${CIFPP_TEST}-test")
set(CIFPP_TEST_SOURCE "${CMAKE_CURRENT_SOURCE_DIR}/test/${CIFPP_TEST}.cpp")
add_executable(
${CIFPP_TEST} ${CIFPP_TEST_SOURCE}
"${CMAKE_CURRENT_SOURCE_DIR}/test/test-main.cpp")
target_link_libraries(${CIFPP_TEST} PRIVATE Threads::Threads cifpp::cifpp
Catch2::Catch2)
target_include_directories(${CIFPP_TEST} PRIVATE "${EIGEN_INCLUDE_DIR}")
if(MSVC)
# Specify unwind semantics so that MSVC knowns how to handle exceptions
target_compile_options(${CIFPP_TEST} PRIVATE /EHsc)
endif()
add_custom_target(
"run-${CIFPP_TEST}"
DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/Run${CIFPP_TEST}.touch ${CIFPP_TEST})
add_custom_command(
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/Run${CIFPP_TEST}.touch
COMMAND $<TARGET_FILE:${CIFPP_TEST}> --data-dir
${CMAKE_CURRENT_SOURCE_DIR}/test)
add_test(NAME ${CIFPP_TEST} COMMAND $<TARGET_FILE:${CIFPP_TEST}> --data-dir
${CMAKE_CURRENT_SOURCE_DIR}/test)
endforeach()
endif() endif()
# Optionally install the update scripts for CCD and dictionary files # Optionally install the update scripts for CCD and dictionary files
......
Version 7.0.0
- Renaming many methods and parameters to be more
consistent with the mmCIF dictionaries.
(Most notably, item used to be called column or
tag sometimes).
- validation_error is now a std::system_error error
value. The exception is gone.
- Added repairSequenceInfo to repair invalid files
Version 6.1.0 Version 6.1.0
- Add formula weight to entity in pdb2cif - Add formula weight to entity in pdb2cif
- Change order of categories inside a datablock to match order in file - Change order of categories inside a datablock to match order in file
......
...@@ -166,6 +166,12 @@ class compound ...@@ -166,6 +166,12 @@ class compound
return m_id == "HOH" or m_id == "H2O" or m_id == "WAT"; return m_id == "HOH" or m_id == "H2O" or m_id == "WAT";
} }
/** \brief Return whether this compound has a type of either 'peptide linking' or 'L-peptide linking' */
bool is_peptide() const;
/** \brief Return whether this compound has a type of either 'DNA linking' or 'RNA linking' */
bool is_base() const;
char one_letter_code() const { return m_one_letter_code; }; ///< Return the one letter code to use in a canonical sequence. If unknown the value '\0' is returned char one_letter_code() const { return m_one_letter_code; }; ///< Return the one letter code to use in a canonical sequence. If unknown the value '\0' is returned
std::string parent_id() const { return m_parent_id; }; ///< Return the parent id code in case a parent is specified (e.g. MET for MSE) std::string parent_id() const { return m_parent_id; }; ///< Return the parent id code in case a parent is specified (e.g. MET for MSE)
...@@ -237,25 +243,53 @@ class compound_factory ...@@ -237,25 +243,53 @@ class compound_factory
void pop_dictionary(); void pop_dictionary();
/// Return whether @a res_name is a valid and known peptide /// Return whether @a res_name is a valid and known peptide
[[deprecated("use is_peptide or is_std_peptide instead)")]]
bool is_known_peptide(const std::string &res_name) const; bool is_known_peptide(const std::string &res_name) const;
/// Return whether @a res_name is a valid and known base /// Return whether @a res_name is a valid and known base
[[deprecated("use is_base or is_std_base instead)")]]
bool is_known_base(const std::string &res_name) const; bool is_known_base(const std::string &res_name) const;
/// Return whether @a res_name is a peptide
bool is_peptide(std::string_view res_name) const;
/// Return whether @a res_name is a base
bool is_base(std::string_view res_name) const;
/// Return whether @a res_name is one of the standard peptides
bool is_std_peptide(std::string_view res_name) const;
/// Return whether @a res_name is one of the standard bases
bool is_std_base(std::string_view res_name) const;
/// Return whether @a res_name is a monomer (either base or peptide)
bool is_monomer(std::string_view res_name) const;
/// Return whether @a res_name is one of the standard bases or peptides
bool is_std_monomer(std::string_view res_name) const
{
return is_std_base(res_name) or is_std_peptide(res_name);
}
bool is_water(std::string_view res_name) const
{
return res_name == "HOH" or res_name == "H2O" or res_name == "WAT";
}
/// \brief Create the compound object for \a id /// \brief Create the compound object for \a id
/// ///
/// This will create the compound instance for \a id if it doesn't exist already. /// This will create the compound instance for \a id if it doesn't exist already.
/// The result is owned by this factory and should not be deleted by the user. /// The result is owned by this factory and should not be deleted by the user.
/// \param id The compound ID, a three letter code usually /// \param id The compound ID, a three letter code usually
/// \result The compound, or nullptr if it could not be created (missing info) /// \result The compound, or nullptr if it could not be created (missing info)
const compound *create(std::string id); const compound *create(std::string_view id);
~compound_factory(); ~compound_factory();
CIFPP_EXPORT static const std::map<std::string, char> kAAMap, ///< Globally accessible static list of the default amino acids CIFPP_EXPORT static const std::map<std::string, char> kAAMap, ///< Globally accessible static list of the default amino acids
kBaseMap; ///< Globally accessible static list of the default bases kBaseMap; ///< Globally accessible static list of the default bases
void report_missing_compound(const std::string &compound_id); void report_missing_compound(std::string_view compound_id);
private: private:
compound_factory(); compound_factory();
......
...@@ -107,6 +107,15 @@ class datablock : public std::list<category> ...@@ -107,6 +107,15 @@ class datablock : public std::list<category>
bool is_valid() const; bool is_valid() const;
/** /**
* @brief Validates the content of this datablock and all its content
* and updates or removes the audit_conform category to match the result.
*
* @return true If the content is valid
* @return false If the content is not valid
*/
bool is_valid();
/**
* @brief Validates all contained data for valid links between parents and children * @brief Validates all contained data for valid links between parents and children
* as defined in the validator * as defined in the validator
* *
...@@ -169,7 +178,16 @@ class datablock : public std::list<category> ...@@ -169,7 +178,16 @@ class datablock : public std::list<category>
/** /**
* @brief Get the preferred order of the categories when writing them * @brief Get the preferred order of the categories when writing them
*/ */
std::vector<std::string> get_tag_order() const; [[deprecated("use get_item_order instead")]]
std::vector<std::string> get_tag_order() const
{
return get_item_order();
}
/**
* @brief Get the preferred order of the categories when writing them
*/
std::vector<std::string> get_item_order() const;
/** /**
* @brief Write out the contents to @a os * @brief Write out the contents to @a os
...@@ -177,9 +195,9 @@ class datablock : public std::list<category> ...@@ -177,9 +195,9 @@ class datablock : public std::list<category>
void write(std::ostream &os) const; void write(std::ostream &os) const;
/** /**
* @brief Write out the contents to @a os using the order defined in @a tag_order * @brief Write out the contents to @a os using the order defined in @a item_name_order
*/ */
void write(std::ostream &os, const std::vector<std::string> &tag_order); void write(std::ostream &os, const std::vector<std::string> &item_name_order);
/** /**
* @brief Friend operator<< to write datablock @a db to std::ostream @a os * @brief Friend operator<< to write datablock @a db to std::ostream @a os
......
...@@ -44,7 +44,7 @@ ...@@ -44,7 +44,7 @@
/** \file item.hpp /** \file item.hpp
* *
* This file contains the declaration of item but also the item_value and item_handle * This file contains the declaration of item but also the item_value and item_handle
* These handle the storage of and access to the data for a single data field. * These handle the storage of and access to the data for a single data item.
*/ */
namespace cif namespace cif
...@@ -120,8 +120,6 @@ class item ...@@ -120,8 +120,6 @@ class item
if (r.ec != std::errc()) if (r.ec != std::errc())
throw std::runtime_error("Could not format number"); throw std::runtime_error("Could not format number");
assert(r.ptr >= buffer and r.ptr < buffer + sizeof(buffer));
*r.ptr = 0;
m_value.assign(buffer, r.ptr - buffer); m_value.assign(buffer, r.ptr - buffer);
} }
...@@ -141,8 +139,6 @@ class item ...@@ -141,8 +139,6 @@ class item
if (r.ec != std::errc()) if (r.ec != std::errc())
throw std::runtime_error("Could not format number"); throw std::runtime_error("Could not format number");
assert(r.ptr >= buffer and r.ptr < buffer + sizeof(buffer));
*r.ptr = 0;
m_value.assign(buffer, r.ptr - buffer); m_value.assign(buffer, r.ptr - buffer);
} }
...@@ -158,8 +154,6 @@ class item ...@@ -158,8 +154,6 @@ class item
if (r.ec != std::errc()) if (r.ec != std::errc())
throw std::runtime_error("Could not format number"); throw std::runtime_error("Could not format number");
assert(r.ptr >= buffer and r.ptr < buffer + sizeof(buffer));
*r.ptr = 0;
m_value.assign(buffer, r.ptr - buffer); m_value.assign(buffer, r.ptr - buffer);
} }
...@@ -174,13 +168,22 @@ class item ...@@ -174,13 +168,22 @@ class item
/// \brief constructor for an item with name \a name and as /// \brief constructor for an item with name \a name and as
/// content value \a value /// content value \a value
item(const std::string_view name, const std::string_view value) item(const std::string_view name, std::string_view value)
: m_name(name) : m_name(name)
, m_value(value) , m_value(value)
{ {
} }
/// \brief constructor for an item with name \a name and as /// \brief constructor for an item with name \a name and as
/// content value \a value
template<typename T, std::enable_if_t<std::is_same_v<T, std::string>, int> = 0>
item(const std::string_view name, T &&value)
: m_name(name)
, m_value(std::move(value))
{
}
/// \brief constructor for an item with name \a name and as
/// content the optional value \a value /// content the optional value \a value
template <typename T> template <typename T>
item(const std::string_view name, const std::optional<T> &value) item(const std::string_view name, const std::optional<T> &value)
...@@ -219,7 +222,8 @@ class item ...@@ -219,7 +222,8 @@ class item
/** @endcond */ /** @endcond */
std::string_view name() const { return m_name; } ///< Return the name of the item std::string_view name() const { return m_name; } ///< Return the name of the item
std::string_view value() const { return m_value; } ///< Return the value of the item std::string_view value() const & { return m_value; } ///< Return the value of the item
std::string value() const && { return std::move(m_value); } ///< Return the value of the item
/// \brief replace the content of the stored value with \a v /// \brief replace the content of the stored value with \a v
void value(std::string_view v) { m_value = v; } void value(std::string_view v) { m_value = v; }
...@@ -227,10 +231,10 @@ class item ...@@ -227,10 +231,10 @@ class item
/// \brief empty means either null or unknown /// \brief empty means either null or unknown
bool empty() const { return m_value.empty(); } bool empty() const { return m_value.empty(); }
/// \brief returns true if the field contains '.' /// \brief returns true if the item contains '.'
bool is_null() const { return m_value == "."; } bool is_null() const { return m_value == "."; }
/// \brief returns true if the field contains '?' /// \brief returns true if the item contains '?'
bool is_unknown() const { return m_value == "?"; } bool is_unknown() const { return m_value == "?"; }
/// \brief the length of the value string /// \brief the length of the value string
...@@ -363,8 +367,35 @@ struct item_handle ...@@ -363,8 +367,35 @@ struct item_handle
template <typename T> template <typename T>
item_handle &operator=(const T &value) item_handle &operator=(const T &value)
{ {
item v{ "", value }; assign_value(item{ "", value }.value());
assign_value(v); return *this;
}
/**
* @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)
{
assign_value(item{ "", std::move(value) }.value());
return *this;
}
/**
* @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 <size_t N>
item_handle &operator=(const char (&value)[N])
{
assign_value(item{ "", std::move(value) }.value());
return *this; return *this;
} }
...@@ -464,14 +495,14 @@ struct item_handle ...@@ -464,14 +495,14 @@ struct item_handle
/** Easy way to test for an empty item */ /** Easy way to test for an empty item */
explicit operator bool() const { return not empty(); } explicit operator bool() const { return not empty(); }
/// is_null return true if the field contains '.' /// is_null return true if the item contains '.'
bool is_null() const bool is_null() const
{ {
auto txt = text(); auto txt = text();
return txt.length() == 1 and txt.front() == '.'; return txt.length() == 1 and txt.front() == '.';
} }
/// is_unknown returns true if the field contains '?' /// is_unknown returns true if the item contains '?'
bool is_unknown() const bool is_unknown() const
{ {
auto txt = text(); auto txt = text();
...@@ -484,11 +515,11 @@ struct item_handle ...@@ -484,11 +515,11 @@ struct item_handle
/** /**
* @brief Construct a new item handle object * @brief Construct a new item handle object
* *
* @param column Column index * @param item Item index
* @param row Reference to the row * @param row Reference to the row
*/ */
item_handle(uint16_t column, row_handle &row) item_handle(uint16_t item, row_handle &row)
: m_column(column) : m_item_ix(item)
, m_row_handle(row) , m_row_handle(row)
{ {
} }
...@@ -505,10 +536,10 @@ struct item_handle ...@@ -505,10 +536,10 @@ struct item_handle
private: private:
item_handle(); item_handle();
uint16_t m_column; uint16_t m_item_ix;
row_handle &m_row_handle; row_handle &m_row_handle;
void assign_value(const item &value); void assign_value(std::string_view value);
}; };
// So sad that older gcc implementations of from_chars did not support floats yet... // So sad that older gcc implementations of from_chars did not support floats yet...
...@@ -556,7 +587,7 @@ struct item_handle::item_value_as<T, std::enable_if_t<std::is_arithmetic_v<T> an ...@@ -556,7 +587,7 @@ struct item_handle::item_value_as<T, std::enable_if_t<std::is_arithmetic_v<T> an
auto txt = ref.text(); auto txt = ref.text();
if (txt.empty()) if (ref.empty())
result = 1; result = 1;
else else
{ {
......
...@@ -90,7 +90,7 @@ class iterator_impl ...@@ -90,7 +90,7 @@ class iterator_impl
: m_category(rhs.m_category) : m_category(rhs.m_category)
, m_current(rhs.m_current) , m_current(rhs.m_current)
, m_value(rhs.m_value) , m_value(rhs.m_value)
, m_column_ix(rhs.m_column_ix) , m_item_ix(rhs.m_item_ix)
{ {
} }
...@@ -99,7 +99,7 @@ class iterator_impl ...@@ -99,7 +99,7 @@ class iterator_impl
: m_category(rhs.m_category) : m_category(rhs.m_category)
, m_current(const_cast<row_type *>(rhs.m_current)) , m_current(const_cast<row_type *>(rhs.m_current))
, m_value(rhs.m_value) , m_value(rhs.m_value)
, m_column_ix(rhs.m_column_ix) , m_item_ix(rhs.m_item_ix)
{ {
m_value = get(std::make_index_sequence<N>()); m_value = get(std::make_index_sequence<N>());
} }
...@@ -108,7 +108,7 @@ class iterator_impl ...@@ -108,7 +108,7 @@ class iterator_impl
iterator_impl(const iterator_impl<IRowType> &rhs, const std::array<uint16_t, N> &cix) iterator_impl(const iterator_impl<IRowType> &rhs, const std::array<uint16_t, N> &cix)
: m_category(rhs.m_category) : m_category(rhs.m_category)
, m_current(rhs.m_current) , m_current(rhs.m_current)
, m_column_ix(cix) , m_item_ix(cix)
{ {
m_value = get(std::make_index_sequence<N>()); m_value = get(std::make_index_sequence<N>());
} }
...@@ -117,7 +117,7 @@ class iterator_impl ...@@ -117,7 +117,7 @@ class iterator_impl
{ {
m_category = i.m_category; m_category = i.m_category;
m_current = i.m_current; m_current = i.m_current;
m_column_ix = i.m_column_ix; m_item_ix = i.m_item_ix;
m_value = i.m_value; m_value = i.m_value;
return *this; return *this;
} }
...@@ -185,7 +185,7 @@ class iterator_impl ...@@ -185,7 +185,7 @@ class iterator_impl
if (m_current != nullptr) if (m_current != nullptr)
{ {
row_handle rh{ *m_category, *m_current }; row_handle rh{ *m_category, *m_current };
return tuple_type{ rh[m_column_ix[Is]].template as<Ts>()... }; return tuple_type{ rh[m_item_ix[Is]].template as<Ts>()... };
} }
return {}; return {};
...@@ -194,7 +194,7 @@ class iterator_impl ...@@ -194,7 +194,7 @@ class iterator_impl
category_type *m_category = nullptr; category_type *m_category = nullptr;
row_type *m_current = nullptr; row_type *m_current = nullptr;
value_type m_value; value_type m_value;
std::array<uint16_t, N> m_column_ix; std::array<uint16_t, N> m_item_ix;
}; };
/** /**
...@@ -348,7 +348,7 @@ class iterator_impl<Category, T> ...@@ -348,7 +348,7 @@ class iterator_impl<Category, T>
: m_category(rhs.m_category) : m_category(rhs.m_category)
, m_current(rhs.m_current) , m_current(rhs.m_current)
, m_value(rhs.m_value) , m_value(rhs.m_value)
, m_column_ix(rhs.m_column_ix) , m_item_ix(rhs.m_item_ix)
{ {
} }
...@@ -357,7 +357,7 @@ class iterator_impl<Category, T> ...@@ -357,7 +357,7 @@ class iterator_impl<Category, T>
: m_category(rhs.m_category) : m_category(rhs.m_category)
, m_current(const_cast<row_type *>(rhs.m_current)) , m_current(const_cast<row_type *>(rhs.m_current))
, m_value(rhs.m_value) , m_value(rhs.m_value)
, m_column_ix(rhs.m_column_ix) , m_item_ix(rhs.m_item_ix)
{ {
m_value = get(m_current); m_value = get(m_current);
} }
...@@ -366,7 +366,7 @@ class iterator_impl<Category, T> ...@@ -366,7 +366,7 @@ class iterator_impl<Category, T>
iterator_impl(const iterator_impl<IRowType> &rhs, const std::array<uint16_t, 1> &cix) iterator_impl(const iterator_impl<IRowType> &rhs, const std::array<uint16_t, 1> &cix)
: m_category(rhs.m_category) : m_category(rhs.m_category)
, m_current(rhs.m_current) , m_current(rhs.m_current)
, m_column_ix(cix[0]) , m_item_ix(cix[0])
{ {
m_value = get(); m_value = get();
} }
...@@ -375,7 +375,7 @@ class iterator_impl<Category, T> ...@@ -375,7 +375,7 @@ class iterator_impl<Category, T>
{ {
m_category = i.m_category; m_category = i.m_category;
m_current = i.m_current; m_current = i.m_current;
m_column_ix = i.m_column_ix; m_item_ix = i.m_item_ix;
m_value = i.m_value; m_value = i.m_value;
return *this; return *this;
} }
...@@ -442,7 +442,7 @@ class iterator_impl<Category, T> ...@@ -442,7 +442,7 @@ class iterator_impl<Category, T>
if (m_current != nullptr) if (m_current != nullptr)
{ {
row_handle rh{ *m_category, *m_current }; row_handle rh{ *m_category, *m_current };
return rh[m_column_ix].template as<T>(); return rh[m_item_ix].template as<T>();
} }
return {}; return {};
...@@ -451,7 +451,7 @@ class iterator_impl<Category, T> ...@@ -451,7 +451,7 @@ class iterator_impl<Category, T>
category_type *m_category = nullptr; category_type *m_category = nullptr;
row_type *m_current = nullptr; row_type *m_current = nullptr;
value_type m_value; value_type m_value;
uint16_t m_column_ix; uint16_t m_item_ix;
}; };
// -------------------------------------------------------------------- // --------------------------------------------------------------------
...@@ -482,8 +482,8 @@ class iterator_proxy ...@@ -482,8 +482,8 @@ class iterator_proxy
using iterator = iterator_impl<category_type, Ts...>; using iterator = iterator_impl<category_type, Ts...>;
using row_iterator = iterator_impl<category_type>; using row_iterator = iterator_impl<category_type>;
iterator_proxy(category_type &cat, row_iterator pos, char const *const columns[N]); iterator_proxy(category_type &cat, row_iterator pos, char const *const items[N]);
iterator_proxy(category_type &cat, row_iterator pos, std::initializer_list<char const *> columns); iterator_proxy(category_type &cat, row_iterator pos, std::initializer_list<char const *> items);
iterator_proxy(iterator_proxy &&p); iterator_proxy(iterator_proxy &&p);
iterator_proxy &operator=(iterator_proxy &&p); iterator_proxy &operator=(iterator_proxy &&p);
...@@ -492,8 +492,8 @@ class iterator_proxy ...@@ -492,8 +492,8 @@ class iterator_proxy
iterator_proxy &operator=(const iterator_proxy &) = delete; iterator_proxy &operator=(const iterator_proxy &) = delete;
/** @endcond */ /** @endcond */
iterator begin() const { return iterator(m_begin, m_column_ix); } ///< Return the iterator pointing to the first row iterator begin() const { return iterator(m_begin, m_item_ix); } ///< Return the iterator pointing to the first row
iterator end() const { return iterator(m_end, m_column_ix); } ///< Return the iterator pointing past the last row iterator end() const { return iterator(m_end, m_item_ix); } ///< Return the iterator pointing past the last row
bool empty() const { return m_begin == m_end; } ///< Return true if the range is empty bool empty() const { return m_begin == m_end; } ///< Return true if the range is empty
explicit operator bool() const { return not empty(); } ///< Easy way to detect if the range is empty explicit operator bool() const { return not empty(); } ///< Easy way to detect if the range is empty
...@@ -510,13 +510,13 @@ class iterator_proxy ...@@ -510,13 +510,13 @@ class iterator_proxy
std::swap(m_category, rhs.m_category); std::swap(m_category, rhs.m_category);
std::swap(m_begin, rhs.m_begin); std::swap(m_begin, rhs.m_begin);
std::swap(m_end, rhs.m_end); std::swap(m_end, rhs.m_end);
std::swap(m_column_ix, rhs.m_column_ix); std::swap(m_item_ix, rhs.m_item_ix);
} }
private: private:
category_type *m_category; category_type *m_category;
row_iterator m_begin, m_end; row_iterator m_begin, m_end;
std::array<uint16_t, N> m_column_ix; std::array<uint16_t, N> m_item_ix;
}; };
// -------------------------------------------------------------------- // --------------------------------------------------------------------
...@@ -562,22 +562,23 @@ class conditional_iterator_proxy ...@@ -562,22 +562,23 @@ class conditional_iterator_proxy
reference operator*() reference operator*()
{ {
return *mBegin; return *m_begin;
} }
pointer operator->() pointer operator->()
{ {
return &*mBegin; m_current = *m_begin;
return &m_current;
} }
conditional_iterator_impl &operator++() conditional_iterator_impl &operator++()
{ {
while (mBegin != mEnd) while (m_begin != m_end)
{ {
if (++mBegin == mEnd) if (++m_begin == m_end)
break; break;
if (m_condition->operator()(mBegin)) if (m_condition->operator()(m_begin))
break; break;
} }
...@@ -591,18 +592,22 @@ class conditional_iterator_proxy ...@@ -591,18 +592,22 @@ class conditional_iterator_proxy
return result; return result;
} }
bool operator==(const conditional_iterator_impl &rhs) const { return mBegin == rhs.mBegin; } bool operator==(const conditional_iterator_impl &rhs) const { return m_begin == rhs.m_begin; }
bool operator!=(const conditional_iterator_impl &rhs) const { return mBegin != rhs.mBegin; } bool operator!=(const conditional_iterator_impl &rhs) const { return m_begin != rhs.m_begin; }
bool operator==(const row_iterator &rhs) const { return m_begin == rhs; }
bool operator!=(const row_iterator &rhs) const { return m_begin != rhs; }
template <typename IRowType, typename... ITs> template <typename IRowType, typename... ITs>
bool operator==(const iterator_impl<IRowType, ITs...> &rhs) const { return mBegin == rhs; } bool operator==(const iterator_impl<IRowType, ITs...> &rhs) const { return m_begin == rhs; }
template <typename IRowType, typename... ITs> template <typename IRowType, typename... ITs>
bool operator!=(const iterator_impl<IRowType, ITs...> &rhs) const { return mBegin != rhs; } bool operator!=(const iterator_impl<IRowType, ITs...> &rhs) const { return m_begin != rhs; }
private: private:
CategoryType *mCat; CategoryType *m_cat;
base_iterator mBegin, mEnd; base_iterator m_begin, m_end;
value_type m_current;
const condition *m_condition; const condition *m_condition;
}; };
...@@ -646,26 +651,26 @@ class conditional_iterator_proxy ...@@ -646,26 +651,26 @@ class conditional_iterator_proxy
/** @cond */ /** @cond */
template <typename Category, typename... Ts> template <typename Category, typename... Ts>
iterator_proxy<Category, Ts...>::iterator_proxy(Category &cat, row_iterator pos, char const *const columns[N]) iterator_proxy<Category, Ts...>::iterator_proxy(Category &cat, row_iterator pos, char const *const items[N])
: m_category(&cat) : m_category(&cat)
, m_begin(pos) , m_begin(pos)
, m_end(cat.end()) , m_end(cat.end())
{ {
for (uint16_t i = 0; i < N; ++i) for (uint16_t i = 0; i < N; ++i)
m_column_ix[i] = m_category->get_column_ix(columns[i]); m_item_ix[i] = m_category->get_item_ix(items[i]);
} }
template <typename Category, typename... Ts> template <typename Category, typename... Ts>
iterator_proxy<Category, Ts...>::iterator_proxy(Category &cat, row_iterator pos, std::initializer_list<char const *> columns) iterator_proxy<Category, Ts...>::iterator_proxy(Category &cat, row_iterator pos, std::initializer_list<char const *> items)
: m_category(&cat) : m_category(&cat)
, m_begin(pos) , m_begin(pos)
, m_end(cat.end()) , m_end(cat.end())
{ {
// static_assert(columns.size() == N, "The list of column names should be exactly the same as the list of requested columns"); // static_assert(items.size() == N, "The list of item names should be exactly the same as the list of requested items");
std::uint16_t i = 0; std::uint16_t i = 0;
for (auto column : columns) for (auto item : items)
m_column_ix[i++] = m_category->get_column_ix(column); m_item_ix[i++] = m_category->get_item_ix(item);
} }
// -------------------------------------------------------------------- // --------------------------------------------------------------------
...@@ -673,13 +678,13 @@ iterator_proxy<Category, Ts...>::iterator_proxy(Category &cat, row_iterator pos, ...@@ -673,13 +678,13 @@ iterator_proxy<Category, Ts...>::iterator_proxy(Category &cat, row_iterator pos,
template <typename Category, typename... Ts> template <typename Category, typename... Ts>
conditional_iterator_proxy<Category, Ts...>::conditional_iterator_impl::conditional_iterator_impl( conditional_iterator_proxy<Category, Ts...>::conditional_iterator_impl::conditional_iterator_impl(
Category &cat, row_iterator pos, const condition &cond, const std::array<uint16_t, N> &cix) Category &cat, row_iterator pos, const condition &cond, const std::array<uint16_t, N> &cix)
: mCat(&cat) : m_cat(&cat)
, mBegin(pos, cix) , m_begin(pos, cix)
, mEnd(cat.end(), cix) , m_end(cat.end(), cix)
, m_condition(&cond) , m_condition(&cond)
{ {
if (m_condition == nullptr or m_condition->empty()) if (m_condition == nullptr or m_condition->empty())
mBegin = mEnd; m_begin = m_end;
} }
template <typename Category, typename... Ts> template <typename Category, typename... Ts>
...@@ -702,7 +707,7 @@ conditional_iterator_proxy<Category, Ts...>::conditional_iterator_proxy(Category ...@@ -702,7 +707,7 @@ conditional_iterator_proxy<Category, Ts...>::conditional_iterator_proxy(Category
, mCBegin(pos) , mCBegin(pos)
, mCEnd(cat.end()) , mCEnd(cat.end())
{ {
static_assert(sizeof...(Ts) == sizeof...(Ns), "Number of column names should be equal to number of requested value types"); static_assert(sizeof...(Ts) == sizeof...(Ns), "Number of item names should be equal to number of requested value types");
if (m_condition) if (m_condition)
{ {
...@@ -715,7 +720,7 @@ conditional_iterator_proxy<Category, Ts...>::conditional_iterator_proxy(Category ...@@ -715,7 +720,7 @@ conditional_iterator_proxy<Category, Ts...>::conditional_iterator_proxy(Category
mCBegin = mCEnd; mCBegin = mCEnd;
uint16_t i = 0; uint16_t i = 0;
((mCix[i++] = m_cat->get_column_ix(names)), ...); ((mCix[i++] = m_cat->get_item_ix(names)), ...);
} }
template <typename Category, typename... Ts> template <typename Category, typename... Ts>
......
...@@ -72,7 +72,7 @@ class structure; ...@@ -72,7 +72,7 @@ class structure;
* *
* The class atom is a kind of flyweight class. It can be copied * The class atom is a kind of flyweight class. It can be copied
* with low overhead. All data is stored in the underlying mmCIF * with low overhead. All data is stored in the underlying mmCIF
* categories but some very often used fields are cached in the * categories but some very often used items are cached in the
* impl. * impl.
* *
* It is also possible to have symmetry copies of atoms. They * It is also possible to have symmetry copies of atoms. They
...@@ -207,7 +207,7 @@ class atom ...@@ -207,7 +207,7 @@ class atom
/// \brief Copy assignement operator /// \brief Copy assignement operator
atom &operator=(const atom &rhs) = default; atom &operator=(const atom &rhs) = default;
/// \brief Return the field named @a name in the _atom_site category for this atom /// \brief Return the item named @a name in the _atom_site category for this atom
std::string get_property(std::string_view name) const std::string get_property(std::string_view name) const
{ {
if (not m_impl) if (not m_impl)
...@@ -215,7 +215,7 @@ class atom ...@@ -215,7 +215,7 @@ class atom
return m_impl->get_property(name); return m_impl->get_property(name);
} }
/// \brief Return the field named @a name in the _atom_site category for this atom cast to an int /// \brief Return the item named @a name in the _atom_site category for this atom cast to an int
int get_property_int(std::string_view name) const int get_property_int(std::string_view name) const
{ {
if (not m_impl) if (not m_impl)
...@@ -223,7 +223,7 @@ class atom ...@@ -223,7 +223,7 @@ class atom
return m_impl->get_property_int(name); return m_impl->get_property_int(name);
} }
/// \brief Return the field named @a name in the _atom_site category for this atom cast to a float /// \brief Return the item named @a name in the _atom_site category for this atom cast to a float
float get_property_float(std::string_view name) const float get_property_float(std::string_view name) const
{ {
if (not m_impl) if (not m_impl)
...@@ -231,7 +231,7 @@ class atom ...@@ -231,7 +231,7 @@ class atom
return m_impl->get_property_float(name); return m_impl->get_property_float(name);
} }
/// \brief Set value for the field named @a name in the _atom_site category to @a value /// \brief Set value for the item named @a name in the _atom_site category to @a value
void set_property(const std::string_view name, const std::string &value) void set_property(const std::string_view name, const std::string &value)
{ {
if (not m_impl) if (not m_impl)
...@@ -239,7 +239,7 @@ class atom ...@@ -239,7 +239,7 @@ class atom
m_impl->set_property(name, value); m_impl->set_property(name, value);
} }
/// \brief Set value for the field named @a name in the _atom_site category to @a value /// \brief Set value for the item named @a name in the _atom_site category to @a value
template <typename T, std::enable_if_t<std::is_arithmetic_v<T>, int> = 0> template <typename T, std::enable_if_t<std::is_arithmetic_v<T>, int> = 0>
void set_property(const std::string_view name, const T &value) void set_property(const std::string_view name, const T &value)
{ {
...@@ -730,7 +730,7 @@ class sugar : public residue ...@@ -730,7 +730,7 @@ class sugar : public residue
/** /**
* @brief Return the sugar number in the glycosylation tree * @brief Return the sugar number in the glycosylation tree
* *
* To store the sugar number, the auth_seq_id field has been overloaded * To store the sugar number, the auth_seq_id item has been overloaded
* in the specification. But since a sugar number should be, ehm, a number * in the specification. But since a sugar number should be, ehm, a number
* and auth_seq_id is specified to contain a string, we do a check here * and auth_seq_id is specified to contain a string, we do a check here
* to see if it really is a number. * to see if it really is a number.
......
...@@ -143,9 +143,9 @@ class sac_parser ...@@ -143,9 +143,9 @@ class sac_parser
enum class CIFToken enum class CIFToken
{ {
Unknown, UNKNOWN,
Eof, END_OF_FILE,
DATA, DATA,
LOOP, LOOP,
...@@ -153,24 +153,24 @@ class sac_parser ...@@ -153,24 +153,24 @@ class sac_parser
SAVE_, SAVE_,
SAVE_NAME, SAVE_NAME,
STOP, STOP,
Tag, ITEM_NAME,
Value VALUE
}; };
static constexpr const char *get_token_name(CIFToken token) static constexpr const char *get_token_name(CIFToken token)
{ {
switch (token) switch (token)
{ {
case CIFToken::Unknown: return "Unknown"; case CIFToken::UNKNOWN: return "Unknown";
case CIFToken::Eof: return "Eof"; case CIFToken::END_OF_FILE: return "Eof";
case CIFToken::DATA: return "DATA"; case CIFToken::DATA: return "DATA";
case CIFToken::LOOP: return "LOOP"; case CIFToken::LOOP: return "LOOP";
case CIFToken::GLOBAL: return "GLOBAL"; case CIFToken::GLOBAL: return "GLOBAL";
case CIFToken::SAVE_: return "SAVE"; case CIFToken::SAVE_: return "SAVE";
case CIFToken::SAVE_NAME: return "SAVE+name"; case CIFToken::SAVE_NAME: return "SAVE+name";
case CIFToken::STOP: return "STOP"; case CIFToken::STOP: return "STOP";
case CIFToken::Tag: return "Tag"; case CIFToken::ITEM_NAME: return "Tag";
case CIFToken::Value: return "Value"; case CIFToken::VALUE: return "Value";
default: return "Invalid token parameter"; default: return "Invalid token parameter";
} }
} }
...@@ -267,9 +267,9 @@ class sac_parser ...@@ -267,9 +267,9 @@ class sac_parser
QuotedString, QuotedString,
QuotedStringQuote, QuotedStringQuote,
UnquotedString, UnquotedString,
Tag, ItemName,
TextField, TextItem,
TextFieldNL, TextItemNL,
Reserved, Reserved,
Value Value
}; };
......
...@@ -28,6 +28,8 @@ ...@@ -28,6 +28,8 @@
#include "cif++/file.hpp" #include "cif++/file.hpp"
#include <system_error>
/** /**
* @file pdb.hpp * @file pdb.hpp
* *
...@@ -107,9 +109,10 @@ inline void write(const std::filesystem::path &p, const file &f) ...@@ -107,9 +109,10 @@ inline void write(const std::filesystem::path &p, const file &f)
* *
* \param file The cif::file that hopefully contains some valid data * \param file The cif::file that hopefully contains some valid data
* \param dictionary The mmcif dictionary to use * \param dictionary The mmcif dictionary to use
* \result Returns true if the resulting file is valid
*/ */
void reconstruct_pdbx(file &pdbx_file, std::string_view dictionary = "mmcif_pdbx"); bool reconstruct_pdbx(file &pdbx_file, std::string_view dictionary = "mmcif_pdbx");
/** \brief This is an extension to cif::validator, use the logic in common /** \brief This is an extension to cif::validator, use the logic in common
* PDBx files to see if the file is internally consistent. * PDBx files to see if the file is internally consistent.
...@@ -119,6 +122,8 @@ void reconstruct_pdbx(file &pdbx_file, std::string_view dictionary = "mmcif_pdbx ...@@ -119,6 +122,8 @@ void reconstruct_pdbx(file &pdbx_file, std::string_view dictionary = "mmcif_pdbx
* atom_site -> pdbx_poly_seq_scheme -> entity_poly_seq -> entity_poly -> entity * atom_site -> pdbx_poly_seq_scheme -> entity_poly_seq -> entity_poly -> entity
* *
* Use the common \ref cif::VERBOSE flag to turn on diagnostic messages. * Use the common \ref cif::VERBOSE flag to turn on diagnostic messages.
*
* This function throws a std::system_error in case of an error
* *
* \param file The input file * \param file The input file
* \param dictionary The mmcif dictionary to use * \param dictionary The mmcif dictionary to use
...@@ -127,6 +132,43 @@ void reconstruct_pdbx(file &pdbx_file, std::string_view dictionary = "mmcif_pdbx ...@@ -127,6 +132,43 @@ void reconstruct_pdbx(file &pdbx_file, std::string_view dictionary = "mmcif_pdbx
bool is_valid_pdbx_file(const file &pdbx_file, std::string_view dictionary = "mmcif_pdbx"); bool is_valid_pdbx_file(const file &pdbx_file, std::string_view dictionary = "mmcif_pdbx");
/** \brief This is an extension to cif::validator, use the logic in common
* PDBx files to see if the file is internally consistent.
*
* This function for now checks if the following categories are consistent:
*
* atom_site -> pdbx_poly_seq_scheme -> entity_poly_seq -> entity_poly -> entity
*
* Use the common \ref cif::VERBOSE flag to turn on diagnostic messages.
*
* The dictionary is assumed to be specified in the file or to be the
* default mmcif_pdbx.dic dictionary.
*
* \param file The input file
* \param ec The error_code in case something was wrong
* \result Returns true if the file was valid and consistent
*/
bool is_valid_pdbx_file(const file &pdbx_file, std::error_code &ec);
/** \brief This is an extension to cif::validator, use the logic in common
* PDBx files to see if the file is internally consistent.
*
* This function for now checks if the following categories are consistent:
*
* atom_site -> pdbx_poly_seq_scheme -> entity_poly_seq -> entity_poly -> entity
*
* Use the common \ref cif::VERBOSE flag to turn on diagnostic messages.
*
* \param file The input file
* \param dictionary The dictionary to use
* \param ec The error_code in case something was wrong
* \result Returns true if the file was valid and consistent
*/
bool is_valid_pdbx_file(const file &pdbx_file, std::string_view dictionary,
std::error_code &ec);
// -------------------------------------------------------------------- // --------------------------------------------------------------------
// Other I/O related routines // Other I/O related routines
......
...@@ -35,7 +35,10 @@ ...@@ -35,7 +35,10 @@
#if __has_include(<clipper/core/coords.h>) #if __has_include(<clipper/core/coords.h>)
#define HAVE_LIBCLIPPER 1 #define HAVE_LIBCLIPPER 1
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wignored-qualifiers"
#include <clipper/core/coords.h> #include <clipper/core/coords.h>
#pragma GCC diagnostic pop
#endif #endif
/** \file point.hpp /** \file point.hpp
......
...@@ -51,7 +51,7 @@ ...@@ -51,7 +51,7 @@
* std::string name = rh["label_atom_id"].as<std::string>(); * std::string name = rh["label_atom_id"].as<std::string>();
* *
* // by index: * // by index:
* uint16_t ix = atom_site.get_column_ix("label_atom_id"); * uint16_t ix = atom_site.get_item_ix("label_atom_id");
* assert(rh[ix].as<std::string() == name); * assert(rh[ix].as<std::string() == name);
* @endcode * @endcode
* *
...@@ -87,15 +87,15 @@ namespace detail ...@@ -87,15 +87,15 @@ namespace detail
{ {
static constexpr size_t N = sizeof...(C); static constexpr size_t N = sizeof...(C);
get_row_result(const row_handle &r, std::array<uint16_t, N> &&columns) get_row_result(const row_handle &r, std::array<uint16_t, N> &&items)
: m_row(r) : m_row(r)
, m_columns(std::move(columns)) , m_items(std::move(items))
{ {
} }
const item_handle operator[](uint16_t ix) const const item_handle operator[](uint16_t ix) const
{ {
return m_row[m_columns[ix]]; return m_row[m_items[ix]];
} }
template <typename... Ts, std::enable_if_t<N == sizeof...(Ts), int> = 0> template <typename... Ts, std::enable_if_t<N == sizeof...(Ts), int> = 0>
...@@ -107,11 +107,11 @@ namespace detail ...@@ -107,11 +107,11 @@ namespace detail
template <typename... Ts, size_t... Is> template <typename... Ts, size_t... Is>
std::tuple<Ts...> get(std::index_sequence<Is...>) const std::tuple<Ts...> get(std::index_sequence<Is...>) const
{ {
return std::tuple<Ts...>{ m_row[m_columns[Is]].template as<Ts>()... }; return std::tuple<Ts...>{ m_row[m_items[Is]].template as<Ts>()... };
} }
const row_handle &m_row; const row_handle &m_row;
std::array<uint16_t, N> m_columns; std::array<uint16_t, N> m_items;
}; };
// we want to be able to tie some variables to a get_row_result, for this we use tiewraps // we want to be able to tie some variables to a get_row_result, for this we use tiewraps
...@@ -244,70 +244,70 @@ class row_handle ...@@ -244,70 +244,70 @@ class row_handle
return not empty(); return not empty();
} }
/// \brief return a cif::item_handle to the item in column @a column_ix /// \brief return a cif::item_handle to the item in item @a item_ix
item_handle operator[](uint16_t column_ix) item_handle operator[](uint16_t item_ix)
{ {
return empty() ? item_handle::s_null_item : item_handle(column_ix, *this); return empty() ? item_handle::s_null_item : item_handle(item_ix, *this);
} }
/// \brief return a const cif::item_handle to the item in column @a column_ix /// \brief return a const cif::item_handle to the item in item @a item_ix
const item_handle operator[](uint16_t column_ix) const const item_handle operator[](uint16_t item_ix) const
{ {
return empty() ? item_handle::s_null_item : item_handle(column_ix, const_cast<row_handle &>(*this)); return empty() ? item_handle::s_null_item : item_handle(item_ix, const_cast<row_handle &>(*this));
} }
/// \brief return a cif::item_handle to the item in the column named @a column_name /// \brief return a cif::item_handle to the item in the item named @a item_name
item_handle operator[](std::string_view column_name) item_handle operator[](std::string_view item_name)
{ {
return empty() ? item_handle::s_null_item : item_handle(add_column(column_name), *this); return empty() ? item_handle::s_null_item : item_handle(add_item(item_name), *this);
} }
/// \brief return a const cif::item_handle to the item in the column named @a column_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 column_name) const const item_handle operator[](std::string_view item_name) const
{ {
return empty() ? item_handle::s_null_item : item_handle(get_column_ix(column_name), const_cast<row_handle &>(*this)); 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 /// \brief Return an object that can be used in combination with cif::tie
/// to assign the values for the columns @a columns /// to assign the values for the items @a items
template <typename... C> template <typename... C>
auto get(C... columns) const auto get(C... items) const
{ {
return detail::get_row_result<C...>(*this, { get_column_ix(columns)... }); return detail::get_row_result<C...>(*this, { get_item_ix(items)... });
} }
/// \brief Return a tuple of values of types @a Ts for the columns @a columns /// \brief Return a tuple of values of types @a Ts for the items @a items
template <typename... Ts, typename... C, std::enable_if_t<sizeof...(Ts) == sizeof...(C) and sizeof...(C) != 1, int> = 0> template <typename... Ts, typename... C, std::enable_if_t<sizeof...(Ts) == sizeof...(C) and sizeof...(C) != 1, int> = 0>
std::tuple<Ts...> get(C... columns) const std::tuple<Ts...> get(C... items) const
{ {
return detail::get_row_result<Ts...>(*this, { get_column_ix(columns)... }); return detail::get_row_result<Ts...>(*this, { get_item_ix(items)... });
} }
/// \brief Get the value of column @a column cast to type @a T /// \brief Get the value of item @a item cast to type @a T
template <typename T> template <typename T>
T get(const char *column) const T get(const char *item) const
{ {
return operator[](get_column_ix(column)).template as<T>(); return operator[](get_item_ix(item)).template as<T>();
} }
/// \brief Get the value of column @a column cast to type @a T /// \brief Get the value of item @a item cast to type @a T
template <typename T> template <typename T>
T get(std::string_view column) const T get(std::string_view item) const
{ {
return operator[](get_column_ix(column)).template as<T>(); return operator[](get_item_ix(item)).template as<T>();
} }
/// \brief assign each of the columns named in @a values to their respective value /// \brief assign each of the items named in @a values to their respective value
void assign(const std::vector<item> &values) void assign(const std::vector<item> &values)
{ {
for (auto &value : values) for (auto &value : values)
assign(value, true); assign(value, true);
} }
/** \brief assign the value @a value to the column named @a name /** \brief assign the value @a value to the item named @a name
* *
* If updateLinked it true, linked records are updated as well. * If updateLinked it true, linked records are updated as well.
* That means that if column @a name is part of the link definition * That means that if item @a name is part of the link definition
* and the link results in a linked record in another category * and the link results in a linked record in another category
* this record in the linked category is updated as well. * this record in the linked category is updated as well.
* *
...@@ -317,13 +317,13 @@ class row_handle ...@@ -317,13 +317,13 @@ class row_handle
void assign(std::string_view name, std::string_view value, bool updateLinked, bool validate = true) void assign(std::string_view name, std::string_view value, bool updateLinked, bool validate = true)
{ {
assign(add_column(name), value, updateLinked, validate); assign(add_item(name), value, updateLinked, validate);
} }
/** \brief assign the value @a value to column at index @a column /** \brief assign the value @a value to item at index @a item
* *
* If updateLinked it true, linked records are updated as well. * If updateLinked it true, linked records are updated as well.
* That means that if column @a column is part of the link definition * That means that if item @a item is part of the link definition
* and the link results in a linked record in another category * and the link results in a linked record in another category
* this record in the linked category is updated as well. * this record in the linked category is updated as well.
* *
...@@ -331,7 +331,7 @@ class row_handle ...@@ -331,7 +331,7 @@ class row_handle
* checked to see if it conforms to the rules defined in the dictionary * checked to see if it conforms to the rules defined in the dictionary
*/ */
void assign(uint16_t column, std::string_view value, bool updateLinked, bool validate = true); void assign(uint16_t item, std::string_view value, bool updateLinked, bool validate = true);
/// \brief compare two rows /// \brief compare two rows
bool operator==(const row_handle &rhs) const { return m_category == rhs.m_category and m_row == rhs.m_row; } bool operator==(const row_handle &rhs) const { return m_category == rhs.m_category and m_row == rhs.m_row; }
...@@ -340,10 +340,10 @@ class row_handle ...@@ -340,10 +340,10 @@ class row_handle
bool operator!=(const row_handle &rhs) const { return m_category != rhs.m_category or m_row != rhs.m_row; } bool operator!=(const row_handle &rhs) const { return m_category != rhs.m_category or m_row != rhs.m_row; }
private: private:
uint16_t get_column_ix(std::string_view name) const; uint16_t get_item_ix(std::string_view name) const;
std::string_view get_column_name(uint16_t ix) const; std::string_view get_item_name(uint16_t ix) const;
uint16_t add_column(std::string_view name); uint16_t add_item(std::string_view name);
row *get_row() row *get_row()
{ {
...@@ -360,7 +360,7 @@ class row_handle ...@@ -360,7 +360,7 @@ class row_handle
assign(i.name(), i.value(), updateLinked); assign(i.name(), i.value(), updateLinked);
} }
void swap(uint16_t column, row_handle &r); void swap(uint16_t item, row_handle &r);
category *m_category = nullptr; category *m_category = nullptr;
row *m_row = nullptr; row *m_row = nullptr;
......
...@@ -317,7 +317,7 @@ inline char tolower(int ch) ...@@ -317,7 +317,7 @@ inline char tolower(int ch)
// -------------------------------------------------------------------- // --------------------------------------------------------------------
/** \brief return a tuple consisting of the category and item name for @a tag /** \brief return a tuple consisting of the category and item name for @a item_name
* *
* The category name is stripped of its leading underscore character. * The category name is stripped of its leading underscore character.
* *
...@@ -325,7 +325,19 @@ inline char tolower(int ch) ...@@ -325,7 +325,19 @@ inline char tolower(int ch)
* cif 1.0 formatted data. * cif 1.0 formatted data.
*/ */
std::tuple<std::string, std::string> split_tag_name(std::string_view tag); [[deprecated("use split_item_name instead")]]
std::tuple<std::string, std::string> split_tag_name(std::string_view item_name);
/** \brief return a tuple consisting of the category and item name for @a item_name
*
* The category name is stripped of its leading underscore character.
*
* If no dot character was found, the category name is empty. That's for
* cif 1.0 formatted data.
*/
std::tuple<std::string, std::string> split_item_name(std::string_view item_name);
// -------------------------------------------------------------------- // --------------------------------------------------------------------
......
...@@ -2712,4 +2712,96 @@ _pdbx_chem_comp_audit.processing_site ...@@ -2712,4 +2712,96 @@ _pdbx_chem_comp_audit.processing_site
HIS "Create component" 1999-07-08 EBI HIS "Create component" 1999-07-08 EBI
HIS "Modify descriptor" 2011-06-04 RCSB HIS "Modify descriptor" 2011-06-04 RCSB
# #
data_HOH
#
_chem_comp.id HOH
_chem_comp.name WATER
_chem_comp.type NON-POLYMER
_chem_comp.pdbx_type HETAS
_chem_comp.formula "H2 O"
_chem_comp.mon_nstd_parent_comp_id ?
_chem_comp.pdbx_synonyms ?
_chem_comp.pdbx_formal_charge 0
_chem_comp.pdbx_initial_date 1999-07-08
_chem_comp.pdbx_modified_date 2011-06-04
_chem_comp.pdbx_ambiguous_flag N
_chem_comp.pdbx_release_status REL
_chem_comp.pdbx_replaced_by ?
_chem_comp.pdbx_replaces MTO
_chem_comp.formula_weight 18.015
_chem_comp.one_letter_code ?
_chem_comp.three_letter_code HOH
_chem_comp.pdbx_model_coordinates_details ?
_chem_comp.pdbx_model_coordinates_missing_flag N
_chem_comp.pdbx_ideal_coordinates_details ?
_chem_comp.pdbx_ideal_coordinates_missing_flag N
_chem_comp.pdbx_model_coordinates_db_code 1NHE
_chem_comp.pdbx_subcomponent_list ?
_chem_comp.pdbx_processing_site RCSB
# #
loop_
_chem_comp_atom.comp_id
_chem_comp_atom.atom_id
_chem_comp_atom.alt_atom_id
_chem_comp_atom.type_symbol
_chem_comp_atom.charge
_chem_comp_atom.pdbx_align
_chem_comp_atom.pdbx_aromatic_flag
_chem_comp_atom.pdbx_leaving_atom_flag
_chem_comp_atom.pdbx_stereo_config
_chem_comp_atom.model_Cartn_x
_chem_comp_atom.model_Cartn_y
_chem_comp_atom.model_Cartn_z
_chem_comp_atom.pdbx_model_Cartn_x_ideal
_chem_comp_atom.pdbx_model_Cartn_y_ideal
_chem_comp_atom.pdbx_model_Cartn_z_ideal
_chem_comp_atom.pdbx_component_atom_id
_chem_comp_atom.pdbx_component_comp_id
_chem_comp_atom.pdbx_ordinal
HOH O O O 0 1 N N N -23.107 18.401 -21.626 -0.064 0.000 0.000 O HOH 1
HOH H1 1H H 0 1 N N N -22.157 18.401 -21.626 0.512 0.000 -0.776 H1 HOH 2
HOH H2 2H H 0 1 N N N -23.424 18.401 -20.730 0.512 0.000 0.776 H2 HOH 3
# #
loop_
_chem_comp_bond.comp_id
_chem_comp_bond.atom_id_1
_chem_comp_bond.atom_id_2
_chem_comp_bond.value_order
_chem_comp_bond.pdbx_aromatic_flag
_chem_comp_bond.pdbx_stereo_config
_chem_comp_bond.pdbx_ordinal
HOH O H1 SING N N 1
HOH O H2 SING N N 2
# #
loop_
_pdbx_chem_comp_descriptor.comp_id
_pdbx_chem_comp_descriptor.type
_pdbx_chem_comp_descriptor.program
_pdbx_chem_comp_descriptor.program_version
_pdbx_chem_comp_descriptor.descriptor
HOH SMILES ACDLabs 10.04 O
HOH SMILES_CANONICAL CACTVS 3.341 O
HOH SMILES CACTVS 3.341 O
HOH SMILES_CANONICAL "OpenEye OEToolkits" 1.5.0 O
HOH SMILES "OpenEye OEToolkits" 1.5.0 O
HOH InChI InChI 1.03 InChI=1S/H2O/h1H2
HOH InChIKey InChI 1.03 XLYOFNOQVPJJNP-UHFFFAOYSA-N
# #
loop_
_pdbx_chem_comp_identifier.comp_id
_pdbx_chem_comp_identifier.type
_pdbx_chem_comp_identifier.program
_pdbx_chem_comp_identifier.program_version
_pdbx_chem_comp_identifier.identifier
HOH "SYSTEMATIC NAME" ACDLabs 10.04 water
HOH "SYSTEMATIC NAME" "OpenEye OEToolkits" 1.5.0 oxidane
# #
loop_
_pdbx_chem_comp_audit.comp_id
_pdbx_chem_comp_audit.action_type
_pdbx_chem_comp_audit.date
_pdbx_chem_comp_audit.processing_site
HOH "Create component" 1999-07-08 RCSB
HOH "Modify descriptor" 2011-06-04 RCSB
##
...@@ -311,6 +311,18 @@ float compound::bond_length(const std::string &atomId_1, const std::string &atom ...@@ -311,6 +311,18 @@ float compound::bond_length(const std::string &atomId_1, const std::string &atom
} }
// -------------------------------------------------------------------- // --------------------------------------------------------------------
bool compound::is_peptide() const
{
return iequals(m_type, "l-peptide linking") or iequals(m_type, "peptide linking");
}
bool compound::is_base() const
{
return iequals(m_type, "dna linking") or iequals(m_type, "rna linking");
}
// --------------------------------------------------------------------
// known amino acids and bases // known amino acids and bases
const std::map<std::string, char> compound_factory::kAAMap{ const std::map<std::string, char> compound_factory::kAAMap{
...@@ -660,25 +672,67 @@ void compound_factory::pop_dictionary() ...@@ -660,25 +672,67 @@ void compound_factory::pop_dictionary()
m_impl = m_impl->next(); m_impl = m_impl->next();
} }
const compound *compound_factory::create(std::string id) const compound *compound_factory::create(std::string_view id)
{ {
auto result = m_impl ? m_impl->get(id) : nullptr; auto result = m_impl ? m_impl->get(std::string{ id }) : nullptr;
if (not result) if (not result)
report_missing_compound(id); report_missing_compound(id);
return result; return result;
} }
bool compound_factory::is_known_peptide(const std::string &resName) const bool compound_factory::is_known_peptide(const std::string &res_name) const
{
return kAAMap.count(res_name) > 0;
}
bool compound_factory::is_known_base(const std::string &res_name) const
{
return kBaseMap.count(res_name) > 0;
}
/// Return whether @a res_name is a peptide
bool compound_factory::is_peptide(std::string_view res_name) const
{
bool result = is_std_peptide(res_name);
if (not result and m_impl)
{
auto compound = const_cast<compound_factory&>(*this).create(res_name);
result = compound != nullptr and compound->is_peptide();
}
return result;
}
/// Return whether @a res_name is a base
bool compound_factory::is_base(std::string_view res_name) const
{
bool result = is_std_base(res_name);
if (not result and m_impl)
{
auto compound = const_cast<compound_factory&>(*this).create(res_name);
result = compound != nullptr and compound->is_base();
}
return result;
}
/// Return whether @a res_name is one of the standard peptides
bool compound_factory::is_std_peptide(std::string_view res_name) const
{
return kAAMap.count(std::string{ res_name }) > 0;
}
/// Return whether @a res_name is one of the standard bases
bool compound_factory::is_std_base(std::string_view res_name) const
{ {
return kAAMap.count(resName) > 0; return kBaseMap.count(std::string{ res_name }) > 0;
} }
bool compound_factory::is_known_base(const std::string &resName) const /// Return whether @a res_name is a monomer (either base or peptide)
bool compound_factory::is_monomer(std::string_view res_name) const
{ {
return kBaseMap.count(resName) > 0; return is_peptide(res_name) or is_base(res_name);
} }
void compound_factory::report_missing_compound(const std::string &compound_id) void compound_factory::report_missing_compound(std::string_view compound_id)
{ {
static bool s_reported = false; static bool s_reported = false;
if (std::exchange(s_reported, true) == false) if (std::exchange(s_reported, true) == false)
...@@ -703,8 +757,8 @@ void compound_factory::report_missing_compound(const std::string &compound_id) ...@@ -703,8 +757,8 @@ void compound_factory::report_missing_compound(const std::string &compound_id)
<< "update=true\n\n" << "update=true\n\n"
<< "If you do not have a working cron script, you can manually update the files\n" << "If you do not have a working cron script, you can manually update the files\n"
<< "in /var/cache/libcifpp using the following commands:\n\n" << "in /var/cache/libcifpp using the following commands:\n\n"
<< "curl -o " << CACHE_DIR << "/components.cif https://files.wwpdb.org/pub/pdb/data/monomers/components.cif.gz\n" << "curl -o " << CACHE_DIR << "/components.cif https://files.wwpdb.org/pub/pdb/data/monomers/components.cif\n"
<< "curl -o " << CACHE_DIR << "/mmcif_pdbx.dic https://mmcif.wwpdb.org/dictionaries/ascii/mmcif_pdbx_v50.dic.gz\n" << "curl -o " << CACHE_DIR << "/mmcif_pdbx.dic https://mmcif.wwpdb.org/dictionaries/ascii/mmcif_pdbx_v50.dic\n"
<< "curl -o " << CACHE_DIR << "/mmcif_ma.dic https://github.com/ihmwg/ModelCIF/raw/master/dist/mmcif_ma.dic\n\n"; << "curl -o " << CACHE_DIR << "/mmcif_ma.dic https://github.com/ihmwg/ModelCIF/raw/master/dist/mmcif_ma.dic\n\n";
#endif #endif
......
...@@ -30,17 +30,17 @@ ...@@ -30,17 +30,17 @@
namespace cif namespace cif
{ {
iset get_category_fields(const category &cat) iset get_category_items(const category &cat)
{ {
return cat.key_fields(); return cat.key_items();
} }
uint16_t get_column_ix(const category &cat, std::string_view col) uint16_t get_item_ix(const category &cat, std::string_view col)
{ {
return cat.get_column_ix(col); return cat.get_item_ix(col);
} }
bool is_column_type_uchar(const category &cat, std::string_view col) bool is_item_type_uchar(const category &cat, std::string_view col)
{ {
bool result = false; bool result = false;
...@@ -63,14 +63,14 @@ namespace detail ...@@ -63,14 +63,14 @@ namespace detail
condition_impl *key_equals_condition_impl::prepare(const category &c) condition_impl *key_equals_condition_impl::prepare(const category &c)
{ {
m_item_ix = c.get_column_ix(m_item_tag); m_item_ix = c.get_item_ix(m_item_name);
m_icase = is_column_type_uchar(c, m_item_tag); m_icase = is_item_type_uchar(c, m_item_name);
if (c.get_cat_validator() != nullptr and if (c.get_cat_validator() != nullptr and
c.key_field_indices().contains(m_item_ix) and c.key_item_indices().contains(m_item_ix) and
c.key_field_indices().size() == 1) c.key_item_indices().size() == 1)
{ {
m_single_hit = c[{ { m_item_tag, m_value } }]; m_single_hit = c[{ { m_item_name, m_value } }];
} }
return this; return this;
......
...@@ -85,6 +85,40 @@ bool datablock::is_valid() const ...@@ -85,6 +85,40 @@ bool datablock::is_valid() const
return result; return result;
} }
bool datablock::is_valid()
{
if (m_validator == nullptr)
throw std::runtime_error("Validator not specified");
bool result = true;
for (auto &cat : *this)
result = cat.is_valid() and result;
// Add or remove the audit_conform block here.
if (result)
{
// If the dictionary declares an audit_conform category, put it in,
// but only if it does not exist already!
if (m_validator->get_validator_for_category("audit_conform") != nullptr)
{
auto &audit_conform = operator[]("audit_conform");
audit_conform.clear();
audit_conform.emplace({
// clang-format off
{ "dict_name", m_validator->name() },
{ "dict_version", m_validator->version() }
// clang-format on
});
}
}
else
erase(std::find_if(begin(), end(), [](category &cat) { return cat.name() == "audit_conform"; }), end());
return result;
}
bool datablock::validate_links() const bool datablock::validate_links() const
{ {
bool result = true; bool result = true;
...@@ -143,13 +177,6 @@ std::tuple<datablock::iterator, bool> datablock::emplace(std::string_view name) ...@@ -143,13 +177,6 @@ std::tuple<datablock::iterator, bool> datablock::emplace(std::string_view name)
if (iequals(name, i->name())) if (iequals(name, i->name()))
{ {
is_new = false; is_new = false;
if (i != begin())
{
auto n = std::next(i);
splice(begin(), *this, i, n);
}
break; break;
} }
...@@ -158,25 +185,24 @@ std::tuple<datablock::iterator, bool> datablock::emplace(std::string_view name) ...@@ -158,25 +185,24 @@ std::tuple<datablock::iterator, bool> datablock::emplace(std::string_view name)
if (is_new) if (is_new)
{ {
auto &c = emplace_back(name); i = insert(end(), {name});
c.set_validator(m_validator, *this); i->set_validator(m_validator, *this);
} }
assert(end() != begin()); assert(i != end());
return std::make_tuple(std::prev(end()), is_new); return std::make_tuple(i, is_new);
} }
std::vector<std::string> datablock::get_tag_order() const std::vector<std::string> datablock::get_item_order() const
{ {
std::vector<std::string> result; std::vector<std::string> result;
// for entry and audit_conform on top // for entry and audit_conform on top
auto ci = find_if(begin(), end(), [](const category &cat) auto ci = find_if(begin(), end(), [](const category &cat)
{ return cat.name() == "entry"; }); { return cat.name() == "entry"; });
if (ci != end()) if (ci != end())
{ {
auto cto = ci->get_tag_order(); auto cto = ci->get_item_order();
result.insert(result.end(), cto.begin(), cto.end()); result.insert(result.end(), cto.begin(), cto.end());
} }
...@@ -184,7 +210,7 @@ std::vector<std::string> datablock::get_tag_order() const ...@@ -184,7 +210,7 @@ std::vector<std::string> datablock::get_tag_order() const
{ return cat.name() == "audit_conform"; }); { return cat.name() == "audit_conform"; });
if (ci != end()) if (ci != end())
{ {
auto cto = ci->get_tag_order(); auto cto = ci->get_item_order();
result.insert(result.end(), cto.begin(), cto.end()); result.insert(result.end(), cto.begin(), cto.end());
} }
...@@ -192,7 +218,7 @@ std::vector<std::string> datablock::get_tag_order() const ...@@ -192,7 +218,7 @@ std::vector<std::string> datablock::get_tag_order() const
{ {
if (cat.name() == "entry" or cat.name() == "audit_conform") if (cat.name() == "entry" or cat.name() == "audit_conform")
continue; continue;
auto cto = cat.get_tag_order(); auto cto = cat.get_item_order();
result.insert(result.end(), cto.begin(), cto.end()); result.insert(result.end(), cto.begin(), cto.end());
} }
...@@ -251,16 +277,6 @@ void datablock::write(std::ostream &os) const ...@@ -251,16 +277,6 @@ void datablock::write(std::ostream &os) const
if (m_validator and size() > 0) if (m_validator and size() > 0)
{ {
// If the dictionary declares an audit_conform category, put it in,
// but only if it does not exist already!
if (get("audit_conform") == nullptr and m_validator->get_validator_for_category("audit_conform") != nullptr)
{
category auditConform("audit_conform");
auditConform.emplace({ { "dict_name", m_validator->name() },
{ "dict_version", m_validator->version() } });
auditConform.write(os);
}
// base order on parent child relationships, parents first // base order on parent child relationships, parents first
cat_order_t cat_order; cat_order_t cat_order;
...@@ -327,16 +343,16 @@ void datablock::write(std::ostream &os) const ...@@ -327,16 +343,16 @@ void datablock::write(std::ostream &os) const
} }
} }
void datablock::write(std::ostream &os, const std::vector<std::string> &tag_order) void datablock::write(std::ostream &os, const std::vector<std::string> &item_name_order)
{ {
os << "data_" << m_name << '\n' os << "data_" << m_name << '\n'
<< "# \n"; << "# \n";
std::vector<std::string> cat_order; std::vector<std::string> cat_order;
for (auto &o : tag_order) for (auto &o : item_name_order)
{ {
std::string cat_name, item_name; std::string cat_name, item_name;
std::tie(cat_name, item_name) = split_tag_name(o); std::tie(cat_name, item_name) = split_item_name(o);
if (find_if(cat_order.rbegin(), cat_order.rend(), [cat_name](const std::string &s) -> bool if (find_if(cat_order.rbegin(), cat_order.rend(), [cat_name](const std::string &s) -> bool
{ return iequals(cat_name, s); }) == cat_order.rend()) { return iequals(cat_name, s); }) == cat_order.rend())
cat_order.push_back(cat_name); cat_order.push_back(cat_name);
...@@ -349,10 +365,10 @@ void datablock::write(std::ostream &os, const std::vector<std::string> &tag_orde ...@@ -349,10 +365,10 @@ void datablock::write(std::ostream &os, const std::vector<std::string> &tag_orde
continue; continue;
std::vector<std::string> items; std::vector<std::string> items;
for (auto &o : tag_order) for (auto &o : item_name_order)
{ {
std::string cat_name, item_name; std::string cat_name, item_name;
std::tie(cat_name, item_name) = split_tag_name(o); std::tie(cat_name, item_name) = split_item_name(o);
if (cat_name == c) if (cat_name == c)
items.push_back(item_name); items.push_back(item_name);
...@@ -374,6 +390,10 @@ void datablock::write(std::ostream &os, const std::vector<std::string> &tag_orde ...@@ -374,6 +390,10 @@ void datablock::write(std::ostream &os, const std::vector<std::string> &tag_orde
bool datablock::operator==(const datablock &rhs) const bool datablock::operator==(const datablock &rhs) const
{ {
// shortcut
if (this == &rhs)
return true;
auto &dbA = *this; auto &dbA = *this;
auto &dbB = rhs; auto &dbB = rhs;
......
...@@ -50,7 +50,7 @@ class dictionary_parser : public parser ...@@ -50,7 +50,7 @@ class dictionary_parser : public parser
try try
{ {
while (m_lookahead != CIFToken::Eof) while (m_lookahead != CIFToken::END_OF_FILE)
{ {
switch (m_lookahead) switch (m_lookahead)
{ {
...@@ -87,7 +87,7 @@ class dictionary_parser : public parser ...@@ -87,7 +87,7 @@ class dictionary_parser : public parser
error("Undefined category '" + iv.first); error("Undefined category '" + iv.first);
for (auto &v : iv.second) for (auto &v : iv.second)
const_cast<category_validator *>(cv)->addItemValidator(std::move(v)); const_cast<category_validator *>(cv)->add_item_validator(std::move(v));
} }
// check all item validators for having a typeValidator // check all item validators for having a typeValidator
...@@ -128,7 +128,7 @@ class dictionary_parser : public parser ...@@ -128,7 +128,7 @@ class dictionary_parser : public parser
datablock::iterator cat = dict.end(); datablock::iterator cat = dict.end();
match(CIFToken::SAVE_NAME); match(CIFToken::SAVE_NAME);
while (m_lookahead == CIFToken::LOOP or m_lookahead == CIFToken::Tag) while (m_lookahead == CIFToken::LOOP or m_lookahead == CIFToken::ITEM_NAME)
{ {
if (m_lookahead == CIFToken::LOOP) if (m_lookahead == CIFToken::LOOP)
{ {
...@@ -136,30 +136,30 @@ class dictionary_parser : public parser ...@@ -136,30 +136,30 @@ class dictionary_parser : public parser
match(CIFToken::LOOP); match(CIFToken::LOOP);
std::vector<std::string> tags; std::vector<std::string> item_names;
while (m_lookahead == CIFToken::Tag) while (m_lookahead == CIFToken::ITEM_NAME)
{ {
std::string catName, item_name; std::string catName, item_name;
std::tie(catName, item_name) = split_tag_name(m_token_value); std::tie(catName, item_name) = split_item_name(m_token_value);
if (cat == dict.end()) if (cat == dict.end())
std::tie(cat, std::ignore) = dict.emplace(catName); std::tie(cat, std::ignore) = dict.emplace(catName);
else if (not iequals(cat->name(), catName)) else if (not iequals(cat->name(), catName))
error("inconsistent categories in loop_"); error("inconsistent categories in loop_");
tags.push_back(item_name); item_names.push_back(item_name);
match(CIFToken::Tag); match(CIFToken::ITEM_NAME);
} }
while (m_lookahead == CIFToken::Value) while (m_lookahead == CIFToken::VALUE)
{ {
cat->emplace({}); cat->emplace({});
auto row = cat->back(); auto row = cat->back();
for (auto tag : tags) for (auto item_name : item_names)
{ {
row[tag] = m_token_value; row[item_name] = m_token_value;
match(CIFToken::Value); match(CIFToken::VALUE);
} }
} }
...@@ -168,18 +168,18 @@ class dictionary_parser : public parser ...@@ -168,18 +168,18 @@ class dictionary_parser : public parser
else else
{ {
std::string catName, item_name; std::string catName, item_name;
std::tie(catName, item_name) = split_tag_name(m_token_value); std::tie(catName, item_name) = split_item_name(m_token_value);
if (cat == dict.end() or not iequals(cat->name(), catName)) if (cat == dict.end() or not iequals(cat->name(), catName))
std::tie(cat, std::ignore) = dict.emplace(catName); std::tie(cat, std::ignore) = dict.emplace(catName);
match(CIFToken::Tag); match(CIFToken::ITEM_NAME);
if (cat->empty()) if (cat->empty())
cat->emplace({}); cat->emplace({});
cat->back()[item_name] = m_token_value; cat->back()[item_name] = m_token_value;
match(CIFToken::Value); match(CIFToken::VALUE);
} }
} }
...@@ -191,7 +191,7 @@ class dictionary_parser : public parser ...@@ -191,7 +191,7 @@ class dictionary_parser : public parser
std::vector<std::string> keys; std::vector<std::string> keys;
for (auto k : dict["category_key"]) for (auto k : dict["category_key"])
keys.push_back(std::get<1>(split_tag_name(k["name"].as<std::string>()))); keys.push_back(std::get<1>(split_item_name(k["name"].as<std::string>())));
iset groups; iset groups;
for (auto g : dict["category_group"]) for (auto g : dict["category_group"])
...@@ -224,20 +224,27 @@ class dictionary_parser : public parser ...@@ -224,20 +224,27 @@ class dictionary_parser : public parser
// } // }
// } // }
std::vector<item_alias> aliases;
for (const auto &[alias_name, dictionary, version] :
dict["item_aliases"].rows<std::string,std::string,std::string>("alias_name", "dictionary", "version"))
{
aliases.emplace_back(alias_name, dictionary, version);
}
// collect the dict from our dataBlock and construct validators // collect the dict from our dataBlock and construct validators
for (auto i : dict["item"]) for (auto i : dict["item"])
{ {
std::string tagName, category, mandatory; std::string item, category, mandatory;
cif::tie(tagName, category, mandatory) = i.get("name", "category_id", "mandatory_code"); cif::tie(item, category, mandatory) = i.get("name", "category_id", "mandatory_code");
std::string cat_name, item_name; std::string cat_name, item_name;
std::tie(cat_name, item_name) = split_tag_name(tagName); std::tie(cat_name, item_name) = split_item_name(item);
if (cat_name.empty() or item_name.empty()) if (cat_name.empty() or item_name.empty())
error("Invalid tag name in _item.name " + tagName); error("Invalid item name in _item.name " + item);
if (not iequals(category, cat_name) and not(category.empty() or category == "?")) if (not iequals(category, cat_name) and not(category.empty() or category == "?"))
error("specified category id does match the implicit category name for tag '" + tagName + '\''); error("specified category id does match the implicit category name for item '" + item + '\'');
else else
category = cat_name; category = cat_name;
...@@ -245,7 +252,7 @@ class dictionary_parser : public parser ...@@ -245,7 +252,7 @@ class dictionary_parser : public parser
auto vi = find(ivs.begin(), ivs.end(), item_validator{ item_name }); auto vi = find(ivs.begin(), ivs.end(), item_validator{ item_name });
if (vi == ivs.end()) if (vi == ivs.end())
ivs.push_back(item_validator{ item_name, iequals(mandatory, "yes"), tv, ess, defaultValue /*, defaultIsNull*/ }); ivs.push_back(item_validator{ item_name, iequals(mandatory, "yes"), tv, ess, defaultValue, nullptr, std::move(aliases) });
else else
{ {
// need to update the itemValidator? // need to update the itemValidator?
...@@ -253,22 +260,22 @@ class dictionary_parser : public parser ...@@ -253,22 +260,22 @@ class dictionary_parser : public parser
{ {
if (VERBOSE > 2) if (VERBOSE > 2)
{ {
std::cerr << "inconsistent mandatory value for " << tagName << " in dictionary\n"; std::cerr << "inconsistent mandatory value for " << item << " in dictionary\n";
if (iequals(tagName, saveFrameName)) if (iequals(item, saveFrameName))
std::cerr << "choosing " << mandatory << '\n'; std::cerr << "choosing " << mandatory << '\n';
else else
std::cerr << "choosing " << (vi->m_mandatory ? "Y" : "N") << '\n'; std::cerr << "choosing " << (vi->m_mandatory ? "Y" : "N") << '\n';
} }
if (iequals(tagName, saveFrameName)) if (iequals(item, saveFrameName))
vi->m_mandatory = (iequals(mandatory, "yes")); vi->m_mandatory = (iequals(mandatory, "yes"));
} }
if (vi->m_type != nullptr and tv != nullptr and vi->m_type != tv) if (vi->m_type != nullptr and tv != nullptr and vi->m_type != tv)
{ {
if (VERBOSE > 1) if (VERBOSE > 1)
std::cerr << "inconsistent type for " << tagName << " in dictionary\n"; std::cerr << "inconsistent type for " << item << " in dictionary\n";
} }
// vi->mMandatory = (iequals(mandatory, "yes")); // vi->mMandatory = (iequals(mandatory, "yes"));
...@@ -351,7 +358,7 @@ class dictionary_parser : public parser ...@@ -351,7 +358,7 @@ class dictionary_parser : public parser
} }
size_t ix = linkIndex.at(key); size_t ix = linkIndex.at(key);
addLink(ix, piv->m_tag, civ->m_tag); addLink(ix, piv->m_item_name, civ->m_item_name);
} }
// Only process inline linked items if the linked group list is absent // Only process inline linked items if the linked group list is absent
...@@ -379,7 +386,7 @@ class dictionary_parser : public parser ...@@ -379,7 +386,7 @@ class dictionary_parser : public parser
} }
size_t ix = linkIndex.at(key); size_t ix = linkIndex.at(key);
addLink(ix, piv->m_tag, civ->m_tag); addLink(ix, piv->m_item_name, civ->m_item_name);
} }
} }
...@@ -410,7 +417,7 @@ class dictionary_parser : public parser ...@@ -410,7 +417,7 @@ class dictionary_parser : public parser
for (auto &iv : cv.m_item_validators) for (auto &iv : cv.m_item_validators)
{ {
if (iv.m_type == nullptr and cif::VERBOSE >= 0) if (iv.m_type == nullptr and cif::VERBOSE >= 0)
std::cerr << "Missing item_type for " << iv.m_tag << '\n'; std::cerr << "Missing item_type for " << iv.m_item_name << '\n';
} }
} }
} }
......
...@@ -158,13 +158,6 @@ std::tuple<file::iterator, bool> file::emplace(std::string_view name) ...@@ -158,13 +158,6 @@ std::tuple<file::iterator, bool> file::emplace(std::string_view name)
if (iequals(name, i->name())) if (iequals(name, i->name()))
{ {
is_new = false; is_new = false;
if (i != begin())
{
auto n = std::next(i);
splice(begin(), *this, i, n);
}
break; break;
} }
...@@ -173,12 +166,12 @@ std::tuple<file::iterator, bool> file::emplace(std::string_view name) ...@@ -173,12 +166,12 @@ std::tuple<file::iterator, bool> file::emplace(std::string_view name)
if (is_new) if (is_new)
{ {
auto &db = emplace_back(name); i = insert(end(), { name });
db.set_validator(m_validator); i->set_validator(m_validator);
} }
assert(begin() != end()); assert(i != end());
return std::make_tuple(std::prev(end()), is_new); return std::make_tuple(i, is_new);
} }
void file::load(const std::filesystem::path &p) void file::load(const std::filesystem::path &p)
......
...@@ -35,7 +35,7 @@ const item_handle item_handle::s_null_item; ...@@ -35,7 +35,7 @@ const item_handle item_handle::s_null_item;
row_handle s_null_row_handle; row_handle s_null_row_handle;
item_handle::item_handle() item_handle::item_handle()
: m_column(std::numeric_limits<uint16_t>::max()) : m_item_ix(std::numeric_limits<uint16_t>::max())
, m_row_handle(s_null_row_handle) , m_row_handle(s_null_row_handle)
{ {
} }
...@@ -44,7 +44,7 @@ std::string_view item_handle::text() const ...@@ -44,7 +44,7 @@ std::string_view item_handle::text() const
{ {
if (not m_row_handle.empty()) if (not m_row_handle.empty())
{ {
auto iv = m_row_handle.m_row->get(m_column); auto iv = m_row_handle.m_row->get(m_item_ix);
if (iv != nullptr) if (iv != nullptr)
return iv->text(); return iv->text();
} }
...@@ -52,17 +52,17 @@ std::string_view item_handle::text() const ...@@ -52,17 +52,17 @@ std::string_view item_handle::text() const
return {}; return {};
} }
void item_handle::assign_value(const item &v) void item_handle::assign_value(std::string_view value)
{ {
assert(not m_row_handle.empty()); assert(not m_row_handle.empty());
m_row_handle.assign(m_column, v.value(), true); m_row_handle.assign(m_item_ix, value, true);
} }
void item_handle::swap(item_handle &b) void item_handle::swap(item_handle &b)
{ {
assert(m_column == b.m_column); assert(m_item_ix == b.m_item_ix);
// assert(&m_row_handle.m_category == &b.m_row_handle.m_category); // assert(&m_row_handle.m_category == &b.m_row_handle.m_category);
m_row_handle.swap(m_column, b.m_row_handle); m_row_handle.swap(m_item_ix, b.m_row_handle);
} }
} }
...@@ -163,9 +163,9 @@ int atom::atom_impl::get_charge() const ...@@ -163,9 +163,9 @@ int atom::atom_impl::get_charge() const
// const std::string atom::atom_impl::get_property(const std::string_view name) const // const std::string atom::atom_impl::get_property(const std::string_view name) const
// { // {
// for (auto &&[tag, ref] : mCachedRefs) // for (auto &&[item_name, ref] : mCachedRefs)
// { // {
// if (tag == name) // if (item_name == name)
// return ref.as<std::string>(); // return ref.as<std::string>();
// } // }
...@@ -175,9 +175,9 @@ int atom::atom_impl::get_charge() const ...@@ -175,9 +175,9 @@ int atom::atom_impl::get_charge() const
// void atom::atom_impl::set_property(const std::string_view name, const std::string &value) // void atom::atom_impl::set_property(const std::string_view name, const std::string &value)
// { // {
// for (auto &&[tag, ref] : mCachedRefs) // for (auto &&[item_name, ref] : mCachedRefs)
// { // {
// if (tag != name) // if (item_name != name)
// continue; // continue;
// ref = value; // ref = value;
...@@ -1783,7 +1783,7 @@ atom &structure::emplace_atom(atom &&atom) ...@@ -1783,7 +1783,7 @@ atom &structure::emplace_atom(atom &&atom)
std::string symbol = atom.get_property("type_symbol"); std::string symbol = atom.get_property("type_symbol");
using namespace cif::literals; using namespace cif::literals;
if (not atom_type.exists("symbol"_key == symbol)) if (not atom_type.contains("symbol"_key == symbol))
atom_type.emplace({ { "symbol", symbol } }); atom_type.emplace({ { "symbol", symbol } });
return m_atoms.emplace_back(std::move(atom)); return m_atoms.emplace_back(std::move(atom));
...@@ -1969,7 +1969,7 @@ void structure::change_residue(residue &res, const std::string &newCompound, ...@@ -1969,7 +1969,7 @@ void structure::change_residue(residue &res, const std::string &newCompound,
// create rest // create rest
auto &chemComp = m_db["chem_comp"]; auto &chemComp = m_db["chem_comp"];
if (not chemComp.exists(key("id") == newCompound)) if (not chemComp.contains(key("id") == newCompound))
{ {
chemComp.emplace({{"id", newCompound}, chemComp.emplace({{"id", newCompound},
{"name", compound->name()}, {"name", compound->name()},
...@@ -2702,7 +2702,7 @@ void structure::cleanup_empty_categories() ...@@ -2702,7 +2702,7 @@ void structure::cleanup_empty_categories()
for (auto chemComp : chem_comp) for (auto chemComp : chem_comp)
{ {
std::string compID = chemComp["id"].as<std::string>(); std::string compID = chemComp["id"].as<std::string>();
if (atomSite.exists("label_comp_id"_key == compID or "auth_comp_id"_key == compID)) if (atomSite.contains("label_comp_id"_key == compID or "auth_comp_id"_key == compID))
continue; continue;
obsoleteChemComps.push_back(chemComp); obsoleteChemComps.push_back(chemComp);
...@@ -2719,7 +2719,7 @@ void structure::cleanup_empty_categories() ...@@ -2719,7 +2719,7 @@ void structure::cleanup_empty_categories()
for (auto entity : entities) for (auto entity : entities)
{ {
std::string entityID = entity["id"].as<std::string>(); std::string entityID = entity["id"].as<std::string>();
if (atomSite.exists("label_entity_id"_key == entityID)) if (atomSite.contains("label_entity_id"_key == entityID))
continue; continue;
obsoleteEntities.push_back(entity); obsoleteEntities.push_back(entity);
......
...@@ -269,7 +269,7 @@ sac_parser::CIFToken sac_parser::get_next_token() ...@@ -269,7 +269,7 @@ sac_parser::CIFToken sac_parser::get_next_token()
{ {
const auto kEOF = std::char_traits<char>::eof(); const auto kEOF = std::char_traits<char>::eof();
CIFToken result = CIFToken::Unknown; CIFToken result = CIFToken::UNKNOWN;
int quoteChar = 0; int quoteChar = 0;
State state = State::Start; State state = State::Start;
m_bol = false; m_bol = false;
...@@ -279,7 +279,7 @@ sac_parser::CIFToken sac_parser::get_next_token() ...@@ -279,7 +279,7 @@ sac_parser::CIFToken sac_parser::get_next_token()
reserved_words_automaton dag; reserved_words_automaton dag;
while (result == CIFToken::Unknown) while (result == CIFToken::UNKNOWN)
{ {
auto ch = get_next_char(); auto ch = get_next_char();
...@@ -287,7 +287,7 @@ sac_parser::CIFToken sac_parser::get_next_token() ...@@ -287,7 +287,7 @@ sac_parser::CIFToken sac_parser::get_next_token()
{ {
case State::Start: case State::Start:
if (ch == kEOF) if (ch == kEOF)
result = CIFToken::Eof; result = CIFToken::END_OF_FILE;
else if (ch == '\n') else if (ch == '\n')
{ {
m_bol = true; m_bol = true;
...@@ -298,9 +298,9 @@ sac_parser::CIFToken sac_parser::get_next_token() ...@@ -298,9 +298,9 @@ sac_parser::CIFToken sac_parser::get_next_token()
else if (ch == '#') else if (ch == '#')
state = State::Comment; state = State::Comment;
else if (ch == '_') else if (ch == '_')
state = State::Tag; state = State::ItemName;
else if (ch == ';' and m_bol) else if (ch == ';' and m_bol)
state = State::TextField; state = State::TextItem;
else if (ch == '?') else if (ch == '?')
state = State::QuestionMark; state = State::QuestionMark;
else if (ch == '\'' or ch == '"') else if (ch == '\'' or ch == '"')
...@@ -316,7 +316,7 @@ sac_parser::CIFToken sac_parser::get_next_token() ...@@ -316,7 +316,7 @@ sac_parser::CIFToken sac_parser::get_next_token()
case State::White: case State::White:
if (ch == kEOF) if (ch == kEOF)
result = CIFToken::Eof; result = CIFToken::END_OF_FILE;
else if (not is_space(ch)) else if (not is_space(ch))
{ {
state = State::Start; state = State::Start;
...@@ -335,7 +335,7 @@ sac_parser::CIFToken sac_parser::get_next_token() ...@@ -335,7 +335,7 @@ sac_parser::CIFToken sac_parser::get_next_token()
m_token_buffer.clear(); m_token_buffer.clear();
} }
else if (ch == kEOF) else if (ch == kEOF)
result = CIFToken::Eof; result = CIFToken::END_OF_FILE;
else if (not is_any_print(ch)) else if (not is_any_print(ch))
error("invalid character in comment"); error("invalid character in comment");
break; break;
...@@ -344,29 +344,29 @@ sac_parser::CIFToken sac_parser::get_next_token() ...@@ -344,29 +344,29 @@ sac_parser::CIFToken sac_parser::get_next_token()
if (not is_non_blank(ch)) if (not is_non_blank(ch))
{ {
retract(); retract();
result = CIFToken::Value; result = CIFToken::VALUE;
} }
else else
state = State::Value; state = State::Value;
break; break;
case State::TextField: case State::TextItem:
if (ch == '\n') if (ch == '\n')
state = State::TextFieldNL; state = State::TextItemNL;
else if (ch == kEOF) else if (ch == kEOF)
error("unterminated textfield"); error("unterminated textfield");
else if (not is_any_print(ch) and cif::VERBOSE > 2) else if (not is_any_print(ch) and cif::VERBOSE > 2)
warning("invalid character in text field '" + std::string({static_cast<char>(ch)}) + "' (" + std::to_string((int)ch) + ")"); warning("invalid character in text field '" + std::string({static_cast<char>(ch)}) + "' (" + std::to_string((int)ch) + ")");
break; break;
case State::TextFieldNL: case State::TextItemNL:
if (is_text_lead(ch) or ch == ' ' or ch == '\t') if (is_text_lead(ch) or ch == ' ' or ch == '\t')
state = State::TextField; state = State::TextItem;
else if (ch == ';') else if (ch == ';')
{ {
assert(m_token_buffer.size() >= 2); assert(m_token_buffer.size() >= 2);
m_token_value = std::string_view(m_token_buffer.data() + 1, m_token_buffer.size() - 3); m_token_value = std::string_view(m_token_buffer.data() + 1, m_token_buffer.size() - 3);
result = CIFToken::Value; result = CIFToken::VALUE;
} }
else if (ch == kEOF) else if (ch == kEOF)
error("unterminated textfield"); error("unterminated textfield");
...@@ -387,7 +387,7 @@ sac_parser::CIFToken sac_parser::get_next_token() ...@@ -387,7 +387,7 @@ sac_parser::CIFToken sac_parser::get_next_token()
if (is_white(ch)) if (is_white(ch))
{ {
retract(); retract();
result = CIFToken::Value; result = CIFToken::VALUE;
if (m_token_buffer.size() < 2) if (m_token_buffer.size() < 2)
error("Invalid quoted string token"); error("Invalid quoted string token");
...@@ -403,11 +403,11 @@ sac_parser::CIFToken sac_parser::get_next_token() ...@@ -403,11 +403,11 @@ sac_parser::CIFToken sac_parser::get_next_token()
error("invalid character in quoted string"); error("invalid character in quoted string");
break; break;
case State::Tag: case State::ItemName:
if (not is_non_blank(ch)) if (not is_non_blank(ch))
{ {
retract(); retract();
result = CIFToken::Tag; result = CIFToken::ITEM_NAME;
m_token_value = std::string_view(m_token_buffer.data(), m_token_buffer.size()); m_token_value = std::string_view(m_token_buffer.data(), m_token_buffer.size());
} }
break; break;
...@@ -422,7 +422,7 @@ sac_parser::CIFToken sac_parser::get_next_token() ...@@ -422,7 +422,7 @@ sac_parser::CIFToken sac_parser::get_next_token()
if (not is_non_blank(ch)) if (not is_non_blank(ch))
{ {
retract(); retract();
result = CIFToken::Value; result = CIFToken::VALUE;
m_token_value = std::string_view(m_token_buffer.data(), m_token_buffer.size()); m_token_value = std::string_view(m_token_buffer.data(), m_token_buffer.size());
} }
else else
...@@ -467,7 +467,7 @@ sac_parser::CIFToken sac_parser::get_next_token() ...@@ -467,7 +467,7 @@ sac_parser::CIFToken sac_parser::get_next_token()
if (not is_non_blank(ch)) if (not is_non_blank(ch))
{ {
retract(); retract();
result = CIFToken::Value; result = CIFToken::VALUE;
m_token_value = std::string_view(m_token_buffer.data(), m_token_buffer.size()); m_token_value = std::string_view(m_token_buffer.data(), m_token_buffer.size());
break; break;
} }
...@@ -483,7 +483,7 @@ sac_parser::CIFToken sac_parser::get_next_token() ...@@ -483,7 +483,7 @@ sac_parser::CIFToken sac_parser::get_next_token()
if (VERBOSE >= 5) if (VERBOSE >= 5)
{ {
std::cerr << get_token_name(result); std::cerr << get_token_name(result);
if (result != CIFToken::Eof) if (result != CIFToken::END_OF_FILE)
std::cerr << " " << std::quoted(m_token_value); std::cerr << " " << std::quoted(m_token_value);
std::cerr << '\n'; std::cerr << '\n';
} }
...@@ -710,7 +710,7 @@ bool sac_parser::parse_single_datablock(const std::string &datablock, const data ...@@ -710,7 +710,7 @@ bool sac_parser::parse_single_datablock(const std::string &datablock, const data
void sac_parser::parse_file() void sac_parser::parse_file()
{ {
while (m_lookahead != CIFToken::Eof) while (m_lookahead != CIFToken::END_OF_FILE)
{ {
switch (m_lookahead) switch (m_lookahead)
{ {
...@@ -735,10 +735,10 @@ void sac_parser::parse_file() ...@@ -735,10 +735,10 @@ void sac_parser::parse_file()
void sac_parser::parse_global() void sac_parser::parse_global()
{ {
match(CIFToken::GLOBAL); match(CIFToken::GLOBAL);
while (m_lookahead == CIFToken::Tag) while (m_lookahead == CIFToken::ITEM_NAME)
{ {
match(CIFToken::Tag); match(CIFToken::ITEM_NAME);
match(CIFToken::Value); match(CIFToken::VALUE);
} }
} }
...@@ -747,7 +747,7 @@ void sac_parser::parse_datablock() ...@@ -747,7 +747,7 @@ void sac_parser::parse_datablock()
static const std::string kUnitializedCategory("<invalid>"); static const std::string kUnitializedCategory("<invalid>");
std::string cat = kUnitializedCategory; // intial value acts as a guard for empty category names std::string cat = kUnitializedCategory; // intial value acts as a guard for empty category names
while (m_lookahead == CIFToken::LOOP or m_lookahead == CIFToken::Tag or m_lookahead == CIFToken::SAVE_NAME) while (m_lookahead == CIFToken::LOOP or m_lookahead == CIFToken::ITEM_NAME or m_lookahead == CIFToken::SAVE_NAME)
{ {
switch (m_lookahead) switch (m_lookahead)
{ {
...@@ -757,12 +757,12 @@ void sac_parser::parse_datablock() ...@@ -757,12 +757,12 @@ void sac_parser::parse_datablock()
match(CIFToken::LOOP); match(CIFToken::LOOP);
std::vector<std::string> tags; std::vector<std::string> item_names;
while (m_lookahead == CIFToken::Tag) while (m_lookahead == CIFToken::ITEM_NAME)
{ {
std::string catName, itemName; std::string catName, itemName;
std::tie(catName, itemName) = split_tag_name(m_token_value); std::tie(catName, itemName) = split_item_name(m_token_value);
if (cat == kUnitializedCategory) if (cat == kUnitializedCategory)
{ {
...@@ -772,19 +772,19 @@ void sac_parser::parse_datablock() ...@@ -772,19 +772,19 @@ void sac_parser::parse_datablock()
else if (not iequals(cat, catName)) else if (not iequals(cat, catName))
error("inconsistent categories in loop_"); error("inconsistent categories in loop_");
tags.push_back(itemName); item_names.push_back(itemName);
match(CIFToken::Tag); match(CIFToken::ITEM_NAME);
} }
while (m_lookahead == CIFToken::Value) while (m_lookahead == CIFToken::VALUE)
{ {
produce_row(); produce_row();
for (auto tag : tags) for (auto item_name : item_names)
{ {
produce_item(cat, tag, m_token_value); produce_item(cat, item_name, m_token_value);
match(CIFToken::Value); match(CIFToken::VALUE);
} }
} }
...@@ -792,10 +792,10 @@ void sac_parser::parse_datablock() ...@@ -792,10 +792,10 @@ void sac_parser::parse_datablock()
break; break;
} }
case CIFToken::Tag: case CIFToken::ITEM_NAME:
{ {
std::string catName, itemName; std::string catName, itemName;
std::tie(catName, itemName) = split_tag_name(m_token_value); std::tie(catName, itemName) = split_item_name(m_token_value);
if (not iequals(cat, catName)) if (not iequals(cat, catName))
{ {
...@@ -804,11 +804,11 @@ void sac_parser::parse_datablock() ...@@ -804,11 +804,11 @@ void sac_parser::parse_datablock()
produce_row(); produce_row();
} }
match(CIFToken::Tag); match(CIFToken::ITEM_NAME);
produce_item(cat, itemName, m_token_value); produce_item(cat, itemName, m_token_value);
match(CIFToken::Value); match(CIFToken::VALUE);
break; break;
} }
......
...@@ -1123,9 +1123,6 @@ void PDBFileParser::PreParseInput(std::istream &is) ...@@ -1123,9 +1123,6 @@ void PDBFileParser::PreParseInput(std::istream &is)
if (lookahead.back() == '\r') if (lookahead.back() == '\r')
lookahead.pop_back(); lookahead.pop_back();
// if (cif::starts_with(lookahead, "HEADER") == false)
// throw std::runtime_error("This does not look like a PDB file, should start with a HEADER line");
auto contNr = [&lookahead](int offset, int len) -> int auto contNr = [&lookahead](int offset, int len) -> int
{ {
std::string cs = lookahead.substr(offset, len); std::string cs = lookahead.substr(offset, len);
...@@ -1558,52 +1555,54 @@ void PDBFileParser::ParseTitle() ...@@ -1558,52 +1555,54 @@ void PDBFileParser::ParseTitle()
// 11 - 80 Specification compound Description of the molecular components. // 11 - 80 Specification compound Description of the molecular components.
// list // list
std::string value{ mRec->vS(11) }; if (mRec->is("COMPND"))
if (value.find(':') == std::string::npos)
{
// special case for dumb, stripped files
auto &comp = GetOrCreateCompound(1);
comp.mInfo["MOLECULE"] = value;
}
else
{ {
SpecificationListParser p(value); std::string value{ mRec->vS(11) };
if (value.find(':') == std::string::npos)
for (;;)
{ {
std::string key, val; // special case for dumb, stripped files
std::tie(key, val) = p.GetNextSpecification(); auto &comp = GetOrCreateCompound(1);
comp.mInfo["MOLECULE"] = value;
if (key.empty()) }
break; else
{
SpecificationListParser p(value);
if (not iequals(key, "MOL_ID") and mCompounds.empty()) for (;;)
{ {
if (cif::VERBOSE > 0) std::string key, val;
std::cerr << "Ignoring invalid COMPND record\n"; std::tie(key, val) = p.GetNextSpecification();
break;
}
if (key == "MOL_ID") if (key.empty())
{ break;
auto &comp = GetOrCreateCompound(stoi(val));
comp.mTitle = title; if (not iequals(key, "MOL_ID") and mCompounds.empty())
}
else if (key == "CHAIN")
{
for (auto c : cif::split<std::string>(val, ","))
{ {
cif::trim(c); if (cif::VERBOSE > 0)
mCompounds.back().mChains.insert(c[0]); std::cerr << "Ignoring invalid COMPND record\n";
break;
} }
if (key == "MOL_ID")
{
auto &comp = GetOrCreateCompound(stoi(val));
comp.mTitle = title;
}
else if (key == "CHAIN")
{
for (auto c : cif::split<std::string>(val, ","))
{
cif::trim(c);
mCompounds.back().mChains.insert(c[0]);
}
}
else
mCompounds.back().mInfo[key] = val;
} }
else
mCompounds.back().mInfo[key] = val;
} }
}
if (mRec->is("COMPND"))
GetNextRecord(); GetNextRecord();
}
// SOURCE // SOURCE
Match("SOURCE", false); Match("SOURCE", false);
...@@ -1740,7 +1739,7 @@ void PDBFileParser::ParseTitle() ...@@ -1740,7 +1739,7 @@ void PDBFileParser::ParseTitle()
int n = 1; int n = 1;
cat = getCategory("audit_author"); cat = getCategory("audit_author");
value = { mRec->vS(11) }; std::string value = { mRec->vS(11) };
for (auto author : cif::split<std::string>(value, ",", true)) for (auto author : cif::split<std::string>(value, ",", true))
{ {
// clang-format off // clang-format off
...@@ -3646,7 +3645,7 @@ void PDBFileParser::ConstructEntities() ...@@ -3646,7 +3645,7 @@ void PDBFileParser::ConstructEntities()
PDBChain::AtomRes ar{ resName, resSeq, iCode }; PDBChain::AtomRes ar{ resName, resSeq, iCode };
if ((chain.mResiduesSeen.empty() or chain.mResiduesSeen.back() != ar) and if ((chain.mResiduesSeen.empty() or chain.mResiduesSeen.back() != ar) and
(cif::compound_factory::instance().is_known_peptide(resName) or cif::compound_factory::instance().is_known_base(resName))) cif::compound_factory::instance().is_monomer(resName))
{ {
chain.mResiduesSeen.push_back(ar); chain.mResiduesSeen.push_back(ar);
} }
...@@ -3731,11 +3730,8 @@ void PDBFileParser::ConstructEntities() ...@@ -3731,11 +3730,8 @@ void PDBFileParser::ConstructEntities()
{ {
std::string resName = chain.mResiduesSeen[ix].mMonID; std::string resName = chain.mResiduesSeen[ix].mMonID;
if (cif::compound_factory::instance().is_known_peptide(resName) or if (cif::compound_factory::instance().is_monomer(resName))
cif::compound_factory::instance().is_known_base(resName))
{
chain.mTerIndex = ix + 1; chain.mTerIndex = ix + 1;
}
InsertChemComp(resName); InsertChemComp(resName);
} }
...@@ -3814,7 +3810,7 @@ void PDBFileParser::ConstructEntities() ...@@ -3814,7 +3810,7 @@ void PDBFileParser::ConstructEntities()
int residueCount = (residuePerChainCounter[chainID] += 1); int residueCount = (residuePerChainCounter[chainID] += 1);
// There appears to be a program that writes out HETATM records as ATOM records.... // There appears to be a program that writes out HETATM records as ATOM records....
if (not(cif::compound_factory::instance().is_known_peptide(resName) or cif::compound_factory::instance().is_known_base(resName)) or if (not cif::compound_factory::instance().is_monomer(resName) or
terminatedChains.count(chainID) or terminatedChains.count(chainID) or
(chain.mTerIndex > 0 and residueCount >= chain.mTerIndex)) (chain.mTerIndex > 0 and residueCount >= chain.mTerIndex))
{ {
...@@ -4559,7 +4555,7 @@ void PDBFileParser::ConstructEntities() ...@@ -4559,7 +4555,7 @@ void PDBFileParser::ConstructEntities()
std::string formula; std::string formula;
std::string type; std::string type;
std::string nstd = "."; std::string nstd = ".";
std::string formulaWeight; std::optional<float> formulaWeight;
if (compound != nullptr) if (compound != nullptr)
{ {
...@@ -4570,7 +4566,7 @@ void PDBFileParser::ConstructEntities() ...@@ -4570,7 +4566,7 @@ void PDBFileParser::ConstructEntities()
nstd = "y"; nstd = "y";
formula = compound->formula(); formula = compound->formula();
formulaWeight = std::to_string(compound->formula_weight()); formulaWeight = compound->formula_weight();
} }
if (name.empty()) if (name.empty())
...@@ -4597,7 +4593,7 @@ void PDBFileParser::ConstructEntities() ...@@ -4597,7 +4593,7 @@ void PDBFileParser::ConstructEntities()
{ "id", cc }, { "id", cc },
{ "name", name }, { "name", name },
{ "formula", formula }, { "formula", formula },
{ "formula_weight", formulaWeight }, { "formula_weight", formulaWeight, 3 },
{ "mon_nstd_flag", nstd }, { "mon_nstd_flag", nstd },
{ "type", type } { "type", type }
}); });
...@@ -4712,7 +4708,7 @@ void PDBFileParser::ConstructEntities() ...@@ -4712,7 +4708,7 @@ void PDBFileParser::ConstructEntities()
} }
if (formula_weight > 0) if (formula_weight > 0)
entity["formula_weight"] = formula_weight; entity.assign({ { "formula_weight", formula_weight, 3 } });
} }
} }
...@@ -5581,31 +5577,6 @@ void PDBFileParser::ParseCrystallographic() ...@@ -5581,31 +5577,6 @@ void PDBFileParser::ParseCrystallographic()
GetNextRecord(); GetNextRecord();
} }
else
{
// clang-format off
// no cryst1, make a simple one, like this:
// CRYST1 1.000 1.000 1.000 90.00 90.00 90.00 P 1 1
getCategory("cell")->emplace({
{ "entry_id", mStructureID }, // 1 - 6 Record name "CRYST1"
{ "length_a", 1 }, // 7 - 15 Real(9.3) a a (Angstroms).
{ "length_b", 1 }, // 16 - 24 Real(9.3) b b (Angstroms).
{ "length_c", 1 }, // 25 - 33 Real(9.3) c c (Angstroms).
{ "angle_alpha", 90 }, // 34 - 40 Real(7.2) alpha alpha (degrees).
{ "angle_beta", 90 }, // 41 - 47 Real(7.2) beta beta (degrees).
{ "angle_gamma", 90 }, // 48 - 54 Real(7.2) gamma gamma (degrees).
/* goes into symmetry */ // 56 - 66 LString sGroup Space group.
{ "Z_PDB", 1 } // 67 - 70 Integer z Z value.
});
getCategory("symmetry")->emplace({
{ "entry_id", mStructureID },
{ "space_group_name_H-M", "P 1" },
{ "Int_Tables_number", 1 }
});
// clang-format on
}
} }
void PDBFileParser::ParseCoordinateTransformation() void PDBFileParser::ParseCoordinateTransformation()
...@@ -6466,7 +6437,12 @@ file read(std::istream &is) ...@@ -6466,7 +6437,12 @@ file read(std::istream &is)
// and so the very first character in a valid PDB file // and so the very first character in a valid PDB file
// is 'H'. It is as simple as that. // is 'H'. It is as simple as that.
if (ch == 'h' or ch == 'H') // Well, not quite, Unfortunately... People insisted that
// having only ATOM records also makes up a valid PDB file...
// Since mmCIF files cannot validly start with a letter character
// apart from the letter 'd', the test has changed into the following:
if (std::isalpha(ch) and std::toupper(ch) != 'D')
read_pdb_file(is, result); read_pdb_file(is, result);
else else
{ {
......
...@@ -1493,8 +1493,8 @@ bool Remark3Parser::parse(const std::string &expMethod, PDBRecord *r, cif::datab ...@@ -1493,8 +1493,8 @@ bool Remark3Parser::parse(const std::string &expMethod, PDBRecord *r, cif::datab
auto r1 = cat1.front(); auto r1 = cat1.front();
auto r2 = cat2.front(); auto r2 = cat2.front();
for (auto column : cat1.key_fields()) for (auto item : cat1.key_items())
r2[column] = r1[column].text(); r2[item] = r1[item].text();
} }
} }
else else
......
...@@ -69,26 +69,65 @@ condition get_parents_condition(const validator &validator, row_handle rh, const ...@@ -69,26 +69,65 @@ condition get_parents_condition(const validator &validator, row_handle rh, const
bool is_valid_pdbx_file(const file &file, std::string_view dictionary) bool is_valid_pdbx_file(const file &file, std::string_view dictionary)
{ {
using namespace cif::literals; std::error_code ec;
bool result = is_valid_pdbx_file(file, dictionary, ec);
return result and ec == std::errc();
}
auto &cf = cif::compound_factory::instance(); bool is_valid_pdbx_file(const file &file, std::error_code &ec)
auto &validator = cif::validator_factory::instance().operator[](dictionary); {
bool result = false;
if (file.empty())
ec = make_error_code(validation_error::empty_file);
else
{
std::string dictionary = "mmcif_pdbx";
for (auto &db : file)
{
auto audit_conform = db.get("audit_conform");
if (audit_conform == nullptr)
continue;
if (not audit_conform->empty())
{
auto specified_dict = audit_conform->front()["dict_name"];
if (not specified_dict.empty())
dictionary = specified_dict.as<std::string>();
}
break;
}
result = is_valid_pdbx_file(file, dictionary, ec);
}
return result;
}
bool is_valid_pdbx_file(const file &file, std::string_view dictionary, std::error_code &ec)
{
using namespace cif::literals;
bool result = true; bool result = true;
try try
{ {
auto &cf = cif::compound_factory::instance();
auto &validator = cif::validator_factory::instance().operator[](dictionary);
if (file.empty()) if (file.empty())
throw validation_error("Empty file"); throw std::runtime_error("Empty file");
auto &db = file.front(); auto &db = file.front();
if (db.empty()) if (db.empty())
throw validation_error("Empty datablock"); throw std::runtime_error("Empty datablock");
auto &atom_site = db["atom_site"]; auto &atom_site = db["atom_site"];
if (atom_site.empty()) if (atom_site.empty())
throw validation_error("Empty or missing atom_site category"); throw std::runtime_error("Empty or missing atom_site category");
auto &pdbx_poly_seq_scheme = db["pdbx_poly_seq_scheme"]; auto &pdbx_poly_seq_scheme = db["pdbx_poly_seq_scheme"];
...@@ -106,34 +145,38 @@ bool is_valid_pdbx_file(const file &file, std::string_view dictionary) ...@@ -106,34 +145,38 @@ bool is_valid_pdbx_file(const file &file, std::string_view dictionary)
last_seq_id = *seq_id; last_seq_id = *seq_id;
auto comp_id = r.get<std::string>("label_comp_id"); auto comp_id = r.get<std::string>("label_comp_id");
if (not cf.is_known_peptide(comp_id)) if (not cf.is_monomer(comp_id))
continue; continue;
auto p = pdbx_poly_seq_scheme.find(get_parents_condition(validator, r, pdbx_poly_seq_scheme)); auto p = pdbx_poly_seq_scheme.find(get_parents_condition(validator, r, pdbx_poly_seq_scheme));
if (p.size() != 1) if (p.size() != 1)
throw validation_error("For each residue in atom_site that is a residue in a polymer there should be exactly one pdbx_poly_seq_scheme record"); {
if (cif::VERBOSE > 0)
std::clog << "In atom_site record: " << r["id"].text() << '\n';
throw std::runtime_error("For each monomer in atom_site there should be exactly one pdbx_poly_seq_scheme record");
}
} }
auto &entity = db["entity"]; auto &entity = db["entity"];
if (entity.empty()) if (entity.empty())
throw validation_error("Entity category is missing or empty"); throw std::runtime_error("Entity category is missing or empty");
auto &entity_poly = db["entity_poly"]; auto &entity_poly = db["entity_poly"];
if (entity_poly.empty()) if (entity_poly.empty())
throw validation_error("Entity_poly category is missing or empty"); throw std::runtime_error("Entity_poly category is missing or empty");
auto &entity_poly_seq = db["entity_poly_seq"]; auto &entity_poly_seq = db["entity_poly_seq"];
if (entity_poly_seq.empty()) if (entity_poly_seq.empty())
throw validation_error("Entity_poly_seq category is missing or empty"); throw std::runtime_error("Entity_poly_seq category is missing or empty");
auto &struct_asym = db["struct_asym"]; auto &struct_asym = db["struct_asym"];
if (struct_asym.empty()) if (struct_asym.empty())
throw validation_error("struct_asym category is missing or empty"); throw std::runtime_error("struct_asym category is missing or empty");
for (auto entity_id : entity.find<std::string>("type"_key == "polymer", "id")) for (auto entity_id : entity.find<std::string>("type"_key == "polymer", "id"))
{ {
if (entity_poly.count("entity_id"_key == entity_id) != 1) if (entity_poly.count("entity_id"_key == entity_id) != 1)
throw validation_error("There should be exactly one entity_poly record per polymer entity"); throw std::runtime_error("There should be exactly one entity_poly record per polymer entity");
const auto entity_poly_type = entity_poly.find1<std::string>("entity_id"_key == entity_id, "type"); const auto entity_poly_type = entity_poly.find1<std::string>("entity_id"_key == entity_id, "type");
...@@ -151,7 +194,7 @@ bool is_valid_pdbx_file(const file &file, std::string_view dictionary) ...@@ -151,7 +194,7 @@ bool is_valid_pdbx_file(const file &file, std::string_view dictionary)
"seq_id"_key == num and "seq_id"_key == num and
"hetero"_key == hetero) != 1) "hetero"_key == hetero) != 1)
{ {
throw validation_error("For each entity_poly_seq record there should be exactly one pdbx_poly_seq record"); throw std::runtime_error("For each entity_poly_seq record there should be exactly one pdbx_poly_seq record");
} }
} }
} }
...@@ -163,11 +206,11 @@ bool is_valid_pdbx_file(const file &file, std::string_view dictionary) ...@@ -163,11 +206,11 @@ bool is_valid_pdbx_file(const file &file, std::string_view dictionary)
"num"_key == seq_id and "num"_key == seq_id and
"hetero"_key == hetero) != 1) "hetero"_key == hetero) != 1)
{ {
throw validation_error("For each pdbx_poly_seq/struct_asym record there should be exactly one entity_poly_seq record"); throw std::runtime_error("For each pdbx_poly_seq/struct_asym record there should be exactly one entity_poly_seq record");
} }
if ((mon_per_seq_id[seq_id].size() > 1) != hetero) if ((mon_per_seq_id[seq_id].size() > 1) != hetero)
throw validation_error("Mismatch between the hetero flag in the poly seq schemes and the number residues per seq_id"); throw std::runtime_error("Mismatch between the hetero flag in the poly seq schemes and the number residues per seq_id");
} }
for (const auto &[seq_id, mon_ids] : mon_per_seq_id) for (const auto &[seq_id, mon_ids] : mon_per_seq_id)
...@@ -183,8 +226,8 @@ bool is_valid_pdbx_file(const file &file, std::string_view dictionary) ...@@ -183,8 +226,8 @@ bool is_valid_pdbx_file(const file &file, std::string_view dictionary)
"label_asym_id"_key == asym_id and "label_asym_id"_key == asym_id and
"label_seq_id"_key == seq_id and not std::move(cond); "label_seq_id"_key == seq_id and not std::move(cond);
if (atom_site.exists(std::move(cond))) if (atom_site.contains(std::move(cond)))
throw validation_error("An atom_site record exists that has no parent in the poly seq scheme categories"); throw std::runtime_error("An atom_site record exists that has no parent in the poly seq scheme categories");
} }
} }
...@@ -205,13 +248,12 @@ bool is_valid_pdbx_file(const file &file, std::string_view dictionary) ...@@ -205,13 +248,12 @@ bool is_valid_pdbx_file(const file &file, std::string_view dictionary)
for (auto comp_id : comp_ids) for (auto comp_id : comp_ids)
{ {
std::string letter; std::string letter;
if (cf.is_known_base(comp_id))
letter = compound_factory::kBaseMap.at(comp_id); if (can)
else if (cf.is_known_peptide(comp_id))
letter = compound_factory::kAAMap.at(comp_id);
else
{ {
if (can) if (compound_factory::kBaseMap.contains(comp_id))
letter = compound_factory::kBaseMap.at(comp_id);
else
{ {
auto c = cf.create(comp_id); auto c = cf.create(comp_id);
if (c and c->one_letter_code()) if (c and c->one_letter_code())
...@@ -219,6 +261,13 @@ bool is_valid_pdbx_file(const file &file, std::string_view dictionary) ...@@ -219,6 +261,13 @@ bool is_valid_pdbx_file(const file &file, std::string_view dictionary)
else else
letter = "X"; letter = "X";
} }
}
else
{
if (compound_factory::kAAMap.contains(comp_id))
letter = compound_factory::kAAMap.at(comp_id);
else if (comp_id.length() == 1 and compound_factory::kBaseMap.contains(comp_id))
letter = compound_factory::kBaseMap.at(comp_id);
else else
letter = '(' + comp_id + ')'; letter = '(' + comp_id + ')';
} }
...@@ -250,7 +299,7 @@ bool is_valid_pdbx_file(const file &file, std::string_view dictionary) ...@@ -250,7 +299,7 @@ bool is_valid_pdbx_file(const file &file, std::string_view dictionary)
seq->erase(std::remove_if(seq->begin(), seq->end(), [](char ch) { return std::isspace(ch); }), seq->end()); seq->erase(std::remove_if(seq->begin(), seq->end(), [](char ch) { return std::isspace(ch); }), seq->end());
if (not seq_match(false, seq->begin(), seq->end())) if (not seq_match(false, seq->begin(), seq->end()))
throw validation_error("Sequences do not match for entity " + entity_id); throw std::runtime_error("Sequences do not match for entity " + entity_id);
} }
if (not seq_can.has_value()) if (not seq_can.has_value())
...@@ -261,11 +310,10 @@ bool is_valid_pdbx_file(const file &file, std::string_view dictionary) ...@@ -261,11 +310,10 @@ bool is_valid_pdbx_file(const file &file, std::string_view dictionary)
else else
{ {
seq_can->erase(std::remove_if(seq_can->begin(), seq_can->end(), [](char ch) { return std::isspace(ch); }), seq_can->end()); seq_can->erase(std::remove_if(seq_can->begin(), seq_can->end(), [](char ch) { return std::isspace(ch); }), seq_can->end());
if (not seq_match(true, seq_can->begin(), seq_can->end())) if (not seq_match(true, seq_can->begin(), seq_can->end()))
throw validation_error("Canonical sequences do not match for entity " + entity_id); throw std::runtime_error("Canonical sequences do not match for entity " + entity_id);
} }
} }
result = true; result = true;
...@@ -275,8 +323,12 @@ bool is_valid_pdbx_file(const file &file, std::string_view dictionary) ...@@ -275,8 +323,12 @@ bool is_valid_pdbx_file(const file &file, std::string_view dictionary)
result = false; result = false;
if (cif::VERBOSE > 0) if (cif::VERBOSE > 0)
std::clog << ex.what() << '\n'; std::clog << ex.what() << '\n';
ec = make_error_code(validation_error::not_valid_pdbx);
} }
if (not result and ec == std::errc())
ec = make_error_code(validation_error::not_valid_pdbx);
return result; return result;
} }
......
...@@ -29,44 +29,44 @@ ...@@ -29,44 +29,44 @@
namespace cif namespace cif
{ {
void row_handle::assign(uint16_t column, std::string_view value, bool updateLinked, bool validate) void row_handle::assign(uint16_t item, std::string_view value, bool updateLinked, bool validate)
{ {
if (not m_category) if (not m_category)
throw std::runtime_error("uninitialized row"); throw std::runtime_error("uninitialized row");
m_category->update_value(m_row, column, value, updateLinked, validate); m_category->update_value(m_row, item, value, updateLinked, validate);
} }
uint16_t row_handle::get_column_ix(std::string_view name) const uint16_t row_handle::get_item_ix(std::string_view name) const
{ {
if (not m_category) if (not m_category)
throw std::runtime_error("uninitialized row"); throw std::runtime_error("uninitialized row");
return m_category->get_column_ix(name); return m_category->get_item_ix(name);
} }
std::string_view row_handle::get_column_name(uint16_t ix) const std::string_view row_handle::get_item_name(uint16_t ix) const
{ {
if (not m_category) if (not m_category)
throw std::runtime_error("uninitialized row"); throw std::runtime_error("uninitialized row");
return m_category->get_column_name(ix); return m_category->get_item_name(ix);
} }
uint16_t row_handle::add_column(std::string_view name) uint16_t row_handle::add_item(std::string_view name)
{ {
if (not m_category) if (not m_category)
throw std::runtime_error("uninitialized row"); throw std::runtime_error("uninitialized row");
return m_category->add_column(name); return m_category->add_item(name);
} }
void row_handle::swap(uint16_t column, row_handle &b) void row_handle::swap(uint16_t item, row_handle &b)
{ {
if (not m_category) if (not m_category)
throw std::runtime_error("uninitialized row"); throw std::runtime_error("uninitialized row");
m_category->swap_item(column, *this, b); m_category->swap_item(item, *this, b);
} }
// -------------------------------------------------------------------- // --------------------------------------------------------------------
...@@ -86,7 +86,7 @@ row_initializer::row_initializer(row_handle rh) ...@@ -86,7 +86,7 @@ row_initializer::row_initializer(row_handle rh)
auto &i = r->operator[](ix); auto &i = r->operator[](ix);
if (not i) if (not i)
continue; continue;
emplace_back(cat.get_column_name(ix), i.text()); emplace_back(cat.get_item_name(ix), i.text());
} }
} }
......
...@@ -39,39 +39,47 @@ ...@@ -39,39 +39,47 @@
// the code will use boost::regex instead. // the code will use boost::regex instead.
#if USE_BOOST_REGEX #if USE_BOOST_REGEX
#include <boost/regex.hpp> # include <boost/regex.hpp>
using boost::regex; using boost::regex;
#else #else
#include <regex> # include <regex>
using std::regex; using std::regex;
#endif #endif
namespace cif namespace cif
{ {
struct regex_impl : public regex validation_exception::validation_exception(std::error_code ec)
: runtime_error(ec.message())
{ {
regex_impl(std::string_view rx) }
: regex(rx.begin(), rx.end(), regex::extended | regex::optimize)
{
}
};
validation_error::validation_error(const std::string &msg) validation_exception::validation_exception(std::error_code ec, std::string_view category)
: m_msg(msg) : runtime_error((ec.message() + "; category: ").append(category))
{ {
} }
validation_error::validation_error(const std::string &cat, const std::string &item, const std::string &msg) validation_exception::validation_exception(std::error_code ec, std::string_view category, std::string_view item)
: m_msg("When validating _" + cat + '.' + item + ": " + msg) : runtime_error((ec.message() + "; category: ").append(category).append("; item: ").append(item))
{ {
} }
// -------------------------------------------------------------------- // --------------------------------------------------------------------
DDL_PrimitiveType map_to_primitive_type(std::string_view s) struct regex_impl : public regex
{
regex_impl(std::string_view rx)
: regex(rx.begin(), rx.end(), regex::extended | regex::optimize)
{
}
};
// --------------------------------------------------------------------
DDL_PrimitiveType map_to_primitive_type(std::string_view s, std::error_code &ec) noexcept
{ {
DDL_PrimitiveType result; ec = {};
DDL_PrimitiveType result = DDL_PrimitiveType::Char;
if (iequals(s, "char")) if (iequals(s, "char"))
result = DDL_PrimitiveType::Char; result = DDL_PrimitiveType::Char;
else if (iequals(s, "uchar")) else if (iequals(s, "uchar"))
...@@ -79,7 +87,16 @@ DDL_PrimitiveType map_to_primitive_type(std::string_view s) ...@@ -79,7 +87,16 @@ DDL_PrimitiveType map_to_primitive_type(std::string_view s)
else if (iequals(s, "numb")) else if (iequals(s, "numb"))
result = DDL_PrimitiveType::Numb; result = DDL_PrimitiveType::Numb;
else else
throw validation_error("Not a known primitive type"); ec = make_error_code(validation_error::not_a_known_primitive_type);
return result;
}
DDL_PrimitiveType map_to_primitive_type(std::string_view s)
{
std::error_code ec;
auto result = map_to_primitive_type(s, ec);
if (ec)
throw std::system_error(ec, std::string{ s });
return result; return result;
} }
...@@ -196,63 +213,72 @@ int type_validator::compare(std::string_view a, std::string_view b) const ...@@ -196,63 +213,72 @@ int type_validator::compare(std::string_view a, std::string_view b) const
// -------------------------------------------------------------------- // --------------------------------------------------------------------
// void ValidateItem::addLinked(ValidateItem* parent, const std::string& parentItem, const std::string& childItem)
//{
//// if (mParent != nullptr and VERBOSE)
//// cerr << "replacing parent in " << mCategory->m_name << " from " << mParent->mCategory->m_name << " to " << parent->mCategory->m_name << endl;
//// mParent = parent;
//
// if (m_type == nullptr and parent != nullptr)
// m_type = parent->m_type;
//
// if (parent != nullptr)
// {
// mLinked.push_back({parent, parentItem, childItem});
//
// parent->mChildren.insert(this);
////
//// if (mCategory->mKeys == std::vector<std::string>{mTag})
//// parent->mForeignKeys.insert(this);
// }
//}
void item_validator::operator()(std::string_view value) const void item_validator::operator()(std::string_view value) const
{ {
std::error_code ec;
if (not validate_value(value, ec))
throw std::system_error(ec, std::string{ value } + " does not match rx for " + m_item_name);
}
bool item_validator::validate_value(std::string_view value, std::error_code &ec) const noexcept
{
ec = {};
if (not value.empty() and value != "?" and value != ".") if (not value.empty() and value != "?" and value != ".")
{ {
if (m_type != nullptr and not regex_match(value.begin(), value.end(), *m_type->m_rx)) if (m_type != nullptr and not regex_match(value.begin(), value.end(), *m_type->m_rx))
throw validation_error(m_category->m_name, m_tag, "Value '" + std::string{ value } + "' does not match type expression for type " + m_type->m_name); 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)
if (not m_enums.empty()) ec = make_error_code(validation_error::value_is_not_in_enumeration_list);
{
if (m_enums.count(std::string{ value }) == 0)
throw validation_error(m_category->m_name, m_tag, "Value '" + std::string{ value } + "' is not in the list of allowed values");
}
} }
return ec == std::errc();
} }
// -------------------------------------------------------------------- // --------------------------------------------------------------------
void category_validator::addItemValidator(item_validator &&v) void category_validator::add_item_validator(item_validator &&v)
{ {
if (v.m_mandatory) if (v.m_mandatory)
m_mandatory_fields.insert(v.m_tag); m_mandatory_items.insert(v.m_item_name);
v.m_category = this; v.m_category = this;
auto r = m_item_validators.insert(std::move(v)); auto r = m_item_validators.insert(std::move(v));
if (not r.second and VERBOSE >= 4) if (not r.second and VERBOSE >= 4)
std::cout << "Could not add validator for item " << v.m_tag << " to category " << m_name << '\n'; std::cout << "Could not add validator for item " << v.m_item_name << " to category " << m_name << '\n';
} }
const item_validator *category_validator::get_validator_for_item(std::string_view tag) const const item_validator *category_validator::get_validator_for_item(std::string_view item_name) const
{ {
const item_validator *result = nullptr; const item_validator *result = nullptr;
auto i = m_item_validators.find(item_validator{ std::string(tag) }); auto i = m_item_validators.find(item_validator{ std::string(item_name) });
if (i != m_item_validators.end()) if (i != m_item_validators.end())
result = &*i; result = &*i;
else if (VERBOSE > 4) else if (VERBOSE > 4)
std::cout << "No validator for tag " << tag << '\n'; std::cout << "No validator for item " << item_name << '\n';
return result;
}
const item_validator *category_validator::get_validator_for_aliased_item(std::string_view item_name) const
{
const item_validator *result = nullptr;
for (auto &iv : m_item_validators)
{
for (auto &ai : iv.m_aliases)
{
const auto &[cat, name] = split_item_name(ai.m_name);
if (iequals(name, item_name) and iequals(cat, m_name))
{
result = &iv;
break;
}
}
if (result)
break;
}
return result; return result;
} }
...@@ -295,19 +321,19 @@ const category_validator *validator::get_validator_for_category(std::string_view ...@@ -295,19 +321,19 @@ const category_validator *validator::get_validator_for_category(std::string_view
return result; return result;
} }
item_validator *validator::get_validator_for_item(std::string_view tag) const item_validator *validator::get_validator_for_item(std::string_view item_name) const
{ {
item_validator *result = nullptr; item_validator *result = nullptr;
std::string cat, item; std::string cat, item;
std::tie(cat, item) = split_tag_name(tag); std::tie(cat, item) = split_item_name(item_name);
auto *cv = get_validator_for_category(cat); auto *cv = get_validator_for_category(cat);
if (cv != nullptr) if (cv != nullptr)
result = const_cast<item_validator *>(cv->get_validator_for_item(item)); result = const_cast<item_validator *>(cv->get_validator_for_item(item));
if (result == nullptr and VERBOSE > 4) if (result == nullptr and VERBOSE > 4)
std::cout << "No validator for item " << tag << '\n'; std::cout << "No validator for item " << item_name << '\n';
return result; return result;
} }
...@@ -332,11 +358,11 @@ void validator::add_link_validator(link_validator &&v) ...@@ -332,11 +358,11 @@ void validator::add_link_validator(link_validator &&v)
auto piv = pcv->get_validator_for_item(v.m_parent_keys[i]); auto piv = pcv->get_validator_for_item(v.m_parent_keys[i]);
if (piv == nullptr) if (piv == nullptr)
throw std::runtime_error("unknown parent tag _" + v.m_parent_category + '.' + v.m_parent_keys[i]); throw std::runtime_error("unknown parent item _" + v.m_parent_category + '.' + v.m_parent_keys[i]);
auto civ = ccv->get_validator_for_item(v.m_child_keys[i]); auto civ = ccv->get_validator_for_item(v.m_child_keys[i]);
if (civ == nullptr) if (civ == nullptr)
throw std::runtime_error("unknown child tag _" + v.m_child_category + '.' + v.m_child_keys[i]); throw std::runtime_error("unknown child item _" + v.m_child_category + '.' + v.m_child_keys[i]);
if (civ->m_type == nullptr and piv->m_type != nullptr) if (civ->m_type == nullptr and piv->m_type != nullptr)
const_cast<item_validator *>(civ)->m_type = piv->m_type; const_cast<item_validator *>(civ)->m_type = piv->m_type;
...@@ -351,7 +377,7 @@ std::vector<const link_validator *> validator::get_links_for_parent(std::string_ ...@@ -351,7 +377,7 @@ std::vector<const link_validator *> validator::get_links_for_parent(std::string_
for (auto &l : m_link_validators) for (auto &l : m_link_validators)
{ {
if (l.m_parent_category == category) if (iequals(l.m_parent_category, category))
result.push_back(&l); result.push_back(&l);
} }
...@@ -364,19 +390,32 @@ std::vector<const link_validator *> validator::get_links_for_child(std::string_v ...@@ -364,19 +390,32 @@ std::vector<const link_validator *> validator::get_links_for_child(std::string_v
for (auto &l : m_link_validators) for (auto &l : m_link_validators)
{ {
if (l.m_child_category == category) if (iequals(l.m_child_category, category))
result.push_back(&l); result.push_back(&l);
} }
return result; return result;
} }
void validator::report_error(const std::string &msg, bool fatal) const void validator::report_error(std::error_code ec, bool fatal) const
{ {
if (m_strict or fatal) if (m_strict or fatal)
throw validation_error(msg); throw validation_exception(ec);
else if (VERBOSE > 0) else
std::cerr << msg << '\n'; std::cerr << ec.message() << '\n';
}
void validator::report_error(std::error_code ec, std::string_view category,
std::string_view item, bool fatal) const
{
auto ex = item.empty() ?
validation_exception(ec, category) :
validation_exception(ec, category, item);
if (m_strict or fatal)
throw ex;
else
std::cerr << ex.what() << '\n';
} }
// -------------------------------------------------------------------- // --------------------------------------------------------------------
...@@ -438,12 +477,12 @@ const validator &validator_factory::operator[](std::string_view dictionary_name) ...@@ -438,12 +477,12 @@ const validator &validator_factory::operator[](std::string_view dictionary_name)
if (not std::filesystem::exists(p, ec) or ec) if (not std::filesystem::exists(p, ec) or ec)
{ {
for (const char *dir : { for (const char *dir : {
#if defined(CACHE_DIR) # if defined(CACHE_DIR)
CACHE_DIR, CACHE_DIR,
#endif # endif
#if defined(DATA_DIR) # if defined(DATA_DIR)
DATA_DIR DATA_DIR
#endif # endif
}) })
{ {
auto p2 = std::filesystem::path(dir) / p; auto p2 = std::filesystem::path(dir) / p;
......
# We're using the older version 2 of Catch2
find_package(Catch2 QUIET)
if(NOT Catch2_FOUND)
FetchContent_Declare(
Catch2
GIT_REPOSITORY https://github.com/catchorg/Catch2.git
GIT_TAG v2.13.9)
FetchContent_MakeAvailable(Catch2)
set(Catch2_VERSION "2.13.9")
endif()
list(
APPEND
CIFPP_tests
unit-v2
unit-3d
format
model
rename-compound
sugar
spinner
reconstruction
validate-pdbx)
add_library(test-main OBJECT "${CMAKE_CURRENT_SOURCE_DIR}/test-main.cpp")
target_link_libraries(test-main cifpp::cifpp Catch2::Catch2)
if(${Catch2_VERSION} VERSION_GREATER_EQUAL 3.0.0)
target_compile_definitions(test-main PUBLIC CATCH22=0)
else()
target_compile_definitions(test-main PUBLIC CATCH22=1)
endif()
foreach(CIFPP_TEST IN LISTS CIFPP_tests)
set(CIFPP_TEST "${CIFPP_TEST}-test")
set(CIFPP_TEST_SOURCE "${CMAKE_CURRENT_SOURCE_DIR}/${CIFPP_TEST}.cpp")
add_executable(
${CIFPP_TEST} ${CIFPP_TEST_SOURCE} $<TARGET_OBJECTS:test-main>)
if(${Catch2_VERSION} VERSION_GREATER_EQUAL 3.0.0)
target_compile_definitions(${CIFPP_TEST} PUBLIC CATCH22=0)
else()
target_compile_definitions(${CIFPP_TEST} PUBLIC CATCH22=1)
endif()
target_link_libraries(${CIFPP_TEST} PRIVATE Threads::Threads cifpp::cifpp
Catch2::Catch2)
target_include_directories(${CIFPP_TEST} PRIVATE "${EIGEN_INCLUDE_DIR}")
if(MSVC)
# Specify unwind semantics so that MSVC knowns how to handle exceptions
target_compile_options(${CIFPP_TEST} PRIVATE /EHsc)
endif()
add_custom_target(
"run-${CIFPP_TEST}"
DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/Run${CIFPP_TEST}.touch ${CIFPP_TEST})
add_custom_command(
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/Run${CIFPP_TEST}.touch
COMMAND $<TARGET_FILE:${CIFPP_TEST}> --data-dir
${CMAKE_CURRENT_SOURCE_DIR})
add_test(NAME ${CIFPP_TEST} COMMAND $<TARGET_FILE:${CIFPP_TEST}> --data-dir
${CMAKE_CURRENT_SOURCE_DIR})
endforeach()
\ No newline at end of file
...@@ -24,7 +24,8 @@ ...@@ -24,7 +24,8 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
#include <catch2/catch.hpp>
#include "test-main.hpp"
#include <stdexcept> #include <stdexcept>
......
...@@ -26,8 +26,6 @@ ...@@ -26,8 +26,6 @@
#include "test-main.hpp" #include "test-main.hpp"
#include <catch2/catch.hpp>
#include <stdexcept> #include <stdexcept>
#include <cif++.hpp> #include <cif++.hpp>
......
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
/*-
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2024 NKI/AVL, Netherlands Cancer Institute
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "test-main.hpp"
#include <cif++.hpp>
#include <iostream>
#include <fstream>
TEST_CASE("reconstruct")
{
cif::compound_factory::instance().push_dictionary(gTestDir / "REA.cif");
for (std::filesystem::directory_iterator i(gTestDir / "reconstruct"); i != std::filesystem::directory_iterator{}; ++i)
{
std::cout << i->path() << '\n';
if (i->path().extension() == ".pdb")
{
cif::file f = cif::pdb::read(i->path());
std::error_code ec;
if (not cif::pdb::is_valid_pdbx_file(f, ec))
CHECK(cif::pdb::reconstruct_pdbx(f));
}
else
{
cif::file f(i->path());
std::error_code ec;
CHECK_FALSE(cif::pdb::is_valid_pdbx_file(f, ec));
CHECK(ec != std::errc{});
CHECK(cif::pdb::reconstruct_pdbx(f));
}
}
}
\ No newline at end of file
...@@ -28,8 +28,6 @@ ...@@ -28,8 +28,6 @@
#include <cif++.hpp> #include <cif++.hpp>
#include <catch2/catch.hpp>
#include <iostream> #include <iostream>
#include <fstream> #include <fstream>
......
#include "cif++/utilities.hpp" /*-
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2024 NKI/AVL, Netherlands Cancer Institute
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "test-main.hpp"
#include <catch2/catch.hpp> #include "cif++/utilities.hpp"
#include <random> #include <random>
#include <thread> #include <thread>
......
...@@ -26,8 +26,6 @@ ...@@ -26,8 +26,6 @@
#include "test-main.hpp" #include "test-main.hpp"
#include <catch2/catch.hpp>
#include <stdexcept> #include <stdexcept>
#include <cif++.hpp> #include <cif++.hpp>
......
#define CATCH_CONFIG_RUNNER 1
#include "test-main.hpp" #include "test-main.hpp"
#include <cif++.hpp> #include <cif++.hpp>
#define CATCH_CONFIG_RUNNER
#include <catch2/catch.hpp>
std::filesystem::path gTestDir = std::filesystem::current_path(); std::filesystem::path gTestDir = std::filesystem::current_path();
int main(int argc, char *argv[]) int main(int argc, char *argv[])
...@@ -12,7 +11,13 @@ int main(int argc, char *argv[]) ...@@ -12,7 +11,13 @@ int main(int argc, char *argv[])
Catch::Session session; // There must be exactly one instance Catch::Session session; // There must be exactly one instance
// Build a new parser on top of Catch2's // Build a new parser on top of Catch2's
#if CATCH22
using namespace Catch::clara; using namespace Catch::clara;
#else
// Build a new parser on top of Catch2's
using namespace Catch::Clara;
#endif
auto cli = session.cli() // Get Catch2's command line parser auto cli = session.cli() // Get Catch2's command line parser
| Opt(gTestDir, "data-dir") // bind variable to a new option, with a hint string | Opt(gTestDir, "data-dir") // bind variable to a new option, with a hint string
["-D"]["--data-dir"] // the option names it will respond to ["-D"]["--data-dir"] // the option names it will respond to
......
/*-
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2024 NKI/AVL, Netherlands Cancer Institute
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#pragma once
#if CATCH22
#include <catch2/catch.hpp>
#else
#include <catch2/catch_all.hpp>
#endif
#include <filesystem> #include <filesystem>
extern std::filesystem::path gTestDir; extern std::filesystem::path gTestDir;
...@@ -26,8 +26,6 @@ ...@@ -26,8 +26,6 @@
#include "test-main.hpp" #include "test-main.hpp"
#include <catch2/catch.hpp>
#include <stdexcept> #include <stdexcept>
#include <cif++.hpp> #include <cif++.hpp>
......
...@@ -26,8 +26,6 @@ ...@@ -26,8 +26,6 @@
#include "test-main.hpp" #include "test-main.hpp"
#include <catch2/catch.hpp>
#include <cif++.hpp> #include <cif++.hpp>
#include "cif++/dictionary_parser.hpp" #include "cif++/dictionary_parser.hpp"
...@@ -568,7 +566,7 @@ _test.value ...@@ -568,7 +566,7 @@ _test.value
auto &test = db["test"]; auto &test = db["test"];
REQUIRE(test.size() == 5); REQUIRE(test.size() == 5);
REQUIRE(test.exists("value"_key == cif::null)); REQUIRE(test.contains("value"_key == cif::null));
REQUIRE(test.find("value"_key == cif::null).size() == 2); REQUIRE(test.find("value"_key == cif::null).size() == 2);
} }
...@@ -746,27 +744,51 @@ _cat_2.desc ...@@ -746,27 +744,51 @@ _cat_2.desc
std::istream is_data(&data_buffer); std::istream is_data(&data_buffer);
f.load(is_data); f.load(is_data);
auto &cat1 = f.front()["cat_1"]; SECTION("one")
auto &cat2 = f.front()["cat_2"]; {
auto &cat1 = f.front()["cat_1"];
auto &cat2 = f.front()["cat_2"];
REQUIRE(cat1.size() == 3); REQUIRE(cat1.size() == 3);
REQUIRE(cat2.size() == 3); REQUIRE(cat2.size() == 3);
cat1.erase(cif::key("id") == 1); cat1.erase(cif::key("id") == 1);
REQUIRE(cat1.size() == 2); REQUIRE(cat1.size() == 2);
REQUIRE(cat2.size() == 1); REQUIRE(cat2.size() == 1);
// REQUIRE_THROWS_AS(cat2.emplace({ // REQUIRE_THROWS_AS(cat2.emplace({
// { "id", 4 }, // { "id", 4 },
// { "parent_id", 4 }, // { "parent_id", 4 },
// { "desc", "moet fout gaan" } // { "desc", "moet fout gaan" }
// }), std::exception); // }), std::exception);
REQUIRE_THROWS_AS(cat2.emplace({ { "id", "vijf" }, // <- invalid value REQUIRE_THROWS_AS(cat2.emplace({ { "id", "vijf" }, // <- invalid value
{ "parent_id", 2 }, { "parent_id", 2 },
{ "desc", "moet fout gaan" } }), { "desc", "moet fout gaan" } }),
std::exception); std::exception);
}
// SECTION("two")
// {
// auto &cat1 = f.front()["cat_1"];
// auto &cat2 = f.front()["cat_2"];
// cat1.update_value(cif::all(), "id", [](std::string_view v) -> std::string
// {
// int vi;
// auto [ec, ptr] = std::from_chars(v.data(), v.data() + v.length(), vi);
// return std::to_string(vi + 1);
// });
// REQUIRE(cat1.find1<std::string>(cif::key("id") == 2, "name") == "Aap");
// REQUIRE(cat1.find1<std::string>(cif::key("id") == 3, "name") == "Noot");
// REQUIRE(cat1.find1<std::string>(cif::key("id") == 4, "name") == "Mies");
// REQUIRE(cat2.find1<int>(cif::key("id") == 1, "parent_id") == 2);
// REQUIRE(cat2.find1<int>(cif::key("id") == 2, "parent_id") == 2);
// REQUIRE(cat2.find1<int>(cif::key("id") == 3, "parent_id") == 3);
// }
} }
// -------------------------------------------------------------------- // --------------------------------------------------------------------
...@@ -3486,4 +3508,11 @@ TEST_CASE("pdb2cif_formula_weight") ...@@ -3486,4 +3508,11 @@ TEST_CASE("pdb2cif_formula_weight")
fw = a.front()["entity"].find1<float>(cif::key("id") == 3, "formula_weight"); fw = a.front()["entity"].find1<float>(cif::key("id") == 3, "formula_weight");
CHECK(fw == 18.015f); CHECK(fw == 18.015f);
}
// --------------------------------------------------------------------
TEST_CASE("update_values_with_provider")
{
} }
\ No newline at end of file
...@@ -26,8 +26,6 @@ ...@@ -26,8 +26,6 @@
#include "test-main.hpp" #include "test-main.hpp"
#include <catch2/catch.hpp>
#include <cif++.hpp> #include <cif++.hpp>
#include <stdexcept> #include <stdexcept>
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment