Commit c3963bc4 by Maarten L. Hekkelman

Fixed linked update (regression)

parent 23bd51ac
...@@ -718,11 +718,13 @@ struct ConditionImpl ...@@ -718,11 +718,13 @@ struct ConditionImpl
virtual void prepare(const Category& c) {} virtual void prepare(const Category& c) {}
virtual bool test(const Category& c, const Row& r) const = 0; virtual bool test(const Category& c, const Row& r) const = 0;
virtual void str(std::ostream& os) const = 0;
}; };
struct AllConditionImpl : public ConditionImpl struct AllConditionImpl : public ConditionImpl
{ {
virtual bool test(const Category& c, const Row& r) const { return true; } virtual bool test(const Category& c, const Row& r) const { return true; }
virtual void str(std::ostream& os) const { os << "*"; }
}; };
struct orConditionImpl; struct orConditionImpl;
...@@ -789,11 +791,20 @@ class Condition ...@@ -789,11 +791,20 @@ class Condition
std::swap(mPrepared, rhs.mPrepared); std::swap(mPrepared, rhs.mPrepared);
} }
friend std::ostream& operator<<(std::ostream& os, const Condition& cond);
private: private:
detail::ConditionImpl* mImpl; detail::ConditionImpl* mImpl;
bool mPrepared = false; bool mPrepared = false;
}; };
inline std::ostream& operator<<(std::ostream& os, const Condition& cond)
{
if (cond.mImpl)
cond.mImpl->str(os);
return os;
}
namespace detail namespace detail
{ {
...@@ -808,7 +819,12 @@ struct KeyIsEmptyConditionImpl : public ConditionImpl ...@@ -808,7 +819,12 @@ struct KeyIsEmptyConditionImpl : public ConditionImpl
{ {
return r[mItemIx].empty(); return r[mItemIx].empty();
} }
virtual void str(std::ostream& os) const
{
os << mItemTag << " IS NULL";
}
std::string mItemTag; std::string mItemTag;
size_t mItemIx; size_t mItemIx;
}; };
...@@ -816,8 +832,8 @@ struct KeyIsEmptyConditionImpl : public ConditionImpl ...@@ -816,8 +832,8 @@ struct KeyIsEmptyConditionImpl : public ConditionImpl
struct KeyCompareConditionImpl : public ConditionImpl struct KeyCompareConditionImpl : public ConditionImpl
{ {
template<typename COMP> template<typename COMP>
KeyCompareConditionImpl(const std::string& ItemTag, COMP&& comp) KeyCompareConditionImpl(const std::string& ItemTag, COMP&& comp, const std::string& s)
: mItemTag(ItemTag), mComp(std::move(comp)) {} : mItemTag(ItemTag), mComp(std::move(comp)), mStr(s) {}
virtual void prepare(const Category& c); virtual void prepare(const Category& c);
...@@ -826,10 +842,16 @@ struct KeyCompareConditionImpl : public ConditionImpl ...@@ -826,10 +842,16 @@ struct KeyCompareConditionImpl : public ConditionImpl
return mComp(c, r, mCaseInsensitive); return mComp(c, r, mCaseInsensitive);
} }
virtual void str(std::ostream& os) const
{
os << mItemTag << (mCaseInsensitive ? "^ " : " ") << mStr;
}
std::string mItemTag; std::string mItemTag;
size_t mItemIx; size_t mItemIx;
bool mCaseInsensitive = false; bool mCaseInsensitive = false;
std::function<bool(const Category&, const Row&, bool)> mComp; std::function<bool(const Category&, const Row&, bool)> mComp;
std::string mStr;
}; };
struct KeyMatchesConditionImpl : public ConditionImpl struct KeyMatchesConditionImpl : public ConditionImpl
...@@ -843,6 +865,11 @@ struct KeyMatchesConditionImpl : public ConditionImpl ...@@ -843,6 +865,11 @@ struct KeyMatchesConditionImpl : public ConditionImpl
{ {
return std::regex_match(r[mItemIx].as<std::string>(), mRx); return std::regex_match(r[mItemIx].as<std::string>(), mRx);
} }
virtual void str(std::ostream& os) const
{
os << mItemTag << " =~ expression";
}
std::string mItemTag; std::string mItemTag;
size_t mItemIx; size_t mItemIx;
...@@ -858,6 +885,10 @@ struct anyIsConditionImpl : public ConditionImpl ...@@ -858,6 +885,10 @@ struct anyIsConditionImpl : public ConditionImpl
: mValue(value) {} : mValue(value) {}
virtual bool test(const Category& c, const Row& r) const; virtual bool test(const Category& c, const Row& r) const;
virtual void str(std::ostream& os) const
{
os << "<any> == " << mValue;
}
valueType mValue; valueType mValue;
}; };
...@@ -868,6 +899,10 @@ struct anyMatchesConditionImpl : public ConditionImpl ...@@ -868,6 +899,10 @@ struct anyMatchesConditionImpl : public ConditionImpl
: mRx(rx) {} : mRx(rx) {}
virtual bool test(const Category& c, const Row& r) const; virtual bool test(const Category& c, const Row& r) const;
virtual void str(std::ostream& os) const
{
os << "<any> =~ expression";
}
std::regex mRx; std::regex mRx;
}; };
...@@ -880,6 +915,11 @@ struct allConditionImpl : public ConditionImpl ...@@ -880,6 +915,11 @@ struct allConditionImpl : public ConditionImpl
{ {
return true; return true;
} }
virtual void str(std::ostream& os) const
{
os << "*";
}
}; };
struct andConditionImpl : public ConditionImpl struct andConditionImpl : public ConditionImpl
...@@ -908,6 +948,15 @@ struct andConditionImpl : public ConditionImpl ...@@ -908,6 +948,15 @@ struct andConditionImpl : public ConditionImpl
return mA->test(c, r) and mB->test(c, r); return mA->test(c, r) and mB->test(c, r);
} }
virtual void str(std::ostream& os) const
{
os << '(';
mA->str(os);
os << ") AND (";
mB->str(os);
os << ')';
}
ConditionImpl* mA; ConditionImpl* mA;
ConditionImpl* mB; ConditionImpl* mB;
}; };
...@@ -937,7 +986,16 @@ struct orConditionImpl : public ConditionImpl ...@@ -937,7 +986,16 @@ struct orConditionImpl : public ConditionImpl
{ {
return mA->test(c, r) or mB->test(c, r); return mA->test(c, r) or mB->test(c, r);
} }
virtual void str(std::ostream& os) const
{
os << '(';
mA->str(os);
os << ") OR (";
mB->str(os);
os << ')';
}
ConditionImpl* mA; ConditionImpl* mA;
ConditionImpl* mB; ConditionImpl* mB;
}; };
...@@ -964,7 +1022,14 @@ struct notConditionImpl : public ConditionImpl ...@@ -964,7 +1022,14 @@ struct notConditionImpl : public ConditionImpl
{ {
return not mA->test(c, r); return not mA->test(c, r);
} }
virtual void str(std::ostream& os) const
{
os << "NOT (";
mA->str(os);
os << ')';
}
ConditionImpl* mA; ConditionImpl* mA;
}; };
...@@ -1004,8 +1069,11 @@ struct Key ...@@ -1004,8 +1069,11 @@ struct Key
template<typename T> template<typename T>
Condition operator==(const Key& key, const T& v) Condition operator==(const Key& key, const T& v)
{ {
std::ostringstream s;
s << "== " << v;
return Condition(new detail::KeyCompareConditionImpl(key.mItemTag, [tag = key.mItemTag, v](const Category& c, const Row& r, bool icase) return Condition(new detail::KeyCompareConditionImpl(key.mItemTag, [tag = key.mItemTag, v](const Category& c, const Row& r, bool icase)
{ return r[tag].template compare<T>(v, icase) == 0; })); { return r[tag].template compare<T>(v, icase) == 0; }, s.str()));
} }
// inline Condition operator==(const Key& key, const detail::ItemReference& v) // inline Condition operator==(const Key& key, const detail::ItemReference& v)
...@@ -1037,29 +1105,41 @@ inline Condition operator!=(const Key& key, const char* v) ...@@ -1037,29 +1105,41 @@ inline Condition operator!=(const Key& key, const char* v)
template<typename T> template<typename T>
Condition operator>(const Key& key, const T& v) Condition operator>(const Key& key, const T& v)
{ {
std::ostringstream s;
s << ">" << v;
return Condition(new detail::KeyCompareConditionImpl(key.mItemTag, [tag = key.mItemTag, v](const Category& c, const Row& r, bool icase) return Condition(new detail::KeyCompareConditionImpl(key.mItemTag, [tag = key.mItemTag, v](const Category& c, const Row& r, bool icase)
{ return r[tag].template compare<T>(v, icase) > 0; })); { return r[tag].template compare<T>(v, icase) > 0; }, s.str()));
} }
template<typename T> template<typename T>
Condition operator>=(const Key& key, const T& v) Condition operator>=(const Key& key, const T& v)
{ {
std::ostringstream s;
s << ">=" << v;
return Condition(new detail::KeyCompareConditionImpl(key.mItemTag, [tag = key.mItemTag, v](const Category& c, const Row& r, bool icase) return Condition(new detail::KeyCompareConditionImpl(key.mItemTag, [tag = key.mItemTag, v](const Category& c, const Row& r, bool icase)
{ return r[tag].template compare<T>(v, icase) >= 0; })); { return r[tag].template compare<T>(v, icase) >= 0; }, s.str()));
} }
template<typename T> template<typename T>
Condition operator<(const Key& key, const T& v) Condition operator<(const Key& key, const T& v)
{ {
std::ostringstream s;
s << "<" << v;
return Condition(new detail::KeyCompareConditionImpl(key.mItemTag, [tag = key.mItemTag, v](const Category& c, const Row& r, bool icase) return Condition(new detail::KeyCompareConditionImpl(key.mItemTag, [tag = key.mItemTag, v](const Category& c, const Row& r, bool icase)
{ return r[tag].template compare<T>(v, icase) < 0; })); { return r[tag].template compare<T>(v, icase) < 0; }, s.str()));
} }
template<typename T> template<typename T>
Condition operator<=(const Key& key, const T& v) Condition operator<=(const Key& key, const T& v)
{ {
std::ostringstream s;
s << "<=" << v;
return Condition(new detail::KeyCompareConditionImpl(key.mItemTag, [tag = key.mItemTag, v](const Category& c, const Row& r, bool icase) return Condition(new detail::KeyCompareConditionImpl(key.mItemTag, [tag = key.mItemTag, v](const Category& c, const Row& r, bool icase)
{ return r[tag].template compare<T>(v, icase) <= 0; })); { return r[tag].template compare<T>(v, icase) <= 0; }, s.str()));
} }
template<> template<>
......
...@@ -2519,7 +2519,7 @@ void Row::assign(size_t column, const std::string& value, bool skipUpdateLinked) ...@@ -2519,7 +2519,7 @@ void Row::assign(size_t column, const std::string& value, bool skipUpdateLinked)
std::string pk = linked->mParentKeys[ix]; std::string pk = linked->mParentKeys[ix];
std::string ck = linked->mChildKeys[ix]; std::string ck = linked->mChildKeys[ix];
// TODO add code to *NOT* test mandatory fiels for Empty // TODO add code to *NOT* test mandatory fields for Empty
if (pk == iv->mTag) if (pk == iv->mTag)
{ {
...@@ -2529,16 +2529,19 @@ void Row::assign(size_t column, const std::string& value, bool skipUpdateLinked) ...@@ -2529,16 +2529,19 @@ void Row::assign(size_t column, const std::string& value, bool skipUpdateLinked)
else else
{ {
const char* value = (*this)[pk].c_str(); const char* value = (*this)[pk].c_str();
cond = std::move(cond) && ((Key(ck) == value) or Key(ck) == Empty()); if (*value == 0)
cond = std::move(cond) && Key(ck) == Empty();
else
cond = std::move(cond) && ((Key(ck) == value) or Key(ck) == Empty());
} }
} }
// if (cif::VERBOSE > 2) if (cif::VERBOSE >= 2)
// { {
// std::std::cerr << "Parent: " << linked->mParentCategory << " Child: " << linked->mChildCategory << std::std::endl std::cerr << "Parent: " << linked->mParentCategory << " Child: " << linked->mChildCategory << std::endl
// << cond << std::std::endl; << cond << std::endl;
// } }
auto rows = childCat->find(std::move(cond)); auto rows = childCat->find(std::move(cond));
for (auto& cr: rows) for (auto& cr: rows)
......
...@@ -894,86 +894,110 @@ void DictParser::linkItems() ...@@ -894,86 +894,110 @@ void DictParser::linkItems()
std::map<std::tuple<std::string,std::string>,size_t> linkIndex; std::map<std::tuple<std::string,std::string>,size_t> linkIndex;
std::map<std::tuple<std::string,std::string>,int> linkGroupIds; std::map<std::tuple<std::string,std::string>,int> linkGroupIds;
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;
for (auto gl: dict["pdbx_item_linked_group_list"]) auto addLink = [&](size_t ix, const std::string& pk, const std::string& ck)
{ {
std::string child, parent; auto&& [pkeys, ckeys] = linkKeys.at(ix);
int link_group_id;
cif::tie(child, parent, link_group_id) = gl.get("child_name", "parent_name", "link_group_id"); bool found = false;
for (size_t i = 0; i < pkeys.size(); ++i)
auto civ = mValidator.getValidatorForItem(child);
if (civ == nullptr)
error("in pdbx_item_linked_group_list, item '" + child + "' is not specified");
auto piv = mValidator.getValidatorForItem(parent);
if (piv == nullptr)
error("in pdbx_item_linked_group_list, item '" + parent + "' is not specified");
auto key = std::make_tuple(piv->mCategory->mName, civ->mCategory->mName);
if (not linkIndex.count(key))
{ {
linkIndex[key] = linkKeys.size(); if (pkeys[i] == pk and ckeys[i] == ck)
linkKeys.push_back({}); {
found = true;
break;
}
}
linkGroupIds[key] = link_group_id; if (not found)
{
pkeys.push_back(pk);
ckeys.push_back(ck);
} }
};
size_t ix = linkIndex.at(key);
std::get<0>(linkKeys.at(ix)).push_back(piv->mTag);
std::get<1>(linkKeys.at(ix)).push_back(civ->mTag);
}
// for links recorded in categories but not in pdbx_item_linked_group_list auto& linkedGroupList = dict["pdbx_item_linked_group_list"];
for (auto li: mImpl->mLinkedItems)
{
std::string child, parent;
std::tie(child, parent) = li;
auto civ = mValidator.getValidatorForItem(child);
if (civ == nullptr)
error("in pdbx_item_linked_group_list, item '" + child + "' is not specified");
auto piv = mValidator.getValidatorForItem(parent);
if (piv == nullptr)
error("in pdbx_item_linked_group_list, item '" + parent + "' is not specified");
auto key = std::make_tuple(piv->mCategory->mName, civ->mCategory->mName); if (linkedGroupList.empty())
if (not linkIndex.count(key)) {
// for links recorded in categories but not in pdbx_item_linked_group_list
for (auto li: mImpl->mLinkedItems)
{ {
linkIndex[key] = linkKeys.size(); std::string child, parent;
linkKeys.push_back({}); std::tie(child, parent) = li;
auto civ = mValidator.getValidatorForItem(child);
if (civ == nullptr)
error("in pdbx_item_linked_group_list, item '" + child + "' is not specified");
auto piv = mValidator.getValidatorForItem(parent);
if (piv == nullptr)
error("in pdbx_item_linked_group_list, item '" + parent + "' is not specified");
auto key = std::make_tuple(piv->mCategory->mName, civ->mCategory->mName);
if (linkIndex.count(key))
{
linkIndex[key] = linkKeys.size();
linkKeys.push_back({});
}
size_t ix = linkIndex.at(key);
addLink(ix, piv->mTag, civ->mTag);
} }
size_t ix = linkIndex.at(key);
std::get<0>(linkKeys.at(ix)).push_back(piv->mTag);
std::get<1>(linkKeys.at(ix)).push_back(civ->mTag);
} }
else
auto& linkedGroup = dict["pdbx_item_linked_group"];
// now store the links in the validator
for (auto& kv: linkIndex)
{ {
ValidateLink link = {}; for (auto gl: linkedGroupList)
std::tie(link.mParentCategory, link.mChildCategory) = kv.first; {
std::string child, parent;
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);
if (civ == nullptr)
error("in pdbx_item_linked_group_list, item '" + child + "' is not specified");
auto piv = mValidator.getValidatorForItem(parent);
if (piv == nullptr)
error("in pdbx_item_linked_group_list, item '" + parent + "' is not specified");
auto key = std::make_tuple(piv->mCategory->mName, civ->mCategory->mName);
if (not linkIndex.count(key))
{
linkIndex[key] = linkKeys.size();
linkKeys.push_back({});
if (linkGroupIds.count(kv.first)) linkGroupIds[key] = link_group_id;
link.mLinkGroupID = linkGroupIds[kv.first]; }
std::tie(link.mParentKeys, link.mChildKeys) = linkKeys[kv.second]; size_t ix = linkIndex.at(key);
addLink(ix, piv->mTag, civ->mTag);
}
// look up the label auto& linkedGroup = dict["pdbx_item_linked_group"];
for (auto r: linkedGroup.find(cif::Key("category_id") == link.mChildCategory and cif::Key("link_group_id") == link.mLinkGroupID))
// now store the links in the validator
for (auto& kv: linkIndex)
{ {
link.mLinkGroupLabel = r["label"].as<std::string>(); ValidateLink link = {};
break; std::tie(link.mParentCategory, link.mChildCategory) = kv.first;
}
if (linkGroupIds.count(kv.first))
link.mLinkGroupID = linkGroupIds[kv.first];
std::tie(link.mParentKeys, link.mChildKeys) = linkKeys[kv.second];
// look up the label
for (auto r: linkedGroup.find(cif::Key("category_id") == link.mChildCategory and cif::Key("link_group_id") == link.mLinkGroupID))
{
link.mLinkGroupLabel = r["label"].as<std::string>();
break;
}
mValidator.addLinkValidator(std::move(link)); mValidator.addLinkValidator(std::move(link));
}
} }
// now make sure the itemType is specified for all itemValidators // now make sure the itemType is specified for all itemValidators
for (auto& cv: mValidator.mCategoryValidators) for (auto& cv: mValidator.mCategoryValidators)
......
...@@ -52,6 +52,8 @@ cif::File operator""_cf(const char* text, size_t length) ...@@ -52,6 +52,8 @@ cif::File operator""_cf(const char* text, size_t length)
BOOST_AUTO_TEST_CASE(ut1) BOOST_AUTO_TEST_CASE(ut1)
{ {
cif::VERBOSE = 1;
// using namespace mmcif; // using namespace mmcif;
auto f = R"(data_TEST auto f = R"(data_TEST
...@@ -403,4 +405,436 @@ mies Mies ...@@ -403,4 +405,436 @@ mies Mies
BOOST_CHECK(cat1.size() == 2); BOOST_CHECK(cat1.size() == 2);
} }
\ No newline at end of file
// --------------------------------------------------------------------
BOOST_AUTO_TEST_CASE(d3)
{
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_linked.child_name '_cat_2.parent_id'
_item_linked.parent_name '_cat_1.id'
_item_type.code code
save_
save__cat_1.name1
_item.name '_cat_1.name1'
_item.category_id cat_1
_item.mandatory_code yes
_item_type.code text
save_
save__cat_1.name2
_item.name '_cat_1.name2'
_item.category_id cat_1
_item.mandatory_code no
_item_linked.child_name '_cat_2.name2'
_item_linked.parent_name '_cat_1.name2'
_item_type.code text
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 code
save_
save__cat_2.name2
_item.name '_cat_2.name2'
_item.category_id cat_2
_item.mandatory_code no
_item_type.code text
save_
save__cat_2.desc
_item.name '_cat_2.desc'
_item.category_id cat_2
_item.mandatory_code yes
_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);
cif::File f;
f.loadDictionary(is_dict);
// --------------------------------------------------------------------
const char data[] = R"(
data_test
loop_
_cat_1.id
_cat_1.name1
_cat_1.name2
1 Aap aap
2 Noot noot
3 Mies mies
loop_
_cat_2.id
_cat_2.parent_id
_cat_2.name2
_cat_2.desc
1 1 aap 'Een dier'
2 1 . 'Een andere aap'
3 2 noot 'walnoot bijvoorbeeld'
4 2 n2 hazelnoot
)";
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;
}
BOOST_CHECK(cat1.size() == 3);
BOOST_CHECK(cat2.size() == 4);
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_id") == 10).size() == 2);
// check a rename in parent and child, this time only one child should be renamed
for (auto r: cat1.find(cif::Key("id") == 2))
{
r["id"] = 20;
break;
}
BOOST_CHECK(cat1.size() == 3);
BOOST_CHECK(cat2.size() == 4);
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() == 1);
BOOST_CHECK(cat2.find(cif::Key("parent_id") == 20).size() == 1);
BOOST_CHECK(cat2.find(cif::Key("parent_id") == 2 and cif::Key("name2") == "noot").size() == 0);
BOOST_CHECK(cat2.find(cif::Key("parent_id") == 2 and cif::Key("name2") == "n2").size() == 1);
BOOST_CHECK(cat2.find(cif::Key("parent_id") == 20 and cif::Key("name2") == "noot").size() == 1);
BOOST_CHECK(cat2.find(cif::Key("parent_id") == 20 and cif::Key("name2") == "n2").size() == 0);
// // --------------------------------------------------------------------
// cat1.erase(cif::Key("id") == 10);
// BOOST_CHECK(cat1.size() == 2);
// BOOST_CHECK(cat2.size() == 2);
// cat1.erase(cif::Key("id") == 20);
// BOOST_CHECK(cat1.size() == 1);
// BOOST_CHECK(cat2.size() == 1);
}
// --------------------------------------------------------------------
BOOST_AUTO_TEST_CASE(d4)
{
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_linked.child_name '_cat_2.parent_id'
_item_linked.parent_name '_cat_1.id'
_item_type.code int
save_
save__cat_1.id2
_item.name '_cat_1.id2'
_item.category_id cat_1
_item.mandatory_code no
_item_linked.child_name '_cat_2.parent_id2'
_item_linked.parent_name '_cat_1.id2'
_item_type.code code
save_
save__cat_1.id3
_item.name '_cat_1.id3'
_item.category_id cat_1
_item.mandatory_code no
_item_linked.child_name '_cat_2.parent_id3'
_item_linked.parent_name '_cat_1.id3'
_item_type.code text
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_
)";
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
_cat_1.id2
_cat_1.id3
1 aap aap
2 . noot
3 mies .
4 . .
loop_
_cat_2.id
_cat_2.parent_id
_cat_2.parent_id2
_cat_2.parent_id3
1 1 aap aap
2 1 . x
3 1 aap .
4 2 noot noot
5 2 . noot
6 2 noot .
7 2 . .
8 3 mies mies
9 3 . mies
10 3 mies .
11 4 roos roos
12 4 . roos
13 4 roos .
)";
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;
}
BOOST_CHECK(cat1.size() == 4);
BOOST_CHECK(cat2.size() == 13);
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() == 1);
BOOST_CHECK(cat2.find(cif::Key("parent_id") == 10).size() == 2);
for (auto r: cat1.find(cif::Key("id") == 2))
{
r["id"] = 20;
break;
}
BOOST_CHECK(cat1.size() == 4);
BOOST_CHECK(cat2.size() == 13);
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() == 2);
BOOST_CHECK(cat2.find(cif::Key("parent_id") == 20).size() == 2);
for (auto r: cat1.find(cif::Key("id") == 3))
{
r["id"] = 30;
break;
}
BOOST_CHECK(cat1.size() == 4);
BOOST_CHECK(cat2.size() == 13);
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() == 2);
BOOST_CHECK(cat2.find(cif::Key("parent_id") == 30).size() == 1);
for (auto r: cat1.find(cif::Key("id") == 4))
{
r["id"] = 40;
break;
}
BOOST_CHECK(cat1.size() == 4);
BOOST_CHECK(cat2.size() == 13);
BOOST_CHECK(cat1.find(cif::Key("id") == 4).size() == 0);
BOOST_CHECK(cat1.find(cif::Key("id") == 10).size() == 1);
BOOST_CHECK(cat2.find(cif::Key("parent_id") == 4).size() == 3);
BOOST_CHECK(cat2.find(cif::Key("parent_id") == 40).size() == 0);
f.write(std::cout, {});
}
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