Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
L
libcifpp
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
open
libcifpp
Commits
4dd4f663
Unverified
Commit
4dd4f663
authored
Aug 04, 2022
by
Maarten L. Hekkelman
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
backup
parent
04b7828a
Hide whitespace changes
Inline
Side-by-side
Showing
11 changed files
with
2070 additions
and
1513 deletions
+2070
-1513
include/cif++/v2/category.hpp
+24
-72
include/cif++/v2/condition.hpp
+0
-5
include/cif++/v2/datablock.hpp
+16
-44
include/cif++/v2/file.hpp
+16
-37
include/cif++/v2/iterator.hpp
+2
-2
include/cif++/v2/row.hpp
+5
-0
include/cif++/v2/validate.hpp
+1
-1
src/v2/category.cpp
+791
-48
src/v2/item.cpp
+1
-1
src/v2/validate.cpp
+59
-55
test/unit-v2-test.cpp
+1155
-1248
No files found.
include/cif++/v2/category.hpp
View file @
4dd4f663
...
@@ -54,88 +54,25 @@ class category
...
@@ -54,88 +54,25 @@ class category
category
()
=
default
;
category
()
=
default
;
category
(
std
::
string_view
name
)
category
(
std
::
string_view
name
);
:
m_name
(
name
)
{
}
category
(
const
category
&
rhs
)
category
(
const
category
&
rhs
);
:
m_name
(
rhs
.
m_name
)
,
m_columns
(
rhs
.
m_columns
)
{
for
(
auto
r
=
rhs
.
m_head
;
r
!=
nullptr
;
r
=
r
->
m_next
)
insert_impl
(
end
(),
clone_row
(
*
r
));
}
category
(
category
&&
rhs
)
category
(
category
&&
rhs
);
:
m_name
(
std
::
move
(
rhs
.
m_name
))
,
m_columns
(
std
::
move
(
rhs
.
m_columns
))
,
m_head
(
rhs
.
m_head
)
,
m_tail
(
rhs
.
m_tail
)
{
rhs
.
m_head
=
nullptr
;
rhs
.
m_tail
=
nullptr
;
}
category
&
operator
=
(
const
category
&
rhs
)
category
&
operator
=
(
const
category
&
rhs
);
{
if
(
this
!=
&
rhs
)
{
if
(
not
empty
())
clear
();
m_name
=
rhs
.
m_name
;
category
&
operator
=
(
category
&&
rhs
);
m_columns
=
rhs
.
m_columns
;
for
(
auto
r
=
rhs
.
m_head
;
r
!=
nullptr
;
r
=
r
->
m_next
)
~
category
();
insert_impl
(
cend
(),
clone_row
(
*
r
));
}
return
*
this
;
}
category
&
operator
=
(
category
&&
rhs
)
{
if
(
this
!=
&
rhs
)
{
if
(
not
empty
())
clear
();
m_name
=
std
::
move
(
rhs
.
m_name
);
m_columns
=
std
::
move
(
rhs
.
m_columns
);
m_head
=
rhs
.
m_head
;
m_tail
=
rhs
.
m_tail
;
rhs
.
m_head
=
rhs
.
m_tail
=
nullptr
;
}
return
*
this
;
}
~
category
()
{
clear
();
}
// --------------------------------------------------------------------
// --------------------------------------------------------------------
const
std
::
string
&
name
()
const
{
return
m_name
;
}
const
std
::
string
&
name
()
const
{
return
m_name
;
}
iset
fields
()
const
iset
fields
()
const
;
{
if
(
m_validator
==
nullptr
)
throw
std
::
runtime_error
(
"No Validator specified"
);
if
(
m_cat_validator
==
nullptr
)
m_validator
->
report_error
(
"undefined Category"
,
true
);
iset
result
;
std
::
set
<
uint16_t
>
key_field_indices
()
const
;
for
(
auto
&
iv
:
m_cat_validator
->
m_item_validators
)
result
.
insert
(
iv
.
m_tag
);
return
result
;
}
void
set_validator
(
const
validator
*
v
,
datablock
&
db
);
void
set_validator
(
const
validator
*
v
,
datablock
&
db
);
void
update_links
(
datablock
&
db
);
void
update_links
(
datablock
&
db
);
...
@@ -374,6 +311,20 @@ class category
...
@@ -374,6 +311,20 @@ class category
}
}
// --------------------------------------------------------------------
// --------------------------------------------------------------------
bool
has_children
(
row_handle
r
)
const
;
bool
has_parents
(
row_handle
r
)
const
;
std
::
vector
<
row_handle
>
get_children
(
row_handle
r
,
category
&
childCat
);
// std::vector<row_handle> getChildren(row_handle r, const char *childCat);
std
::
vector
<
row_handle
>
get_parents
(
row_handle
r
,
category
&
parentCat
);
// std::vector<row_handle> getParents(row_handle r, const char *parentCat);
std
::
vector
<
row_handle
>
get_linked
(
row_handle
r
,
category
&
cat
);
// std::vector<row_handle> getLinked(row_handle r, const char *cat);
// --------------------------------------------------------------------
// void insert(const_iterator pos, const row &row)
// void insert(const_iterator pos, const row &row)
// {
// {
...
@@ -500,7 +451,7 @@ class category
...
@@ -500,7 +451,7 @@ class category
{
{
auto
iv
=
m_cat_validator
->
get_validator_for_item
(
column_name
);
auto
iv
=
m_cat_validator
->
get_validator_for_item
(
column_name
);
if
(
iv
==
nullptr
)
if
(
iv
==
nullptr
)
std
::
cerr
<<
"Invalid name used '"
<<
name
<<
"' is not a known column in "
+
m_name
<<
std
::
endl
;
std
::
cerr
<<
"Invalid name used '"
<<
column_
name
<<
"' is not a known column in "
+
m_name
<<
std
::
endl
;
}
}
return
result
;
return
result
;
...
@@ -690,6 +641,7 @@ class category
...
@@ -690,6 +641,7 @@ class category
const
category_validator
*
m_cat_validator
=
nullptr
;
const
category_validator
*
m_cat_validator
=
nullptr
;
std
::
vector
<
link
>
m_parent_links
,
m_child_links
;
std
::
vector
<
link
>
m_parent_links
,
m_child_links
;
bool
m_cascade
=
true
;
bool
m_cascade
=
true
;
class
category_index
*
m_index
=
nullptr
;
row
*
m_head
=
nullptr
,
*
m_tail
=
nullptr
;
row
*
m_head
=
nullptr
,
*
m_tail
=
nullptr
;
};
};
...
...
include/cif++/v2/condition.hpp
View file @
4dd4f663
...
@@ -604,11 +604,6 @@ inline condition all()
...
@@ -604,11 +604,6 @@ inline condition all()
return
condition
(
new
detail
::
all_condition_impl
());
return
condition
(
new
detail
::
all_condition_impl
());
}
}
// inline condition_t<category> Not(condition_t<category> &&cond)
// {
// return condition_t<category>(new detail::not_condition_impl(std::move(cond)));
// }
namespace
literals
namespace
literals
{
{
inline
key
operator
""
_key
(
const
char
*
text
,
size_t
length
)
inline
key
operator
""
_key
(
const
char
*
text
,
size_t
length
)
...
...
include/cif++/v2/datablock.hpp
View file @
4dd4f663
...
@@ -35,16 +35,9 @@ namespace cif::v2
...
@@ -35,16 +35,9 @@ namespace cif::v2
// --------------------------------------------------------------------
// --------------------------------------------------------------------
class
datablock
class
datablock
:
public
std
::
list
<
category
>
{
{
public
:
public
:
using
category_list
=
std
::
list
<
category
>
;
using
iterator
=
category_list
::
iterator
;
using
const_iterator
=
category_list
::
const_iterator
;
using
reference
=
typename
category_list
::
reference
;
datablock
()
=
default
;
datablock
()
=
default
;
datablock
(
std
::
string_view
name
)
datablock
(
std
::
string_view
name
)
...
@@ -90,48 +83,31 @@ class datablock
...
@@ -90,48 +83,31 @@ class datablock
// --------------------------------------------------------------------
// --------------------------------------------------------------------
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
&
operator
[](
std
::
string_view
name
)
category
&
operator
[](
std
::
string_view
name
)
{
{
auto
i
=
std
::
find_if
(
m_categories
.
begin
(),
m_categories
.
end
(),
[
name
](
const
category
&
c
)
auto
i
=
std
::
find_if
(
begin
(),
end
(),
[
name
](
const
category
&
c
)
{
return
iequals
(
c
.
name
(),
name
);
});
{
return
iequals
(
c
.
name
(),
name
);
});
if
(
i
!=
m_categories
.
end
())
if
(
i
!=
end
())
return
*
i
;
return
*
i
;
m_categories
.
emplace_back
(
name
);
emplace_back
(
name
);
return
m_categories
.
back
();
return
back
();
}
}
const
category
&
operator
[](
std
::
string_view
name
)
const
const
category
&
operator
[](
std
::
string_view
name
)
const
{
{
static
const
category
s_empty
;
static
const
category
s_empty
;
auto
i
=
std
::
find_if
(
m_categories
.
begin
(),
m_categories
.
end
(),
[
name
](
const
category
&
c
)
auto
i
=
std
::
find_if
(
begin
(),
end
(),
[
name
](
const
category
&
c
)
{
return
iequals
(
c
.
name
(),
name
);
});
{
return
iequals
(
c
.
name
(),
name
);
});
return
i
==
m_categories
.
end
()
?
s_empty
:
*
i
;
return
i
==
end
()
?
s_empty
:
*
i
;
}
}
category
*
get
(
std
::
string_view
name
)
category
*
get
(
std
::
string_view
name
)
{
{
auto
i
=
std
::
find_if
(
m_categories
.
begin
(),
m_categories
.
end
(),
[
name
](
const
category
&
c
)
auto
i
=
std
::
find_if
(
begin
(),
end
(),
[
name
](
const
category
&
c
)
{
return
iequals
(
c
.
name
(),
name
);
});
{
return
iequals
(
c
.
name
(),
name
);
});
return
i
==
m_categories
.
end
()
?
nullptr
:
&*
i
;
return
i
==
end
()
?
nullptr
:
&*
i
;
}
}
const
category
*
get
(
std
::
string_view
name
)
const
const
category
*
get
(
std
::
string_view
name
)
const
...
@@ -143,17 +119,17 @@ class datablock
...
@@ -143,17 +119,17 @@ class datablock
{
{
bool
is_new
=
true
;
bool
is_new
=
true
;
auto
i
=
m_categories
.
begin
();
auto
i
=
begin
();
while
(
i
!=
m_categories
.
end
())
while
(
i
!=
end
())
{
{
if
(
iequals
(
name
,
i
->
name
()))
if
(
iequals
(
name
,
i
->
name
()))
{
{
is_new
=
false
;
is_new
=
false
;
if
(
i
!=
m_categories
.
begin
())
if
(
i
!=
begin
())
{
{
auto
n
=
std
::
next
(
i
);
auto
n
=
std
::
next
(
i
);
m_categories
.
splice
(
m_categories
.
begin
(),
m_categorie
s
,
i
,
n
);
splice
(
begin
(),
*
thi
s
,
i
,
n
);
}
}
break
;
break
;
...
@@ -164,14 +140,11 @@ class datablock
...
@@ -164,14 +140,11 @@ class datablock
if
(
is_new
)
if
(
is_new
)
{
{
m_categories
.
emplace
(
m_categories
.
begin
(),
name
);
auto
&
c
=
emplace_front
(
name
);
// m_categories.emplace(begin(), *this, std::string(name), mValidator);
c
.
set_validator
(
m_validator
,
*
this
);
// for (auto &cat : mCategories)
// cat.updateLinks();
}
}
return
std
::
make_tuple
(
m_categories
.
begin
(),
is_new
);
return
std
::
make_tuple
(
begin
(),
is_new
);
}
}
// void write(std::ostream &os) const
// void write(std::ostream &os) const
...
@@ -217,7 +190,6 @@ class datablock
...
@@ -217,7 +190,6 @@ class datablock
// }
// }
private
:
private
:
category_list
m_categories
;
std
::
string
m_name
;
std
::
string
m_name
;
const
validator
*
m_validator
=
nullptr
;
const
validator
*
m_validator
=
nullptr
;
};
};
...
...
include/cif++/v2/file.hpp
View file @
4dd4f663
...
@@ -36,17 +36,9 @@ namespace cif::v2
...
@@ -36,17 +36,9 @@ namespace cif::v2
// --------------------------------------------------------------------
// --------------------------------------------------------------------
class
file
class
file
:
public
std
::
list
<
datablock
>
{
{
public
:
public
:
using
datablock_list
=
std
::
list
<
datablock
>
;
using
value_type
=
datablock_list
::
value_type
;
using
reference
=
datablock_list
::
reference
;
using
pointer
=
datablock_list
::
pointer
;
using
iterator
=
datablock_list
::
iterator
;
using
const_iterator
=
datablock_list
::
const_iterator
;
file
()
=
default
;
file
()
=
default
;
...
@@ -113,39 +105,39 @@ class file
...
@@ -113,39 +105,39 @@ class file
datablock
&
operator
[](
std
::
string_view
name
)
datablock
&
operator
[](
std
::
string_view
name
)
{
{
auto
i
=
std
::
find_if
(
m_datablocks
.
begin
(),
m_datablocks
.
end
(),
[
name
](
const
datablock
&
c
)
auto
i
=
std
::
find_if
(
begin
(),
end
(),
[
name
](
const
datablock
&
c
)
{
return
iequals
(
c
.
name
(),
name
);
});
{
return
iequals
(
c
.
name
(),
name
);
});
if
(
i
!=
m_datablocks
.
end
())
if
(
i
!=
end
())
return
*
i
;
return
*
i
;
m_datablocks
.
emplace_back
(
name
);
emplace_back
(
name
);
return
m_datablocks
.
back
();
return
back
();
}
}
const
datablock
&
operator
[](
std
::
string_view
name
)
const
const
datablock
&
operator
[](
std
::
string_view
name
)
const
{
{
static
const
datablock
s_empty
;
static
const
datablock
s_empty
;
auto
i
=
std
::
find_if
(
m_datablocks
.
begin
(),
m_datablocks
.
end
(),
[
name
](
const
datablock
&
c
)
auto
i
=
std
::
find_if
(
begin
(),
end
(),
[
name
](
const
datablock
&
c
)
{
return
iequals
(
c
.
name
(),
name
);
});
{
return
iequals
(
c
.
name
(),
name
);
});
return
i
==
m_datablocks
.
end
()
?
s_empty
:
*
i
;
return
i
==
end
()
?
s_empty
:
*
i
;
}
}
std
::
tuple
<
iterator
,
bool
>
emplace
(
std
::
string_view
name
)
std
::
tuple
<
iterator
,
bool
>
emplace
(
std
::
string_view
name
)
{
{
bool
is_new
=
true
;
bool
is_new
=
true
;
auto
i
=
m_datablocks
.
begin
();
auto
i
=
begin
();
while
(
i
!=
m_datablocks
.
end
())
while
(
i
!=
end
())
{
{
if
(
iequals
(
name
,
i
->
name
()))
if
(
iequals
(
name
,
i
->
name
()))
{
{
is_new
=
false
;
is_new
=
false
;
if
(
i
!=
m_datablocks
.
begin
())
if
(
i
!=
begin
())
{
{
auto
n
=
std
::
next
(
i
);
auto
n
=
std
::
next
(
i
);
m_datablocks
.
splice
(
m_datablocks
.
begin
(),
m_datablock
s
,
i
,
n
);
splice
(
begin
(),
*
thi
s
,
i
,
n
);
}
}
break
;
break
;
...
@@ -155,26 +147,14 @@ class file
...
@@ -155,26 +147,14 @@ class file
}
}
if
(
is_new
)
if
(
is_new
)
m_datablocks
.
emplace
(
m_datablocks
.
begin
(),
name
);
{
auto
&
db
=
emplace_front
(
name
);
db
.
set_validator
(
m_validator
);
}
return
std
::
make_tuple
(
m_datablocks
.
begin
(),
is_new
);
return
std
::
make_tuple
(
begin
(),
is_new
);
}
}
bool
empty
()
const
{
return
m_datablocks
.
empty
();
}
size_t
size
()
const
{
return
m_datablocks
.
size
();
}
iterator
begin
()
{
return
m_datablocks
.
begin
();
}
iterator
end
()
{
return
m_datablocks
.
end
();
}
const_iterator
cbegin
()
{
return
m_datablocks
.
begin
();
}
const_iterator
cend
()
{
return
m_datablocks
.
end
();
}
const_iterator
begin
()
const
{
return
m_datablocks
.
begin
();
}
const_iterator
end
()
const
{
return
m_datablocks
.
end
();
}
reference
front
()
{
return
m_datablocks
.
front
();
}
reference
back
()
{
return
m_datablocks
.
back
();
}
void
load
(
std
::
istream
&
is
)
void
load
(
std
::
istream
&
is
)
{
{
auto
saved
=
m_validator
;
auto
saved
=
m_validator
;
...
@@ -191,7 +171,6 @@ class file
...
@@ -191,7 +171,6 @@ class file
}
}
private
:
private
:
datablock_list
m_datablocks
;
const
validator
*
m_validator
=
nullptr
;
const
validator
*
m_validator
=
nullptr
;
};
};
...
...
include/cif++/v2/iterator.hpp
View file @
4dd4f663
...
@@ -89,7 +89,7 @@ class iterator_impl
...
@@ -89,7 +89,7 @@ class iterator_impl
template
<
typename
IRowType
>
template
<
typename
IRowType
>
iterator_impl
(
iterator_impl
<
IRowType
,
Ts
...
>
&
rhs
)
iterator_impl
(
iterator_impl
<
IRowType
,
Ts
...
>
&
rhs
)
:
m_category
(
rhs
.
m_category
)
:
m_category
(
rhs
.
m_category
)
,
m_current
(
rhs
.
m_current
)
,
m_current
(
const_cast
<
row_type
*>
(
rhs
.
m_current
)
)
,
m_value
(
rhs
.
m_value
)
,
m_value
(
rhs
.
m_value
)
,
m_column_ix
(
rhs
.
m_column_ix
)
,
m_column_ix
(
rhs
.
m_column_ix
)
{
{
...
@@ -274,7 +274,7 @@ class conditional_iterator_proxy
...
@@ -274,7 +274,7 @@ class conditional_iterator_proxy
using
value_type
=
conditional_iterator_proxy
::
value_type
;
using
value_type
=
conditional_iterator_proxy
::
value_type
;
using
difference_type
=
std
::
ptrdiff_t
;
using
difference_type
=
std
::
ptrdiff_t
;
using
pointer
=
value_type
*
;
using
pointer
=
value_type
*
;
using
reference
=
row_handl
e
;
using
reference
=
value_typ
e
;
conditional_iterator_impl
(
CategoryType
&
cat
,
row_iterator
pos
,
const
condition
&
cond
,
const
std
::
array
<
size_t
,
N
>
&
cix
);
conditional_iterator_impl
(
CategoryType
&
cat
,
row_iterator
pos
,
const
condition
&
cond
,
const
std
::
array
<
size_t
,
N
>
&
cix
);
conditional_iterator_impl
(
const
conditional_iterator_impl
&
i
)
=
default
;
conditional_iterator_impl
(
const
conditional_iterator_impl
&
i
)
=
default
;
...
...
include/cif++/v2/row.hpp
View file @
4dd4f663
...
@@ -110,6 +110,7 @@ class row
...
@@ -110,6 +110,7 @@ class row
private
:
private
:
friend
class
item_handle
;
friend
class
item_handle
;
friend
class
category_index
;
template
<
typename
,
typename
...
>
template
<
typename
,
typename
...
>
friend
class
iterator_impl
;
friend
class
iterator_impl
;
...
@@ -136,6 +137,7 @@ class row_handle
...
@@ -136,6 +137,7 @@ class row_handle
public
:
public
:
friend
class
item_handle
;
friend
class
item_handle
;
friend
class
category
;
friend
class
category
;
friend
class
category_index
;
row_handle
()
=
default
;
row_handle
()
=
default
;
...
@@ -258,6 +260,9 @@ class row_handle
...
@@ -258,6 +260,9 @@ class row_handle
void
assign
(
size_t
column
,
std
::
string_view
value
,
bool
updateLinked
,
bool
validate
=
true
);
void
assign
(
size_t
column
,
std
::
string_view
value
,
bool
updateLinked
,
bool
validate
=
true
);
bool
operator
==
(
const
row_handle
&
rhs
)
const
{
return
m_category
==
rhs
.
m_category
and
m_row
==
rhs
.
m_row
;
}
bool
operator
!=
(
const
row_handle
&
rhs
)
const
{
return
m_category
!=
rhs
.
m_category
or
m_row
!=
rhs
.
m_row
;
}
private
:
private
:
uint16_t
get_column_ix
(
std
::
string_view
name
)
const
;
uint16_t
get_column_ix
(
std
::
string_view
name
)
const
;
...
...
include/cif++/v2/validate.hpp
View file @
4dd4f663
...
@@ -82,7 +82,7 @@ struct type_validator
...
@@ -82,7 +82,7 @@ struct type_validator
return
icompare
(
m_name
,
rhs
.
m_name
)
<
0
;
return
icompare
(
m_name
,
rhs
.
m_name
)
<
0
;
}
}
int
compare
(
const
char
*
a
,
const
char
*
b
)
const
;
int
compare
(
std
::
string_view
a
,
std
::
string_view
b
)
const
;
};
};
struct
item_validator
struct
item_validator
...
...
src/v2/category.cpp
View file @
4dd4f663
...
@@ -27,6 +27,10 @@
...
@@ -27,6 +27,10 @@
#include <cif++/v2/category.hpp>
#include <cif++/v2/category.hpp>
#include <cif++/v2/datablock.hpp>
#include <cif++/v2/datablock.hpp>
// TODO: Find out what the rules are exactly for linked items, the current implementation
// is inconsistent. It all depends whether a link is satified if a field taking part in the
// set of linked items is null at one side and not null in the other.
namespace
cif
::
v2
namespace
cif
::
v2
{
{
...
@@ -56,29 +60,638 @@ std::string join(const V &arr, std::string_view sep)
...
@@ -56,29 +60,638 @@ std::string join(const V &arr, std::string_view sep)
return
s
.
str
();
return
s
.
str
();
}
}
// --------------------------------------------------------------------
class
row_comparator
{
public
:
row_comparator
(
category
&
cat
)
:
m_category
(
cat
)
{
auto
cv
=
cat
.
get_cat_validator
();
for
(
auto
k
:
cv
->
m_keys
)
{
size_t
ix
=
cat
.
get_column_ix
(
k
);
auto
iv
=
cv
->
get_validator_for_item
(
k
);
if
(
iv
==
nullptr
)
throw
std
::
runtime_error
(
"Incomplete dictionary, no Item Validator for Key "
+
k
);
auto
tv
=
iv
->
m_type
;
if
(
tv
==
nullptr
)
throw
std
::
runtime_error
(
"Incomplete dictionary, no type Validator for Item "
+
k
);
using
namespace
std
::
placeholders
;
m_comparator
.
emplace_back
(
ix
,
std
::
bind
(
&
type_validator
::
compare
,
tv
,
_1
,
_2
));
}
}
int
operator
()(
const
row
*
a
,
const
row
*
b
)
const
{
assert
(
a
);
assert
(
b
);
row_handle
rha
(
m_category
,
*
a
);
row_handle
rhb
(
m_category
,
*
b
);
int
d
=
0
;
for
(
auto
&
c
:
m_comparator
)
{
size_t
k
;
compareFunc
f
;
std
::
tie
(
k
,
f
)
=
c
;
std
::
string_view
ka
=
rha
[
k
].
text
();
std
::
string_view
kb
=
rhb
[
k
].
text
();
d
=
f
(
ka
,
kb
);
if
(
d
!=
0
)
break
;
}
return
d
;
}
private
:
typedef
std
::
function
<
int
(
std
::
string_view
,
std
::
string_view
)
>
compareFunc
;
typedef
std
::
tuple
<
size_t
,
compareFunc
>
key_comparator
;
std
::
vector
<
key_comparator
>
m_comparator
;
category
&
m_category
;
};
// --------------------------------------------------------------------
//
// class to keep an index on the keys of a category. This is a red/black
// tree implementation.
class
category_index
{
public
:
category_index
(
category
*
cat
)
:
m_category
(
*
cat
)
,
m_row_comparator
(
m_category
)
,
m_root
(
nullptr
)
{
reconstruct
();
}
~
category_index
()
{
delete
m_root
;
}
row
*
find
(
row
*
k
)
const
;
void
insert
(
row
*
r
);
void
erase
(
row
*
r
);
// batch create
void
reconstruct
();
// reorder the row's and returns new head and tail
std
::
tuple
<
row
*
,
row
*>
reorder
()
{
std
::
tuple
<
row
*
,
row
*>
result
=
std
::
make_tuple
(
nullptr
,
nullptr
);
if
(
m_root
!=
nullptr
)
{
entry
*
head
=
find_min
(
m_root
);
entry
*
tail
=
reorder
(
m_root
);
tail
->
m_row
->
m_next
=
nullptr
;
result
=
std
::
make_tuple
(
head
->
m_row
,
tail
->
m_row
);
}
return
result
;
}
size_t
size
()
const
;
// bool isValid() const;
private
:
struct
entry
{
entry
(
row
*
r
)
:
m_row
(
r
)
,
m_left
(
nullptr
)
,
m_right
(
nullptr
)
,
m_red
(
true
)
{
}
~
entry
()
{
delete
m_left
;
delete
m_right
;
}
row
*
m_row
;
entry
*
m_left
;
entry
*
m_right
;
bool
m_red
;
};
entry
*
insert
(
entry
*
h
,
row
*
v
);
entry
*
erase
(
entry
*
h
,
row
*
k
);
// void validate(entry* h, bool isParentRed, uint32_t blackDepth, uint32_t& minBlack, uint32_t& maxBlack) const;
entry
*
rotateLeft
(
entry
*
h
)
{
entry
*
x
=
h
->
m_right
;
h
->
m_right
=
x
->
m_left
;
x
->
m_left
=
h
;
x
->
m_red
=
h
->
m_red
;
h
->
m_red
=
true
;
return
x
;
}
entry
*
rotateRight
(
entry
*
h
)
{
entry
*
x
=
h
->
m_left
;
h
->
m_left
=
x
->
m_right
;
x
->
m_right
=
h
;
x
->
m_red
=
h
->
m_red
;
h
->
m_red
=
true
;
return
x
;
}
void
flipColour
(
entry
*
h
)
{
h
->
m_red
=
not
h
->
m_red
;
if
(
h
->
m_left
!=
nullptr
)
h
->
m_left
->
m_red
=
not
h
->
m_left
->
m_red
;
if
(
h
->
m_right
!=
nullptr
)
h
->
m_right
->
m_red
=
not
h
->
m_right
->
m_red
;
}
bool
is_red
(
entry
*
h
)
const
{
return
h
!=
nullptr
and
h
->
m_red
;
}
entry
*
move_red_left
(
entry
*
h
)
{
flipColour
(
h
);
if
(
h
->
m_right
!=
nullptr
and
is_red
(
h
->
m_right
->
m_left
))
{
h
->
m_right
=
rotateRight
(
h
->
m_right
);
h
=
rotateLeft
(
h
);
flipColour
(
h
);
}
return
h
;
}
entry
*
move_red_right
(
entry
*
h
)
{
flipColour
(
h
);
if
(
h
->
m_left
!=
nullptr
and
is_red
(
h
->
m_left
->
m_left
))
{
h
=
rotateRight
(
h
);
flipColour
(
h
);
}
return
h
;
}
entry
*
fix_up
(
entry
*
h
)
{
if
(
is_red
(
h
->
m_right
))
h
=
rotateLeft
(
h
);
if
(
is_red
(
h
->
m_left
)
and
is_red
(
h
->
m_left
->
m_left
))
h
=
rotateRight
(
h
);
if
(
is_red
(
h
->
m_left
)
and
is_red
(
h
->
m_right
))
flipColour
(
h
);
return
h
;
}
entry
*
find_min
(
entry
*
h
)
{
while
(
h
->
m_left
!=
nullptr
)
h
=
h
->
m_left
;
return
h
;
}
entry
*
erase_min
(
entry
*
h
)
{
if
(
h
->
m_left
==
nullptr
)
{
delete
h
;
h
=
nullptr
;
}
else
{
if
(
not
is_red
(
h
->
m_left
)
and
not
is_red
(
h
->
m_left
->
m_left
))
h
=
move_red_left
(
h
);
h
->
m_left
=
erase_min
(
h
->
m_left
);
h
=
fix_up
(
h
);
}
return
h
;
}
// Fix m_next fields for rows in order of this index
entry
*
reorder
(
entry
*
e
)
{
auto
result
=
e
;
if
(
e
->
m_left
!=
nullptr
)
{
auto
l
=
reorder
(
e
->
m_left
);
l
->
m_row
->
m_next
=
e
->
m_row
;
}
if
(
e
->
m_right
!=
nullptr
)
{
auto
mr
=
find_min
(
e
->
m_right
);
e
->
m_row
->
m_next
=
mr
->
m_row
;
result
=
reorder
(
e
->
m_right
);
}
return
result
;
}
category
&
m_category
;
row_comparator
m_row_comparator
;
entry
*
m_root
;
};
row
*
category_index
::
find
(
row
*
k
)
const
{
const
entry
*
r
=
m_root
;
while
(
r
!=
nullptr
)
{
int
d
=
m_row_comparator
(
k
,
r
->
m_row
);
if
(
d
<
0
)
r
=
r
->
m_left
;
else
if
(
d
>
0
)
r
=
r
->
m_right
;
else
break
;
}
return
r
?
r
->
m_row
:
nullptr
;
}
void
category_index
::
insert
(
row
*
k
)
{
m_root
=
insert
(
m_root
,
k
);
m_root
->
m_red
=
false
;
}
category_index
::
entry
*
category_index
::
insert
(
entry
*
h
,
row
*
v
)
{
if
(
h
==
nullptr
)
return
new
entry
(
v
);
int
d
=
m_row_comparator
(
v
,
h
->
m_row
);
if
(
d
<
0
)
h
->
m_left
=
insert
(
h
->
m_left
,
v
);
else
if
(
d
>
0
)
h
->
m_right
=
insert
(
h
->
m_right
,
v
);
else
{
row_handle
rh
(
m_category
,
*
v
);
std
::
ostringstream
os
;
for
(
auto
col
:
m_category
.
fields
())
os
<<
col
<<
": "
<<
std
::
quoted
(
rh
[
col
].
text
())
<<
"; "
;
throw
std
::
runtime_error
(
"Duplicate Key violation, cat: "
+
m_category
.
name
()
+
" values: "
+
os
.
str
());
}
if
(
is_red
(
h
->
m_right
)
and
not
is_red
(
h
->
m_left
))
h
=
rotateLeft
(
h
);
if
(
is_red
(
h
->
m_left
)
and
is_red
(
h
->
m_left
->
m_left
))
h
=
rotateRight
(
h
);
if
(
is_red
(
h
->
m_left
)
and
is_red
(
h
->
m_right
))
flipColour
(
h
);
return
h
;
}
void
category_index
::
erase
(
row
*
k
)
{
m_root
=
erase
(
m_root
,
k
);
if
(
m_root
!=
nullptr
)
m_root
->
m_red
=
false
;
}
category_index
::
entry
*
category_index
::
erase
(
entry
*
h
,
row
*
k
)
{
if
(
m_row_comparator
(
k
,
h
->
m_row
)
<
0
)
{
if
(
h
->
m_left
!=
nullptr
)
{
if
(
not
is_red
(
h
->
m_left
)
and
not
is_red
(
h
->
m_left
->
m_left
))
h
=
move_red_left
(
h
);
h
->
m_left
=
erase
(
h
->
m_left
,
k
);
}
}
else
{
if
(
is_red
(
h
->
m_left
))
h
=
rotateRight
(
h
);
if
(
m_row_comparator
(
k
,
h
->
m_row
)
==
0
and
h
->
m_right
==
nullptr
)
{
delete
h
;
return
nullptr
;
}
if
(
h
->
m_right
!=
nullptr
)
{
if
(
not
is_red
(
h
->
m_right
)
and
not
is_red
(
h
->
m_right
->
m_left
))
h
=
move_red_right
(
h
);
if
(
m_row_comparator
(
k
,
h
->
m_row
)
==
0
)
{
h
->
m_row
=
find_min
(
h
->
m_right
)
->
m_row
;
h
->
m_right
=
erase_min
(
h
->
m_right
);
}
else
h
->
m_right
=
erase
(
h
->
m_right
,
k
);
}
}
return
fix_up
(
h
);
}
void
category_index
::
reconstruct
()
{
delete
m_root
;
m_root
=
nullptr
;
for
(
auto
r
:
m_category
)
insert
(
r
);
// maybe reconstruction can be done quicker by using the following commented code.
// however, I've not had the time to think of a way to set the red/black flag correctly in that case.
// std::vector<row*> rows;
// transform(mCat.begin(), mCat.end(), backInserter(rows),
// [](Row r) -> row* { assert(r.mData); return r.mData; });
//
// assert(std::find(rows.begin(), rows.end(), nullptr) == rows.end());
//
// // don't use sort here, it will run out of the stack of something.
// // quicksort is notorious for using excessive recursion.
// // Besides, most of the time, the data is ordered already anyway.
//
// stable_sort(rows.begin(), rows.end(), [this](row* a, row* b) -> bool { return this->mComp(a, b) < 0; });
//
// for (size_t i = 0; i < rows.size() - 1; ++i)
// assert(mComp(rows[i], rows[i + 1]) < 0);
//
// deque<entry*> e;
// transform(rows.begin(), rows.end(), back_inserter(e),
// [](row* r) -> entry* { return new entry(r); });
//
// while (e.size() > 1)
// {
// deque<entry*> ne;
//
// while (not e.empty())
// {
// entry* a = e.front();
// e.pop_front();
//
// if (e.empty())
// ne.push_back(a);
// else
// {
// entry* b = e.front();
// b->mLeft = a;
//
// assert(mComp(a->mRow, b->mRow) < 0);
//
// e.pop_front();
//
// if (not e.empty())
// {
// entry* c = e.front();
// e.pop_front();
//
// assert(mComp(b->mRow, c->mRow) < 0);
//
// b->mRight = c;
// }
//
// ne.push_back(b);
//
// if (not e.empty())
// {
// ne.push_back(e.front());
// e.pop_front();
// }
// }
// }
//
// swap (e, ne);
// }
//
// assert(e.size() == 1);
// mRoot = e.front();
}
size_t
category_index
::
size
()
const
{
std
::
stack
<
entry
*>
s
;
s
.
push
(
m_root
);
size_t
result
=
0
;
while
(
not
s
.
empty
())
{
entry
*
e
=
s
.
top
();
s
.
pop
();
if
(
e
==
nullptr
)
continue
;
++
result
;
s
.
push
(
e
->
m_left
);
s
.
push
(
e
->
m_right
);
}
return
result
;
}
// --------------------------------------------------------------------
category
::
category
(
std
::
string_view
name
)
:
m_name
(
name
)
{
}
category
::
category
(
const
category
&
rhs
)
:
m_name
(
rhs
.
m_name
)
,
m_columns
(
rhs
.
m_columns
)
,
m_validator
(
rhs
.
m_validator
)
,
m_cat_validator
(
rhs
.
m_cat_validator
)
,
m_parent_links
(
rhs
.
m_parent_links
)
,
m_child_links
(
rhs
.
m_child_links
)
,
m_cascade
(
rhs
.
m_cascade
)
{
for
(
auto
r
=
rhs
.
m_head
;
r
!=
nullptr
;
r
=
r
->
m_next
)
insert_impl
(
end
(),
clone_row
(
*
r
));
if
(
m_validator
!=
nullptr
)
m_index
=
new
category_index
(
this
);
}
category
::
category
(
category
&&
rhs
)
:
m_name
(
std
::
move
(
rhs
.
m_name
))
,
m_columns
(
std
::
move
(
rhs
.
m_columns
))
,
m_validator
(
rhs
.
m_validator
)
,
m_cat_validator
(
rhs
.
m_cat_validator
)
,
m_parent_links
(
std
::
move
(
rhs
.
m_parent_links
))
,
m_child_links
(
std
::
move
(
rhs
.
m_child_links
))
,
m_cascade
(
rhs
.
m_cascade
)
,
m_index
(
rhs
.
m_index
)
,
m_head
(
rhs
.
m_head
)
,
m_tail
(
rhs
.
m_tail
)
{
rhs
.
m_head
=
nullptr
;
rhs
.
m_tail
=
nullptr
;
rhs
.
m_index
=
nullptr
;
}
category
&
category
::
operator
=
(
const
category
&
rhs
)
{
if
(
this
!=
&
rhs
)
{
if
(
not
empty
())
clear
();
m_name
=
rhs
.
m_name
;
m_columns
=
rhs
.
m_columns
;
m_cascade
=
rhs
.
m_cascade
;
m_validator
=
nullptr
;
m_cat_validator
=
nullptr
;
delete
m_index
;
m_index
=
nullptr
;
for
(
auto
r
=
rhs
.
m_head
;
r
!=
nullptr
;
r
=
r
->
m_next
)
insert_impl
(
cend
(),
clone_row
(
*
r
));
m_validator
=
rhs
.
m_validator
;
m_cat_validator
=
rhs
.
m_cat_validator
;
m_parent_links
=
rhs
.
m_parent_links
;
m_child_links
=
rhs
.
m_child_links
;
if
(
m_validator
!=
nullptr
)
m_index
=
new
category_index
(
this
);
}
return
*
this
;
}
category
&
category
::
operator
=
(
category
&&
rhs
)
{
if
(
this
!=
&
rhs
)
{
if
(
not
empty
())
clear
();
m_name
=
std
::
move
(
rhs
.
m_name
);
m_columns
=
std
::
move
(
rhs
.
m_columns
);
m_cascade
=
rhs
.
m_cascade
;
m_validator
=
rhs
.
m_validator
;
m_cat_validator
=
rhs
.
m_cat_validator
;
m_parent_links
=
rhs
.
m_parent_links
;
m_child_links
=
rhs
.
m_child_links
;
m_index
=
rhs
.
m_index
;
m_head
=
rhs
.
m_head
;
m_tail
=
rhs
.
m_tail
;
rhs
.
m_head
=
rhs
.
m_tail
=
nullptr
;
rhs
.
m_index
=
nullptr
;
}
return
*
this
;
}
category
::~
category
()
{
clear
();
delete
m_index
;
}
// --------------------------------------------------------------------
iset
category
::
fields
()
const
{
if
(
m_validator
==
nullptr
)
throw
std
::
runtime_error
(
"No Validator specified"
);
if
(
m_cat_validator
==
nullptr
)
m_validator
->
report_error
(
"undefined Category"
,
true
);
iset
result
;
for
(
auto
&
iv
:
m_cat_validator
->
m_item_validators
)
result
.
insert
(
iv
.
m_tag
);
return
result
;
}
std
::
set
<
uint16_t
>
category
::
key_field_indices
()
const
{
if
(
m_validator
==
nullptr
)
throw
std
::
runtime_error
(
"No Validator specified"
);
if
(
m_cat_validator
==
nullptr
)
m_validator
->
report_error
(
"undefined Category"
,
true
);
std
::
set
<
uint16_t
>
result
;
for
(
auto
&
k
:
m_cat_validator
->
m_keys
)
result
.
insert
(
get_column_ix
(
k
));
return
result
;
}
// --------------------------------------------------------------------
void
category
::
set_validator
(
const
validator
*
v
,
datablock
&
db
)
void
category
::
set_validator
(
const
validator
*
v
,
datablock
&
db
)
{
{
m_validator
=
v
;
m_validator
=
v
;
//
if (m_index != nullptr)
if
(
m_index
!=
nullptr
)
//
{
{
//
delete m_index;
delete
m_index
;
//
m_index = nullptr;
m_index
=
nullptr
;
//
}
}
if
(
m_validator
!=
nullptr
)
if
(
m_validator
!=
nullptr
)
{
{
m_cat_validator
=
m_validator
->
get_validator_for_category
(
m_name
);
m_cat_validator
=
m_validator
->
get_validator_for_category
(
m_name
);
// if (m_cat_validator != nullptr)
if
(
m_cat_validator
!=
nullptr
)
// {
m_index
=
new
category_index
(
this
);
// m_index = new CatIndex(this);
// m_index->reconstruct();
// //#if DEBUG
// // assert(m_index->size() == size());
// // m_index->validate();
// //#endif
// }
}
}
else
else
m_cat_validator
=
nullptr
;
m_cat_validator
=
nullptr
;
...
@@ -124,13 +737,13 @@ bool category::is_valid() const
...
@@ -124,13 +737,13 @@ bool category::is_valid() const
if
(
empty
())
if
(
empty
())
{
{
if
(
VERBOSE
>
2
)
if
(
VERBOSE
>
2
)
std
::
cerr
<<
"Skipping validation of empty
C
ategory "
<<
m_name
<<
std
::
endl
;
std
::
cerr
<<
"Skipping validation of empty
c
ategory "
<<
m_name
<<
std
::
endl
;
return
true
;
return
true
;
}
}
if
(
m_cat_validator
==
nullptr
)
if
(
m_cat_validator
==
nullptr
)
{
{
m_validator
->
report_error
(
"undefined
C
ategory "
+
m_name
,
false
);
m_validator
->
report_error
(
"undefined
c
ategory "
+
m_name
,
false
);
return
false
;
return
false
;
}
}
...
@@ -141,7 +754,7 @@ bool category::is_valid() const
...
@@ -141,7 +754,7 @@ bool category::is_valid() const
auto
iv
=
m_cat_validator
->
get_validator_for_item
(
col
.
m_name
);
auto
iv
=
m_cat_validator
->
get_validator_for_item
(
col
.
m_name
);
if
(
iv
==
nullptr
)
if
(
iv
==
nullptr
)
{
{
m_validator
->
report_error
(
"Field "
+
col
.
m_name
+
" is not valid in
C
ategory "
+
m_name
,
false
);
m_validator
->
report_error
(
"Field "
+
col
.
m_name
+
" is not valid in
c
ategory "
+
m_name
,
false
);
result
=
false
;
result
=
false
;
}
}
...
@@ -154,22 +767,22 @@ bool category::is_valid() const
...
@@ -154,22 +767,22 @@ bool category::is_valid() const
if
(
not
mandatory
.
empty
())
if
(
not
mandatory
.
empty
())
{
{
m_validator
->
report_error
(
"In
C
ategory "
+
m_name
+
" the following mandatory fields are missing: "
+
join
(
mandatory
,
", "
),
false
);
m_validator
->
report_error
(
"In
c
ategory "
+
m_name
+
" the following mandatory fields are missing: "
+
join
(
mandatory
,
", "
),
false
);
result
=
false
;
result
=
false
;
}
}
//
#if not defined(NDEBUG)
#if not defined(NDEBUG)
//
//
check index?
// check index?
//
if (m_index)
if
(
m_index
)
//
{
{
//
m_index->validate();
//
m_index->validate();
// for (auto r
: *this)
for
(
auto
r
:
*
this
)
//
{
{
// if (m_index->find(r.mData) != r.mData
)
if
(
m_index
->
find
(
r
)
!=
r
)
// m_validator->report_error("Key not found in index for Category " + m_nam
e);
m_validator
->
report_error
(
"Key not found in index for category "
+
m_name
,
tru
e
);
//
}
}
//
}
}
//
#endif
#endif
// validate all values
// validate all values
mandatory
=
m_cat_validator
->
m_mandatory_fields
;
mandatory
=
m_cat_validator
->
m_mandatory_fields
;
...
@@ -183,7 +796,7 @@ bool category::is_valid() const
...
@@ -183,7 +796,7 @@ bool category::is_valid() const
if
(
iv
==
nullptr
)
if
(
iv
==
nullptr
)
{
{
m_validator
->
report_error
(
"invalid field "
+
m_columns
[
cix
].
m_name
+
" for
C
ategory "
+
m_name
,
false
);
m_validator
->
report_error
(
"invalid field "
+
m_columns
[
cix
].
m_name
+
" for
c
ategory "
+
m_name
,
false
);
result
=
false
;
result
=
false
;
continue
;
continue
;
}
}
...
@@ -210,7 +823,7 @@ bool category::is_valid() const
...
@@ -210,7 +823,7 @@ bool category::is_valid() const
if
(
iv
!=
nullptr
and
iv
->
m_mandatory
)
if
(
iv
!=
nullptr
and
iv
->
m_mandatory
)
{
{
m_validator
->
report_error
(
"missing mandatory field "
+
m_columns
[
cix
].
m_name
+
" for
C
ategory "
+
m_name
,
false
);
m_validator
->
report_error
(
"missing mandatory field "
+
m_columns
[
cix
].
m_name
+
" for
c
ategory "
+
m_name
,
false
);
result
=
false
;
result
=
false
;
}
}
}
}
...
@@ -219,6 +832,136 @@ bool category::is_valid() const
...
@@ -219,6 +832,136 @@ bool category::is_valid() const
return
result
;
return
result
;
}
}
// --------------------------------------------------------------------
bool
category
::
has_children
(
row_handle
r
)
const
{
assert
(
m_validator
!=
nullptr
);
assert
(
m_cat_validator
!=
nullptr
);
bool
result
=
false
;
for
(
auto
&&
[
childCat
,
link
]
:
m_child_links
)
{
condition
cond
;
for
(
size_t
ix
=
0
;
ix
<
link
->
m_parent_keys
.
size
();
++
ix
)
{
std
::
string_view
value
=
r
[
link
->
m_parent_keys
[
ix
]].
text
();
// cond = std::move(cond) and (key(link->m_child_keys[ix]) == value or key(link->m_child_keys[ix]) == null);
cond
=
std
::
move
(
cond
)
and
(
key
(
link
->
m_child_keys
[
ix
])
==
value
);
}
result
=
not
childCat
->
find
(
std
::
move
(
cond
)).
empty
();
if
(
result
)
break
;
}
return
result
;
}
bool
category
::
has_parents
(
row_handle
r
)
const
{
assert
(
m_validator
!=
nullptr
);
assert
(
m_cat_validator
!=
nullptr
);
bool
result
=
false
;
for
(
auto
&&
[
parentCat
,
link
]
:
m_parent_links
)
{
condition
cond
;
for
(
size_t
ix
=
0
;
ix
<
link
->
m_child_keys
.
size
();
++
ix
)
{
std
::
string_view
value
=
r
[
link
->
m_child_keys
[
ix
]].
text
();
cond
=
std
::
move
(
cond
)
and
(
key
(
link
->
m_parent_keys
[
ix
])
==
value
);
}
result
=
not
parentCat
->
find
(
std
::
move
(
cond
)).
empty
();
if
(
result
)
break
;
}
return
result
;
}
std
::
vector
<
row_handle
>
category
::
get_children
(
row_handle
r
,
category
&
childCat
)
{
assert
(
m_validator
!=
nullptr
);
assert
(
m_cat_validator
!=
nullptr
);
std
::
vector
<
row_handle
>
result
;
for
(
auto
&
link
:
m_validator
->
get_links_for_parent
(
m_name
))
{
if
(
link
->
m_child_category
!=
childCat
.
m_name
)
continue
;
condition
cond
;
for
(
size_t
ix
=
0
;
ix
<
link
->
m_parent_keys
.
size
();
++
ix
)
{
std
::
string_view
value
=
r
[
link
->
m_parent_keys
[
ix
]].
text
();
// cond = std::move(cond) and (key(link->m_child_keys[ix]) == value or key(link->m_child_keys[ix]) == null);
cond
=
std
::
move
(
cond
)
and
(
key
(
link
->
m_child_keys
[
ix
])
==
value
);
}
for
(
auto
child
:
childCat
.
find
(
std
::
move
(
cond
)))
{
if
(
std
::
find
(
result
.
begin
(),
result
.
end
(),
child
)
==
result
.
end
())
result
.
push_back
(
child
);
}
}
return
result
;
}
std
::
vector
<
row_handle
>
category
::
get_parents
(
row_handle
r
,
category
&
parentCat
)
{
assert
(
m_validator
!=
nullptr
);
assert
(
m_cat_validator
!=
nullptr
);
std
::
vector
<
row_handle
>
result
;
for
(
auto
&
link
:
m_validator
->
get_links_for_child
(
m_name
))
{
if
(
link
->
m_parent_category
!=
parentCat
.
m_name
)
continue
;
condition
cond
;
for
(
size_t
ix
=
0
;
ix
<
link
->
m_child_keys
.
size
();
++
ix
)
{
std
::
string_view
value
=
r
[
link
->
m_child_keys
[
ix
]].
text
();
cond
=
std
::
move
(
cond
)
and
(
key
(
link
->
m_parent_keys
[
ix
])
==
value
);
}
for
(
auto
parent
:
parentCat
.
find
(
std
::
move
(
cond
)))
{
if
(
std
::
find
(
result
.
begin
(),
result
.
end
(),
parent
)
==
result
.
end
())
result
.
push_back
(
parent
);
}
}
return
result
;
}
std
::
vector
<
row_handle
>
category
::
get_linked
(
row_handle
r
,
category
&
cat
)
{
std
::
vector
<
row_handle
>
result
=
get_children
(
r
,
cat
);
if
(
result
.
empty
())
result
=
get_parents
(
r
,
cat
);
return
result
;
}
// --------------------------------------------------------------------
category
::
iterator
category
::
erase
(
iterator
pos
)
category
::
iterator
category
::
erase
(
iterator
pos
)
{
{
row_handle
rh
=
*
pos
;
row_handle
rh
=
*
pos
;
...
@@ -232,8 +975,8 @@ category::iterator category::erase(iterator pos)
...
@@ -232,8 +975,8 @@ category::iterator category::erase(iterator pos)
if
(
m_head
==
nullptr
)
if
(
m_head
==
nullptr
)
throw
std
::
runtime_error
(
"erase"
);
throw
std
::
runtime_error
(
"erase"
);
// if (mI
ndex != nullptr)
if
(
m_i
ndex
!=
nullptr
)
// mIndex->erase(r.mData
);
m_index
->
erase
(
r
);
if
(
r
==
m_head
)
if
(
r
==
m_head
)
{
{
...
@@ -270,6 +1013,7 @@ category::iterator category::erase(iterator pos)
...
@@ -270,6 +1013,7 @@ category::iterator category::erase(iterator pos)
for
(
size_t
ix
=
0
;
ix
<
link
->
m_parent_keys
.
size
();
++
ix
)
for
(
size_t
ix
=
0
;
ix
<
link
->
m_parent_keys
.
size
();
++
ix
)
{
{
std
::
string_view
value
=
rh
[
link
->
m_parent_keys
[
ix
]].
text
();
std
::
string_view
value
=
rh
[
link
->
m_parent_keys
[
ix
]].
text
();
// cond = std::move(cond) and (key(link->m_child_keys[ix]) == value or key(link->m_child_keys[ix]) == null);
cond
=
std
::
move
(
cond
)
and
(
key
(
link
->
m_child_keys
[
ix
])
==
value
);
cond
=
std
::
move
(
cond
)
and
(
key
(
link
->
m_child_keys
[
ix
])
==
value
);
}
}
...
@@ -288,7 +1032,7 @@ category::iterator category::erase(iterator pos)
...
@@ -288,7 +1032,7 @@ category::iterator category::erase(iterator pos)
m_tail
=
m_tail
->
m_next
;
m_tail
=
m_tail
->
m_next
;
}
}
return
result
;
return
result
;
}
}
size_t
category
::
erase
(
condition
&&
cond
)
size_t
category
::
erase
(
condition
&&
cond
)
...
@@ -351,7 +1095,7 @@ bool category::is_orphan(row_handle r) const
...
@@ -351,7 +1095,7 @@ bool category::is_orphan(row_handle r) const
}
}
// if (VERBOSE > 2)
// if (VERBOSE > 2)
// std::cerr << "Check condition '" << cond << "' in parent category " << link->mParent
Category << " for child cat " << mN
ame << std::endl;
// std::cerr << "Check condition '" << cond << "' in parent category " << link->mParent
category << " for child cat " << m_n
ame << std::endl;
if
(
parentCat
->
exists
(
std
::
move
(
cond
)))
if
(
parentCat
->
exists
(
std
::
move
(
cond
)))
{
{
...
@@ -389,7 +1133,6 @@ void category::erase_orphans(condition &&cond)
...
@@ -389,7 +1133,6 @@ void category::erase_orphans(condition &&cond)
erase
(
iterator
(
*
this
,
r
));
erase
(
iterator
(
*
this
,
r
));
}
}
void
category
::
update_value
(
row
*
row
,
size_t
column
,
std
::
string_view
value
,
bool
updateLinked
,
bool
validate
)
void
category
::
update_value
(
row
*
row
,
size_t
column
,
std
::
string_view
value
,
bool
updateLinked
,
bool
validate
)
{
{
auto
&
col
=
m_columns
[
column
];
auto
&
col
=
m_columns
[
column
];
...
@@ -415,18 +1158,18 @@ void category::update_value(row *row, size_t column, std::string_view value, boo
...
@@ -415,18 +1158,18 @@ void category::update_value(row *row, size_t column, std::string_view value, boo
if
(
col
.
m_validator
and
validate
)
if
(
col
.
m_validator
and
validate
)
col
.
m_validator
->
operator
()(
value
);
col
.
m_validator
->
operator
()(
value
);
// If the field is part of the Key for this
C
ategory, remove it from the index
// If the field is part of the Key for this
c
ategory, remove it from the index
// before updating
// before updating
bool
reinsert
=
false
;
bool
reinsert
=
false
;
//
if (updateLinked and // an update of an Item's value
if
(
updateLinked
and
// an update of an Item's value
// cat->m_index != nullptr and cat->keyFieldsByIndex
().count(column))
m_index
!=
nullptr
and
key_field_indices
().
count
(
column
))
//
{
{
// reinsert = cat->m_index->find(mData
);
reinsert
=
m_index
->
find
(
row
);
//
if (reinsert)
if
(
reinsert
)
// cat->m_index->erase(mData
);
m_index
->
erase
(
row
);
//
}
}
// first remove old value with cix
// first remove old value with cix
...
@@ -470,8 +1213,8 @@ void category::update_value(row *row, size_t column, std::string_view value, boo
...
@@ -470,8 +1213,8 @@ void category::update_value(row *row, size_t column, std::string_view value, boo
}
}
}
}
//
if (reinsert)
if
(
reinsert
)
// cat->mIndex->insert(mData
);
m_index
->
insert
(
row
);
// see if we need to update any child categories that depend on this value
// see if we need to update any child categories that depend on this value
auto
iv
=
col
.
m_validator
;
auto
iv
=
col
.
m_validator
;
...
@@ -515,7 +1258,7 @@ void category::update_value(row *row, size_t column, std::string_view value, boo
...
@@ -515,7 +1258,7 @@ void category::update_value(row *row, size_t column, std::string_view value, boo
// if (cif::VERBOSE > 2)
// if (cif::VERBOSE > 2)
// {
// {
// std::cerr << "Parent: " << linked->mParent
Category << " Child: " << linked->mChildC
ategory << std::endl
// std::cerr << "Parent: " << linked->mParent
category << " Child: " << linked->m_child_c
ategory << std::endl
// << cond << std::endl;
// << cond << std::endl;
// }
// }
...
...
src/v2/item.cpp
View file @
4dd4f663
...
@@ -45,7 +45,7 @@ std::string_view item_handle::text() const
...
@@ -45,7 +45,7 @@ std::string_view item_handle::text() const
void
item_handle
::
assign_value
(
const
item
&
v
)
void
item_handle
::
assign_value
(
const
item
&
v
)
{
{
m_row_handle
.
assign
(
m_column
,
v
.
value
(),
fals
e
);
m_row_handle
.
assign
(
m_column
,
v
.
value
(),
tru
e
);
}
}
}
}
src/v2/validate.cpp
View file @
4dd4f663
...
@@ -31,6 +31,8 @@
...
@@ -31,6 +31,8 @@
#include <cif++/v2/dictionary_parser.hpp>
#include <cif++/v2/dictionary_parser.hpp>
#include <cif++/v2/validate.hpp>
#include <cif++/v2/validate.hpp>
#include <cif++/CifUtils.hpp>
namespace
cif
namespace
cif
{
{
extern
int
VERBOSE
;
extern
int
VERBOSE
;
...
@@ -69,25 +71,27 @@ DDL_PrimitiveType map_to_primitive_type(std::string_view s)
...
@@ -69,25 +71,27 @@ DDL_PrimitiveType map_to_primitive_type(std::string_view s)
// --------------------------------------------------------------------
// --------------------------------------------------------------------
int
type_validator
::
compare
(
const
char
*
a
,
const
char
*
b
)
const
int
type_validator
::
compare
(
std
::
string_view
a
,
std
::
string_view
b
)
const
{
{
int
result
=
0
;
int
result
=
0
;
if
(
*
a
==
0
)
if
(
a
.
empty
()
)
result
=
*
b
==
0
?
0
:
-
1
;
result
=
b
.
empty
()
?
0
:
-
1
;
else
if
(
*
b
==
0
)
else
if
(
b
.
empty
()
)
result
=
*
a
==
0
?
0
:
+
1
;
result
=
a
.
empty
()
?
0
:
+
1
;
else
else
{
{
try
switch
(
m_primitive_type
)
{
{
switch
(
m_primitive_type
)
case
DDL_PrimitiveType
:
:
Numb
:
{
{
case
DDL_PrimitiveType
:
:
Numb
:
double
da
,
db
;
{
double
da
=
strtod
(
a
,
nullptr
);
auto
ra
=
cif
::
from_chars
(
a
.
begin
(),
a
.
end
(),
da
);
double
db
=
strtod
(
b
,
nullptr
);
auto
rb
=
cif
::
from_chars
(
b
.
begin
(),
b
.
end
(),
db
);
if
(
ra
.
ec
==
std
::
errc
()
and
rb
.
ec
==
std
::
errc
())
{
auto
d
=
da
-
db
;
auto
d
=
da
-
db
;
if
(
std
::
abs
(
d
)
>
std
::
numeric_limits
<
double
>::
epsilon
())
if
(
std
::
abs
(
d
)
>
std
::
numeric_limits
<
double
>::
epsilon
())
{
{
...
@@ -96,64 +100,64 @@ int type_validator::compare(const char *a, const char *b) const
...
@@ -96,64 +100,64 @@ int type_validator::compare(const char *a, const char *b) const
else
if
(
d
<
0
)
else
if
(
d
<
0
)
result
=
-
1
;
result
=
-
1
;
}
}
break
;
}
}
else
if
(
ra
.
ec
==
std
::
errc
())
result
=
1
;
else
result
=
-
1
;
break
;
}
case
DDL_PrimitiveType
:
:
UChar
:
case
DDL_PrimitiveType
:
:
UChar
:
case
DDL_PrimitiveType
:
:
Char
:
case
DDL_PrimitiveType
:
:
Char
:
{
{
// CIF is guaranteed to have ascii only, therefore this primitive code will do
// CIF is guaranteed to have ascii only, therefore this primitive code will do
// also, we're collapsing spaces
// also, we're collapsing spaces
auto
ai
=
a
,
bi
=
b
;
auto
ai
=
a
.
begin
(),
bi
=
b
.
begin
();
for
(;;)
for
(;;)
{
if
(
ai
==
a
.
end
())
{
{
if
(
*
ai
==
0
)
if
(
bi
!=
b
.
end
())
{
result
=
-
1
;
if
(
*
bi
!=
0
)
break
;
result
=
-
1
;
}
break
;
else
if
(
bi
==
b
.
end
())
}
{
else
if
(
*
bi
==
0
)
result
=
1
;
{
break
;
result
=
1
;
}
break
;
}
char
ca
=
*
ai
;
char
cb
=
*
bi
;
if
(
m_primitive_type
==
DDL_PrimitiveType
::
UChar
)
char
ca
=
*
ai
;
{
char
cb
=
*
bi
;
ca
=
tolower
(
ca
);
cb
=
tolower
(
cb
);
}
result
=
ca
-
cb
;
if
(
m_primitive_type
==
DDL_PrimitiveType
::
UChar
)
{
ca
=
tolower
(
ca
);
cb
=
tolower
(
cb
);
}
if
(
result
!=
0
)
result
=
ca
-
cb
;
break
;
if
(
ca
==
' '
)
if
(
result
!=
0
)
{
break
;
while
(
ai
[
1
]
==
' '
)
++
ai
;
while
(
bi
[
1
]
==
' '
)
++
bi
;
}
++
ai
;
if
(
ca
==
' '
)
++
bi
;
{
while
(
ai
[
1
]
==
' '
)
++
ai
;
while
(
bi
[
1
]
==
' '
)
++
bi
;
}
}
break
;
++
ai
;
++
bi
;
}
}
break
;
}
}
}
}
catch
(
const
std
::
invalid_argument
&
ex
)
{
result
=
1
;
}
}
}
return
result
;
return
result
;
...
...
test/unit-v2-test.cpp
View file @
4dd4f663
...
@@ -553,1215 +553,1216 @@ _cat_2.desc
...
@@ -553,1215 +553,1216 @@ _cat_2.desc
std
::
exception
);
std
::
exception
);
}
}
// // --------------------------------------------------------------------
// --------------------------------------------------------------------
BOOST_AUTO_TEST_CASE
(
d2
)
{
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 ...
;
ucode uchar
'[][_,.;:"&<>()/\{}'`~!@#$%A-Za-z0-9*|+-]*'
; code item types/single words, case insensitive
;
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.
;
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 code
save_
save__cat_1.c
_item.name '_cat_1.c'
_item.category_id cat_1
_item.mandatory_code yes
_item_type.code ucode
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
::
v2
::
parse_dictionary
(
"test"
,
is_dict
);
cif
::
v2
::
file
f
;
f
.
set_validator
(
&
validator
);
// --------------------------------------------------------------------
const
char
data
[]
=
R"(
data_test
loop_
_cat_1.id
_cat_1.c
aap Aap
noot Noot
mies 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
);
auto
&
cat1
=
f
.
front
()[
"cat_1"
];
BOOST_CHECK
(
cat1
.
size
()
==
3
);
cat1
.
erase
(
cif
::
v2
::
key
(
"id"
)
==
"AAP"
);
BOOST_CHECK
(
cat1
.
size
()
==
3
);
cat1
.
erase
(
cif
::
v2
::
key
(
"id"
)
==
"noot"
);
BOOST_CHECK
(
cat1
.
size
()
==
2
);
}
// --------------------------------------------------------------------
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
);
auto
validator
=
cif
::
v2
::
parse_dictionary
(
"test"
,
is_dict
);
cif
::
v2
::
file
f
;
f
.
set_validator
(
&
validator
);
// --------------------------------------------------------------------
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
.
front
()[
"cat_1"
];
auto
&
cat2
=
f
.
front
()[
"cat_2"
];
// check a rename in parent and child
for
(
auto
r
:
cat1
.
find
(
cif
::
v2
::
key
(
"id"
)
==
1
))
{
r
[
"id"
]
=
10
;
break
;
}
BOOST_CHECK
(
cat1
.
size
()
==
3
);
BOOST_CHECK
(
cat2
.
size
()
==
4
);
BOOST_CHECK
(
cat1
.
find
(
cif
::
v2
::
key
(
"id"
)
==
1
).
size
()
==
0
);
BOOST_CHECK
(
cat1
.
find
(
cif
::
v2
::
key
(
"id"
)
==
10
).
size
()
==
1
);
BOOST_CHECK
(
cat2
.
find
(
cif
::
v2
::
key
(
"parent_id"
)
==
1
).
size
()
==
0
);
BOOST_CHECK
(
cat2
.
find
(
cif
::
v2
::
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
::
v2
::
key
(
"id"
)
==
2
))
{
r
[
"id"
]
=
20
;
break
;
}
BOOST_CHECK
(
cat1
.
size
()
==
3
);
BOOST_CHECK
(
cat2
.
size
()
==
4
);
BOOST_CHECK
(
cat1
.
find
(
cif
::
v2
::
key
(
"id"
)
==
2
).
size
()
==
0
);
BOOST_CHECK
(
cat1
.
find
(
cif
::
v2
::
key
(
"id"
)
==
20
).
size
()
==
1
);
BOOST_CHECK
(
cat2
.
find
(
cif
::
v2
::
key
(
"parent_id"
)
==
2
).
size
()
==
1
);
BOOST_CHECK
(
cat2
.
find
(
cif
::
v2
::
key
(
"parent_id"
)
==
20
).
size
()
==
1
);
BOOST_CHECK
(
cat2
.
find
(
cif
::
v2
::
key
(
"parent_id"
)
==
2
and
cif
::
v2
::
key
(
"name2"
)
==
"noot"
).
size
()
==
0
);
BOOST_CHECK
(
cat2
.
find
(
cif
::
v2
::
key
(
"parent_id"
)
==
2
and
cif
::
v2
::
key
(
"name2"
)
==
"n2"
).
size
()
==
1
);
BOOST_CHECK
(
cat2
.
find
(
cif
::
v2
::
key
(
"parent_id"
)
==
20
and
cif
::
v2
::
key
(
"name2"
)
==
"noot"
).
size
()
==
1
);
BOOST_CHECK
(
cat2
.
find
(
cif
::
v2
::
key
(
"parent_id"
)
==
20
and
cif
::
v2
::
key
(
"name2"
)
==
"n2"
).
size
()
==
0
);
// --------------------------------------------------------------------
cat1
.
erase
(
cif
::
v2
::
key
(
"id"
)
==
10
);
BOOST_CHECK
(
cat1
.
size
()
==
2
);
BOOST_CHECK
(
cat2
.
size
()
==
2
);
cat1
.
erase
(
cif
::
v2
::
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_
)"
;
// BOOST_AUTO_TEST_CASE(d2)
struct
membuf
:
public
std
::
streambuf
// {
{
// const char dict[] = R"(
membuf
(
char
*
text
,
size_t
length
)
// data_test_dict.dic
{
// _datablock.id test_dict.dic
this
->
setg
(
text
,
text
,
text
+
length
);
// _datablock.description
}
// ;
}
buffer
(
const_cast
<
char
*>
(
dict
),
sizeof
(
dict
)
-
1
);
// 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 ...
// ;
// ucode uchar
// '[][_,.;:"&<>()/\{}'`~!@#$%A-Za-z0-9*|+-]*'
// ; code item types/single words, case insensitive
// ;
// 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.
// ;
// 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 code
// save_
// save__cat_1.c
// _item.name '_cat_1.c'
// _item.category_id cat_1
// _item.mandatory_code yes
// _item_type.code ucode
// 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);
std
::
istream
is_dict
(
&
buffer
);
// cif::v2::validator validator
("test", is_dict);
auto
validator
=
cif
::
v2
::
parse_dictionary
(
"test"
,
is_dict
);
// cif::F
ile f;
cif
::
v2
::
f
ile
f
;
// f.setV
alidator(&validator);
f
.
set_v
alidator
(
&
validator
);
//
// --------------------------------------------------------------------
// --------------------------------------------------------------------
// const char data[] = R"(
const
char
data
[]
=
R"(
// data_test
data_test
// loop_
loop_
// _cat_1.id
_cat_1.id
// _cat_1.c
_cat_1.id2
// aap Aap
_cat_1.id3
// noot Noot
1 aap aap
// mies Mies
2 . noot
// )";
3 mies .
4 . .
// 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);
loop_
// f.load(is_data);
_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 .
)"
;
// auto &cat1 = f.front()["cat_1"];
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
);
// BOOST_CHECK(cat1.size() == 3);
std
::
istream
is_data
(
&
data_buffer
);
f
.
load
(
is_data
);
// cat1.erase(cif::v2::key("id") == "AAP");
auto
&
cat1
=
f
.
front
()[
"cat_1"
];
auto
&
cat2
=
f
.
front
()[
"cat_2"
];
// BOOST_CHECK(cat1.size() == 3);
// check a rename in parent and child
// cat1.erase(cif::v2::key("id") == "noot");
for
(
auto
r
:
cat1
.
find
(
cif
::
v2
::
key
(
"id"
)
==
1
))
{
r
[
"id"
]
=
10
;
break
;
}
// BOOST_CHECK(cat1.size() == 2
);
BOOST_CHECK
(
cat1
.
size
()
==
4
);
// }
BOOST_CHECK
(
cat2
.
size
()
==
13
);
// // --------------------------------------------------------------------
BOOST_CHECK
(
cat1
.
find
(
cif
::
v2
::
key
(
"id"
)
==
1
).
size
()
==
0
);
BOOST_CHECK
(
cat1
.
find
(
cif
::
v2
::
key
(
"id"
)
==
10
).
size
()
==
1
);
// BOOST_AUTO_TEST_CASE(d3)
BOOST_CHECK
(
cat2
.
find
(
cif
::
v2
::
key
(
"parent_id"
)
==
1
).
size
()
==
1
);
// {
BOOST_CHECK
(
cat2
.
find
(
cif
::
v2
::
key
(
"parent_id"
)
==
10
).
size
()
==
2
);
// 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);
for
(
auto
r
:
cat1
.
find
(
cif
::
v2
::
key
(
"id"
)
==
2
))
{
r
[
"id"
]
=
20
;
break
;
}
// cif::v2::validator validator("test", is_dict);
BOOST_CHECK
(
cat1
.
size
()
==
4
);
BOOST_CHECK
(
cat2
.
size
()
==
13
);
// cif::File f
;
BOOST_CHECK
(
cat1
.
find
(
cif
::
v2
::
key
(
"id"
)
==
2
).
size
()
==
0
)
;
// f.setValidator(&validator
);
BOOST_CHECK
(
cat1
.
find
(
cif
::
v2
::
key
(
"id"
)
==
20
).
size
()
==
1
);
// // --------------------------------------------------------------------
BOOST_CHECK
(
cat2
.
find
(
cif
::
v2
::
key
(
"parent_id"
)
==
2
).
size
()
==
2
);
BOOST_CHECK
(
cat2
.
find
(
cif
::
v2
::
key
(
"parent_id"
)
==
20
).
size
()
==
2
);
// const char data[] = R"(
for
(
auto
r
:
cat1
.
find
(
cif
::
v2
::
key
(
"id"
)
==
3
))
// data_test
{
// loop_
r
[
"id"
]
=
30
;
// _cat_1.id
break
;
// _cat_1.name1
}
// _cat_1.name2
// 1 Aap aap
// 2 Noot noot
// 3 Mies mies
// loop_
BOOST_CHECK
(
cat1
.
size
()
==
4
);
// _cat_2.id
BOOST_CHECK
(
cat2
.
size
()
==
13
);
// _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
);
BOOST_CHECK
(
cat1
.
find
(
cif
::
v2
::
key
(
"id"
)
==
3
).
size
()
==
0
);
// f.load(is_data
);
BOOST_CHECK
(
cat1
.
find
(
cif
::
v2
::
key
(
"id"
)
==
30
).
size
()
==
1
);
// auto &cat1 = f.front()["cat_1"]
;
BOOST_CHECK
(
cat2
.
find
(
cif
::
v2
::
key
(
"parent_id"
)
==
3
).
size
()
==
2
)
;
// auto &cat2 = f.front()["cat_2"]
;
BOOST_CHECK
(
cat2
.
find
(
cif
::
v2
::
key
(
"parent_id"
)
==
30
).
size
()
==
1
)
;
// // check a rename in parent and child
for
(
auto
r
:
cat1
.
find
(
cif
::
v2
::
key
(
"id"
)
==
4
))
{
r
[
"id"
]
=
40
;
break
;
}
// for (auto r : cat1.find(cif::v2::key("id") == 1))
BOOST_CHECK
(
cat1
.
size
()
==
4
);
// {
BOOST_CHECK
(
cat2
.
size
()
==
13
);
// r["id"] = 10;
// break;
// }
// BOOST_CHECK(cat1.size() == 3
);
BOOST_CHECK
(
cat1
.
find
(
cif
::
v2
::
key
(
"id"
)
==
4
).
size
()
==
0
);
// BOOST_CHECK(cat2.size() == 4
);
BOOST_CHECK
(
cat1
.
find
(
cif
::
v2
::
key
(
"id"
)
==
10
).
size
()
==
1
);
// BOOST_CHECK(cat1.find(cif::v2::key("id") == 1).size() == 0);
BOOST_CHECK
(
cat2
.
find
(
cif
::
v2
::
key
(
"parent_id"
)
==
4
).
size
()
==
3
);
// BOOST_CHECK(cat1.find(cif::v2::key("id") == 10).size() == 1);
BOOST_CHECK
(
cat2
.
find
(
cif
::
v2
::
key
(
"parent_id"
)
==
40
).
size
()
==
0
);
}
// BOOST_CHECK(cat2.find(cif::v2::key("parent_id") == 1).size() == 0);
// --------------------------------------------------------------------
// BOOST_CHECK(cat2.find(cif::v2::key("parent_id") == 10).size() == 2);
// // check a rename in parent and child, this time only one child should be renamed
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
// for (auto r : cat1.find(cif::v2::key("id") == 2))
loop_
// {
_item_type_list.code
// r["id"] = 20;
_item_type_list.primitive_code
// break;
_item_type_list.construct
// }
code char
'[][_,.;:"&<>()/\{}'`~!@#$%A-Za-z0-9*|+-]*'
// BOOST_CHECK(cat1.size() == 3);
text char
// BOOST_CHECK(cat2.size() == 4);
'[][ \n\t()_,.;:"&<>/\{}'`~!@#$%?+=*A-Za-z0-9|^-]*'
// BOOST_CHECK(cat1.find(cif::v2::key("id") == 2).size() == 0);
int numb
// BOOST_CHECK(cat1.find(cif::v2::key("id") == 20).size() == 1);
'[+-]?[0-9]+'
// BOOST_CHECK(cat2.find(cif::v2::key("parent_id") == 2).size() == 1);
save_cat_1
// BOOST_CHECK(cat2.find(cif::v2::key("parent_id") == 20).size() == 1);
_category.description 'A simple test category'
_category.id cat_1
_category.mandatory_code no
_category_key.name '_cat_1.id'
save_
// BOOST_CHECK(cat2.find(cif::v2::key("parent_id") == 2 and cif::v2::key("name2") == "noot").size() == 0);
save__cat_1.id
// BOOST_CHECK(cat2.find(cif::v2::key("parent_id") == 2 and cif::v2::key("name2") == "n2").size() == 1);
_item.name '_cat_1.id'
// BOOST_CHECK(cat2.find(cif::v2::key("parent_id") == 20 and cif::v2::key("name2") == "noot").size() == 1);
_item.category_id cat_1
// BOOST_CHECK(cat2.find(cif::v2::key("parent_id") == 20 and cif::v2::key("name2") == "n2").size() == 0);
_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_
// // cat1.erase(cif::v2::key("id") == 10);
save__cat_2.id
_item.name '_cat_2.id'
_item.category_id cat_2
_item.mandatory_code yes
_item_type.code int
save_
// // BOOST_CHECK(cat1.size() == 2);
save__cat_2.parent_id
// // BOOST_CHECK(cat2.size() == 2);
_item.name '_cat_2.parent_id'
_item.category_id cat_2
_item.mandatory_code yes
_item_type.code int
save_
// // cat1.erase(cif::v2::key("id") == 20);
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_
// // BOOST_CHECK(cat1.size() == 1);
save__cat_2.parent_id3
// // BOOST_CHECK(cat2.size() == 1);
_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
// BOOST_AUTO_TEST_CASE(d4)
loop_
// {
_pdbx_item_linked_group.category_id
// const char dict[] = R"(
_pdbx_item_linked_group.link_group_id
// data_test_dict.dic
_pdbx_item_linked_group.label
// _datablock.id test_dict.dic
cat_2 1 cat_2:cat_1:1
// _datablock.description
cat_2 2 cat_2:cat_1:2
// ;
cat_2 3 cat_2:cat_1:3
// 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);
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
);
// cif::v2::validator validator("test", is_dict
);
std
::
istream
is_dict
(
&
buffer
);
// cif::File f;
auto
validator
=
cif
::
v2
::
parse_dictionary
(
"test"
,
is_dict
);
// f.setValidator(&validator);
// // --------------------------------------------------------------------
cif
::
v2
::
file
f
;
f
.
set_validator
(
&
validator
);
// 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_
const
char
data
[]
=
R"(
// _cat_2.id
data_test
// _cat_2.parent_id
loop_
// _cat_2.parent_id2
_cat_1.id
// _cat_2.parent_id3
1
// 1 1 aap aap
2
// 2 1 . x
3
// 3 1 aap .
// 4 2 noot noot
loop_
// 5 2 . noot
_cat_2.id
// 6 2 noot .
_cat_2.parent_id
// 7 2 . .
_cat_2.parent_id2
// 8 3 mies mies
_cat_2.parent_id3
// 9 3 . mies
1 1 ? ?
// 10 3 mies .
2 ? 1 ?
// 11 4 roos roos
3 ? ? 1
// 12 4 . roos
4 2 2 ?
// 13 4 roos .
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.front()["cat_1"];
struct
data_membuf
:
public
std
::
streambuf
// auto &cat2 = f.front()["cat_2"];
{
data_membuf
(
char
*
text
,
size_t
length
)
{
this
->
setg
(
text
,
text
,
text
+
length
);
}
}
data_buffer
(
const_cast
<
char
*>
(
data
),
sizeof
(
data
)
-
1
);
// // check a rename in parent and child
std
::
istream
is_data
(
&
data_buffer
);
f
.
load
(
is_data
);
// for (auto r : cat1.find(cif::v2::key("id") == 1))
auto
&
cat1
=
f
.
front
()[
"cat_1"
];
// {
auto
&
cat2
=
f
.
front
()[
"cat_2"
];
// r["id"] = 10;
// break;
// }
// BOOST_CHECK(cat1.size() == 4);
// --------------------------------------------------------------------
// BOOST_CHECK(cat2.size() == 13);
// check iterate children
// BOOST_CHECK(cat1.find(cif::v2::key("id") == 1).size() == 0);
auto
PR2set
=
cat1
.
find
(
cif
::
v2
::
key
(
"id"
)
==
2
);
// BOOST_CHECK(cat1.find(cif::v2::key("id") == 10).size() == 1);
BOOST_ASSERT
(
PR2set
.
size
()
==
1
);
auto
PR2
=
PR2set
.
front
();
BOOST_CHECK
(
PR2
[
"id"
].
as
<
int
>
()
==
2
);
// BOOST_CHECK(cat2.find(cif::v2::key("parent_id") == 1).size() == 1
);
auto
CR2set
=
cat1
.
get_children
(
PR2
,
cat2
);
// BOOST_CHECK(cat2.find(cif::v2::key("parent_id") == 10).size() == 2
);
BOOST_ASSERT
(
CR2set
.
size
()
==
3
);
// for (auto r : cat1.find(cif::v2::key("id") == 2))
std
::
vector
<
int
>
CRids
;
// {
std
::
transform
(
CR2set
.
begin
(),
CR2set
.
end
(),
std
::
back_inserter
(
CRids
),
[](
cif
::
v2
::
row_handle
r
)
// r["id"] = 20
;
{
return
r
[
"id"
].
as
<
int
>
();
})
;
// break
;
std
::
sort
(
CRids
.
begin
(),
CRids
.
end
())
;
// }
BOOST_CHECK
(
CRids
==
std
::
vector
<
int
>
({
4
,
5
,
6
}));
// BOOST_CHECK(cat1.size() == 4);
// check a rename in parent and child
// BOOST_CHECK(cat2.size() == 13);
// BOOST_CHECK(cat1.find(cif::v2::key("id") == 2).size() == 0);
for
(
auto
r
:
cat1
.
find
(
cif
::
v2
::
key
(
"id"
)
==
1
))
// BOOST_CHECK(cat1.find(cif::v2::key("id") == 20).size() == 1);
{
r
[
"id"
]
=
10
;
break
;
}
// BOOST_CHECK(cat2.find(cif::v2::key("parent_id") == 2).size() == 2
);
BOOST_CHECK
(
cat1
.
size
()
==
3
);
// BOOST_CHECK(cat2.find(cif::v2::key("parent_id") == 20).size() == 2
);
BOOST_CHECK
(
cat2
.
size
()
==
7
);
// for (auto r : cat1.find(cif::v2::key("id") == 3))
BOOST_CHECK
(
cat1
.
find
(
cif
::
v2
::
key
(
"id"
)
==
1
).
size
()
==
0
);
// {
BOOST_CHECK
(
cat1
.
find
(
cif
::
v2
::
key
(
"id"
)
==
10
).
size
()
==
1
);
// r["id"] = 30;
// break;
// }
// BOOST_CHECK(cat1.size() == 4);
BOOST_CHECK
(
cat2
.
find
(
cif
::
v2
::
key
(
"parent_id"
)
==
1
).
size
()
==
0
);
// BOOST_CHECK(cat2.size() == 13);
BOOST_CHECK
(
cat2
.
find
(
cif
::
v2
::
key
(
"parent_id2"
)
==
1
).
size
()
==
0
);
BOOST_CHECK
(
cat2
.
find
(
cif
::
v2
::
key
(
"parent_id3"
)
==
1
).
size
()
==
0
);
BOOST_CHECK
(
cat2
.
find
(
cif
::
v2
::
key
(
"parent_id"
)
==
10
).
size
()
==
1
);
BOOST_CHECK
(
cat2
.
find
(
cif
::
v2
::
key
(
"parent_id2"
)
==
10
).
size
()
==
1
);
BOOST_CHECK
(
cat2
.
find
(
cif
::
v2
::
key
(
"parent_id3"
)
==
10
).
size
()
==
1
);
// BOOST_CHECK(cat1.find(cif::v2::key("id") == 3).size() == 0);
for
(
auto
r
:
cat1
.
find
(
cif
::
v2
::
key
(
"id"
)
==
2
))
// BOOST_CHECK(cat1.find(cif::v2::key("id") == 30).size() == 1);
{
r
[
"id"
]
=
20
;
break
;
}
// BOOST_CHECK(cat2.find(cif::v2::key("parent_id") == 3).size() == 2
);
BOOST_CHECK
(
cat1
.
size
()
==
3
);
// BOOST_CHECK(cat2.find(cif::v2::key("parent_id") == 30).size() == 1
);
BOOST_CHECK
(
cat2
.
size
()
==
7
);
// for (auto r : cat1.find(cif::v2::key("id") == 4))
BOOST_CHECK
(
cat1
.
find
(
cif
::
v2
::
key
(
"id"
)
==
2
).
size
()
==
0
);
// {
BOOST_CHECK
(
cat1
.
find
(
cif
::
v2
::
key
(
"id"
)
==
20
).
size
()
==
1
);
// r["id"] = 40;
// break;
// }
// BOOST_CHECK(cat1.size() == 4);
BOOST_CHECK
(
cat2
.
find
(
cif
::
v2
::
key
(
"parent_id"
)
==
2
).
size
()
==
0
);
// BOOST_CHECK(cat2.size() == 13);
BOOST_CHECK
(
cat2
.
find
(
cif
::
v2
::
key
(
"parent_id2"
)
==
2
).
size
()
==
0
);
BOOST_CHECK
(
cat2
.
find
(
cif
::
v2
::
key
(
"parent_id3"
)
==
2
).
size
()
==
0
);
BOOST_CHECK
(
cat2
.
find
(
cif
::
v2
::
key
(
"parent_id"
)
==
20
).
size
()
==
2
);
BOOST_CHECK
(
cat2
.
find
(
cif
::
v2
::
key
(
"parent_id2"
)
==
20
).
size
()
==
2
);
BOOST_CHECK
(
cat2
.
find
(
cif
::
v2
::
key
(
"parent_id3"
)
==
20
).
size
()
==
2
);
// BOOST_CHECK(cat1.find(cif::v2::key("id") == 4).size() == 0);
for
(
auto
r
:
cat1
.
find
(
cif
::
v2
::
key
(
"id"
)
==
3
))
// BOOST_CHECK(cat1.find(cif::v2::key("id") == 10).size() == 1);
{
r
[
"id"
]
=
30
;
break
;
}
// BOOST_CHECK(cat2.find(cif::v2::key("parent_id") == 4).size() == 3);
BOOST_CHECK
(
cat1
.
size
()
==
3
);
// BOOST_CHECK(cat2.find(cif::v2::key("parent_id") == 40).size() == 0);
BOOST_CHECK
(
cat2
.
size
()
==
7
);
// }
// // --------------------------------------------------------------------
BOOST_CHECK
(
cat1
.
find
(
cif
::
v2
::
key
(
"id"
)
==
3
).
size
()
==
0
);
BOOST_CHECK
(
cat1
.
find
(
cif
::
v2
::
key
(
"id"
)
==
30
).
size
()
==
1
);
// BOOST_AUTO_TEST_CASE(d5)
BOOST_CHECK
(
cat2
.
find
(
cif
::
v2
::
key
(
"parent_id"
)
==
3
).
size
()
==
0
);
// {
BOOST_CHECK
(
cat2
.
find
(
cif
::
v2
::
key
(
"parent_id2"
)
==
3
).
size
()
==
0
);
// const char dict[] = R"(
BOOST_CHECK
(
cat2
.
find
(
cif
::
v2
::
key
(
"parent_id3"
)
==
3
).
size
()
==
0
);
// data_test_dict.dic
BOOST_CHECK
(
cat2
.
find
(
cif
::
v2
::
key
(
"parent_id"
)
==
30
).
size
()
==
1
);
// _datablock.id test_dict.dic
BOOST_CHECK
(
cat2
.
find
(
cif
::
v2
::
key
(
"parent_id2"
)
==
30
).
size
()
==
1
);
// _datablock.description
BOOST_CHECK
(
cat2
.
find
(
cif
::
v2
::
key
(
"parent_id3"
)
==
30
).
size
()
==
1
);
// ;
// 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_
// test delete
// _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_
cat1
.
erase
(
cif
::
v2
::
key
(
"id"
)
==
10
);
// _pdbx_item_linked_group.category_id
BOOST_CHECK
(
cat1
.
size
()
==
2
);
// _pdbx_item_linked_group.link_group_id
BOOST_CHECK
(
cat2
.
size
()
==
4
);
// _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);
cat1
.
erase
(
cif
::
v2
::
key
(
"id"
)
==
20
);
BOOST_CHECK
(
cat1
.
size
()
==
1
);
BOOST_CHECK
(
cat2
.
size
()
==
1
);
// cif::v2::validator validator("test", is_dict);
cat1
.
erase
(
cif
::
v2
::
key
(
"id"
)
==
30
);
BOOST_CHECK
(
cat1
.
size
()
==
0
);
BOOST_CHECK
(
cat2
.
size
()
==
0
);
}
// cif::File f;
// --------------------------------------------------------------------
// f.setValidator(&validator);
// // --------------------------------------------------------------------
BOOST_AUTO_TEST_CASE
(
c1
)
{
cif
::
VERBOSE
=
1
;
// const char data[] = R"(
auto
f
=
R"(data_TEST
// data_test
#
// loop_
loop_
// _cat_1.id
_test.id
// 1
_test.name
// 2
1 aap
// 3
2 noot
3 mies
4 .
5 ?
)"
_cf
;
// loop_
auto
&
db
=
f
.
front
();
// _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);
for
(
auto
r
:
db
[
"test"
].
find
(
cif
::
v2
::
key
(
"id"
)
==
1
))
// f.load(is_data);
{
const
auto
&
[
id
,
name
]
=
r
.
get
<
int
,
std
::
string
>
({
"id"
,
"name"
});
BOOST_CHECK
(
id
==
1
);
BOOST_CHECK
(
name
==
"aap"
);
}
// auto &cat1 = f.front()["cat_1"];
for
(
auto
r
:
db
[
"test"
].
find
(
cif
::
v2
::
key
(
"id"
)
==
4
))
// auto &cat2 = f.front()["cat_2"];
{
const
auto
&
[
id
,
name
]
=
r
.
get
<
int
,
std
::
string
>
({
"id"
,
"name"
});
BOOST_CHECK
(
id
==
4
);
BOOST_CHECK
(
name
.
empty
());
}
// // --------------------------------------------------------------------
for
(
auto
r
:
db
[
"test"
].
find
(
cif
::
v2
::
key
(
"id"
)
==
5
))
// // check iterate children
{
const
auto
&
[
id
,
name
]
=
r
.
get
<
int
,
std
::
string
>
({
"id"
,
"name"
});
BOOST_CHECK
(
id
==
5
);
BOOST_CHECK
(
name
.
empty
());
}
// auto PR2set = cat1.find(cif::v2::key("id") == 2);
// optional
// BOOST_ASSERT(PR2set.size() == 1);
// auto PR2 = PR2set.front();
// BOOST_CHECK(PR2["id"].as<int>() == 2);
// auto CR2set = cat1.getChildren(PR2, "cat_2");
for
(
auto
r
:
db
[
"test"
])
// BOOST_ASSERT(CR2set.size() == 3);
{
const
auto
&
[
id
,
name
]
=
r
.
get
<
int
,
std
::
optional
<
std
::
string
>>
({
"id"
,
"name"
});
switch
(
id
)
{
case
1
:
BOOST_CHECK
(
name
==
"aap"
);
break
;
case
2
:
BOOST_CHECK
(
name
==
"noot"
);
break
;
case
3
:
BOOST_CHECK
(
name
==
"mies"
);
break
;
case
4
:
case
5
:
BOOST_CHECK
(
not
name
);
break
;
default
:
BOOST_CHECK
(
false
);
}
}
}
// std::vector<int> CRids;
BOOST_AUTO_TEST_CASE
(
c2
)
// std::transform(CR2set.begin(), CR2set.end(), std::back_inserter(CRids), [](cif::Row r)
{
// { return r["id"].as<int>(); });
cif
::
VERBOSE
=
1
;
// std::sort(CRids.begin(), CRids.end());
// BOOST_CHECK(CRids == std::vector<int>({4, 5, 6}));
// // check a rename in parent and child
auto
f
=
R"(data_TEST
#
loop_
_test.id
_test.name
1 aap
2 noot
3 mies
4 .
5 ?
)"
_cf
;
// for (auto r : cat1.find(cif::v2::key("id") == 1))
auto
&
db
=
f
.
front
();
// {
// r["id"] = 10;
// break;
// }
// BOOST_CHECK(cat1.size() == 3);
// query tests
// BOOST_CHECK(cat2.size() == 7);
// BOOST_CHECK(cat1.find(cif::v2::key("id") == 1).size() == 0);
for
(
const
auto
&
[
id
,
name
]
:
db
[
"test"
].
rows
<
int
,
std
::
optional
<
std
::
string
>>
(
"id"
,
"name"
))
// BOOST_CHECK(cat1.find(cif::v2::key("id") == 10).size() == 1);
{
switch
(
id
)
{
case
1
:
BOOST_CHECK
(
name
==
"aap"
);
break
;
case
2
:
BOOST_CHECK
(
name
==
"noot"
);
break
;
case
3
:
BOOST_CHECK
(
name
==
"mies"
);
break
;
case
4
:
case
5
:
BOOST_CHECK
(
not
name
);
break
;
default
:
BOOST_CHECK
(
false
);
}
}
}
// BOOST_CHECK(cat2.find(cif::v2::key("parent_id") == 1).size() == 0);
BOOST_AUTO_TEST_CASE
(
c3
)
// BOOST_CHECK(cat2.find(cif::v2::key("parent_id2") == 1).size() == 0);
{
// BOOST_CHECK(cat2.find(cif::v2::key("parent_id3") == 1).size() == 0);
cif
::
VERBOSE
=
1
;
// BOOST_CHECK(cat2.find(cif::v2::key("parent_id") == 10).size() == 1);
// BOOST_CHECK(cat2.find(cif::v2::key("parent_id2") == 10).size() == 1);
// BOOST_CHECK(cat2.find(cif::v2::key("parent_id3") == 10).size() == 1);
// for (auto r : cat1.find(cif::v2::key("id") == 2))
auto
f
=
R"(data_TEST
// {
#
// r["id"] = 20;
loop_
// break;
_test.id
// }
_test.name
1 aap
2 noot
3 mies
4 .
5 ?
)"
_cf
;
// BOOST_CHECK(cat1.size() == 3);
auto
&
db
=
f
.
front
();
// BOOST_CHECK(cat2.size() == 7);
// BOOST_CHECK(cat1.find(cif::v2::key("id") == 2).size() == 0);
// query tests
// BOOST_CHECK(cat1.find(cif::v2::key("id") == 20).size() == 1);
for
(
const
auto
&
[
id
,
name
]
:
db
[
"test"
].
find
<
int
,
std
::
optional
<
std
::
string
>>
(
cif
::
v2
::
all
(),
"id"
,
"name"
))
{
switch
(
id
)
{
case
1
:
BOOST_CHECK
(
name
==
"aap"
);
break
;
case
2
:
BOOST_CHECK
(
name
==
"noot"
);
break
;
case
3
:
BOOST_CHECK
(
name
==
"mies"
);
break
;
case
4
:
case
5
:
BOOST_CHECK
(
not
name
);
break
;
default
:
BOOST_CHECK
(
false
);
}
}
// BOOST_CHECK(cat2.find(cif::v2::key("parent_id") == 2).size() == 0);
const
auto
&
[
id
,
name
]
=
db
[
"test"
].
find1
<
int
,
std
::
string
>
(
cif
::
v2
::
key
(
"id"
)
==
1
,
"id"
,
"name"
);
// BOOST_CHECK(cat2.find(cif::v2::key("parent_id2") == 2).size() == 0);
// BOOST_CHECK(cat2.find(cif::v2::key("parent_id3") == 2).size() == 0);
// BOOST_CHECK(cat2.find(cif::v2::key("parent_id") == 20).size() == 2);
// BOOST_CHECK(cat2.find(cif::v2::key("parent_id2") == 20).size() == 2);
// BOOST_CHECK(cat2.find(cif::v2::key("parent_id3") == 20).size() == 2);
// for (auto r : cat1.find(cif::v2::key("id") == 3))
BOOST_CHECK
(
id
==
1
);
// {
BOOST_CHECK
(
name
==
"aap"
);
// r["id"] = 30;
}
// break;
// }
//
BOOST_CHECK(cat1.size() == 3);
//
--------------------------------------------------------------------
//
BOOST_CHECK(cat2.size() == 7);
//
rename test
// BOOST_CHECK(cat1.find(cif::v2::key("id") == 3).size() == 0);
BOOST_AUTO_TEST_CASE
(
r1
)
// BOOST_CHECK(cat1.find(cif::v2::key("id") == 30).size() == 1);
{
/*
Rationale:
// BOOST_CHECK(cat2.find(cif::v2::key("parent_id") == 3).size() == 0);
The pdbx_mmcif dictionary contains inconsistent child-parent relations. E.g. atom_site is parent
// BOOST_CHECK(cat2.find(cif::v2::key("parent_id2") == 3).size() == 0);
of pdbx_nonpoly_scheme which itself is a parent of pdbx_entity_nonpoly. If I want to rename a residue
// BOOST_CHECK(cat2.find(cif::v2::key("parent_id3") == 3).size() == 0);
I cannot update pdbx_nonpoly_scheme since changing a parent changes children, but not vice versa.
// BOOST_CHECK(cat2.find(cif::v2::key("parent_id") == 30).size() == 1);
// BOOST_CHECK(cat2.find(cif::v2::key("parent_id2") == 30).size() == 1);
// BOOST_CHECK(cat2.find(cif::v2::key("parent_id3") == 30).size() == 1);
// // test delete
But if I change the comp_id in atom_site, the pdbx_nonpoly_scheme is update, that's good, and then
pdbx_entity_nonpoly is updated and that's bad.
// cat1.erase(cif::v2::key("id") == 10);
The idea is now that if we update a parent and a child that must change as well, we first check
// BOOST_CHECK(cat1.size() == 2);
if there are more parents of this child that will not change. In that case we have to split the
// BOOST_CHECK(cat2.size() == 4);
child into two, one with the new value and one with the old. We then of course have to split all
children of this split row that are direct children.
*/
// cat1.erase(cif::v2::key("id") == 20);
const
char
dict
[]
=
R"(
// BOOST_CHECK(cat1.size() == 1);
data_test_dict.dic
// BOOST_CHECK(cat2.size() == 1);
_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
// cat1.erase(cif::v2::key("id") == 30);
loop_
// BOOST_CHECK(cat1.size() == 0);
_item_type_list.code
// BOOST_CHECK(cat2.size() == 0);
_item_type_list.primitive_code
// }
_item_type_list.construct
code char
'[][_,.;:"&<>()/\{}'`~!@#$%A-Za-z0-9*|+-]*'
// // --------------------------------------------------------------------
text char
'[][ \n\t()_,.;:"&<>/\{}'`~!@#$%?+=*A-Za-z0-9|^-]*'
// BOOST_AUTO_TEST_CASE(c1)
int numb
// {
'[+-]?[0-9]+'
// cif::VERBOSE = 1;
// auto f = R"(data_TEST
save_cat_1
// #
_category.description 'A simple test category'
// loop_
_category.id cat_1
// _test.id
_category.mandatory_code no
// _test.name
_category_key.name '_cat_1.id'
// 1 aap
save_
// 2 noot
// 3 mies
// 4 .
// 5 ?
// )"_cf;
// auto &db = f.front();
// for (auto r : db["test"].find(cif::v2::key("id") == 1))
// {
// const auto &[id, name] = r.get<int, std::string>({"id", "name"});
// BOOST_CHECK(id == 1);
// BOOST_CHECK(name == "aap");
// }
// for (auto r : db["test"].find(cif::v2::key("id") == 4))
save__cat_1.id
// {
_item.name '_cat_1.id'
// const auto &[id, name] = r.get<int, std::string>({"id", "name"});
_item.category_id cat_1
// BOOST_CHECK(id == 4);
_item.mandatory_code yes
// BOOST_CHECK(name.empty());
_item_linked.child_name '_cat_2.parent_id'
// }
_item_linked.parent_name '_cat_1.id'
_item_type.code int
save_
// for (auto r : db["test"].find(cif::v2::key("id") == 5))
save__cat_1.name
// {
_item.name '_cat_1.name'
// const auto &[id, name] = r.get<int, std::string>({"id", "name"});
_item.category_id cat_1
// BOOST_CHECK(id == 5);
_item.mandatory_code yes
// BOOST_CHECK(name.empty());
_item_type.code code
// }
save_
// // optional
save__cat_1.desc
_item.name '_cat_1.desc'
_item.category_id cat_1
_item.mandatory_code yes
_item_type.code text
save_
// for (auto r : db["test"])
save_cat_2
// {
_category.description 'A second simple test category'
// const auto &[id, name] = r.get<int, std::optional<std::string>>({"id", "name"});
_category.id cat_2
// switch (id)
_category.mandatory_code no
// {
_category_key.name '_cat_2.id'
// case 1: BOOST_CHECK(name == "aap"); break;
save_
// case 2: BOOST_CHECK(name == "noot"); break;
// case 3: BOOST_CHECK(name == "mies"); break;
// case 4:
// case 5: BOOST_CHECK(not name); break;
// default:
// BOOST_CHECK(false);
// }
// }
// }
// BOOST_AUTO_TEST_CASE(c2)
save__cat_2.id
// {
_item.name '_cat_2.id'
// cif::VERBOSE = 1;
_item.category_id cat_2
_item.mandatory_code yes
_item_type.code int
save_
// auto f = R"(data_TEST
save__cat_2.name
// #
_item.name '_cat_2.name'
// loop_
_item.category_id cat_2
// _test.id
_item.mandatory_code yes
// _test.name
_item_type.code code
// 1 aap
save_
// 2 noot
// 3 mies
// 4 .
// 5 ?
// )"_cf;
// auto &db = f.front();
save__cat_2.num
_item.name '_cat_2.num'
_item.category_id cat_2
_item.mandatory_code yes
_item_type.code int
save_
// // query tests
save__cat_2.desc
_item.name '_cat_2.desc'
_item.category_id cat_2
_item.mandatory_code yes
_item_type.code text
save_
// for (const auto &[id, name] : db["test"].rows<int, std::optional<std::string>>("id", "name"))
save_cat_3
// {
_category.description 'A third simple test category'
// switch (id)
_category.id cat_3
// {
_category.mandatory_code no
// case 1: BOOST_CHECK(name == "aap"); break;
_category_key.name '_cat_3.id'
// case 2: BOOST_CHECK(name == "noot"); break;
save_
// case 3: BOOST_CHECK(name == "mies"); break;
// case 4:
// case 5: BOOST_CHECK(not name); break;
// default:
// BOOST_CHECK(false);
// }
// }
// }
// BOOST_AUTO_TEST_CASE(c3)
save__cat_3.id
// {
_item.name '_cat_3.id'
// cif::VERBOSE = 1;
_item.category_id cat_3
_item.mandatory_code yes
_item_type.code int
save_
// auto f = R"(data_TEST
save__cat_3.name
// #
_item.name '_cat_3.name'
// loop_
_item.category_id cat_3
// _test.id
_item.mandatory_code yes
// _test.name
_item_type.code code
// 1 aap
save_
// 2 noot
// 3 mies
// 4 .
// 5 ?
// )"_cf;
// auto &db = f.front();
// // query tests
// for (const auto &[id, name] : db["test"].find<int, std::optional<std::string>>(cif::All(), "id", "name"))
// {
// switch (id)
// {
// case 1: BOOST_CHECK(name == "aap"); break;
// case 2: BOOST_CHECK(name == "noot"); break;
// case 3: BOOST_CHECK(name == "mies"); break;
// case 4:
// case 5: BOOST_CHECK(not name); break;
// default:
// BOOST_CHECK(false);
// }
// }
// const auto &[id, name] = db["test"].find1<int, std::string>(cif::v2::key("id") == 1, "id", "name");
save__cat_3.num
_item.name '_cat_3.num'
_item.category_id cat_3
_item.mandatory_code yes
_item_type.code int
save_
// BOOST_CHECK(id == 1);
loop_
// BOOST_CHECK(name == "aap");
_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_1 1 '_cat_1.name' '_cat_2.name' cat_2
cat_2 1 '_cat_2.name' '_cat_3.name' cat_3
cat_2 1 '_cat_2.num' '_cat_3.num' cat_3
// // --------------------------------------------------------------------
)"
;
// // rename test
// BOOST_AUTO_TEST_CASE(r1)
struct
membuf
:
public
std
::
streambuf
// {
{
// /*
membuf
(
char
*
text
,
size_t
length
)
// Rationale:
{
this
->
setg
(
text
,
text
,
text
+
length
);
// The pdbx_mmcif dictionary contains inconsistent child-parent relations. E.g. atom_site is parent
}
// of pdbx_nonpoly_scheme which itself is a parent of pdbx_entity_nonpoly. If I want to rename a residue
}
buffer
(
const_cast
<
char
*>
(
dict
),
sizeof
(
dict
)
-
1
);
// I cannot update pdbx_nonpoly_scheme since changing a parent changes children, but not vice versa.
// But if I change the comp_id in atom_site, the pdbx_nonpoly_scheme is update, that's good, and then
// pdbx_entity_nonpoly is updated and that's bad.
// The idea is now that if we update a parent and a child that must change as well, we first check
// if there are more parents of this child that will not change. In that case we have to split the
// child into two, one with the new value and one with the old. We then of course have to split all
// children of this split row that are direct children.
// */
// 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.name
// _item.name '_cat_1.name'
// _item.category_id cat_1
// _item.mandatory_code yes
// _item_type.code code
// save_
// save__cat_1.desc
// _item.name '_cat_1.desc'
// _item.category_id cat_1
// _item.mandatory_code yes
// _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.name
// _item.name '_cat_2.name'
// _item.category_id cat_2
// _item.mandatory_code yes
// _item_type.code code
// save_
// save__cat_2.num
// _item.name '_cat_2.num'
// _item.category_id cat_2
// _item.mandatory_code yes
// _item_type.code int
// save_
// save__cat_2.desc
// _item.name '_cat_2.desc'
// _item.category_id cat_2
// _item.mandatory_code yes
// _item_type.code text
// save_
// save_cat_3
// _category.description 'A third simple test category'
// _category.id cat_3
// _category.mandatory_code no
// _category_key.name '_cat_3.id'
// save_
// save__cat_3.id
// _item.name '_cat_3.id'
// _item.category_id cat_3
// _item.mandatory_code yes
// _item_type.code int
// save_
// save__cat_3.name
// _item.name '_cat_3.name'
// _item.category_id cat_3
// _item.mandatory_code yes
// _item_type.code code
// save_
// save__cat_3.num
// _item.name '_cat_3.num'
// _item.category_id cat_3
// _item.mandatory_code yes
// _item_type.code int
// save_
// loop_
std
::
istream
is_dict
(
&
buffer
);
// _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_1 1 '_cat_1.name' '_cat_2.name' cat_2
// cat_2 1 '_cat_2.name' '_cat_3.name' cat_3
// cat_2 1 '_cat_2.num' '_cat_3.num' cat_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);
auto
validator
=
cif
::
v2
::
parse_dictionary
(
"test"
,
is_dict
);
// cif::v2::validator validator("test", is_dict);
// cif::F
ile f;
cif
::
v2
::
f
ile
f
;
// f.setV
alidator(&validator);
f
.
set_v
alidator
(
&
validator
);
//
// --------------------------------------------------------------------
// --------------------------------------------------------------------
//
const char data[] = R"(
const
char
data
[]
=
R"(
//
data_test
data_test
//
loop_
loop_
//
_cat_1.id
_cat_1.id
//
_cat_1.name
_cat_1.name
//
_cat_1.desc
_cat_1.desc
//
1 aap Aap
1 aap Aap
//
2 noot Noot
2 noot Noot
//
3 mies Mies
3 mies Mies
//
loop_
loop_
//
_cat_2.id
_cat_2.id
//
_cat_2.name
_cat_2.name
//
_cat_2.num
_cat_2.num
//
_cat_2.desc
_cat_2.desc
//
1 aap 1 'Een dier'
1 aap 1 'Een dier'
//
2 aap 2 'Een andere aap'
2 aap 2 'Een andere aap'
//
3 noot 1 'walnoot bijvoorbeeld'
3 noot 1 'walnoot bijvoorbeeld'
//
loop_
loop_
//
_cat_3.id
_cat_3.id
//
_cat_3.name
_cat_3.name
//
_cat_3.num
_cat_3.num
//
1 aap 1
1 aap 1
//
2 aap 2
2 aap 2
//
)";
)"
;
// using namespace cif
::literals;
using
namespace
cif
::
v2
::
literals
;
//
struct data_membuf : public std::streambuf
struct
data_membuf
:
public
std
::
streambuf
//
{
{
//
data_membuf(char *text, size_t length)
data_membuf
(
char
*
text
,
size_t
length
)
//
{
{
//
this->setg(text, text, text + length);
this
->
setg
(
text
,
text
,
text
+
length
);
//
}
}
//
} data_buffer(const_cast<char *>(data), sizeof(data) - 1);
}
data_buffer
(
const_cast
<
char
*>
(
data
),
sizeof
(
data
)
-
1
);
//
std::istream is_data(&data_buffer);
std
::
istream
is_data
(
&
data_buffer
);
//
f.load(is_data);
f
.
load
(
is_data
);
//
auto &cat1 = f.front()["cat_1"];
auto
&
cat1
=
f
.
front
()[
"cat_1"
];
//
auto &cat2 = f.front()["cat_2"];
auto
&
cat2
=
f
.
front
()[
"cat_2"
];
//
auto &cat3 = f.front()["cat_3"];
auto
&
cat3
=
f
.
front
()[
"cat_3"
];
//
cat3.update_value("name"_key == "aap" and "num"_key == 1, "name", "aapje");
cat3
.
update_value
(
"name"
_key
==
"aap"
and
"num"
_key
==
1
,
"name"
,
"aapje"
);
//
BOOST_CHECK(cat3.size() == 2);
BOOST_CHECK
(
cat3
.
size
()
==
2
);
//
{
{
//
int id, num;
int
id
,
num
;
//
std::string name;
std
::
string
name
;
// cif
::tie(id, name, num) = cat3.front().get("id", "name", "num");
cif
::
v2
::
tie
(
id
,
name
,
num
)
=
cat3
.
front
().
get
(
"id"
,
"name"
,
"num"
);
//
BOOST_CHECK(id == 1);
BOOST_CHECK
(
id
==
1
);
//
BOOST_CHECK(num == 1);
BOOST_CHECK
(
num
==
1
);
//
BOOST_CHECK(name == "aapje");
BOOST_CHECK
(
name
==
"aapje"
);
// cif
::tie(id, name, num) = cat3.back().get("id", "name", "num");
cif
::
v2
::
tie
(
id
,
name
,
num
)
=
cat3
.
back
().
get
(
"id"
,
"name"
,
"num"
);
//
BOOST_CHECK(id == 2);
BOOST_CHECK
(
id
==
2
);
//
BOOST_CHECK(num == 2);
BOOST_CHECK
(
num
==
2
);
//
BOOST_CHECK(name == "aap");
BOOST_CHECK
(
name
==
"aap"
);
//
}
}
//
int i = 0;
int
i
=
0
;
//
for (const auto &[id, name, num, desc] : cat2.rows<int, std::string, int, std::string>("id", "name", "num", "desc"))
for
(
const
auto
&
[
id
,
name
,
num
,
desc
]
:
cat2
.
rows
<
int
,
std
::
string
,
int
,
std
::
string
>
(
"id"
,
"name"
,
"num"
,
"desc"
))
//
{
{
//
switch (++i)
switch
(
++
i
)
//
{
{
//
case 1:
case
1
:
//
BOOST_CHECK(id == 1);
BOOST_CHECK
(
id
==
1
);
//
BOOST_CHECK(num == 1);
BOOST_CHECK
(
num
==
1
);
//
BOOST_CHECK(name == "aapje");
BOOST_CHECK
(
name
==
"aapje"
);
//
BOOST_CHECK(desc == "Een dier");
BOOST_CHECK
(
desc
==
"Een dier"
);
//
break;
break
;
//
case 2:
case
2
:
//
BOOST_CHECK(id == 2);
BOOST_CHECK
(
id
==
2
);
//
BOOST_CHECK(num == 2);
BOOST_CHECK
(
num
==
2
);
//
BOOST_CHECK(name == "aap");
BOOST_CHECK
(
name
==
"aap"
);
//
BOOST_CHECK(desc == "Een andere aap");
BOOST_CHECK
(
desc
==
"Een andere aap"
);
//
break;
break
;
//
case 3:
case
3
:
//
BOOST_CHECK(id == 3);
BOOST_CHECK
(
id
==
3
);
//
BOOST_CHECK(num == 1);
BOOST_CHECK
(
num
==
1
);
//
BOOST_CHECK(name == "noot");
BOOST_CHECK
(
name
==
"noot"
);
//
BOOST_CHECK(desc == "walnoot bijvoorbeeld");
BOOST_CHECK
(
desc
==
"walnoot bijvoorbeeld"
);
//
break;
break
;
//
default:
default
:
//
BOOST_FAIL("Unexpected record");
BOOST_FAIL
(
"Unexpected record"
);
//
}
}
//
}
}
//
BOOST_CHECK(cat1.size() == 4);
BOOST_CHECK
(
cat1
.
size
()
==
4
);
//
i = 0;
i
=
0
;
//
for (const auto &[id, name, desc] : cat1.rows<int, std::string, std::string>("id", "name", "desc"))
for
(
const
auto
&
[
id
,
name
,
desc
]
:
cat1
.
rows
<
int
,
std
::
string
,
std
::
string
>
(
"id"
,
"name"
,
"desc"
))
//
{
{
//
switch (++i)
switch
(
++
i
)
//
{
{
//
case 1:
case
1
:
//
BOOST_CHECK(id == 1);
BOOST_CHECK
(
id
==
1
);
//
BOOST_CHECK(name == "aapje");
BOOST_CHECK
(
name
==
"aapje"
);
//
BOOST_CHECK(desc == "Aap");
BOOST_CHECK
(
desc
==
"Aap"
);
//
break;
break
;
//
case 2:
case
2
:
//
BOOST_CHECK(id == 2);
BOOST_CHECK
(
id
==
2
);
//
BOOST_CHECK(name == "noot");
BOOST_CHECK
(
name
==
"noot"
);
//
BOOST_CHECK(desc == "Noot");
BOOST_CHECK
(
desc
==
"Noot"
);
//
break;
break
;
//
case 3:
case
3
:
//
BOOST_CHECK(id == 3);
BOOST_CHECK
(
id
==
3
);
//
BOOST_CHECK(name == "mies");
BOOST_CHECK
(
name
==
"mies"
);
//
BOOST_CHECK(desc == "Mies");
BOOST_CHECK
(
desc
==
"Mies"
);
//
break;
break
;
//
case 4:
case
4
:
//
BOOST_CHECK(id == 4);
BOOST_CHECK
(
id
==
4
);
//
BOOST_CHECK(name == "aap");
BOOST_CHECK
(
name
==
"aap"
);
//
BOOST_CHECK(desc == "Aap");
BOOST_CHECK
(
desc
==
"Aap"
);
//
break;
break
;
//
default:
default
:
//
BOOST_FAIL("Unexpected record");
BOOST_FAIL
(
"Unexpected record"
);
//
}
}
//
}
}
//
f.save(std::cout);
//
f.save(std::cout);
//
}
}
//
//
--------------------------------------------------------------------
// --------------------------------------------------------------------
// BOOST_AUTO_TEST_CASE(bondmap_1)
// BOOST_AUTO_TEST_CASE(bondmap_1)
// {
// {
...
@@ -1929,195 +1930,101 @@ _cat_2.desc
...
@@ -1929,195 +1930,101 @@ _cat_2.desc
// BOOST_CHECK(mmcif::BondMap::atomIDsForCompound("UN_").empty() == false);
// BOOST_CHECK(mmcif::BondMap::atomIDsForCompound("UN_").empty() == false);
// }
// }
// BOOST_AUTO_TEST_CASE(reading_file_1)
BOOST_AUTO_TEST_CASE
(
reading_file_1
)
// {
{
// std::istringstream is("Hello, world!");
std
::
istringstream
is
(
"Hello, world!"
);
// cif::File file;
// BOOST_CHECK_THROW(file.load(is), std::runtime_error);
// }
// // 3d tests
// using namespace mmcif;
// BOOST_AUTO_TEST_CASE(t1)
// {
// // std::random_device rnd;
// // std::mt19937 gen(rnd());
// // std::uniform_real_distribution<float> dis(0, 1);
// // Quaternion q{ dis(gen), dis(gen), dis(gen), dis(gen) };
// // q = Normalize(q);
// // Quaternion q{ 0.1, 0.2, 0.3, 0.4 };
// Quaternion q{0.5, 0.5, 0.5, 0.5};
// q = Normalize(q);
// const auto &&[angle0, axis0] = QuaternionToAngleAxis(q);
// std::vector<Point> p1{
// {16.979, 13.301, 44.555},
// {18.150, 13.525, 43.680},
// {18.656, 14.966, 43.784},
// {17.890, 15.889, 44.078},
// {17.678, 13.270, 42.255},
// {16.248, 13.734, 42.347},
// {15.762, 13.216, 43.724}};
// auto p2 = p1;
// CenterPoints(p1);
// for (auto &p : p2)
// p.rotate(q);
// CenterPoints(p2);
// auto q2 = AlignPoints(p1, p2);
// const auto &&[angle, axis] = QuaternionToAngleAxis(q2);
// BOOST_TEST(std::fmod(360 + angle, 360) == std::fmod(360 - angle0, 360), tt::tolerance(0.01));
// for (auto &p : p1)
// p.rotate(q2);
// float rmsd = RMSd(p1, p2);
// BOOST_TEST(rmsd < 1e-5);
// // std::cout << "rmsd: " << RMSd(p1, p2) << std::endl;
// }
// BOOST_AUTO_TEST_CASE(t2)
// {
// Point p[] = {
// { 1, 1, 0 },
// { 2, 1, 0 },
// { 1, 2, 0 }
// };
// Point xp = mmcif::CrossProduct(p[1] - p[0], p[2] - p[0]);
// Quaternion q = ConstructFromAngleAxis(45, xp); //mmcif::Normalize(Quaternion{45 * mmcif::kPI / 180, xp.mX, xp.mY, xp.mZ});
// auto &&[angle, axis] = mmcif::QuaternionToAngleAxis(q);
// BOOST_TEST(angle == 45, tt::tolerance(0.01));
// }
// BOOST_AUTO_TEST_CASE(t3)
// {
// Point p[] = {
// { 1, 1, 0 },
// { 2, 1, 0 },
// { 1, 2, 0 }
// };
// Point xp = mmcif::CrossProduct(p[1] - p[0], p[2] - p[0]);
// Quaternion q = ConstructFromAngleAxis(45, xp); //mmcif::Normalize(Quaternion{45 * mmcif::kPI / 180, xp.mX, xp.mY, xp.mZ});
// Point v = p[1];
// v -= p[0];
// v.rotate(q);
// v += p[0];
// std::cout << v << std::endl;
// double a = mmcif::Angle(v, p[0], p[1]);
// BOOST_TEST(a == 45, tt::tolerance(0.01));
// }
// BOOST_AUTO_TEST_CASE(parser_test_1)
// {
// auto data1 = R"(
// data_QM
// _test.text ??
// )"_cf;
// auto &db1 = data1.front();
cif
::
v2
::
file
file
;
// auto &test1 = db1["test"];
BOOST_CHECK_THROW
(
file
.
load
(
is
),
std
::
runtime_error
);
}
// BOOST_CHECK_EQUAL(test1.size(), 1);
BOOST_AUTO_TEST_CASE
(
parser_test_1
)
{
auto
data1
=
R"(
data_QM
_test.text ??
)"
_cf
;
// for (auto r : test1)
auto
&
db1
=
data1
.
front
();
// {
auto
&
test1
=
db1
[
"test"
];
// const auto &[text] = r.get<std::string>({"text"});
// BOOST_CHECK_EQUAL(text, "??");
// }
// std::stringstream ss;
BOOST_CHECK_EQUAL
(
test1
.
size
(),
1
);
// data1.save(ss);
// auto data2 = cif::File(ss);
for
(
auto
r
:
test1
)
{
const
auto
&
[
text
]
=
r
.
get
<
std
::
string
>
({
"text"
});
BOOST_CHECK_EQUAL
(
text
,
"??"
);
}
// auto &db2 = data2.front()
;
std
::
stringstream
ss
;
// auto &test2 = db2["test"]
;
data1
.
save
(
ss
)
;
// BOOST_CHECK_EQUAL(test2.size(), 1
);
auto
data2
=
cif
::
File
(
ss
);
// for (auto r : test2)
auto
&
db2
=
data2
.
front
();
// {
auto
&
test2
=
db2
[
"test"
];
// const auto &[text] = r.get<std::string>({"text"});
// BOOST_CHECK_EQUAL(text, "??");
// }
// }
// BOOST_AUTO_TEST_CASE(output_test_1)
BOOST_CHECK_EQUAL
(
test2
.
size
(),
1
);
// {
// auto data1 = R"(
// data_Q
// loop_
// _test.text
// "stop_the_crap"
// 'and stop_ this too'
// 'data_dinges'
// 'blablaglobal_bla'
// boo.data_.whatever
// )"_cf;
// auto &db1 = data1.front();
for
(
auto
r
:
test2
)
// auto &test1 = db1["test"];
{
const
auto
&
[
text
]
=
r
.
get
<
std
::
string
>
({
"text"
});
BOOST_CHECK_EQUAL
(
text
,
"??"
);
}
}
// struct T {
BOOST_AUTO_TEST_CASE
(
output_test_1
)
// const char *s;
{
// bool q;
auto
data1
=
R"(
// } kS[] = {
data_Q
// { "stop_the_crap", false },
loop_
// { "and stop_ this too", false },
_test.text
// { "data_dinges", false },
"stop_the_crap"
// { "blablaglobal_bla", false },
'and stop_ this too'
// { "boo.data_.whatever", true }
'data_dinges'
// };
'blablaglobal_bla'
boo.data_.whatever
)"
_cf
;
auto
&
db1
=
data1
.
front
();
auto
&
test1
=
db1
[
"test"
];
struct
T
{
const
char
*
s
;
bool
q
;
}
kS
[]
=
{
{
"stop_the_crap"
,
false
},
{
"and stop_ this too"
,
false
},
{
"data_dinges"
,
false
},
{
"blablaglobal_bla"
,
false
},
{
"boo.data_.whatever"
,
true
}
};
//
BOOST_CHECK_EQUAL(test1.size(), sizeof(kS) / sizeof(T));
BOOST_CHECK_EQUAL
(
test1
.
size
(),
sizeof
(
kS
)
/
sizeof
(
T
));
//
size_t i = 0;
size_t
i
=
0
;
//
for (auto r : test1)
for
(
auto
r
:
test1
)
//
{
{
//
const auto &[text] = r.get<std::string>({"text"});
const
auto
&
[
text
]
=
r
.
get
<
std
::
string
>
({
"text"
});
//
BOOST_CHECK_EQUAL(text, kS[i].s);
BOOST_CHECK_EQUAL
(
text
,
kS
[
i
].
s
);
//
BOOST_CHECK_EQUAL(cif::isUnquotedString(kS[i].s), kS[i].q);
BOOST_CHECK_EQUAL
(
cif
::
isUnquotedString
(
kS
[
i
].
s
),
kS
[
i
].
q
);
//
++i;
++
i
;
//
}
}
//
std::stringstream ss;
std
::
stringstream
ss
;
//
data1.save(ss);
data1
.
save
(
ss
);
//
auto data2 = cif::File(ss);
auto
data2
=
cif
::
File
(
ss
);
//
auto &db2 = data2.front();
auto
&
db2
=
data2
.
front
();
//
auto &test2 = db2["test"];
auto
&
test2
=
db2
[
"test"
];
//
BOOST_CHECK_EQUAL(test2.size(), sizeof(kS) / sizeof(T));
BOOST_CHECK_EQUAL
(
test2
.
size
(),
sizeof
(
kS
)
/
sizeof
(
T
));
//
i = 0;
i
=
0
;
//
for (auto r : test2)
for
(
auto
r
:
test2
)
//
{
{
//
const auto &[text] = r.get<std::string>({"text"});
const
auto
&
[
text
]
=
r
.
get
<
std
::
string
>
({
"text"
});
//
BOOST_CHECK_EQUAL(text, kS[i++].s);
BOOST_CHECK_EQUAL
(
text
,
kS
[
i
++
].
s
);
//
}
}
//
}
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment