Commit 71c8541b by Maarten L. Hekkelman

validate links

fix get_parents_condition
parent 3d66c771
...@@ -88,6 +88,7 @@ class category ...@@ -88,6 +88,7 @@ class category
const category_validator *get_cat_validator() const { return m_cat_validator; } const category_validator *get_cat_validator() const { return m_cat_validator; }
bool is_valid() const; bool is_valid() const;
void validate_links() const;
bool operator==(const category &rhs) const; bool operator==(const category &rhs) const;
bool operator!=(const category &rhs) const bool operator!=(const category &rhs) const
......
...@@ -61,6 +61,7 @@ class datablock : public std::list<category> ...@@ -61,6 +61,7 @@ class datablock : public std::list<category>
const validator *get_validator() const; const validator *get_validator() const;
bool is_valid() const; bool is_valid() const;
void validate_links() const;
// -------------------------------------------------------------------- // --------------------------------------------------------------------
......
...@@ -79,6 +79,7 @@ class file : public std::list<datablock> ...@@ -79,6 +79,7 @@ class file : public std::list<datablock>
bool is_valid() const; bool is_valid() const;
bool is_valid(); bool is_valid();
void validate_links() const;
void load_dictionary(); void load_dictionary();
void load_dictionary(std::string_view name); void load_dictionary(std::string_view name);
......
...@@ -812,6 +812,36 @@ bool category::is_valid() const ...@@ -812,6 +812,36 @@ bool category::is_valid() const
return result; return result;
} }
void category::validate_links() const
{
if (not m_validator)
return;
for (auto &link : m_parent_links)
{
auto parent = link.linked;
if (parent == nullptr)
continue;
size_t missing = 0;
for (auto r : *this)
{
auto cond = get_parents_condition(r, *parent);
if (not cond)
continue;
if (not parent->exists(std::move(cond)))
++missing;
}
if (missing)
{
std::cerr << "Links for " << link.v->m_link_group_label << " are incomplete" << std::endl
<< " There are " << missing << " items in " << m_name << " that don't have matching parent items in " << parent->m_name << std::endl;
}
}
}
// -------------------------------------------------------------------- // --------------------------------------------------------------------
condition category::get_parents_condition(row_handle rh, const category &parentCat) const condition category::get_parents_condition(row_handle rh, const category &parentCat) const
...@@ -833,9 +863,9 @@ condition category::get_parents_condition(row_handle rh, const category &parentC ...@@ -833,9 +863,9 @@ condition category::get_parents_condition(row_handle rh, const category &parentC
auto childValue = rh[link->m_child_keys[ix]]; auto childValue = rh[link->m_child_keys[ix]];
if (childValue.empty()) if (childValue.empty())
cond = std::move(cond) and key(link->m_parent_keys[ix]) == null; continue;
else
cond = std::move(cond) and key(link->m_parent_keys[ix]) == childValue.text(); cond = std::move(cond) and key(link->m_parent_keys[ix]) == childValue.text();
} }
if (result) if (result)
......
...@@ -54,6 +54,12 @@ bool datablock::is_valid() const ...@@ -54,6 +54,12 @@ bool datablock::is_valid() const
return result; return result;
} }
void datablock::validate_links() const
{
for (auto &cat : *this)
cat.validate_links();
}
// -------------------------------------------------------------------- // --------------------------------------------------------------------
category &datablock::operator[](std::string_view name) category &datablock::operator[](std::string_view name)
...@@ -167,18 +173,19 @@ void datablock::write(std::ostream &os) const ...@@ -167,18 +173,19 @@ void datablock::write(std::ostream &os) const
cat.write(os); cat.write(os);
if (m_validator != nullptr)
{
category auditConform("audit_conform");
auditConform.emplace({
{"dict_name", m_validator->name()},
{"dict_version", m_validator->version()}});
auditConform.write(os);
}
break; break;
} }
// If the dictionary declares an audit_conform category, put it in
if (m_validator != nullptr and m_validator->get_validator_for_category("audit_conform") != nullptr)
{
category auditConform("audit_conform");
auditConform.emplace({
{"dict_name", m_validator->name()},
{"dict_version", m_validator->version()}});
auditConform.write(os);
}
for (auto &cat : *this) for (auto &cat : *this)
{ {
if (cat.name() != "entry" and cat.name() != "audit_conform") if (cat.name() != "entry" and cat.name() != "audit_conform")
......
...@@ -103,9 +103,9 @@ class dictionary_parser : public parser ...@@ -103,9 +103,9 @@ class dictionary_parser : public parser
// store meta information // store meta information
datablock::iterator info; datablock::iterator info;
bool n; bool is_new;
std::tie(info, n) = m_datablock->emplace("dictionary"); std::tie(info, is_new) = m_datablock->emplace("dictionary");
if (n) if (not is_new and not info->empty())
{ {
auto r = info->front(); auto r = info->front();
m_validator.set_name(r["title"].as<std::string>()); m_validator.set_name(r["title"].as<std::string>());
......
...@@ -69,6 +69,15 @@ bool file::is_valid() ...@@ -69,6 +69,15 @@ bool file::is_valid()
return result; return result;
} }
void file::validate_links() const
{
if (m_validator == nullptr)
std::runtime_error("No validator loaded explicitly, cannot continue");
for (auto &db : *this)
db.validate_links();
}
void file::load_dictionary() void file::load_dictionary()
{ {
if (not empty()) if (not empty())
...@@ -80,7 +89,17 @@ void file::load_dictionary() ...@@ -80,7 +89,17 @@ void file::load_dictionary()
cif::tie(name) = audit_conform->front().get("dict_name"); cif::tie(name) = audit_conform->front().get("dict_name");
if (not name.empty()) if (not name.empty())
load_dictionary(name); {
try
{
load_dictionary(name);
}
catch (const std::exception &ex)
{
if (VERBOSE)
std::cerr << "Failed to load dictionary " << std::quoted(name) << ": " << ex.what() << std::endl;
}
}
} }
} }
......
...@@ -29,11 +29,7 @@ ...@@ -29,11 +29,7 @@
#include <stdexcept> #include <stdexcept>
// #include <cif++/DistanceMap.hpp>
// #include <cif++/BondMap.hpp>
#include <cif++.hpp> #include <cif++.hpp>
// #include <cif++Validator.hpp>
// #include <cif++Parser.hpp>
#include <cif++/parser.hpp> #include <cif++/parser.hpp>
#include <cif++/dictionary_parser.hpp> #include <cif++/dictionary_parser.hpp>
...@@ -62,17 +58,17 @@ cif::file operator""_cf(const char *text, size_t length) ...@@ -62,17 +58,17 @@ cif::file operator""_cf(const char *text, size_t length)
bool init_unit_test() bool init_unit_test()
{ {
// cif::VERBOSE = 1; cif::VERBOSE = 1;
// // not a test, just initialize test dir // not a test, just initialize test dir
// if (boost::unit_test::framework::master_test_suite().argc == 2) if (boost::unit_test::framework::master_test_suite().argc == 2)
// gTestDir = boost::unit_test::framework::master_test_suite().argv[1]; gTestDir = boost::unit_test::framework::master_test_suite().argv[1];
// // do this now, avoids the need for installing // do this now, avoids the need for installing
// cif::add_file_resource("mmcif_pdbx.dic", gTestDir / ".." / "rsrc" / "mmcif_pdbx.dic"); cif::add_file_resource("mmcif_pdbx.dic", gTestDir / ".." / "rsrc" / "mmcif_pdbx.dic");
// // initialize CCD location // initialize CCD location
// cif::add_file_resource("components.cif", gTestDir / ".." / "data" / "ccd-subset.cif"); cif::add_file_resource("components.cif", gTestDir / ".." / "data" / "ccd-subset.cif");
return true; return true;
} }
...@@ -2379,4 +2375,203 @@ _cat_1.name ...@@ -2379,4 +2375,203 @@ _cat_1.name
BOOST_CHECK_EQUAL(name, ts[n - 1]); BOOST_CHECK_EQUAL(name, ts[n - 1]);
++n; ++n;
} }
}
// --------------------------------------------------------------------
BOOST_AUTO_TEST_CASE(audit_conform_test)
{
const char dict[] = R"(
data_test_dict.dic
_datablock.id test_dict.dic
_datablock.description
;
A test dictionary
;
_dictionary.title test_dict.dic
_dictionary.datablock_id test_dict.dic
_dictionary.version 1.0
loop_
_item_type_list.code
_item_type_list.primitive_code
_item_type_list.construct
_item_type_list.detail
code char
'[][_,.;:"&<>()/\{}'`~!@#$%A-Za-z0-9*|+-]*'
; code item types/single words ...
;
text char
'[][ \n\t()_,.;:"&<>/\{}'`~!@#$%?+=*A-Za-z0-9|^-]*'
; text item types / multi-line text ...
;
int numb
'[+-]?[0-9]+'
; int item types are the subset of numbers that are the negative
or positive integers.
;
###################
## AUDIT_CONFORM ##
###################
save_audit_conform
_category.description
; Data items in the AUDIT_CONFORM category describe the
dictionary versions against which the data names appearing in
the current data block are conformant.
;
_category.id audit_conform
_category.mandatory_code no
loop_
_category_key.name '_audit_conform.dict_name'
'_audit_conform.dict_version'
loop_
_category_group.id 'inclusive_group'
'audit_group'
loop_
_category_examples.detail
_category_examples.case
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;
Example 1 - any file conforming to the current CIF core dictionary.
;
;
_audit_conform.dict_name cif_core.dic
_audit_conform.dict_version 2.3.1
_audit_conform.dict_location
ftp://ftp.iucr.org/pub/cif_core.2.3.1.dic
;
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
save_
save__audit_conform.dict_location
_item_description.description
; A file name or uniform resource locator (URL) for the
dictionary to which the current data block conforms.
;
_item.name '_audit_conform.dict_location'
_item.category_id audit_conform
_item.mandatory_code no
_item_aliases.alias_name '_audit_conform_dict_location'
_item_aliases.dictionary cif_core.dic
_item_aliases.version 2.0.1
_item_type.code text
save_
save__audit_conform.dict_name
_item_description.description
; The string identifying the highest-level dictionary defining
data names used in this file.
;
_item.name '_audit_conform.dict_name'
_item.category_id audit_conform
_item.mandatory_code yes
_item_aliases.alias_name '_audit_conform_dict_name'
_item_aliases.dictionary cif_core.dic
_item_aliases.version 2.0.1
_item_type.code text
save_
save__audit_conform.dict_version
_item_description.description
; The version number of the dictionary to which the current
data block conforms.
;
_item.name '_audit_conform.dict_version'
_item.category_id audit_conform
_item.mandatory_code yes
_item_aliases.alias_name '_audit_conform_dict_version'
_item_aliases.dictionary cif_core.dic
_item_aliases.version 2.0.1
_item_type.code text
save_
save_cat_1
_category.description 'A simple test category'
_category.id cat_1
_category.mandatory_code no
_category_key.name '_cat_1.id'
save_
save__cat_1.id
_item.name '_cat_1.id'
_item.category_id cat_1
_item.mandatory_code yes
_item_aliases.dictionary cif_core.dic
_item_aliases.version 2.0.1
_item_type.code code
save_
save__cat_1.name
_item.name '_cat_1.name'
_item.category_id cat_1
_item.mandatory_code yes
_item_aliases.dictionary cif_core.dic
_item_aliases.version 2.0.1
_item_type.code text
save_
)";
struct membuf : public std::streambuf
{
membuf(char *text, size_t length)
{
this->setg(text, text, text + length);
}
} buffer(const_cast<char *>(dict), sizeof(dict) - 1);
std::istream is_dict(&buffer);
auto validator = cif::parse_dictionary("test_dict.dic", is_dict);
cif::file f;
f.set_validator(&validator);
// --------------------------------------------------------------------
const char data[] = R"(
data_test
#
_audit_conform.dict_name test_dict.dic
_audit_conform.dict_version 1.0
#
loop_
_cat_1.id
_cat_1.name
2 Noot
1 Aap
3 Mies
)";
struct data_membuf : public std::streambuf
{
data_membuf(char *text, size_t length)
{
this->setg(text, text, text + length);
}
} data_buffer(const_cast<char *>(data), sizeof(data) - 1);
std::istream is_data(&data_buffer);
f.load(is_data);
BOOST_ASSERT(f.is_valid());
std::stringstream ss;
ss << f;
std::cout << f << std::endl;
cif::file f2(ss);
f2.set_validator(&validator);
BOOST_ASSERT(f2.is_valid());
auto &audit_conform = f2.front()["audit_conform"];
BOOST_CHECK_EQUAL(audit_conform.front()["dict_name"].as<std::string>(), "test_dict.dic");
BOOST_CHECK_EQUAL(audit_conform.front()["dict_version"].as<float>(), 1.0);
} }
\ No newline at end of file
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