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
345c4778
Unverified
Commit
345c4778
authored
Sep 08, 2021
by
Maarten L. Hekkelman
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
start structure-test unit test
added compare functions for Datablock and Category
parent
0ccb2f88
Hide whitespace changes
Inline
Side-by-side
Showing
7 changed files
with
2083 additions
and
1608 deletions
+2083
-1608
CMakeLists.txt
+1
-0
include/cif++/Cif++.hpp
+859
-745
include/cif++/Structure.hpp
+22
-7
src/Cif++.cpp
+998
-812
src/Structure.cpp
+78
-42
test/structure-test.cpp
+124
-0
test/unit-test.cpp
+1
-2
No files found.
CMakeLists.txt
View file @
345c4778
...
...
@@ -385,6 +385,7 @@ if(CIFPP_BUILD_TESTS)
list
(
APPEND CIFPP_tests
# pdb2cif
rename-compound
structure
unit
)
foreach
(
CIFPP_TEST IN LISTS CIFPP_tests
)
...
...
include/cif++/Cif++.hpp
View file @
345c4778
...
...
@@ -28,14 +28,14 @@
#include <string>
#include <regex>
#include <array>
#include <functional>
#include <iostream>
#include <sstream>
#include <set>
#include <list>
#include <array>
#include <optional>
#include <functional>
#include <regex>
#include <set>
#include <sstream>
#include "cif++/CifUtils.hpp"
...
...
@@ -117,7 +117,7 @@ CIFPP_EXPORT extern int VERBOSE;
class
File
;
class
Datablock
;
class
Category
;
class
Row
;
// a flyweight class that references data in categories
class
Row
;
// a flyweight class that references data in categories
class
RowSet
;
class
Item
;
class
Validator
;
...
...
@@ -141,43 +141,57 @@ class Item
public
:
Item
()
{}
template
<
typename
T
,
std
::
enable_if_t
<
std
::
is_arithmetic_v
<
T
>
,
int
>
=
0
>
Item
(
const
std
::
string
&
name
,
const
T
&
value
)
:
mName
(
name
),
mValue
(
std
::
to_string
(
value
))
{}
template
<
typename
T
,
std
::
enable_if_t
<
std
::
is_arithmetic_v
<
T
>
,
int
>
=
0
>
Item
(
const
std
::
string
&
name
,
const
T
&
value
)
:
mName
(
name
)
,
mValue
(
std
::
to_string
(
value
))
{
}
Item
(
const
std
::
string
&
name
,
const
std
::
string
&
value
)
:
mName
(
name
),
mValue
(
value
)
{}
Item
(
const
std
::
string
&
name
,
const
std
::
string
&
value
)
:
mName
(
name
)
,
mValue
(
value
)
{
}
Item
(
const
Item
&
rhs
)
:
mName
(
rhs
.
mName
),
mValue
(
rhs
.
mValue
)
{}
Item
(
Item
&&
rhs
)
noexcept
:
mName
(
std
::
move
(
rhs
.
mName
)),
mValue
(
std
::
move
(
rhs
.
mValue
))
{}
Item
(
const
Item
&
rhs
)
:
mName
(
rhs
.
mName
)
,
mValue
(
rhs
.
mValue
)
{
}
Item
(
Item
&&
rhs
)
noexcept
:
mName
(
std
::
move
(
rhs
.
mName
))
,
mValue
(
std
::
move
(
rhs
.
mValue
))
{
}
Item
&
operator
=
(
const
Item
&
rhs
)
Item
&
operator
=
(
const
Item
&
rhs
)
{
if
(
this
!=
&
rhs
)
{
mName
=
rhs
.
mName
;
mValue
=
rhs
.
mValue
;
}
return
*
this
;
}
Item
&
operator
=
(
Item
&&
rhs
)
noexcept
Item
&
operator
=
(
Item
&&
rhs
)
noexcept
{
if
(
this
!=
&
rhs
)
{
mName
=
std
::
move
(
rhs
.
mName
);
mValue
=
std
::
move
(
rhs
.
mValue
);
}
return
*
this
;
}
const
std
::
string
&
name
()
const
{
return
mName
;
}
const
std
::
string
&
value
()
const
{
return
mValue
;
}
void
value
(
const
std
::
string
&
v
)
{
mValue
=
v
;
}
const
std
::
string
&
name
()
const
{
return
mName
;
}
const
std
::
string
&
value
()
const
{
return
mValue
;
}
void
value
(
const
std
::
string
&
v
)
{
mValue
=
v
;
}
// empty means either null or unknown
bool
empty
()
const
;
...
...
@@ -187,12 +201,12 @@ class Item
// is_unknown means the field contains '?'
bool
is_unknown
()
const
;
size_t
length
()
const
{
return
mValue
.
length
();
}
const
char
*
c_str
()
const
{
return
mValue
.
c_str
();
}
size_t
length
()
const
{
return
mValue
.
length
();
}
const
char
*
c_str
()
const
{
return
mValue
.
c_str
();
}
private
:
std
::
string
mName
;
std
::
string
mValue
;
std
::
string
mName
;
std
::
string
mValue
;
};
// --------------------------------------------------------------------
...
...
@@ -202,54 +216,58 @@ class Datablock
{
public
:
friend
class
File
;
using
CategoryList
=
std
::
list
<
Category
>
;
using
iterator
=
CategoryList
::
iterator
;
using
const_iterator
=
CategoryList
::
const_iterator
;
Datablock
(
const
std
::
string
&
name
);
Datablock
(
const
std
::
string
&
name
);
~
Datablock
();
Datablock
(
const
Datablock
&
)
=
delete
;
Datablock
&
operator
=
(
const
Datablock
&
)
=
delete
;
Datablock
(
const
Datablock
&
)
=
delete
;
Datablock
&
operator
=
(
const
Datablock
&
)
=
delete
;
std
::
string
getName
()
const
{
return
mName
;
}
void
setName
(
const
std
::
string
&
n
)
{
mName
=
n
;
}
std
::
string
firstItem
(
const
std
::
string
&
tag
)
const
;
std
::
string
getName
()
const
{
return
mName
;
}
void
setName
(
const
std
::
string
&
n
)
{
mName
=
n
;
}
iterator
begin
()
{
return
mCategories
.
begin
();
}
iterator
end
()
{
return
mCategories
.
end
();
}
std
::
string
firstItem
(
const
std
::
string
&
tag
)
const
;
const_iterator
begin
()
const
{
return
mCategories
.
begin
();
}
const_iterator
end
()
const
{
return
mCategories
.
end
();
}
iterator
begin
()
{
return
mCategories
.
begin
();
}
iterator
end
()
{
return
mCategories
.
end
();
}
Category
&
operator
[](
const
std
::
string
&
name
);
const_iterator
begin
()
const
{
return
mCategories
.
begin
();
}
const_iterator
end
()
const
{
return
mCategories
.
end
();
}
Category
&
operator
[](
const
std
::
string
&
name
);
std
::
tuple
<
iterator
,
bool
>
emplace
(
const
std
::
string
&
name
);
std
::
tuple
<
iterator
,
bool
>
emplace
(
const
std
::
string
&
name
);
bool
isValid
();
void
validateLinks
()
const
;
void
setValidator
(
Validator
*
v
);
void
setValidator
(
Validator
*
v
);
// this one only looks up a Category, returns nullptr if it does not exist
Category
*
get
(
const
std
::
string
&
name
);
const
Category
*
get
(
const
std
::
string
&
name
)
const
;
Category
*
get
(
const
std
::
string
&
name
);
void
getTagOrder
(
std
::
vector
<
std
::
string
>
&
tags
)
const
;
void
write
(
std
::
ostream
&
os
,
const
std
::
vector
<
std
::
string
>
&
order
);
void
write
(
std
::
ostream
&
os
);
void
getTagOrder
(
std
::
vector
<
std
::
string
>&
tags
)
const
;
void
write
(
std
::
ostream
&
os
,
const
std
::
vector
<
std
::
string
>&
order
);
void
write
(
std
::
ostream
&
os
);
// convenience function, add a line to the software category
void
add_software
(
const
std
::
string
&
name
,
const
std
::
string
&
classification
,
const
std
::
string
&
versionNr
,
const
std
::
string
&
versionDate
);
void
add_software
(
const
std
::
string
&
name
,
const
std
::
string
&
classification
,
const
std
::
string
&
versionNr
,
const
std
::
string
&
versionDate
);
private
:
friend
bool
operator
==
(
const
Datablock
&
lhs
,
const
Datablock
&
rhs
);
std
::
list
<
Category
>
mCategories
;
std
::
string
mName
;
Validator
*
mValidator
;
Datablock
*
mNext
;
friend
std
::
ostream
&
operator
<<
(
std
::
ostream
&
os
,
const
Datablock
&
data
);
private
:
std
::
list
<
Category
>
mCategories
;
std
::
string
mName
;
Validator
*
mValidator
;
Datablock
*
mNext
;
};
// --------------------------------------------------------------------
...
...
@@ -264,20 +282,19 @@ namespace detail
class
ItemReference
{
public
:
// conversion helper class
template
<
typename
T
,
typename
=
void
>
template
<
typename
T
,
typename
=
void
>
struct
item_value_as
;
template
<
typename
T
,
std
::
enable_if_t
<
std
::
is_arithmetic_v
<
T
>
,
int
>
=
0
>
ItemReference
&
operator
=
(
const
T
&
value
)
template
<
typename
T
,
std
::
enable_if_t
<
std
::
is_arithmetic_v
<
T
>
,
int
>
=
0
>
ItemReference
&
operator
=
(
const
T
&
value
)
{
this
->
operator
=
(
std
::
to_string
(
value
));
return
*
this
;
}
template
<
typename
T
>
ItemReference
&
operator
=
(
const
std
::
optional
<
T
>&
value
)
template
<
typename
T
>
ItemReference
&
operator
=
(
const
std
::
optional
<
T
>
&
value
)
{
if
(
value
)
this
->
operator
=
(
*
value
);
...
...
@@ -286,34 +303,34 @@ namespace detail
return
*
this
;
}
ItemReference
&
operator
=
(
const
std
::
string
&
value
);
ItemReference
&
operator
=
(
const
std
::
string
&
value
);
template
<
typename
...
Ts
>
void
os
(
const
Ts
&
...
v
)
template
<
typename
...
Ts
>
void
os
(
const
Ts
&
...
v
)
{
std
::
ostringstream
ss
;
((
ss
<<
v
),
...);
this
->
operator
=
(
ss
.
str
());
}
void
swap
(
ItemReference
&
b
);
template
<
typename
T
=
std
::
string
>
void
swap
(
ItemReference
&
b
);
template
<
typename
T
=
std
::
string
>
auto
as
()
const
{
using
value_type
=
std
::
remove_cv_t
<
std
::
remove_reference_t
<
T
>>
;
return
item_value_as
<
value_type
>::
convert
(
*
this
);
}
template
<
typename
T
>
int
compare
(
const
T
&
value
,
bool
icase
)
const
template
<
typename
T
>
int
compare
(
const
T
&
value
,
bool
icase
)
const
{
return
item_value_as
<
T
>::
compare
(
*
this
,
value
,
icase
);
}
// empty means either null or unknown
bool
empty
()
const
;
explicit
operator
bool
()
const
{
return
not
empty
();
}
explicit
operator
bool
()
const
{
return
not
empty
();
}
// is_null means the field contains '.'
bool
is_null
()
const
;
...
...
@@ -321,36 +338,45 @@ namespace detail
// is_unknown means the field contains '?'
bool
is_unknown
()
const
;
const
char
*
c_str
()
const
;
const
char
*
c_str
()
const
;
// the following returns the defaultValue from either the parameter
// or, if specified, the value from _item_default.value in the dictionary
const
char
*
c_str
(
const
char
*
defaultValue
)
const
;
bool
operator
!=
(
const
std
::
string
&
s
)
const
{
return
s
!=
c_str
();
}
bool
operator
==
(
const
std
::
string
&
s
)
const
{
return
s
==
c_str
();
}
const
char
*
c_str
(
const
char
*
defaultValue
)
const
;
bool
operator
!=
(
const
std
::
string
&
s
)
const
{
return
s
!=
c_str
();
}
bool
operator
==
(
const
std
::
string
&
s
)
const
{
return
s
==
c_str
();
}
private
:
private
:
friend
class
::
cif
::
Row
;
ItemReference
(
const
char
*
name
,
size_t
column
,
Row
&
row
)
:
mName
(
name
),
mColumn
(
column
),
mRow
(
row
)
{}
ItemReference
(
const
char
*
name
,
size_t
column
,
Row
&
row
)
:
mName
(
name
)
,
mColumn
(
column
)
,
mRow
(
row
)
{
}
ItemReference
(
const
char
*
name
,
size_t
column
,
const
Row
&
row
)
:
mName
(
name
),
mColumn
(
column
),
mRow
(
const_cast
<
Row
&>
(
row
)),
mConst
(
true
)
{}
ItemReference
(
const
char
*
name
,
size_t
column
,
const
Row
&
row
)
:
mName
(
name
)
,
mColumn
(
column
)
,
mRow
(
const_cast
<
Row
&>
(
row
))
,
mConst
(
true
)
{
}
const
char
*
mName
;
size_t
mColumn
;
Row
&
mRow
;
bool
mConst
=
false
;
const
char
*
mName
;
size_t
mColumn
;
Row
&
mRow
;
bool
mConst
=
false
;
};
template
<
typename
T
>
template
<
typename
T
>
struct
ItemReference
::
item_value_as
<
T
,
std
::
enable_if_t
<
std
::
is_floating_point_v
<
T
>>>
{
using
value_type
=
std
::
remove_reference_t
<
std
::
remove_cv_t
<
T
>>
;
static
value_type
convert
(
const
ItemReference
&
ref
)
static
value_type
convert
(
const
ItemReference
&
ref
)
{
value_type
result
=
{};
if
(
not
ref
.
empty
())
...
...
@@ -358,11 +384,11 @@ namespace detail
return
result
;
}
static
int
compare
(
const
ItemReference
&
ref
,
double
value
,
bool
icase
)
static
int
compare
(
const
ItemReference
&
ref
,
double
value
,
bool
icase
)
{
int
result
=
0
;
const
char
*
s
=
ref
.
c_str
();
const
char
*
s
=
ref
.
c_str
();
if
(
s
==
nullptr
or
*
s
==
0
)
result
=
1
;
...
...
@@ -383,15 +409,15 @@ namespace detail
result
=
1
;
}
}
return
result
;
}
};
template
<
typename
T
>
template
<
typename
T
>
struct
ItemReference
::
item_value_as
<
T
,
std
::
enable_if_t
<
std
::
is_integral_v
<
T
>
and
std
::
is_unsigned_v
<
T
>
and
not
std
::
is_same_v
<
T
,
bool
>>>
{
static
T
convert
(
const
ItemReference
&
ref
)
static
T
convert
(
const
ItemReference
&
ref
)
{
T
result
=
{};
if
(
not
ref
.
empty
())
...
...
@@ -399,11 +425,11 @@ namespace detail
return
result
;
}
static
int
compare
(
const
ItemReference
&
ref
,
unsigned
long
value
,
bool
icase
)
static
int
compare
(
const
ItemReference
&
ref
,
unsigned
long
value
,
bool
icase
)
{
int
result
=
0
;
const
char
*
s
=
ref
.
c_str
();
const
char
*
s
=
ref
.
c_str
();
if
(
s
==
nullptr
or
*
s
==
0
)
result
=
1
;
...
...
@@ -424,15 +450,15 @@ namespace detail
result
=
1
;
}
}
return
result
;
}
};
template
<
typename
T
>
template
<
typename
T
>
struct
ItemReference
::
item_value_as
<
T
,
std
::
enable_if_t
<
std
::
is_integral_v
<
T
>
and
std
::
is_signed_v
<
T
>
and
not
std
::
is_same_v
<
T
,
bool
>>>
{
static
T
convert
(
const
ItemReference
&
ref
)
static
T
convert
(
const
ItemReference
&
ref
)
{
T
result
=
{};
if
(
not
ref
.
empty
())
...
...
@@ -440,11 +466,11 @@ namespace detail
return
result
;
}
static
int
compare
(
const
ItemReference
&
ref
,
long
value
,
bool
icase
)
static
int
compare
(
const
ItemReference
&
ref
,
long
value
,
bool
icase
)
{
int
result
=
0
;
const
char
*
s
=
ref
.
c_str
();
const
char
*
s
=
ref
.
c_str
();
if
(
s
==
nullptr
or
*
s
==
0
)
result
=
1
;
...
...
@@ -465,15 +491,15 @@ namespace detail
result
=
1
;
}
}
return
result
;
}
};
template
<
typename
T
>
template
<
typename
T
>
struct
ItemReference
::
item_value_as
<
std
::
optional
<
T
>>
{
static
std
::
optional
<
T
>
convert
(
const
ItemReference
&
ref
)
static
std
::
optional
<
T
>
convert
(
const
ItemReference
&
ref
)
{
std
::
optional
<
T
>
result
;
if
(
ref
)
...
...
@@ -481,11 +507,11 @@ namespace detail
return
result
;
}
static
int
compare
(
const
ItemReference
&
ref
,
std
::
optional
<
T
>
value
,
bool
icase
)
static
int
compare
(
const
ItemReference
&
ref
,
std
::
optional
<
T
>
value
,
bool
icase
)
{
if
(
ref
.
empty
()
and
not
value
)
return
0
;
if
(
ref
.
empty
())
return
-
1
;
else
if
(
not
value
)
...
...
@@ -495,10 +521,10 @@ namespace detail
}
};
template
<
typename
T
>
template
<
typename
T
>
struct
ItemReference
::
item_value_as
<
T
,
std
::
enable_if_t
<
std
::
is_same_v
<
T
,
bool
>>>
{
static
bool
convert
(
const
ItemReference
&
ref
)
static
bool
convert
(
const
ItemReference
&
ref
)
{
bool
result
=
false
;
if
(
not
ref
.
empty
())
...
...
@@ -506,110 +532,114 @@ namespace detail
return
result
;
}
static
int
compare
(
const
ItemReference
&
ref
,
bool
value
,
bool
icase
)
static
int
compare
(
const
ItemReference
&
ref
,
bool
value
,
bool
icase
)
{
bool
rv
=
convert
(
ref
);
return
value
&&
rv
?
0
:
(
rv
<
value
?
-
1
:
1
);
:
(
rv
<
value
?
-
1
:
1
);
}
};
template
<
size_t
N
>
template
<
size_t
N
>
struct
ItemReference
::
item_value_as
<
char
[
N
]
>
{
static
int
compare
(
const
ItemReference
&
ref
,
const
char
(
&
value
)[
N
],
bool
icase
)
static
int
compare
(
const
ItemReference
&
ref
,
const
char
(
&
value
)[
N
],
bool
icase
)
{
return
icase
?
cif
::
icompare
(
ref
.
c_str
(),
value
)
:
std
::
strcmp
(
ref
.
c_str
(),
value
);
}
};
template
<>
struct
ItemReference
::
item_value_as
<
const
char
*>
template
<>
struct
ItemReference
::
item_value_as
<
const
char
*>
{
static
const
char
*
convert
(
const
ItemReference
&
ref
)
static
const
char
*
convert
(
const
ItemReference
&
ref
)
{
return
ref
.
c_str
();
}
static
int
compare
(
const
ItemReference
&
ref
,
const
char
*
value
,
bool
icase
)
static
int
compare
(
const
ItemReference
&
ref
,
const
char
*
value
,
bool
icase
)
{
return
icase
?
cif
::
icompare
(
ref
.
c_str
(),
value
)
:
std
::
strcmp
(
ref
.
c_str
(),
value
);
}
};
template
<>
template
<>
struct
ItemReference
::
item_value_as
<
std
::
string
>
{
static
std
::
string
convert
(
const
ItemReference
&
ref
)
static
std
::
string
convert
(
const
ItemReference
&
ref
)
{
return
ref
.
c_str
();
}
static
int
compare
(
const
ItemReference
&
ref
,
const
std
::
string
&
value
,
bool
icase
)
static
int
compare
(
const
ItemReference
&
ref
,
const
std
::
string
&
value
,
bool
icase
)
{
return
icase
?
cif
::
icompare
(
ref
.
c_str
(),
value
)
:
std
::
strcmp
(
ref
.
c_str
(),
value
.
c_str
());
}
};
// some helper classes to help create tuple result types
template
<
typename
...
C
>
template
<
typename
...
C
>
struct
getRowResult
{
static
constexpr
size_t
N
=
sizeof
...(
C
);
getRowResult
(
const
Row
&
r
,
std
::
array
<
size_t
,
N
>&&
columns
)
:
mRow
(
r
),
mColumns
(
std
::
move
(
columns
))
getRowResult
(
const
Row
&
r
,
std
::
array
<
size_t
,
N
>
&&
columns
)
:
mRow
(
r
)
,
mColumns
(
std
::
move
(
columns
))
{
}
const
ItemReference
operator
[](
size_t
ix
)
const
{
return
mRow
[
mColumns
[
ix
]];
}
template
<
typename
...
Ts
,
std
::
enable_if_t
<
N
==
sizeof
...(
Ts
),
int
>
=
0
>
template
<
typename
...
Ts
,
std
::
enable_if_t
<
N
==
sizeof
...(
Ts
),
int
>
=
0
>
operator
std
::
tuple
<
Ts
...
>
()
const
{
return
get
<
Ts
...
>
(
std
::
index_sequence_for
<
Ts
...
>
{});
}
template
<
typename
...
Ts
,
std
::
size_t
...
Is
>
template
<
typename
...
Ts
,
std
::
size_t
...
Is
>
std
::
tuple
<
Ts
...
>
get
(
std
::
index_sequence
<
Is
...
>
)
const
{
return
std
::
tuple
<
Ts
...
>
{
mRow
[
mColumns
[
Is
]].
template
as
<
Ts
>
()...};
}
const
Row
&
mRow
;
const
Row
&
mRow
;
std
::
array
<
size_t
,
N
>
mColumns
;
};
// we want to be able to tie some variables to a RowResult, for this we use tiewraps
template
<
typename
...
Ts
>
template
<
typename
...
Ts
>
struct
tieWrap
{
tieWrap
(
Ts
...
args
)
:
mVal
(
args
...)
{}
tieWrap
(
Ts
...
args
)
:
mVal
(
args
...)
{
}
template
<
typename
RR
>
void
operator
=
(
const
RR
&&
rr
)
template
<
typename
RR
>
void
operator
=
(
const
RR
&&
rr
)
{
// getRowResult will do the conversion, but only if the types
// are compatible. That means the number of parameters to the get()
// of the row should be equal to the number of items in the tuple
// you are trying to tie.
using
RType
=
std
::
tuple
<
typename
std
::
remove_reference
<
Ts
>::
type
...
>
;
using
RType
=
std
::
tuple
<
typename
std
::
remove_reference
<
Ts
>::
type
...
>
;
mVal
=
static_cast
<
RType
>
(
rr
);
}
std
::
tuple
<
Ts
...
>
mVal
;
std
::
tuple
<
Ts
...
>
mVal
;
};
}
}
// namespace detail
template
<
typename
...
Ts
>
auto
tie
(
Ts
&
...
v
)
template
<
typename
...
Ts
>
auto
tie
(
Ts
&
...
v
)
{
return
detail
::
tieWrap
<
Ts
&
...
>
(
std
::
forward
<
Ts
&>
(
v
)...);
return
detail
::
tieWrap
<
Ts
&
...
>
(
std
::
forward
<
Ts
&>
(
v
)...);
}
class
Row
...
...
@@ -622,19 +652,26 @@ class Row
friend
class
RowSet
;
Row
()
:
mData
(
nullptr
)
{}
:
mData
(
nullptr
)
{
}
Row
(
ItemRow
*
data
)
:
mData
(
data
)
{}
Row
(
ItemRow
*
data
)
:
mData
(
data
)
{
}
Row
(
const
ItemRow
*
data
)
:
mData
(
const_cast
<
ItemRow
*>
(
data
)),
mCascade
(
false
)
{}
Row
(
const
ItemRow
*
data
)
:
mData
(
const_cast
<
ItemRow
*>
(
data
))
,
mCascade
(
false
)
{
}
Row
(
const
Row
&
rhs
);
Row
&
operator
=
(
const
Row
&
rhs
);
Row
(
const
Row
&
rhs
);
Row
&
operator
=
(
const
Row
&
rhs
);
Row
(
Row
&&
rhs
);
Row
&
operator
=
(
Row
&&
rhs
);
Row
(
Row
&&
rhs
);
Row
&
operator
=
(
Row
&&
rhs
);
~
Row
();
...
...
@@ -643,49 +680,53 @@ class Row
mCascade
=
cascade
;
}
void
next
();
///< make this row point to the next ItemRow
void
next
();
///< make this row point to the next ItemRow
struct
const_iterator
{
using
iterator_category
=
std
::
forward_iterator_tag
;
using
value_type
=
const
Item
;
using
difference_type
=
std
::
ptrdiff_t
;
using
pointer
=
value_type
*
;
using
reference
=
value_type
&
;
const_iterator
(
ItemRow
*
data
,
ItemValue
*
ptr
);
reference
operator
*
()
{
return
mCurrent
;
}
pointer
operator
->
()
{
return
&
mCurrent
;
}
const_iterator
&
operator
++
();
const_iterator
operator
++
(
int
)
{
const_iterator
result
(
*
this
);
this
->
operator
++
();
return
result
;
}
bool
operator
==
(
const
const_iterator
&
rhs
)
const
{
return
mPtr
==
rhs
.
mPtr
;
}
bool
operator
!=
(
const
const_iterator
&
rhs
)
const
{
return
mPtr
!=
rhs
.
mPtr
;
}
private
:
using
pointer
=
value_type
*
;
using
reference
=
value_type
&
;
const_iterator
(
ItemRow
*
data
,
ItemValue
*
ptr
);
reference
operator
*
()
{
return
mCurrent
;
}
pointer
operator
->
()
{
return
&
mCurrent
;
}
const_iterator
&
operator
++
();
const_iterator
operator
++
(
int
)
{
const_iterator
result
(
*
this
);
this
->
operator
++
();
return
result
;
}
bool
operator
==
(
const
const_iterator
&
rhs
)
const
{
return
mPtr
==
rhs
.
mPtr
;
}
bool
operator
!=
(
const
const_iterator
&
rhs
)
const
{
return
mPtr
!=
rhs
.
mPtr
;
}
private
:
void
fetch
();
ItemRow
*
mData
;
ItemValue
*
mPtr
;
Item
mCurrent
;
ItemRow
*
mData
;
ItemValue
*
mPtr
;
Item
mCurrent
;
};
// checks for an initialized Row:
explicit
operator
bool
()
const
{
return
mData
!=
nullptr
;
}
explicit
operator
bool
()
const
{
return
mData
!=
nullptr
;
}
// for debugging
uint32_t
lineNr
()
const
;
void
lineNr
(
uint32_t
l
);
bool
empty
()
const
;
const_iterator
begin
()
const
;
const_iterator
end
()
const
;
// TODO: implement real const version?
friend
class
detail
::
ItemReference
;
const
detail
::
ItemReference
operator
[](
size_t
column
)
const
...
...
@@ -693,32 +734,32 @@ class Row
return
detail
::
ItemReference
(
"<anonymous column>"
,
column
,
*
this
);
}
const
detail
::
ItemReference
operator
[](
const
char
*
itemTag
)
const
const
detail
::
ItemReference
operator
[](
const
char
*
itemTag
)
const
{
size_t
column
=
ColumnForItemTag
(
itemTag
);
return
detail
::
ItemReference
(
itemTag
,
column
,
*
this
);
}
detail
::
ItemReference
operator
[](
const
char
*
itemTag
)
detail
::
ItemReference
operator
[](
const
char
*
itemTag
)
{
size_t
column
=
ColumnForItemTag
(
itemTag
);
return
detail
::
ItemReference
(
itemTag
,
column
,
*
this
);
}
const
detail
::
ItemReference
operator
[](
const
std
::
string
&
itemTag
)
const
const
detail
::
ItemReference
operator
[](
const
std
::
string
&
itemTag
)
const
{
size_t
column
=
ColumnForItemTag
(
itemTag
.
c_str
());
return
detail
::
ItemReference
(
itemTag
.
c_str
(),
column
,
*
this
);
}
detail
::
ItemReference
operator
[](
const
std
::
string
&
itemTag
)
detail
::
ItemReference
operator
[](
const
std
::
string
&
itemTag
)
{
size_t
column
=
ColumnForItemTag
(
itemTag
.
c_str
());
return
detail
::
ItemReference
(
itemTag
.
c_str
(),
column
,
*
this
);
}
template
<
typename
...
Ts
,
size_t
N
>
std
::
tuple
<
Ts
...
>
get
(
char
const
*
const
(
&
columns
)[
N
])
const
template
<
typename
...
Ts
,
size_t
N
>
std
::
tuple
<
Ts
...
>
get
(
char
const
*
const
(
&
columns
)[
N
])
const
{
static_assert
(
sizeof
...(
Ts
)
==
N
,
"Number of columns should be equal to number of types to return"
);
...
...
@@ -728,46 +769,45 @@ class Row
return
detail
::
getRowResult
<
Ts
...
>
(
*
this
,
std
::
move
(
cix
));
}
template
<
typename
...
C
>
template
<
typename
...
C
>
auto
get
(
C
...
columns
)
const
{
return
detail
::
getRowResult
<
C
...
>
(
*
this
,
{
ColumnForItemTag
(
columns
)...
});
return
detail
::
getRowResult
<
C
...
>
(
*
this
,
{
ColumnForItemTag
(
columns
)...
});
}
void
assign
(
const
std
::
vector
<
Item
>&
values
);
void
assign
(
const
std
::
string
&
name
,
const
std
::
string
&
value
,
bool
updateLinked
);
bool
operator
==
(
const
Row
&
rhs
)
const
void
assign
(
const
std
::
vector
<
Item
>
&
values
);
void
assign
(
const
std
::
string
&
name
,
const
std
::
string
&
value
,
bool
updateLinked
);
bool
operator
==
(
const
Row
&
rhs
)
const
{
return
mData
==
rhs
.
mData
;
}
bool
operator
!=
(
const
Row
&
rhs
)
const
bool
operator
!=
(
const
Row
&
rhs
)
const
{
return
mData
!=
rhs
.
mData
;
}
ItemRow
*
data
()
const
{
return
mData
;
}
ItemRow
*
data
()
const
{
return
mData
;
}
void
swap
(
Row
&
rhs
)
void
swap
(
Row
&
rhs
)
{
std
::
swap
(
mData
,
rhs
.
mData
);
}
friend
std
::
ostream
&
operator
<<
(
std
::
ostream
&
os
,
const
Row
&
row
);
friend
std
::
ostream
&
operator
<<
(
std
::
ostream
&
os
,
const
Row
&
row
);
private
:
void
assign
(
size_t
column
,
const
std
::
string
&
value
,
bool
updateLinked
);
void
assign
(
const
Item
&
i
,
bool
updateLinked
);
void
assign
(
size_t
column
,
const
std
::
string
&
value
,
bool
updateLinked
);
void
assign
(
const
Item
&
i
,
bool
updateLinked
);
static
void
swap
(
size_t
column
,
ItemRow
*
a
,
ItemRow
*
b
);
static
void
swap
(
size_t
column
,
ItemRow
*
a
,
ItemRow
*
b
);
size_t
ColumnForItemTag
(
const
char
*
itemTag
)
const
;
size_t
ColumnForItemTag
(
const
char
*
itemTag
)
const
;
ItemRow
*
mData
;
uint32_t
mLineNr
=
0
;
bool
mCascade
=
true
;
ItemRow
*
mData
;
uint32_t
mLineNr
=
0
;
bool
mCascade
=
true
;
};
// --------------------------------------------------------------------
...
...
@@ -776,45 +816,50 @@ class Row
namespace
detail
{
struct
ConditionImpl
{
virtual
~
ConditionImpl
()
{}
virtual
void
prepare
(
const
Category
&
c
)
{}
virtual
bool
test
(
const
Category
&
c
,
const
Row
&
r
)
const
=
0
;
virtual
void
str
(
std
::
ostream
&
os
)
const
=
0
;
};
struct
ConditionImpl
{
virtual
~
ConditionImpl
()
{}
struct
AllConditionImpl
:
public
ConditionImpl
{
virtual
bool
test
(
const
Category
&
c
,
const
Row
&
r
)
const
{
return
true
;
}
virtual
void
str
(
std
::
ostream
&
os
)
const
{
os
<<
"*"
;
}
};
virtual
void
prepare
(
const
Category
&
c
)
{}
virtual
bool
test
(
const
Category
&
c
,
const
Row
&
r
)
const
=
0
;
virtual
void
str
(
std
::
ostream
&
os
)
const
=
0
;
};
struct
OrConditionImpl
;
struct
AndConditionImpl
;
struct
NotConditionImpl
;
struct
AllConditionImpl
:
public
ConditionImpl
{
virtual
bool
test
(
const
Category
&
c
,
const
Row
&
r
)
const
{
return
true
;
}
virtual
void
str
(
std
::
ostream
&
os
)
const
{
os
<<
"*"
;
}
};
}
struct
OrConditionImpl
;
struct
AndConditionImpl
;
struct
NotConditionImpl
;
}
// namespace detail
class
Condition
{
public
:
Condition
()
:
mImpl
(
nullptr
)
{
}
Condition
(
detail
::
ConditionImpl
*
impl
)
:
mImpl
(
impl
)
{
}
Condition
()
:
mImpl
(
nullptr
)
{}
Condition
(
detail
::
ConditionImpl
*
impl
)
:
mImpl
(
impl
)
{}
Condition
(
const
Condition
&
)
=
delete
;
Condition
(
const
Condition
&
)
=
delete
;
Condition
(
Condition
&&
rhs
)
noexcept
Condition
(
Condition
&&
rhs
)
noexcept
:
mImpl
(
nullptr
)
{
std
::
swap
(
mImpl
,
rhs
.
mImpl
);
}
Condition
&
operator
=
(
const
Condition
&
)
=
delete
;
Condition
&
operator
=
(
const
Condition
&
)
=
delete
;
Condition
&
operator
=
(
Condition
&&
rhs
)
noexcept
Condition
&
operator
=
(
Condition
&&
rhs
)
noexcept
{
std
::
swap
(
mImpl
,
rhs
.
mImpl
);
return
*
this
;
...
...
@@ -825,44 +870,44 @@ class Condition
delete
mImpl
;
mImpl
=
nullptr
;
}
void
prepare
(
const
Category
&
c
)
void
prepare
(
const
Category
&
c
)
{
if
(
mImpl
)
mImpl
->
prepare
(
c
);
mPrepared
=
true
;
}
bool
operator
()(
const
Category
&
c
,
const
Row
&
r
)
const
bool
operator
()(
const
Category
&
c
,
const
Row
&
r
)
const
{
assert
(
mImpl
);
assert
(
mPrepared
);
return
mImpl
?
mImpl
->
test
(
c
,
r
)
:
false
;
}
bool
empty
()
const
{
return
mImpl
==
nullptr
;
}
friend
Condition
operator
||
(
Condition
&&
a
,
Condition
&&
b
);
friend
Condition
operator
&&
(
Condition
&&
a
,
Condition
&&
b
);
bool
empty
()
const
{
return
mImpl
==
nullptr
;
}
friend
Condition
operator
||
(
Condition
&&
a
,
Condition
&&
b
);
friend
Condition
operator
&&
(
Condition
&&
a
,
Condition
&&
b
);
friend
struct
detail
::
OrConditionImpl
;
friend
struct
detail
::
AndConditionImpl
;
friend
struct
detail
::
NotConditionImpl
;
void
swap
(
Condition
&
rhs
)
void
swap
(
Condition
&
rhs
)
{
std
::
swap
(
mImpl
,
rhs
.
mImpl
);
std
::
swap
(
mPrepared
,
rhs
.
mPrepared
);
}
friend
std
::
ostream
&
operator
<<
(
std
::
ostream
&
os
,
const
Condition
&
cond
);
friend
std
::
ostream
&
operator
<<
(
std
::
ostream
&
os
,
const
Condition
&
cond
);
private
:
detail
::
ConditionImpl
*
mImpl
;
detail
::
ConditionImpl
*
mImpl
;
bool
mPrepared
=
false
;
};
inline
std
::
ostream
&
operator
<<
(
std
::
ostream
&
os
,
const
Condition
&
cond
)
inline
std
::
ostream
&
operator
<<
(
std
::
ostream
&
os
,
const
Condition
&
cond
)
{
if
(
cond
.
mImpl
)
cond
.
mImpl
->
str
(
os
);
...
...
@@ -872,219 +917,235 @@ inline std::ostream& operator<<(std::ostream& os, const Condition& cond)
namespace
detail
{
struct
KeyIsEmptyConditionImpl
:
public
ConditionImpl
{
KeyIsEmptyConditionImpl
(
const
std
::
string
&
ItemTag
)
:
mItemTag
(
ItemTag
)
{}
virtual
void
prepare
(
const
Category
&
c
);
virtual
bool
test
(
const
Category
&
c
,
const
Row
&
r
)
const
struct
KeyIsEmptyConditionImpl
:
public
ConditionImpl
{
return
r
[
mItemIx
].
empty
();
}
KeyIsEmptyConditionImpl
(
const
std
::
string
&
ItemTag
)
:
mItemTag
(
ItemTag
)
{
}
virtual
void
str
(
std
::
ostream
&
os
)
const
{
os
<<
mItemTag
<<
" IS NULL"
;
}
virtual
void
prepare
(
const
Category
&
c
);
std
::
string
mItemTag
;
size_t
mItemIx
=
0
;
};
virtual
bool
test
(
const
Category
&
c
,
const
Row
&
r
)
const
{
return
r
[
mItemIx
].
empty
();
}
struct
KeyCompareConditionImpl
:
public
ConditionImpl
{
template
<
typename
COMP
>
KeyCompareConditionImpl
(
const
std
::
string
&
ItemTag
,
COMP
&&
comp
,
const
std
::
string
&
s
)
:
mItemTag
(
ItemTag
),
mComp
(
std
::
move
(
comp
)),
mStr
(
s
)
{}
virtual
void
prepare
(
const
Category
&
c
);
virtual
bool
test
(
const
Category
&
c
,
const
Row
&
r
)
const
{
return
mComp
(
c
,
r
,
mCaseInsensitive
);
}
virtual
void
str
(
std
::
ostream
&
os
)
const
{
os
<<
mItemTag
<<
(
mCaseInsensitive
?
"^ "
:
" "
)
<<
mStr
;
}
virtual
void
str
(
std
::
ostream
&
os
)
const
{
os
<<
mItemTag
<<
" IS NULL"
;
}
std
::
string
mItemTag
;
size_t
mItemIx
=
0
;
bool
mCaseInsensitive
=
false
;
std
::
function
<
bool
(
const
Category
&
,
const
Row
&
,
bool
)
>
mComp
;
std
::
string
mStr
;
};
std
::
string
mItemTag
;
size_t
mItemIx
=
0
;
};
struct
KeyMatchesConditionImpl
:
public
ConditionImpl
{
KeyMatchesConditionImpl
(
const
std
::
string
&
ItemTag
,
const
std
::
regex
&
rx
)
:
mItemTag
(
ItemTag
),
mItemIx
(
0
),
mRx
(
rx
)
{}
virtual
void
prepare
(
const
Category
&
c
);
virtual
bool
test
(
const
Category
&
c
,
const
Row
&
r
)
const
struct
KeyCompareConditionImpl
:
public
ConditionImpl
{
return
std
::
regex_match
(
r
[
mItemIx
].
as
<
std
::
string
>
(),
mRx
);
}
template
<
typename
COMP
>
KeyCompareConditionImpl
(
const
std
::
string
&
ItemTag
,
COMP
&&
comp
,
const
std
::
string
&
s
)
:
mItemTag
(
ItemTag
)
,
mComp
(
std
::
move
(
comp
))
,
mStr
(
s
)
{
}
virtual
void
str
(
std
::
ostream
&
os
)
const
{
os
<<
mItemTag
<<
" =~ expression"
;
}
std
::
string
mItemTag
;
size_t
mItemIx
;
std
::
regex
mRx
;
};
virtual
void
prepare
(
const
Category
&
c
);
template
<
typename
T
>
struct
AnyIsConditionImpl
:
public
ConditionImpl
{
typedef
T
valueType
;
AnyIsConditionImpl
(
const
valueType
&
value
)
:
mValue
(
value
)
{}
virtual
bool
test
(
const
Category
&
c
,
const
Row
&
r
)
const
;
virtual
void
str
(
std
::
ostream
&
os
)
const
{
os
<<
"<any> == "
<<
mValue
;
}
virtual
bool
test
(
const
Category
&
c
,
const
Row
&
r
)
const
{
return
mComp
(
c
,
r
,
mCaseInsensitive
);
}
valueType
mValue
;
};
virtual
void
str
(
std
::
ostream
&
os
)
const
{
os
<<
mItemTag
<<
(
mCaseInsensitive
?
"^ "
:
" "
)
<<
mStr
;
}
struct
AnyMatchesConditionImpl
:
public
ConditionImpl
{
AnyMatchesConditionImpl
(
const
std
::
regex
&
rx
)
:
mRx
(
rx
)
{}
virtual
bool
test
(
const
Category
&
c
,
const
Row
&
r
)
const
;
virtual
void
str
(
std
::
ostream
&
os
)
const
std
::
string
mItemTag
;
size_t
mItemIx
=
0
;
bool
mCaseInsensitive
=
false
;
std
::
function
<
bool
(
const
Category
&
,
const
Row
&
,
bool
)
>
mComp
;
std
::
string
mStr
;
};
struct
KeyMatchesConditionImpl
:
public
ConditionImpl
{
os
<<
"<any> =~ expression"
;
}
KeyMatchesConditionImpl
(
const
std
::
string
&
ItemTag
,
const
std
::
regex
&
rx
)
:
mItemTag
(
ItemTag
)
,
mItemIx
(
0
)
,
mRx
(
rx
)
{
}
std
::
regex
mRx
;
};
virtual
void
prepare
(
const
Category
&
c
);
struct
AndConditionImpl
:
public
ConditionImpl
{
AndConditionImpl
(
Condition
&&
a
,
Condition
&&
b
)
:
mA
(
nullptr
),
mB
(
nullptr
)
{
std
::
swap
(
mA
,
a
.
mImpl
);
std
::
swap
(
mB
,
b
.
mImpl
);
}
~
AndConditionImpl
()
{
delete
mA
;
delete
mB
;
}
virtual
void
prepare
(
const
Category
&
c
)
{
mA
->
prepare
(
c
);
mB
->
prepare
(
c
);
}
virtual
bool
test
(
const
Category
&
c
,
const
Row
&
r
)
const
{
return
mA
->
test
(
c
,
r
)
and
mB
->
test
(
c
,
r
);
}
virtual
bool
test
(
const
Category
&
c
,
const
Row
&
r
)
const
{
return
std
::
regex_match
(
r
[
mItemIx
].
as
<
std
::
string
>
(),
mRx
);
}
virtual
void
str
(
std
::
ostream
&
os
)
const
{
os
<<
'('
;
mA
->
str
(
os
);
os
<<
") AND ("
;
mB
->
str
(
os
);
os
<<
')'
;
}
virtual
void
str
(
std
::
ostream
&
os
)
const
{
os
<<
mItemTag
<<
" =~ expression"
;
}
ConditionImpl
*
mA
;
ConditionImpl
*
mB
;
};
std
::
string
mItemTag
;
size_t
mItemIx
;
std
::
regex
mRx
;
};
struct
OrConditionImpl
:
public
ConditionImpl
{
OrConditionImpl
(
Condition
&&
a
,
Condition
&&
b
)
:
mA
(
nullptr
),
mB
(
nullptr
)
{
std
::
swap
(
mA
,
a
.
mImpl
);
std
::
swap
(
mB
,
b
.
mImpl
);
}
~
OrConditionImpl
()
{
delete
mA
;
delete
mB
;
}
virtual
void
prepare
(
const
Category
&
c
)
{
mA
->
prepare
(
c
);
mB
->
prepare
(
c
);
}
virtual
bool
test
(
const
Category
&
c
,
const
Row
&
r
)
const
template
<
typename
T
>
struct
AnyIsConditionImpl
:
public
ConditionImpl
{
return
mA
->
test
(
c
,
r
)
or
mB
->
test
(
c
,
r
);
}
typedef
T
valueType
;
virtual
void
str
(
std
::
ostream
&
os
)
const
{
os
<<
'('
;
mA
->
str
(
os
);
os
<<
") OR ("
;
mB
->
str
(
os
);
os
<<
')'
;
}
AnyIsConditionImpl
(
const
valueType
&
value
)
:
mValue
(
value
)
{
}
ConditionImpl
*
mA
;
ConditionImpl
*
mB
;
};
virtual
bool
test
(
const
Category
&
c
,
const
Row
&
r
)
const
;
virtual
void
str
(
std
::
ostream
&
os
)
const
{
os
<<
"<any> == "
<<
mValue
;
}
struct
NotConditionImpl
:
public
ConditionImpl
{
NotConditionImpl
(
Condition
&&
a
)
:
mA
(
nullptr
)
{
std
::
swap
(
mA
,
a
.
mImpl
);
}
~
NotConditionImpl
()
valueType
mValue
;
};
struct
AnyMatchesConditionImpl
:
public
ConditionImpl
{
delete
mA
;
}
virtual
void
prepare
(
const
Category
&
c
)
AnyMatchesConditionImpl
(
const
std
::
regex
&
rx
)
:
mRx
(
rx
)
{
}
virtual
bool
test
(
const
Category
&
c
,
const
Row
&
r
)
const
;
virtual
void
str
(
std
::
ostream
&
os
)
const
{
os
<<
"<any> =~ expression"
;
}
std
::
regex
mRx
;
};
struct
AndConditionImpl
:
public
ConditionImpl
{
mA
->
prepare
(
c
);
}
virtual
bool
test
(
const
Category
&
c
,
const
Row
&
r
)
const
AndConditionImpl
(
Condition
&&
a
,
Condition
&&
b
)
:
mA
(
nullptr
)
,
mB
(
nullptr
)
{
std
::
swap
(
mA
,
a
.
mImpl
);
std
::
swap
(
mB
,
b
.
mImpl
);
}
~
AndConditionImpl
()
{
delete
mA
;
delete
mB
;
}
virtual
void
prepare
(
const
Category
&
c
)
{
mA
->
prepare
(
c
);
mB
->
prepare
(
c
);
}
virtual
bool
test
(
const
Category
&
c
,
const
Row
&
r
)
const
{
return
mA
->
test
(
c
,
r
)
and
mB
->
test
(
c
,
r
);
}
virtual
void
str
(
std
::
ostream
&
os
)
const
{
os
<<
'('
;
mA
->
str
(
os
);
os
<<
") AND ("
;
mB
->
str
(
os
);
os
<<
')'
;
}
ConditionImpl
*
mA
;
ConditionImpl
*
mB
;
};
struct
OrConditionImpl
:
public
ConditionImpl
{
return
not
mA
->
test
(
c
,
r
);
}
OrConditionImpl
(
Condition
&&
a
,
Condition
&&
b
)
:
mA
(
nullptr
)
,
mB
(
nullptr
)
{
std
::
swap
(
mA
,
a
.
mImpl
);
std
::
swap
(
mB
,
b
.
mImpl
);
}
~
OrConditionImpl
()
{
delete
mA
;
delete
mB
;
}
virtual
void
prepare
(
const
Category
&
c
)
{
mA
->
prepare
(
c
);
mB
->
prepare
(
c
);
}
virtual
void
str
(
std
::
ostream
&
os
)
const
virtual
bool
test
(
const
Category
&
c
,
const
Row
&
r
)
const
{
return
mA
->
test
(
c
,
r
)
or
mB
->
test
(
c
,
r
);
}
virtual
void
str
(
std
::
ostream
&
os
)
const
{
os
<<
'('
;
mA
->
str
(
os
);
os
<<
") OR ("
;
mB
->
str
(
os
);
os
<<
')'
;
}
ConditionImpl
*
mA
;
ConditionImpl
*
mB
;
};
struct
NotConditionImpl
:
public
ConditionImpl
{
os
<<
"NOT ("
;
mA
->
str
(
os
);
os
<<
')'
;
}
NotConditionImpl
(
Condition
&&
a
)
:
mA
(
nullptr
)
{
std
::
swap
(
mA
,
a
.
mImpl
);
}
ConditionImpl
*
mA
;
};
~
NotConditionImpl
()
{
delete
mA
;
}
}
virtual
void
prepare
(
const
Category
&
c
)
{
mA
->
prepare
(
c
);
}
virtual
bool
test
(
const
Category
&
c
,
const
Row
&
r
)
const
{
return
not
mA
->
test
(
c
,
r
);
}
virtual
void
str
(
std
::
ostream
&
os
)
const
{
os
<<
"NOT ("
;
mA
->
str
(
os
);
os
<<
')'
;
}
ConditionImpl
*
mA
;
};
inline
Condition
operator
&&
(
Condition
&&
a
,
Condition
&&
b
)
}
// namespace detail
inline
Condition
operator
&&
(
Condition
&&
a
,
Condition
&&
b
)
{
if
(
a
.
mImpl
and
b
.
mImpl
)
return
Condition
(
new
detail
::
AndConditionImpl
(
std
::
move
(
a
),
std
::
move
(
b
)));
...
...
@@ -1093,7 +1154,7 @@ inline Condition operator&&(Condition&& a, Condition&& b)
return
Condition
(
std
::
move
(
b
));
}
inline
Condition
operator
||
(
Condition
&&
a
,
Condition
&&
b
)
inline
Condition
operator
||
(
Condition
&&
a
,
Condition
&&
b
)
{
if
(
a
.
mImpl
and
b
.
mImpl
)
return
Condition
(
new
detail
::
OrConditionImpl
(
std
::
move
(
a
),
std
::
move
(
b
)));
...
...
@@ -1102,38 +1163,50 @@ inline Condition operator||(Condition&& a, Condition&& b)
return
Condition
(
std
::
move
(
b
));
}
struct
Empty
{};
struct
Empty
{
};
struct
Key
{
Key
(
const
std
::
string
&
itemTag
)
:
mItemTag
(
itemTag
)
{}
Key
(
const
char
*
itemTag
)
:
mItemTag
(
itemTag
)
{}
Key
(
const
std
::
string
&
itemTag
)
:
mItemTag
(
itemTag
)
{
}
Key
(
const
char
*
itemTag
)
:
mItemTag
(
itemTag
)
{
}
Key
(
const
Key
&
)
=
delete
;
Key
&
operator
=
(
const
Key
&
)
=
delete
;
Key
(
const
Key
&
)
=
delete
;
Key
&
operator
=
(
const
Key
&
)
=
delete
;
std
::
string
mItemTag
;
};
template
<
typename
T
>
Condition
operator
==
(
const
Key
&
key
,
const
T
&
v
)
template
<
typename
T
>
Condition
operator
==
(
const
Key
&
key
,
const
T
&
v
)
{
std
::
ostringstream
s
;
s
<<
" == "
<<
v
;
return
Condition
(
new
detail
::
KeyCompareConditionImpl
(
key
.
mItemTag
,
[
tag
=
key
.
mItemTag
,
v
](
const
Category
&
c
,
const
Row
&
r
,
bool
icase
)
{
return
r
[
tag
].
template
compare
<
T
>
(
v
,
icase
)
==
0
;
},
s
.
str
()));
return
Condition
(
new
detail
::
KeyCompareConditionImpl
(
key
.
mItemTag
,
[
tag
=
key
.
mItemTag
,
v
](
const
Category
&
c
,
const
Row
&
r
,
bool
icase
)
{
return
r
[
tag
].
template
compare
<
T
>
(
v
,
icase
)
==
0
;
},
s
.
str
()));
}
inline
Condition
operator
==
(
const
Key
&
key
,
const
char
*
value
)
inline
Condition
operator
==
(
const
Key
&
key
,
const
char
*
value
)
{
if
(
value
!=
nullptr
and
*
value
!=
0
)
{
std
::
ostringstream
s
;
s
<<
" == "
<<
value
;
return
Condition
(
new
detail
::
KeyCompareConditionImpl
(
key
.
mItemTag
,
[
tag
=
key
.
mItemTag
,
value
](
const
Category
&
c
,
const
Row
&
r
,
bool
icase
)
{
return
r
[
tag
].
compare
(
value
,
icase
)
==
0
;
},
s
.
str
()));
return
Condition
(
new
detail
::
KeyCompareConditionImpl
(
key
.
mItemTag
,
[
tag
=
key
.
mItemTag
,
value
](
const
Category
&
c
,
const
Row
&
r
,
bool
icase
)
{
return
r
[
tag
].
compare
(
value
,
icase
)
==
0
;
},
s
.
str
()));
}
else
return
Condition
(
new
detail
::
KeyIsEmptyConditionImpl
(
key
.
mItemTag
));
...
...
@@ -1148,169 +1221,181 @@ inline Condition operator==(const Key& key, const char *value)
// { return r[tag].template compare<(v, icase) == 0; }));
// }
inline
Condition
operator
==
(
const
Key
&
key
,
const
Empty
&
)
inline
Condition
operator
==
(
const
Key
&
key
,
const
Empty
&
)
{
return
Condition
(
new
detail
::
KeyIsEmptyConditionImpl
(
key
.
mItemTag
));
}
template
<
typename
T
>
Condition
operator
!=
(
const
Key
&
key
,
const
T
&
v
)
template
<
typename
T
>
Condition
operator
!=
(
const
Key
&
key
,
const
T
&
v
)
{
return
Condition
(
new
detail
::
NotConditionImpl
(
operator
==
(
key
,
v
)));
}
inline
Condition
operator
!=
(
const
Key
&
key
,
const
char
*
v
)
inline
Condition
operator
!=
(
const
Key
&
key
,
const
char
*
v
)
{
std
::
string
value
(
v
?
v
:
""
);
return
Condition
(
new
detail
::
NotConditionImpl
(
operator
==
(
key
,
value
)));
}
template
<
typename
T
>
Condition
operator
>
(
const
Key
&
key
,
const
T
&
v
)
template
<
typename
T
>
Condition
operator
>
(
const
Key
&
key
,
const
T
&
v
)
{
std
::
ostringstream
s
;
s
<<
" > "
<<
v
;
return
Condition
(
new
detail
::
KeyCompareConditionImpl
(
key
.
mItemTag
,
[
tag
=
key
.
mItemTag
,
v
](
const
Category
&
c
,
const
Row
&
r
,
bool
icase
)
{
return
r
[
tag
].
template
compare
<
T
>
(
v
,
icase
)
>
0
;
},
s
.
str
()));
return
Condition
(
new
detail
::
KeyCompareConditionImpl
(
key
.
mItemTag
,
[
tag
=
key
.
mItemTag
,
v
](
const
Category
&
c
,
const
Row
&
r
,
bool
icase
)
{
return
r
[
tag
].
template
compare
<
T
>
(
v
,
icase
)
>
0
;
},
s
.
str
()));
}
template
<
typename
T
>
Condition
operator
>=
(
const
Key
&
key
,
const
T
&
v
)
template
<
typename
T
>
Condition
operator
>=
(
const
Key
&
key
,
const
T
&
v
)
{
std
::
ostringstream
s
;
s
<<
" >= "
<<
v
;
return
Condition
(
new
detail
::
KeyCompareConditionImpl
(
key
.
mItemTag
,
[
tag
=
key
.
mItemTag
,
v
](
const
Category
&
c
,
const
Row
&
r
,
bool
icase
)
{
return
r
[
tag
].
template
compare
<
T
>
(
v
,
icase
)
>=
0
;
},
s
.
str
()));
return
Condition
(
new
detail
::
KeyCompareConditionImpl
(
key
.
mItemTag
,
[
tag
=
key
.
mItemTag
,
v
](
const
Category
&
c
,
const
Row
&
r
,
bool
icase
)
{
return
r
[
tag
].
template
compare
<
T
>
(
v
,
icase
)
>=
0
;
},
s
.
str
()));
}
template
<
typename
T
>
Condition
operator
<
(
const
Key
&
key
,
const
T
&
v
)
template
<
typename
T
>
Condition
operator
<
(
const
Key
&
key
,
const
T
&
v
)
{
std
::
ostringstream
s
;
s
<<
" < "
<<
v
;
return
Condition
(
new
detail
::
KeyCompareConditionImpl
(
key
.
mItemTag
,
[
tag
=
key
.
mItemTag
,
v
](
const
Category
&
c
,
const
Row
&
r
,
bool
icase
)
{
return
r
[
tag
].
template
compare
<
T
>
(
v
,
icase
)
<
0
;
},
s
.
str
()));
return
Condition
(
new
detail
::
KeyCompareConditionImpl
(
key
.
mItemTag
,
[
tag
=
key
.
mItemTag
,
v
](
const
Category
&
c
,
const
Row
&
r
,
bool
icase
)
{
return
r
[
tag
].
template
compare
<
T
>
(
v
,
icase
)
<
0
;
},
s
.
str
()));
}
template
<
typename
T
>
Condition
operator
<=
(
const
Key
&
key
,
const
T
&
v
)
template
<
typename
T
>
Condition
operator
<=
(
const
Key
&
key
,
const
T
&
v
)
{
std
::
ostringstream
s
;
s
<<
" <= "
<<
v
;
return
Condition
(
new
detail
::
KeyCompareConditionImpl
(
key
.
mItemTag
,
[
tag
=
key
.
mItemTag
,
v
](
const
Category
&
c
,
const
Row
&
r
,
bool
icase
)
{
return
r
[
tag
].
template
compare
<
T
>
(
v
,
icase
)
<=
0
;
},
s
.
str
()));
return
Condition
(
new
detail
::
KeyCompareConditionImpl
(
key
.
mItemTag
,
[
tag
=
key
.
mItemTag
,
v
](
const
Category
&
c
,
const
Row
&
r
,
bool
icase
)
{
return
r
[
tag
].
template
compare
<
T
>
(
v
,
icase
)
<=
0
;
},
s
.
str
()));
}
template
<>
inline
Condition
operator
==
(
const
Key
&
key
,
const
std
::
regex
&
rx
)
template
<>
inline
Condition
operator
==
(
const
Key
&
key
,
const
std
::
regex
&
rx
)
{
return
Condition
(
new
detail
::
KeyMatchesConditionImpl
(
key
.
mItemTag
,
rx
));
}
template
<>
inline
Condition
operator
==
(
const
Key
&
key
,
const
Empty
&
)
template
<>
inline
Condition
operator
==
(
const
Key
&
key
,
const
Empty
&
)
{
return
Condition
(
new
detail
::
KeyIsEmptyConditionImpl
(
key
.
mItemTag
));
}
struct
any
{
template
<
typename
T
>
Condition
operator
==
(
const
T
&
v
)
const
template
<
typename
T
>
Condition
operator
==
(
const
T
&
v
)
const
{
return
Condition
(
new
detail
::
AnyIsConditionImpl
<
T
>
(
v
));
}
};
template
<>
inline
Condition
any
::
operator
==
(
const
std
::
regex
&
rx
)
const
template
<>
inline
Condition
any
::
operator
==
(
const
std
::
regex
&
rx
)
const
{
return
Condition
(
new
detail
::
AnyMatchesConditionImpl
(
rx
));
}
inline
Condition
All
()
{
return
Condition
(
new
detail
::
AllConditionImpl
());
return
Condition
(
new
detail
::
AllConditionImpl
());
}
inline
Condition
Not
(
Condition
&&
cond
)
inline
Condition
Not
(
Condition
&&
cond
)
{
return
Condition
(
new
detail
::
NotConditionImpl
(
std
::
move
(
cond
)));
return
Condition
(
new
detail
::
NotConditionImpl
(
std
::
move
(
cond
)));
}
namespace
literals
{
inline
Key
operator
""
_key
(
const
char
*
text
,
size_t
length
)
{
return
Key
(
std
::
string
(
text
,
length
));
}
inline
constexpr
Empty
Null
=
Empty
();
inline
Key
operator
""
_key
(
const
char
*
text
,
size_t
length
)
{
return
Key
(
std
::
string
(
text
,
length
));
}
}
inline
constexpr
Empty
Null
=
Empty
();
}
// namespace literals
// -----------------------------------------------------------------------
// iterators
template
<
typename
RowType
,
typename
...
Ts
>
template
<
typename
RowType
,
typename
...
Ts
>
class
iterator_impl
{
public
:
template
<
typename
,
typename
...
>
friend
class
iterator_impl
;
template
<
typename
,
typename
...
>
friend
class
iterator_impl
;
static
constexpr
size_t
N
=
sizeof
...(
Ts
);
using
iterator_category
=
std
::
forward_iterator_tag
;
using
value_type
=
std
::
conditional_t
<
N
==
0
,
RowType
,
std
::
tuple
<
Ts
...
>>
;
using
difference_type
=
std
::
ptrdiff_t
;
using
pointer
=
value_type
*
;
using
reference
=
value_type
&
;
using
pointer
=
value_type
*
;
using
reference
=
value_type
&
;
friend
class
Category
;
// default constructor, equal to end()
iterator_impl
()
{}
iterator_impl
(
const
iterator_impl
&
rhs
)
:
mCurrent
(
rhs
.
mCurrent
),
mValue
(
rhs
.
mValue
),
mCix
(
rhs
.
mCix
)
iterator_impl
(
const
iterator_impl
&
rhs
)
:
mCurrent
(
rhs
.
mCurrent
)
,
mValue
(
rhs
.
mValue
)
,
mCix
(
rhs
.
mCix
)
{
}
iterator_impl
(
ItemRow
*
data
)
iterator_impl
(
ItemRow
*
data
)
:
mCurrent
(
data
)
{
static_assert
(
N
==
0
,
"Only valid if this is a row iterator, not a row<xxx> iterator"
);
}
iterator_impl
(
ItemRow
*
data
,
const
std
::
array
<
size_t
,
N
>&
cix
)
:
mCurrent
(
data
),
mCix
(
cix
)
{}
iterator_impl
(
ItemRow
*
data
,
const
std
::
array
<
size_t
,
N
>
&
cix
)
:
mCurrent
(
data
)
,
mCix
(
cix
)
{
}
template
<
typename
IRowType
>
iterator_impl
(
iterator_impl
<
IRowType
,
Ts
...
>&
rhs
)
:
mCurrent
(
rhs
.
mCurrent
),
mCix
(
rhs
.
mCix
)
template
<
typename
IRowType
>
iterator_impl
(
iterator_impl
<
IRowType
,
Ts
...
>
&
rhs
)
:
mCurrent
(
rhs
.
mCurrent
)
,
mCix
(
rhs
.
mCix
)
{
if
constexpr
(
N
>
0
)
mValue
=
get
(
mCurrent
,
std
::
make_index_sequence
<
N
>
());
}
template
<
typename
IRowType
>
iterator_impl
(
const
iterator_impl
<
IRowType
>&
rhs
,
const
std
::
array
<
size_t
,
N
>&
cix
)
:
mCurrent
(
rhs
.
mCurrent
),
mCix
(
cix
)
template
<
typename
IRowType
>
iterator_impl
(
const
iterator_impl
<
IRowType
>
&
rhs
,
const
std
::
array
<
size_t
,
N
>
&
cix
)
:
mCurrent
(
rhs
.
mCurrent
)
,
mCix
(
cix
)
{
if
constexpr
(
N
>
0
)
mValue
=
get
(
mCurrent
,
std
::
make_index_sequence
<
N
>
());
}
iterator_impl
&
operator
=
(
const
iterator_impl
&
i
)
iterator_impl
&
operator
=
(
const
iterator_impl
&
i
)
{
mCurrent
=
i
.
mCurrent
;
if
constexpr
(
N
!=
0
)
...
...
@@ -1343,8 +1428,8 @@ class iterator_impl
{
return
mCurrent
;
}
iterator_impl
&
operator
++
()
iterator_impl
&
operator
++
()
{
mCurrent
.
next
();
...
...
@@ -1359,26 +1444,25 @@ class iterator_impl
iterator_impl
result
(
*
this
);
this
->
operator
++
();
return
result
;
}
}
bool
operator
==
(
const
iterator_impl
&
rhs
)
const
{
return
mCurrent
==
rhs
.
mCurrent
;
}
bool
operator
!=
(
const
iterator_impl
&
rhs
)
const
{
return
mCurrent
!=
rhs
.
mCurrent
;
}
bool
operator
==
(
const
iterator_impl
&
rhs
)
const
{
return
mCurrent
==
rhs
.
mCurrent
;
}
bool
operator
!=
(
const
iterator_impl
&
rhs
)
const
{
return
mCurrent
!=
rhs
.
mCurrent
;
}
template
<
typename
IRowType
,
typename
...
ITs
>
bool
operator
==
(
const
iterator_impl
<
IRowType
,
ITs
...
>
&
rhs
)
const
template
<
typename
IRowType
,
typename
...
ITs
>
bool
operator
==
(
const
iterator_impl
<
IRowType
,
ITs
...
>
&
rhs
)
const
{
return
mCurrent
==
rhs
.
mCurrent
;
}
template
<
typename
IRowType
,
typename
...
ITs
>
bool
operator
!=
(
const
iterator_impl
<
IRowType
,
ITs
...
>
&
rhs
)
const
template
<
typename
IRowType
,
typename
...
ITs
>
bool
operator
!=
(
const
iterator_impl
<
IRowType
,
ITs
...
>
&
rhs
)
const
{
return
mCurrent
!=
rhs
.
mCurrent
;
}
private
:
template
<
std
::
size_t
...
Is
>
template
<
std
::
size_t
...
Is
>
std
::
tuple
<
Ts
...
>
get
(
Row
row
,
std
::
index_sequence
<
Is
...
>
)
const
{
if
(
row
)
...
...
@@ -1394,7 +1478,7 @@ class iterator_impl
// --------------------------------------------------------------------
// iterator proxy
template
<
typename
RowType
,
typename
...
Ts
>
template
<
typename
RowType
,
typename
...
Ts
>
class
iterator_proxy
{
public
:
...
...
@@ -1403,30 +1487,30 @@ class iterator_proxy
using
iterator
=
iterator_impl
<
RowType
,
Ts
...
>
;
using
row_iterator
=
iterator_impl
<
RowType
>
;
iterator_proxy
(
Category
&
cat
,
row_iterator
pos
,
char
const
*
const
columns
[
N
]);
iterator_proxy
(
Category
&
cat
,
row_iterator
pos
,
std
::
initializer_list
<
char
const
*>
columns
);
iterator_proxy
(
Category
&
cat
,
row_iterator
pos
,
char
const
*
const
columns
[
N
]);
iterator_proxy
(
Category
&
cat
,
row_iterator
pos
,
std
::
initializer_list
<
char
const
*>
columns
);
iterator_proxy
(
iterator_proxy
&&
p
);
iterator_proxy
&
operator
=
(
iterator_proxy
&&
p
);
iterator_proxy
(
iterator_proxy
&&
p
);
iterator_proxy
&
operator
=
(
iterator_proxy
&&
p
);
iterator_proxy
(
const
iterator_proxy
&
)
=
delete
;
iterator_proxy
&
operator
=
(
const
iterator_proxy
&
)
=
delete
;
iterator_proxy
(
const
iterator_proxy
&
)
=
delete
;
iterator_proxy
&
operator
=
(
const
iterator_proxy
&
)
=
delete
;
iterator
begin
()
const
{
return
iterator
(
mCBegin
,
mCix
);
}
iterator
end
()
const
{
return
iterator
(
mCEnd
,
mCix
);
}
iterator
begin
()
const
{
return
iterator
(
mCBegin
,
mCix
);
}
iterator
end
()
const
{
return
iterator
(
mCEnd
,
mCix
);
}
bool
empty
()
const
{
return
mCBegin
==
mCEnd
;
}
bool
empty
()
const
{
return
mCBegin
==
mCEnd
;
}
explicit
operator
bool
()
const
{
return
not
empty
();
}
explicit
operator
bool
()
const
{
return
not
empty
();
}
size_t
size
()
const
{
return
std
::
distance
(
begin
(),
end
());
}
size_t
size
()
const
{
return
std
::
distance
(
begin
(),
end
());
}
RowType
front
()
{
return
*
begin
();
}
RowType
back
()
{
return
*
(
std
::
prev
(
end
()));
}
RowType
front
()
{
return
*
begin
();
}
RowType
back
()
{
return
*
(
std
::
prev
(
end
()));
}
Category
&
category
()
const
{
return
*
mCat
;
}
Category
&
category
()
const
{
return
*
mCat
;
}
void
swap
(
iterator_proxy
&
rhs
)
void
swap
(
iterator_proxy
&
rhs
)
{
std
::
swap
(
mCat
,
rhs
.
mCat
);
std
::
swap
(
mCBegin
,
rhs
.
mCBegin
);
...
...
@@ -1435,15 +1519,15 @@ class iterator_proxy
}
private
:
Category
*
mCat
;
Category
*
mCat
;
row_iterator
mCBegin
,
mCEnd
;
std
::
array
<
size_t
,
N
>
mCix
;
std
::
array
<
size_t
,
N
>
mCix
;
};
// --------------------------------------------------------------------
// conditional iterator proxy
template
<
typename
RowType
,
typename
...
Ts
>
template
<
typename
RowType
,
typename
...
Ts
>
class
conditional_iterator_proxy
{
public
:
...
...
@@ -1461,12 +1545,12 @@ class conditional_iterator_proxy
using
iterator_category
=
std
::
forward_iterator_tag
;
using
value_type
=
conditional_iterator_proxy
::
value_type
;
using
difference_type
=
std
::
ptrdiff_t
;
using
pointer
=
value_type
*
;
using
reference
=
value_type
&
;
using
pointer
=
value_type
*
;
using
reference
=
value_type
&
;
conditional_iterator_impl
(
Category
&
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
&
operator
=
(
const
conditional_iterator_impl
&
i
)
=
default
;
conditional_iterator_impl
(
Category
&
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
&
operator
=
(
const
conditional_iterator_impl
&
i
)
=
default
;
virtual
~
conditional_iterator_impl
()
=
default
;
...
...
@@ -1479,14 +1563,14 @@ class conditional_iterator_proxy
{
return
&*
mBegin
;
}
conditional_iterator_impl
&
operator
++
()
conditional_iterator_impl
&
operator
++
()
{
while
(
mBegin
!=
mEnd
)
{
if
(
++
mBegin
==
mEnd
)
break
;
if
((
*
mCondition
)(
*
mCat
,
mBegin
.
row
()))
break
;
}
...
...
@@ -1499,58 +1583,57 @@ class conditional_iterator_proxy
conditional_iterator_impl
result
(
*
this
);
this
->
operator
++
();
return
result
;
}
}
bool
operator
==
(
const
conditional_iterator_impl
&
rhs
)
const
{
return
mBegin
==
rhs
.
mBegin
;
}
bool
operator
!=
(
const
conditional_iterator_impl
&
rhs
)
const
{
return
mBegin
!=
rhs
.
mBegin
;
}
bool
operator
==
(
const
conditional_iterator_impl
&
rhs
)
const
{
return
mBegin
==
rhs
.
mBegin
;
}
bool
operator
!=
(
const
conditional_iterator_impl
&
rhs
)
const
{
return
mBegin
!=
rhs
.
mBegin
;
}
template
<
typename
IRowType
,
typename
...
ITs
>
bool
operator
==
(
const
iterator_impl
<
IRowType
,
ITs
...
>
&
rhs
)
const
{
return
mBegin
==
rhs
;
}
template
<
typename
IRowType
,
typename
...
ITs
>
bool
operator
==
(
const
iterator_impl
<
IRowType
,
ITs
...
>
&
rhs
)
const
{
return
mBegin
==
rhs
;
}
template
<
typename
IRowType
,
typename
...
ITs
>
bool
operator
!=
(
const
iterator_impl
<
IRowType
,
ITs
...
>
&
rhs
)
const
{
return
mBegin
!=
rhs
;
}
template
<
typename
IRowType
,
typename
...
ITs
>
bool
operator
!=
(
const
iterator_impl
<
IRowType
,
ITs
...
>
&
rhs
)
const
{
return
mBegin
!=
rhs
;
}
private
:
Category
*
mCat
;
Category
*
mCat
;
base_iterator
mBegin
,
mEnd
;
const
Condition
*
mCondition
;
const
Condition
*
mCondition
;
};
using
iterator
=
conditional_iterator_impl
;
using
reference
=
typename
iterator
::
reference
;
conditional_iterator_proxy
(
Category
&
cat
,
row_iterator
pos
,
Condition
&&
cond
);
conditional_iterator_proxy
(
Category
&
cat
,
row_iterator
pos
,
Condition
&&
cond
);
template
<
std
::
size_t
TN
=
N
,
std
::
enable_if_t
<
TN
!=
0
,
bool
>
=
true
>
conditional_iterator_proxy
(
Category
&
cat
,
row_iterator
pos
,
Condition
&&
cond
,
char
const
*
const
columns
[
N
]);
template
<
std
::
size_t
TN
=
N
,
std
::
enable_if_t
<
TN
!=
0
,
bool
>
=
true
>
conditional_iterator_proxy
(
Category
&
cat
,
row_iterator
pos
,
Condition
&&
cond
,
char
const
*
const
columns
[
N
]);
conditional_iterator_proxy
(
conditional_iterator_proxy
&&
p
);
conditional_iterator_proxy
&
operator
=
(
conditional_iterator_proxy
&&
p
);
conditional_iterator_proxy
(
conditional_iterator_proxy
&&
p
);
conditional_iterator_proxy
&
operator
=
(
conditional_iterator_proxy
&&
p
);
conditional_iterator_proxy
(
const
conditional_iterator_proxy
&
)
=
delete
;
conditional_iterator_proxy
&
operator
=
(
const
conditional_iterator_proxy
&
)
=
delete
;
conditional_iterator_proxy
(
const
conditional_iterator_proxy
&
)
=
delete
;
conditional_iterator_proxy
&
operator
=
(
const
conditional_iterator_proxy
&
)
=
delete
;
iterator
begin
()
const
;
iterator
end
()
const
;
bool
empty
()
const
;
explicit
operator
bool
()
const
{
return
not
empty
();
}
explicit
operator
bool
()
const
{
return
not
empty
();
}
size_t
size
()
const
{
return
std
::
distance
(
begin
(),
end
());
}
size_t
size
()
const
{
return
std
::
distance
(
begin
(),
end
());
}
RowType
front
()
{
return
*
begin
();
}
RowType
front
()
{
return
*
begin
();
}
Category
&
category
()
const
{
return
*
mCat
;
}
Category
&
category
()
const
{
return
*
mCat
;
}
void
swap
(
conditional_iterator_proxy
&
rhs
);
void
swap
(
conditional_iterator_proxy
&
rhs
);
private
:
Category
*
mCat
;
Category
*
mCat
;
Condition
mCondition
;
row_iterator
mCBegin
,
mCEnd
;
std
::
array
<
size_t
,
N
>
mCix
;
std
::
array
<
size_t
,
N
>
mCix
;
};
// --------------------------------------------------------------------
...
...
@@ -1723,7 +1806,7 @@ class RowSet
};
// --------------------------------------------------------------------
// class Category acts as an STL container for Row objects
// class Category acts as an STL container for Row objects
class
Category
{
...
...
@@ -1732,16 +1815,16 @@ class Category
friend
class
Row
;
friend
class
detail
::
ItemReference
;
Category
(
Datablock
&
db
,
const
std
::
string
&
name
,
Validator
*
Validator
);
Category
(
const
Category
&
)
=
delete
;
Category
&
operator
=
(
const
Category
&
)
=
delete
;
Category
(
Datablock
&
db
,
const
std
::
string
&
name
,
Validator
*
Validator
);
Category
(
const
Category
&
)
=
delete
;
Category
&
operator
=
(
const
Category
&
)
=
delete
;
~
Category
();
const
std
::
string
name
()
const
{
return
mName
;
}
const
std
::
string
name
()
const
{
return
mName
;
}
using
iterator
=
iterator_impl
<
Row
>
;
using
const_iterator
=
iterator_impl
<
const
Row
>
;
iterator
begin
();
iterator
end
();
...
...
@@ -1753,115 +1836,119 @@ class Category
bool
empty
()
const
;
size_t
size
()
const
;
void
clear
();
Row
front
()
{
return
Row
(
mHead
);
}
Row
back
()
{
return
Row
(
mTail
);
}
Row
operator
[](
Condition
&&
cond
);
template
<
typename
...
Ts
,
size_t
N
>
iterator_proxy
<
Row
,
Ts
...
>
rows
(
char
const
*
const
(
&
columns
)[
N
])
Row
front
()
{
return
Row
(
mHead
);
}
Row
back
()
{
return
Row
(
mTail
);
}
Row
operator
[](
Condition
&&
cond
);
template
<
typename
...
Ts
,
size_t
N
>
iterator_proxy
<
Row
,
Ts
...
>
rows
(
char
const
*
const
(
&
columns
)[
N
])
{
static_assert
(
sizeof
...(
Ts
)
==
N
,
"The number of column titles should be equal to the number of types to return"
);
return
iterator_proxy
<
Row
,
Ts
...
>
(
*
this
,
begin
(),
columns
);
return
iterator_proxy
<
Row
,
Ts
...
>
(
*
this
,
begin
(),
columns
);
}
template
<
typename
...
Ts
,
typename
...
Ns
>
template
<
typename
...
Ts
,
typename
...
Ns
>
iterator_proxy
<
Row
,
Ts
...
>
rows
(
Ns
...
names
)
{
static_assert
(
sizeof
...(
Ts
)
==
sizeof
...(
Ns
),
"The number of column titles should be equal to the number of types to return"
);
return
iterator_proxy
<
Row
,
Ts
...
>
(
*
this
,
begin
(),
{
names
...
});
return
iterator_proxy
<
Row
,
Ts
...
>
(
*
this
,
begin
(),
{
names
...
});
}
conditional_iterator_proxy
<
Row
>
find
(
Condition
&&
cond
)
conditional_iterator_proxy
<
Row
>
find
(
Condition
&&
cond
)
{
return
find
(
cbegin
(),
std
::
forward
<
Condition
>
(
cond
));
}
conditional_iterator_proxy
<
Row
>
find
(
const_iterator
pos
,
Condition
&&
cond
)
conditional_iterator_proxy
<
Row
>
find
(
const_iterator
pos
,
Condition
&&
cond
)
{
return
{
*
this
,
pos
,
std
::
forward
<
Condition
>
(
cond
)
};
return
{
*
this
,
pos
,
std
::
forward
<
Condition
>
(
cond
)
};
}
template
<
typename
...
Ts
,
size_t
N
>
conditional_iterator_proxy
<
Row
,
Ts
...
>
find
(
Condition
&&
cond
,
char
const
*
const
(
&
columns
)[
N
])
template
<
typename
...
Ts
,
size_t
N
>
conditional_iterator_proxy
<
Row
,
Ts
...
>
find
(
Condition
&&
cond
,
char
const
*
const
(
&
columns
)[
N
])
{
static_assert
(
sizeof
...(
Ts
)
==
N
,
"The number of column titles should be equal to the number of types to return"
);
return
find
<
Ts
...
>
(
cbegin
(),
std
::
forward
<
Condition
>
(
cond
),
std
::
forward
<
char
const
*
const
[
N
]
>
(
columns
));
return
find
<
Ts
...
>
(
cbegin
(),
std
::
forward
<
Condition
>
(
cond
),
std
::
forward
<
char
const
*
const
[
N
]
>
(
columns
));
}
template
<
typename
...
Ts
,
size_t
N
>
conditional_iterator_proxy
<
Row
,
Ts
...
>
find
(
const_iterator
pos
,
Condition
&&
cond
,
char
const
*
const
(
&
columns
)[
N
])
template
<
typename
...
Ts
,
size_t
N
>
conditional_iterator_proxy
<
Row
,
Ts
...
>
find
(
const_iterator
pos
,
Condition
&&
cond
,
char
const
*
const
(
&
columns
)[
N
])
{
static_assert
(
sizeof
...(
Ts
)
==
N
,
"The number of column titles should be equal to the number of types to return"
);
return
{
*
this
,
pos
,
std
::
forward
<
Condition
>
(
cond
),
columns
};
return
{
*
this
,
pos
,
std
::
forward
<
Condition
>
(
cond
),
columns
};
}
// --------------------------------------------------------------------
// if you only expect a single row
Row
find1
(
Condition
&&
cond
)
Row
find1
(
Condition
&&
cond
)
{
return
find1
(
cbegin
(),
std
::
forward
<
Condition
>
(
cond
));
}
Row
find1
(
const_iterator
pos
,
Condition
&&
cond
)
Row
find1
(
const_iterator
pos
,
Condition
&&
cond
)
{
auto
h
=
find
(
pos
,
std
::
forward
<
Condition
>
(
cond
));
if
(
h
.
empty
())
throw
std
::
runtime_error
(
"No hits found"
);
if
(
h
.
size
()
!=
1
)
throw
std
::
runtime_error
(
"Hit not unique"
);
return
*
h
.
begin
();
}
template
<
typename
...
Ts
,
size_t
N
>
std
::
tuple
<
Ts
...
>
find1
(
Condition
&&
cond
,
char
const
*
const
(
&
columns
)[
N
])
template
<
typename
...
Ts
,
size_t
N
>
std
::
tuple
<
Ts
...
>
find1
(
Condition
&&
cond
,
char
const
*
const
(
&
columns
)[
N
])
{
static_assert
(
sizeof
...(
Ts
)
==
N
,
"The number of column titles should be equal to the number of types to return"
);
return
find1
<
Ts
...
>
(
cbegin
(),
std
::
forward
<
Condition
>
(
cond
),
std
::
forward
<
char
const
*
const
[
N
]
>
(
columns
));
return
find1
<
Ts
...
>
(
cbegin
(),
std
::
forward
<
Condition
>
(
cond
),
std
::
forward
<
char
const
*
const
[
N
]
>
(
columns
));
}
template
<
typename
...
Ts
,
size_t
N
>
std
::
tuple
<
Ts
...
>
find1
(
const_iterator
pos
,
Condition
&&
cond
,
char
const
*
const
(
&
columns
)[
N
])
template
<
typename
...
Ts
,
size_t
N
>
std
::
tuple
<
Ts
...
>
find1
(
const_iterator
pos
,
Condition
&&
cond
,
char
const
*
const
(
&
columns
)[
N
])
{
static_assert
(
sizeof
...(
Ts
)
==
N
,
"The number of column titles should be equal to the number of types to return"
);
auto
h
=
find
<
Ts
...
>
(
pos
,
std
::
forward
<
Condition
>
(
cond
),
std
::
forward
<
char
const
*
const
[
N
]
>
(
columns
));
auto
h
=
find
<
Ts
...
>
(
pos
,
std
::
forward
<
Condition
>
(
cond
),
std
::
forward
<
char
const
*
const
[
N
]
>
(
columns
));
if
(
h
.
empty
())
throw
std
::
runtime_error
(
"No hits found"
);
if
(
h
.
size
()
!=
1
)
throw
std
::
runtime_error
(
"Hit not unique"
);
return
*
h
.
begin
();
}
bool
exists
(
Condition
&&
cond
)
const
;
RowSet
orderBy
(
const
std
::
string
&
Item
)
{
return
orderBy
({
Item
});
}
bool
exists
(
Condition
&&
cond
)
const
;
RowSet
orderBy
(
const
std
::
string
&
Item
)
{
return
orderBy
({
Item
});
}
RowSet
orderBy
(
std
::
initializer_list
<
std
::
string
>
Items
);
std
::
tuple
<
Row
,
bool
>
emplace
(
Item
value
)
{
return
emplace
({
value
});
}
std
::
tuple
<
Row
,
bool
>
emplace
(
std
::
initializer_list
<
Item
>
values
)
{
return
emplace
(
values
.
begin
(),
values
.
end
());
}
std
::
tuple
<
Row
,
bool
>
emplace
(
Item
value
)
{
return
emplace
({
value
});
}
std
::
tuple
<
Row
,
bool
>
emplace
(
Row
r
);
template
<
class
Iter
>
std
::
tuple
<
Row
,
bool
>
emplace
(
Iter
b
,
Iter
e
);
std
::
tuple
<
Row
,
bool
>
emplace
(
std
::
initializer_list
<
Item
>
values
)
{
return
emplace
(
values
.
begin
(),
values
.
end
());
}
std
::
tuple
<
Row
,
bool
>
emplace
(
Row
r
);
size_t
erase
(
Condition
&&
cond
);
size_t
erase
(
Condition
&&
cond
,
std
::
function
<
void
(
const
Row
&
)
>&&
visit
);
template
<
class
Iter
>
std
::
tuple
<
Row
,
bool
>
emplace
(
Iter
b
,
Iter
e
);
size_t
erase
(
Condition
&&
cond
);
size_t
erase
(
Condition
&&
cond
,
std
::
function
<
void
(
const
Row
&
)
>
&&
visit
);
void
erase
(
Row
r
);
iterator
erase
(
iterator
ri
);
...
...
@@ -1872,67 +1959,67 @@ class Category
// erase without cascade, should only be used when speed is needed
size_t
erase_nocascade
(
Condition
&&
cond
)
size_t
erase_nocascade
(
Condition
&&
cond
)
{
return
erase_nocascade
(
std
::
forward
<
Condition
>
(
cond
),
[](
auto
r
){});
return
erase_nocascade
(
std
::
forward
<
Condition
>
(
cond
),
[](
auto
r
)
{});
}
size_t
erase_nocascade
(
Condition
&&
cond
,
std
::
function
<
void
(
const
Row
&
)
>&&
visit
)
size_t
erase_nocascade
(
Condition
&&
cond
,
std
::
function
<
void
(
const
Row
&
)
>
&&
visit
)
{
auto
savedValidator
=
mValidator
;
mValidator
=
nullptr
;
auto
result
=
erase
(
std
::
forward
<
Condition
>
(
cond
),
std
::
forward
<
std
::
function
<
void
(
const
Row
&
)
>>
(
visit
));
auto
result
=
erase
(
std
::
forward
<
Condition
>
(
cond
),
std
::
forward
<
std
::
function
<
void
(
const
Row
&
)
>>
(
visit
));
mValidator
=
savedValidator
;
return
result
;
}
void
eraseOrphans
(
Condition
&&
cond
);
void
eraseOrphans
(
Condition
&&
cond
);
/// an orphan is a row that is the child side of one or more
/// links and for which there is no single parent left.
bool
isOrphan
(
Row
r
);
bool
hasParent
(
Row
r
,
const
Category
&
parentCat
,
const
ValidateLink
&
link
)
const
;
bool
hasParent
(
Row
r
,
const
Category
&
parentCat
,
const
ValidateLink
&
link
)
const
;
bool
hasChildren
(
Row
r
)
const
;
bool
hasParents
(
Row
r
)
const
;
RowSet
getChildren
(
Row
r
,
Category
&
childCat
);
RowSet
getChildren
(
Row
r
,
const
char
*
childCat
);
RowSet
getChildren
(
Row
r
,
Category
&
childCat
);
RowSet
getChildren
(
Row
r
,
const
char
*
childCat
);
RowSet
getParents
(
Row
r
,
Category
&
parentCat
);
RowSet
getParents
(
Row
r
,
const
char
*
parentCat
);
RowSet
getParents
(
Row
r
,
Category
&
parentCat
);
RowSet
getParents
(
Row
r
,
const
char
*
parentCat
);
RowSet
getLinked
(
Row
r
,
Category
&
cat
);
RowSet
getLinked
(
Row
r
,
const
char
*
cat
);
RowSet
getLinked
(
Row
r
,
Category
&
cat
);
RowSet
getLinked
(
Row
r
,
const
char
*
cat
);
bool
isValid
();
void
validateLinks
()
const
;
const
Validator
&
getValidator
()
const
;
const
ValidateCategory
*
getCatValidator
()
const
{
return
mCatValidator
;
}
const
Validator
&
getValidator
()
const
;
const
ValidateCategory
*
getCatValidator
()
const
{
return
mCatValidator
;
}
Datablock
&
db
()
{
return
mDb
;
}
void
setValidator
(
Validator
*
v
);
Datablock
&
db
()
{
return
mDb
;
}
void
setValidator
(
Validator
*
v
);
iset
fields
()
const
;
iset
mandatoryFields
()
const
;
iset
keyFields
()
const
;
std
::
set
<
size_t
>
keyFieldsByIndex
()
const
;
void
drop
(
const
std
::
string
&
field
);
void
getTagOrder
(
std
::
vector
<
std
::
string
>&
tags
)
const
;
void
drop
(
const
std
::
string
&
field
);
void
getTagOrder
(
std
::
vector
<
std
::
string
>
&
tags
)
const
;
// return index for known column, or the next available column index
size_t
getColumnIndex
(
const
std
::
string
&
name
)
const
;
bool
hasColumn
(
const
std
::
string
&
name
)
const
;
const
std
::
string
&
getColumnName
(
size_t
columnIndex
)
const
;
size_t
getColumnIndex
(
const
std
::
string
&
name
)
const
;
bool
hasColumn
(
const
std
::
string
&
name
)
const
;
const
std
::
string
&
getColumnName
(
size_t
columnIndex
)
const
;
std
::
vector
<
std
::
string
>
getColumnNames
()
const
;
void
reorderByIndex
();
void
sort
(
std
::
function
<
int
(
const
Row
&
,
const
Row
&
)
>
comparator
);
void
sort
(
std
::
function
<
int
(
const
Row
&
,
const
Row
&
)
>
comparator
);
// --------------------------------------------------------------------
/// Rename a single column in the rows that match \a cond to value \a value
...
...
@@ -1942,7 +2029,7 @@ class Category
void
update_value
(
Condition
&&
cond
,
const
std
::
string
&
tag
,
const
std
::
string
&
value
)
{
update_value
(
RowSet
{
*
this
,
std
::
move
(
cond
)
},
tag
,
value
);
update_value
(
RowSet
{
*
this
,
std
::
move
(
cond
)
},
tag
,
value
);
}
void
update_value
(
RowSet
&&
rows
,
const
std
::
string
&
tag
,
const
std
::
string
&
value
);
...
...
@@ -1954,25 +2041,30 @@ class Category
std
::
string
getUniqueID
(
std
::
function
<
std
::
string
(
int
)
>
generator
=
cif
::
cifIdForNumber
);
std
::
string
getUniqueID
(
const
std
::
string
&
prefix
)
{
return
getUniqueID
([
prefix
](
int
nr
)
{
return
prefix
+
std
::
to_string
(
nr
);
});
return
getUniqueID
([
prefix
](
int
nr
)
{
return
prefix
+
std
::
to_string
(
nr
);
});
}
private
:
// --------------------------------------------------------------------
// for debugging
void
write
(
std
::
ostream
&
os
);
void
write
(
std
::
ostream
&
os
,
const
std
::
vector
<
std
::
string
>&
order
);
void
write
(
std
::
ostream
&
os
,
const
std
::
vector
<
size_t
>&
order
,
bool
includeEmptyColumns
);
friend
bool
operator
==
(
const
Category
&
lhs
,
const
Category
&
rhs
);
size_t
addColumn
(
const
std
::
string
&
name
);
Datablock
&
mDb
;
std
::
string
mName
;
Validator
*
mValidator
;
const
ValidateCategory
*
mCatValidator
=
nullptr
;
std
::
vector
<
ItemColumn
>
mColumns
;
ItemRow
*
mHead
;
ItemRow
*
mTail
;
class
CatIndex
*
mIndex
;
private
:
void
write
(
std
::
ostream
&
os
);
void
write
(
std
::
ostream
&
os
,
const
std
::
vector
<
std
::
string
>
&
order
);
void
write
(
std
::
ostream
&
os
,
const
std
::
vector
<
size_t
>
&
order
,
bool
includeEmptyColumns
);
size_t
addColumn
(
const
std
::
string
&
name
);
Datablock
&
mDb
;
std
::
string
mName
;
Validator
*
mValidator
;
const
ValidateCategory
*
mCatValidator
=
nullptr
;
std
::
vector
<
ItemColumn
>
mColumns
;
ItemRow
*
mHead
;
ItemRow
*
mTail
;
class
CatIndex
*
mIndex
;
};
// --------------------------------------------------------------------
...
...
@@ -1984,82 +2076,96 @@ class File
friend
class
Validator
;
File
();
File
(
std
::
istream
&
is
,
bool
validate
=
false
);
File
(
const
std
::
string
&
path
,
bool
validate
=
false
);
File
(
File
&&
rhs
);
File
(
const
File
&
rhs
)
=
delete
;
File
&
operator
=
(
const
File
&
rhs
)
=
delete
;
File
(
std
::
istream
&
is
,
bool
validate
=
false
);
File
(
const
std
::
string
&
path
,
bool
validate
=
false
);
File
(
File
&&
rhs
);
File
(
const
File
&
rhs
)
=
delete
;
File
&
operator
=
(
const
File
&
rhs
)
=
delete
;
~
File
();
void
load
(
const
std
::
string
&
p
);
void
save
(
const
std
::
string
&
p
);
void
load
(
const
std
::
string
&
p
);
void
save
(
const
std
::
string
&
p
);
void
load
(
std
::
istream
&
is
);
void
save
(
std
::
ostream
&
os
);
void
load
(
std
::
istream
&
is
);
void
save
(
std
::
ostream
&
os
);
/// \brief Load only the data block \a datablock from the mmCIF file
void
load
(
std
::
istream
&
is
,
const
std
::
string
&
datablock
);
void
load
(
std
::
istream
&
is
,
const
std
::
string
&
datablock
);
void
save
(
std
::
ostream
&
os
,
const
std
::
vector
<
std
::
string
>&
order
)
{
write
(
os
,
order
);
}
void
write
(
std
::
ostream
&
os
,
const
std
::
vector
<
std
::
string
>&
order
);
void
save
(
std
::
ostream
&
os
,
const
std
::
vector
<
std
::
string
>
&
order
)
{
write
(
os
,
order
);
}
void
write
(
std
::
ostream
&
os
,
const
std
::
vector
<
std
::
string
>
&
order
);
void
loadDictionary
();
// load the default dictionary, that is mmcifDdl in this case
void
loadDictionary
(
const
char
*
dict
);
// load one of the compiled in dictionaries
void
loadDictionary
(
std
::
istream
&
is
);
// load dictionary from input stream
void
loadDictionary
();
// load the default dictionary, that is mmcifDdl in this case
void
loadDictionary
(
const
char
*
dict
);
// load one of the compiled in dictionaries
void
loadDictionary
(
std
::
istream
&
is
);
// load dictionary from input stream
bool
isValid
();
void
validateLinks
()
const
;
Datablock
&
firstDatablock
()
const
Datablock
&
firstDatablock
()
const
{
if
(
mHead
==
nullptr
)
throw
std
::
runtime_error
(
"No datablocks in file"
);
return
*
mHead
;
}
void
append
(
Datablock
*
e
);
Datablock
*
get
(
const
std
::
string
&
name
)
const
;
Datablock
&
operator
[](
const
std
::
string
&
name
);
Datablock
&
firstDatablock
()
{
if
(
mHead
==
nullptr
)
throw
std
::
runtime_error
(
"No datablocks in file"
);
return
*
mHead
;
}
void
append
(
Datablock
*
e
);
struct
iterator
Datablock
*
get
(
const
std
::
string
&
name
)
const
;
Datablock
&
operator
[](
const
std
::
string
&
name
);
struct
iterator
{
using
iterator_category
=
std
::
forward_iterator_tag
;
using
value_type
=
Datablock
;
using
difference_type
=
std
::
ptrdiff_t
;
using
pointer
=
value_type
*
;
using
reference
=
value_type
&
;
iterator
(
Datablock
*
db
)
:
mCurrent
(
db
)
{}
reference
operator
*
()
{
return
*
mCurrent
;
}
pointer
operator
->
()
{
return
mCurrent
;
}
iterator
&
operator
++
();
iterator
operator
++
(
int
)
{
iterator
result
(
*
this
);
this
->
operator
++
();
return
result
;
}
bool
operator
==
(
const
iterator
&
rhs
)
const
{
return
mCurrent
==
rhs
.
mCurrent
;
}
bool
operator
!=
(
const
iterator
&
rhs
)
const
{
return
not
(
mCurrent
==
rhs
.
mCurrent
);
}
using
pointer
=
value_type
*
;
using
reference
=
value_type
&
;
iterator
(
Datablock
*
db
)
:
mCurrent
(
db
)
{
}
reference
operator
*
()
{
return
*
mCurrent
;
}
pointer
operator
->
()
{
return
mCurrent
;
}
iterator
&
operator
++
();
iterator
operator
++
(
int
)
{
iterator
result
(
*
this
);
this
->
operator
++
();
return
result
;
}
bool
operator
==
(
const
iterator
&
rhs
)
const
{
return
mCurrent
==
rhs
.
mCurrent
;
}
bool
operator
!=
(
const
iterator
&
rhs
)
const
{
return
not
(
mCurrent
==
rhs
.
mCurrent
);
}
private
:
Datablock
*
mCurrent
;
Datablock
*
mCurrent
;
};
iterator
begin
()
const
;
iterator
end
()
const
;
bool
empty
()
const
{
return
mHead
==
nullptr
;
}
const
Validator
&
getValidator
()
const
;
void
getTagOrder
(
std
::
vector
<
std
::
string
>&
tags
)
const
;
private
:
bool
empty
()
const
{
return
mHead
==
nullptr
;
}
void
setValidator
(
Validator
*
v
);
const
Validator
&
getValidator
()
const
;
void
getTagOrder
(
std
::
vector
<
std
::
string
>
&
tags
)
const
;
Datablock
*
mHead
;
Validator
*
mValidator
;
private
:
void
setValidator
(
Validator
*
v
);
Datablock
*
mHead
;
Validator
*
mValidator
;
};
// --------------------------------------------------------------------
...
...
@@ -2068,64 +2174,67 @@ class File
namespace
detail
{
template
<
typename
T
>
inline
bool
AnyIsConditionImpl
<
T
>::
test
(
const
Category
&
c
,
const
Row
&
r
)
const
{
bool
result
=
false
;
for
(
auto
&
f
:
c
.
fields
())
template
<
typename
T
>
inline
bool
AnyIsConditionImpl
<
T
>::
test
(
const
Category
&
c
,
const
Row
&
r
)
const
{
try
bool
result
=
false
;
for
(
auto
&
f
:
c
.
fields
())
{
if
(
r
[
f
].
as
<
valueType
>
()
==
mValue
)
try
{
if
(
r
[
f
].
as
<
valueType
>
()
==
mValue
)
{
result
=
true
;
break
;
}
}
catch
(...)
{
result
=
true
;
break
;
}
}
catch
(...)
{}
return
result
;
}
return
result
;
}
inline
bool
AnyMatchesConditionImpl
::
test
(
const
Category
&
c
,
const
Row
&
r
)
const
{
bool
result
=
false
;
for
(
auto
&
f
:
c
.
fields
())
inline
bool
AnyMatchesConditionImpl
::
test
(
const
Category
&
c
,
const
Row
&
r
)
const
{
try
bool
result
=
false
;
for
(
auto
&
f
:
c
.
fields
())
{
if
(
std
::
regex_match
(
r
[
f
].
as
<
std
::
string
>
(),
mRx
))
try
{
if
(
std
::
regex_match
(
r
[
f
].
as
<
std
::
string
>
(),
mRx
))
{
result
=
true
;
break
;
}
}
catch
(...)
{
result
=
true
;
break
;
}
}
catch
(...)
{}
return
result
;
}
return
result
;
}
}
}
// namespace detail
// these should be here, as I learned today
inline
void
swap
(
cif
::
Row
&
a
,
cif
::
Row
&
b
)
inline
void
swap
(
cif
::
Row
&
a
,
cif
::
Row
&
b
)
{
a
.
swap
(
b
);
}
inline
void
swap
(
cif
::
detail
::
ItemReference
&
a
,
cif
::
detail
::
ItemReference
&
b
)
inline
void
swap
(
cif
::
detail
::
ItemReference
&
a
,
cif
::
detail
::
ItemReference
&
b
)
{
a
.
swap
(
b
);
}
// --------------------------------------------------------------------
template
<
typename
RowType
,
typename
...
Ts
>
iterator_proxy
<
RowType
,
Ts
...
>::
iterator_proxy
(
Category
&
cat
,
row_iterator
pos
,
char
const
*
const
columns
[
N
])
template
<
typename
RowType
,
typename
...
Ts
>
iterator_proxy
<
RowType
,
Ts
...
>::
iterator_proxy
(
Category
&
cat
,
row_iterator
pos
,
char
const
*
const
columns
[
N
])
:
mCat
(
&
cat
)
,
mCBegin
(
pos
)
,
mCEnd
(
cat
.
end
())
...
...
@@ -2134,8 +2243,8 @@ iterator_proxy<RowType, Ts...>::iterator_proxy(Category& cat, row_iterator pos,
mCix
[
i
]
=
mCat
->
getColumnIndex
(
columns
[
i
]);
}
template
<
typename
RowType
,
typename
...
Ts
>
iterator_proxy
<
RowType
,
Ts
...
>::
iterator_proxy
(
Category
&
cat
,
row_iterator
pos
,
std
::
initializer_list
<
char
const
*>
columns
)
template
<
typename
RowType
,
typename
...
Ts
>
iterator_proxy
<
RowType
,
Ts
...
>::
iterator_proxy
(
Category
&
cat
,
row_iterator
pos
,
std
::
initializer_list
<
char
const
*>
columns
)
:
mCat
(
&
cat
)
,
mCBegin
(
pos
)
,
mCEnd
(
cat
.
end
())
...
...
@@ -2143,30 +2252,36 @@ iterator_proxy<RowType, Ts...>::iterator_proxy(Category& cat, row_iterator pos,
// static_assert(columns.size() == N, "The list of column names should be exactly the same as the list of requested columns");
std
::
size_t
i
=
0
;
for
(
auto
column
:
columns
)
for
(
auto
column
:
columns
)
mCix
[
i
++
]
=
mCat
->
getColumnIndex
(
column
);
}
// --------------------------------------------------------------------
template
<
typename
RowType
,
typename
...
Ts
>
template
<
typename
RowType
,
typename
...
Ts
>
conditional_iterator_proxy
<
RowType
,
Ts
...
>::
conditional_iterator_impl
::
conditional_iterator_impl
(
Category
&
cat
,
row_iterator
pos
,
const
Condition
&
cond
,
const
std
::
array
<
size_t
,
N
>&
cix
)
:
mCat
(
&
cat
),
mBegin
(
pos
,
cix
),
mEnd
(
cat
.
end
(),
cix
),
mCondition
(
&
cond
)
Category
&
cat
,
row_iterator
pos
,
const
Condition
&
cond
,
const
std
::
array
<
size_t
,
N
>
&
cix
)
:
mCat
(
&
cat
)
,
mBegin
(
pos
,
cix
)
,
mEnd
(
cat
.
end
(),
cix
)
,
mCondition
(
&
cond
)
{
}
template
<
typename
RowType
,
typename
...
Ts
>
conditional_iterator_proxy
<
RowType
,
Ts
...
>::
conditional_iterator_proxy
(
conditional_iterator_proxy
&&
p
)
:
mCat
(
nullptr
),
mCBegin
(
p
.
mCBegin
),
mCEnd
(
p
.
mCEnd
),
mCix
(
p
.
mCix
)
template
<
typename
RowType
,
typename
...
Ts
>
conditional_iterator_proxy
<
RowType
,
Ts
...
>::
conditional_iterator_proxy
(
conditional_iterator_proxy
&&
p
)
:
mCat
(
nullptr
)
,
mCBegin
(
p
.
mCBegin
)
,
mCEnd
(
p
.
mCEnd
)
,
mCix
(
p
.
mCix
)
{
std
::
swap
(
mCat
,
p
.
mCat
);
std
::
swap
(
mCix
,
p
.
mCix
);
mCondition
.
swap
(
p
.
mCondition
);
}
template
<
typename
RowType
,
typename
...
Ts
>
conditional_iterator_proxy
<
RowType
,
Ts
...
>::
conditional_iterator_proxy
(
Category
&
cat
,
row_iterator
pos
,
Condition
&&
cond
)
template
<
typename
RowType
,
typename
...
Ts
>
conditional_iterator_proxy
<
RowType
,
Ts
...
>::
conditional_iterator_proxy
(
Category
&
cat
,
row_iterator
pos
,
Condition
&&
cond
)
:
mCat
(
&
cat
)
,
mCondition
(
std
::
move
(
cond
))
,
mCBegin
(
pos
)
...
...
@@ -2178,9 +2293,9 @@ conditional_iterator_proxy<RowType, Ts...>::conditional_iterator_proxy(Category&
++
mCBegin
;
}
template
<
typename
RowType
,
typename
...
Ts
>
template
<
std
::
size_t
TN
,
std
::
enable_if_t
<
TN
!=
0
,
bool
>>
conditional_iterator_proxy
<
RowType
,
Ts
...
>::
conditional_iterator_proxy
(
Category
&
cat
,
row_iterator
pos
,
Condition
&&
cond
,
const
char
*
const
columns
[
N
])
template
<
typename
RowType
,
typename
...
Ts
>
template
<
std
::
size_t
TN
,
std
::
enable_if_t
<
TN
!=
0
,
bool
>>
conditional_iterator_proxy
<
RowType
,
Ts
...
>::
conditional_iterator_proxy
(
Category
&
cat
,
row_iterator
pos
,
Condition
&&
cond
,
const
char
*
const
columns
[
N
])
:
mCat
(
&
cat
)
,
mCondition
(
std
::
move
(
cond
))
,
mCBegin
(
pos
)
...
...
@@ -2195,33 +2310,33 @@ conditional_iterator_proxy<RowType, Ts...>::conditional_iterator_proxy(Category&
mCix
[
i
]
=
mCat
->
getColumnIndex
(
columns
[
i
]);
}
template
<
typename
RowType
,
typename
...
Ts
>
conditional_iterator_proxy
<
RowType
,
Ts
...
>
&
conditional_iterator_proxy
<
RowType
,
Ts
...
>::
operator
=
(
conditional_iterator_proxy
&&
p
)
template
<
typename
RowType
,
typename
...
Ts
>
conditional_iterator_proxy
<
RowType
,
Ts
...
>
&
conditional_iterator_proxy
<
RowType
,
Ts
...
>::
operator
=
(
conditional_iterator_proxy
&&
p
)
{
swap
(
p
);
return
*
this
;
}
template
<
typename
RowType
,
typename
...
Ts
>
template
<
typename
RowType
,
typename
...
Ts
>
typename
conditional_iterator_proxy
<
RowType
,
Ts
...
>::
iterator
conditional_iterator_proxy
<
RowType
,
Ts
...
>::
begin
()
const
{
return
iterator
(
*
mCat
,
mCBegin
,
mCondition
,
mCix
);
}
template
<
typename
RowType
,
typename
...
Ts
>
template
<
typename
RowType
,
typename
...
Ts
>
typename
conditional_iterator_proxy
<
RowType
,
Ts
...
>::
iterator
conditional_iterator_proxy
<
RowType
,
Ts
...
>::
end
()
const
{
return
iterator
(
*
mCat
,
mCEnd
,
mCondition
,
mCix
);
}
template
<
typename
RowType
,
typename
...
Ts
>
template
<
typename
RowType
,
typename
...
Ts
>
bool
conditional_iterator_proxy
<
RowType
,
Ts
...
>::
empty
()
const
{
return
mCBegin
==
mCEnd
;
}
template
<
typename
RowType
,
typename
...
Ts
>
void
conditional_iterator_proxy
<
RowType
,
Ts
...
>::
swap
(
conditional_iterator_proxy
&
rhs
)
template
<
typename
RowType
,
typename
...
Ts
>
void
conditional_iterator_proxy
<
RowType
,
Ts
...
>::
swap
(
conditional_iterator_proxy
&
rhs
)
{
std
::
swap
(
mCat
,
rhs
.
mCat
);
mCondition
.
swap
(
rhs
.
mCondition
);
...
...
@@ -2230,5 +2345,4 @@ void conditional_iterator_proxy<RowType, Ts...>::swap(conditional_iterator_proxy
std
::
swap
(
mCix
,
rhs
.
mCix
);
}
}
}
// namespace cif
include/cif++/Structure.hpp
View file @
345c4778
...
...
@@ -199,15 +199,15 @@ class Residue
// constructor for waters
Residue
(
const
Structure
&
structure
,
const
std
::
string
&
compoundID
,
const
std
::
string
&
asymID
,
const
std
::
string
&
authSeqID
);
const
std
::
string
&
asymID
,
const
std
::
string
&
authSeqID
);
// constructor for a residue without a sequence number
Residue
(
const
Structure
&
structure
,
const
std
::
string
&
compoundID
,
const
std
::
string
&
asymID
);
const
std
::
string
&
asymID
);
// constructor for a residue with a sequence number
Residue
(
const
Structure
&
structure
,
const
std
::
string
&
compoundID
,
const
std
::
string
&
asymID
,
int
seqID
,
const
std
::
string
&
authSeqID
);
const
std
::
string
&
asymID
,
int
seqID
,
const
std
::
string
&
authSeqID
);
Residue
(
const
Residue
&
rhs
)
=
delete
;
Residue
&
operator
=
(
const
Residue
&
rhs
)
=
delete
;
...
...
@@ -297,7 +297,7 @@ class Monomer : public Residue
Monomer
&
operator
=
(
Monomer
&&
rhs
);
Monomer
(
const
Polymer
&
polymer
,
size_t
index
,
int
seqID
,
const
std
::
string
&
authSeqID
,
const
std
::
string
&
compoundID
);
const
std
::
string
&
compoundID
);
bool
is_first_in_chain
()
const
;
bool
is_last_in_chain
()
const
;
...
...
@@ -397,6 +397,8 @@ class File : public std::enable_shared_from_this<File>
File
(
const
File
&
)
=
delete
;
File
&
operator
=
(
const
File
&
)
=
delete
;
cif
::
Datablock
&
createDatablock
(
const
std
::
string
&
name
);
void
load
(
const
std
::
string
&
path
);
void
save
(
const
std
::
string
&
path
);
...
...
@@ -450,7 +452,7 @@ class Structure
// Atom getAtomByLocation(Point pt, float maxDistance) const;
Atom
getAtomByLabel
(
const
std
::
string
&
atomID
,
const
std
::
string
&
asymID
,
const
std
::
string
&
compID
,
int
seqID
,
const
std
::
string
&
altID
=
""
);
const
std
::
string
&
compID
,
int
seqID
,
const
std
::
string
&
altID
=
""
);
/// \brief Get a residue, if \a seqID is zero, the non-polymers are searched
const
Residue
&
getResidue
(
const
std
::
string
&
asymID
,
const
std
::
string
&
compID
,
int
seqID
=
0
)
const
;
...
...
@@ -458,7 +460,7 @@ class Structure
// map between auth and label locations
std
::
tuple
<
std
::
string
,
int
,
std
::
string
>
MapAuthToLabel
(
const
std
::
string
&
asymID
,
const
std
::
string
&
seqID
,
const
std
::
string
&
compID
,
const
std
::
string
&
insCode
=
""
);
const
std
::
string
&
seqID
,
const
std
::
string
&
compID
,
const
std
::
string
&
insCode
=
""
);
std
::
tuple
<
std
::
string
,
std
::
string
,
std
::
string
,
std
::
string
>
MapLabelToAuth
(
const
std
::
string
&
asymID
,
int
seqID
,
const
std
::
string
&
compID
);
...
...
@@ -480,7 +482,20 @@ class Structure
void
swapAtoms
(
Atom
&
a1
,
Atom
&
a2
);
// swap the labels for these atoms
void
moveAtom
(
Atom
&
a
,
Point
p
);
// move atom to a new location
void
changeResidue
(
const
Residue
&
res
,
const
std
::
string
&
newCompound
,
const
std
::
vector
<
std
::
tuple
<
std
::
string
,
std
::
string
>>
&
remappedAtoms
);
const
std
::
vector
<
std
::
tuple
<
std
::
string
,
std
::
string
>>
&
remappedAtoms
);
/// \brief Create a new non-polymer entity, returns new ID
/// \param data The data to use to fill the entity
/// \param mon_id The mon_id for the new nonpoly
/// \param name The name of the nonpoly
/// \return The ID of the created entity
std
::
string
createEntityNonPoly
(
std
::
vector
<
cif
::
Item
>
data
,
const
std
::
string
&
mon_id
);
/// \brief Create a new NonPolymer struct_asym with atoms constructed from \a atom_data, returns asym_id
/// \param entity_id The entity ID of the new nonpoly
/// \param atoms The array of atom data fields
/// \return The newly create asym ID
std
::
string
createNonpoly
(
const
std
::
string
&
entity_id
,
const
std
::
vector
<
cif
::
Item
>
&
atoms
);
/// To sort the atoms in order of model > asym-id > res-id > atom-id
/// Will asssign new atom_id's to all atoms. Be carefull
...
...
src/Cif++.cpp
View file @
345c4778
...
...
@@ -26,26 +26,26 @@
#include <cassert>
#include <
stack
>
#include <
tuple
>
#include <
fstream
>
#include <
numeric
>
#include <regex>
#include <set>
#include <stack>
#include <tuple>
#include <unordered_map>
#include <numeric>
#include <fstream>
#include <filesystem>
#include <boost/algorithm/string.hpp>
#include <boost/iostreams/filtering_stream.hpp>
#include <boost/iostreams/filter/bzip2.hpp>
#include <boost/iostreams/filter/gzip.hpp>
#include <boost/iostreams/filtering_stream.hpp>
#include <boost/logic/tribool.hpp>
#include "cif++/Cif++.hpp"
#include "cif++/CifParser.hpp"
#include "cif++/CifValidator.hpp"
#include "cif++/CifUtils.hpp"
#include "cif++/CifValidator.hpp"
namespace
ba
=
boost
::
algorithm
;
namespace
io
=
boost
::
iostreams
;
...
...
@@ -56,34 +56,35 @@ namespace cif
CIFPP_EXPORT
int
VERBOSE
=
0
;
static
const
char
*
kEmptyResult
=
""
;
static
const
char
*
kEmptyResult
=
""
;
// --------------------------------------------------------------------
// most internal data structures are stored as linked lists
// Item values are stored in a simple struct. They should be const anyway
struct
ItemValue
{
ItemValue
*
mNext
;
uint32_t
mColumnIndex
;
char
mText
[
4
];
ItemValue
(
const
char
*
v
,
size_t
columnIndex
);
ItemValue
*
mNext
;
uint32_t
mColumnIndex
;
char
mText
[
4
];
ItemValue
(
const
char
*
v
,
size_t
columnIndex
);
~
ItemValue
();
bool
empty
()
const
{
return
mText
[
0
]
==
0
or
((
mText
[
0
]
==
'.'
or
mText
[
0
]
==
'?'
)
and
mText
[
1
]
==
0
);
}
bool
null
()
const
{
return
mText
[
0
]
==
'.'
and
mText
[
1
]
==
0
;
}
bool
unknown
()
const
{
return
mText
[
0
]
==
'?'
and
mText
[
1
]
==
0
;
}
void
*
operator
new
(
size_t
size
,
size_t
dataSize
);
void
operator
delete
(
void
*
p
);
void
operator
delete
(
void
*
p
,
size_t
dataSize
);
bool
empty
()
const
{
return
mText
[
0
]
==
0
or
((
mText
[
0
]
==
'.'
or
mText
[
0
]
==
'?'
)
and
mText
[
1
]
==
0
);
}
bool
null
()
const
{
return
mText
[
0
]
==
'.'
and
mText
[
1
]
==
0
;
}
bool
unknown
()
const
{
return
mText
[
0
]
==
'?'
and
mText
[
1
]
==
0
;
}
void
*
operator
new
(
size_t
size
,
size_t
dataSize
);
void
operator
delete
(
void
*
p
);
void
operator
delete
(
void
*
p
,
size_t
dataSize
);
};
// --------------------------------------------------------------------
ItemValue
::
ItemValue
(
const
char
*
value
,
size_t
columnIndex
)
:
mNext
(
nullptr
),
mColumnIndex
(
uint32_t
(
columnIndex
))
ItemValue
::
ItemValue
(
const
char
*
value
,
size_t
columnIndex
)
:
mNext
(
nullptr
)
,
mColumnIndex
(
uint32_t
(
columnIndex
))
{
assert
(
columnIndex
<
std
::
numeric_limits
<
uint32_t
>::
max
());
strcpy
(
mText
,
value
);
...
...
@@ -101,17 +102,17 @@ ItemValue::~ItemValue()
}
}
void
*
ItemValue
::
operator
new
(
size_t
size
,
size_t
dataSize
)
void
*
ItemValue
::
operator
new
(
size_t
size
,
size_t
dataSize
)
{
return
malloc
(
size
-
4
+
dataSize
+
1
);
}
void
ItemValue
::
operator
delete
(
void
*
p
)
void
ItemValue
::
operator
delete
(
void
*
p
)
{
free
(
p
);
}
void
ItemValue
::
operator
delete
(
void
*
p
,
size_t
dataSize
)
void
ItemValue
::
operator
delete
(
void
*
p
,
size_t
dataSize
)
{
free
(
p
);
}
...
...
@@ -121,8 +122,8 @@ void ItemValue::operator delete(void* p, size_t dataSize)
struct
ItemColumn
{
std
::
string
mName
;
// store lower-case, for optimization
const
ValidateItem
*
mValidator
;
std
::
string
mName
;
// store lower-case, for optimization
const
ValidateItem
*
mValidator
;
};
// itemRow contains the actual values for a Row in a Category
...
...
@@ -132,8 +133,8 @@ struct ItemRow
~
ItemRow
();
void
drop
(
size_t
columnIx
);
const
char
*
c_str
(
size_t
columnIx
)
const
;
const
char
*
c_str
(
size_t
columnIx
)
const
;
std
::
string
str
()
const
{
std
::
stringstream
s
;
...
...
@@ -144,21 +145,21 @@ struct ItemRow
s
<<
mCategory
->
getColumnName
(
v
->
mColumnIndex
)
<<
':'
<<
v
->
mText
;
if
(
v
->
mNext
!=
nullptr
)
s
<<
", "
;
if
(
v
->
mNext
!=
nullptr
)
s
<<
", "
;
}
s
<<
'}'
;
return
s
.
str
();
}
ItemRow
*
mNext
;
Category
*
mCategory
;
ItemValue
*
mValues
;
uint32_t
mLineNr
=
0
;
ItemRow
*
mNext
;
Category
*
mCategory
;
ItemValue
*
mValues
;
uint32_t
mLineNr
=
0
;
};
std
::
ostream
&
operator
<<
(
std
::
ostream
&
os
,
const
ItemRow
&
r
)
std
::
ostream
&
operator
<<
(
std
::
ostream
&
os
,
const
ItemRow
&
r
)
{
os
<<
r
.
mCategory
->
name
()
<<
'['
;
for
(
auto
iv
=
r
.
mValues
;
iv
!=
nullptr
;
iv
=
iv
->
mNext
)
...
...
@@ -168,7 +169,7 @@ std::ostream& operator<<(std::ostream& os, const ItemRow& r)
os
<<
','
;
}
os
<<
']'
;
return
os
;
}
...
...
@@ -219,10 +220,10 @@ void ItemRow::drop(size_t columnIx)
#endif
}
const
char
*
ItemRow
::
c_str
(
size_t
columnIx
)
const
const
char
*
ItemRow
::
c_str
(
size_t
columnIx
)
const
{
const
char
*
result
=
kEmptyResult
;
const
char
*
result
=
kEmptyResult
;
for
(
auto
v
=
mValues
;
v
!=
nullptr
;
v
=
v
->
mNext
)
{
if
(
v
->
mColumnIndex
==
columnIx
)
...
...
@@ -240,119 +241,121 @@ const char* ItemRow::c_str(size_t columnIx) const
namespace
detail
{
ItemReference
&
ItemReference
::
operator
=
(
const
std
::
string
&
value
)
{
if
(
mConst
)
throw
std
::
logic_error
(
"Attempt to write to a constant row"
);
ItemReference
&
ItemReference
::
operator
=
(
const
std
::
string
&
value
)
{
if
(
mConst
)
throw
std
::
logic_error
(
"Attempt to write to a constant row"
);
if
(
mRow
.
mData
==
nullptr
)
throw
std
::
logic_error
(
"Attempt to write to an uninitialized row"
);
if
(
mRow
.
mData
==
nullptr
)
throw
std
::
logic_error
(
"Attempt to write to an uninitialized row"
);
mRow
.
assign
(
mName
,
value
,
false
);
return
*
this
;
}
mRow
.
assign
(
mName
,
value
,
false
);
return
*
this
;
}
const
char
*
ItemReference
::
c_str
()
const
{
const
char
*
result
=
kEmptyResult
;
if
(
mRow
.
mData
!=
nullptr
/* and mRow.mData->mCategory != nullptr*/
)
const
char
*
ItemReference
::
c_str
()
const
{
// assert(mRow.mData->mCategory)
;
for
(
auto
iv
=
mRow
.
mData
->
mValues
;
iv
!=
nullptr
;
iv
=
iv
->
mNext
)
const
char
*
result
=
kEmptyResult
;
if
(
mRow
.
mData
!=
nullptr
/* and mRow.mData->mCategory != nullptr*/
)
{
if
(
iv
->
mColumnIndex
==
mColumn
)
// assert(mRow.mData->mCategory);
for
(
auto
iv
=
mRow
.
mData
->
mValues
;
iv
!=
nullptr
;
iv
=
iv
->
mNext
)
{
if
(
iv
->
mText
[
0
]
!=
'.'
or
iv
->
mText
[
1
]
!=
0
)
result
=
iv
->
mText
;
break
;
if
(
iv
->
mColumnIndex
==
mColumn
)
{
if
(
iv
->
mText
[
0
]
!=
'.'
or
iv
->
mText
[
1
]
!=
0
)
result
=
iv
->
mText
;
break
;
}
}
}
return
result
;
}
return
result
;
}
const
char
*
ItemReference
::
c_str
(
const
char
*
defaultValue
)
const
{
const
char
*
result
=
defaultValue
;
if
(
mRow
.
mData
!=
nullptr
and
mRow
.
mData
->
mCategory
!=
nullptr
)
const
char
*
ItemReference
::
c_str
(
const
char
*
defaultValue
)
const
{
for
(
auto
iv
=
mRow
.
mData
->
mValues
;
iv
!=
nullptr
;
iv
=
iv
->
mNext
)
const
char
*
result
=
defaultValue
;
if
(
mRow
.
mData
!=
nullptr
and
mRow
.
mData
->
mCategory
!=
nullptr
)
{
if
(
iv
->
mColumnIndex
==
mColumn
)
for
(
auto
iv
=
mRow
.
mData
->
mValues
;
iv
!=
nullptr
;
iv
=
iv
->
mNext
)
{
// only really non-empty values
if
(
iv
->
mText
[
0
]
!=
0
and
((
iv
->
mText
[
0
]
!=
'.'
and
iv
->
mText
[
0
]
!=
'?'
)
or
iv
->
mText
[
1
]
!=
0
))
result
=
iv
->
mText
;
if
(
iv
->
mColumnIndex
==
mColumn
)
{
// only really non-empty values
if
(
iv
->
mText
[
0
]
!=
0
and
((
iv
->
mText
[
0
]
!=
'.'
and
iv
->
mText
[
0
]
!=
'?'
)
or
iv
->
mText
[
1
]
!=
0
))
result
=
iv
->
mText
;
break
;
break
;
}
}
}
if
(
result
==
defaultValue
and
mColumn
<
mRow
.
mData
->
mCategory
->
mColumns
.
size
())
// not found, perhaps the category has a default defined?
{
auto
iv
=
mRow
.
mData
->
mCategory
->
mColumns
[
mColumn
].
mValidator
;
if
(
iv
!=
nullptr
and
not
iv
->
mDefault
.
empty
())
result
=
iv
->
mDefault
.
c_str
();
if
(
result
==
defaultValue
and
mColumn
<
mRow
.
mData
->
mCategory
->
mColumns
.
size
())
// not found, perhaps the category has a default defined?
{
auto
iv
=
mRow
.
mData
->
mCategory
->
mColumns
[
mColumn
].
mValidator
;
if
(
iv
!=
nullptr
and
not
iv
->
mDefault
.
empty
())
result
=
iv
->
mDefault
.
c_str
();
}
}
return
result
;
}
return
result
;
}
bool
ItemReference
::
empty
()
const
{
return
c_str
()
==
kEmptyResult
;
}
bool
ItemReference
::
empty
()
const
{
return
c_str
()
==
kEmptyResult
;
}
bool
ItemReference
::
is_null
()
const
{
boost
::
tribool
result
;
if
(
mRow
.
mData
!=
nullptr
and
mRow
.
mData
->
mCategory
!=
nullptr
)
bool
ItemReference
::
is_null
()
const
{
for
(
auto
iv
=
mRow
.
mData
->
mValues
;
iv
!=
nullptr
;
iv
=
iv
->
mNext
)
boost
::
tribool
result
;
if
(
mRow
.
mData
!=
nullptr
and
mRow
.
mData
->
mCategory
!=
nullptr
)
{
if
(
iv
->
mColumnIndex
==
mColumn
)
for
(
auto
iv
=
mRow
.
mData
->
mValues
;
iv
!=
nullptr
;
iv
=
iv
->
mNext
)
{
result
=
iv
->
mText
[
0
]
==
'.'
and
iv
->
mText
[
1
]
==
0
;
break
;
if
(
iv
->
mColumnIndex
==
mColumn
)
{
result
=
iv
->
mText
[
0
]
==
'.'
and
iv
->
mText
[
1
]
==
0
;
break
;
}
}
}
if
(
result
==
boost
::
indeterminate
and
mColumn
<
mRow
.
mData
->
mCategory
->
mColumns
.
size
())
// not found, perhaps the category has a default defined?
{
auto
iv
=
mRow
.
mData
->
mCategory
->
mColumns
[
mColumn
].
mValidator
;
if
(
iv
!=
nullptr
)
result
=
iv
->
mDefaultIsNull
;
if
(
result
==
boost
::
indeterminate
and
mColumn
<
mRow
.
mData
->
mCategory
->
mColumns
.
size
())
// not found, perhaps the category has a default defined?
{
auto
iv
=
mRow
.
mData
->
mCategory
->
mColumns
[
mColumn
].
mValidator
;
if
(
iv
!=
nullptr
)
result
=
iv
->
mDefaultIsNull
;
}
}
return
result
?
true
:
false
;
}
return
result
?
true
:
false
;
}
void
ItemReference
::
swap
(
ItemReference
&
b
)
{
Row
::
swap
(
mColumn
,
mRow
.
mData
,
b
.
mRow
.
mData
);
}
void
ItemReference
::
swap
(
ItemReference
&
b
)
{
Row
::
swap
(
mColumn
,
mRow
.
mData
,
b
.
mRow
.
mData
);
}
std
::
ostream
&
operator
<<
(
std
::
ostream
&
os
,
ItemReference
&
item
)
{
os
<<
item
.
c_str
();
return
os
;
}
std
::
ostream
&
operator
<<
(
std
::
ostream
&
os
,
ItemReference
&
item
)
{
os
<<
item
.
c_str
();
return
os
;
}
}
}
// namespace detail
// --------------------------------------------------------------------
// Datablock implementation
Datablock
::
Datablock
(
const
std
::
string
&
name
)
:
mName
(
name
),
mValidator
(
nullptr
),
mNext
(
nullptr
)
Datablock
::
Datablock
(
const
std
::
string
&
name
)
:
mName
(
name
)
,
mValidator
(
nullptr
)
,
mNext
(
nullptr
)
{
}
...
...
@@ -361,18 +364,18 @@ Datablock::~Datablock()
delete
mNext
;
}
std
::
string
Datablock
::
firstItem
(
const
std
::
string
&
tag
)
const
std
::
string
Datablock
::
firstItem
(
const
std
::
string
&
tag
)
const
{
std
::
string
result
;
std
::
string
catName
,
itemName
;
std
::
tie
(
catName
,
itemName
)
=
splitTagName
(
tag
);
for
(
auto
&
cat
:
mCategories
)
for
(
auto
&
cat
:
mCategories
)
{
if
(
iequals
(
cat
.
name
(),
catName
))
{
for
(
auto
row
:
cat
)
for
(
auto
row
:
cat
)
{
result
=
row
[
itemName
].
as
<
std
::
string
>
();
break
;
...
...
@@ -385,33 +388,41 @@ std::string Datablock::firstItem(const std::string& tag) const
return
result
;
}
auto
Datablock
::
emplace
(
const
std
::
string
&
name
)
->
std
::
tuple
<
iterator
,
bool
>
auto
Datablock
::
emplace
(
const
std
::
string
&
name
)
->
std
::
tuple
<
iterator
,
bool
>
{
bool
isNew
=
false
;
iterator
i
=
find_if
(
begin
(),
end
(),
[
name
](
const
Category
&
cat
)
->
bool
iterator
i
=
find_if
(
begin
(),
end
(),
[
name
](
const
Category
&
cat
)
->
bool
{
return
iequals
(
cat
.
name
(),
name
);
});
if
(
i
==
end
())
{
isNew
=
true
;
i
=
mCategories
.
emplace
(
end
(),
*
this
,
name
,
mValidator
);
}
return
std
::
make_tuple
(
i
,
isNew
);
}
Category
&
Datablock
::
operator
[](
const
std
::
string
&
name
)
Category
&
Datablock
::
operator
[](
const
std
::
string
&
name
)
{
iterator
i
;
std
::
tie
(
i
,
std
::
ignore
)
=
emplace
(
name
);
return
*
i
;
}
Category
*
Datablock
::
get
(
const
std
::
string
&
name
)
Category
*
Datablock
::
get
(
const
std
::
string
&
name
)
{
auto
i
=
find_if
(
begin
(),
end
(),
[
name
](
const
Category
&
cat
)
->
bool
auto
i
=
find_if
(
begin
(),
end
(),
[
name
](
const
Category
&
cat
)
->
bool
{
return
iequals
(
cat
.
name
(),
name
);
});
return
i
==
end
()
?
nullptr
:
&*
i
;
}
const
Category
*
Datablock
::
get
(
const
std
::
string
&
name
)
const
{
auto
i
=
find_if
(
begin
(),
end
(),
[
name
](
const
Category
&
cat
)
->
bool
{
return
iequals
(
cat
.
name
(),
name
);
});
return
i
==
end
()
?
nullptr
:
&*
i
;
}
...
...
@@ -421,188 +432,257 @@ bool Datablock::isValid()
throw
std
::
runtime_error
(
"Validator not specified"
);
bool
result
=
true
;
for
(
auto
&
cat
:
*
this
)
for
(
auto
&
cat
:
*
this
)
result
=
cat
.
isValid
()
and
result
;
return
result
;
}
void
Datablock
::
validateLinks
()
const
{
for
(
auto
&
cat
:
*
this
)
for
(
auto
&
cat
:
*
this
)
cat
.
validateLinks
();
}
void
Datablock
::
setValidator
(
Validator
*
v
)
void
Datablock
::
setValidator
(
Validator
*
v
)
{
mValidator
=
v
;
for
(
auto
&
cat
:
*
this
)
for
(
auto
&
cat
:
*
this
)
cat
.
setValidator
(
v
);
}
void
Datablock
::
add_software
(
const
std
::
string
&
name
,
const
std
::
string
&
classification
,
const
std
::
string
&
versionNr
,
const
std
::
string
&
versionDate
)
void
Datablock
::
add_software
(
const
std
::
string
&
name
,
const
std
::
string
&
classification
,
const
std
::
string
&
versionNr
,
const
std
::
string
&
versionDate
)
{
Category
&
cat
=
operator
[](
"software"
);
Category
&
cat
=
operator
[](
"software"
);
auto
ordNr
=
cat
.
size
()
+
1
;
// TODO: should we check this ordinal number???
cat
.
emplace
({
{
"pdbx_ordinal"
,
ordNr
},
{
"name"
,
name
},
{
"version"
,
versionNr
},
{
"date"
,
versionDate
},
{
"classification"
,
classification
}
});
cat
.
emplace
({{
"pdbx_ordinal"
,
ordNr
},
{
"name"
,
name
},
{
"version"
,
versionNr
},
{
"date"
,
versionDate
},
{
"classification"
,
classification
}});
}
void
Datablock
::
getTagOrder
(
std
::
vector
<
std
::
string
>
&
tags
)
const
void
Datablock
::
getTagOrder
(
std
::
vector
<
std
::
string
>
&
tags
)
const
{
for
(
auto
&
cat
:
*
this
)
for
(
auto
&
cat
:
*
this
)
cat
.
getTagOrder
(
tags
);
}
void
Datablock
::
write
(
std
::
ostream
&
os
)
void
Datablock
::
write
(
std
::
ostream
&
os
)
{
os
<<
"data_"
<<
mName
<<
std
::
endl
<<
"# "
<<
std
::
endl
;
// mmcif support, sort of. First write the 'entry' Category
// and if it exists, _AND_ we have a Validator, write out the
// audit_conform record.
for
(
auto
&
cat
:
mCategories
)
for
(
auto
&
cat
:
mCategories
)
{
if
(
cat
.
name
()
==
"entry"
)
{
cat
.
write
(
os
);
if
(
mValidator
!=
nullptr
)
{
Category
auditConform
(
*
this
,
"audit_conform"
,
nullptr
);
auditConform
.
emplace
({
{
"dict_name"
,
mValidator
->
dictName
()
},
{
"dict_version"
,
mValidator
->
dictVersion
()
}
});
auditConform
.
emplace
({{
"dict_name"
,
mValidator
->
dictName
()},
{
"dict_version"
,
mValidator
->
dictVersion
()}});
auditConform
.
write
(
os
);
}
break
;
}
}
for
(
auto
&
cat
:
mCategories
)
for
(
auto
&
cat
:
mCategories
)
{
if
(
cat
.
name
()
!=
"entry"
and
cat
.
name
()
!=
"audit_conform"
)
cat
.
write
(
os
);
}
}
void
Datablock
::
write
(
std
::
ostream
&
os
,
const
std
::
vector
<
std
::
string
>&
order
)
void
Datablock
::
write
(
std
::
ostream
&
os
,
const
std
::
vector
<
std
::
string
>
&
order
)
{
os
<<
"data_"
<<
mName
<<
std
::
endl
<<
"# "
<<
std
::
endl
;
std
::
vector
<
std
::
string
>
catOrder
;
for
(
auto
&
o
:
order
)
for
(
auto
&
o
:
order
)
{
std
::
string
cat
,
Item
;
std
::
tie
(
cat
,
Item
)
=
splitTagName
(
o
);
if
(
find_if
(
catOrder
.
rbegin
(),
catOrder
.
rend
(),
[
cat
](
const
std
::
string
&
s
)
->
bool
{
return
iequals
(
cat
,
s
);
})
==
catOrder
.
rend
())
if
(
find_if
(
catOrder
.
rbegin
(),
catOrder
.
rend
(),
[
cat
](
const
std
::
string
&
s
)
->
bool
{
return
iequals
(
cat
,
s
);
})
==
catOrder
.
rend
())
catOrder
.
push_back
(
cat
);
}
for
(
auto
&
c
:
catOrder
)
for
(
auto
&
c
:
catOrder
)
{
auto
cat
=
get
(
c
);
if
(
cat
==
nullptr
)
continue
;
std
::
vector
<
std
::
string
>
items
;
for
(
auto
&
o
:
order
)
for
(
auto
&
o
:
order
)
{
std
::
string
catName
,
Item
;
std
::
tie
(
catName
,
Item
)
=
splitTagName
(
o
);
if
(
catName
==
c
)
items
.
push_back
(
Item
);
}
cat
->
write
(
os
,
items
);
}
// for any Category we missed in the catOrder
for
(
auto
&
cat
:
mCategories
)
for
(
auto
&
cat
:
mCategories
)
{
if
(
find_if
(
catOrder
.
begin
(),
catOrder
.
end
(),
[
&
](
const
std
::
string
&
s
)
->
bool
{
return
iequals
(
cat
.
name
(),
s
);
})
!=
catOrder
.
end
())
if
(
find_if
(
catOrder
.
begin
(),
catOrder
.
end
(),
[
&
](
const
std
::
string
&
s
)
->
bool
{
return
iequals
(
cat
.
name
(),
s
);
})
!=
catOrder
.
end
())
continue
;
cat
.
write
(
os
);
}
// // mmcif support, sort of. First write the 'entry' Category
// // and if it exists, _AND_ we have a Validator, write out the
// // auditConform record.
//
// for (auto& cat: mCategories)
// {
// if (cat.name() == "entry")
// {
// cat.write(os);
//
// if (mValidator != nullptr)
// {
// Category auditConform(*this, "audit_conform", nullptr);
// auditConform.emplace({
// { "dict_name", mValidator->dictName() },
// { "dict_version", mValidator->dictVersion() }
// });
// auditConform.write(os);
// }
//
// break;
// }
// }
//
// for (auto& cat: mCategories)
// {
// if (cat.name() != "entry" and cat.name() != "audit_conform")
// cat.write(os);
// }
}
// --------------------------------------------------------------------
//
// // mmcif support, sort of. First write the 'entry' Category
// // and if it exists, _AND_ we have a Validator, write out the
// // auditConform record.
//
// for (auto& cat: mCategories)
// {
// if (cat.name() == "entry")
// {
// cat.write(os);
//
// if (mValidator != nullptr)
// {
// Category auditConform(*this, "audit_conform", nullptr);
// auditConform.emplace({
// { "dict_name", mValidator->dictName() },
// { "dict_version", mValidator->dictVersion() }
// });
// auditConform.write(os);
// }
//
// break;
// }
// }
//
// for (auto& cat: mCategories)
// {
// if (cat.name() != "entry" and cat.name() != "audit_conform")
// cat.write(os);
// }
}
namespace
detail
bool
operator
==
(
const
cif
::
Datablock
&
dbA
,
const
cif
::
Datablock
&
dbB
)
{
std
::
vector
<
std
::
string
>
catA
,
catB
;
void
KeyCompareConditionImpl
::
prepare
(
const
Category
&
c
)
{
mItemIx
=
c
.
getColumnIndex
(
mItemTag
);
for
(
auto
&
cat
:
dbA
)
{
if
(
not
cat
.
empty
())
catA
.
push_back
(
cat
.
name
());
}
sort
(
catA
.
begin
(),
catA
.
end
());
for
(
auto
&
cat
:
dbB
)
{
if
(
not
cat
.
empty
())
catB
.
push_back
(
cat
.
name
());
}
sort
(
catB
.
begin
(),
catB
.
end
());
// loop over categories twice, to group output
// First iteration is to list missing categories.
std
::
vector
<
std
::
string
>
missingA
,
missingB
;
auto
catA_i
=
catA
.
begin
(),
catB_i
=
catB
.
begin
();
while
(
catA_i
!=
catA
.
end
()
and
catB_i
!=
catB
.
end
())
{
if
(
not
iequals
(
*
catA_i
,
*
catB_i
))
return
false
;
++
catA_i
,
++
catB_i
;
}
if
(
catA_i
!=
catA
.
end
()
or
catB_i
!=
catB
.
end
())
return
false
;
// Second loop, now compare category values
catA_i
=
catA
.
begin
(),
catB_i
=
catB
.
begin
();
auto
cv
=
c
.
getCatValidator
();
if
(
cv
)
while
(
catA_i
!=
catA
.
end
()
and
catB_i
!=
catB
.
end
())
{
auto
iv
=
cv
->
getValidatorForItem
(
mItemTag
);
if
(
iv
!=
nullptr
and
iv
->
mType
!=
nullptr
)
std
::
string
nA
=
*
catA_i
;
ba
::
to_lower
(
nA
);
std
::
string
nB
=
*
catB_i
;
ba
::
to_lower
(
nB
);
int
d
=
nA
.
compare
(
nB
);
if
(
d
>
0
)
++
catB_i
;
else
if
(
d
<
0
)
++
catA_i
;
else
{
auto
type
=
iv
->
mType
;
mCaseInsensitive
=
type
->
mPrimitiveType
==
DDL_PrimitiveType
::
UChar
;
if
(
not
(
*
dbA
.
get
(
*
catA_i
)
==
*
dbB
.
get
(
*
catB_i
)))
return
false
;
++
catA_i
;
++
catB_i
;
}
}
return
true
;
}
void
KeyIsEmptyConditionImpl
::
prepare
(
const
Category
&
c
)
std
::
ostream
&
operator
<<
(
std
::
ostream
&
os
,
const
Datablock
&
data
)
{
mItemIx
=
c
.
getColumnIndex
(
mItemTag
);
// whoohoo... this sucks!
const_cast
<
Datablock
&>
(
data
).
write
(
os
);
return
os
;
}
void
KeyMatchesConditionImpl
::
prepare
(
const
Category
&
c
)
// --------------------------------------------------------------------
//
namespace
detail
{
mItemIx
=
c
.
getColumnIndex
(
mItemTag
);
}
}
void
KeyCompareConditionImpl
::
prepare
(
const
Category
&
c
)
{
mItemIx
=
c
.
getColumnIndex
(
mItemTag
);
auto
cv
=
c
.
getCatValidator
();
if
(
cv
)
{
auto
iv
=
cv
->
getValidatorForItem
(
mItemTag
);
if
(
iv
!=
nullptr
and
iv
->
mType
!=
nullptr
)
{
auto
type
=
iv
->
mType
;
mCaseInsensitive
=
type
->
mPrimitiveType
==
DDL_PrimitiveType
::
UChar
;
}
}
}
void
KeyIsEmptyConditionImpl
::
prepare
(
const
Category
&
c
)
{
mItemIx
=
c
.
getColumnIndex
(
mItemTag
);
}
void
KeyMatchesConditionImpl
::
prepare
(
const
Category
&
c
)
{
mItemIx
=
c
.
getColumnIndex
(
mItemTag
);
}
}
// namespace detail
// --------------------------------------------------------------------
//
...
...
@@ -611,77 +691,76 @@ void KeyMatchesConditionImpl::prepare(const Category& c)
class
RowComparator
{
public
:
RowComparator
(
Category
*
cat
)
RowComparator
(
Category
*
cat
)
:
RowComparator
(
cat
,
cat
->
getCatValidator
()
->
mKeys
.
begin
(),
cat
->
getCatValidator
()
->
mKeys
.
end
())
{
}
template
<
typename
KeyIter
>
RowComparator
(
Category
*
cat
,
KeyIter
b
,
KeyIter
e
);
int
operator
()(
const
ItemRow
*
a
,
const
ItemRow
*
b
)
const
;
int
operator
()(
const
Row
&
a
,
const
Row
&
b
)
const
template
<
typename
KeyIter
>
RowComparator
(
Category
*
cat
,
KeyIter
b
,
KeyIter
e
);
int
operator
()(
const
ItemRow
*
a
,
const
ItemRow
*
b
)
const
;
int
operator
()(
const
Row
&
a
,
const
Row
&
b
)
const
{
return
operator
()(
a
.
mData
,
b
.
mData
);
}
private
:
typedef
std
::
function
<
int
(
const
char
*
,
const
char
*
)
>
compareFunc
;
typedef
std
::
function
<
int
(
const
char
*
,
const
char
*
)
>
compareFunc
;
typedef
std
::
tuple
<
size_t
,
compareFunc
>
keyComp
;
typedef
std
::
tuple
<
size_t
,
compareFunc
>
keyComp
;
std
::
vector
<
keyComp
>
mComp
;
std
::
vector
<
keyComp
>
mComp
;
};
template
<
typename
KeyIter
>
RowComparator
::
RowComparator
(
Category
*
cat
,
KeyIter
b
,
KeyIter
e
)
template
<
typename
KeyIter
>
RowComparator
::
RowComparator
(
Category
*
cat
,
KeyIter
b
,
KeyIter
e
)
{
auto
cv
=
cat
->
getCatValidator
();
for
(
auto
ki
=
b
;
ki
!=
e
;
++
ki
)
{
std
::
string
k
=
*
ki
;
size_t
ix
=
cat
->
getColumnIndex
(
k
);
auto
iv
=
cv
->
getValidatorForItem
(
k
);
if
(
iv
==
nullptr
)
throw
std
::
runtime_error
(
"Incomplete dictionary, no Item Validator for Key "
+
k
);
auto
tv
=
iv
->
mType
;
if
(
tv
==
nullptr
)
throw
std
::
runtime_error
(
"Incomplete dictionary, no type Validator for Item "
+
k
);
using
namespace
std
::
placeholders
;
mComp
.
emplace_back
(
ix
,
std
::
bind
(
&
ValidateType
::
compare
,
tv
,
_1
,
_2
));
}
}
int
RowComparator
::
operator
()(
const
ItemRow
*
a
,
const
ItemRow
*
b
)
const
int
RowComparator
::
operator
()(
const
ItemRow
*
a
,
const
ItemRow
*
b
)
const
{
assert
(
a
);
assert
(
b
);
int
d
=
0
;
for
(
auto
&
c
:
mComp
)
for
(
auto
&
c
:
mComp
)
{
size_t
k
;
compareFunc
f
;
std
::
tie
(
k
,
f
)
=
c
;
const
char
*
ka
=
a
->
c_str
(
k
);
const
char
*
kb
=
b
->
c_str
(
k
);
const
char
*
ka
=
a
->
c_str
(
k
);
const
char
*
kb
=
b
->
c_str
(
k
);
d
=
f
(
ka
,
kb
);
if
(
d
!=
0
)
break
;
}
return
d
;
}
...
...
@@ -693,148 +772,152 @@ int RowComparator::operator()(const ItemRow* a, const ItemRow* b) const
class
CatIndex
{
public
:
CatIndex
(
Category
*
cat
);
CatIndex
(
Category
*
cat
);
~
CatIndex
();
ItemRow
*
find
(
ItemRow
*
k
)
const
;
void
insert
(
ItemRow
*
r
);
void
erase
(
ItemRow
*
r
);
ItemRow
*
find
(
ItemRow
*
k
)
const
;
void
insert
(
ItemRow
*
r
);
void
erase
(
ItemRow
*
r
);
// batch create
void
reconstruct
();
// reorder the ItemRow's and returns new head and tail
std
::
tuple
<
ItemRow
*
,
ItemRow
*>
reorder
()
std
::
tuple
<
ItemRow
*
,
ItemRow
*>
reorder
()
{
std
::
tuple
<
ItemRow
*
,
ItemRow
*>
result
=
std
::
make_tuple
(
nullptr
,
nullptr
);
std
::
tuple
<
ItemRow
*
,
ItemRow
*>
result
=
std
::
make_tuple
(
nullptr
,
nullptr
);
if
(
mRoot
!=
nullptr
)
{
entry
*
head
=
findMin
(
mRoot
);
entry
*
tail
=
reorder
(
mRoot
);
entry
*
head
=
findMin
(
mRoot
);
entry
*
tail
=
reorder
(
mRoot
);
tail
->
mRow
->
mNext
=
nullptr
;
result
=
std
::
make_tuple
(
head
->
mRow
,
tail
->
mRow
);
}
return
result
;
}
size_t
size
()
const
;
// bool isValid() const;
private
:
// bool isValid() const;
private
:
struct
entry
{
entry
(
ItemRow
*
r
)
:
mRow
(
r
),
mLeft
(
nullptr
),
mRight
(
nullptr
),
mRed
(
true
)
{}
entry
(
ItemRow
*
r
)
:
mRow
(
r
)
,
mLeft
(
nullptr
)
,
mRight
(
nullptr
)
,
mRed
(
true
)
{
}
~
entry
()
{
delete
mLeft
;
delete
mRight
;
}
ItemRow
*
mRow
;
entry
*
mLeft
;
entry
*
mRight
;
bool
mRed
;
ItemRow
*
mRow
;
entry
*
mLeft
;
entry
*
mRight
;
bool
mRed
;
};
entry
*
insert
(
entry
*
h
,
ItemRow
*
v
);
entry
*
erase
(
entry
*
h
,
ItemRow
*
k
);
entry
*
insert
(
entry
*
h
,
ItemRow
*
v
);
entry
*
erase
(
entry
*
h
,
ItemRow
*
k
);
// void validate(entry* h, bool isParentRed, uint32_t blackDepth, uint32_t& minBlack, uint32_t& maxBlack) const;
// void validate(entry* h, bool isParentRed, uint32_t blackDepth, uint32_t& minBlack, uint32_t& maxBlack) const;
entry
*
rotateLeft
(
entry
*
h
)
entry
*
rotateLeft
(
entry
*
h
)
{
entry
*
x
=
h
->
mRight
;
entry
*
x
=
h
->
mRight
;
h
->
mRight
=
x
->
mLeft
;
x
->
mLeft
=
h
;
x
->
mRed
=
h
->
mRed
;
h
->
mRed
=
true
;
return
x
;
}
entry
*
rotateRight
(
entry
*
h
)
entry
*
rotateRight
(
entry
*
h
)
{
entry
*
x
=
h
->
mLeft
;
entry
*
x
=
h
->
mLeft
;
h
->
mLeft
=
x
->
mRight
;
x
->
mRight
=
h
;
x
->
mRed
=
h
->
mRed
;
h
->
mRed
=
true
;
return
x
;
}
void
flipColour
(
entry
*
h
)
void
flipColour
(
entry
*
h
)
{
h
->
mRed
=
not
h
->
mRed
;
if
(
h
->
mLeft
!=
nullptr
)
h
->
mLeft
->
mRed
=
not
h
->
mLeft
->
mRed
;
if
(
h
->
mRight
!=
nullptr
)
h
->
mRight
->
mRed
=
not
h
->
mRight
->
mRed
;
}
bool
isRed
(
entry
*
h
)
const
bool
isRed
(
entry
*
h
)
const
{
return
h
!=
nullptr
and
h
->
mRed
;
}
entry
*
moveRedLeft
(
entry
*
h
)
entry
*
moveRedLeft
(
entry
*
h
)
{
flipColour
(
h
);
if
(
h
->
mRight
!=
nullptr
and
isRed
(
h
->
mRight
->
mLeft
))
{
h
->
mRight
=
rotateRight
(
h
->
mRight
);
h
=
rotateLeft
(
h
);
flipColour
(
h
);
}
return
h
;
}
entry
*
moveRedRight
(
entry
*
h
)
entry
*
moveRedRight
(
entry
*
h
)
{
flipColour
(
h
);
if
(
h
->
mLeft
!=
nullptr
and
isRed
(
h
->
mLeft
->
mLeft
))
{
h
=
rotateRight
(
h
);
flipColour
(
h
);
}
return
h
;
}
entry
*
fixUp
(
entry
*
h
)
entry
*
fixUp
(
entry
*
h
)
{
if
(
isRed
(
h
->
mRight
))
h
=
rotateLeft
(
h
);
if
(
isRed
(
h
->
mLeft
)
and
isRed
(
h
->
mLeft
->
mLeft
))
h
=
rotateRight
(
h
);
if
(
isRed
(
h
->
mLeft
)
and
isRed
(
h
->
mRight
))
flipColour
(
h
);
return
h
;
}
entry
*
findMin
(
entry
*
h
)
entry
*
findMin
(
entry
*
h
)
{
while
(
h
->
mLeft
!=
nullptr
)
h
=
h
->
mLeft
;
return
h
;
}
entry
*
eraseMin
(
entry
*
h
)
entry
*
eraseMin
(
entry
*
h
)
{
if
(
h
->
mLeft
==
nullptr
)
{
...
...
@@ -845,44 +928,46 @@ class CatIndex
{
if
(
not
isRed
(
h
->
mLeft
)
and
not
isRed
(
h
->
mLeft
->
mLeft
))
h
=
moveRedLeft
(
h
);
h
->
mLeft
=
eraseMin
(
h
->
mLeft
);
h
=
fixUp
(
h
);
}
return
h
;
}
// Fix mNext fields for rows in order of this index
entry
*
reorder
(
entry
*
e
)
entry
*
reorder
(
entry
*
e
)
{
auto
result
=
e
;
if
(
e
->
mLeft
!=
nullptr
)
{
auto
l
=
reorder
(
e
->
mLeft
);
l
->
mRow
->
mNext
=
e
->
mRow
;
}
if
(
e
->
mRight
!=
nullptr
)
{
auto
mr
=
findMin
(
e
->
mRight
);
e
->
mRow
->
mNext
=
mr
->
mRow
;
result
=
reorder
(
e
->
mRight
);
}
return
result
;
}
Category
&
mCat
;
RowComparator
mComp
;
entry
*
mRoot
;
Category
&
mCat
;
RowComparator
mComp
;
entry
*
mRoot
;
};
CatIndex
::
CatIndex
(
Category
*
cat
)
:
mCat
(
*
cat
),
mComp
(
cat
),
mRoot
(
nullptr
)
CatIndex
::
CatIndex
(
Category
*
cat
)
:
mCat
(
*
cat
)
,
mComp
(
cat
)
,
mRoot
(
nullptr
)
{
}
...
...
@@ -891,9 +976,9 @@ CatIndex::~CatIndex()
delete
mRoot
;
}
ItemRow
*
CatIndex
::
find
(
ItemRow
*
k
)
const
ItemRow
*
CatIndex
::
find
(
ItemRow
*
k
)
const
{
const
entry
*
r
=
mRoot
;
const
entry
*
r
=
mRoot
;
while
(
r
!=
nullptr
)
{
int
d
=
mComp
(
k
,
r
->
mRow
);
...
...
@@ -904,24 +989,26 @@ ItemRow* CatIndex::find(ItemRow* k) const
else
break
;
}
return
r
?
r
->
mRow
:
nullptr
;
}
void
CatIndex
::
insert
(
ItemRow
*
k
)
void
CatIndex
::
insert
(
ItemRow
*
k
)
{
mRoot
=
insert
(
mRoot
,
k
);
mRoot
->
mRed
=
false
;
}
CatIndex
::
entry
*
CatIndex
::
insert
(
entry
*
h
,
ItemRow
*
v
)
CatIndex
::
entry
*
CatIndex
::
insert
(
entry
*
h
,
ItemRow
*
v
)
{
if
(
h
==
nullptr
)
return
new
entry
(
v
);
int
d
=
mComp
(
v
,
h
->
mRow
);
if
(
d
<
0
)
h
->
mLeft
=
insert
(
h
->
mLeft
,
v
);
else
if
(
d
>
0
)
h
->
mRight
=
insert
(
h
->
mRight
,
v
);
if
(
d
<
0
)
h
->
mLeft
=
insert
(
h
->
mLeft
,
v
);
else
if
(
d
>
0
)
h
->
mRight
=
insert
(
h
->
mRight
,
v
);
else
throw
std
::
runtime_error
(
"Duplicate Key violation, cat: "
+
mCat
.
name
()
+
" values: "
+
v
->
str
());
...
...
@@ -930,21 +1017,21 @@ CatIndex::entry* CatIndex::insert(entry* h, ItemRow* v)
if
(
isRed
(
h
->
mLeft
)
and
isRed
(
h
->
mLeft
->
mLeft
))
h
=
rotateRight
(
h
);
if
(
isRed
(
h
->
mLeft
)
and
isRed
(
h
->
mRight
))
if
(
isRed
(
h
->
mLeft
)
and
isRed
(
h
->
mRight
))
flipColour
(
h
);
return
h
;
}
void
CatIndex
::
erase
(
ItemRow
*
k
)
void
CatIndex
::
erase
(
ItemRow
*
k
)
{
mRoot
=
erase
(
mRoot
,
k
);
if
(
mRoot
!=
nullptr
)
mRoot
->
mRed
=
false
;
}
CatIndex
::
entry
*
CatIndex
::
erase
(
entry
*
h
,
ItemRow
*
k
)
CatIndex
::
entry
*
CatIndex
::
erase
(
entry
*
h
,
ItemRow
*
k
)
{
if
(
mComp
(
k
,
h
->
mRow
)
<
0
)
{
...
...
@@ -960,18 +1047,18 @@ CatIndex::entry* CatIndex::erase(entry* h, ItemRow* k)
{
if
(
isRed
(
h
->
mLeft
))
h
=
rotateRight
(
h
);
if
(
mComp
(
k
,
h
->
mRow
)
==
0
and
h
->
mRight
==
nullptr
)
{
delete
h
;
return
nullptr
;
}
if
(
h
->
mRight
!=
nullptr
)
{
if
(
not
isRed
(
h
->
mRight
)
and
not
isRed
(
h
->
mRight
->
mLeft
))
h
=
moveRedRight
(
h
);
if
(
mComp
(
k
,
h
->
mRow
)
==
0
)
{
h
->
mRow
=
findMin
(
h
->
mRight
)
->
mRow
;
...
...
@@ -981,7 +1068,7 @@ CatIndex::entry* CatIndex::erase(entry* h, ItemRow* k)
h
->
mRight
=
erase
(
h
->
mRight
,
k
);
}
}
return
fixUp
(
h
);
}
...
...
@@ -989,133 +1076,133 @@ void CatIndex::reconstruct()
{
delete
mRoot
;
mRoot
=
nullptr
;
for
(
auto
r
:
mCat
)
for
(
auto
r
:
mCat
)
insert
(
r
.
mData
);
// 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 std::set the red/black flag correctly in that case.
// std::vector<ItemRow*> rows;
// transform(mCat.begin(), mCat.end(), backInserter(rows),
// [](Row r) -> ItemRow* { 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](ItemRow* a, ItemRow* 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),
// [](ItemRow* 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();
// 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 std::set the red/black flag correctly in that case.
// std::vector<ItemRow*> rows;
// transform(mCat.begin(), mCat.end(), backInserter(rows),
// [](Row r) -> ItemRow* { 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](ItemRow* a, ItemRow* 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),
// [](ItemRow* 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
CatIndex
::
size
()
const
{
std
::
stack
<
entry
*>
s
;
std
::
stack
<
entry
*>
s
;
s
.
push
(
mRoot
);
size_t
result
=
0
;
while
(
not
s
.
empty
())
{
entry
*
e
=
s
.
top
();
entry
*
e
=
s
.
top
();
s
.
pop
();
if
(
e
==
nullptr
)
continue
;
++
result
;
s
.
push
(
e
->
mLeft
);
s
.
push
(
e
->
mRight
);
}
return
result
;
}
//bool CatIndex::isValid() const
//{
// bool result = true;
//
//
// if (mRoot != nullptr)
// {
// uint32_t minBlack = numeric_limits<uint32_t>::max();
// uint32_t maxBlack = 0;
//
//
// assert(not mRoot->mRed);
//
//
// result = isValid(mRoot, false, 0, minBlack, maxBlack);
// assert(minBlack == maxBlack);
// }
//
//
// return result;
//}
//
//bool CatIndex::validate(entry* h, bool isParentRed, uint32_t blackDepth, uint32_t& minBlack, uint32_t& maxBlack) const
//{
// bool result = true;
//
//
// if (h->mRed)
// assert(not isParentRed);
// else
// ++blackDepth;
//
//
// if (isParentRed)
// assert(not h->mRed);
//
//
// if (h->mLeft != nullptr and h->mRight != nullptr)
// {
// if (isRed(h->mLeft))
...
...
@@ -1123,7 +1210,7 @@ size_t CatIndex::size() const
// if (isRed(h->mRight))
// assert(not isRed(h->mLeft));
// }
//
//
// if (h->mLeft != nullptr)
// {
// assert(mComp(h->mLeft->mRow, h->mRow) < 0);
...
...
@@ -1136,7 +1223,7 @@ size_t CatIndex::size() const
// if (maxBlack < blackDepth)
// maxBlack = blackDepth;
// }
//
//
// if (h->mRight != nullptr)
// {
// assert(mComp(h->mRight->mRow, h->mRow) > 0);
...
...
@@ -1153,36 +1240,36 @@ size_t CatIndex::size() const
// --------------------------------------------------------------------
RowSet
::
RowSet
(
Category
&
cat
)
RowSet
::
RowSet
(
Category
&
cat
)
:
mCat
(
&
cat
)
// , mCond(nullptr)
// , mCond(nullptr)
{
}
RowSet
::
RowSet
(
Category
&
cat
,
Condition
&&
cond
)
RowSet
::
RowSet
(
Category
&
cat
,
Condition
&&
cond
)
:
mCat
(
&
cat
)
// , mCond(new Condition(std::forward<Condition>(cond)))
// , mCond(new Condition(std::forward<Condition>(cond)))
{
cond
.
prepare
(
cat
);
for
(
auto
r
:
cat
)
for
(
auto
r
:
cat
)
{
if
(
cond
(
cat
,
r
))
mItems
.
push_back
(
r
.
mData
);
}
}
RowSet
::
RowSet
(
const
RowSet
&
rhs
)
RowSet
::
RowSet
(
const
RowSet
&
rhs
)
:
mCat
(
rhs
.
mCat
)
,
mItems
(
rhs
.
mItems
)
// , mCond(nullptr)
// , mCond(nullptr)
{
}
RowSet
::
RowSet
(
RowSet
&&
rhs
)
RowSet
::
RowSet
(
RowSet
&&
rhs
)
:
mCat
(
rhs
.
mCat
)
,
mItems
(
std
::
move
(
rhs
.
mItems
))
// , mCond(rhs.mCond)
// , mCond(rhs.mCond)
{
// rhs.mCond = nullptr;
}
...
...
@@ -1192,59 +1279,63 @@ RowSet::~RowSet()
// delete mCond;
}
RowSet
&
RowSet
::
operator
=
(
const
RowSet
&
rhs
)
RowSet
&
RowSet
::
operator
=
(
const
RowSet
&
rhs
)
{
if
(
this
!=
&
rhs
)
{
mItems
=
rhs
.
mItems
;
mCat
=
rhs
.
mCat
;
}
return
*
this
;
}
RowSet
&
RowSet
::
operator
=
(
RowSet
&&
rhs
)
RowSet
&
RowSet
::
operator
=
(
RowSet
&&
rhs
)
{
if
(
this
!=
&
rhs
)
{
std
::
swap
(
mItems
,
rhs
.
mItems
);
mCat
=
rhs
.
mCat
;
}
return
*
this
;
}
RowSet
&
RowSet
::
orderBy
(
std
::
initializer_list
<
std
::
string
>
items
)
RowSet
&
RowSet
::
orderBy
(
std
::
initializer_list
<
std
::
string
>
items
)
{
RowComparator
c
(
mCat
,
items
.
begin
(),
items
.
end
());
stable_sort
(
mItems
.
begin
(),
mItems
.
end
(),
c
);
return
*
this
;
}
// --------------------------------------------------------------------
Category
::
Category
(
Datablock
&
db
,
const
std
::
string
&
name
,
Validator
*
Validator
)
:
mDb
(
db
),
mName
(
name
),
mValidator
(
Validator
)
,
mHead
(
nullptr
),
mTail
(
nullptr
),
mIndex
(
nullptr
)
Category
::
Category
(
Datablock
&
db
,
const
std
::
string
&
name
,
Validator
*
Validator
)
:
mDb
(
db
)
,
mName
(
name
)
,
mValidator
(
Validator
)
,
mHead
(
nullptr
)
,
mTail
(
nullptr
)
,
mIndex
(
nullptr
)
{
if
(
mName
.
empty
())
throw
ValidationError
(
"invalid empty name for Category"
);
if
(
mValidator
!=
nullptr
)
{
mCatValidator
=
mValidator
->
getValidatorForCategory
(
mName
);
if
(
mCatValidator
!=
nullptr
)
{
// make sure all required columns are added
for
(
auto
&
k
:
mCatValidator
->
mKeys
)
for
(
auto
&
k
:
mCatValidator
->
mKeys
)
addColumn
(
k
);
for
(
auto
&
k
:
mCatValidator
->
mMandatoryFields
)
for
(
auto
&
k
:
mCatValidator
->
mMandatoryFields
)
addColumn
(
k
);
mIndex
=
new
CatIndex
(
this
);
}
}
...
...
@@ -1256,16 +1347,16 @@ Category::~Category()
delete
mIndex
;
}
void
Category
::
setValidator
(
Validator
*
v
)
void
Category
::
setValidator
(
Validator
*
v
)
{
mValidator
=
v
;
if
(
mIndex
!=
nullptr
)
{
delete
mIndex
;
mIndex
=
nullptr
;
}
if
(
mValidator
!=
nullptr
)
{
mCatValidator
=
mValidator
->
getValidatorForCategory
(
mName
);
...
...
@@ -1273,22 +1364,22 @@ void Category::setValidator(Validator* v)
{
mIndex
=
new
CatIndex
(
this
);
mIndex
->
reconstruct
();
//#if DEBUG
// assert(mIndex->size() == size());
// mIndex->validate();
//#endif
//#if DEBUG
// assert(mIndex->size() == size());
// mIndex->validate();
//#endif
}
}
else
mCatValidator
=
nullptr
;
}
bool
Category
::
hasColumn
(
const
std
::
string
&
name
)
const
bool
Category
::
hasColumn
(
const
std
::
string
&
name
)
const
{
return
getColumnIndex
(
name
)
<
mColumns
.
size
();
return
getColumnIndex
(
name
)
<
mColumns
.
size
();
}
size_t
Category
::
getColumnIndex
(
const
std
::
string
&
name
)
const
size_t
Category
::
getColumnIndex
(
const
std
::
string
&
name
)
const
{
size_t
result
;
...
...
@@ -1297,18 +1388,18 @@ size_t Category::getColumnIndex(const std::string& name) const
if
(
iequals
(
name
,
mColumns
[
result
].
mName
))
break
;
}
if
(
VERBOSE
and
result
==
mColumns
.
size
()
and
mCatValidator
!=
nullptr
)
// validate the name, if it is known at all (since it was not found)
if
(
VERBOSE
and
result
==
mColumns
.
size
()
and
mCatValidator
!=
nullptr
)
// validate the name, if it is known at all (since it was not found)
{
auto
iv
=
mCatValidator
->
getValidatorForItem
(
name
);
if
(
iv
==
nullptr
)
std
::
cerr
<<
"Invalid name used '"
+
name
+
"' is not a known column in "
+
mName
<<
std
::
endl
;
}
return
result
;
}
const
std
::
string
&
Category
::
getColumnName
(
size_t
columnIx
)
const
const
std
::
string
&
Category
::
getColumnName
(
size_t
columnIx
)
const
{
return
mColumns
.
at
(
columnIx
).
mName
;
}
...
...
@@ -1316,29 +1407,29 @@ const std::string& Category::getColumnName(size_t columnIx) const
std
::
vector
<
std
::
string
>
Category
::
getColumnNames
()
const
{
std
::
vector
<
std
::
string
>
result
;
for
(
auto
&
c
:
mColumns
)
for
(
auto
&
c
:
mColumns
)
result
.
push_back
(
c
.
mName
);
return
result
;
}
size_t
Category
::
addColumn
(
const
std
::
string
&
name
)
size_t
Category
::
addColumn
(
const
std
::
string
&
name
)
{
size_t
result
=
getColumnIndex
(
name
);
if
(
result
==
mColumns
.
size
())
{
const
ValidateItem
*
itemValidator
=
nullptr
;
const
ValidateItem
*
itemValidator
=
nullptr
;
if
(
mCatValidator
!=
nullptr
)
{
itemValidator
=
mCatValidator
->
getValidatorForItem
(
name
);
if
(
itemValidator
==
nullptr
)
mValidator
->
reportError
(
"tag "
+
name
+
" not allowed in Category "
+
mName
,
false
);
}
mColumns
.
push_back
({
name
,
itemValidator
});
}
return
result
;
}
...
...
@@ -1363,17 +1454,17 @@ void Category::reorderByIndex()
std
::
tie
(
mHead
,
mTail
)
=
mIndex
->
reorder
();
}
void
Category
::
sort
(
std
::
function
<
int
(
const
Row
&
,
const
Row
&
)
>
comparator
)
void
Category
::
sort
(
std
::
function
<
int
(
const
Row
&
,
const
Row
&
)
>
comparator
)
{
if
(
mHead
==
nullptr
)
return
;
std
::
vector
<
ItemRow
*>
rows
;
std
::
vector
<
ItemRow
*>
rows
;
for
(
auto
itemRow
=
mHead
;
itemRow
!=
nullptr
;
itemRow
=
itemRow
->
mNext
)
rows
.
push_back
(
itemRow
);
std
::
stable_sort
(
rows
.
begin
(),
rows
.
end
(),
[
&
comparator
](
ItemRow
*
ia
,
ItemRow
*
ib
)
[
&
comparator
](
ItemRow
*
ia
,
ItemRow
*
ib
)
{
Row
ra
(
ia
);
Row
rb
(
ib
);
...
...
@@ -1416,10 +1507,10 @@ std::string Category::getUniqueID(std::function<std::string(int)> generator)
size_t
Category
::
size
()
const
{
size_t
result
=
0
;
for
(
auto
pi
=
mHead
;
pi
!=
nullptr
;
pi
=
pi
->
mNext
)
++
result
;
return
result
;
}
...
...
@@ -1428,30 +1519,31 @@ bool Category::empty() const
return
mHead
==
nullptr
or
mHead
->
mValues
==
nullptr
;
}
void
Category
::
drop
(
const
std
::
string
&
field
)
void
Category
::
drop
(
const
std
::
string
&
field
)
{
using
namespace
std
::
placeholders
;
auto
ci
=
find_if
(
mColumns
.
begin
(),
mColumns
.
end
(),
[
field
](
ItemColumn
&
c
)
->
bool
{
return
iequals
(
c
.
mName
,
field
);
});
[
field
](
ItemColumn
&
c
)
->
bool
{
return
iequals
(
c
.
mName
,
field
);
});
if
(
ci
!=
mColumns
.
end
())
{
size_t
columnIx
=
ci
-
mColumns
.
begin
();
for
(
auto
pi
=
mHead
;
pi
!=
nullptr
;
pi
=
pi
->
mNext
)
pi
->
drop
(
columnIx
);
mColumns
.
erase
(
ci
);
}
}
Row
Category
::
operator
[](
Condition
&&
cond
)
Row
Category
::
operator
[](
Condition
&&
cond
)
{
Row
result
;
cond
.
prepare
(
*
this
);
for
(
auto
r
:
*
this
)
for
(
auto
r
:
*
this
)
{
if
(
cond
(
*
this
,
r
))
{
...
...
@@ -1461,15 +1553,15 @@ Row Category::operator[](Condition&& cond)
}
return
result
;
}
}
// RowSet Category::find(Condition&& cond)
// {
// // return RowSet(*this, std::forward<Condition>(cond));
// RowSet result(*this);
// cond.prepare(*this);
// for (auto r: *this)
// {
// if (cond(*this, r))
...
...
@@ -1478,13 +1570,13 @@ Row Category::operator[](Condition&& cond)
// return result;
// }
bool
Category
::
exists
(
Condition
&&
cond
)
const
bool
Category
::
exists
(
Condition
&&
cond
)
const
{
bool
result
=
false
;
cond
.
prepare
(
*
this
);
for
(
auto
r
:
*
this
)
for
(
auto
r
:
*
this
)
{
if
(
cond
(
*
this
,
r
))
{
...
...
@@ -1500,7 +1592,7 @@ RowSet Category::orderBy(std::initializer_list<std::string> items)
{
RowSet
result
(
*
this
);
result
.
insert
(
result
.
begin
(),
begin
(),
end
());
return
result
.
orderBy
(
items
);
}
...
...
@@ -1508,7 +1600,7 @@ void Category::clear()
{
delete
mHead
;
mHead
=
mTail
=
nullptr
;
if
(
mIndex
!=
nullptr
)
{
delete
mIndex
;
...
...
@@ -1516,8 +1608,8 @@ void Category::clear()
}
}
template
<
class
Iter
>
std
::
tuple
<
Row
,
bool
>
Category
::
emplace
(
Iter
b
,
Iter
e
)
template
<
class
Iter
>
std
::
tuple
<
Row
,
bool
>
Category
::
emplace
(
Iter
b
,
Iter
e
)
{
// First, make sure all mandatory fields are supplied
Row
result
;
...
...
@@ -1525,15 +1617,15 @@ std::tuple<Row,bool> Category::emplace(Iter b, Iter e)
if
(
mCatValidator
!=
nullptr
and
b
!=
e
)
{
for
(
auto
&
col
:
mColumns
)
for
(
auto
&
col
:
mColumns
)
{
auto
iv
=
mCatValidator
->
getValidatorForItem
(
col
.
mName
);
if
(
iv
==
nullptr
)
continue
;
bool
seen
=
false
;
for
(
auto
v
=
b
;
v
!=
e
;
++
v
)
{
if
(
iequals
(
v
->
name
(),
col
.
mName
))
...
...
@@ -1542,23 +1634,23 @@ std::tuple<Row,bool> Category::emplace(Iter b, Iter e)
break
;
}
}
if
(
not
seen
and
iv
->
mMandatory
)
throw
std
::
runtime_error
(
"missing mandatory field "
+
col
.
mName
+
" for Category "
+
mName
);
}
if
(
mIndex
!=
nullptr
)
{
std
::
unique_ptr
<
ItemRow
>
nr
(
new
ItemRow
{
nullptr
,
this
,
nullptr
});
Row
r
(
nr
.
get
());
auto
keys
=
keyFields
();
auto
keys
=
keyFields
();
for
(
auto
v
=
b
;
v
!=
e
;
++
v
)
{
if
(
keys
.
count
(
v
->
name
()))
r
.
assign
(
v
->
name
(),
v
->
value
(),
true
);
}
auto
test
=
mIndex
->
find
(
nr
.
get
());
if
(
test
!=
nullptr
)
{
...
...
@@ -1578,7 +1670,7 @@ std::tuple<Row,bool> Category::emplace(Iter b, Iter e)
for
(
auto
v
=
b
;
v
!=
e
;
++
v
)
r
.
assign
(
*
v
,
true
);
// if (isOrphan(r))
// throw std::runtime_error("Cannot insert row in category " + mName + " since it would be an orphan");
...
...
@@ -1600,16 +1692,16 @@ std::tuple<Row,bool> Category::emplace(Iter b, Iter e)
if
(
mIndex
!=
nullptr
)
mIndex
->
insert
(
nr
);
}
return
{
result
,
isNew
};
return
{
result
,
isNew
};
}
std
::
tuple
<
Row
,
bool
>
Category
::
emplace
(
Row
r
)
std
::
tuple
<
Row
,
bool
>
Category
::
emplace
(
Row
r
)
{
return
emplace
(
r
.
begin
(),
r
.
end
());
}
size_t
Category
::
erase
(
Condition
&&
cond
)
size_t
Category
::
erase
(
Condition
&&
cond
)
{
size_t
result
=
0
;
...
...
@@ -1627,10 +1719,10 @@ size_t Category::erase(Condition&& cond)
++
ri
;
}
return
result
;
return
result
;
}
size_t
Category
::
erase
(
Condition
&&
cond
,
std
::
function
<
void
(
const
Row
&
)
>&&
verbose
)
size_t
Category
::
erase
(
Condition
&&
cond
,
std
::
function
<
void
(
const
Row
&
)
>
&&
verbose
)
{
size_t
result
=
0
;
...
...
@@ -1652,26 +1744,26 @@ size_t Category::erase(Condition&& cond, std::function<void(const Row&)>&& verbo
return
result
;
}
void
Category
::
eraseOrphans
(
Condition
&&
cond
)
void
Category
::
eraseOrphans
(
Condition
&&
cond
)
{
std
::
vector
<
ItemRow
*>
remove
;
std
::
vector
<
ItemRow
*>
remove
;
cond
.
prepare
(
*
this
);
for
(
auto
r
:
*
this
)
for
(
auto
r
:
*
this
)
{
if
(
cond
(
*
this
,
r
)
and
isOrphan
(
r
))
{
if
(
VERBOSE
>
1
)
std
::
cerr
<<
"Removing orphaned record: "
<<
std
::
endl
<<
r
<<
std
::
endl
<<
std
::
endl
;
<<
r
<<
std
::
endl
<<
std
::
endl
;
remove
.
push_back
(
r
.
mData
);
}
}
for
(
auto
r
:
remove
)
for
(
auto
r
:
remove
)
erase
(
iterator
(
r
));
}
...
...
@@ -1694,7 +1786,7 @@ auto Category::erase(iterator pos) -> iterator
if
(
mIndex
!=
nullptr
)
mIndex
->
erase
(
r
.
mData
);
if
(
r
==
mHead
)
{
mHead
=
mHead
->
mNext
;
...
...
@@ -1716,25 +1808,25 @@ auto Category::erase(iterator pos) -> iterator
// links are created based on the _pdbx_item_linked_group_list entries
// in mmcif_pdbx_v50.dic dictionary.
//
// For each link group in _pdbx_item_linked_group_list
// For each link group in _pdbx_item_linked_group_list
// a std::set of keys from one category is mapped to another.
// If all values in a child are the same as the specified parent ones
// the child is removed as well, recursively of course.
if
(
mValidator
!=
nullptr
)
{
for
(
auto
&
link
:
mValidator
->
getLinksForParent
(
mName
))
for
(
auto
&
link
:
mValidator
->
getLinksForParent
(
mName
))
{
auto
childCat
=
mDb
.
get
(
link
->
mChildCategory
);
if
(
childCat
==
nullptr
)
continue
;
Condition
cond
;
for
(
size_t
ix
=
0
;
ix
<
link
->
mParentKeys
.
size
();
++
ix
)
{
const
char
*
value
=
r
[
link
->
mParentKeys
[
ix
]].
c_str
();
const
char
*
value
=
r
[
link
->
mParentKeys
[
ix
]].
c_str
();
cond
=
std
::
move
(
cond
)
&&
(
Key
(
link
->
mChildKeys
[
ix
])
==
value
);
}
...
...
@@ -1780,15 +1872,15 @@ Row Category::copyRow(const Row &row)
}
}
auto
&&
[
result
,
inserted
]
=
emplace
(
items
.
begin
(),
items
.
end
());
auto
&&
[
result
,
inserted
]
=
emplace
(
items
.
begin
(),
items
.
end
());
// assert(inserted);
return
result
;
}
void
Category
::
getTagOrder
(
std
::
vector
<
std
::
string
>
&
tags
)
const
void
Category
::
getTagOrder
(
std
::
vector
<
std
::
string
>
&
tags
)
const
{
for
(
auto
&
c
:
mColumns
)
for
(
auto
&
c
:
mColumns
)
tags
.
push_back
(
"_"
+
mName
+
"."
+
c
.
mName
);
}
...
...
@@ -1822,7 +1914,7 @@ Category::const_iterator Category::end() const
return
const_iterator
();
}
bool
Category
::
hasParent
(
Row
r
,
const
Category
&
parentCat
,
const
ValidateLink
&
link
)
const
bool
Category
::
hasParent
(
Row
r
,
const
Category
&
parentCat
,
const
ValidateLink
&
link
)
const
{
assert
(
mValidator
!=
nullptr
);
assert
(
mCatValidator
!=
nullptr
);
...
...
@@ -1832,7 +1924,7 @@ bool Category::hasParent(Row r, const Category& parentCat, const ValidateLink& l
Condition
cond
;
for
(
size_t
ix
=
0
;
ix
<
link
.
mChildKeys
.
size
();
++
ix
)
{
auto
&
name
=
link
.
mChildKeys
[
ix
];
auto
&
name
=
link
.
mChildKeys
[
ix
];
auto
field
=
r
[
name
];
if
(
field
.
empty
())
{
...
...
@@ -1841,7 +1933,7 @@ bool Category::hasParent(Row r, const Category& parentCat, const ValidateLink& l
}
else
{
const
char
*
value
=
field
.
c_str
();
const
char
*
value
=
field
.
c_str
();
cond
=
std
::
move
(
cond
)
and
(
Key
(
link
.
mParentKeys
[
ix
])
==
value
);
}
}
...
...
@@ -1866,16 +1958,16 @@ bool Category::isOrphan(Row r)
return
false
;
bool
isOrphan
=
true
;
for
(
auto
&
link
:
mValidator
->
getLinksForChild
(
mName
))
for
(
auto
&
link
:
mValidator
->
getLinksForChild
(
mName
))
{
auto
parentCat
=
mDb
.
get
(
link
->
mParentCategory
);
if
(
parentCat
==
nullptr
)
continue
;
Condition
cond
;
for
(
size_t
ix
=
0
;
ix
<
link
->
mChildKeys
.
size
();
++
ix
)
{
const
char
*
value
=
r
[
link
->
mChildKeys
[
ix
]].
c_str
();
const
char
*
value
=
r
[
link
->
mChildKeys
[
ix
]].
c_str
();
cond
=
std
::
move
(
cond
)
&&
(
Key
(
link
->
mParentKeys
[
ix
])
==
value
);
}
...
...
@@ -1902,18 +1994,18 @@ bool Category::hasChildren(Row r) const
bool
result
=
false
;
for
(
auto
&
link
:
mValidator
->
getLinksForParent
(
mName
))
for
(
auto
&
link
:
mValidator
->
getLinksForParent
(
mName
))
{
auto
childCat
=
mDb
.
get
(
link
->
mChildCategory
);
if
(
childCat
==
nullptr
)
continue
;
Condition
cond
;
for
(
size_t
ix
=
0
;
ix
<
link
->
mParentKeys
.
size
();
++
ix
)
{
const
char
*
value
=
r
[
link
->
mParentKeys
[
ix
]].
c_str
();
const
char
*
value
=
r
[
link
->
mParentKeys
[
ix
]].
c_str
();
cond
=
std
::
move
(
cond
)
&&
(
Key
(
link
->
mChildKeys
[
ix
])
==
value
);
}
...
...
@@ -1933,18 +2025,18 @@ bool Category::hasParents(Row r) const
bool
result
=
false
;
for
(
auto
&
link
:
mValidator
->
getLinksForChild
(
mName
))
for
(
auto
&
link
:
mValidator
->
getLinksForChild
(
mName
))
{
auto
parentCat
=
mDb
.
get
(
link
->
mParentCategory
);
if
(
parentCat
==
nullptr
)
continue
;
Condition
cond
;
for
(
size_t
ix
=
0
;
ix
<
link
->
mChildKeys
.
size
();
++
ix
)
{
const
char
*
value
=
r
[
link
->
mChildKeys
[
ix
]].
c_str
();
const
char
*
value
=
r
[
link
->
mChildKeys
[
ix
]].
c_str
();
cond
=
std
::
move
(
cond
)
&&
(
Key
(
link
->
mParentKeys
[
ix
])
==
value
);
}
...
...
@@ -1957,29 +2049,29 @@ bool Category::hasParents(Row r) const
return
result
;
}
RowSet
Category
::
getChildren
(
Row
r
,
const
char
*
childCat
)
RowSet
Category
::
getChildren
(
Row
r
,
const
char
*
childCat
)
{
return
getChildren
(
r
,
mDb
[
childCat
]);
}
RowSet
Category
::
getChildren
(
Row
r
,
Category
&
childCat
)
RowSet
Category
::
getChildren
(
Row
r
,
Category
&
childCat
)
{
assert
(
mValidator
!=
nullptr
);
assert
(
mCatValidator
!=
nullptr
);
RowSet
result
(
childCat
);
for
(
auto
&
link
:
mValidator
->
getLinksForParent
(
mName
))
for
(
auto
&
link
:
mValidator
->
getLinksForParent
(
mName
))
{
if
(
link
->
mChildCategory
!=
childCat
.
mName
)
continue
;
Condition
cond
;
for
(
size_t
ix
=
0
;
ix
<
link
->
mParentKeys
.
size
();
++
ix
)
{
const
char
*
value
=
r
[
link
->
mParentKeys
[
ix
]].
c_str
();
const
char
*
value
=
r
[
link
->
mParentKeys
[
ix
]].
c_str
();
cond
=
std
::
move
(
cond
)
&&
(
Key
(
link
->
mChildKeys
[
ix
])
==
value
);
}
...
...
@@ -1993,29 +2085,29 @@ RowSet Category::getChildren(Row r, Category& childCat)
return
result
;
}
RowSet
Category
::
getParents
(
Row
r
,
const
char
*
parentCat
)
RowSet
Category
::
getParents
(
Row
r
,
const
char
*
parentCat
)
{
return
getParents
(
r
,
mDb
[
parentCat
]);
}
RowSet
Category
::
getParents
(
Row
r
,
Category
&
parentCat
)
RowSet
Category
::
getParents
(
Row
r
,
Category
&
parentCat
)
{
assert
(
mValidator
!=
nullptr
);
assert
(
mCatValidator
!=
nullptr
);
RowSet
result
(
parentCat
);
for
(
auto
&
link
:
mValidator
->
getLinksForChild
(
mName
))
for
(
auto
&
link
:
mValidator
->
getLinksForChild
(
mName
))
{
if
(
link
->
mParentCategory
!=
parentCat
.
mName
)
continue
;
Condition
cond
;
for
(
size_t
ix
=
0
;
ix
<
link
->
mChildKeys
.
size
();
++
ix
)
{
const
char
*
value
=
r
[
link
->
mChildKeys
[
ix
]].
c_str
();
const
char
*
value
=
r
[
link
->
mChildKeys
[
ix
]].
c_str
();
cond
=
std
::
move
(
cond
)
&&
(
Key
(
link
->
mParentKeys
[
ix
])
==
value
);
}
...
...
@@ -2029,12 +2121,12 @@ RowSet Category::getParents(Row r, Category& parentCat)
return
result
;
}
RowSet
Category
::
getLinked
(
Row
r
,
const
char
*
cat
)
RowSet
Category
::
getLinked
(
Row
r
,
const
char
*
cat
)
{
return
getLinked
(
r
,
mDb
[
cat
]);
}
RowSet
Category
::
getLinked
(
Row
r
,
Category
&
cat
)
RowSet
Category
::
getLinked
(
Row
r
,
Category
&
cat
)
{
RowSet
result
=
getChildren
(
r
,
cat
);
if
(
result
.
empty
())
...
...
@@ -2045,7 +2137,7 @@ RowSet Category::getLinked(Row r, Category& cat)
bool
Category
::
isValid
()
{
bool
result
=
true
;
if
(
mValidator
==
nullptr
)
throw
std
::
runtime_error
(
"no Validator specified"
);
...
...
@@ -2055,16 +2147,16 @@ bool Category::isValid()
std
::
cerr
<<
"Skipping validation of empty Category "
<<
mName
<<
std
::
endl
;
return
true
;
}
if
(
mCatValidator
==
nullptr
)
{
mValidator
->
reportError
(
"undefined Category "
+
mName
,
false
);
return
false
;
}
auto
mandatory
=
mCatValidator
->
mMandatoryFields
;
for
(
auto
&
col
:
mColumns
)
for
(
auto
&
col
:
mColumns
)
{
auto
iv
=
mCatValidator
->
getValidatorForItem
(
col
.
mName
);
if
(
iv
==
nullptr
)
...
...
@@ -2072,48 +2164,48 @@ bool Category::isValid()
mValidator
->
reportError
(
"Field "
+
col
.
mName
+
" is not valid in Category "
+
mName
,
false
);
result
=
false
;
}
col
.
mValidator
=
iv
;
mandatory
.
erase
(
col
.
mName
);
}
if
(
not
mandatory
.
empty
())
{
mValidator
->
reportError
(
"In Category "
+
mName
+
" the following mandatory fields are missing: "
+
ba
::
join
(
mandatory
,
", "
),
false
);
result
=
false
;
}
//#if not defined(NDEBUG)
// // check index?
// if (mIndex)
// {
// mIndex->validate();
// for (auto r: *this)
// {
// if (mIndex->find(r.mData) != r.mData)
// mValidator->reportError("Key not found in index for Category " + mName);
// }
// }
//#endif
//#if not defined(NDEBUG)
// // check index?
// if (mIndex)
// {
// mIndex->validate();
// for (auto r: *this)
// {
// if (mIndex->find(r.mData) != r.mData)
// mValidator->reportError("Key not found in index for Category " + mName);
// }
// }
//#endif
// validate all values
mandatory
=
mCatValidator
->
mMandatoryFields
;
for
(
auto
ri
=
mHead
;
ri
!=
nullptr
;
ri
=
ri
->
mNext
)
{
for
(
size_t
cix
=
0
;
cix
<
mColumns
.
size
();
++
cix
)
{
bool
seen
=
false
;
auto
iv
=
mColumns
[
cix
].
mValidator
;
if
(
iv
==
nullptr
)
{
mValidator
->
reportError
(
"invalid field "
+
mColumns
[
cix
].
mName
+
" for Category "
+
mName
,
false
);
result
=
false
;
continue
;
}
for
(
auto
vi
=
ri
->
mValues
;
vi
!=
nullptr
;
vi
=
vi
->
mNext
)
{
if
(
vi
->
mColumnIndex
==
cix
)
...
...
@@ -2123,17 +2215,17 @@ bool Category::isValid()
{
(
*
iv
)(
vi
->
mText
);
}
catch
(
const
std
::
exception
&
e
)
catch
(
const
std
::
exception
&
e
)
{
mValidator
->
reportError
(
"Error validating "
+
mColumns
[
cix
].
mName
+
": "
+
e
.
what
(),
false
);
continue
;
}
}
}
if
(
seen
or
ri
!=
mHead
)
continue
;
if
(
iv
!=
nullptr
and
iv
->
mMandatory
)
{
mValidator
->
reportError
(
"missing mandatory field "
+
mColumns
[
cix
].
mName
+
" for Category "
+
mName
,
false
);
...
...
@@ -2141,34 +2233,34 @@ bool Category::isValid()
}
}
}
return
result
;
}
void
Category
::
validateLinks
()
const
{
auto
&
validator
=
getValidator
();
auto
&
validator
=
getValidator
();
for
(
auto
linkValidator
:
validator
.
getLinksForChild
(
mName
))
for
(
auto
linkValidator
:
validator
.
getLinksForChild
(
mName
))
{
auto
parent
=
mDb
.
get
(
linkValidator
->
mParentCategory
);
if
(
parent
==
nullptr
)
continue
;
size_t
missing
=
0
;
for
(
auto
r
:
*
this
)
for
(
auto
r
:
*
this
)
if
(
not
hasParent
(
r
,
*
parent
,
*
linkValidator
))
++
missing
;
if
(
missing
)
{
std
::
cerr
<<
"Links for "
<<
linkValidator
->
mLinkGroupLabel
<<
" are incomplete"
<<
std
::
endl
<<
" There are "
<<
missing
<<
" items in "
<<
mName
<<
" that don't have matching parent items in "
<<
parent
->
mName
<<
std
::
endl
;
<<
" There are "
<<
missing
<<
" items in "
<<
mName
<<
" that don't have matching parent items in "
<<
parent
->
mName
<<
std
::
endl
;
}
}
}
const
Validator
&
Category
::
getValidator
()
const
const
Validator
&
Category
::
getValidator
()
const
{
if
(
mValidator
==
nullptr
)
throw
std
::
runtime_error
(
"no Validator defined yet"
);
...
...
@@ -2179,12 +2271,12 @@ iset Category::fields() const
{
if
(
mValidator
==
nullptr
)
throw
std
::
runtime_error
(
"No Validator specified"
);
if
(
mCatValidator
==
nullptr
)
mValidator
->
reportError
(
"undefined Category"
,
true
);
iset
result
;
for
(
auto
&
iv
:
mCatValidator
->
mItemValidators
)
for
(
auto
&
iv
:
mCatValidator
->
mItemValidators
)
result
.
insert
(
iv
.
mTag
);
return
result
;
}
...
...
@@ -2193,10 +2285,10 @@ iset Category::mandatoryFields() const
{
if
(
mValidator
==
nullptr
)
throw
std
::
runtime_error
(
"No Validator specified"
);
if
(
mCatValidator
==
nullptr
)
mValidator
->
reportError
(
"undefined Category"
,
true
);
return
mCatValidator
->
mMandatoryFields
;
}
...
...
@@ -2204,28 +2296,121 @@ iset Category::keyFields() const
{
if
(
mValidator
==
nullptr
)
throw
std
::
runtime_error
(
"No Validator specified"
);
if
(
mCatValidator
==
nullptr
)
mValidator
->
reportError
(
"undefined Category"
,
true
);
return
iset
{
mCatValidator
->
mKeys
.
begin
(),
mCatValidator
->
mKeys
.
end
()
};
return
iset
{
mCatValidator
->
mKeys
.
begin
(),
mCatValidator
->
mKeys
.
end
()
};
}
std
::
set
<
size_t
>
Category
::
keyFieldsByIndex
()
const
{
if
(
mValidator
==
nullptr
)
throw
std
::
runtime_error
(
"No Validator specified"
);
if
(
mCatValidator
==
nullptr
)
mValidator
->
reportError
(
"undefined Category"
,
true
);
std
::
set
<
size_t
>
result
;
for
(
auto
&
k
:
mCatValidator
->
mKeys
)
for
(
auto
&
k
:
mCatValidator
->
mKeys
)
result
.
insert
(
getColumnIndex
(
k
));
return
result
;
}
bool
operator
==
(
const
Category
&
a
,
const
Category
&
b
)
{
using
namespace
std
::
placeholders
;
// set<std::string> tagsA(a.fields()), tagsB(b.fields());
//
// if (tagsA != tagsB)
// std::cout << "Unequal number of fields" << std::endl;
auto
&
validator
=
a
.
getValidator
();
auto
catValidator
=
validator
.
getValidatorForCategory
(
a
.
name
());
if
(
catValidator
==
nullptr
)
throw
std
::
runtime_error
(
"missing cat validator"
);
typedef
std
::
function
<
int
(
const
char
*
,
const
char
*
)
>
compType
;
std
::
vector
<
std
::
tuple
<
std
::
string
,
compType
>>
tags
;
auto
keys
=
catValidator
->
mKeys
;
std
::
vector
<
size_t
>
keyIx
;
for
(
auto
&
tag
:
a
.
fields
())
{
auto
iv
=
catValidator
->
getValidatorForItem
(
tag
);
if
(
iv
==
nullptr
)
throw
std
::
runtime_error
(
"missing item validator"
);
auto
tv
=
iv
->
mType
;
if
(
tv
==
nullptr
)
throw
std
::
runtime_error
(
"missing type validator"
);
tags
.
push_back
(
std
::
make_tuple
(
tag
,
std
::
bind
(
&
cif
::
ValidateType
::
compare
,
tv
,
std
::
placeholders
::
_1
,
std
::
placeholders
::
_2
)));
auto
pred
=
[
tag
](
const
std
::
string
&
s
)
->
bool
{
return
cif
::
iequals
(
tag
,
s
)
==
0
;
};
if
(
find_if
(
keys
.
begin
(),
keys
.
end
(),
pred
)
==
keys
.
end
())
keyIx
.
push_back
(
tags
.
size
()
-
1
);
}
// a.reorderByIndex();
// b.reorderByIndex();
auto
rowEqual
=
[
&
](
const
cif
::
Row
&
a
,
const
cif
::
Row
&
b
)
{
int
d
=
0
;
for
(
auto
kix
:
keyIx
)
{
std
::
string
tag
;
compType
compare
;
std
::
tie
(
tag
,
compare
)
=
tags
[
kix
];
d
=
compare
(
a
[
tag
].
c_str
(),
b
[
tag
].
c_str
());
if
(
d
!=
0
)
break
;
}
return
d
==
0
;
};
auto
ai
=
a
.
begin
(),
bi
=
b
.
begin
();
while
(
ai
!=
a
.
end
()
or
bi
!=
b
.
end
())
{
if
(
ai
==
a
.
end
()
or
bi
==
b
.
end
())
return
false
;
cif
::
Row
ra
=
*
ai
,
rb
=
*
bi
;
if
(
not
rowEqual
(
ra
,
rb
))
return
false
;
std
::
vector
<
std
::
string
>
missingA
,
missingB
,
different
;
for
(
auto
&
tt
:
tags
)
{
std
::
string
tag
;
compType
compare
;
std
::
tie
(
tag
,
compare
)
=
tt
;
// make it an option to compare unapplicable to empty or something
const
char
*
ta
=
ra
[
tag
].
c_str
();
if
(
strcmp
(
ta
,
"."
)
==
0
)
ta
=
""
;
const
char
*
tb
=
rb
[
tag
].
c_str
();
if
(
strcmp
(
tb
,
"."
)
==
0
)
tb
=
""
;
if
(
compare
(
ta
,
tb
)
!=
0
)
return
false
;
}
++
ai
;
++
bi
;
}
return
true
;
}
// auto Category::iterator::operator++() -> iterator&
// {
// mCurrent = Row(mCurrent.data()->mNext);
...
...
@@ -2241,52 +2426,27 @@ std::set<size_t> Category::keyFieldsByIndex() const
namespace
detail
{
size_t
writeValue
(
std
::
ostream
&
os
,
std
::
string
value
,
size_t
offset
,
size_t
width
)
{
if
(
value
.
find
(
'\n'
)
!=
std
::
string
::
npos
or
width
==
0
or
value
.
length
()
>=
132
)
// write as text field
{
ba
::
replace_all
(
value
,
"
\n
;"
,
"
\n\\
;"
);
if
(
offset
>
0
)
os
<<
std
::
endl
;
os
<<
';'
<<
value
;
if
(
not
ba
::
ends_with
(
value
,
"
\n
"
))
os
<<
std
::
endl
;
os
<<
';'
<<
std
::
endl
;
offset
=
0
;
}
else
if
(
isUnquotedString
(
value
.
c_str
()))
size_t
writeValue
(
std
::
ostream
&
os
,
std
::
string
value
,
size_t
offset
,
size_t
width
)
{
os
<<
value
;
if
(
value
.
length
()
<
width
)
{
os
<<
std
::
string
(
width
-
value
.
length
(),
' '
);
offset
+=
width
;
}
else
if
(
value
.
find
(
'\n'
)
!=
std
::
string
::
npos
or
width
==
0
or
value
.
length
()
>=
132
)
// write as text field
{
os
<<
' '
;
offset
+=
value
.
length
()
+
1
;
ba
::
replace_all
(
value
,
"
\n
;"
,
"
\n\\
;"
);
if
(
offset
>
0
)
os
<<
std
::
endl
;
os
<<
';'
<<
value
;
if
(
not
ba
::
ends_with
(
value
,
"
\n
"
))
os
<<
std
::
endl
;
os
<<
';'
<<
std
::
endl
;
offset
=
0
;
}
}
else
{
bool
done
=
false
;
for
(
char
q
:
{
'\''
,
'"'
})
else
if
(
isUnquotedString
(
value
.
c_str
()))
{
auto
p
=
value
.
find
(
q
);
// see if we can use the quote character
while
(
p
!=
std
::
string
::
npos
and
isNonBlank
(
value
[
p
+
1
])
and
value
[
p
+
1
]
!=
q
)
p
=
value
.
find
(
q
,
p
+
1
);
if
(
p
!=
std
::
string
::
npos
)
continue
;
os
<<
q
<<
value
<<
q
;
os
<<
value
;
if
(
value
.
length
()
+
2
<
width
)
if
(
value
.
length
()
<
width
)
{
os
<<
std
::
string
(
width
-
value
.
length
()
-
2
,
' '
);
os
<<
std
::
string
(
width
-
value
.
length
(),
' '
);
offset
+=
width
;
}
else
...
...
@@ -2294,47 +2454,72 @@ size_t writeValue(std::ostream& os, std::string value, size_t offset, size_t wid
os
<<
' '
;
offset
+=
value
.
length
()
+
1
;
}
done
=
true
;
break
;
}
if
(
not
done
)
else
{
if
(
offset
>
0
)
os
<<
std
::
endl
;
os
<<
';'
<<
value
<<
std
::
endl
<<
';'
<<
std
::
endl
;
offset
=
0
;
bool
done
=
false
;
for
(
char
q
:
{
'\''
,
'"'
})
{
auto
p
=
value
.
find
(
q
);
// see if we can use the quote character
while
(
p
!=
std
::
string
::
npos
and
isNonBlank
(
value
[
p
+
1
])
and
value
[
p
+
1
]
!=
q
)
p
=
value
.
find
(
q
,
p
+
1
);
if
(
p
!=
std
::
string
::
npos
)
continue
;
os
<<
q
<<
value
<<
q
;
if
(
value
.
length
()
+
2
<
width
)
{
os
<<
std
::
string
(
width
-
value
.
length
()
-
2
,
' '
);
offset
+=
width
;
}
else
{
os
<<
' '
;
offset
+=
value
.
length
()
+
1
;
}
done
=
true
;
break
;
}
if
(
not
done
)
{
if
(
offset
>
0
)
os
<<
std
::
endl
;
os
<<
';'
<<
value
<<
std
::
endl
<<
';'
<<
std
::
endl
;
offset
=
0
;
}
}
return
offset
;
}
return
offset
;
}
}
}
// namespace detail
void
Category
::
write
(
std
::
ostream
&
os
,
const
std
::
vector
<
size_t
>&
order
,
bool
includeEmptyColumns
)
void
Category
::
write
(
std
::
ostream
&
os
,
const
std
::
vector
<
size_t
>
&
order
,
bool
includeEmptyColumns
)
{
if
(
empty
())
return
;
// If the first Row has a next, we need a loop_
bool
needLoop
=
(
mHead
->
mNext
!=
nullptr
);
if
(
needLoop
)
{
os
<<
"loop_"
<<
std
::
endl
;
std
::
vector
<
size_t
>
columnWidths
;
for
(
auto
cix
:
order
)
for
(
auto
cix
:
order
)
{
auto
&
col
=
mColumns
[
cix
];
auto
&
col
=
mColumns
[
cix
];
os
<<
'_'
<<
mName
<<
'.'
<<
col
.
mName
<<
' '
<<
std
::
endl
;
columnWidths
.
push_back
(
2
);
}
for
(
auto
Row
=
mHead
;
Row
!=
nullptr
;
Row
=
Row
->
mNext
)
{
for
(
auto
v
=
Row
->
mValues
;
v
!=
nullptr
;
v
=
v
->
mNext
)
...
...
@@ -2342,7 +2527,7 @@ void Category::write(std::ostream& os, const std::vector<size_t>& order, bool in
if
(
strchr
(
v
->
mText
,
'\n'
)
==
nullptr
)
{
size_t
l
=
strlen
(
v
->
mText
);
if
(
not
isUnquotedString
(
v
->
mText
))
l
+=
2
;
...
...
@@ -2354,15 +2539,15 @@ void Category::write(std::ostream& os, const std::vector<size_t>& order, bool in
}
}
}
for
(
auto
Row
=
mHead
;
Row
!=
nullptr
;
Row
=
Row
->
mNext
)
// loop over rows
for
(
auto
Row
=
mHead
;
Row
!=
nullptr
;
Row
=
Row
->
mNext
)
// loop over rows
{
size_t
offset
=
0
;
for
(
size_t
cix
:
order
)
for
(
size_t
cix
:
order
)
{
size_t
w
=
columnWidths
[
cix
];
std
::
string
s
;
for
(
auto
iv
=
Row
->
mValues
;
iv
!=
nullptr
;
iv
=
iv
->
mNext
)
{
...
...
@@ -2372,10 +2557,10 @@ void Category::write(std::ostream& os, const std::vector<size_t>& order, bool in
break
;
}
}
if
(
s
.
empty
())
s
=
"?"
;
size_t
l
=
s
.
length
();
if
(
not
isUnquotedString
(
s
.
c_str
()))
l
+=
2
;
...
...
@@ -2387,41 +2572,41 @@ void Category::write(std::ostream& os, const std::vector<size_t>& order, bool in
os
<<
std
::
endl
;
offset
=
0
;
}
offset
=
detail
::
writeValue
(
os
,
s
,
offset
,
w
);
if
(
offset
>=
132
)
{
os
<<
std
::
endl
;
offset
=
0
;
}
}
if
(
offset
>
0
)
os
<<
std
::
endl
;
}
}
else
{
// first find the indent level
// first find the indent level
size_t
l
=
0
;
for
(
auto
&
col
:
mColumns
)
for
(
auto
&
col
:
mColumns
)
{
std
::
string
tag
=
'_'
+
mName
+
'.'
+
col
.
mName
;
if
(
l
<
tag
.
length
())
l
=
tag
.
length
();
}
l
+=
3
;
for
(
size_t
cix
:
order
)
for
(
size_t
cix
:
order
)
{
auto
&
col
=
mColumns
[
cix
];
auto
&
col
=
mColumns
[
cix
];
os
<<
'_'
<<
mName
<<
'.'
<<
col
.
mName
<<
std
::
string
(
l
-
col
.
mName
.
length
()
-
mName
.
length
()
-
2
,
' '
);
std
::
string
s
;
for
(
auto
iv
=
mHead
->
mValues
;
iv
!=
nullptr
;
iv
=
iv
->
mNext
)
{
...
...
@@ -2431,10 +2616,10 @@ void Category::write(std::ostream& os, const std::vector<size_t>& order, bool in
break
;
}
}
if
(
s
.
empty
())
s
=
"?"
;
size_t
offset
=
l
;
if
(
s
.
length
()
+
l
>=
kMaxLineLength
)
{
...
...
@@ -2450,23 +2635,23 @@ void Category::write(std::ostream& os, const std::vector<size_t>& order, bool in
os
<<
"# "
<<
std
::
endl
;
}
void
Category
::
write
(
std
::
ostream
&
os
)
void
Category
::
write
(
std
::
ostream
&
os
)
{
std
::
vector
<
size_t
>
order
(
mColumns
.
size
());
iota
(
order
.
begin
(),
order
.
end
(),
0
);
write
(
os
,
order
,
false
);
}
void
Category
::
write
(
std
::
ostream
&
os
,
const
std
::
vector
<
std
::
string
>&
columns
)
void
Category
::
write
(
std
::
ostream
&
os
,
const
std
::
vector
<
std
::
string
>
&
columns
)
{
// make sure all columns are present
for
(
auto
&
c
:
columns
)
for
(
auto
&
c
:
columns
)
addColumn
(
c
);
std
::
vector
<
size_t
>
order
;
order
.
reserve
(
mColumns
.
size
());
for
(
auto
&
c
:
columns
)
for
(
auto
&
c
:
columns
)
order
.
push_back
(
getColumnIndex
(
c
));
for
(
size_t
i
=
0
;
i
<
mColumns
.
size
();
++
i
)
...
...
@@ -2489,7 +2674,7 @@ void Category::update_value(RowSet &&rows, const std::string &tag, const std::st
if
(
colIx
>=
mColumns
.
size
())
throw
std
::
runtime_error
(
"Invalid column "
+
value
+
" for "
+
mName
);
auto
&
col
=
mColumns
[
colIx
];
auto
&
col
=
mColumns
[
colIx
];
// check the value
if
(
col
.
mValidator
)
...
...
@@ -2497,26 +2682,26 @@ void Category::update_value(RowSet &&rows, const std::string &tag, const std::st
// first some sanity checks, what was the old value and is it the same for all rows?
std
::
string
oldValue
=
rows
.
front
()[
tag
].
c_str
();
for
(
auto
&
row
:
rows
)
for
(
auto
&
row
:
rows
)
{
if
(
oldValue
!=
row
[
tag
].
c_str
())
throw
std
::
runtime_error
(
"Inconsistent old values in update_value"
);
}
if
(
oldValue
==
value
)
// no need to do anything
if
(
oldValue
==
value
)
// no need to do anything
return
;
// update rows, but do not cascade
for
(
auto
&
row
:
rows
)
for
(
auto
&
row
:
rows
)
row
.
assign
(
colIx
,
value
,
true
);
// see if we need to update any child categories that depend on this value
auto
&
validator
=
getValidator
();
auto
&
db
=
mDb
;
auto
&
validator
=
getValidator
();
auto
&
db
=
mDb
;
for
(
auto
parent
:
rows
)
for
(
auto
parent
:
rows
)
{
for
(
auto
linked
:
validator
.
getLinksForParent
(
mName
))
for
(
auto
linked
:
validator
.
getLinksForParent
(
mName
))
{
auto
childCat
=
db
.
get
(
linked
->
mChildCategory
);
if
(
childCat
==
nullptr
)
...
...
@@ -2527,7 +2712,7 @@ void Category::update_value(RowSet &&rows, const std::string &tag, const std::st
Condition
cond
;
std
::
string
childTag
;
for
(
size_t
ix
=
0
;
ix
<
linked
->
mParentKeys
.
size
();
++
ix
)
{
std
::
string
pk
=
linked
->
mParentKeys
[
ix
];
...
...
@@ -2544,7 +2729,7 @@ void Category::update_value(RowSet &&rows, const std::string &tag, const std::st
cond
=
std
::
move
(
cond
)
&&
Key
(
ck
)
==
parent
[
pk
].
c_str
();
}
auto
children
=
RowSet
{
*
childCat
,
std
::
move
(
cond
)
};
auto
children
=
RowSet
{
*
childCat
,
std
::
move
(
cond
)
};
if
(
children
.
empty
())
continue
;
...
...
@@ -2554,10 +2739,10 @@ void Category::update_value(RowSet &&rows, const std::string &tag, const std::st
RowSet
process
(
*
childCat
);
for
(
auto
child
:
children
)
for
(
auto
child
:
children
)
{
Condition
cond_c
;
for
(
size_t
ix
=
0
;
ix
<
linked
->
mParentKeys
.
size
();
++
ix
)
{
std
::
string
pk
=
linked
->
mParentKeys
[
ix
];
...
...
@@ -2577,7 +2762,7 @@ void Category::update_value(RowSet &&rows, const std::string &tag, const std::st
// oops, we need to split this child, unless a row already exists for the new value
Condition
check
;
for
(
size_t
ix
=
0
;
ix
<
linked
->
mParentKeys
.
size
();
++
ix
)
{
std
::
string
pk
=
linked
->
mParentKeys
[
ix
];
...
...
@@ -2591,7 +2776,7 @@ void Category::update_value(RowSet &&rows, const std::string &tag, const std::st
check
=
std
::
move
(
check
)
&&
Key
(
ck
)
==
parent
[
pk
].
c_str
();
}
if
(
childCat
->
exists
(
std
::
move
(
check
)))
// phew..., narrow escape
if
(
childCat
->
exists
(
std
::
move
(
check
)))
// phew..., narrow escape
continue
;
// create the actual copy, if we can...
...
...
@@ -2605,7 +2790,7 @@ void Category::update_value(RowSet &&rows, const std::string &tag, const std::st
}
}
// cannot update this...
// cannot update this...
if
(
cif
::
VERBOSE
)
std
::
cerr
<<
"Cannot update child "
<<
childCat
->
mName
<<
"."
<<
childTag
<<
" with value "
<<
value
<<
std
::
endl
;
}
...
...
@@ -2613,19 +2798,19 @@ void Category::update_value(RowSet &&rows, const std::string &tag, const std::st
// finally, update the children
if
(
not
process
.
empty
())
childCat
->
update_value
(
std
::
move
(
process
),
childTag
,
value
);
}
}
}
}
// --------------------------------------------------------------------
Row
::
Row
(
const
Row
&
rhs
)
Row
::
Row
(
const
Row
&
rhs
)
:
mData
(
rhs
.
mData
)
,
mCascade
(
rhs
.
mCascade
)
{
}
Row
::
Row
(
Row
&&
rhs
)
Row
::
Row
(
Row
&&
rhs
)
:
mData
(
rhs
.
mData
)
,
mCascade
(
rhs
.
mCascade
)
{
...
...
@@ -2634,7 +2819,6 @@ Row::Row(Row&& rhs)
Row
::~
Row
()
{
}
void
Row
::
next
()
...
...
@@ -2643,30 +2827,31 @@ void Row::next()
mData
=
mData
->
mNext
;
}
Row
&
Row
::
operator
=
(
Row
&&
rhs
)
Row
&
Row
::
operator
=
(
Row
&&
rhs
)
{
mData
=
rhs
.
mData
;
rhs
.
mData
=
nullptr
;
mData
=
rhs
.
mData
;
rhs
.
mData
=
nullptr
;
mCascade
=
rhs
.
mCascade
;
return
*
this
;
}
Row
&
Row
::
operator
=
(
const
Row
&
rhs
)
Row
&
Row
::
operator
=
(
const
Row
&
rhs
)
{
mData
=
rhs
.
mData
;
mCascade
=
rhs
.
mCascade
;
return
*
this
;
}
void
Row
::
assign
(
const
std
::
vector
<
Item
>
&
values
)
void
Row
::
assign
(
const
std
::
vector
<
Item
>
&
values
)
{
auto
cat
=
mData
->
mCategory
;
std
::
map
<
std
::
string
,
std
::
tuple
<
size_t
,
std
::
string
,
std
::
string
>>
changed
;
std
::
map
<
std
::
string
,
std
::
tuple
<
size_t
,
std
::
string
,
std
::
string
>>
changed
;
for
(
auto
&
value
:
values
)
for
(
auto
&
value
:
values
)
{
auto
columnIx
=
cat
->
addColumn
(
value
.
name
());
auto
&
col
=
cat
->
mColumns
[
columnIx
];
auto
&
col
=
cat
->
mColumns
[
columnIx
];
std
::
string
tag
=
col
.
mValidator
?
col
.
mValidator
->
mTag
:
std
::
to_string
(
columnIx
);
changed
[
tag
]
=
std
::
make_tuple
(
columnIx
,
operator
[](
columnIx
).
c_str
(),
value
.
value
());
...
...
@@ -2678,10 +2863,10 @@ void Row::assign(const std::vector<Item>& values)
// auto iv = col.mValidator;
if
(
mCascade
)
{
auto
&
validator
=
cat
->
getValidator
();
auto
&
db
=
cat
->
db
();
auto
&
validator
=
cat
->
getValidator
();
auto
&
db
=
cat
->
db
();
for
(
auto
linked
:
validator
.
getLinksForParent
(
cat
->
mName
))
for
(
auto
linked
:
validator
.
getLinksForParent
(
cat
->
mName
))
{
auto
childCat
=
db
.
get
(
linked
->
mChildCategory
);
if
(
childCat
==
nullptr
)
...
...
@@ -2694,7 +2879,7 @@ void Row::assign(const std::vector<Item>& values)
std
::
string
childTag
;
std
::
vector
<
Item
>
newValues
;
for
(
size_t
ix
=
0
;
ix
<
linked
->
mParentKeys
.
size
();
++
ix
)
{
std
::
string
pk
=
linked
->
mParentKeys
[
ix
];
...
...
@@ -2708,46 +2893,46 @@ void Row::assign(const std::vector<Item>& values)
}
else
{
const
char
*
value
=
(
*
this
)[
pk
].
c_str
();
const
char
*
value
=
(
*
this
)[
pk
].
c_str
();
cond
=
std
::
move
(
cond
)
&&
(
Key
(
ck
)
==
value
);
}
}
auto
rows
=
childCat
->
find
(
std
::
move
(
cond
));
for
(
auto
&
cr
:
rows
)
for
(
auto
&
cr
:
rows
)
cr
.
assign
(
newValues
);
}
}
}
void
Row
::
assign
(
const
Item
&
value
,
bool
skipUpdateLinked
)
void
Row
::
assign
(
const
Item
&
value
,
bool
skipUpdateLinked
)
{
assign
(
value
.
name
(),
value
.
value
(),
skipUpdateLinked
);
}
void
Row
::
assign
(
const
std
::
string
&
name
,
const
std
::
string
&
value
,
bool
skipUpdateLinked
)
void
Row
::
assign
(
const
std
::
string
&
name
,
const
std
::
string
&
value
,
bool
skipUpdateLinked
)
{
try
{
auto
cat
=
mData
->
mCategory
;
assign
(
cat
->
addColumn
(
name
),
value
,
skipUpdateLinked
);
}
catch
(
const
std
::
exception
&
ex
)
catch
(
const
std
::
exception
&
ex
)
{
std
::
cerr
<<
"Could not assign value '"
<<
value
<<
"' to column _"
<<
mData
->
mCategory
->
name
()
<<
'.'
<<
name
<<
std
::
endl
;
throw
;
}
}
void
Row
::
assign
(
size_t
column
,
const
std
::
string
&
value
,
bool
skipUpdateLinked
)
void
Row
::
assign
(
size_t
column
,
const
std
::
string
&
value
,
bool
skipUpdateLinked
)
{
if
(
mData
==
nullptr
)
throw
std
::
logic_error
(
"invalid Row, no data assigning value '"
+
value
+
"' to column with index "
+
std
::
to_string
(
column
));
auto
cat
=
mData
->
mCategory
;
auto
&
col
=
cat
->
mColumns
[
column
];
auto
&
col
=
cat
->
mColumns
[
column
];
const
char
*
oldValue
=
nullptr
;
const
char
*
oldValue
=
nullptr
;
for
(
auto
iv
=
mData
->
mValues
;
iv
!=
nullptr
;
iv
=
iv
->
mNext
)
{
assert
(
iv
!=
iv
->
mNext
and
(
iv
->
mNext
==
nullptr
or
iv
!=
iv
->
mNext
->
mNext
));
...
...
@@ -2758,8 +2943,8 @@ void Row::assign(size_t column, const std::string& value, bool skipUpdateLinked)
break
;
}
}
if
(
oldValue
!=
nullptr
and
value
==
oldValue
)
// no need to update
if
(
oldValue
!=
nullptr
and
value
==
oldValue
)
// no need to update
return
;
std
::
string
oldStrValue
=
oldValue
?
oldValue
:
""
;
...
...
@@ -2770,10 +2955,10 @@ void Row::assign(size_t column, const std::string& value, bool skipUpdateLinked)
// If the field is part of the Key for this Category, remove it from the index
// before updating
bool
reinsert
=
false
;
if
(
not
skipUpdateLinked
and
// an update of an Item's value
if
(
not
skipUpdateLinked
and
// an update of an Item's value
cat
->
mIndex
!=
nullptr
and
cat
->
keyFieldsByIndex
().
count
(
column
))
{
reinsert
=
cat
->
mIndex
->
find
(
mData
);
...
...
@@ -2784,7 +2969,7 @@ void Row::assign(size_t column, const std::string& value, bool skipUpdateLinked)
// first remove old value with cix
if
(
mData
->
mValues
==
nullptr
)
;
// nothing to do
;
// nothing to do
else
if
(
mData
->
mValues
->
mColumnIndex
==
column
)
{
auto
iv
=
mData
->
mValues
;
...
...
@@ -2802,7 +2987,7 @@ void Row::assign(size_t column, const std::string& value, bool skipUpdateLinked)
iv
->
mNext
=
nv
->
mNext
;
nv
->
mNext
=
nullptr
;
delete
nv
;
break
;
}
}
...
...
@@ -2810,8 +2995,8 @@ void Row::assign(size_t column, const std::string& value, bool skipUpdateLinked)
if
(
not
value
.
empty
())
{
auto
nv
=
new
(
value
.
length
())
ItemValue
(
value
.
c_str
(),
column
);
auto
nv
=
new
(
value
.
length
())
ItemValue
(
value
.
c_str
(),
column
);
if
(
mData
->
mValues
==
nullptr
)
mData
->
mValues
=
nv
;
else
...
...
@@ -2830,10 +3015,10 @@ void Row::assign(size_t column, const std::string& value, bool skipUpdateLinked)
auto
iv
=
col
.
mValidator
;
if
(
not
skipUpdateLinked
and
iv
!=
nullptr
and
mCascade
)
{
auto
&
validator
=
cat
->
getValidator
();
auto
&
db
=
cat
->
db
();
auto
&
validator
=
cat
->
getValidator
();
auto
&
db
=
cat
->
db
();
for
(
auto
linked
:
validator
.
getLinksForParent
(
cat
->
mName
))
for
(
auto
linked
:
validator
.
getLinksForParent
(
cat
->
mName
))
{
auto
childCat
=
db
.
get
(
linked
->
mChildCategory
);
if
(
childCat
==
nullptr
)
...
...
@@ -2844,7 +3029,7 @@ void Row::assign(size_t column, const std::string& value, bool skipUpdateLinked)
Condition
cond
;
std
::
string
childTag
;
for
(
size_t
ix
=
0
;
ix
<
linked
->
mParentKeys
.
size
();
++
ix
)
{
std
::
string
pk
=
linked
->
mParentKeys
[
ix
];
...
...
@@ -2859,7 +3044,7 @@ void Row::assign(size_t column, const std::string& value, bool skipUpdateLinked)
}
else
{
const
char
*
pk_value
=
(
*
this
)[
pk
].
c_str
();
const
char
*
pk_value
=
(
*
this
)[
pk
].
c_str
();
if
(
*
pk_value
==
0
)
cond
=
std
::
move
(
cond
)
&&
Key
(
ck
)
==
Empty
();
else
...
...
@@ -2881,7 +3066,7 @@ void Row::assign(size_t column, const std::string& value, bool skipUpdateLinked)
// we then skip this renam
Condition
cond_n
;
for
(
size_t
ix
=
0
;
ix
<
linked
->
mParentKeys
.
size
();
++
ix
)
{
std
::
string
pk
=
linked
->
mParentKeys
[
ix
];
...
...
@@ -2893,7 +3078,7 @@ void Row::assign(size_t column, const std::string& value, bool skipUpdateLinked)
cond_n
=
std
::
move
(
cond_n
)
&&
Key
(
ck
)
==
value
;
else
{
const
char
*
pk_value
=
(
*
this
)[
pk
].
c_str
();
const
char
*
pk_value
=
(
*
this
)[
pk
].
c_str
();
if
(
*
pk_value
==
0
)
cond_n
=
std
::
move
(
cond_n
)
&&
Key
(
ck
)
==
Empty
();
else
...
...
@@ -2906,17 +3091,17 @@ void Row::assign(size_t column, const std::string& value, bool skipUpdateLinked)
{
if
(
cif
::
VERBOSE
)
std
::
cerr
<<
"Will not rename in child category since there are already rows that link to the parent"
<<
std
::
endl
;
continue
;
}
for
(
auto
&
cr
:
rows
)
for
(
auto
&
cr
:
rows
)
cr
.
assign
(
childTag
,
value
,
false
);
}
}
}
void
Row
::
swap
(
size_t
cix
,
ItemRow
*
a
,
ItemRow
*
b
)
void
Row
::
swap
(
size_t
cix
,
ItemRow
*
a
,
ItemRow
*
b
)
{
if
(
a
==
nullptr
or
b
==
nullptr
)
throw
std
::
logic_error
(
"invalid Rows in swap"
);
...
...
@@ -2924,14 +3109,14 @@ void Row::swap(size_t cix, ItemRow* a, ItemRow* b)
assert
(
a
->
mCategory
==
b
->
mCategory
);
if
(
a
->
mCategory
!=
b
->
mCategory
)
throw
std
::
logic_error
(
"Categories not same in swap"
);
auto
cat
=
a
->
mCategory
;
// If the field is part of the Key for this Category, remove it from the index
// before updating
bool
reinsert
=
false
;
if
(
cat
->
mIndex
!=
nullptr
and
cat
->
keyFieldsByIndex
().
count
(
cix
))
{
reinsert
=
true
;
...
...
@@ -2939,11 +3124,11 @@ void Row::swap(size_t cix, ItemRow* a, ItemRow* b)
cat
->
mIndex
->
erase
(
b
);
}
ItemValue
*
ap
=
nullptr
;
// parent of ai
ItemValue
*
ai
=
nullptr
;
ItemValue
*
bp
=
nullptr
;
// parent of bi
ItemValue
*
bi
=
nullptr
;
ItemValue
*
ap
=
nullptr
;
// parent of ai
ItemValue
*
ai
=
nullptr
;
ItemValue
*
bp
=
nullptr
;
// parent of bi
ItemValue
*
bi
=
nullptr
;
if
(
a
->
mValues
==
nullptr
)
;
else
if
(
a
->
mValues
->
mColumnIndex
==
cix
)
...
...
@@ -2964,7 +3149,6 @@ void Row::swap(size_t cix, ItemRow* a, ItemRow* b)
}
}
if
(
b
->
mValues
==
nullptr
)
;
else
if
(
b
->
mValues
->
mColumnIndex
==
cix
)
...
...
@@ -3018,18 +3202,18 @@ void Row::swap(size_t cix, ItemRow* a, ItemRow* b)
auto
parentColName
=
cat
->
getColumnName
(
cix
);
// see if we need to update any child categories that depend on these values
auto
&
validator
=
cat
->
getValidator
();
auto
&
validator
=
cat
->
getValidator
();
auto
parentCatValidator
=
cat
->
getCatValidator
();
for
(
auto
&
link
:
validator
.
getLinksForParent
(
cat
->
mName
))
for
(
auto
&
link
:
validator
.
getLinksForParent
(
cat
->
mName
))
{
if
(
find
(
link
->
mParentKeys
.
begin
(),
link
->
mParentKeys
.
end
(),
parentColName
)
==
link
->
mParentKeys
.
end
())
continue
;
auto
childCat
=
cat
->
db
().
get
(
link
->
mChildCategory
);
if
(
childCat
==
nullptr
or
childCat
->
empty
())
continue
;
auto
childCatValidator
=
childCat
->
getCatValidator
();
if
(
childCatValidator
==
nullptr
)
continue
;
...
...
@@ -3057,13 +3241,13 @@ void Row::swap(size_t cix, ItemRow* a, ItemRow* b)
if
(
pcix
==
cix
)
{
linkChildColName
=
childColName
;
if
(
not
(
i
==
nullptr
or
strcmp
(
i
->
mText
,
"."
)
==
0
or
strcmp
(
i
->
mText
,
"?"
)
==
0
))
if
(
not
(
i
==
nullptr
or
strcmp
(
i
->
mText
,
"."
)
==
0
or
strcmp
(
i
->
mText
,
"?"
)
==
0
))
childValue
=
i
->
mText
;
}
else
{
std
::
string
ps
=
r
->
c_str
(
pcix
);
if
(
not
(
ps
.
empty
()
or
ps
==
"."
or
ps
==
"?"
))
if
(
not
(
ps
.
empty
()
or
ps
==
"."
or
ps
==
"?"
))
childValue
=
ps
;
}
...
...
@@ -3090,7 +3274,7 @@ void Row::swap(size_t cix, ItemRow* a, ItemRow* b)
// if (VERBOSE > 1)
// std::cerr << "Fixing link from " << cat->mName << " to " << childCat->mName << " with " << std::endl
// << cond[ab] << std::endl;
rs
.
push_back
(
childCat
->
find
(
std
::
move
(
cond
[
ab
])));
}
...
...
@@ -3098,17 +3282,17 @@ void Row::swap(size_t cix, ItemRow* a, ItemRow* b)
{
auto
i
=
ab
==
0
?
bi
:
ai
;
for
(
auto
r
:
rs
[
ab
])
for
(
auto
r
:
rs
[
ab
])
{
// now due to the way links are defined, we might have found a row
// that contains an empty value for all child columns...
// Now, that's not a real hit, is it?
size_t
n
=
0
;
for
(
auto
c
:
link
->
mChildKeys
)
for
(
auto
c
:
link
->
mChildKeys
)
if
(
r
[
c
].
empty
())
++
n
;
if
(
n
==
link
->
mChildKeys
.
size
())
{
if
(
VERBOSE
>
1
)
...
...
@@ -3126,7 +3310,7 @@ void Row::swap(size_t cix, ItemRow* a, ItemRow* b)
}
}
size_t
Row
::
ColumnForItemTag
(
const
char
*
itemTag
)
const
size_t
Row
::
ColumnForItemTag
(
const
char
*
itemTag
)
const
{
size_t
result
=
0
;
if
(
mData
!=
nullptr
)
...
...
@@ -3163,21 +3347,22 @@ void Row::lineNr(uint32_t l)
mData
->
mLineNr
=
l
;
}
Row
::
const_iterator
::
const_iterator
(
ItemRow
*
data
,
ItemValue
*
ptr
)
:
mData
(
data
),
mPtr
(
ptr
)
Row
::
const_iterator
::
const_iterator
(
ItemRow
*
data
,
ItemValue
*
ptr
)
:
mData
(
data
)
,
mPtr
(
ptr
)
{
if
(
mPtr
!=
nullptr
)
fetch
();
}
Row
::
const_iterator
&
Row
::
const_iterator
::
operator
++
()
Row
::
const_iterator
&
Row
::
const_iterator
::
operator
++
()
{
if
(
mPtr
!=
nullptr
)
mPtr
=
mPtr
->
mNext
;
if
(
mPtr
!=
nullptr
)
fetch
();
return
*
this
;
}
...
...
@@ -3188,7 +3373,7 @@ void Row::const_iterator::fetch()
mPtr
->
mText
);
}
std
::
ostream
&
operator
<<
(
std
::
ostream
&
os
,
const
Row
&
row
)
std
::
ostream
&
operator
<<
(
std
::
ostream
&
os
,
const
Row
&
row
)
{
auto
category
=
row
.
mData
->
mCategory
;
std
::
string
catName
=
category
->
name
();
...
...
@@ -3209,28 +3394,29 @@ File::File()
{
}
File
::
File
(
std
::
istream
&
is
,
bool
validate
)
File
::
File
(
std
::
istream
&
is
,
bool
validate
)
:
File
()
{
load
(
is
);
}
File
::
File
(
const
std
::
string
&
path
,
bool
validate
)
File
::
File
(
const
std
::
string
&
path
,
bool
validate
)
:
File
()
{
try
{
load
(
path
);
}
catch
(
const
std
::
exception
&
ex
)
catch
(
const
std
::
exception
&
ex
)
{
std
::
cerr
<<
"Error while loading file "
<<
path
<<
std
::
endl
;
throw
;
}
}
File
::
File
(
File
&&
rhs
)
:
mHead
(
nullptr
),
mValidator
(
nullptr
)
File
::
File
(
File
&&
rhs
)
:
mHead
(
nullptr
)
,
mValidator
(
nullptr
)
{
std
::
swap
(
mHead
,
rhs
.
mHead
);
std
::
swap
(
mValidator
,
rhs
.
mValidator
);
...
...
@@ -3242,10 +3428,10 @@ File::~File()
delete
mValidator
;
}
void
File
::
append
(
Datablock
*
e
)
void
File
::
append
(
Datablock
*
e
)
{
e
->
setValidator
(
mValidator
);
if
(
mHead
==
nullptr
)
mHead
=
e
;
else
...
...
@@ -3261,23 +3447,23 @@ void File::append(Datablock* e)
ie
->
mNext
=
e
;
break
;
}
ie
=
ie
->
mNext
;
}
}
}
void
File
::
load
(
const
std
::
string
&
p
)
void
File
::
load
(
const
std
::
string
&
p
)
{
fs
::
path
path
(
p
);
std
::
ifstream
inFile
(
p
,
std
::
ios_base
::
in
|
std
::
ios_base
::
binary
);
if
(
not
inFile
.
is_open
())
throw
std
::
runtime_error
(
"Could not open file: "
+
path
.
string
());
io
::
filtering_stream
<
io
::
input
>
in
;
std
::
string
ext
;
if
(
path
.
extension
()
==
".bz2"
)
{
in
.
push
(
io
::
bzip2_decompressor
());
...
...
@@ -3288,27 +3474,27 @@ void File::load(const std::string& p)
in
.
push
(
io
::
gzip_decompressor
());
ext
=
path
.
stem
().
extension
().
string
();
}
in
.
push
(
inFile
);
try
{
load
(
in
);
}
catch
(
const
std
::
exception
&
ex
)
catch
(
const
std
::
exception
&
ex
)
{
std
::
cerr
<<
"Error loading file "
<<
path
<<
std
::
endl
;
throw
;
}
}
void
File
::
save
(
const
std
::
string
&
p
)
void
File
::
save
(
const
std
::
string
&
p
)
{
fs
::
path
path
(
p
);
std
::
ofstream
outFile
(
p
,
std
::
ios_base
::
out
|
std
::
ios_base
::
binary
);
io
::
filtering_stream
<
io
::
output
>
out
;
if
(
path
.
extension
()
==
".gz"
)
{
out
.
push
(
io
::
gzip_compressor
());
...
...
@@ -3319,19 +3505,19 @@ void File::save(const std::string& p)
out
.
push
(
io
::
bzip2_compressor
());
path
=
path
.
stem
();
}
out
.
push
(
outFile
);
save
(
out
);
}
void
File
::
load
(
std
::
istream
&
is
)
void
File
::
load
(
std
::
istream
&
is
)
{
Validator
*
saved
=
mValidator
;
Validator
*
saved
=
mValidator
;
setValidator
(
nullptr
);
Parser
p
(
is
,
*
this
);
p
.
parseFile
();
if
(
saved
!=
nullptr
)
{
setValidator
(
saved
);
...
...
@@ -3339,14 +3525,14 @@ void File::load(std::istream& is)
}
}
void
File
::
load
(
std
::
istream
&
is
,
const
std
::
string
&
datablock
)
void
File
::
load
(
std
::
istream
&
is
,
const
std
::
string
&
datablock
)
{
Validator
*
saved
=
mValidator
;
Validator
*
saved
=
mValidator
;
setValidator
(
nullptr
);
Parser
p
(
is
,
*
this
);
p
.
parseSingleDatablock
(
datablock
);
if
(
saved
!=
nullptr
)
{
setValidator
(
saved
);
...
...
@@ -3354,9 +3540,9 @@ void File::load(std::istream& is, const std::string& datablock)
}
}
void
File
::
save
(
std
::
ostream
&
os
)
void
File
::
save
(
std
::
ostream
&
os
)
{
Datablock
*
e
=
mHead
;
Datablock
*
e
=
mHead
;
while
(
e
!=
nullptr
)
{
e
->
write
(
os
);
...
...
@@ -3364,9 +3550,9 @@ void File::save(std::ostream& os)
}
}
void
File
::
write
(
std
::
ostream
&
os
,
const
std
::
vector
<
std
::
string
>&
order
)
void
File
::
write
(
std
::
ostream
&
os
,
const
std
::
vector
<
std
::
string
>
&
order
)
{
Datablock
*
e
=
mHead
;
Datablock
*
e
=
mHead
;
while
(
e
!=
nullptr
)
{
e
->
write
(
os
,
order
);
...
...
@@ -3374,17 +3560,17 @@ void File::write(std::ostream& os, const std::vector<std::string>& order)
}
}
Datablock
*
File
::
get
(
const
std
::
string
&
name
)
const
Datablock
*
File
::
get
(
const
std
::
string
&
name
)
const
{
const
Datablock
*
result
=
mHead
;
const
Datablock
*
result
=
mHead
;
while
(
result
!=
nullptr
and
not
iequals
(
result
->
mName
,
name
))
result
=
result
->
mNext
;
return
const_cast
<
Datablock
*>
(
result
);
return
const_cast
<
Datablock
*>
(
result
);
}
Datablock
&
File
::
operator
[](
const
std
::
string
&
name
)
Datablock
&
File
::
operator
[](
const
std
::
string
&
name
)
{
Datablock
*
result
=
mHead
;
Datablock
*
result
=
mHead
;
while
(
result
!=
nullptr
and
not
iequals
(
result
->
mName
,
name
))
result
=
result
->
mNext
;
if
(
result
==
nullptr
)
...
...
@@ -3398,7 +3584,7 @@ bool File::isValid()
{
if
(
VERBOSE
)
std
::
cerr
<<
"No dictionary loaded explicitly, loading default"
<<
std
::
endl
;
loadDictionary
();
}
...
...
@@ -3414,7 +3600,7 @@ void File::validateLinks() const
d
->
validateLinks
();
}
const
Validator
&
File
::
getValidator
()
const
const
Validator
&
File
::
getValidator
()
const
{
if
(
mValidator
==
nullptr
)
throw
std
::
runtime_error
(
"no Validator defined yet"
);
...
...
@@ -3426,7 +3612,7 @@ void File::loadDictionary()
loadDictionary
(
"mmcif_ddl"
);
}
void
File
::
loadDictionary
(
const
char
*
dict
)
void
File
::
loadDictionary
(
const
char
*
dict
)
{
fs
::
path
dict_name
(
dict
);
...
...
@@ -3437,7 +3623,7 @@ void File::loadDictionary(const char* dict)
if
(
data
)
loadDictionary
(
*
data
);
else
else
{
// might be a compressed dictionary on disk
fs
::
path
p
=
dict
;
...
...
@@ -3449,7 +3635,7 @@ void File::loadDictionary(const char* dict)
#if defined(CACHE_DIR) and defined(DATA_DIR)
if
(
not
fs
::
exists
(
p
))
{
for
(
const
char
*
dir
:
{
CACHE_DIR
,
DATA_DIR
})
for
(
const
char
*
dir
:
{
CACHE_DIR
,
DATA_DIR
})
{
auto
p2
=
fs
::
path
(
dir
)
/
p
;
if
(
fs
::
exists
(
p2
))
...
...
@@ -3478,7 +3664,7 @@ void File::loadDictionary(const char* dict)
}
}
void
File
::
loadDictionary
(
std
::
istream
&
is
)
void
File
::
loadDictionary
(
std
::
istream
&
is
)
{
std
::
unique_ptr
<
Validator
>
v
(
new
Validator
());
...
...
@@ -3488,7 +3674,7 @@ void File::loadDictionary(std::istream& is)
setValidator
(
v
.
release
());
}
void
File
::
setValidator
(
Validator
*
v
)
void
File
::
setValidator
(
Validator
*
v
)
{
mValidator
=
v
;
...
...
@@ -3496,13 +3682,13 @@ void File::setValidator(Validator* v)
d
->
setValidator
(
mValidator
);
}
void
File
::
getTagOrder
(
std
::
vector
<
std
::
string
>
&
tags
)
const
void
File
::
getTagOrder
(
std
::
vector
<
std
::
string
>
&
tags
)
const
{
for
(
auto
d
=
mHead
;
d
!=
nullptr
;
d
=
d
->
mNext
)
d
->
getTagOrder
(
tags
);
}
auto
File
::
iterator
::
operator
++
()
->
iterator
&
auto
File
::
iterator
::
operator
++
()
->
iterator
&
{
mCurrent
=
mCurrent
->
mNext
;
return
*
this
;
...
...
@@ -3518,4 +3704,4 @@ auto File::end() const -> iterator
return
iterator
(
nullptr
);
}
}
}
// namespace cif
src/Structure.cpp
View file @
345c4778
...
...
@@ -795,7 +795,7 @@ std::ostream &operator<<(std::ostream &os, const Atom &atom)
// First constructor used to be for waters only, but now accepts sugars as well.
Residue
::
Residue
(
const
Structure
&
structure
,
const
std
::
string
&
compoundID
,
const
std
::
string
&
asymID
,
const
std
::
string
&
authSeqID
)
const
std
::
string
&
asymID
,
const
std
::
string
&
authSeqID
)
:
mStructure
(
&
structure
)
,
mCompoundID
(
compoundID
)
,
mAsymID
(
asymID
)
...
...
@@ -804,7 +804,7 @@ Residue::Residue(const Structure &structure, const std::string &compoundID,
for
(
auto
&
a
:
mStructure
->
atoms
())
{
if
(
a
.
labelAsymID
()
!=
mAsymID
or
a
.
labelCompID
()
!=
mCompoundID
)
a
.
labelCompID
()
!=
mCompoundID
)
continue
;
if
(
compoundID
==
"HOH"
)
...
...
@@ -830,7 +830,7 @@ Residue::Residue(const Structure &structure, const std::string &compoundID, cons
}
Residue
::
Residue
(
const
Structure
&
structure
,
const
std
::
string
&
compoundID
,
const
std
::
string
&
asymID
,
int
seqID
,
const
std
::
string
&
authSeqID
)
const
std
::
string
&
asymID
,
int
seqID
,
const
std
::
string
&
authSeqID
)
:
mStructure
(
&
structure
)
,
mCompoundID
(
compoundID
)
,
mAsymID
(
asymID
)
...
...
@@ -845,7 +845,7 @@ Residue::Residue(const Structure &structure, const std::string &compoundID,
continue
;
if
(
a
.
labelAsymID
()
!=
mAsymID
or
a
.
labelCompID
()
!=
mCompoundID
)
a
.
labelCompID
()
!=
mCompoundID
)
continue
;
mAtoms
.
push_back
(
a
);
...
...
@@ -968,7 +968,7 @@ std::string Residue::unique_alt_id() const
throw
std
::
runtime_error
(
"Invalid Residue object"
);
auto
firstAlt
=
std
::
find_if
(
mAtoms
.
begin
(),
mAtoms
.
end
(),
[](
auto
&
a
)
{
return
not
a
.
labelAltID
().
empty
();
});
{
return
not
a
.
labelAltID
().
empty
();
});
return
firstAlt
!=
mAtoms
.
end
()
?
firstAlt
->
labelAltID
()
:
""
;
}
...
...
@@ -1104,7 +1104,7 @@ std::tuple<Point, float> Residue::centerAndRadius() const
bool
Residue
::
hasAlternateAtoms
()
const
{
return
std
::
find_if
(
mAtoms
.
begin
(),
mAtoms
.
end
(),
[](
const
Atom
&
atom
)
{
return
atom
.
isAlternate
();
})
!=
mAtoms
.
end
();
{
return
atom
.
isAlternate
();
})
!=
mAtoms
.
end
();
}
std
::
set
<
std
::
string
>
Residue
::
getAtomIDs
()
const
...
...
@@ -1476,7 +1476,7 @@ float Monomer::chiralVolume() const
auto
atom3
=
atomByID
(
"CD2"
);
result
=
DotProduct
(
atom1
.
location
()
-
centre
.
location
(),
CrossProduct
(
atom2
.
location
()
-
centre
.
location
(),
atom3
.
location
()
-
centre
.
location
()));
CrossProduct
(
atom2
.
location
()
-
centre
.
location
(),
atom3
.
location
()
-
centre
.
location
()));
}
else
if
(
mCompoundID
==
"VAL"
)
{
...
...
@@ -1486,7 +1486,7 @@ float Monomer::chiralVolume() const
auto
atom3
=
atomByID
(
"CG2"
);
result
=
DotProduct
(
atom1
.
location
()
-
centre
.
location
(),
CrossProduct
(
atom2
.
location
()
-
centre
.
location
(),
atom3
.
location
()
-
centre
.
location
()));
CrossProduct
(
atom2
.
location
()
-
centre
.
location
(),
atom3
.
location
()
-
centre
.
location
()));
}
return
result
;
...
...
@@ -1740,6 +1740,14 @@ File::~File()
delete
mImpl
;
}
cif
::
Datablock
&
File
::
createDatablock
(
const
std
::
string
&
name
)
{
auto
db
=
new
cif
::
Datablock
(
name
);
mImpl
->
mData
.
append
(
db
);
mImpl
->
mDb
=
db
;
}
void
File
::
load
(
const
std
::
string
&
p
)
{
mImpl
->
load
(
p
);
...
...
@@ -1778,8 +1786,11 @@ Structure::Structure(File &f, size_t modelNr, StructureOpenOptions options)
:
mFile
(
f
)
,
mModelNr
(
modelNr
)
{
auto
&
db
=
*
mFile
.
impl
().
mDb
;
auto
&
atomCat
=
db
[
"atom_site"
];
auto
db
=
mFile
.
impl
().
mDb
;
if
(
db
==
nullptr
)
throw
std
::
logic_error
(
"Empty file!"
);
auto
&
atomCat
=
(
*
db
)[
"atom_site"
];
for
(
auto
&
a
:
atomCat
)
{
...
...
@@ -1864,13 +1875,13 @@ void Structure::updateAtomIndex()
iota
(
mAtomIndex
.
begin
(),
mAtomIndex
.
end
(),
0
);
sort
(
mAtomIndex
.
begin
(),
mAtomIndex
.
end
(),
[
this
](
size_t
a
,
size_t
b
)
{
return
mAtoms
[
a
].
id
()
<
mAtoms
[
b
].
id
();
});
{
return
mAtoms
[
a
].
id
()
<
mAtoms
[
b
].
id
();
});
}
void
Structure
::
sortAtoms
()
{
sort
(
mAtoms
.
begin
(),
mAtoms
.
end
(),
[](
auto
&
a
,
auto
&
b
)
{
return
a
.
compare
(
b
)
<
0
;
});
{
return
a
.
compare
(
b
)
<
0
;
});
int
id
=
1
;
for
(
auto
&
atom
:
mAtoms
)
...
...
@@ -1914,8 +1925,8 @@ AtomView Structure::waters() const
Atom
Structure
::
getAtomByID
(
std
::
string
id
)
const
{
auto
i
=
std
::
lower_bound
(
mAtomIndex
.
begin
(),
mAtomIndex
.
end
(),
id
,
[
this
](
auto
&
a
,
auto
&
b
)
{
return
mAtoms
[
a
].
id
()
<
b
;
});
id
,
[
this
](
auto
&
a
,
auto
&
b
)
{
return
mAtoms
[
a
].
id
()
<
b
;
});
// auto i = find_if(mAtoms.begin(), mAtoms.end(),
// [&id](auto& a) { return a.id() == id; });
...
...
@@ -1931,10 +1942,10 @@ Atom Structure::getAtomByLabel(const std::string &atomID, const std::string &asy
for
(
auto
&
a
:
mAtoms
)
{
if
(
a
.
labelAtomID
()
==
atomID
and
a
.
labelAsymID
()
==
asymID
and
a
.
labelCompID
()
==
compID
and
a
.
labelSeqID
()
==
seqID
and
a
.
labelAltID
()
==
altID
)
a
.
labelAsymID
()
==
asymID
and
a
.
labelCompID
()
==
compID
and
a
.
labelSeqID
()
==
seqID
and
a
.
labelAltID
()
==
altID
)
{
return
a
;
}
...
...
@@ -2013,12 +2024,12 @@ std::tuple<char, int, char> Structure::MapLabelToAuth(
try
{
result
=
std
::
make_tuple
(
auth_asym_id
.
front
(),
std
::
stoi
(
auth_seq_num
),
pdb_ins_code
.
empty
()
?
' '
:
pdb_ins_code
.
front
());
pdb_ins_code
.
empty
()
?
' '
:
pdb_ins_code
.
front
());
}
catch
(
const
std
::
exception
&
ex
)
{
result
=
std
::
make_tuple
(
auth_asym_id
.
front
(),
0
,
pdb_ins_code
.
empty
()
?
' '
:
pdb_ins_code
.
front
());
pdb_ins_code
.
empty
()
?
' '
:
pdb_ins_code
.
front
());
}
found
=
true
;
...
...
@@ -2037,7 +2048,7 @@ std::tuple<char, int, char> Structure::MapLabelToAuth(
r
.
front
().
get
(
"pdb_strand_id"
,
"pdb_seq_num"
,
"pdb_ins_code"
);
result
=
std
::
make_tuple
(
pdb_strand_id
.
front
(),
pdb_seq_num
,
pdb_ins_code
.
empty
()
?
' '
:
pdb_ins_code
.
front
());
pdb_ins_code
.
empty
()
?
' '
:
pdb_ins_code
.
front
());
found
=
true
;
}
...
...
@@ -2089,7 +2100,7 @@ std::tuple<std::string, int, std::string, std::string> Structure::MapLabelToPDB(
}
std
::
tuple
<
std
::
string
,
int
,
std
::
string
>
Structure
::
MapPDBToLabel
(
const
std
::
string
&
asymID
,
int
seqID
,
const
std
::
string
&
compID
,
const
std
::
string
&
iCode
)
const
const
std
::
string
&
compID
,
const
std
::
string
&
iCode
)
const
{
auto
&
db
=
datablock
();
...
...
@@ -2161,10 +2172,10 @@ void Structure::insertCompound(const std::string &compoundID, bool isEntity)
if
(
r
.
empty
())
{
chemComp
.
emplace
({{
"id"
,
compoundID
},
{
"name"
,
compound
->
name
()},
{
"formula"
,
compound
->
formula
()},
{
"formula_weight"
,
compound
->
formulaWeight
()},
{
"type"
,
compound
->
type
()}});
{
"name"
,
compound
->
name
()},
{
"formula"
,
compound
->
formula
()},
{
"formula_weight"
,
compound
->
formulaWeight
()},
{
"type"
,
compound
->
type
()}});
}
if
(
isEntity
)
...
...
@@ -2176,13 +2187,13 @@ void Structure::insertCompound(const std::string &compoundID, bool isEntity)
std
::
string
entityID
=
std
::
to_string
(
entity
.
size
()
+
1
);
entity
.
emplace
({{
"id"
,
entityID
},
{
"type"
,
"non-polymer"
},
{
"pdbx_description"
,
compound
->
name
()},
{
"formula_weight"
,
compound
->
formulaWeight
()}});
{
"type"
,
"non-polymer"
},
{
"pdbx_description"
,
compound
->
name
()},
{
"formula_weight"
,
compound
->
formulaWeight
()}});
pdbxEntityNonpoly
.
emplace
({{
"entity_id"
,
entityID
},
{
"name"
,
compound
->
name
()},
{
"comp_id"
,
compoundID
}});
{
"name"
,
compound
->
name
()},
{
"comp_id"
,
compoundID
}});
}
}
}
...
...
@@ -2305,7 +2316,7 @@ void Structure::moveAtom(Atom &a, Point p)
}
void
Structure
::
changeResidue
(
const
Residue
&
res
,
const
std
::
string
&
newCompound
,
const
std
::
vector
<
std
::
tuple
<
std
::
string
,
std
::
string
>>
&
remappedAtoms
)
const
std
::
vector
<
std
::
tuple
<
std
::
string
,
std
::
string
>>
&
remappedAtoms
)
{
using
namespace
cif
::
literals
;
...
...
@@ -2333,15 +2344,15 @@ void Structure::changeResidue(const Residue &res, const std::string &newCompound
{
entityID
=
entity
.
getUniqueID
(
""
);
entity
.
emplace
({{
"id"
,
entityID
},
{
"type"
,
"non-polymer"
},
{
"pdbx_description"
,
compound
->
name
()},
{
"formula_weight"
,
compound
->
formulaWeight
()}});
{
"type"
,
"non-polymer"
},
{
"pdbx_description"
,
compound
->
name
()},
{
"formula_weight"
,
compound
->
formulaWeight
()}});
}
auto
&
pdbxEntityNonpoly
=
db
[
"pdbx_entity_nonpoly"
];
pdbxEntityNonpoly
.
emplace
({{
"entity_id"
,
entityID
},
{
"name"
,
compound
->
name
()},
{
"comp_id"
,
newCompound
}});
{
"name"
,
compound
->
name
()},
{
"comp_id"
,
newCompound
}});
auto
&
pdbxNonPolyScheme
=
db
[
"pdbx_nonpoly_scheme"
];
for
(
auto
&
nps
:
pdbxNonPolyScheme
.
find
(
"asym_id"
_key
==
asymID
))
...
...
@@ -2356,10 +2367,10 @@ void Structure::changeResidue(const Residue &res, const std::string &newCompound
if
(
not
chemComp
.
exists
(
cif
::
Key
(
"id"
)
==
newCompound
))
{
chemComp
.
emplace
({{
"id"
,
newCompound
},
{
"name"
,
compound
->
name
()},
{
"formula"
,
compound
->
formula
()},
{
"formula_weight"
,
compound
->
formulaWeight
()},
{
"type"
,
compound
->
type
()}});
{
"name"
,
compound
->
name
()},
{
"formula"
,
compound
->
formula
()},
{
"formula_weight"
,
compound
->
formulaWeight
()},
{
"type"
,
compound
->
type
()}});
}
// update the struct_asym for the new entity
...
...
@@ -2377,7 +2388,7 @@ void Structure::changeResidue(const Residue &res, const std::string &newCompound
tie
(
a1
,
a2
)
=
a
;
auto
i
=
find_if
(
atoms
.
begin
(),
atoms
.
end
(),
[
&
](
const
Atom
&
a
)
{
return
a
.
labelAtomID
()
==
a1
;
});
{
return
a
.
labelAtomID
()
==
a1
;
});
if
(
i
==
atoms
.
end
())
{
std
::
cerr
<<
"Missing atom for atom ID "
<<
a1
<<
std
::
endl
;
...
...
@@ -2400,6 +2411,31 @@ void Structure::changeResidue(const Residue &res, const std::string &newCompound
}
}
std
::
string
Structure
::
createEntityNonPoly
(
std
::
vector
<
cif
::
Item
>
data
,
const
std
::
string
&
mon_id
)
{
auto
&
db
=
mFile
.
data
();
auto
&
entity
=
db
[
"entity"
];
std
::
string
entity_id
=
entity
.
getUniqueID
(
""
);
// remove any ID fields
data
.
erase
(
std
::
remove_if
(
data
.
begin
(),
data
.
end
(),
[](
cif
::
Item
&
item
)
{
return
item
.
name
()
==
"id"
;
}),
data
.
end
());
// add our new ID
data
.
emplace_back
(
"id"
,
entity_id
);
data
.
emplace_back
(
"type"
,
"non-polymer"
);
entity
.
emplace
(
data
.
begin
(),
data
.
end
());
return
entity_id
;
}
std
::
string
Structure
::
createNonpoly
(
const
std
::
string
&
entity_id
,
const
std
::
vector
<
cif
::
Item
>
&
atoms
)
{
return
{};
}
void
Structure
::
cleanupEmptyCategories
()
{
using
namespace
cif
::
literals
;
...
...
test/structure-test.cpp
0 → 100644
View file @
345c4778
/*-
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2021 NKI/AVL, Netherlands Cancer Institute
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#define BOOST_TEST_MODULE Structure_Test
#include <boost/test/included/unit_test.hpp>
#include <stdexcept>
#include "cif++/Cif++.hpp"
#include "cif++/Structure.hpp"
// --------------------------------------------------------------------
cif
::
File
operator
""
_cf
(
const
char
*
text
,
size_t
length
)
{
struct
membuf
:
public
std
::
streambuf
{
membuf
(
char
*
text
,
size_t
length
)
{
this
->
setg
(
text
,
text
,
text
+
length
);
}
}
buffer
(
const_cast
<
char
*>
(
text
),
length
);
std
::
istream
is
(
&
buffer
);
return
cif
::
File
(
is
);
}
// --------------------------------------------------------------------
BOOST_AUTO_TEST_CASE
(
create_nonpoly_1
)
{
cif
::
VERBOSE
=
1
;
// do this now, avoids the need for installing
cif
::
addFileResource
(
"mmcif_pdbx_v50.dic"
,
"../rsrc/mmcif_pdbx_v50.dic"
);
auto
expected
=
R"(
data_TEST
loop_
_entity.id
_entity.type
_entity.src_method
_entity.pdbx_description
_entity.formula_weight
1 non-polymer syn 'PROTOPORPHYRIN IX CONTAINING FE' 616.487
)"
_cf
;
expected
.
loadDictionary
(
"mmcif_pdbx_v50.dic"
);
mmcif
::
File
file
;
file
.
file
().
loadDictionary
(
"mmcif_pdbx_v50.dic"
);
file
.
createDatablock
(
"TEST"
);
// create a datablock
mmcif
::
Structure
structure
(
file
);
structure
.
createEntityNonPoly
({
{
"src_method"
,
"syn"
},
{
"pdbx_description"
,
"PROTOPORPHYRIN IX CONTAINING FE"
},
{
"formula_weight"
,
616.487
}
},
"HEM"
);
BOOST_TEST
(
expected
.
firstDatablock
()
==
structure
.
getFile
().
data
());
std
::
cout
<<
expected
.
firstDatablock
()
<<
std
::
endl
<<
std
::
endl
<<
structure
.
getFile
().
data
()
<<
std
::
endl
;
// // using namespace mmcif;
// auto f = R"(data_TEST
// #
// loop_
// _test.id
// _test.name
// 1 aap
// 2 noot
// 3 mies
// )"_cf;
// auto& db = f.firstDatablock();
// BOOST_CHECK(db.getName() == "TEST");
// auto& test = db["test"];
// BOOST_CHECK(test.size() == 3);
// // wrong! the next lines will crash. And that's OK, don't do that
// // for (auto r: test)
// // test.erase(r);
// // BOOST_CHECK(test.empty());
// // test.purge();
// auto n = test.erase(cif::Key("id") == 1, [](const cif::Row& r) {
// BOOST_CHECK_EQUAL(r["id"].as<int>(), 1);
// BOOST_CHECK_EQUAL(r["name"].as<std::string>(), "aap");
// });
// BOOST_CHECK_EQUAL(n, 1);
}
test/unit-test.cpp
View file @
345c4778
...
...
@@ -1664,4 +1664,4 @@ BOOST_AUTO_TEST_CASE(bondmap_2)
mmcif
::
CompoundFactory
::
instance
().
pushDictionary
(
"./UN_.cif"
);
BOOST_CHECK
(
mmcif
::
BondMap
::
atomIDsForCompound
(
"UN_"
).
empty
()
==
false
);
}
\ No newline at end of file
}
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