mirror of
https://github.com/PDB-REDO/libcifpp.git
synced 2026-06-05 06:25:52 +08:00
Compare commits
23 Commits
v6.1.0
...
develop-ci
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
446438bf8c | ||
|
|
4e012cbd48 | ||
|
|
12ee4a792c | ||
|
|
e59750386f | ||
|
|
4e19d54867 | ||
|
|
db603e5438 | ||
|
|
5320cb123a | ||
|
|
30a2ebdbb4 | ||
|
|
a5d43998a3 | ||
|
|
2792caec70 | ||
|
|
fb2b1e984c | ||
|
|
13ab1caf95 | ||
|
|
5d4534fac4 | ||
|
|
f450643861 | ||
|
|
fc14a65511 | ||
|
|
bbd1e27c5e | ||
|
|
369a83b718 | ||
|
|
afc541b956 | ||
|
|
7e4d2ffb4d | ||
|
|
e09913a94f | ||
|
|
b4d1c4cc04 | ||
|
|
22537c0e7e | ||
|
|
39c0db8d6a |
3
.gitignore
vendored
3
.gitignore
vendored
@@ -11,4 +11,5 @@ Testing/
|
||||
include/cif++/exports.hpp
|
||||
docs/api
|
||||
docs/conf.py
|
||||
build_ci/
|
||||
build_ci/
|
||||
data/components.cif
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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
|
||||
*/
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
};
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
##
|
||||
|
||||
515
src/category.cpp
515
src/category.cpp
File diff suppressed because it is too large
Load Diff
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
15
src/file.cpp
15
src/file.cpp
@@ -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)
|
||||
|
||||
10
src/item.cpp
10
src/item.cpp
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -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
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
22
src/row.cpp
22
src/row.cpp
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
18
src/text.cpp
18
src/text.cpp
@@ -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)};
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
165
src/validate.cpp
165
src/validate.cpp
@@ -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;
|
||||
|
||||
1234
test/reconstruct/1cbs-stripped-2.cif
Normal file
1234
test/reconstruct/1cbs-stripped-2.cif
Normal file
File diff suppressed because it is too large
Load Diff
1234
test/reconstruct/1cbs-stripped.cif
Normal file
1234
test/reconstruct/1cbs-stripped.cif
Normal file
File diff suppressed because it is too large
Load Diff
1492
test/reconstruct/cif2fasta-1cbs_insert.cif
Normal file
1492
test/reconstruct/cif2fasta-1cbs_insert.cif
Normal file
File diff suppressed because it is too large
Load Diff
1529
test/reconstruct/cif2fasta-1cbs_mutate.cif
Normal file
1529
test/reconstruct/cif2fasta-1cbs_mutate.cif
Normal file
File diff suppressed because it is too large
Load Diff
1535
test/reconstruct/cif2fasta-1cbs_mutate_extend.cif
Normal file
1535
test/reconstruct/cif2fasta-1cbs_mutate_extend.cif
Normal file
File diff suppressed because it is too large
Load Diff
10486
test/reconstruct/phenix.cif
Normal file
10486
test/reconstruct/phenix.cif
Normal file
File diff suppressed because it is too large
Load Diff
64
test/reconstruction-test.cpp
Normal file
64
test/reconstruction-test.cpp
Normal 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));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user