Compare commits

..

11 Commits

Author SHA1 Message Date
Maarten L. Hekkelman
cdb694d4a6 Working on item_value 2026-02-09 16:11:50 +01:00
Maarten L. Hekkelman
60f80673e3 Converting code to work with item_value 2026-02-07 20:36:40 +01:00
Maarten L. Hekkelman
a0f4eada6f a start 2026-02-04 16:39:39 +01:00
Maarten L. Hekkelman
64e6b3cd2d pfft 2025-12-27 13:37:21 +01:00
Maarten L. Hekkelman
f19c6d078e Merge branch 'trunk' into develop 2025-12-20 08:40:04 +01:00
Maarten L. Hekkelman
73f18a4da2 PCRE2 is not thread safe, the way it is used in libcifpp type validator 2025-12-20 08:38:59 +01:00
Maarten L. Hekkelman
7a9d94bc57 private linking to fast_float 2025-11-27 15:34:20 +01:00
Maarten L. Hekkelman
a3ba760ab5 Merge branch 'develop' into trunk 2025-11-27 15:29:46 +01:00
Maarten L. Hekkelman
913abcd1b3 Fixes in makefile 2025-11-27 11:26:47 +01:00
Maarten L. Hekkelman
510e336306 exclude from all for fast_float 2025-11-27 09:06:22 +01:00
Maarten L. Hekkelman
ffff2479d2 revert version-string code 2025-11-26 13:17:14 +01:00
23 changed files with 1984 additions and 4017 deletions

View File

@@ -169,6 +169,8 @@ if(MSVC)
endforeach()
endif()
set(CMAKE_CXX_STANDARD 20)
# Using fast_float for float parsing, but only if needed
try_compile(STD_CHARCONV_COMPILING
SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/cmake/test-charconv.cpp)
@@ -257,19 +259,18 @@ set(project_sources
${CMAKE_CURRENT_SOURCE_DIR}/src/validate.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/text.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/utilities.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/atom_type.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/compound.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/point.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/symmetry.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/model.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/cql/transaction.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/pdb/cif2pdb.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/pdb/pdb2cif.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/pdb/pdb_record.hpp
${CMAKE_CURRENT_SOURCE_DIR}/src/pdb/pdb2cif_remark_3.hpp
${CMAKE_CURRENT_SOURCE_DIR}/src/pdb/pdb2cif_remark_3.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/pdb/reconstruct.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/pdb/validate-pdbx.cpp
# ${CMAKE_CURRENT_SOURCE_DIR}/src/atom_type.cpp
# ${CMAKE_CURRENT_SOURCE_DIR}/src/compound.cpp
# ${CMAKE_CURRENT_SOURCE_DIR}/src/point.cpp
# ${CMAKE_CURRENT_SOURCE_DIR}/src/symmetry.cpp
# ${CMAKE_CURRENT_SOURCE_DIR}/src/model.cpp
# ${CMAKE_CURRENT_SOURCE_DIR}/src/pdb/cif2pdb.cpp
# ${CMAKE_CURRENT_SOURCE_DIR}/src/pdb/pdb2cif.cpp
# ${CMAKE_CURRENT_SOURCE_DIR}/src/pdb/pdb_record.hpp
# ${CMAKE_CURRENT_SOURCE_DIR}/src/pdb/pdb2cif_remark_3.hpp
# ${CMAKE_CURRENT_SOURCE_DIR}/src/pdb/pdb2cif_remark_3.cpp
# ${CMAKE_CURRENT_SOURCE_DIR}/src/pdb/reconstruct.cpp
# ${CMAKE_CURRENT_SOURCE_DIR}/src/pdb/validate-pdbx.cpp
)
set(project_headers
@@ -299,7 +300,6 @@ set(project_headers
include/cif++/row.hpp
include/cif++/symmetry.hpp
include/cif++/text.hpp
include/cif++/cql/transaction.hpp
include/cif++/utilities.hpp
include/cif++/validate.hpp
)
@@ -349,7 +349,7 @@ else()
endif()
if(NOT STD_CHARCONV_COMPILING)
target_link_libraries(cifpp PUBLIC FastFloat::fast_float)
target_link_libraries(cifpp PRIVATE FastFloat::fast_float)
endif()
if(CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang")

View File

@@ -28,12 +28,14 @@
#include "cif++/forward_decl.hpp"
#include "cif++/condition.hpp"
// #include "cif++/condition.hpp"
#include "cif++/item.hpp"
#include "cif++/iterator.hpp"
#include "cif++/row.hpp"
#include "cif++/text.hpp"
#include <array>
// #include <array>
#include <functional>
/** \file category.hpp
* Documentation for the cif::category class
@@ -108,16 +110,6 @@ class multiple_results_error : public std::runtime_error
}
};
// --------------------------------------------------------------------
// These should be moved elsewhere, one day.
/// \cond
template <typename _Tp>
inline constexpr bool is_optional_v = false;
template <typename _Tp>
inline constexpr bool is_optional_v<std::optional<_Tp>> = true;
/// \endcond
// --------------------------------------------------------------------
/// The class category is a sequence container for rows of data values.
@@ -181,12 +173,8 @@ class category
const std::string &name() const { return m_name; } ///< Returns the name of the category
[[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
@@ -336,7 +324,7 @@ class category
struct key_element_type
{
std::string name; ///< Name of the item
std::string value; ///< Value to be found
item_value value; ///< Value to be found
bool may_be_null = false; ///< If true, value should be same or empty
};
@@ -961,7 +949,7 @@ class category
for (auto i = b; i != e; ++i)
{
// item_value *new_item = this->create_item(*i);
r->append(add_item(i->name()), { i->value() });
r->append(add_item(i->name()), i->value());
}
}
catch (...)
@@ -1005,7 +993,7 @@ class category
// --------------------------------------------------------------------
using value_provider_type = std::function<std::string_view(std::string_view)>;
using value_provider_type = std::function<item_value(const item_value &)>;
/// \brief Update a single item named @a item_name in the rows that match
/// \a cond to values provided by a callback function \a value_provider
@@ -1036,7 +1024,7 @@ class category
/// That means, child categories are updated if the links are absolute
/// and unique. If they are not, the child category rows are split.
void update_value(condition &&cond, std::string_view item_name, std::string_view value)
void update_value(condition &&cond, std::string_view item_name, item_value value)
{
auto rs = find(std::move(cond));
std::vector<row_handle> rows;
@@ -1049,66 +1037,12 @@ class category
/// That means, child categories are updated if the links are absolute
/// and unique. If they are not, the child category rows are split.
void update_value(const std::vector<row_handle> &rows, std::string_view item_name, std::string_view value)
void update_value(const std::vector<row_handle> &rows, std::string_view item_name, item_value value)
{
update_value(rows, item_name, [value](std::string_view)
update_value(rows, item_name, [value](const item_value &v)
{ return value; });
}
// --------------------------------------------------------------------
// 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
{
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
{
return get_item_name(ix);
}
/// @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)
{
return add_item(item_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);
}
/** @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 has_item(name);
}
/// @brief Return the cif::iset of columns in this category
[[deprecated("use get_items instead")]] iset get_columns() const
{
return get_items();
}
// --------------------------------------------------------------------
/// \brief Return the index number for \a item_name
@@ -1117,7 +1051,7 @@ class category
/// @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
const std::string &get_item_name(uint16_t ix) const
{
if (ix >= m_items.size())
throw std::out_of_range("item index is out of range");
@@ -1200,7 +1134,7 @@ class category
}
private:
void update_value(row *row, uint16_t item, std::string_view value, bool updateLinked, bool validate = true);
void update_value(row *row, uint16_t item, item_value value, bool updateLinked, bool validate = true);
void erase_orphans(condition &&cond, category &parent);

View File

@@ -371,7 +371,7 @@ namespace detail
{
key_equals_condition_impl(item &&i)
: m_item_name(i.name())
, m_value(std::forward<item>(i).value())
, m_value(std::forward<item_value>(i.value()))
{
}
@@ -409,7 +409,7 @@ namespace detail
std::string m_item_name;
uint16_t m_item_ix = 0;
bool m_icase = false;
std::string m_value;
item_value m_value;
std::optional<row_handle> m_single_hit;
};
@@ -466,111 +466,11 @@ namespace detail
std::string m_item_name;
uint16_t m_item_ix = 0;
std::string m_value;
item_value &m_value;
bool m_icase = false;
std::optional<row_handle> m_single_hit;
};
struct key_equals_number_condition_impl : public condition_impl
{
key_equals_number_condition_impl(const std::string &name, double v)
: m_item_name(name)
, m_value(v)
{
}
condition_impl *prepare(const category &c) override;
bool test(row_handle r) const override
{
return m_single_hit.has_value() ? *m_single_hit == r : r[m_item_ix].compare(m_value) == 0;
}
void str(std::ostream &os) const override
{
os << m_item_name << " == " << m_value;
}
virtual std::optional<row_handle> single() const override
{
return m_single_hit;
}
virtual bool equals(const condition_impl *rhs) const override
{
if (typeid(*rhs) == typeid(key_equals_number_condition_impl))
{
auto ri = static_cast<const key_equals_number_condition_impl *>(rhs);
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 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_name;
uint16_t m_item_ix = 0;
double m_value;
std::optional<row_handle> m_single_hit;
};
struct key_equals_number_or_empty_condition_impl : public condition_impl
{
key_equals_number_or_empty_condition_impl(key_equals_number_condition_impl *equals)
: m_item_name(equals->m_item_name)
, m_value(equals->m_value)
, m_single_hit(equals->m_single_hit)
{
}
condition_impl *prepare(const category &c) override
{
m_item_ix = get_item_ix(c, m_item_name);
return this;
}
bool test(row_handle r) const override
{
bool result = false;
if (m_single_hit.has_value())
result = *m_single_hit == r;
else
result = r[m_item_ix].empty() or r[m_item_ix].compare(m_value) == 0;
return result;
}
void str(std::ostream &os) const override
{
os << '(' << m_item_name << " == " << m_value << " OR " << m_item_name << " IS NULL)";
}
virtual std::optional<row_handle> single() const override
{
return m_single_hit;
}
virtual bool equals(const condition_impl *rhs) const override
{
if (typeid(*rhs) == typeid(key_equals_number_or_empty_condition_impl))
{
auto ri = static_cast<const key_equals_number_or_empty_condition_impl *>(rhs);
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 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_name;
uint16_t m_item_ix = 0;
double m_value;
std::optional<row_handle> m_single_hit;
};
struct key_compare_condition_impl : public condition_impl
{
template <typename COMP>
@@ -622,7 +522,7 @@ namespace detail
bool test(row_handle r) const override
{
std::string_view txt = r[m_item_ix].text();
auto txt = r[m_item_ix].get<std::string>();
return std::regex_match(txt.begin(), txt.end(), mRx);
}
@@ -693,7 +593,7 @@ namespace detail
{
try
{
std::string_view txt = r[f].text();
auto txt = r[f].get<std::string>();
if (std::regex_match(txt.begin(), txt.end(), mRx))
{
result = true;
@@ -970,26 +870,6 @@ inline condition operator or(condition &&a, condition &&b)
return condition(new detail::key_equals_or_empty_condition_impl(ci));
}
if (typeid(*a.m_impl) == typeid(detail::key_equals_number_condition_impl) and
typeid(*b.m_impl) == typeid(detail::key_is_empty_condition_impl))
{
auto ci = static_cast<detail::key_equals_number_condition_impl *>(a.m_impl);
auto ce = static_cast<detail::key_is_empty_condition_impl *>(b.m_impl);
if (ci->m_item_name == ce->m_item_name)
return condition(new detail::key_equals_number_or_empty_condition_impl(ci));
}
if (typeid(*b.m_impl) == typeid(detail::key_equals_number_condition_impl) and
typeid(*a.m_impl) == typeid(detail::key_is_empty_condition_impl))
{
auto ci = static_cast<detail::key_equals_number_condition_impl *>(b.m_impl);
auto ce = static_cast<detail::key_is_empty_condition_impl *>(a.m_impl);
if (ci->m_item_name == ce->m_item_name)
return condition(new detail::key_equals_number_or_empty_condition_impl(ci));
}
return condition(new detail::or_condition_impl(std::move(a), std::move(b)));
}
@@ -1066,20 +946,10 @@ struct key
template <typename T>
concept Numeric = ((std::is_floating_point_v<T> or std::is_integral_v<T>) and not std::is_same_v<T, bool>);
/**
* @brief Operator to create an equals condition based on a key @a key and a numeric value @a v
*/
template <Numeric T>
condition operator==(const key &key, const T &v)
{
// TODO: change key_equals_etc... to use std::variant<double,int64_t> or something
return condition(new detail::key_equals_number_condition_impl(key.m_item_name, static_cast<double>(v)));
}
/**
* @brief Operator to create an equals condition based on a key @a key and a value @a value
*/
inline condition operator==(const key &key, std::string_view value)
inline condition operator==(const key &key, item_value value)
{
if (not value.empty())
return condition(new detail::key_equals_condition_impl({ key.m_item_name, value }));
@@ -1087,16 +957,6 @@ inline condition operator==(const key &key, std::string_view value)
return condition(new detail::key_is_empty_condition_impl(key.m_item_name));
}
/**
* @brief Operator to create an equals condition based on a key @a key and a value @a value
*/
template <typename T>
requires std::is_same_v<T, bool>
inline condition operator==(const key &key, T value)
{
return condition(new detail::key_equals_condition_impl({ key.m_item_name, value ? "y" : "n" }));
}
/**
* @brief Operator to create a not equals condition based on a key @a key and a value @a v
*/

View File

@@ -1,451 +0,0 @@
/*-
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2025 NKI/AVL, Netherlands Cancer Institute
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#pragma once
#include "cif++/category.hpp"
#include "cif++/condition.hpp"
#include "cif++/datablock.hpp"
#include "cif++/item.hpp"
#include "cif++/row.hpp"
#include "cif++/validate.hpp"
#include <algorithm>
#include <iterator>
#include <memory>
#include <stdexcept>
#include <string>
#include <vector>
// --------------------------------------------------------------------
namespace cif::cql
{
class result;
class row;
class transaction;
class view;
// --------------------------------------------------------------------
struct column
{
std::string name;
size_t index;
};
using column_list = std::vector<column>;
// --------------------------------------------------------------------
class field_ref
{
public:
std::string_view name() const &
{
return m_col->name;
}
constexpr size_t num() const noexcept
{
return m_col->index;
}
std::string_view text() const &
{
return m_row[m_col->index].text();
}
/** Return the contents of this item as type @tparam T */
template <typename T = std::string>
auto as() const -> T
{
return m_row[m_col->index].as<T>();
}
/** Return the contents of this item as type @tparam T or, if not
* set, use @a dv as the default value.
*/
template <typename T>
auto value_or(const T &dv) const
{
return m_row[m_col->index].value_or(dv);
}
field_ref(row_handle rh, column_list::const_iterator col)
: m_row(rh)
, m_col(col)
{
}
field_ref(const field_ref &) = default;
field_ref(field_ref &&) = default;
field_ref &operator=(const field_ref &) = default;
field_ref &operator=(field_ref &&) = default;
private:
row_handle m_row;
column_list::const_iterator m_col;
};
// --------------------------------------------------------------------
class row_ref final
{
public:
class const_field_iterator
{
public:
friend class result;
using iterator_category = std::forward_iterator_tag;
using value_type = const field_ref;
using difference_type = std::ptrdiff_t;
using pointer = value_type *;
using reference = value_type &;
const_field_iterator(const const_field_iterator &) = default;
const_field_iterator(const_field_iterator &&) = default;
const_field_iterator &operator=(const const_field_iterator &) = default;
const_field_iterator &operator=(const_field_iterator &&) = default;
reference operator*()
{
return m_current;
}
pointer operator->()
{
return &m_current;
}
const_field_iterator &operator++()
{
if (m_row)
{
++m_col;
m_current = field_ref(m_row, m_col);
}
return *this;
}
const_field_iterator operator++(int)
{
const_field_iterator result(*this);
this->operator++();
return result;
}
bool operator==(const const_field_iterator &rhs) const
{
return m_row == rhs.m_row and m_col == rhs.m_col;
}
bool operator!=(const const_field_iterator &rhs) const
{
return m_row != rhs.m_row or m_col != rhs.m_col;
}
private:
friend class row_ref;
const_field_iterator(const row_handle &row, column_list::const_iterator col)
: m_row(row)
, m_col(col)
, m_current(m_row, m_col)
{
}
row_handle m_row;
column_list::const_iterator m_col;
field_ref m_current;
};
// --------------------------------------------------------------------
row_ref() = default;
row_ref(row_handle rh, const column_list &cols)
: m_row(rh)
, m_cols(&cols)
{
}
row_ref(row_ref r, const column_list &cols)
: m_row(r.m_row)
, m_cols(&cols)
{
}
row_ref(const row_ref &) = default;
row_ref &operator=(const row_ref &) = default;
// --------------------------------------------------------------------
const_field_iterator cbegin() const noexcept { return const_field_iterator(m_row, m_cols->cbegin()); }
const_field_iterator begin() const noexcept { return const_field_iterator(m_row, m_cols->cbegin()); }
const_field_iterator cend() const noexcept { return const_field_iterator(m_row, m_cols->cend()); }
const_field_iterator end() const noexcept { return const_field_iterator(m_row, m_cols->cend()); }
field_ref front() const noexcept { return field_ref(m_row, m_cols->cbegin()); }
field_ref back() const noexcept { return field_ref(m_row, m_cols->cend()); }
size_t size() const noexcept { return m_cols->size(); }
bool empty() const noexcept { return m_cols->empty(); }
field_ref operator[](size_t index) const noexcept;
field_ref operator[](std::string_view name) const noexcept;
// --------------------------------------------------------------------
bool operator==(const row_ref &rhs) const { return m_row == rhs.m_row and m_cols == rhs.m_cols; }
bool operator!=(const row_ref &rhs) const { return m_row != rhs.m_row or m_cols != rhs.m_cols; }
private:
row_handle m_row;
const column_list *m_cols = nullptr;
};
// --------------------------------------------------------------------
class view : public std::enable_shared_from_this<view>
{
public:
virtual ~view() = default;
class const_row_iterator
{
public:
friend class view;
using iterator_category = std::forward_iterator_tag;
using value_type = const row_ref;
using difference_type = std::ptrdiff_t;
using pointer = value_type *;
using reference = value_type &;
// const_row_iterator() = default;
const_row_iterator(const const_row_iterator &) = default;
const_row_iterator(const_row_iterator &&) = default;
// const_row_iterator &operator=(const const_row_iterator &) = default;
// const_row_iterator &operator=(const_row_iterator &&) = default;
reference operator*()
{
return m_current;
}
pointer operator->()
{
return &m_current;
}
const_row_iterator &operator++()
{
++m_index;
if (m_index < m_data.size())
m_current = m_data.at(m_index);
return *this;
}
const_row_iterator operator++(int)
{
const_row_iterator result(*this);
this->operator++();
return result;
}
bool operator==(const const_row_iterator &rhs) const
{
return &m_data == &rhs.m_data and m_index == rhs.m_index;
}
bool operator!=(const const_row_iterator &rhs) const
{
return &m_data != &rhs.m_data or m_index != rhs.m_index;
}
private:
const_row_iterator(const view &result, size_t index, row_ref current)
: m_data(result)
, m_index(index)
, m_current(current)
{
}
const view &m_data;
size_t m_index = 0;
row_ref m_current;
};
// --------------------------------------------------------------------
const_row_iterator begin() const noexcept { return const_row_iterator(*this, 0, at(0)); }
const_row_iterator cbegin() const noexcept { return const_row_iterator(*this, 0, at(0)); }
const_row_iterator end() const noexcept { return const_row_iterator(*this, size(), row_ref{}); }
const_row_iterator cend() const noexcept { return const_row_iterator(*this, size(), row_ref{}); }
virtual row_ref front() const noexcept = 0;
virtual row_ref back() const noexcept = 0;
virtual size_t size() const noexcept = 0;
bool empty() const noexcept { return size() == 0; }
virtual row_ref at(size_t index) const = 0;
// --------------------------------------------------------------------
std::vector<std::string> columns() const
{
std::vector<std::string> result;
for (const auto &[name, ignore] : m_columns)
result.emplace_back(name);
return result;
}
protected:
friend class const_row_iterator;
view(const column_list &cols)
: m_columns(cols)
{
}
view(column_list &&cols)
: m_columns(std::forward<column_list>(cols))
{
}
column_list m_columns;
};
// --------------------------------------------------------------------
class simple_view : public view
{
public:
simple_view(const category &cat)
: view(get_column_list_for_category(cat))
, m_cat(cat)
{
}
simple_view(const simple_view &) = default;
simple_view(simple_view &&) = default;
virtual size_t size() const noexcept override { return m_cat.size(); }
virtual row_ref front() const noexcept override;
virtual row_ref back() const noexcept override;
virtual row_ref at(size_t index) const override;
protected:
static column_list get_column_list_for_category(const category &cat);
const category &m_cat;
};
// --------------------------------------------------------------------
class result
{
public:
// --------------------------------------------------------------------
using const_row_iterator = view::const_row_iterator;
// --------------------------------------------------------------------
result();
result(result const &rhs) noexcept = default;
result(result &&rhs) noexcept = default;
result &operator=(result const &rhs) noexcept = default;
result &operator=(result &&rhs) noexcept = default;
result(view &vw, const std::string &query = "");
row_ref one_row() const;
field_ref one_field() const;
// --------------------------------------------------------------------
const_row_iterator begin() const noexcept;
const_row_iterator cbegin() const noexcept;
const_row_iterator end() const noexcept;
const_row_iterator cend() const noexcept;
row_ref front() const noexcept;
row_ref back() const noexcept;
size_t size() const noexcept;
bool empty() const noexcept;
size_t column_count() const;
private:
friend class transaction;
friend class SelectStatement;
result expect_columns(size_t cols) const
{
if (auto actual = column_count(); cols != actual)
throw std::runtime_error("Unexpected number of columns");
return *this;
}
row_ref at(size_t index) const;
std::string m_query;
std::shared_ptr<view> m_view;
};
// --------------------------------------------------------------------
class transaction
{
public:
transaction(const datablock &db)
: m_db(const_cast<datablock &>(db))
{
}
result exec(std::string_view query);
private:
datablock &m_db;
};
} // namespace cif::cql

File diff suppressed because it is too large Load Diff

View File

@@ -26,6 +26,7 @@
#pragma once
#include "cif++/condition.hpp"
#include "cif++/row.hpp"
#include <array>
@@ -179,7 +180,7 @@ class iterator_impl
template <std::size_t... Is>
tuple_type get(std::index_sequence<Is...>) const
{
return m_current ? tuple_type{ m_current[m_item_ix[Is]].template as<Ts>()... } : tuple_type{};
return m_current ? tuple_type{ m_current[m_item_ix[Is]].template get<Ts>()... } : tuple_type{};
}
row_handle m_current;
@@ -422,7 +423,7 @@ class iterator_impl<Category, T>
private:
value_type get() const
{
return m_current ? m_current[m_item_ix].template as<value_type>() : value_type{};
return m_current ? m_current[m_item_ix].template get<value_type>() : value_type{};
}
row_handle m_current;

View File

@@ -190,7 +190,7 @@ class atom
* @param row The row containing the data for this atom
*/
atom(const datablock &db, const row_handle &row)
: atom(std::make_shared<atom_impl>(db, row["id"].as<std::string>()))
: atom(std::make_shared<atom_impl>(db, row["id"].get<std::string>()))
{
}

View File

@@ -164,7 +164,13 @@ class sac_parser
SAVE_NAME,
STOP,
ITEM_NAME,
VALUE
VALUE_INAPPLICABLE,
VALUE_UNKNOWN,
VALUE_NUMERIC_INTEGER,
VALUE_NUMERIC_FLOAT,
VALUE_CHARSTRING,
VALUE_TEXTFIELD
};
static constexpr const char *get_token_name(CIFToken token)
@@ -180,7 +186,15 @@ class sac_parser
case CIFToken::SAVE_NAME: return "SAVE+name";
case CIFToken::STOP: return "STOP";
case CIFToken::ITEM_NAME: return "Tag";
case CIFToken::VALUE: return "Value";
// case CIFToken::VALUE: return "Value";
case CIFToken::VALUE_INAPPLICABLE: return "Inapplicable value";
case CIFToken::VALUE_UNKNOWN: return "'Unknown' value (=null)";
case CIFToken::VALUE_NUMERIC_INTEGER: return "Integer value";
case CIFToken::VALUE_NUMERIC_FLOAT: return "Float value";
case CIFToken::VALUE_CHARSTRING: return "Charstring value";
case CIFToken::VALUE_TEXTFIELD: return "Textfield value";
default: return "Invalid token parameter";
}
}
@@ -262,7 +276,7 @@ class sac_parser
virtual void produce_datablock(std::string_view name) = 0;
virtual void produce_category(std::string_view name) = 0;
virtual void produce_row() = 0;
virtual void produce_item(std::string_view category, std::string_view item, std::string_view value) = 0;
virtual void produce_item(std::string_view category, std::string_view item, item_value value) = 0;
protected:
@@ -281,7 +295,12 @@ class sac_parser
TextItem,
TextItemNL,
Reserved,
Value
Value,
Numeric_Integer,
Numeric_Float,
Numeric_Exponent1,
Numeric_Exponent2
};
std::streambuf &m_source;
@@ -294,6 +313,8 @@ class sac_parser
// token buffer
std::vector<char> m_token_buffer;
std::string_view m_token_value;
int64_t m_token_value_int;
double m_token_value_float;
/** @endcond */
};
@@ -331,7 +352,7 @@ class parser : public sac_parser
void produce_row() override;
void produce_item(std::string_view category, std::string_view item, std::string_view value) override;
void produce_item(std::string_view category, std::string_view item, item_value value) override;
protected:
file &m_file;

View File

@@ -41,7 +41,7 @@
* that's not the case.
*
* You can access the values of stored items by name or index.
* The return value of operator[] is an cif::item_handle object.
* The return value of operator[] is a reference to a cif::item_value object.
*
* @code {.cpp}
* cif::category &atom_site = my_db["atom_site"];
@@ -93,7 +93,7 @@ namespace detail
{
}
const item_handle operator[](uint16_t ix) const
const item_value &operator[](uint16_t ix) const
{
return m_row[m_items[ix]];
}
@@ -107,7 +107,7 @@ namespace detail
template <typename... Ts, std::size_t... Is>
std::tuple<Ts...> get(std::index_sequence<Is...>) const
{
return std::tuple<Ts...>{ m_row[m_items[Is]].template as<Ts>()... };
return std::tuple<Ts...>{ m_row[m_items[Is]].template get<Ts>()... };
}
const row_handle &m_row;
@@ -173,14 +173,14 @@ class row : public std::vector<item_value>
return ix < size() ? &data()[ix] : nullptr;
}
private:
// private:
friend class category;
friend class category_index;
template <typename, typename...>
friend class iterator_impl;
void append(uint16_t ix, item_value &&iv)
void append(uint16_t ix, item_value iv)
{
if (ix >= size())
resize(ix + 1);
@@ -204,7 +204,6 @@ class row_handle
{
public:
/** @cond */
friend struct item_handle;
friend class category;
friend class category_index;
friend class row_initializer;
@@ -245,29 +244,20 @@ class row_handle
return not empty();
}
/// \brief return a cif::item_handle to the item in item @a item_ix
item_handle operator[](uint16_t item_ix)
{
return empty() ? item_handle::s_null_item : item_handle(item_ix, *this);
}
/// \brief return the count of the items
[[nodiscard]] size_t size() const { return m_row->size(); }
/// \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(item_ix, const_cast<row_handle &>(*this));
}
/// \brief return a reference to a cif::item_value to the item in item @a item_ix
item_value &operator[](uint16_t item_ix);
/// \brief return a cif::item_handle to the item in the item named @a item_name
item_handle operator[](std::string_view item_name)
{
return empty() ? item_handle::s_null_item : item_handle(add_item(item_name), *this);
}
/// \brief return a const reference to a cif::item_value to the item in item @a item_ix
const item_value &operator[](uint16_t item_ix) 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_item_ix(item_name), const_cast<row_handle &>(*this));
}
/// \brief return a reference to a cif::item_value to the item in the item named @a item_name
item_value &operator[](std::string_view item_name);
/// \brief return a const reference to a cif::item_value to the item in the item named @a item_name
const item_value &operator[](std::string_view item_name) const;
/// \brief Return an object that can be used in combination with cif::tie
/// to assign the values for the items @a items
@@ -288,14 +278,14 @@ class row_handle
template <typename T>
T get(const char *item) const
{
return operator[](get_item_ix(item)).template as<T>();
return operator[](get_item_ix(item)).template get<T>();
}
/// \brief Get the value of item @a item cast to type @a T
template <typename T>
T get(std::string_view item) const
{
return operator[](get_item_ix(item)).template as<T>();
return operator[](get_item_ix(item)).template get<T>();
}
/// \brief assign each of the items named in @a values to their respective value
@@ -316,9 +306,9 @@ class row_handle
* checked to see if it conforms to the rules defined in the dictionary
*/
void assign(std::string_view name, std::string_view value, bool updateLinked, bool validate = true)
void assign(std::string_view name, item_value value, bool updateLinked, bool validate = true)
{
assign(add_item(name), value, updateLinked, validate);
assign(add_item(name), std::move(value), updateLinked, validate);
}
/** \brief assign the value @a value to item at index @a item
@@ -332,7 +322,7 @@ class row_handle
* checked to see if it conforms to the rules defined in the dictionary
*/
void assign(uint16_t item, std::string_view value, bool updateLinked, bool validate = true);
void assign(uint16_t item, item_value 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; }
@@ -356,10 +346,10 @@ class row_handle
return m_row;
}
void assign(const item &i, bool updateLinked)
void assign(const item &i, bool updateLinked);/*
{
assign(i.name(), i.value(), updateLinked);
}
} */
void swap(uint16_t item, row_handle &r);
@@ -408,7 +398,7 @@ class row_initializer : public std::vector<item>
/// \brief set the value for item name @a name to @a value
void set_value(std::string_view name, std::string_view value);
void set_value(std::string name, item_value value);
/// \brief set the value for item based on @a i
void set_value(const item &i)
@@ -417,7 +407,7 @@ class row_initializer : public std::vector<item>
}
/// \brief set the value for item name @a name to @a value, but only if the item did not have a value already
void set_value_if_empty(std::string_view name, std::string_view value);
void set_value_if_empty(std::string name, item_value value);
/// \brief set the value for item @a i, but only if the item did not have a value already
void set_value_if_empty(const item &i)

View File

@@ -329,10 +329,10 @@ struct item_validator
/// @brief Validate the value in @a value for this item
/// Will throw a std::system_error exception if it fails
void operator()(std::string_view value) const;
void operator()(const item_value &value) const;
/// @brief A more gentle version of value validation
bool validate_value(std::string_view value, std::error_code &ec) const noexcept;
bool validate_value(const item_value &value, std::error_code &ec) const noexcept;
};
/**

File diff suppressed because it is too large Load Diff

View File

@@ -123,21 +123,6 @@ namespace detail
return this;
}
condition_impl *key_equals_number_condition_impl::prepare(const category &c)
{
m_item_ix = c.get_item_ix(m_item_name);
if (c.get_cat_validator() != nullptr and
c.key_item_indices().contains(m_item_ix) and
c.key_item_indices().size() == 1)
{
item v(m_item_name, m_value);
m_single_hit = c[{ { m_item_name, std::string{ v.value() }, false } }];
}
return this;
}
bool found_in_range(condition_impl *c, std::vector<and_condition_impl *>::iterator b, std::vector<and_condition_impl *>::iterator e)
{
bool result = true;
@@ -234,17 +219,6 @@ namespace detail
continue;
}
if (auto s = dynamic_cast<const key_equals_number_condition_impl *>(sub); s != nullptr)
{
if (keys.contains(s->m_item_name))
{
item v{ s->m_item_name, s->m_value };
lookup.emplace_back(s->m_item_name, std::string{ v.value() } );
subs.emplace_back(sub);
}
continue;
}
if (auto s = dynamic_cast<const key_equals_or_empty_condition_impl *>(sub); s != nullptr)
{
if (keys.contains(s->m_item_name))
@@ -255,17 +229,6 @@ namespace detail
}
continue;
}
if (auto s = dynamic_cast<const key_equals_number_or_empty_condition_impl *>(sub); s != nullptr)
{
if (keys.contains(s->m_item_name))
{
item v{ s->m_item_name, s->m_value };
lookup.emplace_back(s->m_item_name, std::string{ v.value() }, true );
subs.emplace_back(sub);
}
continue;
}
}
if (lookup.size() == keys.size())

File diff suppressed because it is too large Load Diff

View File

@@ -153,7 +153,7 @@ class dictionary_parser : public parser
match(CIFToken::ITEM_NAME);
}
while (m_lookahead == CIFToken::VALUE)
while (m_lookahead >= CIFToken::VALUE_INAPPLICABLE)
{
cat->emplace({});
auto row = cat->back();
@@ -161,7 +161,7 @@ class dictionary_parser : public parser
for (auto item_name : item_names)
{
row[item_name] = m_token_value;
match(CIFToken::VALUE);
match(m_lookahead);
}
}
@@ -181,7 +181,7 @@ class dictionary_parser : public parser
cat->emplace({});
cat->back()[item_name] = m_token_value;
match(CIFToken::VALUE);
match(m_lookahead >= CIFToken::VALUE_INAPPLICABLE ? m_lookahead : CIFToken::VALUE_CHARSTRING);
}
}
@@ -193,11 +193,11 @@ class dictionary_parser : public parser
std::vector<std::string> keys;
for (auto k : dict["category_key"])
keys.push_back(std::get<1>(split_item_name(k["name"].as<std::string>())));
keys.push_back(std::get<1>(split_item_name(k["name"].get<std::string>())));
iset groups;
for (auto g : dict["category_group"])
groups.insert(g["id"].as<std::string>());
groups.insert(g["id"].get<std::string>());
mCategoryValidators.push_back(category_validator{ category, keys, groups });
}
@@ -212,7 +212,7 @@ class dictionary_parser : public parser
iset ess;
for (auto e : dict["item_enumeration"])
ess.insert(e["value"].as<std::string>());
ess.insert(e["value"].get<std::string>());
std::string defaultValue = dict["item_default"].front().get<std::string>("value");
// bool defaultIsNull = false;
@@ -405,7 +405,7 @@ class dictionary_parser : public parser
// look up the label
for (auto r : linkedGroup.find("category_id"_key == link.m_child_category and "link_group_id"_key == link.m_link_group_id))
{
link.m_link_group_label = r["label"].as<std::string>();
link.m_link_group_label = r["label"].get<std::string>();
break;
}

View File

@@ -24,45 +24,109 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "cif++/item.hpp"
#include "cif++/row.hpp"
#include <cassert>
#include <compare>
#include <ios>
namespace cif
{
const item_handle item_handle::s_null_item;
row_handle s_null_row_handle;
item_handle::item_handle()
: m_item_ix(std::numeric_limits<uint16_t>::max())
, m_row_handle(s_null_row_handle)
int item_value::compare(const item_value &b, bool ignore_case) const noexcept
{
}
int d = static_cast<int>(m_data.m_type) - static_cast<int>(b.m_data.m_type);
std::string_view item_handle::text() const
{
if (not m_row_handle.empty())
if (d == 0)
{
auto iv = m_row_handle.m_row->get(m_item_ix);
if (iv != nullptr)
return iv->text();
switch (m_data.m_type)
{
case cif::item_value_type::BOOLEAN:
d = static_cast<int>(m_data.m_value.m_boolean) - static_cast<int>(b.m_data.m_value.m_boolean);
break;
case cif::item_value_type::INT:
d = m_data.m_value.m_integer - b.m_data.m_value.m_integer;
break;
case cif::item_value_type::FLOAT:
{
auto dp = (m_data.m_value.m_float <=> b.m_data.m_value.m_float);
if (dp == std::partial_ordering::less)
d = -1;
else if (dp == std::partial_ordering::greater)
d = 1;
break;
}
case cif::item_value_type::TEXT:
d = m_data.sv().compare(b.m_data.sv());
break;
default:;
}
}
return {};
return d;
}
void item_handle::assign_value(std::string_view value)
// const item_handle item_handle::s_null_item;
// row_handle s_null_row_handle;
// item_handle::item_handle()
// : m_item_ix(std::numeric_limits<uint16_t>::max())
// , m_row_handle(s_null_row_handle)
// {
// }
// std::string_view item_handle::text() const
// {
// if (not m_row_handle.empty())
// {
// auto iv = m_row_handle.m_row->get(m_item_ix);
// if (iv != nullptr)
// return iv->text();
// }
// return {};
// }
// void item_handle::assign_value(std::string_view value)
// {
// assert(not m_row_handle.empty());
// m_row_handle.assign(m_item_ix, value, true);
// }
// void item_handle::swap(item_handle &b)
// {
// assert(m_item_ix == b.m_item_ix);
// // assert(&m_row_handle.m_category == &b.m_row_handle.m_category);
// m_row_handle.swap(m_item_ix, b.m_row_handle);
// }
std::ostream &operator<<(std::ostream &os, const item_value &v)
{
assert(not m_row_handle.empty());
m_row_handle.assign(m_item_ix, value, true);
switch (v.type())
{
case cif::item_value_type::BOOLEAN:
os << std::boolalpha << v.m_data.m_value.m_boolean;
break;
case cif::item_value_type::INT:
os << v.m_data.m_value.m_integer;
break;
case cif::item_value_type::FLOAT:
os << v.m_data.m_value.m_float;
break;
case cif::item_value_type::TEXT:
os << v.m_data.sv();
break;
case cif::item_value_type::MISSING:
os << '?';
break;
case cif::item_value_type::EMPTY:
os << '.';
break;
}
return os;
}
void item_handle::swap(item_handle &b)
{
assert(m_item_ix == b.m_item_ix);
// assert(&m_row_handle.m_category == &b.m_row_handle.m_category);
m_row_handle.swap(m_item_ix, b.m_row_handle);
}
}
} // namespace cif

View File

@@ -62,7 +62,7 @@ void atom::atom_impl::moveTo(const point &p)
std::string atom::atom_impl::get_property(std::string_view name) const
{
return row()[name].as<std::string>();
return row()[name].get<std::string>();
}
int atom::atom_impl::get_property_int(std::string_view name) const
@@ -135,7 +135,7 @@ int atom::atom_impl::compare(const atom_impl &b) const
int atom::atom_impl::get_charge() const
{
auto formalCharge = row()["pdbx_formal_charge"].as<std::optional<int>>();
auto formalCharge = row()["pdbx_formal_charge"].get<std::optional<int>>();
if (not formalCharge.has_value())
{
@@ -1891,11 +1891,7 @@ void structure::swap_atoms(atom a1, atom a2)
auto r2 = atomSites.find1(key("id") == a2.id());
for (std::string fld : std::initializer_list<std::string>{ "label_atom_id", "auth_atom_id", "type_symbol" })
{
auto l1 = r1[fld];
auto l2 = r2[fld];
l1.swap(l2);
}
swap(r1[fld], r2[fld]);
}
catch (const std::exception &ex)
{
@@ -2758,7 +2754,7 @@ void structure::cleanup_empty_categories()
for (auto chemComp : chem_comp)
{
std::string compID = chemComp["id"].as<std::string>();
std::string compID = chemComp["id"].get<std::string>();
if (atomSite.contains("label_comp_id"_key == compID or "auth_comp_id"_key == compID) or
pdbxPolySeqScheme.contains("mon_id"_key == compID or "auth_mon_id"_key == compID or "pdb_mon_id"_key == compID) or
entityPolySeq.contains("mon_id"_key == compID))
@@ -2779,7 +2775,7 @@ void structure::cleanup_empty_categories()
for (auto entity : entities)
{
std::string entityID = entity["id"].as<std::string>();
std::string entityID = entity["id"].get<std::string>();
if (atomSite.contains("label_entity_id"_key == entityID))
continue;

View File

@@ -24,15 +24,19 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "cif++/utilities.hpp"
#include "cif++/forward_decl.hpp"
#include "cif++/parser.hpp"
#include "cif++/file.hpp"
#include "cif++/forward_decl.hpp"
#include "cif++/item.hpp"
#include "cif++/utilities.hpp"
#include <cassert>
#include <charconv>
#include <iostream>
#include <map>
#include <stack>
#include <system_error>
namespace cif
{
@@ -58,12 +62,12 @@ class reserved_words_automaton
constexpr bool finished() const
{
return m_state <= 0;
return m_state <= 0;
}
constexpr bool matched() const
{
return m_state < 0;
return m_state < 0;
}
constexpr move_result move(int ch)
@@ -75,7 +79,7 @@ class reserved_words_automaton
case 0:
break;
case -1: // data_
case -1: // data_
if (sac_parser::is_non_blank(ch))
m_seen_trailing_chars = true;
else if (m_seen_trailing_chars)
@@ -84,15 +88,15 @@ class reserved_words_automaton
result = no_keyword;
break;
case -2: // global_
case -2: // global_
result = sac_parser::is_non_blank(ch) ? no_keyword : global;
break;
case -3: // loop_
case -3: // loop_
result = sac_parser::is_non_blank(ch) ? no_keyword : loop;
break;
case -4: // save_
case -4: // save_
if (sac_parser::is_non_blank(ch))
m_seen_trailing_chars = true;
else if (m_seen_trailing_chars)
@@ -101,10 +105,10 @@ class reserved_words_automaton
result = save;
break;
case -5: // stop_
case -5: // stop_
result = sac_parser::is_non_blank(ch) ? no_keyword : stop;
break;
default:
assert(m_state > 0 and m_state < NODE_COUNT);
@@ -141,13 +145,13 @@ class reserved_words_automaton
int8_t next_nomatch;
} s_dag[] = {
{ 0 },
{ 'D', 5, 2 },
{ 'G', 9, 3 },
{ 'D', 5, 2 },
{ 'G', 9, 3 },
{ 'L', 15, 4 },
{ 'S', 19, 0 },
{ 'A', 6, 0 },
{ 'T', 7, 0 },
{ 'A', 8, 0 },
{ 'A', 6, 0 },
{ 'T', 7, 0 },
{ 'A', 8, 0 },
{ '_', -1, 0 },
{ 'L', 10, 0 },
{ 'O', 11, 0 },
@@ -155,7 +159,7 @@ class reserved_words_automaton
{ 'A', 13, 0 },
{ 'L', 14, 0 },
{ '_', -2, 0 },
{ 'O', 16, 0},
{ 'O', 16, 0 },
{ 'O', 17, 0 },
{ 'P', 18, 0 },
{ '_', -3, 0 },
@@ -238,7 +242,7 @@ int sac_parser::get_next_char()
}
else if (result == '\n')
++m_line_nr;
m_token_buffer.push_back(std::char_traits<char>::to_char_type(result));
}
@@ -277,6 +281,8 @@ sac_parser::CIFToken sac_parser::get_next_token()
m_token_buffer.clear();
m_token_value = {};
bool negative = false;
reserved_words_automaton dag;
while (result == CIFToken::UNKNOWN)
@@ -310,6 +316,15 @@ sac_parser::CIFToken sac_parser::get_next_token()
}
else if (dag.move(ch) == reserved_words_automaton::undefined)
state = State::Reserved;
else if (ch == '+' or ch == '-')
{
negative = true;
state = State::Numeric_Integer;
}
else if (ch >= '0' and ch <= '9')
state = State::Numeric_Integer;
else if (ch == '.')
state = State::Numeric_Float;
else
state = State::Value;
break;
@@ -326,7 +341,7 @@ sac_parser::CIFToken sac_parser::get_next_token()
else
m_bol = (ch == '\n');
break;
case State::Comment:
if (ch == '\n')
{
@@ -339,12 +354,12 @@ sac_parser::CIFToken sac_parser::get_next_token()
else if (not is_any_print(ch))
error("invalid character in comment");
break;
case State::QuestionMark:
if (not is_non_blank(ch))
{
retract();
result = CIFToken::VALUE;
result = CIFToken::VALUE_UNKNOWN;
}
else
state = State::Value;
@@ -356,7 +371,7 @@ sac_parser::CIFToken sac_parser::get_next_token()
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) + ")");
warning("invalid character in text field '" + std::string({ static_cast<char>(ch) }) + "' (" + std::to_string((int)ch) + ")");
break;
case State::TextItemNL:
@@ -366,7 +381,7 @@ sac_parser::CIFToken sac_parser::get_next_token()
{
assert(m_token_buffer.size() >= 2);
m_token_value = std::string_view(m_token_buffer.data() + 1, m_token_buffer.size() - 3);
result = CIFToken::VALUE;
result = CIFToken::VALUE_TEXTFIELD;
}
else if (ch == kEOF)
error("unterminated textfield");
@@ -380,14 +395,14 @@ sac_parser::CIFToken sac_parser::get_next_token()
else if (ch == quoteChar)
state = State::QuotedStringQuote;
else if (not is_any_print(ch) and cif::VERBOSE > 2)
warning("invalid character in quoted string: '" + std::string({static_cast<char>(ch)}) + "' (" + std::to_string((int)ch) + ")");
warning("invalid character in quoted string: '" + std::string({ static_cast<char>(ch) }) + "' (" + std::to_string((int)ch) + ")");
break;
case State::QuotedStringQuote:
if (is_white(ch))
{
retract();
result = CIFToken::VALUE;
result = CIFToken::VALUE_CHARSTRING;
if (m_token_buffer.size() < 2)
error("Invalid quoted string token");
@@ -422,7 +437,7 @@ sac_parser::CIFToken sac_parser::get_next_token()
if (not is_non_blank(ch))
{
retract();
result = CIFToken::VALUE;
result = CIFToken::VALUE_CHARSTRING;
m_token_value = std::string_view(m_token_buffer.data(), m_token_buffer.size());
}
else
@@ -463,11 +478,65 @@ sac_parser::CIFToken sac_parser::get_next_token()
}
break;
case State::Numeric_Integer:
if (ch == '.')
state = State::Numeric_Float;
else if (ch == 'e' or ch == 'E')
state = State::Numeric_Exponent1;
else if (not is_non_blank(ch))
{
retract();
result = CIFToken::VALUE_NUMERIC_INTEGER;
}
else if (ch < '0' or ch > '9')
state = State::Value;
break;
case State::Numeric_Float:
if (not is_non_blank(ch))
{
retract();
if (m_token_buffer.size() == 1)
result = CIFToken::VALUE_INAPPLICABLE;
else
result = CIFToken::VALUE_NUMERIC_FLOAT;
}
else if (ch == 'e' or ch == 'E')
state = State::Numeric_Exponent1;
else if (ch < '0' or ch > '9')
state = State::Value;
break;
case State::Numeric_Exponent1:
if (ch == '+' or ch == '-' or (ch >= '0' and ch <= '9'))
state = State::Numeric_Exponent2;
else
{
if (VERBOSE > 0)
std::cerr << "parsing " << std::string_view{ m_token_buffer.data(), m_token_buffer.size() } << " Invalid floating point value, expected digit or sign character\n";
state = State::Value;
}
break;
case State::Numeric_Exponent2:
if (not is_non_blank(ch))
{
retract();
result = CIFToken::VALUE_NUMERIC_FLOAT;
}
else if (ch < '0' or ch > '9')
{
if (VERBOSE > 0)
std::cerr << "parsing " << std::string_view{ m_token_buffer.data(), m_token_buffer.size() } << " Invalid floating point value, expected exponent digit\n";
state = State::Value;
}
break;
case State::Value:
if (not is_non_blank(ch))
{
retract();
result = CIFToken::VALUE;
result = CIFToken::VALUE_CHARSTRING;
m_token_value = std::string_view(m_token_buffer.data(), m_token_buffer.size());
break;
}
@@ -480,12 +549,25 @@ sac_parser::CIFToken sac_parser::get_next_token()
}
}
if (VERBOSE >= 5)
// if (VERBOSE >= 5)
// {
// std::cerr << get_token_name(result);
// if (result != CIFToken::END_OF_FILE)
// std::cerr << " " << std::quoted(m_token_value);
// std::cerr << '\n';
// }
if (result == CIFToken::VALUE_NUMERIC_INTEGER)
{
std::cerr << get_token_name(result);
if (result != CIFToken::END_OF_FILE)
std::cerr << " " << std::quoted(m_token_value);
std::cerr << '\n';
auto [ptr, ec] = std::from_chars(m_token_buffer.data(), m_token_buffer.data() + m_token_buffer.size(), m_token_value_int);
if (ec != std::errc{})
error("Invalid integer value: " + std::make_error_code(ec).message());
}
else if (result == CIFToken::VALUE_NUMERIC_FLOAT)
{
auto [ptr, ec] = std::from_chars(m_token_buffer.data(), m_token_buffer.data() + m_token_buffer.size(), m_token_value_float);
if (ec != std::errc{})
error("Invalid integer value: " + std::make_error_code(ec).message());
}
return result;
@@ -661,7 +743,7 @@ sac_parser::datablock_index sac_parser::index_datablocks()
case data:
if (dblk[si] == 0 and is_non_blank(ch))
{
datablock = {static_cast<char>(ch)};
datablock = { static_cast<char>(ch) };
state = data_name;
}
else if (dblk[si++] != ch)
@@ -738,14 +820,17 @@ void sac_parser::parse_global()
while (m_lookahead == CIFToken::ITEM_NAME)
{
match(CIFToken::ITEM_NAME);
match(CIFToken::VALUE);
if (m_lookahead >= CIFToken::VALUE_INAPPLICABLE)
match(m_lookahead);
else
match(CIFToken::VALUE_CHARSTRING);
}
}
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
std::string cat = kUnitializedCategory; // intial value acts as a guard for empty category names
while (m_lookahead == CIFToken::LOOP or m_lookahead == CIFToken::ITEM_NAME or m_lookahead == CIFToken::SAVE_NAME)
{
@@ -777,14 +862,38 @@ void sac_parser::parse_datablock()
match(CIFToken::ITEM_NAME);
}
while (m_lookahead == CIFToken::VALUE)
while (m_lookahead >= CIFToken::VALUE_INAPPLICABLE)
{
produce_row();
for (auto item_name : item_names)
{
produce_item(cat, item_name, m_token_value);
match(CIFToken::VALUE);
switch (m_lookahead)
{
case CIFToken::VALUE_INAPPLICABLE:
produce_item(cat, item_name, nullptr);
match(m_lookahead);
break;
case CIFToken::VALUE_UNKNOWN:
produce_item(cat, item_name, std::optional<std::string>{});
match(m_lookahead);
break;
case CIFToken::VALUE_NUMERIC_INTEGER:
produce_item(cat, item_name, m_token_value_int);
match(m_lookahead);
break;
case CIFToken::VALUE_NUMERIC_FLOAT:
produce_item(cat, item_name, m_token_value_float);
match(m_lookahead);
break;
case CIFToken::VALUE_CHARSTRING:
case CIFToken::VALUE_TEXTFIELD:
produce_item(cat, item_name, m_token_value);
match(m_lookahead);
break;
default:;
match(CIFToken::VALUE_CHARSTRING);
}
}
}
@@ -806,9 +915,33 @@ void sac_parser::parse_datablock()
match(CIFToken::ITEM_NAME);
produce_item(cat, itemName, m_token_value);
switch (m_lookahead)
{
case CIFToken::VALUE_INAPPLICABLE:
produce_item(cat, itemName, nullptr);
match(CIFToken::VALUE_INAPPLICABLE);
break;
case CIFToken::VALUE_UNKNOWN:
produce_item(cat, itemName, item_value{ std::optional<std::string>{} });
match(CIFToken::VALUE_UNKNOWN);
break;
case CIFToken::VALUE_NUMERIC_INTEGER:
produce_item(cat, itemName, m_token_value_int);
match(CIFToken::VALUE_NUMERIC_INTEGER);
break;
case CIFToken::VALUE_NUMERIC_FLOAT:
produce_item(cat, itemName, m_token_value_float);
match(CIFToken::VALUE_NUMERIC_FLOAT);
break;
case CIFToken::VALUE_CHARSTRING:
case CIFToken::VALUE_TEXTFIELD:
produce_item(cat, itemName, m_token_value);
match(m_lookahead);
break;
default:
match(CIFToken::VALUE_CHARSTRING);
}
match(CIFToken::VALUE);
break;
}
@@ -864,7 +997,7 @@ void parser::produce_row()
// m_row.lineNr(m_line_nr);
}
void parser::produce_item(std::string_view category, std::string_view item, std::string_view value)
void parser::produce_item(std::string_view category, std::string_view item, item_value value)
{
if (VERBOSE >= 4)
std::cerr << "producing _" << category << '.' << item << " -> " << value << '\n';
@@ -872,7 +1005,7 @@ void parser::produce_item(std::string_view category, std::string_view item, std:
if (m_category == nullptr or not iequals(category, m_category->name()))
error("inconsistent categories in loop_");
m_row[item] = m_token_value;
m_row[item] = std::move(value);
}
} // namespace cif

View File

@@ -24,17 +24,50 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "cif++/row.hpp"
#include "cif++/category.hpp"
#include "cif++/item.hpp"
#include <stdexcept>
namespace cif
{
// --------------------------------------------------------------------
void row_handle::assign(uint16_t item, std::string_view value, bool updateLinked, bool validate)
item_value s_null_item;
item_value &row_handle::operator[](uint16_t item_ix)
{
return empty() or item_ix >= m_row->size() ? s_null_item : m_row->operator[](item_ix);
}
const item_value &row_handle::operator[](uint16_t item_ix) const
{
return empty() or item_ix >= m_row->size() ? s_null_item : m_row->operator[](item_ix);
}
item_value &row_handle::operator[](std::string_view item_name)
{
auto ix = add_item(item_name);
if (ix >= size())
m_row->resize(ix + 1);
return m_row->operator[](ix);
}
const item_value &row_handle::operator[](std::string_view item_name) const
{
return operator[](get_item_ix(item_name));
}
// --------------------------------------------------------------------
void row_handle::assign(uint16_t item, item_value value, bool updateLinked, bool validate)
{
if (not m_category)
throw std::runtime_error("uninitialized row");
m_category->update_value(m_row, item, value, updateLinked, validate);
m_category->update_value(m_row, item, std::move(value), updateLinked, validate);
}
uint16_t row_handle::get_item_ix(std::string_view name) const
@@ -86,28 +119,29 @@ row_initializer::row_initializer(row_handle rh)
auto &i = r->operator[](ix);
if (not i)
continue;
emplace_back(cat.get_item_name(ix), i.text());
emplace_back(cat.get_item_name(ix), i);
}
}
void row_initializer::set_value(std::string_view name, std::string_view value)
void row_initializer::set_value(std::string name, item_value value)
{
for (auto &i : *this)
{
if (i.name() == name)
{
i.value(value);
i.value(std::move(value));
return;
}
}
emplace_back(name, value);
emplace_back(std::move(name), std::move(value));
}
void row_initializer::set_value_if_empty(std::string_view name, std::string_view value)
void row_initializer::set_value_if_empty(std::string name, item_value value)
{
if (find_if(begin(), end(), [name](auto &i) { return i.name() == name; }) == end())
emplace_back(name, value);
if (std::ranges::find_if(*this, [name](auto &i)
{ return i.name() == name; }) == end())
emplace_back(std::move(name), std::move(value));
}
} // namespace cif

View File

@@ -27,12 +27,11 @@
#include "cif++/validate.hpp"
#include "cif++/category.hpp"
#include "cif++/dictionary_parser.hpp"
#include "cif++/gzio.hpp"
#include "cif++/utilities.hpp"
#include <cassert>
#include <fstream>
#include <iostream>
#include <mutex>
// The validator depends on regular expressions. Unfortunately,
// the implementation of std::regex in g++ is buggy and crashes
@@ -75,6 +74,7 @@ struct regex_impl
private:
pcre2_code *m_rx = nullptr;
pcre2_match_data *m_data = nullptr;
mutable std::mutex m_mutex;
};
regex_impl::regex_impl(std::string_view rx)
@@ -95,6 +95,8 @@ regex_impl::regex_impl(std::string_view rx)
regex_impl::~regex_impl()
{
std::unique_lock lock(m_mutex);
if (m_data)
pcre2_match_data_free(m_data);
@@ -104,6 +106,8 @@ regex_impl::~regex_impl()
bool regex_impl::match(std::string_view v) const
{
std::unique_lock lock(m_mutex);
bool result = false;
if (int rc = pcre2_match(m_rx, (PCRE2_SPTR)v.data(), v.length(), 0, 0, m_data, nullptr); rc >= 0)
@@ -259,24 +263,24 @@ int type_validator::compare(std::string_view a, std::string_view b) const
// --------------------------------------------------------------------
void item_validator::operator()(std::string_view value) const
void item_validator::operator()(const item_value &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);
// 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
bool item_validator::validate_value(const item_value &value, std::error_code &ec) const noexcept
{
ec.clear();
if (not value.empty() and value != "?" and value != ".")
{
if (m_type != nullptr and not m_type->m_rx->match(value))
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);
}
// if (not value.empty() and value != "?" and value != ".")
// {
// if (m_type != nullptr and not m_type->m_rx->match(value))
// 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 not(bool) ec;
}

View File

@@ -41,16 +41,15 @@ list(
APPEND
CIFPP_tests
unit-v2
unit-3d
model
query
rename-compound
sugar
spinner
reconstruction
validate-pdbx
cql
matrix
# unit-3d
# model
# query
# rename-compound
# sugar
# spinner
# reconstruction
# validate-pdbx
# matrix
)
add_library(test-main OBJECT "${CMAKE_CURRENT_SOURCE_DIR}/test-main.cpp")

View File

@@ -1,157 +0,0 @@
/*-
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2025 NKI/AVL, Netherlands Cancer Institute
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "test-main.hpp"
#include <catch2/catch_test_macros.hpp>
#include <cif++.hpp>
#include <cif++/cql/transaction.hpp>
#include <stdexcept>
// --------------------------------------------------------------------
cif::file operator""_cf(const char *text, std::size_t length)
{
struct membuf : public std::streambuf
{
membuf(char *text, std::size_t length)
{
this->setg(text, text, text + length);
}
} buffer(const_cast<char *>(text), length);
std::istream is(&buffer);
return cif::file(is);
}
// --------------------------------------------------------------------
TEST_CASE("cql-1")
{
cif::file f(gTestDir / ".." / "examples" / "1cbs.cif.gz");
auto &db = f.front();
cif::cql::transaction tx(db);
// CHECK(tx.exec("SELECT COUNT(*) FROM entry").one_field().as<int>() == 1);
// CHECK(tx.exec("SELECT COUNT(*) FROM entry WHERE id = '1CBS'").one_field().as<int>() == 1);
// CHECK(tx.exec("SELECT COUNT(*) FROM entry WHERE id = 'XXXX'").one_field().as<int>() == 0);
// CHECK(tx.exec("SELECT COUNT(*) FROM citation").one_field().as<int>() == 4);
// CHECK(tx.exec("SELECT COUNT(page_last) FROM citation").one_field().as<int>() == 1);
const char *kPrimaryAuthors[] = {
"Kleywegt, G.J.",
"Bergfors, T.",
"Senn, H.",
"Le Motte, P.",
"Gsell, B.",
"Shudo, K.",
"Jones, T.A."
};
auto r = tx.exec("SELECT name, ordinal FROM citation_author WHERE citation_id = 'primary';");
CHECK(r.size() == 7);
for (size_t ix = 0; auto row : r)
{
REQUIRE(ix < (sizeof(kPrimaryAuthors) / sizeof(char *)));
CHECK(row[0].as<std::string>() == kPrimaryAuthors[ix++]);
CHECK(row[1].as<size_t>() == ix);
// CHECK(row["name"].as<std::string>() == kPrimaryAuthors[ix++]);
// CHECK(row["ordinal"].as<int>() == ix);
}
r = tx.exec("SELECT ordinal, name FROM citation_author WHERE citation_id = 'primary';");
CHECK(r.size() == 7);
for (size_t ix = 0; auto row : r)
{
REQUIRE(ix < (sizeof(kPrimaryAuthors) / sizeof(char *)));
CHECK(row[1].as<std::string>() == kPrimaryAuthors[ix++]);
CHECK(row[0].as<size_t>() == ix);
// CHECK(row["name"].as<std::string>() == kPrimaryAuthors[ix++]);
// CHECK(row["ordinal"].as<int>() == ix);
}
r = tx.exec("SELECT * FROM citation_author WHERE citation_id = 'primary';");
CHECK(r.size() == 7);
for (size_t ix = 0; auto row : r)
{
REQUIRE(ix < (sizeof(kPrimaryAuthors) / sizeof(char *)));
for (auto fld : row)
{
switch (fld.num())
{
case 0:
CHECK(fld.name() == "citation_id");
CHECK(fld.as<std::string>() == "primary");
break;
case 1:
CHECK(fld.name() == "name");
CHECK(fld.as<std::string>() == kPrimaryAuthors[ix]);
break;
case 2:
CHECK(fld.name() == "ordinal");
CHECK(fld.as<int>() == ix);
break;
default:
REQUIRE(false);
break;
}
}
++ix;
// CHECK(row[0].as<std::string>() == kPrimaryAuthors[ix++]);
// CHECK(row[1].as<int>() == ix);
CHECK(row["name"].as<std::string>() == kPrimaryAuthors[ix++]);
CHECK(row["ordinal"].as<size_t>() == ix);
}
// CHECK(tx.query_value<int>("SELECT COUNT(*) FROM citation_author WHERE citation_id = 'primary';") == 7);
// for (size_t ix = 0; auto row : r)
// {
// REQUIRE(ix < (sizeof(kPrimaryAuthors) / sizeof(char*)));
// // CHECK(row["name"].as<std::string>() == kPrimaryAuthors[ix++]);
// // CHECK(row["ordinal"].as<int>() == ix);
// }
// for (size_t ix = 0; const auto &[name, ordinal] : tx.stream<std::string, int>("SELECT name FROM citation_author WHERE citation_id = 'primary'"))
// {
// REQUIRE(ix < (sizeof(kPrimaryAuthors) / sizeof(char*)));
// CHECK(name == kPrimaryAuthors[ix++]);
// CHECK(ordinal == ix);
// }
}

View File

@@ -2,7 +2,7 @@
#include "test-main.hpp"
#include <cif++.hpp>
#include <cif++/utilities.hpp>
std::filesystem::path gTestDir = std::filesystem::current_path();
@@ -33,7 +33,7 @@ int main(int argc, char *argv[])
// initialize CCD location
cif::add_file_resource("components.cif", gTestDir / ".." / "rsrc" / "ccd-subset.cif");
cif::compound_factory::instance().push_dictionary(gTestDir / "HEM.cif");
// cif::compound_factory::instance().push_dictionary(gTestDir / "HEM.cif");
return session.run();
}

View File

@@ -24,10 +24,18 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "cif++/category.hpp"
#include "test-main.hpp"
#include <cif++.hpp>
// #include <cif++.hpp>
#include <catch2/catch_test_macros.hpp>
#include <cif++/item.hpp>
#include <cif++/category.hpp>
#include <cif++/datablock.hpp>
#include <cif++/file.hpp>
#include <cif++/row.hpp>
#include <cif++/validate.hpp>
#include <exception>
#include <stdexcept>
// --------------------------------------------------------------------
@@ -51,41 +59,41 @@ cif::file operator""_cf(const char *text, std::size_t length)
TEST_CASE("text_1")
{
CHECK(cif::iequals("TEST", "test"));
CHECK(cif::iequals(std::string_view{"TEST"}, std::string_view{"test"}));
CHECK(cif::iequals(std::string_view{ "TEST" }, std::string_view{ "test" }));
CHECK(cif::icompare("TEST", "test") == 0);
CHECK(cif::icompare(std::string_view{"TEST"}, std::string_view{"test"}) == 0);
CHECK(cif::icompare(std::string_view{ "TEST" }, std::string_view{ "test" }) == 0);
CHECK(cif::icompare("TEST1", "test") > 0);
CHECK(cif::icompare(std::string_view{"TEST1"}, std::string_view{"test"}) > 0);
CHECK(cif::icompare(std::string_view{ "TEST1" }, std::string_view{ "test" }) > 0);
CHECK(cif::icompare("aap", "noot") < 0);
CHECK(cif::icompare(std::string_view{"aap"}, std::string_view{"noot"}) < 0);
CHECK(cif::icompare(std::string_view{ "aap" }, std::string_view{ "noot" }) < 0);
}
// --------------------------------------------------------------------
// // --------------------------------------------------------------------
TEST_CASE("from_chars_1")
{
auto f = R"(data_TEST
#
loop_
_test.v
616.487
616.487000
)"_cf;
// TEST_CASE("from_chars_1")
// {
// auto f = R"(data_TEST
// #
// loop_
// _test.v
// 616.487
// 616.487000
// )"_cf;
auto &db = f.front();
auto &c = db.front();
// auto &db = f.front();
// auto &c = db.front();
auto r1 = c.front();
CHECK(r1.get<double>("v") == 616.487);
CHECK(r1["v"].compare(616.487) == 0);
// auto r1 = c.front();
// CHECK(r1.get<double>("v") == 616.487);
// CHECK(r1["v"].compare(616.487) == 0);
auto r2 = c.back();
CHECK(r2.get<double>("v") == 616.487);
CHECK(r2["v"].compare(616.487) == 0);
}
// auto r2 = c.back();
// CHECK(r2.get<double>("v") == 616.487);
// CHECK(r2["v"].compare(616.487) == 0);
// }
// --------------------------------------------------------------------
@@ -162,33 +170,84 @@ TEST_CASE("cc_2")
// This line generates a linker error on Windows:
// CHECK(buffer == test);
CHECK(std::string{ buffer } == std::string { test });
CHECK(std::string{ buffer } == std::string{ test });
}
}
TEST_CASE("item_0")
{
cif::item i1("v1", "tekst");
cif::item i2("v2", 2);
cif::item i3("v3", 3.0);
cif::item i4("v4", true);
cif::item i5("v5", nullptr);
static_assert(cif::IntegralType<int>);
static_assert(cif::FloatType<double>);
CHECK(i1.value().get<std::string>() == "tekst");
CHECK(i2.value().get<int>() == 2);
CHECK(i3.value().get<double>() == 3.0);
CHECK(i4.value().get<bool>() == true);
CHECK(i5.value().is_empty());
i2.value() = false;
CHECK(i2.value().type() == cif::item_value_type::BOOLEAN);
CHECK(i2.value().get<bool>() == false);
cif::item i6 = std::move(i1);
CHECK(i6.value().get<std::string>() == "tekst");
CHECK(i1.value().is_empty());
}
TEST_CASE("row_1")
{
cif::row r;
r.append(0, 1);
r.append(1, "twee");
cif::category cat("cat");
cat.add_item("een");
cat.add_item("twee");
cif::row_handle rh(cat, r);
const auto &[a, b] = rh.get<int, std::string>("een", "twee");
CHECK(a == 1);
CHECK(b == "twee");
rh.assign("twee", 3.0, false);
CHECK(rh[1].type() == cif::item_value_type::FLOAT);
CHECK(rh[1].get<double>() == 3.0);
}
TEST_CASE("cc_3")
{
cif::category c("foo");
c.emplace({
{ "f-1", 1 },
{ "f-2", "-1" },
{ "f-3", "+1" },
{ "f-4", " 1" },
{ "f-5", " +1" },
{ "f-6", "1 " },
});
cif::row_initializer ri{
{
{ "f-1", 1 },
{ "f-2", "-1" },
{ "f-3", "+1" },
{ "f-4", " 1" },
{ "f-5", " +1" },
{ "f-6", "1 " },
}
};
c.emplace(std::move(ri));
auto row = c.front();
CHECK(row["f-1"].as<int>() == 1);
CHECK(row["f-2"].as<int>() == -1);
CHECK(row["f-3"].as<int>() == 1);
CHECK(row["f-1"].get<int>() == 1);
CHECK(row["f-2"].get<int>() == -1);
// CHECK_THROWS_AS(row["f-4"].as<int>(), std::exception);
// CHECK_THROWS_AS(row["f-5"].as<int>(), std::exception);
// CHECK_THROWS_AS(row["f-6"].as<int>(), std::exception);
CHECK(row["f-4"].as<int>() == 0);
CHECK(row["f-5"].as<int>() == 0);
CHECK(row["f-6"].as<int>() == 0);
CHECK_FALSE(row["f-3"].is_number());
CHECK_FALSE(row["f-4"].is_number());
CHECK_FALSE(row["f-5"].is_number());
CHECK_FALSE(row["f-6"].is_number());
}
TEST_CASE("item_1")
@@ -225,19 +284,21 @@ TEST_CASE("item_2")
using namespace cif;
cif::item i0("test1");
CHECK(i0.value() == ".");
CHECK(i0.value().is_empty());
CHECK(i0.value().type() == cif::item_value_type::EMPTY);
cif::item i1("test1", std:: optional<float>());
CHECK(i1.value() == "?");
cif::item i1("test1", std::optional<float>());
CHECK(i0.value().is_empty());
// CHECK(i1.value() == "?");
cif::item i2("test1", std::make_optional<float>(1.f));
CHECK(i2.value() == "1");
// CHECK(i2.value() == "1");
cif::item i3("test1", std::optional<float>(), 2);
CHECK(i3.value() == "?");
// cif::item i3("test1", std::optional<float>(), 2);
// CHECK(i3.value() == "?");
cif::item i4("test1", std::make_optional<float>(1.f), 2);
CHECK(i4.value() == "1.00");
// cif::item i4("test1", std::make_optional<float>(1.f), 2);
// CHECK(i4.value() == "1.00");
}
// --------------------------------------------------------------------
@@ -248,13 +309,13 @@ TEST_CASE("r_1")
c.emplace({
{ "f-1", 1 },
{ "f-2", "two" },
{ "f-3", 3.0f, 3 },
{ "f-3", 3.0f/* , 3 */ },
});
auto row = c.front();
CHECK(row["f-1"].compare(1) == 0);
CHECK(row["f-2"].compare("two") == 0);
CHECK(row["f-3"].compare(3.0f) == 0); // This fails when running in valgrind... sigh
// CHECK(row["f-1"].compare(1) == 0);
// CHECK(row["f-2"].compare("two") == 0);
// CHECK(row["f-3"].compare(3.0f) == 0); // This fails when running in valgrind... sigh
const auto &[f1, f2, f3] = row.get<int, std::string, float>("f-1", "f-2", "f-3");
@@ -302,7 +363,7 @@ TEST_CASE("c_1")
for (auto r : c)
{
CHECK(r["id"].as<int>() == n);
CHECK(r["id"].get<int>() == n);
CHECK(r["s"].compare(ts[n - 1]) == 0);
++n;
}
@@ -413,36 +474,36 @@ TEST_CASE("ci_1")
CHECK(i1 == i5);
}
TEST_CASE("os_1")
{
using namespace cif::literals;
using namespace std::literals;
// TEST_CASE("os_1")
// {
// using namespace cif::literals;
// using namespace std::literals;
std::tuple<int, const char *> D[] = {
{ 1, "aap" },
{ 2, "noot" },
{ 3, "mies" }
};
// std::tuple<int, const char *> D[] = {
// { 1, "aap" },
// { 2, "noot" },
// { 3, "mies" }
// };
cif::category c("foo");
// cif::category c("foo");
for (const auto &[id, s] : D)
c.emplace({ { "id", id }, { "s", s } });
// for (const auto &[id, s] : D)
// c.emplace({ { "id", id }, { "s", s } });
for (auto rh : c)
{
rh["o"].os(1, ',', 2, ": ", rh.get<std::string>("s"));
}
// for (auto rh : c)
// {
// rh["o"].os(1, ',', 2, ": ", rh.get<std::string>("s"));
// }
for (const auto &[id, s] : D)
{
auto rh = c.find1("id"_key == id);
// for (const auto &[id, s] : D)
// {
// auto rh = c.find1("id"_key == id);
CHECK(rh.get<int>("id") == id);
CHECK(rh.get<std::string>("s") == s);
CHECK(rh.get<std::string>("o") == "1,2: "s + s);
}
}
// CHECK(rh.get<int>("id") == id);
// CHECK(rh.get<std::string>("s") == s);
// CHECK(rh.get<std::string>("o") == "1,2: "s + s);
// }
// }
// --------------------------------------------------------------------
@@ -460,6 +521,13 @@ _test.name
5 .
)"_cf;
REQUIRE(f.size() == 1);
REQUIRE(f.contains("TEST"));
REQUIRE(f.front().size() == 1);
REQUIRE(f.front().contains("test"));
REQUIRE(f.front()["test"].size() == 5);
REQUIRE(f.front()["test"].front().size() == 2);
for (auto r : f.front()["test"])
{
int id;
@@ -472,7 +540,9 @@ _test.name
case 1: CHECK(*name == "aap"); break;
case 2: CHECK(*name == "noot"); break;
case 3: CHECK(*name == "mies"); break;
default: CHECK(name.has_value() == false);
case 4:
case 5: CHECK_FALSE(name.has_value());break;
default: CHECK(false);
}
}
}
@@ -515,8 +585,8 @@ _test.name
auto n2 = test.erase(cif::key("id") == 1, [](cif::row_handle r)
{
CHECK(r["id"].as<int>() == 1);
CHECK(r["name"].as<std::string>() == "aap"); });
CHECK(r["id"].get<int>() == 1);
CHECK(r["name"].get<std::string>() == "aap"); });
CHECK(n2 == 1);
@@ -569,18 +639,18 @@ _test.value
for (auto r : test.find(cif::key("name") == "aap"))
{
CHECK(++n == 1);
CHECK(r["id"].as<int>() == 1);
CHECK(r["name"].as<std::string>() == "aap");
CHECK(r["value"].as<float>() == 1.0f);
CHECK(r["id"].get<int>() == 1);
CHECK(r["name"].get<std::string>() == "aap");
CHECK(r["value"].get<float>() == 1.0f);
}
auto t = test.find(cif::key("id") == 1);
CHECK(not t.empty());
CHECK(t.front()["name"].as<std::string>() == "aap");
REQUIRE(not t.empty());
CHECK(t.front()["name"].get<std::string>() == "aap");
auto t2 = test.find(cif::key("value") == 1.2);
CHECK(not t2.empty());
CHECK(t2.front()["name"].as<std::string>() == "mies");
REQUIRE(not t2.empty());
CHECK(t2.front()["name"].get<std::string>() == "mies");
}
TEST_CASE("ut3")
@@ -1554,9 +1624,9 @@ _cat_2.parent_id3
// check iterate children
auto PR2set = cat1.find(cif::key("id") == 2);
CHECK(PR2set.size() == 1);
REQUIRE(PR2set.size() == 1);
auto PR2 = PR2set.front();
CHECK(PR2["id"].as<int>() == 2);
CHECK(PR2["id"].get<int>() == 2);
auto CR2set = cat1.get_children(PR2, cat2);
CHECK(CR2set.size() == 3);
@@ -1564,7 +1634,7 @@ _cat_2.parent_id3
std::vector<int> CRids;
std::transform(CR2set.begin(), CR2set.end(), std::back_inserter(CRids), [](cif::row_handle r)
{ return r["id"].as<int>(); });
{ return r["id"].get<int>(); });
std::sort(CRids.begin(), CRids.end());
CHECK(CRids == std::vector<int>({ 4, 5, 6 }));
@@ -3099,7 +3169,6 @@ data_test_dict.dic
or positive integers.
;
###################
## AUDIT_CONFORM ##
###################
@@ -3176,7 +3245,6 @@ save__audit_conform.dict_version
_item_type.code text
save_
save_cat_1
_category.description 'A simple test category'
_category.id cat_1
@@ -3256,8 +3324,8 @@ _cat_1.name
CHECK(f2.is_valid());
auto &audit_conform = f2.front()["audit_conform"];
CHECK(audit_conform.front()["dict_name"].as<std::string>() == "test_dict.dic");
CHECK(audit_conform.front()["dict_version"].as<float>() == 1.0);
CHECK(audit_conform.front()["dict_name"].get<std::string>() == "test_dict.dic");
CHECK(audit_conform.front()["dict_version"].get<float>() == 1.0);
}
// --------------------------------------------------------------------
@@ -3442,8 +3510,8 @@ _date today
CHECK(not cat.empty());
auto r = cat.front();
CHECK(r["version"].as<std::string>() == "1.0");
CHECK(r["date"].as<std::string>() == "today");
CHECK(r["version"].get<std::string>() == "1.0");
CHECK(r["date"].get<std::string>() == "today");
std::stringstream ss;
ss << db;
@@ -3481,92 +3549,92 @@ _test.value
CHECK(v.has_value() == false);
}
// --------------------------------------------------------------------
// // --------------------------------------------------------------------
TEST_CASE("compound_test_1")
{
cif::compound_factory::instance().push_dictionary(gTestDir / "REA_v2.cif");
auto compound = cif::compound_factory::instance().create("REA_v2");
CHECK(compound != nullptr);
CHECK(cif::iequals(compound->id(), "REA_v2"));
}
// TEST_CASE("compound_test_1")
// {
// cif::compound_factory::instance().push_dictionary(gTestDir / "REA_v2.cif");
// auto compound = cif::compound_factory::instance().create("REA_v2");
// CHECK(compound != nullptr);
// CHECK(cif::iequals(compound->id(), "REA_v2"));
// }
// --------------------------------------------------------------------
// // --------------------------------------------------------------------
TEST_CASE("pdb_parser_test_1")
{
char k1CBS[] = R"(HEADER RETINOIC-ACID TRANSPORT 28-SEP-94 1CBS
TITLE CRYSTAL STRUCTURE OF CELLULAR RETINOIC-ACID-BINDING
TITLE 2 PROTEINS I AND II IN COMPLEX WITH ALL-TRANS-RETINOIC ACID
TITLE 3 AND A SYNTHETIC RETINOID
COMPND MOL_ID: 1;
COMPND 2 MOLECULE: CELLULAR RETINOIC ACID BINDING PROTEIN TYPE II;
COMPND 3 CHAIN: A;
COMPND 4 ENGINEERED: YES
SOURCE MOL_ID: 1;
SOURCE 2 ORGANISM_SCIENTIFIC: HOMO SAPIENS;
SOURCE 3 ORGANISM_COMMON: HUMAN;
SOURCE 4 ORGANISM_TAXID: 9606;
SOURCE 5 CELL_LINE: BL21;
SOURCE 6 GENE: HUMAN CRABP-II;
SOURCE 7 EXPRESSION_SYSTEM: ESCHERICHIA COLI BL21(DE3);
SOURCE 8 EXPRESSION_SYSTEM_TAXID: 469008;
SOURCE 9 EXPRESSION_SYSTEM_STRAIN: BL21 (DE3);
SOURCE 10 EXPRESSION_SYSTEM_PLASMID: PET-3A
KEYWDS RETINOIC-ACID TRANSPORT
EXPDTA X-RAY DIFFRACTION
AUTHOR G.J.KLEYWEGT,T.BERGFORS,T.A.JONES
ATOM 1 N PRO A 1 16.979 13.301 44.555 1.00 30.05 N
ATOM 2 CA PRO A 1 18.150 13.525 43.680 1.00 28.82 C
ATOM 3 C PRO A 1 18.656 14.966 43.784 1.00 26.59 C
ATOM 4 O PRO A 1 17.890 15.889 44.078 1.00 26.84 O
ATOM 5 CB PRO A 1 17.678 13.270 42.255 1.00 29.24 C
ATOM 6 CG PRO A 1 16.248 13.734 42.347 1.00 29.29 C
ATOM 7 CD PRO A 1 15.762 13.216 43.724 1.00 30.71 C)";
// TEST_CASE("pdb_parser_test_1")
// {
// char k1CBS[] = R"(HEADER RETINOIC-ACID TRANSPORT 28-SEP-94 1CBS
// TITLE CRYSTAL STRUCTURE OF CELLULAR RETINOIC-ACID-BINDING
// TITLE 2 PROTEINS I AND II IN COMPLEX WITH ALL-TRANS-RETINOIC ACID
// TITLE 3 AND A SYNTHETIC RETINOID
// COMPND MOL_ID: 1;
// COMPND 2 MOLECULE: CELLULAR RETINOIC ACID BINDING PROTEIN TYPE II;
// COMPND 3 CHAIN: A;
// COMPND 4 ENGINEERED: YES
// SOURCE MOL_ID: 1;
// SOURCE 2 ORGANISM_SCIENTIFIC: HOMO SAPIENS;
// SOURCE 3 ORGANISM_COMMON: HUMAN;
// SOURCE 4 ORGANISM_TAXID: 9606;
// SOURCE 5 CELL_LINE: BL21;
// SOURCE 6 GENE: HUMAN CRABP-II;
// SOURCE 7 EXPRESSION_SYSTEM: ESCHERICHIA COLI BL21(DE3);
// SOURCE 8 EXPRESSION_SYSTEM_TAXID: 469008;
// SOURCE 9 EXPRESSION_SYSTEM_STRAIN: BL21 (DE3);
// SOURCE 10 EXPRESSION_SYSTEM_PLASMID: PET-3A
// KEYWDS RETINOIC-ACID TRANSPORT
// EXPDTA X-RAY DIFFRACTION
// AUTHOR G.J.KLEYWEGT,T.BERGFORS,T.A.JONES
// ATOM 1 N PRO A 1 16.979 13.301 44.555 1.00 30.05 N
// ATOM 2 CA PRO A 1 18.150 13.525 43.680 1.00 28.82 C
// ATOM 3 C PRO A 1 18.656 14.966 43.784 1.00 26.59 C
// ATOM 4 O PRO A 1 17.890 15.889 44.078 1.00 26.84 O
// ATOM 5 CB PRO A 1 17.678 13.270 42.255 1.00 29.24 C
// ATOM 6 CG PRO A 1 16.248 13.734 42.347 1.00 29.29 C
// ATOM 7 CD PRO A 1 15.762 13.216 43.724 1.00 30.71 C)";
struct membuf : public std::streambuf
{
membuf(char *text, std::size_t length)
{
this->setg(text, text, text + length);
}
} buffer(k1CBS, sizeof(k1CBS) - 1);
// struct membuf : public std::streambuf
// {
// membuf(char *text, std::size_t length)
// {
// this->setg(text, text, text + length);
// }
// } buffer(k1CBS, sizeof(k1CBS) - 1);
std::istream is(&buffer);
// std::istream is(&buffer);
auto f = cif::pdb::read(is);
}
// auto f = cif::pdb::read(is);
// }
// --------------------------------------------------------------------
// // --------------------------------------------------------------------
TEST_CASE("compound_not_found_test_1")
{
auto cmp = cif::compound_factory::instance().create("&&&");
CHECK(cmp == nullptr);
}
// TEST_CASE("compound_not_found_test_1")
// {
// auto cmp = cif::compound_factory::instance().create("&&&");
// CHECK(cmp == nullptr);
// }
// --------------------------------------------------------------------
// PDB2CIF tests
// // --------------------------------------------------------------------
// // PDB2CIF tests
TEST_CASE("pdb2cif_formula_weight")
{
cif::compound_factory::instance().push_dictionary(gTestDir / "REA.cif");
// TEST_CASE("pdb2cif_formula_weight")
// {
// cif::compound_factory::instance().push_dictionary(gTestDir / "REA.cif");
cif::file a = cif::pdb::read(gTestDir / "pdb1cbs.ent.gz");
auto fw = a.front()["entity"].find1<float>(cif::key("id") == 1, "formula_weight");
CHECK(std::abs(fw - 15581.802f) < 0.1f);
// cif::file a = cif::pdb::read(gTestDir / "pdb1cbs.ent.gz");
fw = a.front()["entity"].find1<float>(cif::key("id") == 2, "formula_weight");
CHECK(fw == 300.435f);
// auto fw = a.front()["entity"].find1<float>(cif::key("id") == 1, "formula_weight");
// CHECK(std::abs(fw - 15581.802f) < 0.1f);
fw = a.front()["entity"].find1<float>(cif::key("id") == 3, "formula_weight");
CHECK(fw == 18.015f);
}
// fw = a.front()["entity"].find1<float>(cif::key("id") == 2, "formula_weight");
// CHECK(fw == 300.435f);
// --------------------------------------------------------------------
// fw = a.front()["entity"].find1<float>(cif::key("id") == 3, "formula_weight");
// CHECK(fw == 18.015f);
// }
TEST_CASE("update_values_with_provider")
{
// // --------------------------------------------------------------------
}
// TEST_CASE("update_values_with_provider")
// {
// }