Compare commits

..

23 Commits

Author SHA1 Message Date
Maarten L. Hekkelman
446438bf8c Fixing pdb2cif, and sequence checking 2024-01-29 16:12:01 +01:00
Maarten L. Hekkelman
4e012cbd48 gcc diagnostics and clipper 2024-01-29 16:11:08 +01:00
Maarten L. Hekkelman
12ee4a792c pdb2cif work 2024-01-29 13:00:50 +01:00
Maarten L. Hekkelman
e59750386f - changed compound::is_known_peptide/is_know_base
- Add audit_conform only if file is really valid
- Added reconstruction code for PDBx
2024-01-24 16:14:08 +01:00
Maarten L. Hekkelman
4e19d54867 atom_site_anisotrop check 2024-01-23 14:08:25 +01:00
Maarten L. Hekkelman
db603e5438 version bump 2024-01-23 11:57:29 +01:00
Maarten L. Hekkelman
5320cb123a Fix validate pdbx 2024-01-23 11:57:21 +01:00
Maarten L. Hekkelman
30a2ebdbb4 Merge remote-tracking branch 'github/develop-cif2fasta' into develop 2024-01-23 11:42:12 +01:00
Maarten L. Hekkelman
a5d43998a3 replace tag with item or item_name 2024-01-23 11:41:13 +01:00
Maarten L. Hekkelman
2792caec70 renaming field and column to item 2024-01-23 11:23:20 +01:00
Maarten L. Hekkelman
fb2b1e984c Fixed serious bug in emplace of both datablock and file. 2024-01-22 16:04:57 +01:00
Maarten L. Hekkelman
13ab1caf95 macOS... 2024-01-22 15:33:43 +01:00
Maarten L. Hekkelman
5d4534fac4 Added rename_column
Added item_alias
Rename columns in reconstruct
2024-01-22 15:15:11 +01:00
Maarten L. Hekkelman
f450643861 Added remove column 2024-01-22 14:03:22 +01:00
Maarten L. Hekkelman
fc14a65511 attempt 2 to build on macOS 2024-01-22 13:08:25 +01:00
Maarten L. Hekkelman
bbd1e27c5e Attempt to get code compiling on macOS 2024-01-22 12:49:11 +01:00
Maarten L. Hekkelman
369a83b718 Version bump 2024-01-22 12:00:38 +01:00
Maarten L. Hekkelman
afc541b956 Merge remote-tracking branch 'github/develop-cif2fasta' into develop 2024-01-22 12:00:11 +01:00
Maarten L. Hekkelman
7e4d2ffb4d formatting data in reconstruction 2024-01-22 11:43:56 +01:00
Maarten L. Hekkelman
e09913a94f - checking and optionally dropping ndb_poly_seq_scheme
- fix in iterator_proxy
2024-01-22 11:18:00 +01:00
Maarten L. Hekkelman
b4d1c4cc04 - renamed exists to contains
- fix compare for ints where item is empty
2024-01-17 16:31:18 +01:00
Maarten L. Hekkelman
22537c0e7e Merge branch 'develop' into trunk 2024-01-09 16:45:46 +01:00
Maarten L. Hekkelman
39c0db8d6a Do not validate on copy category 2024-01-09 16:29:53 +01:00
41 changed files with 20121 additions and 1076 deletions

3
.gitignore vendored
View File

@@ -11,4 +11,5 @@ Testing/
include/cif++/exports.hpp
docs/api
docs/conf.py
build_ci/
build_ci/
data/components.cif

View File

@@ -27,7 +27,7 @@ cmake_minimum_required(VERSION 3.16)
# set the project name
project(
libcifpp
VERSION 6.1.0
VERSION 7.0.0
LANGUAGES CXX)
list(PREPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
@@ -564,6 +564,7 @@ if(BUILD_TESTING)
rename-compound
sugar
spinner
reconstruction
validate-pdbx)
foreach(CIFPP_TEST IN LISTS CIFPP_tests)

View File

@@ -1,3 +1,12 @@
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
- Add formula weight to entity in pdb2cif
- Change order of categories inside a datablock to match order in file

View File

@@ -31,22 +31,22 @@
#include "cif++/condition.hpp"
#include "cif++/iterator.hpp"
#include "cif++/row.hpp"
#include "cif++/validate.hpp"
#include "cif++/text.hpp"
#include "cif++/validate.hpp"
#include <array>
/** \file category.hpp
* Documentation for the cif::category class
*
* The category class should meet the requirements of Container and
* SequenceContainer.
*
* TODO: implement all of:
* https://en.cppreference.com/w/cpp/named_req/Container
* https://en.cppreference.com/w/cpp/named_req/SequenceContainer
* and more?
*/
* Documentation for the cif::category class
*
* The category class should meet the requirements of Container and
* SequenceContainer.
*
* TODO: implement all of:
* https://en.cppreference.com/w/cpp/named_req/Container
* https://en.cppreference.com/w/cpp/named_req/SequenceContainer
* and more?
*/
namespace cif
{
@@ -61,23 +61,43 @@ namespace cif
class duplicate_key_error : public std::runtime_error
{
public:
/**
* @brief Construct a new duplicate key error object
*/
/**
* @brief Construct a new duplicate key error object
*/
duplicate_key_error(const std::string &msg)
: std::runtime_error(msg)
{
}
};
/// @brief A missing_key_error is thrown when an attempt is made
/// to create an index when one of the key items is missing.
class missing_key_error : public std::runtime_error
{
public:
/**
* @brief Construct a new duplicate key error object
*/
missing_key_error(const std::string &msg, const std::string &key)
: std::runtime_error(msg)
, m_key(key)
{
}
const std::string &get_key() const noexcept { return m_key; }
private:
std::string m_key;
};
/// @brief A multiple_results_error is throw when you request a single
/// row using a query but the query contains more than exactly one row.
class multiple_results_error : public std::runtime_error
{
public:
/**
* @brief Construct a new multiple results error object
*/
/**
* @brief Construct a new multiple results error object
*/
multiple_results_error()
: std::runtime_error("query should have returned exactly one row")
{
@@ -136,8 +156,16 @@ class category
// --------------------------------------------------------------------
const std::string &name() const { return m_name; } ///< Returns the name of the category
iset key_fields() const; ///< Returns the cif::iset of key field names. Retrieved from the @ref category_validator for this category
std::set<uint16_t> key_field_indices() const; ///< Returns a set of indices for the key fields.
[[deprecated("use key_items instead")]]
iset key_fields() const; ///< Returns the cif::iset of key item names. Retrieved from the @ref category_validator for this category
iset key_items() const; ///< Returns the cif::iset of key item names. Retrieved from the @ref category_validator for this category
[[deprecated("use key_item_indices instead")]]
std::set<uint16_t> key_field_indices() const; ///< Returns a set of indices for the key items.
std::set<uint16_t> key_item_indices() const; ///< Returns a set of indices for the key items.
/// @brief Set the validator for this category to @a v
/// @param v The category_validator to assign. A nullptr value is allowed.
@@ -162,7 +190,7 @@ class category
/// @brief Validate links, that means, values in this category should have an
/// accompanying value in parent categories.
///
///
/// @note
/// The code makes one exception when validating missing links and that's between
/// *atom_site* and a parent *pdbx_poly_seq_scheme* or *entity_poly_seq*.
@@ -265,7 +293,7 @@ class category
/// Return the theoretical maximum number or rows that can be stored
size_t max_size() const
{
return std::numeric_limits<size_t>::max(); // this is a bit optimistic, I guess
return std::numeric_limits<size_t>::max(); // this is a bit optimistic, I guess
}
/// Return true if the category is empty
@@ -281,12 +309,12 @@ class category
using key_type = row_initializer;
/// @brief Return a row_handle for the row specified by \a key
/// @param key The value for the key, fields specified in the dictionary should have a value
/// @param key The value for the key, items specified in the dictionary should have a value
/// @return The row found in the index, or an undefined row_handle
row_handle operator[](const key_type &key);
/// @brief Return a const row_handle for the row specified by \a key
/// @param key The value for the key, fields specified in the dictionary should have a value
/// @param key The value for the key, items specified in the dictionary should have a value
/// @return The row found in the index, or an undefined row_handle
const row_handle operator[](const key_type &key) const
{
@@ -301,15 +329,15 @@ class category
/// @code{.cpp}
/// for (const auto &[name, value] : cat.rows<std::string,int>("item_name", "item_value"))
/// std::cout << name << ": " << value << '\n';
/// @endcode
/// @endcode
///
/// @tparam Ts The types for the columns requested
/// @param names The names for the columns requested
/// @tparam Ts The types for the items requested
/// @param names The names for the items requested
template <typename... Ts, typename... Ns>
iterator_proxy<const category, Ts...> rows(Ns... names) const
{
static_assert(sizeof...(Ts) == sizeof...(Ns), "The number of column titles should be equal to the number of types to return");
static_assert(sizeof...(Ts) == sizeof...(Ns), "The number of item names should be equal to the number of types to return");
return iterator_proxy<const category, Ts...>(*this, begin(), { names... });
}
@@ -320,19 +348,19 @@ class category
/// for (const auto &[name, value] : cat.rows<std::string,int>("item_name", "item_value"))
/// std::cout << name << ": " << value << '\n';
///
/// // or in case we only need one column:
/// // or in case we only need one item:
///
/// for (int id : cat.rows<int>("id"))
/// std::cout << id << '\n';
/// @endcode
/// @endcode
///
/// @tparam Ts The types for the columns requested
/// @param names The names for the columns requested
/// @tparam Ts The types for the items requested
/// @param names The names for the items requested
template <typename... Ts, typename... Ns>
iterator_proxy<category, Ts...> rows(Ns... names)
{
static_assert(sizeof...(Ts) == sizeof...(Ns), "The number of column titles should be equal to the number of types to return");
static_assert(sizeof...(Ts) == sizeof...(Ns), "The number of item names should be equal to the number of types to return");
return iterator_proxy<category, Ts...>(*this, begin(), { names... });
}
@@ -343,7 +371,7 @@ class category
/// @code{.cpp}
/// for (row_handle rh : cat.find(cif::key("first_name") == "John" and cif::key("last_name") == "Doe"))
/// .. // do something with rh
/// @endcode
/// @endcode
///
/// @param cond The condition for the query
/// @return A special iterator that loops over all elements that match. The iterator can be dereferenced
@@ -397,17 +425,17 @@ class category
/// @code{.cpp}
/// for (const auto &[name, value] : cat.find<std::string,int>(cif::key("item_value") > 10, "item_name", "item_value"))
/// std::cout << name << ": " << value << '\n';
/// @endcode
/// @endcode
///
/// @param cond The condition for the query
/// @tparam Ts The types for the columns requested
/// @param names The names for the columns requested
/// @tparam Ts The types for the items requested
/// @param names The names for the items requested
/// @return A special iterator that loops over all elements that match.
template <typename... Ts, typename... Ns>
conditional_iterator_proxy<category, Ts...> find(condition &&cond, Ns... names)
{
static_assert(sizeof...(Ts) == sizeof...(Ns), "The number of column titles should be equal to the number of types to return");
static_assert(sizeof...(Ts) == sizeof...(Ns), "The number of item names should be equal to the number of types to return");
return find<Ts...>(cbegin(), std::move(cond), std::forward<Ns>(names)...);
}
@@ -415,14 +443,14 @@ class category
/// iterator can be used in a structured binding context.
///
/// @param cond The condition for the query
/// @tparam Ts The types for the columns requested
/// @param names The names for the columns requested
/// @tparam Ts The types for the items requested
/// @param names The names for the items requested
/// @return A special iterator that loops over all elements that match.
template <typename... Ts, typename... Ns>
conditional_iterator_proxy<const category, Ts...> find(condition &&cond, Ns... names) const
{
static_assert(sizeof...(Ts) == sizeof...(Ns), "The number of column titles should be equal to the number of types to return");
static_assert(sizeof...(Ts) == sizeof...(Ns), "The number of item names should be equal to the number of types to return");
return find<Ts...>(cbegin(), std::move(cond), std::forward<Ns>(names)...);
}
@@ -431,14 +459,14 @@ class category
///
/// @param pos Iterator pointing to the location where to start
/// @param cond The condition for the query
/// @tparam Ts The types for the columns requested
/// @param names The names for the columns requested
/// @tparam Ts The types for the items requested
/// @param names The names for the items requested
/// @return A special iterator that loops over all elements that match.
template <typename... Ts, typename... Ns>
conditional_iterator_proxy<category, Ts...> find(const_iterator pos, condition &&cond, Ns... names)
{
static_assert(sizeof...(Ts) == sizeof...(Ns), "The number of column titles should be equal to the number of types to return");
static_assert(sizeof...(Ts) == sizeof...(Ns), "The number of item names should be equal to the number of types to return");
return { *this, pos, std::move(cond), std::forward<Ns>(names)... };
}
@@ -447,14 +475,14 @@ class category
///
/// @param pos Iterator pointing to the location where to start
/// @param cond The condition for the query
/// @tparam Ts The types for the columns requested
/// @param names The names for the columns requested
/// @tparam Ts The types for the items requested
/// @param names The names for the items requested
/// @return A special iterator that loops over all elements that match.
template <typename... Ts, typename... Ns>
conditional_iterator_proxy<const category, Ts...> find(const_iterator pos, condition &&cond, Ns... names) const
{
static_assert(sizeof...(Ts) == sizeof...(Ns), "The number of column titles should be equal to the number of types to return");
static_assert(sizeof...(Ts) == sizeof...(Ns), "The number of item names should be equal to the number of types to return");
return { *this, pos, std::move(cond), std::forward<Ns>(names)... };
}
@@ -509,30 +537,30 @@ class category
return *h.begin();
}
/// @brief Return value for the column named @a column for the single row that
/// @brief Return value for the item named @a item for the single row that
/// matches @a cond. Throws @a multiple_results_error if there are is not exactly one row
/// @tparam The type to use for the result
/// @param cond The condition to search for
/// @param column The name of the column to return the value for
/// @param item The name of the item to return the value for
/// @return The value found
template <typename T>
T find1(condition &&cond, const char *column) const
T find1(condition &&cond, std::string_view item) const
{
return find1<T>(cbegin(), std::move(cond), column);
return find1<T>(cbegin(), std::move(cond), item);
}
/// @brief Return value for the column named @a column for the single row that
/// @brief Return value for the item named @a item for the single row that
/// matches @a cond when starting to search at @a pos.
/// Throws @a multiple_results_error if there are is not exactly one row
/// @tparam The type to use for the result
/// @param pos The location to start the search
/// @param cond The condition to search for
/// @param column The name of the column to return the value for
/// @param item The name of the item to return the value for
/// @return The value found
template <typename T, std::enable_if_t<not is_optional_v<T>, int> = 0>
T find1(const_iterator pos, condition &&cond, const char *column) const
T find1(const_iterator pos, condition &&cond, std::string_view item) const
{
auto h = find<T>(pos, std::move(cond), column);
auto h = find<T>(pos, std::move(cond), item);
if (h.size() != 1)
throw multiple_results_error();
@@ -540,18 +568,18 @@ class category
return *h.begin();
}
/// @brief Return a value of type std::optional<T> for the column named @a column for the single row that
/// @brief Return a value of type std::optional<T> for the item named @a item for the single row that
/// matches @a cond when starting to search at @a pos.
/// If the row was not found, an empty value is returned.
/// @tparam The type to use for the result
/// @param pos The location to start the search
/// @param cond The condition to search for
/// @param column The name of the column to return the value for
/// @param item The name of the item to return the value for
/// @return The value found, can be empty if no row matches the condition
template <typename T, std::enable_if_t<is_optional_v<T>, int> = 0>
T find1(const_iterator pos, condition &&cond, const char *column) const
T find1(const_iterator pos, condition &&cond, std::string_view item) const
{
auto h = find<typename T::value_type>(pos, std::move(cond), column);
auto h = find<typename T::value_type>(pos, std::move(cond), item);
if (h.size() > 1)
throw multiple_results_error();
@@ -562,34 +590,34 @@ class category
return *h.begin();
}
/// @brief Return a std::tuple for the values for the columns named in @a columns
/// @brief Return a std::tuple for the values for the items named in @a items
/// for the single row that matches @a cond
/// Throws @a multiple_results_error if there are is not exactly one row
/// @tparam The types to use for the resulting tuple
/// @param cond The condition to search for
/// @param columns The names of the columns to return the value for
/// @param items The names of the items to return the value for
/// @return The values found as a single tuple of type std::tuple<Ts...>
template <typename... Ts, typename... Cs, typename U = std::enable_if_t<sizeof...(Ts) != 1>>
std::tuple<Ts...> find1(condition &&cond, Cs... columns) const
std::tuple<Ts...> find1(condition &&cond, Cs... items) const
{
static_assert(sizeof...(Ts) == sizeof...(Cs), "The number of column titles should be equal to the number of types to return");
// static_assert(std::is_same_v<Cs, const char*>..., "The column names should be const char");
return find1<Ts...>(cbegin(), std::move(cond), std::forward<Cs>(columns)...);
static_assert(sizeof...(Ts) == sizeof...(Cs), "The number of item names should be equal to the number of types to return");
// static_assert(std::is_same_v<Cs, const char*>..., "The item names should be const char");
return find1<Ts...>(cbegin(), std::move(cond), std::forward<Cs>(items)...);
}
/// @brief Return a std::tuple for the values for the columns named in @a columns
/// @brief Return a std::tuple for the values for the items named in @a items
/// for the single row that matches @a cond when starting to search at @a pos
/// Throws @a multiple_results_error if there are is not exactly one row
/// @tparam The types to use for the resulting tuple
/// @param pos The location to start the search
/// @param cond The condition to search for
/// @param columns The names of the columns to return the value for
/// @param items The names of the items to return the value for
/// @return The values found as a single tuple of type std::tuple<Ts...>
template <typename... Ts, typename... Cs, typename U = std::enable_if_t<sizeof...(Ts) != 1>>
std::tuple<Ts...> find1(const_iterator pos, condition &&cond, Cs... columns) const
std::tuple<Ts...> find1(const_iterator pos, condition &&cond, Cs... items) const
{
static_assert(sizeof...(Ts) == sizeof...(Cs), "The number of column titles should be equal to the number of types to return");
auto h = find<Ts...>(pos, std::move(cond), std::forward<Cs>(columns)...);
static_assert(sizeof...(Ts) == sizeof...(Cs), "The number of item names should be equal to the number of types to return");
auto h = find<Ts...>(pos, std::move(cond), std::forward<Cs>(items)...);
if (h.size() != 1)
throw multiple_results_error();
@@ -638,74 +666,74 @@ class category
return h.empty() ? row_handle{} : *h.begin();
}
/// @brief Return the value for column @a column for the first row that matches condition @a cond
/// @brief Return the value for item @a item for the first row that matches condition @a cond
/// @tparam The type of the value to return
/// @param cond The condition to search for
/// @param column The column for which the value should be returned
/// @param item The item for which the value should be returned
/// @return The value found or a default constructed value if not found
template <typename T>
T find_first(condition &&cond, const char *column) const
T find_first(condition &&cond, std::string_view item) const
{
return find_first<T>(cbegin(), std::move(cond), column);
return find_first<T>(cbegin(), std::move(cond), item);
}
/// @brief Return the value for column @a column for the first row that matches condition @a cond
/// @brief Return the value for item @a item for the first row that matches condition @a cond
/// when starting the search at @a pos
/// @tparam The type of the value to return
/// @param pos The location to start searching
/// @param cond The condition to search for
/// @param column The column for which the value should be returned
/// @param item The item for which the value should be returned
/// @return The value found or a default constructed value if not found
template <typename T>
T find_first(const_iterator pos, condition &&cond, const char *column) const
T find_first(const_iterator pos, condition &&cond, std::string_view item) const
{
auto h = find<T>(pos, std::move(cond), column);
auto h = find<T>(pos, std::move(cond), item);
return h.empty() ? T{} : *h.begin();
}
/// @brief Return a tuple containing the values for the columns @a columns for the first row that matches condition @a cond
/// @brief Return a tuple containing the values for the items @a items for the first row that matches condition @a cond
/// @tparam The types of the values to return
/// @param cond The condition to search for
/// @param columns The columns for which the values should be returned
/// @param items The items for which the values should be returned
/// @return The values found or default constructed values if not found
template <typename... Ts, typename... Cs, typename U = std::enable_if_t<sizeof...(Ts) != 1>>
std::tuple<Ts...> find_first(condition &&cond, Cs... columns) const
std::tuple<Ts...> find_first(condition &&cond, Cs... items) const
{
static_assert(sizeof...(Ts) == sizeof...(Cs), "The number of column titles should be equal to the number of types to return");
// static_assert(std::is_same_v<Cs, const char*>..., "The column names should be const char");
return find_first<Ts...>(cbegin(), std::move(cond), std::forward<Cs>(columns)...);
static_assert(sizeof...(Ts) == sizeof...(Cs), "The number of item names should be equal to the number of types to return");
// static_assert(std::is_same_v<Cs, const char*>..., "The item names should be const char");
return find_first<Ts...>(cbegin(), std::move(cond), std::forward<Cs>(items)...);
}
/// @brief Return a tuple containing the values for the columns @a columns for the first row that matches condition @a cond
/// @brief Return a tuple containing the values for the items @a items for the first row that matches condition @a cond
/// when starting the search at @a pos
/// @tparam The types of the values to return
/// @param pos The location to start searching
/// @param cond The condition to search for
/// @param columns The columns for which the values should be returned
/// @param items The items for which the values should be returned
/// @return The values found or default constructed values if not found
template <typename... Ts, typename... Cs, typename U = std::enable_if_t<sizeof...(Ts) != 1>>
std::tuple<Ts...> find_first(const_iterator pos, condition &&cond, Cs... columns) const
std::tuple<Ts...> find_first(const_iterator pos, condition &&cond, Cs... items) const
{
static_assert(sizeof...(Ts) == sizeof...(Cs), "The number of column titles should be equal to the number of types to return");
auto h = find<Ts...>(pos, std::move(cond), std::forward<Cs>(columns)...);
static_assert(sizeof...(Ts) == sizeof...(Cs), "The number of item names should be equal to the number of types to return");
auto h = find<Ts...>(pos, std::move(cond), std::forward<Cs>(items)...);
return h.empty() ? std::tuple<Ts...>{} : *h.begin();
}
// --------------------------------------------------------------------
/// @brief Return the maximum value for column @a column for all rows that match condition @a cond
/// @brief Return the maximum value for item @a item for all rows that match condition @a cond
/// @tparam The type of the value to return
/// @param column The column to use for the value
/// @param item The item to use for the value
/// @param cond The condition to search for
/// @return The value found or the minimal value for the type
template <typename T, std::enable_if_t<std::is_arithmetic_v<T>, int> = 0>
T find_max(const char *column, condition &&cond) const
T find_max(std::string_view item, condition &&cond) const
{
T result = std::numeric_limits<T>::min();
for (auto v : find<T>(std::move(cond), column))
for (auto v : find<T>(std::move(cond), item))
{
if (result < v)
result = v;
@@ -714,27 +742,27 @@ class category
return result;
}
/// @brief Return the maximum value for column @a column for all rows
/// @brief Return the maximum value for item @a item for all rows
/// @tparam The type of the value to return
/// @param column The column to use for the value
/// @param item The item to use for the value
/// @return The value found or the minimal value for the type
template <typename T, std::enable_if_t<std::is_arithmetic_v<T>, int> = 0>
T find_max(const char *column) const
T find_max(std::string_view item) const
{
return find_max<T>(column, all());
return find_max<T>(item, all());
}
/// @brief Return the minimum value for column @a column for all rows that match condition @a cond
/// @brief Return the minimum value for item @a item for all rows that match condition @a cond
/// @tparam The type of the value to return
/// @param column The column to use for the value
/// @param item The item to use for the value
/// @param cond The condition to search for
/// @return The value found or the maximum value for the type
template <typename T, std::enable_if_t<std::is_arithmetic_v<T>, int> = 0>
T find_min(const char *column, condition &&cond) const
T find_min(std::string_view item, condition &&cond) const
{
T result = std::numeric_limits<T>::max();
for (auto v : find<T>(std::move(cond), column))
for (auto v : find<T>(std::move(cond), item))
{
if (result > v)
result = v;
@@ -743,20 +771,28 @@ class category
return result;
}
/// @brief Return the maximum value for column @a column for all rows
/// @brief Return the maximum value for item @a item for all rows
/// @tparam The type of the value to return
/// @param column The column to use for the value
/// @param item The item to use for the value
/// @return The value found or the maximum value for the type
template <typename T, std::enable_if_t<std::is_arithmetic_v<T>, int> = 0>
T find_min(const char *column) const
T find_min(std::string_view item) const
{
return find_min<T>(column, all());
return find_min<T>(item, all());
}
/// @brief Return whether a row exists that matches condition @a cond
/// @param cond The condition to match
/// @return True if a row exists
bool exists(condition &&cond) const
[[deprecated("Use contains instead")]] bool exists(condition &&cond) const
{
return contains(std::move(cond));
}
/// @brief Return whether a row exists that matches condition @a cond
/// @param cond The condition to match
/// @return True if a row exists
bool contains(condition &&cond) const
{
bool result = false;
@@ -846,7 +882,7 @@ class category
// insert_impl(pos, std::move(row));
// }
/// Erase the row pointed to by @a pos and return the iterator to the
/// Erase the row pointed to by @a pos and return the iterator to the
/// row following pos.
iterator erase(iterator pos);
@@ -890,7 +926,7 @@ class category
for (auto i = b; i != e; ++i)
{
// item_value *new_item = this->create_item(*i);
r->append(add_column(i->name()), { i->value() });
r->append(add_item(i->name()), { i->value() });
}
}
catch (...)
@@ -912,7 +948,6 @@ class category
/// result is unique in the context of this category
std::string get_unique_id(std::function<std::string(int)> generator = cif::cif_id_for_number);
/// @brief Generate a new, unique ID based on a string prefix followed by a number
/// @param prefix The string prefix
/// @return a new unique ID
@@ -922,98 +957,172 @@ class category
{ return prefix + std::to_string(nr + 1); });
}
/// @brief Generate a new, unique value for a item named @a item_name
/// @param item_name The name of the item
/// @return a new unique value
std::string get_unique_value(std::string_view item_name);
// --------------------------------------------------------------------
/// \brief Update a single column named @a tag in the rows that match \a cond to value \a value
/// \brief Update a single item named @a item_name in the rows that match \a cond to value \a value
/// making sure the linked categories are updated according to the link.
/// That means, child categories are updated if the links are absolute
/// and unique. If they are not, the child category rows are split.
void update_value(condition &&cond, std::string_view tag, std::string_view value)
void update_value(condition &&cond, std::string_view item_name, std::string_view value)
{
auto rs = find(std::move(cond));
std::vector<row_handle> rows;
std::copy(rs.begin(), rs.end(), std::back_inserter(rows));
update_value(rows, tag, value);
update_value(rows, item_name, value);
}
/// \brief Update a single column named @a tag in @a rows to value \a value
/// \brief Update a single item named @a item_name in @a rows to value \a value
/// making sure the linked categories are updated according to the link.
/// That means, child categories are updated if the links are absolute
/// and unique. If they are not, the child category rows are split.
void update_value(const std::vector<row_handle> &rows, std::string_view tag, std::string_view value);
void update_value(const std::vector<row_handle> &rows, std::string_view item_name, std::string_view value);
// --------------------------------------------------------------------
/// \brief Return the index number for \a column_name
// Naming used to be very inconsistent. For backward compatibility,
// the old function names are here as deprecated variants.
/// \brief Return the index number for \a column_name
[[deprecated("Use get_item_ix instead")]]
uint16_t get_column_ix(std::string_view column_name) const
{
uint16_t result;
for (result = 0; result < m_columns.size(); ++result)
{
if (iequals(column_name, m_columns[result].m_name))
break;
}
if (VERBOSE > 0 and result == m_columns.size() and m_cat_validator != nullptr) // validate the name, if it is known at all (since it was not found)
{
auto iv = m_cat_validator->get_validator_for_item(column_name);
if (iv == nullptr)
std::cerr << "Invalid name used '" << column_name << "' is not a known column in " + m_name << '\n';
}
return result;
return get_item_ix(column_name);
}
/// @brief Return the name for column with index @a ix
/// @param ix The index number
/// @return The name of the column
[[deprecated("use get_item_name instead")]]
std::string_view get_column_name(uint16_t ix) const
{
if (ix >= m_columns.size())
throw std::out_of_range("column index is out of range");
return m_columns[ix].m_name;
return get_item_name(ix);
}
/// @brief Make sure a column with name @a column_name is known and return its index number
/// @param column_name The name of the column
/// @return The index number of the column
uint16_t add_column(std::string_view column_name)
/// @brief Make sure a item with name @a item_name is known and return its index number
/// @param item_name The name of the item
/// @return The index number of the item
[[deprecated("use add_item instead")]]
uint16_t add_column(std::string_view item_name)
{
using namespace std::literals;
return add_item(item_name);
}
uint16_t result = get_column_ix(column_name);
/** @brief Remove column name @a colum_name
* @param column_name The column to be removed
*/
[[deprecated("use remove_item instead")]]
void remove_column(std::string_view column_name)
{
remove_item(column_name);
}
if (result == m_columns.size())
{
const item_validator *item_validator = nullptr;
if (m_cat_validator != nullptr)
{
item_validator = m_cat_validator->get_validator_for_item(column_name);
if (item_validator == nullptr)
m_validator->report_error("tag " + std::string(column_name) + " not allowed in category " + m_name, false);
}
m_columns.emplace_back(column_name, item_validator);
}
return result;
/** @brief Rename column @a from_name to @a to_name */
[[deprecated("use rename_item instead")]]
void rename_column(std::string_view from_name, std::string_view to_name)
{
rename_item(from_name, to_name);
}
/// @brief Return whether a column with name @a name exists in this category
/// @param name The name of the column
/// @return True if the column exists
[[deprecated("use has_item instead")]]
bool has_column(std::string_view name) const
{
return get_column_ix(name) < m_columns.size();
return has_item(name);
}
/// @brief Return the cif::iset of columns in this category
iset get_columns() const;
[[deprecated("use get_items instead")]]
iset get_columns() const
{
return get_items();
}
// --------------------------------------------------------------------
/// \brief Return the index number for \a item_name
uint16_t get_item_ix(std::string_view item_name) const
{
uint16_t result;
for (result = 0; result < m_items.size(); ++result)
{
if (iequals(item_name, m_items[result].m_name))
break;
}
if (VERBOSE > 0 and result == m_items.size() and m_cat_validator != nullptr) // validate the name, if it is known at all (since it was not found)
{
auto iv = m_cat_validator->get_validator_for_item(item_name);
if (iv == nullptr)
std::cerr << "Invalid name used '" << item_name << "' is not a known item in " + m_name << '\n';
}
return result;
}
/// @brief Return the name for item with index @a ix
/// @param ix The index number
/// @return The name of the item
std::string_view get_item_name(uint16_t ix) const
{
if (ix >= m_items.size())
throw std::out_of_range("item index is out of range");
return m_items[ix].m_name;
}
/// @brief Make sure a item with name @a item_name is known and return its index number
/// @param item_name The name of the item
/// @return The index number of the item
uint16_t add_item(std::string_view item_name)
{
using namespace std::literals;
uint16_t result = get_item_ix(item_name);
if (result == m_items.size())
{
const item_validator *item_validator = nullptr;
if (m_cat_validator != nullptr)
{
item_validator = m_cat_validator->get_validator_for_item(item_name);
if (item_validator == nullptr)
m_validator->report_error( validation_error::item_not_allowed_in_category, m_name, item_name, false);
}
m_items.emplace_back(item_name, item_validator);
}
return result;
}
/** @brief Remove item name @a colum_name
* @param item_name The item to be removed
*/
void remove_item(std::string_view item_name);
/** @brief Rename item @a from_name to @a to_name */
void rename_item(std::string_view from_name, std::string_view to_name);
/// @brief Return whether a item with name @a name exists in this category
/// @param name The name of the item
/// @return True if the item exists
bool has_item(std::string_view name) const
{
return get_item_ix(name) < m_items.size();
}
/// @brief Return the cif::iset of items in this category
iset get_items() const;
// --------------------------------------------------------------------
@@ -1029,30 +1138,37 @@ class category
// --------------------------------------------------------------------
/// This function returns effectively the list of fully qualified column
/// names, that is category_name + '.' + column_name for each column
std::vector<std::string> get_tag_order() const;
/// This function returns effectively the list of fully qualified item
/// names, that is category_name + '.' + item_name for each item
[[deprecated("use get_item_order instead")]]
std::vector<std::string> get_tag_order() const
{
return get_item_order();
}
/// This function returns effectively the list of fully qualified item
/// names, that is category_name + '.' + item_name for each item
std::vector<std::string> get_item_order() const;
/// Write the contents of the category to the std::ostream @a os
void write(std::ostream &os) const;
/// @brief Write the contents of the category to the std::ostream @a os and
/// use @a order as the order of the columns. If @a addMissingColumns is
/// false, columns that do not contain any value will be suppressed
/// use @a order as the order of the items. If @a addMissingItems is
/// false, items that do not contain any value will be suppressed
/// @param os The std::ostream to write to
/// @param order The order in which the columns should appear
/// @param addMissingColumns When false, empty columns are suppressed from the output
void write(std::ostream &os, const std::vector<std::string> &order, bool addMissingColumns = true);
/// @param order The order in which the items should appear
/// @param addMissingItems When false, empty items are suppressed from the output
void write(std::ostream &os, const std::vector<std::string> &order, bool addMissingItems = true);
private:
void write(std::ostream &os, const std::vector<uint16_t> &order, bool includeEmptyColumns) const;
void write(std::ostream &os, const std::vector<uint16_t> &order, bool includeEmptyItems) const;
public:
/// friend function to make it possible to do:
/// @code {.cpp}
/// std::cout << my_category;
/// @endcode
/// @endcode
friend std::ostream &operator<<(std::ostream &os, const category &cat)
{
cat.write(os);
@@ -1060,7 +1176,7 @@ class category
}
private:
void update_value(row *row, uint16_t column, std::string_view value, bool updateLinked, bool validate = true);
void update_value(row *row, uint16_t item, std::string_view value, bool updateLinked, bool validate = true);
void erase_orphans(condition &&cond, category &parent);
@@ -1097,12 +1213,12 @@ class category
row_handle create_copy(row_handle r);
struct item_column
struct item_entry
{
std::string m_name;
const item_validator *m_validator;
item_column(std::string_view name, const item_validator *validator)
item_entry(std::string_view name, const item_validator *validator)
: m_name(name)
, m_validator(validator)
{
@@ -1132,12 +1248,12 @@ class category
// --------------------------------------------------------------------
void swap_item(uint16_t column_ix, row_handle &a, row_handle &b);
void swap_item(uint16_t item_ix, row_handle &a, row_handle &b);
// --------------------------------------------------------------------
std::string m_name;
std::vector<item_column> m_columns;
std::vector<item_entry> m_items;
const validator *m_validator = nullptr;
const category_validator *m_cat_validator = nullptr;
std::vector<link> m_parent_links, m_child_links;

View File

@@ -166,6 +166,12 @@ class compound
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
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
void pop_dictionary();
/// 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;
/// 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;
/// 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
///
/// 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.
/// \param id The compound ID, a three letter code usually
/// \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();
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
void report_missing_compound(const std::string &compound_id);
void report_missing_compound(std::string_view compound_id);
private:
compound_factory();

View File

@@ -39,17 +39,17 @@
* query you can use to find rows in a @ref cif::category
*
* Conditions are created as standard C++ expressions. That means
* you can use the standard comparison operators to compare field
* you can use the standard comparison operators to compare item
* contents with a value and boolean operators to chain everything
* together.
*
* To create a query that simply compares one field with one value:
* To create a query that simply compares one item with one value:
*
* @code {.cpp}
* cif::condition c = cif::key("id") == 1;
* @endcode
*
* That will find rows where the ID field contains the number 1. If
* That will find rows where the ID item contains the number 1. If
* using cif::key is a bit too much typing, you can also write:
*
* @code{.cpp}
@@ -64,7 +64,7 @@
* auto c3 = "id"_key == 1 or "id"_key == 2;
* @endcode
*
* There are some special values you can use. To find rows with field that
* There are some special values you can use. To find rows with item that
* do not have a value:
*
* @code{.cpp}
@@ -83,7 +83,7 @@
* auto c6 = cif::all;
* @endcode
*
* And when you want to search for any column containing the value 'foo':
* And when you want to search for any item containing the value 'foo':
*
* @code{.cpp}
* auto c7 = cif::any == "foo";
@@ -104,31 +104,40 @@ namespace cif
/// we declare a function to access its contents
/**
* @brief Get the fields that can be used as key in conditions for a category
* @brief Get the items that can be used as key in conditions for a category
*
* @param cat The category whose fields to return
* @return iset The set of key field names
* @param cat The category whose items to return
* @return iset The set of key item names
*/
[[deprecated("use get_category_items instead")]]
iset get_category_fields(const category &cat);
/**
* @brief Get the column index for column @a col in category @a cat
* @brief Get the items that can be used as key in conditions for a category
*
* @param cat The category
* @param col The name of the column
* @return uint16_t The index
* @param cat The category whose items to return
* @return iset The set of key field names
*/
uint16_t get_column_ix(const category &cat, std::string_view col);
iset get_category_items(const category &cat);
/**
* @brief Return whether the column @a col in category @a cat has a primitive type of *uchar*
* @brief Get the item index for item @a col in category @a cat
*
* @param cat The category
* @param col The column name
* @param col The name of the item
* @return uint16_t The index
*/
uint16_t get_item_ix(const category &cat, std::string_view col);
/**
* @brief Return whether the item @a col in category @a cat has a primitive type of *uchar*
*
* @param cat The category
* @param col The item name
* @return true If the primitive type is of type *uchar*
* @return false If the primitive type is not of type *uchar*
*/
bool is_column_type_uchar(const category &cat, std::string_view col);
bool is_item_type_uchar(const category &cat, std::string_view col);
// --------------------------------------------------------------------
// some more templates to be able to do querying
@@ -219,7 +228,7 @@ class condition
/**
* @brief Prepare the condition to be used on category @a c. This will
* take care of setting the correct indices for fields e.g.
* take care of setting the correct indices for items e.g.
*
* @param c The category this query should act upon
*/
@@ -305,14 +314,14 @@ namespace detail
{
struct key_is_empty_condition_impl : public condition_impl
{
key_is_empty_condition_impl(const std::string &item_tag)
: m_item_tag(item_tag)
key_is_empty_condition_impl(const std::string &item_name)
: m_item_name(item_name)
{
}
condition_impl *prepare(const category &c) override
{
m_item_ix = get_column_ix(c, m_item_tag);
m_item_ix = get_item_ix(c, m_item_name);
return this;
}
@@ -323,23 +332,23 @@ namespace detail
void str(std::ostream &os) const override
{
os << m_item_tag << " IS NULL";
os << m_item_name << " IS NULL";
}
std::string m_item_tag;
std::string m_item_name;
uint16_t m_item_ix = 0;
};
struct key_is_not_empty_condition_impl : public condition_impl
{
key_is_not_empty_condition_impl(const std::string &item_tag)
: m_item_tag(item_tag)
key_is_not_empty_condition_impl(const std::string &item_name)
: m_item_name(item_name)
{
}
condition_impl *prepare(const category &c) override
{
m_item_ix = get_column_ix(c, m_item_tag);
m_item_ix = get_item_ix(c, m_item_name);
return this;
}
@@ -350,17 +359,17 @@ namespace detail
void str(std::ostream &os) const override
{
os << m_item_tag << " IS NOT NULL";
os << m_item_name << " IS NOT NULL";
}
std::string m_item_tag;
std::string m_item_name;
uint16_t m_item_ix = 0;
};
struct key_equals_condition_impl : public condition_impl
{
key_equals_condition_impl(item &&i)
: m_item_tag(i.name())
: m_item_name(i.name())
, m_value(i.value())
{
}
@@ -374,7 +383,7 @@ namespace detail
void str(std::ostream &os) const override
{
os << m_item_tag << (m_icase ? "^ " : " ") << " == " << m_value;
os << m_item_name << (m_icase ? "^ " : " ") << " == " << m_value;
}
virtual std::optional<row_handle> single() const override
@@ -390,13 +399,13 @@ namespace detail
if (m_single_hit.has_value() or ri->m_single_hit.has_value())
return m_single_hit == ri->m_single_hit;
else
// watch out, both m_item_ix might be the same while tags might be diffent (in case they both do not exist in the category)
return m_item_ix == ri->m_item_ix and m_value == ri->m_value and m_item_tag == ri->m_item_tag;
// watch out, both m_item_ix might be the same while item_names might be diffent (in case they both do not exist in the category)
return m_item_ix == ri->m_item_ix and m_value == ri->m_value and m_item_name == ri->m_item_name;
}
return this == rhs;
}
std::string m_item_tag;
std::string m_item_name;
uint16_t m_item_ix = 0;
bool m_icase = false;
std::string m_value;
@@ -406,7 +415,7 @@ namespace detail
struct key_equals_or_empty_condition_impl : public condition_impl
{
key_equals_or_empty_condition_impl(key_equals_condition_impl *equals)
: m_item_tag(equals->m_item_tag)
: m_item_name(equals->m_item_name)
, m_value(equals->m_value)
, m_icase(equals->m_icase)
, m_single_hit(equals->m_single_hit)
@@ -415,8 +424,8 @@ namespace detail
condition_impl *prepare(const category &c) override
{
m_item_ix = get_column_ix(c, m_item_tag);
m_icase = is_column_type_uchar(c, m_item_tag);
m_item_ix = get_item_ix(c, m_item_name);
m_icase = is_item_type_uchar(c, m_item_name);
return this;
}
@@ -432,7 +441,7 @@ namespace detail
void str(std::ostream &os) const override
{
os << '(' << m_item_tag << (m_icase ? "^ " : " ") << " == " << m_value << " OR " << m_item_tag << " IS NULL)";
os << '(' << m_item_name << (m_icase ? "^ " : " ") << " == " << m_value << " OR " << m_item_name << " IS NULL)";
}
virtual std::optional<row_handle> single() const override
@@ -448,13 +457,13 @@ namespace detail
if (m_single_hit.has_value() or ri->m_single_hit.has_value())
return m_single_hit == ri->m_single_hit;
else
// watch out, both m_item_ix might be the same while tags might be diffent (in case they both do not exist in the category)
return m_item_ix == ri->m_item_ix and m_value == ri->m_value and m_item_tag == ri->m_item_tag;
// watch out, both m_item_ix might be the same while item_names might be diffent (in case they both do not exist in the category)
return m_item_ix == ri->m_item_ix and m_value == ri->m_value and m_item_name == ri->m_item_name;
}
return this == rhs;
}
std::string m_item_tag;
std::string m_item_name;
uint16_t m_item_ix = 0;
std::string m_value;
bool m_icase = false;
@@ -464,8 +473,8 @@ namespace detail
struct key_compare_condition_impl : public condition_impl
{
template <typename COMP>
key_compare_condition_impl(const std::string &item_tag, COMP &&comp, const std::string &s)
: m_item_tag(item_tag)
key_compare_condition_impl(const std::string &item_name, COMP &&comp, const std::string &s)
: m_item_name(item_name)
, m_compare(std::move(comp))
, m_str(s)
{
@@ -473,8 +482,8 @@ namespace detail
condition_impl *prepare(const category &c) override
{
m_item_ix = get_column_ix(c, m_item_tag);
m_icase = is_column_type_uchar(c, m_item_tag);
m_item_ix = get_item_ix(c, m_item_name);
m_icase = is_item_type_uchar(c, m_item_name);
return this;
}
@@ -485,10 +494,10 @@ namespace detail
void str(std::ostream &os) const override
{
os << m_item_tag << (m_icase ? "^ " : " ") << m_str;
os << m_item_name << (m_icase ? "^ " : " ") << m_str;
}
std::string m_item_tag;
std::string m_item_name;
uint16_t m_item_ix = 0;
bool m_icase = false;
std::function<bool(row_handle, bool)> m_compare;
@@ -497,8 +506,8 @@ namespace detail
struct key_matches_condition_impl : public condition_impl
{
key_matches_condition_impl(const std::string &item_tag, const std::regex &rx)
: m_item_tag(item_tag)
key_matches_condition_impl(const std::string &item_name, const std::regex &rx)
: m_item_name(item_name)
, m_item_ix(0)
, mRx(rx)
{
@@ -506,7 +515,7 @@ namespace detail
condition_impl *prepare(const category &c) override
{
m_item_ix = get_column_ix(c, m_item_tag);
m_item_ix = get_item_ix(c, m_item_name);
return this;
}
@@ -518,10 +527,10 @@ namespace detail
void str(std::ostream &os) const override
{
os << m_item_tag << " =~ expression";
os << m_item_name << " =~ expression";
}
std::string m_item_tag;
std::string m_item_name;
uint16_t m_item_ix;
std::regex mRx;
};
@@ -541,7 +550,7 @@ namespace detail
auto &c = r.get_category();
bool result = false;
for (auto &f : get_category_fields(c))
for (auto &f : get_category_items(c))
{
try
{
@@ -579,7 +588,7 @@ namespace detail
auto &c = r.get_category();
bool result = false;
for (auto &f : get_category_fields(c))
for (auto &f : get_category_items(c))
{
try
{
@@ -864,7 +873,7 @@ inline condition operator or(condition &&a, condition &&b)
auto ci = static_cast<detail::key_equals_condition_impl *>(a.m_impl);
auto ce = static_cast<detail::key_is_empty_condition_impl *>(b.m_impl);
if (ci->m_item_tag == ce->m_item_tag)
if (ci->m_item_name == ce->m_item_name)
return condition(new detail::key_equals_or_empty_condition_impl(ci));
}
else if (typeid(*b.m_impl) == typeid(detail::key_equals_condition_impl) and
@@ -873,7 +882,7 @@ inline condition operator or(condition &&a, condition &&b)
auto ci = static_cast<detail::key_equals_condition_impl *>(b.m_impl);
auto ce = static_cast<detail::key_is_empty_condition_impl *>(a.m_impl);
if (ci->m_item_tag == ce->m_item_tag)
if (ci->m_item_name == ce->m_item_name)
return condition(new detail::key_equals_or_empty_condition_impl(ci));
}
@@ -887,7 +896,7 @@ inline condition operator or(condition &&a, condition &&b)
}
/**
* @brief A helper class to make it possible to search for empty fields (NULL)
* @brief A helper class to make it possible to search for empty items (NULL)
*
* @code{.cpp}
* "id"_key == cif::empty_type();
@@ -909,35 +918,45 @@ struct empty_type
inline constexpr empty_type null = empty_type();
/**
* @brief Class to use in creating conditions, creates a reference to a field or column
* @brief Class to use in creating conditions, creates a reference to a item or item
*
*/
struct key
{
/**
* @brief Construct a new key object using @a itemTag as name
* @brief Construct a new key object using @a item_name as name
*
* @param itemTag
* @param item_name
*/
explicit key(const std::string &itemTag)
: m_item_tag(itemTag)
explicit key(const std::string &item_name)
: m_item_name(item_name)
{
}
/**
* @brief Construct a new key object using @a itemTag as name
* @brief Construct a new key object using @a item_name as name
*
* @param itemTag
* @param item_name
*/
explicit key(const char *itemTag)
: m_item_tag(itemTag)
explicit key(const char *item_name)
: m_item_name(item_name)
{
}
/**
* @brief Construct a new key object using @a item_name as name
*
* @param item_name
*/
explicit key(std::string_view item_name)
: m_item_name(item_name)
{
}
key(const key &) = delete;
key &operator=(const key &) = delete;
std::string m_item_tag; ///< The column name
std::string m_item_name; ///< The item name
};
/**
@@ -946,7 +965,7 @@ struct key
template <typename T>
condition operator==(const key &key, const T &v)
{
return condition(new detail::key_equals_condition_impl({ key.m_item_tag, v }));
return condition(new detail::key_equals_condition_impl({ key.m_item_name, v }));
}
/**
@@ -955,9 +974,9 @@ condition operator==(const key &key, const T &v)
inline condition operator==(const key &key, std::string_view value)
{
if (not value.empty())
return condition(new detail::key_equals_condition_impl({ key.m_item_tag, value }));
return condition(new detail::key_equals_condition_impl({ key.m_item_name, value }));
else
return condition(new detail::key_is_empty_condition_impl(key.m_item_tag));
return condition(new detail::key_is_empty_condition_impl(key.m_item_name));
}
/**
@@ -987,8 +1006,8 @@ condition operator>(const key &key, const T &v)
s << " > " << v;
return condition(new detail::key_compare_condition_impl(
key.m_item_tag, [tag = key.m_item_tag, v](row_handle r, bool icase)
{ return r[tag].template compare<T>(v, icase) > 0; },
key.m_item_name, [item_name = key.m_item_name, v](row_handle r, bool icase)
{ return r[item_name].template compare<T>(v, icase) > 0; },
s.str()));
}
@@ -1002,8 +1021,8 @@ condition operator>=(const key &key, const T &v)
s << " >= " << v;
return condition(new detail::key_compare_condition_impl(
key.m_item_tag, [tag = key.m_item_tag, v](row_handle r, bool icase)
{ return r[tag].template compare<T>(v, icase) >= 0; },
key.m_item_name, [item_name = key.m_item_name, v](row_handle r, bool icase)
{ return r[item_name].template compare<T>(v, icase) >= 0; },
s.str()));
}
@@ -1017,8 +1036,8 @@ condition operator<(const key &key, const T &v)
s << " < " << v;
return condition(new detail::key_compare_condition_impl(
key.m_item_tag, [tag = key.m_item_tag, v](row_handle r, bool icase)
{ return r[tag].template compare<T>(v, icase) < 0; },
key.m_item_name, [item_name = key.m_item_name, v](row_handle r, bool icase)
{ return r[item_name].template compare<T>(v, icase) < 0; },
s.str()));
}
@@ -1032,8 +1051,8 @@ condition operator<=(const key &key, const T &v)
s << " <= " << v;
return condition(new detail::key_compare_condition_impl(
key.m_item_tag, [tag = key.m_item_tag, v](row_handle r, bool icase)
{ return r[tag].template compare<T>(v, icase) <= 0; },
key.m_item_name, [item_name = key.m_item_name, v](row_handle r, bool icase)
{ return r[item_name].template compare<T>(v, icase) <= 0; },
s.str()));
}
@@ -1042,7 +1061,7 @@ condition operator<=(const key &key, const T &v)
*/
inline condition operator==(const key &key, const std::regex &rx)
{
return condition(new detail::key_matches_condition_impl(key.m_item_tag, rx));
return condition(new detail::key_matches_condition_impl(key.m_item_name, rx));
}
/**
@@ -1050,7 +1069,7 @@ inline condition operator==(const key &key, const std::regex &rx)
*/
inline condition operator==(const key &key, const empty_type &)
{
return condition(new detail::key_is_empty_condition_impl(key.m_item_tag));
return condition(new detail::key_is_empty_condition_impl(key.m_item_name));
}
/**
@@ -1058,20 +1077,20 @@ inline condition operator==(const key &key, const empty_type &)
*/
inline condition operator!=(const key &key, const empty_type &)
{
return condition(new detail::key_is_not_empty_condition_impl(key.m_item_tag));
return condition(new detail::key_is_not_empty_condition_impl(key.m_item_name));
}
/**
* @brief Create a condition to search any column for a value @a v if @a v contains a value
* @brief Create a condition to search any item for a value @a v if @a v contains a value
* compare to null if not.
*/
template <typename T>
condition operator==(const key &key, const std::optional<T> &v)
{
if (v.has_value())
return condition(new detail::key_equals_condition_impl({ key.m_item_tag, *v }));
return condition(new detail::key_equals_condition_impl({ key.m_item_name, *v }));
else
return condition(new detail::key_is_empty_condition_impl(key.m_item_tag));
return condition(new detail::key_is_empty_condition_impl(key.m_item_name));
}
/**
@@ -1089,12 +1108,12 @@ struct any_type
/** @endcond */
/**
* @brief A helper for any field constructs
* @brief A helper for any item constructs
*/
inline constexpr any_type any = any_type{};
/**
* @brief Create a condition to search any column for a value @a v
* @brief Create a condition to search any item for a value @a v
*/
template <typename T>
condition operator==(const any_type &, const T &v)
@@ -1103,7 +1122,7 @@ condition operator==(const any_type &, const T &v)
}
/**
* @brief Create a condition to search any column for a regular expression @a rx
* @brief Create a condition to search any item for a regular expression @a rx
*/
inline condition operator==(const any_type &, const std::regex &rx)
{
@@ -1121,9 +1140,9 @@ inline condition all()
namespace literals
{
/**
* @brief Return a cif::key for the column name @a text
* @brief Return a cif::key for the item name @a text
*
* @param text The name of the column
* @param text The name of the item
* @param length The length of @a text
* @return key The cif::key created
*/

View File

@@ -106,6 +106,15 @@ class datablock : public std::list<category>
*/
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
* as defined in the validator
@@ -169,7 +178,16 @@ class datablock : public std::list<category>
/**
* @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
@@ -177,9 +195,9 @@ class datablock : public std::list<category>
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

View File

@@ -44,7 +44,7 @@
/** \file item.hpp
*
* 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
@@ -227,10 +227,10 @@ class item
/// \brief empty means either null or unknown
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 == "."; }
/// \brief returns true if the field contains '?'
/// \brief returns true if the item contains '?'
bool is_unknown() const { return m_value == "?"; }
/// \brief the length of the value string
@@ -464,14 +464,14 @@ struct item_handle
/** Easy way to test for an empty item */
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
{
auto txt = text();
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
{
auto txt = text();
@@ -484,11 +484,11 @@ struct item_handle
/**
* @brief Construct a new item handle object
*
* @param column Column index
* @param item Item index
* @param row Reference to the row
*/
item_handle(uint16_t column, row_handle &row)
: m_column(column)
item_handle(uint16_t item, row_handle &row)
: m_item_ix(item)
, m_row_handle(row)
{
}
@@ -505,7 +505,7 @@ struct item_handle
private:
item_handle();
uint16_t m_column;
uint16_t m_item_ix;
row_handle &m_row_handle;
void assign_value(const item &value);
@@ -556,7 +556,7 @@ struct item_handle::item_value_as<T, std::enable_if_t<std::is_arithmetic_v<T> an
auto txt = ref.text();
if (txt.empty())
if (ref.empty())
result = 1;
else
{

View File

@@ -90,7 +90,7 @@ class iterator_impl
: m_category(rhs.m_category)
, m_current(rhs.m_current)
, 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
: m_category(rhs.m_category)
, m_current(const_cast<row_type *>(rhs.m_current))
, 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>());
}
@@ -108,7 +108,7 @@ class iterator_impl
iterator_impl(const iterator_impl<IRowType> &rhs, const std::array<uint16_t, N> &cix)
: m_category(rhs.m_category)
, m_current(rhs.m_current)
, m_column_ix(cix)
, m_item_ix(cix)
{
m_value = get(std::make_index_sequence<N>());
}
@@ -117,7 +117,7 @@ class iterator_impl
{
m_category = i.m_category;
m_current = i.m_current;
m_column_ix = i.m_column_ix;
m_item_ix = i.m_item_ix;
m_value = i.m_value;
return *this;
}
@@ -185,7 +185,7 @@ class iterator_impl
if (m_current != nullptr)
{
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 {};
@@ -194,7 +194,7 @@ class iterator_impl
category_type *m_category = nullptr;
row_type *m_current = nullptr;
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>
: m_category(rhs.m_category)
, m_current(rhs.m_current)
, 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>
: m_category(rhs.m_category)
, m_current(const_cast<row_type *>(rhs.m_current))
, m_value(rhs.m_value)
, m_column_ix(rhs.m_column_ix)
, m_item_ix(rhs.m_item_ix)
{
m_value = get(m_current);
}
@@ -366,7 +366,7 @@ class iterator_impl<Category, T>
iterator_impl(const iterator_impl<IRowType> &rhs, const std::array<uint16_t, 1> &cix)
: m_category(rhs.m_category)
, m_current(rhs.m_current)
, m_column_ix(cix[0])
, m_item_ix(cix[0])
{
m_value = get();
}
@@ -375,7 +375,7 @@ class iterator_impl<Category, T>
{
m_category = i.m_category;
m_current = i.m_current;
m_column_ix = i.m_column_ix;
m_item_ix = i.m_item_ix;
m_value = i.m_value;
return *this;
}
@@ -442,7 +442,7 @@ class iterator_impl<Category, T>
if (m_current != nullptr)
{
row_handle rh{ *m_category, *m_current };
return rh[m_column_ix].template as<T>();
return rh[m_item_ix].template as<T>();
}
return {};
@@ -451,7 +451,7 @@ class iterator_impl<Category, T>
category_type *m_category = nullptr;
row_type *m_current = nullptr;
value_type m_value;
uint16_t m_column_ix;
uint16_t m_item_ix;
};
// --------------------------------------------------------------------
@@ -482,8 +482,8 @@ class iterator_proxy
using iterator = iterator_impl<category_type, Ts...>;
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, std::initializer_list<char const *> columns);
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 *> items);
iterator_proxy(iterator_proxy &&p);
iterator_proxy &operator=(iterator_proxy &&p);
@@ -492,8 +492,8 @@ class iterator_proxy
iterator_proxy &operator=(const iterator_proxy &) = delete;
/** @endcond */
iterator begin() const { return iterator(m_begin, m_column_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 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_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
explicit operator bool() const { return not empty(); } ///< Easy way to detect if the range is empty
@@ -510,13 +510,13 @@ class iterator_proxy
std::swap(m_category, rhs.m_category);
std::swap(m_begin, rhs.m_begin);
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:
category_type *m_category;
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
reference operator*()
{
return *mBegin;
return *m_begin;
}
pointer operator->()
{
return &*mBegin;
m_current = *m_begin;
return &m_current;
}
conditional_iterator_impl &operator++()
{
while (mBegin != mEnd)
while (m_begin != m_end)
{
if (++mBegin == mEnd)
if (++m_begin == m_end)
break;
if (m_condition->operator()(mBegin))
if (m_condition->operator()(m_begin))
break;
}
@@ -591,18 +592,22 @@ class conditional_iterator_proxy
return result;
}
bool operator==(const conditional_iterator_impl &rhs) const { return mBegin == rhs.mBegin; }
bool operator!=(const conditional_iterator_impl &rhs) const { return mBegin != rhs.mBegin; }
bool operator==(const conditional_iterator_impl &rhs) const { return m_begin == rhs.m_begin; }
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>
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>
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:
CategoryType *mCat;
base_iterator mBegin, mEnd;
CategoryType *m_cat;
base_iterator m_begin, m_end;
value_type m_current;
const condition *m_condition;
};
@@ -646,26 +651,26 @@ class conditional_iterator_proxy
/** @cond */
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_begin(pos)
, m_end(cat.end())
{
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>
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_begin(pos)
, 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;
for (auto column : columns)
m_column_ix[i++] = m_category->get_column_ix(column);
for (auto item : items)
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,
template <typename Category, typename... Ts>
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)
: mCat(&cat)
, mBegin(pos, cix)
, mEnd(cat.end(), cix)
: m_cat(&cat)
, m_begin(pos, cix)
, m_end(cat.end(), cix)
, m_condition(&cond)
{
if (m_condition == nullptr or m_condition->empty())
mBegin = mEnd;
m_begin = m_end;
}
template <typename Category, typename... Ts>
@@ -702,7 +707,7 @@ conditional_iterator_proxy<Category, Ts...>::conditional_iterator_proxy(Category
, mCBegin(pos)
, 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)
{
@@ -715,7 +720,7 @@ conditional_iterator_proxy<Category, Ts...>::conditional_iterator_proxy(Category
mCBegin = mCEnd;
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>

View File

@@ -72,7 +72,7 @@ class structure;
*
* The class atom is a kind of flyweight class. It can be copied
* 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.
*
* It is also possible to have symmetry copies of atoms. They
@@ -207,7 +207,7 @@ class atom
/// \brief Copy assignement operator
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
{
if (not m_impl)
@@ -215,7 +215,7 @@ class atom
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
{
if (not m_impl)
@@ -223,7 +223,7 @@ class atom
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
{
if (not m_impl)
@@ -231,7 +231,7 @@ class atom
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)
{
if (not m_impl)
@@ -239,7 +239,7 @@ class atom
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>
void set_property(const std::string_view name, const T &value)
{
@@ -730,7 +730,7 @@ class sugar : public residue
/**
* @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
* and auth_seq_id is specified to contain a string, we do a check here
* to see if it really is a number.

View File

@@ -143,9 +143,9 @@ class sac_parser
enum class CIFToken
{
Unknown,
UNKNOWN,
Eof,
END_OF_FILE,
DATA,
LOOP,
@@ -153,24 +153,24 @@ class sac_parser
SAVE_,
SAVE_NAME,
STOP,
Tag,
Value
ITEM_NAME,
VALUE
};
static constexpr const char *get_token_name(CIFToken token)
{
switch (token)
{
case CIFToken::Unknown: return "Unknown";
case CIFToken::Eof: return "Eof";
case CIFToken::UNKNOWN: return "Unknown";
case CIFToken::END_OF_FILE: return "Eof";
case CIFToken::DATA: return "DATA";
case CIFToken::LOOP: return "LOOP";
case CIFToken::GLOBAL: return "GLOBAL";
case CIFToken::SAVE_: return "SAVE";
case CIFToken::SAVE_NAME: return "SAVE+name";
case CIFToken::STOP: return "STOP";
case CIFToken::Tag: return "Tag";
case CIFToken::Value: return "Value";
case CIFToken::ITEM_NAME: return "Tag";
case CIFToken::VALUE: return "Value";
default: return "Invalid token parameter";
}
}
@@ -267,9 +267,9 @@ class sac_parser
QuotedString,
QuotedStringQuote,
UnquotedString,
Tag,
TextField,
TextFieldNL,
ItemName,
TextItem,
TextItemNL,
Reserved,
Value
};

View File

@@ -28,6 +28,8 @@
#include "cif++/file.hpp"
#include <system_error>
/**
* @file pdb.hpp
*
@@ -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 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
* 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
* atom_site -> pdbx_poly_seq_scheme -> entity_poly_seq -> entity_poly -> entity
*
* 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 dictionary The mmcif dictionary to use
@@ -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");
/** \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

View File

@@ -35,7 +35,10 @@
#if __has_include(<clipper/core/coords.h>)
#define HAVE_LIBCLIPPER 1
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wignored-qualifiers"
#include <clipper/core/coords.h>
#pragma GCC diagnostic pop
#endif
/** \file point.hpp

View File

@@ -51,7 +51,7 @@
* std::string name = rh["label_atom_id"].as<std::string>();
*
* // 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);
* @endcode
*
@@ -87,15 +87,15 @@ namespace detail
{
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_columns(std::move(columns))
, m_items(std::move(items))
{
}
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>
@@ -107,11 +107,11 @@ namespace detail
template <typename... Ts, size_t... Is>
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;
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
@@ -244,70 +244,70 @@ class row_handle
return not empty();
}
/// \brief return a cif::item_handle to the item in column @a column_ix
item_handle operator[](uint16_t column_ix)
/// \brief return a cif::item_handle to the item in item @a item_ix
item_handle operator[](uint16_t item_ix)
{
return empty() ? item_handle::s_null_item : item_handle(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
const item_handle operator[](uint16_t column_ix) const
/// \brief return a const cif::item_handle to the item in item @a item_ix
const item_handle operator[](uint16_t item_ix) const
{
return empty() ? item_handle::s_null_item : item_handle(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
item_handle operator[](std::string_view column_name)
/// \brief return a cif::item_handle to the item in the item named @a item_name
item_handle operator[](std::string_view item_name)
{
return empty() ? item_handle::s_null_item : item_handle(add_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
const item_handle operator[](std::string_view column_name) const
/// \brief return a const cif::item_handle to the item in the item named @a item_name
const item_handle operator[](std::string_view item_name) const
{
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
/// to assign the values for the columns @a columns
/// to assign the values for the items @a items
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>
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>
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>
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)
{
for (auto &value : values)
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.
* 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
* this record in the linked category is updated as well.
*
@@ -317,13 +317,13 @@ class row_handle
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.
* 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
* this record in the linked category is updated as well.
*
@@ -331,7 +331,7 @@ class row_handle
* 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
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
bool operator!=(const row_handle &rhs) const { return m_category != rhs.m_category or m_row != rhs.m_row; }
private:
uint16_t get_column_ix(std::string_view name) const;
std::string_view get_column_name(uint16_t ix) const;
uint16_t get_item_ix(std::string_view name) 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()
{
@@ -360,7 +360,7 @@ class row_handle
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;
row *m_row = nullptr;

View File

@@ -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.
*
@@ -325,7 +325,19 @@ inline char tolower(int ch)
* 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);
// --------------------------------------------------------------------

View File

@@ -28,9 +28,11 @@
#include "cif++/text.hpp"
#include <cassert>
#include <filesystem>
#include <list>
#include <mutex>
#include <system_error>
#include <utility>
/**
@@ -49,27 +51,148 @@ namespace cif
struct category_validator;
// --------------------------------------------------------------------
// New: error_code
/**
* @brief The exception thrown when a validation error occurs
* @enum validation_error
*
* @brief A stronly typed class containing the error codes reported by @ref cif::validator and friends
*/
enum class validation_error
{
value_does_not_match_rx = 1, /**< The value of an item does not conform to the regular expression specified for it */
value_is_not_in_enumeration_list, /**< The value of an item is not in the list of values allowed */
not_a_known_primitive_type, /**< The type is not a known primitive type */
undefined_category, /**< Category has no definition in the dictionary */
unknown_item, /**< The item is not defined to be part of the category */
incorrect_item_validator, /**< Incorrectly specified validator for item */
missing_mandatory_items, /**< Missing mandatory items */
missing_key_items, /**< An index could not be constructed due to missing key items */
item_not_allowed_in_category, /**< Requested item allowed in category according to dictionary */
empty_file, /**< The file contains no datablocks */
empty_datablock, /**< The datablock contains no categories */
empty_category, /**< The category is empty */
not_valid_pdbx, /**< The file is not a valid PDBx file */
};
/**
* @brief The implementation for @ref validation_category error messages
*
*/
class validation_error : public std::exception
class validation_category_impl : public std::error_category
{
public:
/// @brief Constructor
validation_error(const std::string &msg);
/**
* @brief User friendly name
*
* @return const char*
*/
/// @brief Constructor
validation_error(const std::string &cat, const std::string &item,
const std::string &msg);
const char *name() const noexcept override
{
return "cif::validation";
}
/// @brief The description of the error
const char *what() const noexcept { return m_msg.c_str(); }
/**
* @brief Provide the error message as a string for the error code @a ev
*
* @param ev The error code
* @return std::string
*/
/// @cond
std::string m_msg;
/// @endcond
std::string message(int ev) const override
{
switch (static_cast<validation_error>(ev))
{
case validation_error::value_does_not_match_rx:
return "Value in item does not match regular expression";
case validation_error::value_is_not_in_enumeration_list:
return "Value is not in the enumerated list of valid values";
case validation_error::not_a_known_primitive_type:
return "The type is not a known primitive type";
case validation_error::undefined_category:
return "Category has no definition in the dictionary";
case validation_error::unknown_item:
return "Item is not defined to be part of the category";
case validation_error::incorrect_item_validator:
return "Incorrectly specified validator for item";
case validation_error::missing_mandatory_items:
return "Missing mandatory items";
case validation_error::missing_key_items:
return "An index could not be constructed due to missing key items";
case validation_error::item_not_allowed_in_category:
return "Requested item allowed in category according to dictionary";
case validation_error::empty_file:
return "The file contains no datablocks";
case validation_error::empty_datablock:
return "The datablock contains no categories";
case validation_error::empty_category:
return "The category is empty";
case validation_error::not_valid_pdbx:
return "The file is not a valid PDBx file";
default:
assert(false);
return "unknown error code";
}
}
/**
* @brief Return whether two error codes are equivalent, always false in this case
*
*/
bool equivalent(const std::error_code & /*code*/, int /*condition*/) const noexcept override
{
return false;
}
};
/**
* @brief Return the implementation for the validation_category
*
* @return std::error_category&
*/
inline std::error_category &validation_category()
{
static validation_category_impl instance;
return instance;
}
inline std::error_code make_error_code(validation_error e)
{
return std::error_code(static_cast<int>(e), validation_category());
}
inline std::error_condition make_error_condition(validation_error e)
{
return std::error_condition(static_cast<int>(e), validation_category());
}
// --------------------------------------------------------------------
class validation_exception : public std::runtime_error
{
public:
validation_exception(validation_error err)
: validation_exception(make_error_code(err))
{
}
validation_exception(validation_error err, std::string_view category)
: validation_exception(make_error_code(err), category)
{
}
validation_exception(validation_error err, std::string_view category, std::string_view item)
: validation_exception(make_error_code(err), category, item)
{
}
validation_exception(std::error_code ec);
validation_exception(std::error_code ec, std::string_view category);
validation_exception(std::error_code ec, std::string_view category, std::string_view item);
};
// --------------------------------------------------------------------
@@ -85,6 +208,9 @@ enum class DDL_PrimitiveType
/// @brief Return the DDL_PrimitiveType encoded in @a s
DDL_PrimitiveType map_to_primitive_type(std::string_view s);
/// @brief Return the DDL_PrimitiveType encoded in @a s, error reporting variant
DDL_PrimitiveType map_to_primitive_type(std::string_view s, std::error_code &ec) noexcept;
struct regex_impl;
/**
@@ -146,6 +272,26 @@ struct type_validator
int compare(std::string_view a, std::string_view b) const;
};
/** @brief Item alias, items can be renamed over time
*/
struct item_alias
{
item_alias(const std::string &alias_name, const std::string &dictionary, const std::string &version)
: m_name(alias_name)
, m_dict(dictionary)
, m_vers(version)
{
}
item_alias(const item_alias &) = default;
item_alias &operator=(const item_alias &) = default;
std::string m_name; ///< The alias_name
std::string m_dict; ///< The dictionary in which it was known
std::string m_vers; ///< The version of the dictionary
};
/**
* @brief An item_validator binds a type_validator to an item in
* a category along with other information found in the dictionary.
@@ -157,28 +303,32 @@ struct type_validator
*/
struct item_validator
{
std::string m_tag; ///< The item name
std::string m_item_name; ///< The item name
bool m_mandatory; ///< Flag indicating this item is mandatory
const type_validator *m_type; ///< The type for this item
cif::iset m_enums; ///< If filled, the set of allowed values
std::string m_default; ///< If filled, a default value for this item
category_validator *m_category = nullptr; ///< The category_validator this item_validator belongs to
std::vector<item_alias> m_aliases; ///< The aliases for this item
/// @brief Compare based on the name
bool operator<(const item_validator &rhs) const
{
return icompare(m_tag, rhs.m_tag) < 0;
return icompare(m_item_name, rhs.m_item_name) < 0;
}
/// @brief Compare based on the name
bool operator==(const item_validator &rhs) const
{
return iequals(m_tag, rhs.m_tag);
return iequals(m_item_name, rhs.m_item_name);
}
/// @brief Validate the value in @a value for this item
/// Will throw a validation_error exception if it fails
/// Will throw a std::system_error exception if it fails
void operator()(std::string_view value) const;
/// @brief A more gentle version of value validation
bool validate_value(std::string_view value, std::error_code &ec) const noexcept;
};
/**
@@ -191,8 +341,8 @@ struct category_validator
{
std::string m_name; ///< The name of the category
std::vector<std::string> m_keys; ///< The list of items that make up the key
cif::iset m_groups; ///< The category groups this category belongs to
cif::iset m_mandatory_fields; ///< The mandatory fields for this category
cif::iset m_groups; ///< The category groups this category belongs to
cif::iset m_mandatory_items; ///< The mandatory items for this category
std::set<item_validator> m_item_validators; ///< The item validators for the items in this category
/// @brief return true if this category sorts before @a rhs
@@ -202,10 +352,13 @@ struct category_validator
}
/// @brief Add item_validator @a v to the list of item validators
void addItemValidator(item_validator &&v);
void add_item_validator(item_validator &&v);
/// @brief Return the item_validator for item @a tag, may return nullptr
const item_validator *get_validator_for_item(std::string_view tag) const;
/// @brief Return the item_validator for item @a item_name, may return nullptr
const item_validator *get_validator_for_item(std::string_view item_name) const;
/// @brief Return the item_validator for an item that has as alias name @a item_name, may return nullptr
const item_validator *get_validator_for_aliased_item(std::string_view item_name) const;
};
/**
@@ -284,7 +437,24 @@ class validator
std::vector<const link_validator *> get_links_for_child(std::string_view category) const;
/// @brief Bottleneck function to report an error in validation
void report_error(const std::string &msg, bool fatal) const;
void report_error(validation_error err, bool fatal = true) const
{
report_error(make_error_code(err), fatal);
}
/// @brief Bottleneck function to report an error in validation
void report_error(std::error_code ec, bool fatal = true) const;
/// @brief Bottleneck function to report an error in validation
void report_error(validation_error err, std::string_view category,
std::string_view item, bool fatal = true) const
{
report_error(make_error_code(err), category, item, fatal);
}
/// @brief Bottleneck function to report an error in validation
void report_error(std::error_code ec, std::string_view category,
std::string_view item, bool fatal = true) const;
const std::string &name() const { return m_name; } ///< Get the name of this validator
void set_name(const std::string &name) { m_name = name; } ///< Set the name of this validator

View File

@@ -2712,4 +2712,96 @@ _pdbx_chem_comp_audit.processing_site
HIS "Create component" 1999-07-08 EBI
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
##

File diff suppressed because it is too large Load Diff

View File

@@ -310,6 +310,18 @@ float compound::bond_length(const std::string &atomId_1, const std::string &atom
return result;
}
// --------------------------------------------------------------------
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
@@ -660,25 +672,67 @@ void compound_factory::pop_dictionary()
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)
report_missing_compound(id);
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(resName) > 0;
return kAAMap.count(res_name) > 0;
}
bool compound_factory::is_known_base(const std::string &resName) const
bool compound_factory::is_known_base(const std::string &res_name) const
{
return kBaseMap.count(resName) > 0;
return kBaseMap.count(res_name) > 0;
}
void compound_factory::report_missing_compound(const std::string &compound_id)
/// 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 kBaseMap.count(std::string{ res_name }) > 0;
}
/// Return whether @a res_name is a monomer (either base or peptide)
bool compound_factory::is_monomer(std::string_view res_name) const
{
return is_peptide(res_name) or is_base(res_name);
}
void compound_factory::report_missing_compound(std::string_view compound_id)
{
static bool s_reported = false;
if (std::exchange(s_reported, true) == false)

View File

@@ -30,17 +30,17 @@
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;
@@ -63,14 +63,14 @@ namespace detail
condition_impl *key_equals_condition_impl::prepare(const category &c)
{
m_item_ix = c.get_column_ix(m_item_tag);
m_icase = is_column_type_uchar(c, m_item_tag);
m_item_ix = c.get_item_ix(m_item_name);
m_icase = is_item_type_uchar(c, m_item_name);
if (c.get_cat_validator() != nullptr and
c.key_field_indices().contains(m_item_ix) and
c.key_field_indices().size() == 1)
c.key_item_indices().contains(m_item_ix) and
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;

View File

@@ -85,6 +85,40 @@ bool datablock::is_valid() const
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 result = true;
@@ -143,13 +177,6 @@ std::tuple<datablock::iterator, bool> datablock::emplace(std::string_view name)
if (iequals(name, i->name()))
{
is_new = false;
if (i != begin())
{
auto n = std::next(i);
splice(begin(), *this, i, n);
}
break;
}
@@ -158,25 +185,24 @@ std::tuple<datablock::iterator, bool> datablock::emplace(std::string_view name)
if (is_new)
{
auto &c = emplace_back(name);
c.set_validator(m_validator, *this);
i = insert(end(), {name});
i->set_validator(m_validator, *this);
}
assert(end() != begin());
return std::make_tuple(std::prev(end()), is_new);
assert(i != end());
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;
// for entry and audit_conform on top
auto ci = find_if(begin(), end(), [](const category &cat)
{ return cat.name() == "entry"; });
if (ci != end())
{
auto cto = ci->get_tag_order();
auto cto = ci->get_item_order();
result.insert(result.end(), cto.begin(), cto.end());
}
@@ -184,7 +210,7 @@ std::vector<std::string> datablock::get_tag_order() const
{ return cat.name() == "audit_conform"; });
if (ci != end())
{
auto cto = ci->get_tag_order();
auto cto = ci->get_item_order();
result.insert(result.end(), cto.begin(), cto.end());
}
@@ -192,7 +218,7 @@ std::vector<std::string> datablock::get_tag_order() const
{
if (cat.name() == "entry" or cat.name() == "audit_conform")
continue;
auto cto = cat.get_tag_order();
auto cto = cat.get_item_order();
result.insert(result.end(), cto.begin(), cto.end());
}
@@ -251,16 +277,6 @@ void datablock::write(std::ostream &os) const
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
cat_order_t cat_order;
@@ -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'
<< "# \n";
std::vector<std::string> cat_order;
for (auto &o : tag_order)
for (auto &o : item_name_order)
{
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
{ return iequals(cat_name, s); }) == cat_order.rend())
cat_order.push_back(cat_name);
@@ -349,10 +365,10 @@ void datablock::write(std::ostream &os, const std::vector<std::string> &tag_orde
continue;
std::vector<std::string> items;
for (auto &o : tag_order)
for (auto &o : item_name_order)
{
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)
items.push_back(item_name);
@@ -374,6 +390,10 @@ void datablock::write(std::ostream &os, const std::vector<std::string> &tag_orde
bool datablock::operator==(const datablock &rhs) const
{
// shortcut
if (this == &rhs)
return true;
auto &dbA = *this;
auto &dbB = rhs;

View File

@@ -50,7 +50,7 @@ class dictionary_parser : public parser
try
{
while (m_lookahead != CIFToken::Eof)
while (m_lookahead != CIFToken::END_OF_FILE)
{
switch (m_lookahead)
{
@@ -87,7 +87,7 @@ class dictionary_parser : public parser
error("Undefined category '" + iv.first);
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
@@ -128,7 +128,7 @@ class dictionary_parser : public parser
datablock::iterator cat = dict.end();
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)
{
@@ -136,30 +136,30 @@ class dictionary_parser : public parser
match(CIFToken::LOOP);
std::vector<std::string> tags;
while (m_lookahead == CIFToken::Tag)
std::vector<std::string> item_names;
while (m_lookahead == CIFToken::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())
std::tie(cat, std::ignore) = dict.emplace(catName);
else if (not iequals(cat->name(), catName))
error("inconsistent categories in loop_");
tags.push_back(item_name);
match(CIFToken::Tag);
item_names.push_back(item_name);
match(CIFToken::ITEM_NAME);
}
while (m_lookahead == CIFToken::Value)
while (m_lookahead == CIFToken::VALUE)
{
cat->emplace({});
auto row = cat->back();
for (auto tag : tags)
for (auto item_name : item_names)
{
row[tag] = m_token_value;
match(CIFToken::Value);
row[item_name] = m_token_value;
match(CIFToken::VALUE);
}
}
@@ -168,18 +168,18 @@ class dictionary_parser : public parser
else
{
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))
std::tie(cat, std::ignore) = dict.emplace(catName);
match(CIFToken::Tag);
match(CIFToken::ITEM_NAME);
if (cat->empty())
cat->emplace({});
cat->back()[item_name] = m_token_value;
match(CIFToken::Value);
match(CIFToken::VALUE);
}
}
@@ -191,7 +191,7 @@ class dictionary_parser : public parser
std::vector<std::string> keys;
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;
for (auto g : dict["category_group"])
@@ -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
for (auto i : dict["item"])
{
std::string tagName, category, mandatory;
cif::tie(tagName, category, mandatory) = i.get("name", "category_id", "mandatory_code");
std::string item, category, mandatory;
cif::tie(item, category, mandatory) = i.get("name", "category_id", "mandatory_code");
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())
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 == "?"))
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
category = cat_name;
@@ -245,7 +252,7 @@ class dictionary_parser : public parser
auto vi = find(ivs.begin(), ivs.end(), item_validator{ item_name });
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
{
// need to update the itemValidator?
@@ -253,22 +260,22 @@ class dictionary_parser : public parser
{
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';
else
std::cerr << "choosing " << (vi->m_mandatory ? "Y" : "N") << '\n';
}
if (iequals(tagName, saveFrameName))
if (iequals(item, saveFrameName))
vi->m_mandatory = (iequals(mandatory, "yes"));
}
if (vi->m_type != nullptr and tv != nullptr and vi->m_type != tv)
{
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"));
@@ -351,7 +358,7 @@ class dictionary_parser : public parser
}
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
@@ -379,7 +386,7 @@ class dictionary_parser : public parser
}
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
for (auto &iv : cv.m_item_validators)
{
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';
}
}
}

View File

@@ -158,13 +158,6 @@ std::tuple<file::iterator, bool> file::emplace(std::string_view name)
if (iequals(name, i->name()))
{
is_new = false;
if (i != begin())
{
auto n = std::next(i);
splice(begin(), *this, i, n);
}
break;
}
@@ -173,12 +166,12 @@ std::tuple<file::iterator, bool> file::emplace(std::string_view name)
if (is_new)
{
auto &db = emplace_back(name);
db.set_validator(m_validator);
i = insert(end(), { name });
i->set_validator(m_validator);
}
assert(begin() != end());
return std::make_tuple(std::prev(end()), is_new);
assert(i != end());
return std::make_tuple(i, is_new);
}
void file::load(const std::filesystem::path &p)

View File

@@ -35,7 +35,7 @@ const item_handle item_handle::s_null_item;
row_handle s_null_row_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)
{
}
@@ -44,7 +44,7 @@ std::string_view item_handle::text() const
{
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)
return iv->text();
}
@@ -55,14 +55,14 @@ std::string_view item_handle::text() const
void item_handle::assign_value(const item &v)
{
assert(not m_row_handle.empty());
m_row_handle.assign(m_column, v.value(), true);
m_row_handle.assign(m_item_ix, v.value(), true);
}
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);
m_row_handle.swap(m_column, b.m_row_handle);
m_row_handle.swap(m_item_ix, b.m_row_handle);
}
}

View File

@@ -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
// {
// for (auto &&[tag, ref] : mCachedRefs)
// for (auto &&[item_name, ref] : mCachedRefs)
// {
// if (tag == name)
// if (item_name == name)
// return ref.as<std::string>();
// }
@@ -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)
// {
// for (auto &&[tag, ref] : mCachedRefs)
// for (auto &&[item_name, ref] : mCachedRefs)
// {
// if (tag != name)
// if (item_name != name)
// continue;
// ref = value;
@@ -1783,7 +1783,7 @@ atom &structure::emplace_atom(atom &&atom)
std::string symbol = atom.get_property("type_symbol");
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 } });
return m_atoms.emplace_back(std::move(atom));
@@ -1969,7 +1969,7 @@ void structure::change_residue(residue &res, const std::string &newCompound,
// create rest
auto &chemComp = m_db["chem_comp"];
if (not chemComp.exists(key("id") == newCompound))
if (not chemComp.contains(key("id") == newCompound))
{
chemComp.emplace({{"id", newCompound},
{"name", compound->name()},
@@ -2702,7 +2702,7 @@ void structure::cleanup_empty_categories()
for (auto chemComp : chem_comp)
{
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;
obsoleteChemComps.push_back(chemComp);
@@ -2719,7 +2719,7 @@ void structure::cleanup_empty_categories()
for (auto entity : entities)
{
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;
obsoleteEntities.push_back(entity);

View File

@@ -269,7 +269,7 @@ sac_parser::CIFToken sac_parser::get_next_token()
{
const auto kEOF = std::char_traits<char>::eof();
CIFToken result = CIFToken::Unknown;
CIFToken result = CIFToken::UNKNOWN;
int quoteChar = 0;
State state = State::Start;
m_bol = false;
@@ -279,7 +279,7 @@ sac_parser::CIFToken sac_parser::get_next_token()
reserved_words_automaton dag;
while (result == CIFToken::Unknown)
while (result == CIFToken::UNKNOWN)
{
auto ch = get_next_char();
@@ -287,7 +287,7 @@ sac_parser::CIFToken sac_parser::get_next_token()
{
case State::Start:
if (ch == kEOF)
result = CIFToken::Eof;
result = CIFToken::END_OF_FILE;
else if (ch == '\n')
{
m_bol = true;
@@ -298,9 +298,9 @@ sac_parser::CIFToken sac_parser::get_next_token()
else if (ch == '#')
state = State::Comment;
else if (ch == '_')
state = State::Tag;
state = State::ItemName;
else if (ch == ';' and m_bol)
state = State::TextField;
state = State::TextItem;
else if (ch == '?')
state = State::QuestionMark;
else if (ch == '\'' or ch == '"')
@@ -316,7 +316,7 @@ sac_parser::CIFToken sac_parser::get_next_token()
case State::White:
if (ch == kEOF)
result = CIFToken::Eof;
result = CIFToken::END_OF_FILE;
else if (not is_space(ch))
{
state = State::Start;
@@ -335,7 +335,7 @@ sac_parser::CIFToken sac_parser::get_next_token()
m_token_buffer.clear();
}
else if (ch == kEOF)
result = CIFToken::Eof;
result = CIFToken::END_OF_FILE;
else if (not is_any_print(ch))
error("invalid character in comment");
break;
@@ -344,29 +344,29 @@ sac_parser::CIFToken sac_parser::get_next_token()
if (not is_non_blank(ch))
{
retract();
result = CIFToken::Value;
result = CIFToken::VALUE;
}
else
state = State::Value;
break;
case State::TextField:
case State::TextItem:
if (ch == '\n')
state = State::TextFieldNL;
state = State::TextItemNL;
else if (ch == kEOF)
error("unterminated textfield");
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) + ")");
break;
case State::TextFieldNL:
case State::TextItemNL:
if (is_text_lead(ch) or ch == ' ' or ch == '\t')
state = State::TextField;
state = State::TextItem;
else if (ch == ';')
{
assert(m_token_buffer.size() >= 2);
m_token_value = std::string_view(m_token_buffer.data() + 1, m_token_buffer.size() - 3);
result = CIFToken::Value;
result = CIFToken::VALUE;
}
else if (ch == kEOF)
error("unterminated textfield");
@@ -387,7 +387,7 @@ sac_parser::CIFToken sac_parser::get_next_token()
if (is_white(ch))
{
retract();
result = CIFToken::Value;
result = CIFToken::VALUE;
if (m_token_buffer.size() < 2)
error("Invalid quoted string token");
@@ -403,11 +403,11 @@ sac_parser::CIFToken sac_parser::get_next_token()
error("invalid character in quoted string");
break;
case State::Tag:
case State::ItemName:
if (not is_non_blank(ch))
{
retract();
result = CIFToken::Tag;
result = CIFToken::ITEM_NAME;
m_token_value = std::string_view(m_token_buffer.data(), m_token_buffer.size());
}
break;
@@ -422,7 +422,7 @@ sac_parser::CIFToken sac_parser::get_next_token()
if (not is_non_blank(ch))
{
retract();
result = CIFToken::Value;
result = CIFToken::VALUE;
m_token_value = std::string_view(m_token_buffer.data(), m_token_buffer.size());
}
else
@@ -467,7 +467,7 @@ sac_parser::CIFToken sac_parser::get_next_token()
if (not is_non_blank(ch))
{
retract();
result = CIFToken::Value;
result = CIFToken::VALUE;
m_token_value = std::string_view(m_token_buffer.data(), m_token_buffer.size());
break;
}
@@ -483,7 +483,7 @@ sac_parser::CIFToken sac_parser::get_next_token()
if (VERBOSE >= 5)
{
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 << '\n';
}
@@ -710,7 +710,7 @@ bool sac_parser::parse_single_datablock(const std::string &datablock, const data
void sac_parser::parse_file()
{
while (m_lookahead != CIFToken::Eof)
while (m_lookahead != CIFToken::END_OF_FILE)
{
switch (m_lookahead)
{
@@ -735,10 +735,10 @@ void sac_parser::parse_file()
void sac_parser::parse_global()
{
match(CIFToken::GLOBAL);
while (m_lookahead == CIFToken::Tag)
while (m_lookahead == CIFToken::ITEM_NAME)
{
match(CIFToken::Tag);
match(CIFToken::Value);
match(CIFToken::ITEM_NAME);
match(CIFToken::VALUE);
}
}
@@ -747,7 +747,7 @@ void sac_parser::parse_datablock()
static const std::string kUnitializedCategory("<invalid>");
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)
{
@@ -757,12 +757,12 @@ void sac_parser::parse_datablock()
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::tie(catName, itemName) = split_tag_name(m_token_value);
std::tie(catName, itemName) = split_item_name(m_token_value);
if (cat == kUnitializedCategory)
{
@@ -772,19 +772,19 @@ void sac_parser::parse_datablock()
else if (not iequals(cat, catName))
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();
for (auto tag : tags)
for (auto item_name : item_names)
{
produce_item(cat, tag, m_token_value);
match(CIFToken::Value);
produce_item(cat, item_name, m_token_value);
match(CIFToken::VALUE);
}
}
@@ -792,10 +792,10 @@ void sac_parser::parse_datablock()
break;
}
case CIFToken::Tag:
case CIFToken::ITEM_NAME:
{
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))
{
@@ -804,11 +804,11 @@ void sac_parser::parse_datablock()
produce_row();
}
match(CIFToken::Tag);
match(CIFToken::ITEM_NAME);
produce_item(cat, itemName, m_token_value);
match(CIFToken::Value);
match(CIFToken::VALUE);
break;
}

View File

@@ -1123,9 +1123,6 @@ void PDBFileParser::PreParseInput(std::istream &is)
if (lookahead.back() == '\r')
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
{
std::string cs = lookahead.substr(offset, len);
@@ -1558,52 +1555,54 @@ void PDBFileParser::ParseTitle()
// 11 - 80 Specification compound Description of the molecular components.
// list
std::string value{ mRec->vS(11) };
if (value.find(':') == std::string::npos)
{
// special case for dumb, stripped files
auto &comp = GetOrCreateCompound(1);
comp.mInfo["MOLECULE"] = value;
}
else
{
SpecificationListParser p(value);
for (;;)
{
std::string key, val;
std::tie(key, val) = p.GetNextSpecification();
if (key.empty())
break;
if (not iequals(key, "MOL_ID") and mCompounds.empty())
{
if (cif::VERBOSE > 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;
}
}
if (mRec->is("COMPND"))
{
std::string value{ mRec->vS(11) };
if (value.find(':') == std::string::npos)
{
// special case for dumb, stripped files
auto &comp = GetOrCreateCompound(1);
comp.mInfo["MOLECULE"] = value;
}
else
{
SpecificationListParser p(value);
for (;;)
{
std::string key, val;
std::tie(key, val) = p.GetNextSpecification();
if (key.empty())
break;
if (not iequals(key, "MOL_ID") and mCompounds.empty())
{
if (cif::VERBOSE > 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;
}
}
GetNextRecord();
}
// SOURCE
Match("SOURCE", false);
@@ -1740,7 +1739,7 @@ void PDBFileParser::ParseTitle()
int n = 1;
cat = getCategory("audit_author");
value = { mRec->vS(11) };
std::string value = { mRec->vS(11) };
for (auto author : cif::split<std::string>(value, ",", true))
{
// clang-format off
@@ -3646,7 +3645,7 @@ void PDBFileParser::ConstructEntities()
PDBChain::AtomRes ar{ resName, resSeq, iCode };
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);
}
@@ -3731,11 +3730,8 @@ void PDBFileParser::ConstructEntities()
{
std::string resName = chain.mResiduesSeen[ix].mMonID;
if (cif::compound_factory::instance().is_known_peptide(resName) or
cif::compound_factory::instance().is_known_base(resName))
{
if (cif::compound_factory::instance().is_monomer(resName))
chain.mTerIndex = ix + 1;
}
InsertChemComp(resName);
}
@@ -3814,7 +3810,7 @@ void PDBFileParser::ConstructEntities()
int residueCount = (residuePerChainCounter[chainID] += 1);
// 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
(chain.mTerIndex > 0 and residueCount >= chain.mTerIndex))
{
@@ -4559,7 +4555,7 @@ void PDBFileParser::ConstructEntities()
std::string formula;
std::string type;
std::string nstd = ".";
std::string formulaWeight;
std::optional<float> formulaWeight;
if (compound != nullptr)
{
@@ -4570,7 +4566,7 @@ void PDBFileParser::ConstructEntities()
nstd = "y";
formula = compound->formula();
formulaWeight = std::to_string(compound->formula_weight());
formulaWeight = compound->formula_weight();
}
if (name.empty())
@@ -4597,7 +4593,7 @@ void PDBFileParser::ConstructEntities()
{ "id", cc },
{ "name", name },
{ "formula", formula },
{ "formula_weight", formulaWeight },
{ "formula_weight", formulaWeight, 3 },
{ "mon_nstd_flag", nstd },
{ "type", type }
});
@@ -4712,7 +4708,7 @@ void PDBFileParser::ConstructEntities()
}
if (formula_weight > 0)
entity["formula_weight"] = formula_weight;
entity.assign({ { "formula_weight", formula_weight, 3 } });
}
}
@@ -5581,31 +5577,6 @@ void PDBFileParser::ParseCrystallographic()
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()
@@ -6466,7 +6437,12 @@ file read(std::istream &is)
// and so the very first character in a valid PDB file
// 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);
else
{

View File

@@ -1493,8 +1493,8 @@ bool Remark3Parser::parse(const std::string &expMethod, PDBRecord *r, cif::datab
auto r1 = cat1.front();
auto r2 = cat2.front();
for (auto column : cat1.key_fields())
r2[column] = r1[column].text();
for (auto item : cat1.key_items())
r2[item] = r1[item].text();
}
}
else

File diff suppressed because it is too large Load Diff

View File

@@ -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)
{
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();
auto &validator = cif::validator_factory::instance().operator[](dictionary);
bool is_valid_pdbx_file(const file &file, std::error_code &ec)
{
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;
try
{
auto &cf = cif::compound_factory::instance();
auto &validator = cif::validator_factory::instance().operator[](dictionary);
if (file.empty())
throw validation_error("Empty file");
throw std::runtime_error("Empty file");
auto &db = file.front();
if (db.empty())
throw validation_error("Empty datablock");
throw std::runtime_error("Empty datablock");
auto &atom_site = db["atom_site"];
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"];
@@ -106,34 +145,38 @@ bool is_valid_pdbx_file(const file &file, std::string_view dictionary)
last_seq_id = *seq_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;
auto p = pdbx_poly_seq_scheme.find(get_parents_condition(validator, r, pdbx_poly_seq_scheme));
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"];
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"];
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"];
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"];
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"))
{
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");
@@ -151,7 +194,7 @@ bool is_valid_pdbx_file(const file &file, std::string_view dictionary)
"seq_id"_key == num and
"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)
"num"_key == seq_id and
"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)
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)
@@ -183,8 +226,8 @@ bool is_valid_pdbx_file(const file &file, std::string_view dictionary)
"label_asym_id"_key == asym_id and
"label_seq_id"_key == seq_id and not std::move(cond);
if (atom_site.exists(std::move(cond)))
throw validation_error("An atom_site record exists that has no parent in the poly seq scheme categories");
if (atom_site.contains(std::move(cond)))
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)
for (auto comp_id : comp_ids)
{
std::string letter;
if (cf.is_known_base(comp_id))
letter = compound_factory::kBaseMap.at(comp_id);
else if (cf.is_known_peptide(comp_id))
letter = compound_factory::kAAMap.at(comp_id);
else
if (can)
{
if (can)
if (compound_factory::kBaseMap.contains(comp_id))
letter = compound_factory::kBaseMap.at(comp_id);
else
{
auto c = cf.create(comp_id);
if (c and c->one_letter_code())
@@ -219,6 +261,13 @@ bool is_valid_pdbx_file(const file &file, std::string_view dictionary)
else
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
letter = '(' + comp_id + ')';
}
@@ -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());
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())
@@ -261,11 +310,10 @@ bool is_valid_pdbx_file(const file &file, std::string_view dictionary)
else
{
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()))
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;
@@ -275,8 +323,12 @@ bool is_valid_pdbx_file(const file &file, std::string_view dictionary)
result = false;
if (cif::VERBOSE > 0)
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;
}

View File

@@ -29,44 +29,44 @@
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)
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)
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)
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)
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)
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)
auto &i = r->operator[](ix);
if (not i)
continue;
emplace_back(cat.get_column_name(ix), i.text());
emplace_back(cat.get_item_name(ix), i.text());
}
}

View File

@@ -215,19 +215,19 @@ std::string trim_copy(std::string_view s)
// --------------------------------------------------------------------
std::tuple<std::string, std::string> split_tag_name(std::string_view tag)
std::tuple<std::string, std::string> split_item_name(std::string_view item_name)
{
if (tag.empty())
throw std::runtime_error("empty tag");
if (tag[0] != '_')
throw std::runtime_error("tag '" + std::string { tag } + "' does not start with underscore");
if (item_name.empty())
throw std::runtime_error("empty item_name");
if (item_name[0] != '_')
throw std::runtime_error("item_name '" + std::string { item_name } + "' does not start with underscore");
auto s = tag.find('.');
auto s = item_name.find('.');
if (s == std::string::npos)
// throw std::runtime_error("tag does not contain dot (" + std::string{ tag } + ')');
return std::tuple<std::string, std::string>{ "", tag.substr(1) };
// throw std::runtime_error("item_name does not contain dot (" + std::string{ item_name } + ')');
return std::tuple<std::string, std::string>{ "", item_name.substr(1) };
else
return std::tuple<std::string, std::string>{tag.substr(1, s - 1), tag.substr(s + 1)};
return std::tuple<std::string, std::string>{item_name.substr(1, s - 1), item_name.substr(s + 1)};
}
// --------------------------------------------------------------------

View File

@@ -39,16 +39,35 @@
// the code will use boost::regex instead.
#if USE_BOOST_REGEX
#include <boost/regex.hpp>
# include <boost/regex.hpp>
using boost::regex;
#else
#include <regex>
# include <regex>
using std::regex;
#endif
namespace cif
{
validation_exception::validation_exception(std::error_code ec)
: runtime_error(ec.message())
{
}
validation_exception::validation_exception(std::error_code ec, std::string_view category)
: runtime_error(
(std::ostringstream{} << ec.message() << "; category: " << std::quoted(category)).str())
{
}
validation_exception::validation_exception(std::error_code ec, std::string_view category, std::string_view item)
: runtime_error(
(std::ostringstream{} << ec.message() << "; category: " << std::quoted(category) << "; item: " << std::quoted(item)).str())
{
}
// --------------------------------------------------------------------
struct regex_impl : public regex
{
regex_impl(std::string_view rx)
@@ -57,20 +76,11 @@ struct regex_impl : public regex
}
};
validation_error::validation_error(const std::string &msg)
: m_msg(msg)
{
}
validation_error::validation_error(const std::string &cat, const std::string &item, const std::string &msg)
: m_msg("When validating _" + cat + '.' + item + ": " + msg)
{
}
// --------------------------------------------------------------------
DDL_PrimitiveType map_to_primitive_type(std::string_view s)
DDL_PrimitiveType map_to_primitive_type(std::string_view s, std::error_code &ec) noexcept
{
ec = {};
DDL_PrimitiveType result;
if (iequals(s, "char"))
result = DDL_PrimitiveType::Char;
@@ -79,7 +89,16 @@ DDL_PrimitiveType map_to_primitive_type(std::string_view s)
else if (iequals(s, "numb"))
result = DDL_PrimitiveType::Numb;
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;
}
@@ -196,63 +215,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
{
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 (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);
if (not m_enums.empty())
{
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");
}
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)
ec = make_error_code(validation_error::value_is_not_in_enumeration_list);
}
return ec == std::errc();
}
// --------------------------------------------------------------------
void category_validator::addItemValidator(item_validator &&v)
void category_validator::add_item_validator(item_validator &&v)
{
if (v.m_mandatory)
m_mandatory_fields.insert(v.m_tag);
m_mandatory_items.insert(v.m_item_name);
v.m_category = this;
auto r = m_item_validators.insert(std::move(v));
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;
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())
result = &*i;
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;
}
@@ -295,19 +323,19 @@ const category_validator *validator::get_validator_for_category(std::string_view
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;
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);
if (cv != nullptr)
result = const_cast<item_validator *>(cv->get_validator_for_item(item));
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;
}
@@ -332,11 +360,11 @@ void validator::add_link_validator(link_validator &&v)
auto piv = pcv->get_validator_for_item(v.m_parent_keys[i]);
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]);
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)
const_cast<item_validator *>(civ)->m_type = piv->m_type;
@@ -351,7 +379,7 @@ std::vector<const link_validator *> validator::get_links_for_parent(std::string_
for (auto &l : m_link_validators)
{
if (l.m_parent_category == category)
if (iequals(l.m_parent_category, category))
result.push_back(&l);
}
@@ -364,19 +392,32 @@ std::vector<const link_validator *> validator::get_links_for_child(std::string_v
for (auto &l : m_link_validators)
{
if (l.m_child_category == category)
if (iequals(l.m_child_category, category))
result.push_back(&l);
}
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)
throw validation_error(msg);
else if (VERBOSE > 0)
std::cerr << msg << '\n';
throw validation_exception(ec);
else
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 +479,12 @@ const validator &validator_factory::operator[](std::string_view dictionary_name)
if (not std::filesystem::exists(p, ec) or ec)
{
for (const char *dir : {
#if defined(CACHE_DIR)
# if defined(CACHE_DIR)
CACHE_DIR,
#endif
#if defined(DATA_DIR)
# endif
# if defined(DATA_DIR)
DATA_DIR
#endif
# endif
})
{
auto p2 = std::filesystem::path(dir) / p;

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

10486
test/reconstruct/phenix.cif Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,64 @@
/*-
* 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 <catch2/catch.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));
}
}
}

View File

@@ -568,7 +568,7 @@ _test.value
auto &test = db["test"];
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);
}