Xmipp  v3.23.11-Nereus
datablock.cpp
Go to the documentation of this file.
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (c) 2022 NKI/AVL, Netherlands Cancer Institute
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions are met:
8  *
9  * 1. Redistributions of source code must retain the above copyright notice, this
10  * list of conditions and the following disclaimer
11  * 2. Redistributions in binary form must reproduce the above copyright notice,
12  * this list of conditions and the following disclaimer in the documentation
13  * and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
16  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
17  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18  * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
19  * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
20  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
21  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
22  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
24  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25  */
26 
27 #include "cif++/datablock.hpp"
28 
29 namespace cif
30 {
31 
32 datablock::datablock(const datablock &db)
33  : std::list<category>(db)
34  , m_name(db.m_name)
35  , m_validator(db.m_validator)
36 {
37  for (auto &cat : *this)
38  cat.update_links(*this);
39 }
40 
41 datablock &datablock::operator=(const datablock &db)
42 {
43  if (this != &db)
44  {
45  std::list<category>::operator=(db);
46  m_name = db.m_name;
47  m_validator = db.m_validator;
48 
49  for (auto &cat : *this)
50  cat.update_links(*this);
51  }
52 
53  return *this;
54 }
55 
56 void datablock::set_validator(const validator *v)
57 {
58  m_validator = v;
59 
60  try
61  {
62  for (auto &cat : *this)
63  cat.set_validator(v, *this);
64  }
65  catch (const std::exception &)
66  {
67  throw_with_nested(std::runtime_error("Error while setting validator in datablock " + m_name));
68  }
69 }
70 
71 const validator *datablock::get_validator() const
72 {
73  return m_validator;
74 }
75 
76 bool datablock::is_valid() const
77 {
78  if (m_validator == nullptr)
79  throw std::runtime_error("Validator not specified");
80 
81  bool result = true;
82  for (auto &cat : *this)
83  result = cat.is_valid() and result;
84 
85  return result;
86 }
87 
88 bool datablock::validate_links() const
89 {
90  bool result = true;
91 
92  for (auto &cat : *this)
93  result = cat.validate_links() and result;
94 
95  return result;
96 }
97 
98 // --------------------------------------------------------------------
99 
100 category &datablock::operator[](std::string_view name)
101 {
102  auto i = std::find_if(begin(), end(), [name](const category &c)
103  { return iequals(c.name(), name); });
104 
105  if (i != end())
106  return *i;
107 
108  auto &cat = emplace_back(name);
109 
110  if (m_validator)
111  cat.set_validator(m_validator, *this);
112 
113  return back();
114 }
115 
116 const category &datablock::operator[](std::string_view name) const
117 {
118  static const category s_empty;
119  auto i = std::find_if(begin(), end(), [name](const category &c)
120  { return iequals(c.name(), name); });
121  return i == end() ? s_empty : *i;
122 }
123 
124 category *datablock::get(std::string_view name)
125 {
126  auto i = std::find_if(begin(), end(), [name](const category &c)
127  { return iequals(c.name(), name); });
128  return i == end() ? nullptr : &*i;
129 }
130 
131 const category *datablock::get(std::string_view name) const
132 {
133  return const_cast<datablock *>(this)->get(name);
134 }
135 
136 std::tuple<datablock::iterator, bool> datablock::emplace(std::string_view name)
137 {
138  bool is_new = true;
139 
140  auto i = begin();
141  while (i != end())
142  {
143  if (iequals(name, i->name()))
144  {
145  is_new = false;
146 
147  if (i != begin())
148  {
149  auto n = std::next(i);
150  splice(begin(), *this, i, n);
151  }
152 
153  break;
154  }
155 
156  ++i;
157  }
158 
159  if (is_new)
160  {
161  auto &c = emplace_front(name);
162  c.set_validator(m_validator, *this);
163  }
164 
165  return std::make_tuple(begin(), is_new);
166 }
167 
168 std::vector<std::string> datablock::get_tag_order() const
169 {
170  std::vector<std::string> result;
171 
172  // for entry and audit_conform on top
173 
174  auto ci = find_if(begin(), end(), [](const category &cat) { return cat.name() == "entry"; });
175  if (ci != end())
176  {
177  auto cto = ci->get_tag_order();
178  result.insert(result.end(), cto.begin(), cto.end());
179  }
180 
181  ci = find_if(begin(), end(), [](const category &cat) { return cat.name() == "audit_conform"; });
182  if (ci != end())
183  {
184  auto cto = ci->get_tag_order();
185  result.insert(result.end(), cto.begin(), cto.end());
186  }
187 
188  for (auto &cat : *this)
189  {
190  if (cat.name() == "entry" or cat.name() == "audit_conform")
191  continue;
192  auto cto = cat.get_tag_order();
193  result.insert(result.end(), cto.begin(), cto.end());
194  }
195 
196  return result;
197 }
198 
199 void datablock::write(std::ostream &os) const
200 {
201  os << "data_" << m_name << std::endl
202  << "# " << std::endl;
203 
204  // mmcif support, sort of. First write the 'entry' Category
205  // and if it exists, _AND_ we have a Validator, write out the
206  // audit_conform record.
207 
208  for (auto &cat : *this)
209  {
210  if (cat.name() != "entry")
211  continue;
212 
213  cat.write(os);
214 
215  break;
216  }
217 
218  // If the dictionary declares an audit_conform category, put it in,
219  // but only if it does not exist already!
220  if (get("audit_conform"))
221  get("audit_conform")->write(os);
222  else if (m_validator != nullptr and m_validator->get_validator_for_category("audit_conform") != nullptr)
223  {
224  category auditConform("audit_conform");
225  auditConform.emplace({
226  {"dict_name", m_validator->name()},
227  {"dict_version", m_validator->version()}});
228  auditConform.write(os);
229  }
230 
231  for (auto &cat : *this)
232  {
233  if (cat.name() != "entry" and cat.name() != "audit_conform")
234  cat.write(os);
235  }
236 }
237 
238 void datablock::write(std::ostream &os, const std::vector<std::string> &tag_order)
239 {
240  os << "data_" << m_name << std::endl
241  << "# " << std::endl;
242 
243  std::vector<std::string> cat_order;
244  for (auto &o : tag_order)
245  {
246  std::string cat_name, item_name;
247  std::tie(cat_name, item_name) = split_tag_name(o);
248  if (find_if(cat_order.rbegin(), cat_order.rend(), [cat_name](const std::string &s) -> bool
249  { return iequals(cat_name, s); }) == cat_order.rend())
250  cat_order.push_back(cat_name);
251  }
252 
253  for (auto &c : cat_order)
254  {
255  auto cat = get(c);
256  if (cat == nullptr)
257  continue;
258 
259  std::vector<std::string> items;
260  for (auto &o : tag_order)
261  {
262  std::string cat_name, item_name;
263  std::tie(cat_name, item_name) = split_tag_name(o);
264 
265  if (cat_name == c)
266  items.push_back(item_name);
267  }
268 
269  cat->write(os, items);
270  }
271 
272  // for any Category we missed in the catOrder
273  for (auto &cat : *this)
274  {
275  if (find_if(cat_order.begin(), cat_order.end(), [&](const std::string &s) -> bool
276  { return iequals(cat.name(), s); }) != cat_order.end())
277  continue;
278 
279  cat.write(os);
280  }
281 }
282 
283 bool datablock::operator==(const datablock &rhs) const
284 {
285  auto &dbA = *this;
286  auto &dbB = rhs;
287 
288  std::vector<std::string> catA, catB;
289 
290  for (auto &cat : dbA)
291  {
292  if (not cat.empty())
293  catA.push_back(cat.name());
294  }
295  std::sort(catA.begin(), catA.end());
296 
297  for (auto &cat : dbB)
298  {
299  if (not cat.empty())
300  catB.push_back(cat.name());
301  }
302  std::sort(catB.begin(), catB.end());
303 
304  // loop over categories twice, to group output
305  // First iteration is to list missing categories.
306 
307  std::vector<std::string> missingA, missingB;
308 
309  auto catA_i = catA.begin(), catB_i = catB.begin();
310 
311  while (catA_i != catA.end() and catB_i != catB.end())
312  {
313  if (not iequals(*catA_i, *catB_i))
314  return false;
315 
316  ++catA_i, ++catB_i;
317  }
318 
319  if (catA_i != catA.end() or catB_i != catB.end())
320  return false;
321 
322  // Second loop, now compare category values
323  catA_i = catA.begin(), catB_i = catB.begin();
324 
325  while (catA_i != catA.end() and catB_i != catB.end())
326  {
327  std::string nA = *catA_i;
328  to_lower(nA);
329 
330  std::string nB = *catB_i;
331  to_lower(nB);
332 
333  int d = nA.compare(nB);
334  if (d > 0)
335  ++catB_i;
336  else if (d < 0)
337  ++catA_i;
338  else
339  {
340  if (not (*dbA.get(*catA_i) == *dbB.get(*catB_i)))
341  return false;
342  ++catA_i;
343  ++catB_i;
344  }
345  }
346 
347  return true;
348 }
349 
350 } // namespace cif::cif
void to_lower(std::string &s)
Definition: text.cpp:114
void write(std::ostream &os, const datablock &db)
Definition: cif2pdb.cpp:3747
doublereal * c
bool operator==(faketype, faketype)
Definition: gtest.h:1366
bool iequals(std::string_view a, std::string_view b)
Definition: text.cpp:59
#define i
doublereal * d
std::tuple< std::string, std::string > split_tag_name(std::string_view tag)
Definition: text.cpp:218
void sort(struct DCEL_T *dcel)
Definition: sorting.cpp:18
int * n