Commit e5eb6225 by Maarten L. Hekkelman

started with validator, running into the ground

parent 98ff7943
...@@ -214,6 +214,9 @@ set(project_sources ...@@ -214,6 +214,9 @@ set(project_sources
${PROJECT_SOURCE_DIR}/src/Structure.cpp ${PROJECT_SOURCE_DIR}/src/Structure.cpp
${PROJECT_SOURCE_DIR}/src/Symmetry.cpp ${PROJECT_SOURCE_DIR}/src/Symmetry.cpp
${PROJECT_SOURCE_DIR}/src/TlsParser.cpp ${PROJECT_SOURCE_DIR}/src/TlsParser.cpp
${PROJECT_SOURCE_DIR}/src/v2/dictionary_parser.cpp
${PROJECT_SOURCE_DIR}/src/v2/validate.cpp
) )
set(project_headers set(project_headers
......
...@@ -26,6 +26,8 @@ ...@@ -26,6 +26,8 @@
#pragma once #pragma once
#include <cif++/v2/forward_decl.hpp>
#include <cif++/v2/condition.hpp> #include <cif++/v2/condition.hpp>
#include <cif++/v2/iterator.hpp> #include <cif++/v2/iterator.hpp>
#include <cif++/v2/row.hpp> #include <cif++/v2/row.hpp>
...@@ -35,8 +37,7 @@ namespace cif::v2 ...@@ -35,8 +37,7 @@ namespace cif::v2
// -------------------------------------------------------------------- // --------------------------------------------------------------------
template < template <typename Alloc>
typename Alloc = std::allocator<std::byte>>
class category_t class category_t
{ {
private: private:
...@@ -106,7 +107,6 @@ class category_t ...@@ -106,7 +107,6 @@ class category_t
class row class row
{ {
public: public:
row() = default; row() = default;
private: private:
...@@ -611,10 +611,9 @@ class category_t ...@@ -611,10 +611,9 @@ class category_t
// for (auto &cr : rows) // for (auto &cr : rows)
// cr.assign(childTag, value, false); // cr.assign(childTag, value, false);
// } // }
// } // }
} }
private: private:
using char_allocator_type = typename std::allocator_traits<Alloc>::template rebind_alloc<char>; using char_allocator_type = typename std::allocator_traits<Alloc>::template rebind_alloc<char>;
using char_allocator_traits = std::allocator_traits<char_allocator_type>; using char_allocator_traits = std::allocator_traits<char_allocator_type>;
...@@ -706,7 +705,7 @@ class category_t ...@@ -706,7 +705,7 @@ class category_t
delete_row(result); delete_row(result);
throw; throw;
} }
return result; return result;
} }
......
...@@ -26,6 +26,7 @@ ...@@ -26,6 +26,7 @@
#pragma once #pragma once
#include <cassert>
#include <functional> #include <functional>
#include <iostream> #include <iostream>
#include <regex> #include <regex>
...@@ -119,8 +120,8 @@ class condition_t ...@@ -119,8 +120,8 @@ class condition_t
bool operator()(const category_type &c, const row_type &r) const bool operator()(const category_type &c, const row_type &r) const
{ {
assert(m_impl); assert(this->m_impl != nullptr);
assert(m_prepared); assert(this->m_prepared);
return m_impl ? m_impl->test(c, r) : false; return m_impl ? m_impl->test(c, r) : false;
} }
...@@ -448,27 +449,26 @@ struct empty ...@@ -448,27 +449,26 @@ struct empty
inline constexpr empty null = empty(); inline constexpr empty null = empty();
template <typename Category> struct key
struct key_t
{ {
explicit key_t(const std::string &itemTag) explicit key(const std::string &itemTag)
: m_item_tag(itemTag) : m_item_tag(itemTag)
{ {
} }
explicit key_t(const char *itemTag) explicit key(const char *itemTag)
: m_item_tag(itemTag) : m_item_tag(itemTag)
{ {
} }
key_t(const key_t &) = delete; key(const key &) = delete;
key_t &operator=(const key_t &) = delete; key &operator=(const key &) = delete;
std::string m_item_tag; std::string m_item_tag;
}; };
template <typename Category, typename T> template <typename Category, typename T>
condition_t<Category> operator==(const key_t<Category> &key, const T &v) condition_t<Category> operator==(const key &key, const T &v)
{ {
using category_type = Category; using category_type = Category;
using row_type = row_handle<category_type>; using row_type = row_handle<category_type>;
...@@ -483,7 +483,7 @@ condition_t<Category> operator==(const key_t<Category> &key, const T &v) ...@@ -483,7 +483,7 @@ condition_t<Category> operator==(const key_t<Category> &key, const T &v)
} }
template <typename Category> template <typename Category>
inline condition_t<Category> operator==(const key_t<Category> &key, const char *value) inline condition_t<Category> operator==(const key &key, const char *value)
{ {
using category_type = Category; using category_type = Category;
using row_type = row_handle<category_type>; using row_type = row_handle<category_type>;
...@@ -512,20 +512,20 @@ inline condition_t<Category> operator==(const key_t<Category> &key, const char * ...@@ -512,20 +512,20 @@ inline condition_t<Category> operator==(const key_t<Category> &key, const char *
// } // }
template <typename Category, typename T> template <typename Category, typename T>
condition_t<Category> operator!=(const key_t<Category> &key, const T &v) condition_t<Category> operator!=(const key &key, const T &v)
{ {
return condition_t<Category>(new detail::not_condition_impl<Category>(operator==(key, v))); return condition_t<Category>(new detail::not_condition_impl<Category>(operator==(key, v)));
} }
template <typename Category> template <typename Category>
inline condition_t<Category> operator!=(const key_t<Category> &key, const char *v) inline condition_t<Category> operator!=(const key &key, const char *v)
{ {
std::string value(v ? v : ""); std::string value(v ? v : "");
return condition_t<Category>(new detail::not_condition_impl<Category>(operator==(key, value))); return condition_t<Category>(new detail::not_condition_impl<Category>(operator==(key, value)));
} }
template <typename Category, typename T> template <typename Category, typename T>
condition_t<Category> operator>(const key_t<Category> &key, const T &v) condition_t<Category> operator>(const key &key, const T &v)
{ {
using category_type = Category; using category_type = Category;
using row_type = row_handle<category_type>; using row_type = row_handle<category_type>;
...@@ -540,7 +540,7 @@ condition_t<Category> operator>(const key_t<Category> &key, const T &v) ...@@ -540,7 +540,7 @@ condition_t<Category> operator>(const key_t<Category> &key, const T &v)
} }
template <typename Category, typename T> template <typename Category, typename T>
condition_t<Category> operator>=(const key_t<Category> &key, const T &v) condition_t<Category> operator>=(const key &key, const T &v)
{ {
using category_type = Category; using category_type = Category;
using row_type = row_handle<category_type>; using row_type = row_handle<category_type>;
...@@ -555,7 +555,7 @@ condition_t<Category> operator>=(const key_t<Category> &key, const T &v) ...@@ -555,7 +555,7 @@ condition_t<Category> operator>=(const key_t<Category> &key, const T &v)
} }
template <typename Category, typename T> template <typename Category, typename T>
condition_t<Category> operator<(const key_t<Category> &key, const T &v) condition_t<Category> operator<(const key &key, const T &v)
{ {
using category_type = Category; using category_type = Category;
using row_type = row_handle<category_type>; using row_type = row_handle<category_type>;
...@@ -570,7 +570,7 @@ condition_t<Category> operator<(const key_t<Category> &key, const T &v) ...@@ -570,7 +570,7 @@ condition_t<Category> operator<(const key_t<Category> &key, const T &v)
} }
template <typename Category, typename T> template <typename Category, typename T>
condition_t<Category> operator<=(const key_t<Category> &key, const T &v) condition_t<Category> operator<=(const key &key, const T &v)
{ {
using category_type = Category; using category_type = Category;
using row_type = row_handle<category_type>; using row_type = row_handle<category_type>;
...@@ -585,13 +585,13 @@ condition_t<Category> operator<=(const key_t<Category> &key, const T &v) ...@@ -585,13 +585,13 @@ condition_t<Category> operator<=(const key_t<Category> &key, const T &v)
} }
template <typename Category> template <typename Category>
inline condition_t<Category> operator==(const key_t<Category> &key, const std::regex &rx) inline condition_t<Category> operator==(const key &key, const std::regex &rx)
{ {
return condition_t<Category>(new detail::keyMatchescondition_impl<Category>(key.m_item_tag, rx)); return condition_t<Category>(new detail::keyMatchescondition_impl<Category>(key.m_item_tag, rx));
} }
template <typename Category> template <typename Category>
inline condition_t<Category> operator==(const key_t<Category> &key, const empty &) inline condition_t<Category> operator==(const key &key, const empty &)
{ {
return condition_t<Category>(new detail::keyIsemptycondition_impl<Category>(key.m_item_tag)); return condition_t<Category>(new detail::keyIsemptycondition_impl<Category>(key.m_item_tag));
} }
...@@ -626,12 +626,10 @@ inline condition_t<Category> all() ...@@ -626,12 +626,10 @@ inline condition_t<Category> all()
namespace literals namespace literals
{ {
inline key operator""_key(const char *text, size_t length)
// template<typename Category> {
// inline key_t<Category> operator""_key(const char *text, size_t length) return key(std::string(text, length));
// { }
// return key<Category>(std::string(text, length));
// }
inline constexpr empty null = empty(); inline constexpr empty null = empty();
......
...@@ -26,6 +26,8 @@ ...@@ -26,6 +26,8 @@
#pragma once #pragma once
#include <cif++/v2/forward_decl.hpp>
#include <cif++/v2/category.hpp> #include <cif++/v2/category.hpp>
namespace cif::v2 namespace cif::v2
...@@ -33,9 +35,7 @@ namespace cif::v2 ...@@ -33,9 +35,7 @@ namespace cif::v2
// -------------------------------------------------------------------- // --------------------------------------------------------------------
template < template <typename Alloc, typename Category>
typename Alloc = std::allocator<void>,
typename Category = category_t<Alloc>>
class datablock_t class datablock_t
{ {
public: public:
...@@ -48,6 +48,8 @@ class datablock_t ...@@ -48,6 +48,8 @@ class datablock_t
using iterator = category_type_list::iterator; using iterator = category_type_list::iterator;
using const_iterator = category_type_list::const_iterator; using const_iterator = category_type_list::const_iterator;
using reference = typename category_type_list::reference;
datablock_t(std::string_view name, const allocator_type &alloc = allocator_type()) datablock_t(std::string_view name, const allocator_type &alloc = allocator_type())
: m_categories(alloc) : m_categories(alloc)
, m_name(name) , m_name(name)
...@@ -81,11 +83,28 @@ class datablock_t ...@@ -81,11 +83,28 @@ class datablock_t
// -------------------------------------------------------------------- // --------------------------------------------------------------------
bool empty() const { return m_categories.empty(); }
size_t size() const { return m_categories.size(); }
reference front() { return m_categories.front(); }
reference back() { return m_categories.back(); }
iterator begin() { return m_categories.begin(); }
iterator end() { return m_categories.end(); }
const_iterator cbegin() { return m_categories.cbegin(); }
const_iterator cend() { return m_categories.cend(); }
const_iterator begin() const { return m_categories.begin(); }
const_iterator end() const { return m_categories.end(); }
// --------------------------------------------------------------------
category_type &operator[](std::string_view name) category_type &operator[](std::string_view name)
{ {
auto i = std::find_if(m_categories.begin(), m_categories.end(), [name](const category_type &c) auto i = std::find_if(m_categories.begin(), m_categories.end(), [name](const category_type &c)
{ return iequals(c.name(), name); }); { return iequals(c.name(), name); });
if (i != m_categories.end()) if (i != m_categories.end())
return *i; return *i;
...@@ -133,7 +152,7 @@ class datablock_t ...@@ -133,7 +152,7 @@ class datablock_t
// cat.updateLinks(); // cat.updateLinks();
} }
return std::make_tuple(m_categories.begin(), is_new); return std::make_tuple(m_categories.begin(), is_new);
} }
void write(std::ostream &os) const void write(std::ostream &os) const
...@@ -185,4 +204,4 @@ class datablock_t ...@@ -185,4 +204,4 @@ class datablock_t
using datablock = datablock_t<>; using datablock = datablock_t<>;
} } // namespace cif::v2
\ No newline at end of file \ No newline at end of file
/*-
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2020 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++/v2/validate.hpp>
namespace cif::v2
{
Validator parse_dictionary(std::string_view name, std::istream &is);
} // namespace cif::v2
...@@ -26,8 +26,10 @@ ...@@ -26,8 +26,10 @@
#pragma once #pragma once
#include "datablock.hpp" #include <cif++/v2/forward_decl.hpp>
#include "parser.hpp"
#include <cif++/v2/datablock.hpp>
#include <cif++/v2/parser.hpp>
namespace cif::v2 namespace cif::v2
{ {
...@@ -35,9 +37,9 @@ namespace cif::v2 ...@@ -35,9 +37,9 @@ namespace cif::v2
// -------------------------------------------------------------------- // --------------------------------------------------------------------
template < template <
typename Alloc = std::allocator<void>, typename Alloc,
typename Datablock = datablock_t<Alloc>, typename Datablock,
typename Category = typename Datablock::category_type> typename Category>
class file_t class file_t
{ {
public: public:
...@@ -56,7 +58,7 @@ class file_t ...@@ -56,7 +58,7 @@ class file_t
using iterator = datablock_list::iterator; using iterator = datablock_list::iterator;
using const_iterator = datablock_list::const_iterator; using const_iterator = datablock_list::const_iterator;
using parser_type = parser_t<file_t, datablock_type, category_type>; using parser_type = parser_t<allocator_type, file_t, datablock_type, category_type>;
file_t() = default; file_t() = default;
......
/*-
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2020 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 <memory>
namespace cif::v2
{
template <typename Alloc = std::allocator<void>>
class category_t;
template <typename Alloc = std::allocator<void>, typename Category = category_t<Alloc>>
class datablock_t;
template <typename Alloc = std::allocator<void>, typename Datablock = datablock_t<Alloc>, typename Category = typename Datablock::category_type>
class file_t;
template <typename Alloc = std::allocator<void>, typename File = file_t<Alloc>, typename Datablock = datablock_t<Alloc>, typename Category = typename Datablock::category_type>
class parser_t;
} // namespace cif::v2
\ No newline at end of file
...@@ -192,7 +192,7 @@ struct item_handle ...@@ -192,7 +192,7 @@ struct item_handle
void swap(item_handle &b); void swap(item_handle &b);
template <typename T = std::string> template <typename T = std::string>
auto as() const auto as() const -> T
{ {
using value_type = std::remove_cv_t<std::remove_reference_t<T>>; using value_type = std::remove_cv_t<std::remove_reference_t<T>>;
return item_value_as<value_type>::convert(*this); return item_value_as<value_type>::convert(*this);
...@@ -398,10 +398,10 @@ template <typename Row> ...@@ -398,10 +398,10 @@ template <typename Row>
template <size_t N> template <size_t N>
struct item_handle<Row>::item_value_as<char[N]> struct item_handle<Row>::item_value_as<char[N]>
{ {
static std::string_view convert(const item_handle &ref) // static std::string_view convert(const item_handle &ref)
{ // {
return ref.text(); // return ref.text();
} // }
static int compare(const item_handle &ref, const char (&value)[N], bool icase) static int compare(const item_handle &ref, const char (&value)[N], bool icase)
{ {
...@@ -413,10 +413,10 @@ template <typename Row> ...@@ -413,10 +413,10 @@ template <typename Row>
template <typename T> template <typename T>
struct item_handle<Row>::item_value_as<T, std::enable_if_t<std::is_same_v<T, const char *>>> struct item_handle<Row>::item_value_as<T, std::enable_if_t<std::is_same_v<T, const char *>>>
{ {
static std::string_view convert(const item_handle &ref) // static std::string_view convert(const item_handle &ref)
{ // {
return ref.text(); // return ref.text();
} // }
static int compare(const item_handle &ref, const char *value, bool icase) static int compare(const item_handle &ref, const char *value, bool icase)
{ {
...@@ -428,9 +428,10 @@ template <typename Row> ...@@ -428,9 +428,10 @@ template <typename Row>
template <typename T> template <typename T>
struct item_handle<Row>::item_value_as<T, std::enable_if_t<std::is_same_v<T, std::string>>> struct item_handle<Row>::item_value_as<T, std::enable_if_t<std::is_same_v<T, std::string>>>
{ {
static std::string_view convert(const item_handle &ref) static std::string convert(const item_handle &ref)
{ {
return ref.text(); std::string_view txt = ref.text();
return { txt.data(), txt.size() };
} }
static int compare(const item_handle &ref, const std::string &value, bool icase) static int compare(const item_handle &ref, const std::string &value, bool icase)
......
...@@ -26,8 +26,19 @@ ...@@ -26,8 +26,19 @@
#pragma once #pragma once
#include <cassert>
#include <iostream>
#include <map> #include <map>
#include <stack> #include <stack>
#include <regex>
#include <cif++/v2/forward_decl.hpp>
#include <cif++/CifUtils.hpp>
namespace cif
{
extern int VERBOSE;
}
namespace cif::v2 namespace cif::v2
{ {
...@@ -955,10 +966,7 @@ class sac_parser ...@@ -955,10 +966,7 @@ class sac_parser
// -------------------------------------------------------------------- // --------------------------------------------------------------------
template < template <typename Alloc, typename File, typename Datablock, typename Category>
typename File,
typename Datablock,
typename Category>
class parser_t : public sac_parser class parser_t : public sac_parser
{ {
public: public:
...@@ -975,7 +983,8 @@ class parser_t : public sac_parser ...@@ -975,7 +983,8 @@ class parser_t : public sac_parser
void produce_datablock(const std::string &name) override void produce_datablock(const std::string &name) override
{ {
std::tie(m_datablock, std::ignore) = m_file.emplace(name); const auto &[iter, ignore] = m_file.emplace(name);
m_datablock = &(*iter);
} }
void produce_category(const std::string &name) override void produce_category(const std::string &name) override
...@@ -1009,31 +1018,9 @@ class parser_t : public sac_parser ...@@ -1009,31 +1018,9 @@ class parser_t : public sac_parser
protected: protected:
file_type &m_file; file_type &m_file;
file_type::iterator m_datablock; datablock_type *m_datablock;
datablock_type::iterator m_category; datablock_type::iterator m_category;
row_handle_type m_row; row_handle_type m_row;
}; };
// --------------------------------------------------------------------
// class DictParser : public Parser
// {
// public:
// DictParser(Validator &validator, std::istream &is);
// ~DictParser();
// void loadDictionary();
// private:
// virtual void parse_save_frame();
// bool collectItemTypes();
// void linkItems();
// Validator &mValidator;
// File mFile;
// struct DictParserDataImpl *mImpl;
// bool mCollectedItemTypes = false;
// };
} // namespace cif::v2 } // namespace cif::v2
...@@ -26,14 +26,20 @@ ...@@ -26,14 +26,20 @@
#pragma once #pragma once
#include <filesystem>
// duh.. https://gcc.gnu.org/bugzilla/show_bug.cgi?id=86164 // duh.. https://gcc.gnu.org/bugzilla/show_bug.cgi?id=86164
// #include <regex> // #include <regex>
#include <boost/regex.hpp>
#include <set> // TODO: get rid of boost::iostreams
#include <boost/iostreams/filter/gzip.hpp>
#include <boost/iostreams/filtering_stream.hpp>
#include <boost/regex.hpp>
#include <cif++/CifUtils.hpp> #include <cif++/CifUtils.hpp>
namespace io = boost::iostreams;
namespace cif::v2 namespace cif::v2
{ {
...@@ -155,8 +161,11 @@ struct ValidateLink ...@@ -155,8 +161,11 @@ struct ValidateLink
class Validator class Validator
{ {
public: public:
Validator(std::string_view name)
: m_name(name)
{
}
Validator(std::string_view name, std::istream &is);
~Validator(); ~Validator();
Validator(const Validator &rhs) = delete; Validator(const Validator &rhs) = delete;
...@@ -165,8 +174,8 @@ class Validator ...@@ -165,8 +174,8 @@ class Validator
Validator(Validator &&rhs); Validator(Validator &&rhs);
Validator &operator=(Validator &&rhs); Validator &operator=(Validator &&rhs);
friend class DictParser; friend class dictionary_parser;
friend class ValidatorFactory; // friend class ValidatorFactory;
void addTypeValidator(ValidateType &&v); void addTypeValidator(ValidateType &&v);
const ValidateType *getValidatorForType(std::string_view typeCode) const; const ValidateType *getValidatorForType(std::string_view typeCode) const;
...@@ -180,47 +189,45 @@ class Validator ...@@ -180,47 +189,45 @@ class Validator
void reportError(const std::string &msg, bool fatal) const; void reportError(const std::string &msg, bool fatal) const;
std::string dictName() const { return mName; } const std::string &dictName() const { return m_name; }
void dictName(const std::string &name) { mName = name; } void dictName(const std::string &name) { m_name = name; }
std::string dictVersion() const { return mVersion; } const std::string &dictVersion() const { return m_version; }
void dictVersion(const std::string &version) { mVersion = version; } void dictVersion(const std::string &version) { m_version = version; }
private: private:
// name is fully qualified here: // name is fully qualified here:
ValidateItem *getValidatorForItem(std::string_view name) const; ValidateItem *getValidatorForItem(std::string_view name) const;
std::string mName; std::string m_name;
std::string mVersion; std::string m_version;
bool mStrict = false; bool mStrict = false;
// std::set<uint32_t> mSubCategories;
std::set<ValidateType> mTypeValidators; std::set<ValidateType> mTypeValidators;
std::set<ValidateCategory> mCategoryValidators; std::set<ValidateCategory> mCategoryValidators;
std::vector<ValidateLink> mLinkValidators; std::vector<ValidateLink> mLinkValidators;
}; };
// -------------------------------------------------------------------- // --------------------------------------------------------------------
class ValidatorFactory class ValidatorFactory
{ {
public: public:
static ValidatorFactory &instance() static ValidatorFactory &instance()
{ {
return sInstance; static ValidatorFactory s_instance;
return s_instance;
} }
const Validator &operator[](std::string_view dictionary); const Validator &operator[](std::string_view dictionary_name);
private: private:
void construct_validator(std::string_view name, std::istream &is);
static ValidatorFactory sInstance; // --------------------------------------------------------------------
ValidatorFactory(); ValidatorFactory() = default;
std::mutex mMutex; std::mutex mMutex;
std::list<Validator> mValidators; std::list<Validator> mValidators;
}; };
} // namespace cif } // namespace cif::v2
/*-
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2020 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 <cif++/v2/condition.hpp>
#include <cif++/v2/dictionary_parser.hpp>
#include <cif++/v2/file.hpp>
#include <cif++/v2/parser.hpp>
namespace cif::v2
{
using namespace literals;
class dictionary_parser : public parser_t<>
{
public:
using base_type = parser_t<>;
using file_type = typename base_type::file_type;
using datablock_type = typename file_type::datablock_type;
using category_type = typename datablock_type::category_type;
dictionary_parser(Validator &validator, std::istream &is, file_type &f)
: base_type(is, f)
, m_validator(validator)
{
}
void load_dictionary()
{
std::unique_ptr<datablock_type> dict;
auto savedDatablock = m_datablock;
try
{
while (m_lookahead != CIFToken::Eof)
{
switch (m_lookahead)
{
case CIFToken::GLOBAL:
parse_global();
break;
default:
{
dict.reset(new datablock_type(m_token_value)); // dummy datablock, for constructing the validator only
m_datablock = dict.get();
match(CIFToken::DATA);
parse_datablock();
break;
}
}
}
}
catch (const std::exception &ex)
{
error(ex.what());
}
// store all validators
for (auto &ic : mCategoryValidators)
m_validator.addCategoryValidator(std::move(ic));
mCategoryValidators.clear();
for (auto &iv : mItemValidators)
{
auto cv = m_validator.getValidatorForCategory(iv.first);
if (cv == nullptr)
error("Undefined category '" + iv.first);
for (auto &v : iv.second)
const_cast<ValidateCategory *>(cv)->addItemValidator(std::move(v));
}
// check all item validators for having a typeValidator
if (dict)
link_items();
// store meta information
datablock_type::iterator info;
bool n;
std::tie(info, n) = m_datablock->emplace("dictionary");
if (n)
{
auto r = info->front();
m_validator.dictName(r["title"].as<std::string>());
m_validator.dictVersion(r["version"].as<std::string>());
}
m_datablock = savedDatablock;
mItemValidators.clear();
}
private:
void parse_save_frame() override
{
if (not m_collected_item_types)
m_collected_item_types = collect_item_types();
std::string saveFrameName = m_token_value;
if (saveFrameName.empty())
error("Invalid save frame, should contain more than just 'save_' here");
bool isCategorySaveFrame = m_token_value[0] != '_';
datablock_type dict(m_token_value);
datablock_type::iterator cat = dict.end();
match(CIFToken::SAVE);
while (m_lookahead == CIFToken::LOOP or m_lookahead == CIFToken::Tag)
{
if (m_lookahead == CIFToken::LOOP)
{
cat = dict.end(); // should start a new category
match(CIFToken::LOOP);
std::vector<std::string> tags;
while (m_lookahead == CIFToken::Tag)
{
std::string catName, itemName;
std::tie(catName, itemName) = splitTagName(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(itemName);
match(CIFToken::Tag);
}
while (m_lookahead == CIFToken::Value)
{
cat->emplace({});
auto row = cat->back();
for (auto tag : tags)
{
row[tag] = m_token_value;
match(CIFToken::Value);
}
}
cat = dict.end();
}
else
{
std::string catName, itemName;
std::tie(catName, itemName) = splitTagName(m_token_value);
if (cat == dict.end() or not iequals(cat->name(), catName))
std::tie(cat, std::ignore) = dict.emplace(catName);
match(CIFToken::Tag);
if (cat->empty())
cat->emplace({});
cat->back()[itemName] = m_token_value;
match(CIFToken::Value);
}
}
match(CIFToken::SAVE);
if (isCategorySaveFrame)
{
std::string category;
cif::v2::tie(category) = dict["category"].front().get("id");
std::vector<std::string> keys;
for (auto k : dict["category_key"])
keys.push_back(std::get<1>(splitTagName(k["name"].as<std::string>())));
iset groups;
for (auto g : dict["category_group"])
groups.insert(g["id"].as<std::string>());
mCategoryValidators.push_back(ValidateCategory{category, keys, groups});
}
else
{
// if the type code is missing, this must be a pointer, just skip it
std::string typeCode;
cif::v2::tie(typeCode) = dict["item_type"].front().get("code");
const ValidateType *tv = nullptr;
if (not(typeCode.empty() or typeCode == "?"))
tv = m_validator.getValidatorForType(typeCode);
iset ess;
for (auto e : dict["item_enumeration"])
ess.insert(e["value"].as<std::string>());
std::string defaultValue;
cif::v2::tie(defaultValue) = dict["item_default"].front().get("value");
bool defaultIsNull = false;
if (defaultValue.empty())
{
// TODO: Is this correct???
for (auto r : dict["_item_default"])
{
defaultIsNull = r["value"].is_null();
break;
}
}
// collect the dict from our dataBlock and construct validators
for (auto i : dict["item"])
{
std::string tagName, category, mandatory;
cif::v2::tie(tagName, category, mandatory) = i.get("name", "category_id", "mandatory_code");
std::string catName, itemName;
std::tie(catName, itemName) = splitTagName(tagName);
if (catName.empty() or itemName.empty())
error("Invalid tag name in _item.name " + tagName);
if (not iequals(category, catName) and not(category.empty() or category == "?"))
error("specified category id does match the implicit category name for tag '" + tagName + '\'');
else
category = catName;
auto &ivs = mItemValidators[category];
auto vi = find(ivs.begin(), ivs.end(), ValidateItem{itemName});
if (vi == ivs.end())
ivs.push_back(ValidateItem{itemName, iequals(mandatory, "yes"), tv, ess, defaultValue, defaultIsNull});
else
{
// need to update the itemValidator?
if (vi->mMandatory != (iequals(mandatory, "yes")))
{
if (VERBOSE > 2)
{
std::cerr << "inconsistent mandatory value for " << tagName << " in dictionary" << std::endl;
if (iequals(tagName, saveFrameName))
std::cerr << "choosing " << mandatory << std::endl;
else
std::cerr << "choosing " << (vi->mMandatory ? "Y" : "N") << std::endl;
}
if (iequals(tagName, saveFrameName))
vi->mMandatory = (iequals(mandatory, "yes"));
}
if (vi->mType != nullptr and tv != nullptr and vi->mType != tv)
{
if (VERBOSE > 1)
std::cerr << "inconsistent type for " << tagName << " in dictionary" << std::endl;
}
// vi->mMandatory = (iequals(mandatory, "yes"));
if (vi->mType == nullptr)
vi->mType = tv;
vi->mEnums.insert(ess.begin(), ess.end());
// anything else yet?
// ...
}
}
// collect the dict from our dataBlock and construct validators
for (auto i : dict["item_linked"])
{
std::string childTagName, parentTagName;
cif::v2::tie(childTagName, parentTagName) = i.get("child_name", "parent_name");
mLinkedItems.emplace(childTagName, parentTagName);
}
}
}
void link_items()
{
if (not m_datablock)
error("no datablock");
auto &dict = *m_datablock;
// links are identified by a parent category, a child category and a group ID
using key_type = std::tuple<std::string, std::string, int>;
std::map<key_type, size_t> linkIndex;
// Each link group consists of a set of keys
std::vector<std::tuple<std::vector<std::string>, std::vector<std::string>>> linkKeys;
auto addLink = [&](size_t ix, const std::string &pk, const std::string &ck)
{
auto &&[pkeys, ckeys] = linkKeys.at(ix);
bool found = false;
for (size_t i = 0; i < pkeys.size(); ++i)
{
if (pkeys[i] == pk and ckeys[i] == ck)
{
found = true;
break;
}
}
if (not found)
{
pkeys.push_back(pk);
ckeys.push_back(ck);
}
};
auto &linkedGroupList = dict["pdbx_item_linked_group_list"];
for (auto gl : linkedGroupList)
{
std::string child, parent;
int link_group_id;
cif::v2::tie(child, parent, link_group_id) = gl.get("child_name", "parent_name", "link_group_id");
auto civ = m_validator.getValidatorForItem(child);
if (civ == nullptr)
error("in pdbx_item_linked_group_list, item '" + child + "' is not specified");
auto piv = m_validator.getValidatorForItem(parent);
if (piv == nullptr)
error("in pdbx_item_linked_group_list, item '" + parent + "' is not specified");
key_type key{piv->mCategory->mName, civ->mCategory->mName, link_group_id};
if (not linkIndex.count(key))
{
linkIndex[key] = linkKeys.size();
linkKeys.push_back({});
}
size_t ix = linkIndex.at(key);
addLink(ix, piv->mTag, civ->mTag);
}
// Only process inline linked items if the linked group list is absent
if (linkedGroupList.empty())
{
// for links recorded in categories but not in pdbx_item_linked_group_list
for (auto li : mLinkedItems)
{
std::string child, parent;
std::tie(child, parent) = li;
auto civ = m_validator.getValidatorForItem(child);
if (civ == nullptr)
error("in pdbx_item_linked_group_list, item '" + child + "' is not specified");
auto piv = m_validator.getValidatorForItem(parent);
if (piv == nullptr)
error("in pdbx_item_linked_group_list, item '" + parent + "' is not specified");
key_type key{piv->mCategory->mName, civ->mCategory->mName, 0};
if (not linkIndex.count(key))
{
linkIndex[key] = linkKeys.size();
linkKeys.push_back({});
}
size_t ix = linkIndex.at(key);
addLink(ix, piv->mTag, civ->mTag);
}
}
auto &linkedGroup = dict["pdbx_item_linked_group"];
// now store the links in the validator
for (auto &kv : linkIndex)
{
ValidateLink link = {};
std::tie(link.mParentCategory, link.mChildCategory, link.mLinkGroupID) = kv.first;
std::tie(link.mParentKeys, link.mChildKeys) = linkKeys[kv.second];
// look up the label
for (auto r : linkedGroup.find("category_id"_key == link.mChildCategory and "link_group_id"_key == link.mLinkGroupID))
{
link.mLinkGroupLabel = r["label"].as<std::string>();
break;
}
m_validator.addLinkValidator(std::move(link));
}
// now make sure the itemType is specified for all itemValidators
for (auto &cv : m_validator.mCategoryValidators)
{
for (auto &iv : cv.mItemValidators)
{
if (iv.mType == nullptr and cif::VERBOSE >= 0)
std::cerr << "Missing item_type for " << iv.mTag << std::endl;
}
}
}
bool collect_item_types()
{
bool result = false;
if (not m_datablock)
error("no datablock");
auto &dict = *m_datablock;
for (auto &t : dict["item_type_list"])
{
std::string code, primitiveCode, construct;
cif::v2::tie(code, primitiveCode, construct) = t.get("code", "primitive_code", "construct");
ba::replace_all(construct, "\\n", "\n");
ba::replace_all(construct, "\\t", "\t");
ba::replace_all(construct, "\\\n", "");
try
{
ValidateType v = {
code, mapToPrimitiveType(primitiveCode), boost::regex(construct, boost::regex::extended | boost::regex::optimize)};
m_validator.addTypeValidator(std::move(v));
}
catch (const std::exception &)
{
std::throw_with_nested(CifParserError(t.lineNr(), "error in regular expression"));
}
// Do not replace an already defined type validator, this won't work with pdbx_v40
// as it has a name that is too strict for its own names :-)
// if (mFileImpl.mTypeValidators.count(v))
// mFileImpl.mTypeValidators.erase(v);
if (VERBOSE >= 5)
std::cerr << "Added type " << code << " (" << primitiveCode << ") => " << construct << std::endl;
result = true;
}
return result;
}
Validator &m_validator;
bool m_collected_item_types = false;
std::vector<ValidateCategory> mCategoryValidators;
std::map<std::string, std::vector<ValidateItem>> mItemValidators;
std::set<std::tuple<std::string, std::string>> mLinkedItems;
};
// --------------------------------------------------------------------
Validator parse_dictionary(std::string_view name, std::istream &is);
{
Validator result(name);
dictionary_parser p(v, is, file{});
p.load_dictionary();
return result;
}
} // namespace cif::v2
\ No newline at end of file
/*-
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2020 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 <fstream>
#include <iostream>
#include <cif++/v2/validate.hpp>
#include <cif++/v2/dictionary_parser.hpp>
extern int VERBOSE;
namespace cif::v2
{
ValidationError::ValidationError(const std::string &msg)
: mMsg(msg)
{
}
ValidationError::ValidationError(const std::string &cat, const std::string &item, const std::string &msg)
: mMsg("When validating _" + cat + '.' + item + ": " + msg)
{
}
// --------------------------------------------------------------------
DDL_PrimitiveType mapToPrimitiveType(std::string_view s)
{
DDL_PrimitiveType result;
if (iequals(s, "char"))
result = DDL_PrimitiveType::Char;
else if (iequals(s, "uchar"))
result = DDL_PrimitiveType::UChar;
else if (iequals(s, "numb"))
result = DDL_PrimitiveType::Numb;
else
throw ValidationError("Not a known primitive type");
return result;
}
// --------------------------------------------------------------------
int ValidateType::compare(const char *a, const char *b) const
{
int result = 0;
if (*a == 0)
result = *b == 0 ? 0 : -1;
else if (*b == 0)
result = *a == 0 ? 0 : +1;
else
{
try
{
switch (mPrimitiveType)
{
case DDL_PrimitiveType::Numb:
{
double da = strtod(a, nullptr);
double db = strtod(b, nullptr);
auto d = da - db;
if (std::abs(d) > std::numeric_limits<double>::epsilon())
{
if (d > 0)
result = 1;
else if (d < 0)
result = -1;
}
break;
}
case DDL_PrimitiveType::UChar:
case DDL_PrimitiveType::Char:
{
// CIF is guaranteed to have ascii only, therefore this primitive code will do
// also, we're collapsing spaces
auto ai = a, bi = b;
for (;;)
{
if (*ai == 0)
{
if (*bi != 0)
result = -1;
break;
}
else if (*bi == 0)
{
result = 1;
break;
}
char ca = *ai;
char cb = *bi;
if (mPrimitiveType == DDL_PrimitiveType::UChar)
{
ca = tolower(ca);
cb = tolower(cb);
}
result = ca - cb;
if (result != 0)
break;
if (ca == ' ')
{
while (ai[1] == ' ')
++ai;
while (bi[1] == ' ')
++bi;
}
++ai;
++bi;
}
break;
}
}
}
catch (const std::invalid_argument &ex)
{
result = 1;
}
}
return result;
}
// --------------------------------------------------------------------
//void ValidateItem::addLinked(ValidateItem* parent, const std::string& parentItem, const std::string& childItem)
//{
//// if (mParent != nullptr and VERBOSE)
//// cerr << "replacing parent in " << mCategory->mName << " from " << mParent->mCategory->mName << " to " << parent->mCategory->mName << endl;
//// mParent = parent;
//
// if (mType == nullptr and parent != nullptr)
// mType = parent->mType;
//
// 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 ValidateItem::operator()(std::string value) const
{
if (not value.empty() and value != "?" and value != ".")
{
if (mType != nullptr and not regex_match(value, mType->mRx))
throw ValidationError(mCategory->mName, mTag, "Value '" + value + "' does not match type expression for type " + mType->mName);
if (not mEnums.empty())
{
if (mEnums.count(value) == 0)
throw ValidationError(mCategory->mName, mTag, "Value '" + value + "' is not in the list of allowed values");
}
}
}
// --------------------------------------------------------------------
void ValidateCategory::addItemValidator(ValidateItem &&v)
{
if (v.mMandatory)
mMandatoryFields.insert(v.mTag);
v.mCategory = this;
auto r = mItemValidators.insert(std::move(v));
if (not r.second and VERBOSE >= 4)
std::cout << "Could not add validator for item " << v.mTag << " to category " << mName << std::endl;
}
const ValidateItem *ValidateCategory::getValidatorForItem(std::string_view tag) const
{
const ValidateItem *result = nullptr;
auto i = mItemValidators.find(ValidateItem{std::string(tag)});
if (i != mItemValidators.end())
result = &*i;
else if (VERBOSE > 4)
std::cout << "No validator for tag " << tag << std::endl;
return result;
}
// --------------------------------------------------------------------
Validator::Validator(std::string_view name, std::istream &is)
: mName(name)
{
DictParser p(*this, is);
p.loadDictionary();
}
Validator::~Validator()
{
}
void Validator::addTypeValidator(ValidateType &&v)
{
auto r = mTypeValidators.insert(std::move(v));
if (not r.second and VERBOSE > 4)
std::cout << "Could not add validator for type " << v.mName << std::endl;
}
const ValidateType *Validator::getValidatorForType(std::string_view typeCode) const
{
const ValidateType *result = nullptr;
auto i = mTypeValidators.find(ValidateType{std::string(typeCode), DDL_PrimitiveType::Char, boost::regex()});
if (i != mTypeValidators.end())
result = &*i;
else if (VERBOSE > 4)
std::cout << "No validator for type " << typeCode << std::endl;
return result;
}
void Validator::addCategoryValidator(ValidateCategory &&v)
{
auto r = mCategoryValidators.insert(std::move(v));
if (not r.second and VERBOSE > 4)
std::cout << "Could not add validator for category " << v.mName << std::endl;
}
const ValidateCategory *Validator::getValidatorForCategory(std::string_view category) const
{
const ValidateCategory *result = nullptr;
auto i = mCategoryValidators.find(ValidateCategory{std::string(category)});
if (i != mCategoryValidators.end())
result = &*i;
else if (VERBOSE > 4)
std::cout << "No validator for category " << category << std::endl;
return result;
}
ValidateItem *Validator::getValidatorForItem(std::string_view tag) const
{
ValidateItem *result = nullptr;
std::string cat, item;
std::tie(cat, item) = splitTagName(tag);
auto *cv = getValidatorForCategory(cat);
if (cv != nullptr)
result = const_cast<ValidateItem *>(cv->getValidatorForItem(item));
if (result == nullptr and VERBOSE > 4)
std::cout << "No validator for item " << tag << std::endl;
return result;
}
void Validator::addLinkValidator(ValidateLink &&v)
{
assert(v.mParentKeys.size() == v.mChildKeys.size());
if (v.mParentKeys.size() != v.mChildKeys.size())
throw std::runtime_error("unequal number of keys for parent and child in link");
auto pcv = getValidatorForCategory(v.mParentCategory);
auto ccv = getValidatorForCategory(v.mChildCategory);
if (pcv == nullptr)
throw std::runtime_error("unknown parent category " + v.mParentCategory);
if (ccv == nullptr)
throw std::runtime_error("unknown child category " + v.mChildCategory);
for (size_t i = 0; i < v.mParentKeys.size(); ++i)
{
auto piv = pcv->getValidatorForItem(v.mParentKeys[i]);
if (piv == nullptr)
throw std::runtime_error("unknown parent tag _" + v.mParentCategory + '.' + v.mParentKeys[i]);
auto civ = ccv->getValidatorForItem(v.mChildKeys[i]);
if (civ == nullptr)
throw std::runtime_error("unknown child tag _" + v.mChildCategory + '.' + v.mChildKeys[i]);
if (civ->mType == nullptr and piv->mType != nullptr)
const_cast<ValidateItem *>(civ)->mType = piv->mType;
}
mLinkValidators.emplace_back(std::move(v));
}
std::vector<const ValidateLink *> Validator::getLinksForParent(std::string_view category) const
{
std::vector<const ValidateLink *> result;
for (auto &l : mLinkValidators)
{
if (l.mParentCategory == category)
result.push_back(&l);
}
return result;
}
std::vector<const ValidateLink *> Validator::getLinksForChild(std::string_view category) const
{
std::vector<const ValidateLink *> result;
for (auto &l : mLinkValidators)
{
if (l.mChildCategory == category)
result.push_back(&l);
}
return result;
}
void Validator::reportError(const std::string &msg, bool fatal) const
{
if (mStrict or fatal)
throw ValidationError(msg);
else if (VERBOSE > 0)
std::cerr << msg << std::endl;
}
// --------------------------------------------------------------------
const Validator &ValidatorFactory::operator[](std::string_view dictionary_name)
{
std::lock_guard lock(mMutex);
for (auto &validator : mValidators)
{
if (iequals(validator.m_name, dictionary_name))
return validator;
}
// not found, add it
// too bad clang version 10 did not have a constructor for std::filesystem::path that accepts a std::string_view
std::filesystem::path dictionary(dictionary_name.data(), dictionary_name.data() + dictionary_name.length());
auto data = loadResource(dictionary_name);
if (not data and dictionary.extension().string() != ".dic")
data = loadResource(dictionary.parent_path() / (dictionary.filename().string() + ".dic"));
if (data)
construct_validator(dictionary_name, *data);
else
{
std::error_code ec;
// might be a compressed dictionary on disk
std::filesystem::path p = dictionary;
if (p.extension() == ".dic")
p = p.parent_path() / (p.filename().string() + ".gz");
else
p = p.parent_path() / (p.filename().string() + ".dic.gz");
#if defined(CACHE_DIR) or defined(DATA_DIR)
if (not std::filesystem::exists(p, ec) or ec)
{
for (const char *dir : {
#if defined(CACHE_DIR)
CACHE_DIR,
#endif
#if defined(DATA_DIR)
DATA_DIR
#endif
})
{
auto p2 = std::filesystem::path(dir) / p;
if (std::filesystem::exists(p2, ec) and not ec)
{
swap(p, p2);
break;
}
}
}
#endif
if (std::filesystem::exists(p, ec) and not ec)
{
std::ifstream file(p, std::ios::binary);
if (not file.is_open())
throw std::runtime_error("Could not open dictionary (" + p.string() + ")");
io::filtering_stream<io::input> in;
in.push(io::gzip_decompressor());
in.push(file);
construct_validator(dictionary_name, in);
}
else
throw std::runtime_error("Dictionary not found or defined (" + dictionary.string() + ")");
}
assert(iequals(mValidators.back().m_name, dictionary_name));
return mValidators.back();
}
void ValiatorFactory::construct_validator(std::string_view name, std::istream &is)
{
mValidators.emplace_back(parse_dictionary(name, is));
}
} // namespace cif
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment