Commit e450fee0 by Maarten L. Hekkelman

Fixed processing links

parent d7c162c7
...@@ -13,3 +13,5 @@ libcif++.la ...@@ -13,3 +13,5 @@ libcif++.la
config.status config.status
config.log config.log
libtool libtool
rsrc/lib-version.txt
version-info*.txt
...@@ -347,9 +347,16 @@ namespace detail ...@@ -347,9 +347,16 @@ namespace detail
static int compare(const ItemReference& ref, double value, bool icase) static int compare(const ItemReference& ref, double value, bool icase)
{ {
int result = 0; int result = 0;
const char* s = ref.c_str();
if (s == nullptr or *s == 0)
result = 1;
else
{
try try
{ {
double v = std::stod(ref.c_str()); auto v = std::strtod(s, nullptr);
if (v < value) if (v < value)
result = -1; result = -1;
else if (v > value) else if (v > value)
...@@ -361,6 +368,7 @@ namespace detail ...@@ -361,6 +368,7 @@ namespace detail
std::cerr << "conversion error in compare for '" << ref.c_str() << '\'' << std::endl; std::cerr << "conversion error in compare for '" << ref.c_str() << '\'' << std::endl;
result = 1; result = 1;
} }
}
return result; return result;
} }
...@@ -380,9 +388,16 @@ namespace detail ...@@ -380,9 +388,16 @@ namespace detail
static int compare(const ItemReference& ref, unsigned long value, bool icase) static int compare(const ItemReference& ref, unsigned long value, bool icase)
{ {
int result = 0; int result = 0;
const char* s = ref.c_str();
if (s == nullptr or *s == 0)
result = 1;
else
{
try try
{ {
auto v = std::stoul(ref.c_str()); auto v = std::strtoul(s, nullptr, 10);
if (v < value) if (v < value)
result = -1; result = -1;
else if (v > value) else if (v > value)
...@@ -394,6 +409,7 @@ namespace detail ...@@ -394,6 +409,7 @@ namespace detail
std::cerr << "conversion error in compare for '" << ref.c_str() << '\'' << std::endl; std::cerr << "conversion error in compare for '" << ref.c_str() << '\'' << std::endl;
result = 1; result = 1;
} }
}
return result; return result;
} }
...@@ -413,9 +429,16 @@ namespace detail ...@@ -413,9 +429,16 @@ namespace detail
static int compare(const ItemReference& ref, long value, bool icase) static int compare(const ItemReference& ref, long value, bool icase)
{ {
int result = 0; int result = 0;
const char* s = ref.c_str();
if (s == nullptr or *s == 0)
result = 1;
else
{
try try
{ {
auto v = std::stol(ref.c_str()); auto v = std::strtol(s, nullptr, 10);
if (v < value) if (v < value)
result = -1; result = -1;
else if (v > value) else if (v > value)
...@@ -427,6 +450,7 @@ namespace detail ...@@ -427,6 +450,7 @@ namespace detail
std::cerr << "conversion error in compare for '" << ref.c_str() << '\'' << std::endl; std::cerr << "conversion error in compare for '" << ref.c_str() << '\'' << std::endl;
result = 1; result = 1;
} }
}
return result; return result;
} }
......
...@@ -2524,7 +2524,7 @@ void Row::assign(size_t column, const std::string& value, bool skipUpdateLinked) ...@@ -2524,7 +2524,7 @@ void Row::assign(size_t column, const std::string& value, bool skipUpdateLinked)
if (pk == iv->mTag) if (pk == iv->mTag)
{ {
childTag = ck; childTag = ck;
cond = std::move(cond) && ((Key(ck) == oldStrValue) or Key(ck) == Empty()); cond = std::move(cond) && Key(ck) == oldStrValue;
} }
else else
{ {
...@@ -2534,7 +2534,6 @@ void Row::assign(size_t column, const std::string& value, bool skipUpdateLinked) ...@@ -2534,7 +2534,6 @@ void Row::assign(size_t column, const std::string& value, bool skipUpdateLinked)
else else
cond = std::move(cond) && ((Key(ck) == value) or Key(ck) == Empty()); cond = std::move(cond) && ((Key(ck) == value) or Key(ck) == Empty());
} }
} }
if (cif::VERBOSE >= 2) if (cif::VERBOSE >= 2)
......
...@@ -891,8 +891,13 @@ void DictParser::linkItems() ...@@ -891,8 +891,13 @@ void DictParser::linkItems()
auto& dict = *mDataBlock; auto& dict = *mDataBlock;
std::map<std::tuple<std::string,std::string>,size_t> linkIndex; // links are identified by a parent category, a child category and a group ID
std::map<std::tuple<std::string,std::string>,int> linkGroupIds;
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; 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 addLink = [&](size_t ix, const std::string& pk, const std::string& ck)
...@@ -918,13 +923,11 @@ void DictParser::linkItems() ...@@ -918,13 +923,11 @@ void DictParser::linkItems()
auto& linkedGroupList = dict["pdbx_item_linked_group_list"]; auto& linkedGroupList = dict["pdbx_item_linked_group_list"];
if (linkedGroupList.empty()) for (auto gl: linkedGroupList)
{
// for links recorded in categories but not in pdbx_item_linked_group_list
for (auto li: mImpl->mLinkedItems)
{ {
std::string child, parent; std::string child, parent;
std::tie(child, parent) = li; int link_group_id;
cif::tie(child, parent, link_group_id) = gl.get("child_name", "parent_name", "link_group_id");
auto civ = mValidator.getValidatorForItem(child); auto civ = mValidator.getValidatorForItem(child);
if (civ == nullptr) if (civ == nullptr)
...@@ -934,7 +937,7 @@ void DictParser::linkItems() ...@@ -934,7 +937,7 @@ void DictParser::linkItems()
if (piv == nullptr) if (piv == nullptr)
error("in pdbx_item_linked_group_list, item '" + parent + "' is not specified"); error("in pdbx_item_linked_group_list, item '" + parent + "' is not specified");
auto key = std::make_tuple(piv->mCategory->mName, civ->mCategory->mName); key_type key{ piv->mCategory->mName, civ->mCategory->mName, link_group_id };
if (not linkIndex.count(key)) if (not linkIndex.count(key))
{ {
linkIndex[key] = linkKeys.size(); linkIndex[key] = linkKeys.size();
...@@ -944,14 +947,15 @@ void DictParser::linkItems() ...@@ -944,14 +947,15 @@ void DictParser::linkItems()
size_t ix = linkIndex.at(key); size_t ix = linkIndex.at(key);
addLink(ix, piv->mTag, civ->mTag); addLink(ix, piv->mTag, civ->mTag);
} }
}
else // Only process inline linked items if the linked group list is absent
if (linkedGroupList.empty())
{ {
for (auto gl: linkedGroupList) // for links recorded in categories but not in pdbx_item_linked_group_list
for (auto li: mImpl->mLinkedItems)
{ {
std::string child, parent; std::string child, parent;
int link_group_id; std::tie(child, parent) = li;
cif::tie(child, parent, link_group_id) = gl.get("child_name", "parent_name", "link_group_id");
auto civ = mValidator.getValidatorForItem(child); auto civ = mValidator.getValidatorForItem(child);
if (civ == nullptr) if (civ == nullptr)
...@@ -961,13 +965,11 @@ void DictParser::linkItems() ...@@ -961,13 +965,11 @@ void DictParser::linkItems()
if (piv == nullptr) if (piv == nullptr)
error("in pdbx_item_linked_group_list, item '" + parent + "' is not specified"); error("in pdbx_item_linked_group_list, item '" + parent + "' is not specified");
auto key = std::make_tuple(piv->mCategory->mName, civ->mCategory->mName); key_type key{ piv->mCategory->mName, civ->mCategory->mName, 0 };
if (not linkIndex.count(key)) if (not linkIndex.count(key))
{ {
linkIndex[key] = linkKeys.size(); linkIndex[key] = linkKeys.size();
linkKeys.push_back({}); linkKeys.push_back({});
linkGroupIds[key] = link_group_id;
} }
size_t ix = linkIndex.at(key); size_t ix = linkIndex.at(key);
...@@ -981,10 +983,7 @@ void DictParser::linkItems() ...@@ -981,10 +983,7 @@ void DictParser::linkItems()
for (auto& kv: linkIndex) for (auto& kv: linkIndex)
{ {
ValidateLink link = {}; ValidateLink link = {};
std::tie(link.mParentCategory, link.mChildCategory) = kv.first; std::tie(link.mParentCategory, link.mChildCategory, link.mLinkGroupID) = kv.first;
if (linkGroupIds.count(kv.first))
link.mLinkGroupID = linkGroupIds[kv.first];
std::tie(link.mParentKeys, link.mChildKeys) = linkKeys[kv.second]; std::tie(link.mParentKeys, link.mChildKeys) = linkKeys[kv.second];
......
...@@ -611,7 +611,6 @@ _cat_2.desc ...@@ -611,7 +611,6 @@ _cat_2.desc
} }
// -------------------------------------------------------------------- // --------------------------------------------------------------------
BOOST_AUTO_TEST_CASE(d4) BOOST_AUTO_TEST_CASE(d4)
...@@ -838,3 +837,226 @@ _cat_2.parent_id3 ...@@ -838,3 +837,226 @@ _cat_2.parent_id3
f.write(std::cout, {}); f.write(std::cout, {});
} }
// --------------------------------------------------------------------
BOOST_AUTO_TEST_CASE(d5)
{
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
code char
'[][_,.;:"&<>()/\{}'`~!@#$%A-Za-z0-9*|+-]*'
text char
'[][ \n\t()_,.;:"&<>/\{}'`~!@#$%?+=*A-Za-z0-9|^-]*'
int numb
'[+-]?[0-9]+'
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_type.code int
save_
save_cat_2
_category.description 'A second simple test category'
_category.id cat_2
_category.mandatory_code no
_category_key.name '_cat_2.id'
save_
save__cat_2.id
_item.name '_cat_2.id'
_item.category_id cat_2
_item.mandatory_code yes
_item_type.code int
save_
save__cat_2.parent_id
_item.name '_cat_2.parent_id'
_item.category_id cat_2
_item.mandatory_code yes
_item_type.code int
save_
save__cat_2.parent_id2
_item.name '_cat_2.parent_id2'
_item.category_id cat_2
_item.mandatory_code no
_item_type.code code
save_
save__cat_2.parent_id3
_item.name '_cat_2.parent_id3'
_item.category_id cat_2
_item.mandatory_code no
_item_type.code code
save_
loop_
_pdbx_item_linked_group_list.child_category_id
_pdbx_item_linked_group_list.link_group_id
_pdbx_item_linked_group_list.child_name
_pdbx_item_linked_group_list.parent_name
_pdbx_item_linked_group_list.parent_category_id
cat_2 1 '_cat_2.parent_id' '_cat_1.id' cat_1
cat_2 2 '_cat_2.parent_id2' '_cat_1.id' cat_1
cat_2 3 '_cat_2.parent_id3' '_cat_1.id' cat_1
loop_
_pdbx_item_linked_group.category_id
_pdbx_item_linked_group.link_group_id
_pdbx_item_linked_group.label
cat_2 1 cat_2:cat_1:1
cat_2 2 cat_2:cat_1:2
cat_2 3 cat_2:cat_1:3
)";
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);
cif::File f;
f.loadDictionary(is_dict);
// --------------------------------------------------------------------
const char data[] = R"(
data_test
loop_
_cat_1.id
1
2
3
loop_
_cat_2.id
_cat_2.parent_id
_cat_2.parent_id2
_cat_2.parent_id3
1 1 ? ?
2 ? 1 ?
3 ? ? 1
4 2 2 ?
5 2 ? 2
6 ? 2 2
7 3 3 3
)";
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);
auto& cat1 = f.firstDatablock()["cat_1"];
auto& cat2 = f.firstDatablock()["cat_2"];
// check a rename in parent and child
for (auto r: cat1.find(cif::Key("id") == 1))
{
r["id"] = 10;
break;
}
f.write(std::cout, {});
BOOST_CHECK(cat1.size() == 3);
BOOST_CHECK(cat2.size() == 7);
BOOST_CHECK(cat1.find(cif::Key("id") == 1).size() == 0);
BOOST_CHECK(cat1.find(cif::Key("id") == 10).size() == 1);
BOOST_CHECK(cat2.find(cif::Key("parent_id") == 1).size() == 0);
BOOST_CHECK(cat2.find(cif::Key("parent_id2") == 1).size() == 0);
BOOST_CHECK(cat2.find(cif::Key("parent_id3") == 1).size() == 0);
BOOST_CHECK(cat2.find(cif::Key("parent_id") == 10).size() == 1);
BOOST_CHECK(cat2.find(cif::Key("parent_id2") == 10).size() == 1);
BOOST_CHECK(cat2.find(cif::Key("parent_id3") == 10).size() == 1);
for (auto r: cat1.find(cif::Key("id") == 2))
{
r["id"] = 20;
break;
}
BOOST_CHECK(cat1.size() == 3);
BOOST_CHECK(cat2.size() == 7);
BOOST_CHECK(cat1.find(cif::Key("id") == 2).size() == 0);
BOOST_CHECK(cat1.find(cif::Key("id") == 20).size() == 1);
BOOST_CHECK(cat2.find(cif::Key("parent_id") == 2).size() == 0);
BOOST_CHECK(cat2.find(cif::Key("parent_id2") == 2).size() == 0);
BOOST_CHECK(cat2.find(cif::Key("parent_id3") == 2).size() == 0);
BOOST_CHECK(cat2.find(cif::Key("parent_id") == 20).size() == 2);
BOOST_CHECK(cat2.find(cif::Key("parent_id2") == 20).size() == 2);
BOOST_CHECK(cat2.find(cif::Key("parent_id3") == 20).size() == 2);
for (auto r: cat1.find(cif::Key("id") == 3))
{
r["id"] = 30;
break;
}
BOOST_CHECK(cat1.size() == 3);
BOOST_CHECK(cat2.size() == 7);
BOOST_CHECK(cat1.find(cif::Key("id") == 3).size() == 0);
BOOST_CHECK(cat1.find(cif::Key("id") == 30).size() == 1);
BOOST_CHECK(cat2.find(cif::Key("parent_id") == 3).size() == 0);
BOOST_CHECK(cat2.find(cif::Key("parent_id2") == 3).size() == 0);
BOOST_CHECK(cat2.find(cif::Key("parent_id3") == 3).size() == 0);
BOOST_CHECK(cat2.find(cif::Key("parent_id") == 30).size() == 1);
BOOST_CHECK(cat2.find(cif::Key("parent_id2") == 30).size() == 1);
BOOST_CHECK(cat2.find(cif::Key("parent_id3") == 30).size() == 1);
// test delete
cat1.erase(cif::Key("id") == 10);
BOOST_CHECK(cat1.size() == 2);
BOOST_CHECK(cat2.size() == 4);
cat1.erase(cif::Key("id") == 20);
BOOST_CHECK(cat1.size() == 1);
BOOST_CHECK(cat2.size() == 1);
cat1.erase(cif::Key("id") == 30);
BOOST_CHECK(cat1.size() == 0);
BOOST_CHECK(cat2.size() == 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