Gemmi C++ API
Loading...
Searching...
No Matches
cifdoc.hpp
Go to the documentation of this file.
1// Copyright 2017 Global Phasing Ltd.
2//
3// struct Document that represents the CIF file (but can also be
4// read from a different representation, such as CIF-JSON or mmJSON).
5
6#ifndef GEMMI_CIFDOC_HPP_
7#define GEMMI_CIFDOC_HPP_
8#include "iterator.hpp" // for StrideIter, IndirectIter
9#include "atox.hpp" // for string_to_int
10#include "fail.hpp" // for fail
11#include "util.hpp" // for starts_with, to_lower, cat
12#include <cstddef> // for size_t
13#include <cstring> // for memchr
14#include <algorithm> // for move, find_if, all_of, min, rotate
15#include <array>
16#include <initializer_list>
17#include <new>
18#include <stdexcept>
19#include <string>
20#include <unordered_set>
21#include <vector>
22
23#if defined(_MSC_VER)
24#pragma warning(push)
25// warning C4244: an integer type is converted to a smaller integer type
26#pragma warning(disable: 4244)
27// warning C4267: conversion from 'size_t' to 'type', possible loss of data
28#pragma warning(disable: 4267)
29#endif
30
31namespace gemmi {
32namespace cif {
33using std::size_t;
34using gemmi::fail;
35
36enum class ItemType : unsigned char {
37 Pair,
38 Loop,
39 Frame,
40 Comment,
41 Erased,
42};
43
44inline uint8_t char_table(char c) {
45 static const uint8_t table[256] = {
46 // 0 1 2 3 4 5 6 7 8 9 A B C D E F
47 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 0, 0, 2, 0, 0, // 0
48 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1
49 2, 1, 0, 0, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, // 2
50 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, // 3
51 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 4
52 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, // 5
53 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 6
54 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, // 7
55 // 128-255
56 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
57 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
58 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
59 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
60 };
61 return table[static_cast<unsigned char>(c)];
62}
63
64inline void assert_tag(const std::string& tag) {
65 if (tag[0] != '_')
66 fail("Tag should start with '_', got: " + tag);
67}
68
69inline void ensure_mmcif_category(std::string& cat) {
70 if (cat[0] != '_')
71 fail("Category should start with '_', got: " + cat);
72 if (*(cat.end() - 1) != '.')
73 cat += '.';
74}
75
76inline bool is_null(const std::string& value) {
77 return value.size() == 1 && (value[0] == '?' || value[0] == '.');
78}
79
80inline std::string as_string(const std::string& value) {
81 if (value.empty() || is_null(value))
82 return "";
83 if (value[0] == '"' || value[0] == '\'')
84 return std::string(value.begin() + 1, value.end() - 1);
85 if (value[0] == ';' && value.size() > 2 && *(value.end() - 2) == '\n') {
86 bool crlf = *(value.end() - 3) == '\r';
87 return std::string(value.begin() + 1, value.end() - (crlf ? 3 : 2));
88 }
89 return value;
90}
91
92inline std::string as_string(const std::string* value) {
93 return value ? as_string(*value) : std::string();
94}
95
96inline char as_char(const std::string& value, char null) {
97 if (is_null(value))
98 return null;
99 if (value.size() < 2)
100 return value[0];
101 const std::string s = as_string(value);
102 if (s.size() < 2)
103 return s[0];
104 fail("Not a single character: " + value);
105}
106
107inline int as_int(const std::string& str) {
108 return string_to_int(str, true);
109}
110
111inline int as_int(const std::string& str, int null) {
112 return is_null(str) ? null : as_int(str);
113}
114
115// for use in templates (see also as_any() functions in numb.hpp)
116inline int as_any(const std::string& s, int null) { return as_int(s, null); }
117inline char as_any(const std::string& s, char null) { return as_char(s, null); }
118
119
120using Pair = std::array<std::string, 2>;
121
122// used only as arguments when creating Item
123struct LoopArg {};
124struct FrameArg { std::string str; };
125struct CommentArg { std::string str; };
126
127struct Loop {
128 std::vector<std::string> tags;
129 std::vector<std::string> values;
130
131 // search and access
132 int find_tag_lc(const std::string& lctag) const {
133 auto f = std::find_if(tags.begin(), tags.end(),
134 [&lctag](const std::string& t) { return gemmi::iequal(t, lctag); });
135 return f == tags.end() ? -1 : f - tags.begin();
136 }
137 int find_tag(const std::string& tag) const {
138 return find_tag_lc(gemmi::to_lower(tag));
139 }
140 bool has_tag(const std::string& tag) const { return find_tag(tag) != -1; }
141 size_t width() const { return tags.size(); }
142 size_t length() const { return values.size() / tags.size(); }
143
144 std::string& val(size_t row, size_t col) { return values[row * tags.size() + col]; }
145 const std::string& val(size_t row, size_t col) const {
146 return const_cast<Loop*>(this)->val(row, col);
147 }
148
149 void clear() { tags.clear(); values.clear(); }
150
151 template <typename T> void add_values(T new_values, int pos=-1) {
152 auto it = values.end();
153 if (pos >= 0 && pos * width() < values.size())
154 it = values.begin() + pos * tags.size();
155 values.insert(it, new_values.begin(), new_values.end());
156 }
157 void add_values(std::initializer_list<std::string> new_values, int pos=-1) {
159 }
160 template <typename T> void add_row(T new_values, int pos=-1) {
161 if (new_values.size() != tags.size())
162 fail("add_row(): wrong row length.");
164 }
165 void add_row(std::initializer_list<std::string> new_values, int pos=-1) {
167 }
168 // comments are added relying on how cif writing works
169 void add_comment_and_row(std::initializer_list<std::string> ss) {
170 if (ss.size() != tags.size() + 1)
171 fail("add_comment_and_row(): wrong row length.");
172 std::vector<std::string> vec(ss.begin() + 1, ss.end());
173 vec[0] = cat('#', *ss.begin(), '\n', vec[0]);
174 add_row(vec);
175 }
176 void pop_row() {
177 if (values.size() < tags.size())
178 fail("pop_row() called on empty Loop");
179 values.resize(values.size() - tags.size());
180 }
181
182 // the arguments must be valid row indices
183 void move_row(int old_pos, int new_pos) {
184 size_t w = width();
185 auto src = values.begin() + old_pos * w;
186 auto dst = values.begin() + new_pos * w;
187 if (src < dst)
188 std::rotate(src, src+w, dst+w);
189 else
190 std::rotate(dst, src, src+w);
191 }
192
193 // column_names are not checked for duplicates nor for category name
194 void add_columns(const std::vector<std::string>& column_names,
195 const std::string& value, int pos=-1) {
196 for (const std::string& name : column_names)
197 assert_tag(name);
198 size_t old_width = tags.size();
199 size_t len = length();
200 size_t upos = std::min((size_t)pos, old_width);
201 tags.insert(tags.begin() + upos, column_names.begin(), column_names.end());
203 }
204
205 void remove_column(const std::string& column_name) {
206 int n = find_tag(column_name);
207 if (n == -1)
208 fail("remove_column(): tag not found: " + column_name);
210 }
211
213 void remove_column_at(size_t n) {
214 tags.erase(tags.begin() + n);
215 vector_remove_column(values, tags.size(), n);
216 }
217
218 void set_all_values(std::vector<std::vector<std::string>> columns);
219
220 std::string common_prefix() const {
221 if (tags.empty())
222 return {};
223 size_t len = tags[0].size();
224 for (auto it = tags.begin() + 1; it != tags.end(); ++it)
225 for (size_t n = 0; n != len; ++n)
226 if (!isame(tags[0][n], (*it)[n])) {
227 len = n;
228 break;
229 }
230 return tags[0].substr(0, len);
231 }
232};
233
234
235struct Item;
236struct Block;
237
238// Accessor to a specific loop column, or to a single value from a Pair.
239class Column {
240public:
241 Column() : item_(nullptr) {}
242 Column(Item* item, size_t col) : item_(item), col_(col) {}
244 iterator begin();
245 iterator end();
247 const_iterator begin() const { return const_cast<Column*>(this)->begin(); }
248 const_iterator end() const { return const_cast<Column*>(this)->end(); }
249
250 Loop* get_loop() const;
251 std::string* get_tag();
252 const std::string* get_tag() const {
253 return const_cast<Column*>(this)->get_tag();
254 }
255 int length() const {
256 if (const Loop* loop = get_loop())
257 return loop->length();
258 return item_ ? 1 : 0;
259 }
260 explicit operator bool() const { return item_ != nullptr ; }
261 std::string& operator[](int n);
262 std::string& at(int n) {
263 if (n < 0)
264 n += length();
265 if (n < 0 || n >= length())
266 throw std::out_of_range("Cannot access element " + std::to_string(n) +
267 " in Column with length " + std::to_string(length()));
268 return operator[](n);
269 }
270 const std::string& at(int n) const {
271 return const_cast<Column*>(this)->at(n);
272 }
273
274 std::string str(int n) const { return as_string(at(n)); }
275 const Item* item() const { return item_; }
276 Item* item() { return item_; }
277 size_t col() const { return col_; }
278
279 void erase();
280
281private:
282 Item* item_;
283 size_t col_; // for loop this is a column index in item_->loop
284};
285
286// Some values can be given either in loop or as tag-value pairs.
287// The latter case is equivalent to a loop with a single row.
288// We optimized for loops, and in case of tag-values we copy the values
289// into the `values` vector.
290struct Table {
293 std::vector<int> positions;
295
296 struct Row {
299
300 std::string& value_at_unsafe(int pos);
301 std::string& value_at(int pos) {
302 if (pos == -1)
303 throw std::out_of_range("Cannot access missing optional tag.");
304 return value_at_unsafe(pos);
305 }
306 const std::string& value_at(int pos) const {
307 return const_cast<Row*>(this)->value_at(pos);
308 }
309
310 std::string& at(int n) {
311 return value_at(tab.positions.at(n < 0 ? n + size() : n));
312 }
313 const std::string& at(int n) const { return const_cast<Row*>(this)->at(n); }
314
315 std::string& operator[](size_t n);
316 const std::string& operator[](size_t n) const {
317 return const_cast<Row*>(this)->operator[](n);
318 }
319
320 std::string* ptr_at(int n) {
321 int pos = tab.positions.at(n < 0 ? n + size() : n);
322 return pos >= 0 ? &value_at(pos) : nullptr;
323 }
324 const std::string* ptr_at(int n) const {
325 return const_cast<Row*>(this)->ptr_at(n);
326 }
327
328 bool has(size_t n) const { return tab.positions.at(n) >= 0; }
329 bool has2(size_t n) const { return has(n) && !cif::is_null(operator[](n)); }
330
331 const std::string& one_of(size_t n1, size_t n2) const {
332 static const std::string nul(1, '.');
333 if (has2(n1))
334 return operator[](n1);
335 if (has(n2))
336 return operator[](n2);
337 return nul;
338 }
339
340 size_t size() const { return tab.width(); }
341
342 std::string str(int n) const { return as_string(at(n)); }
343
346 iterator begin() { return iterator({this, tab.positions.begin()}); }
347 iterator end() { return iterator({this, tab.positions.end()}); }
349 return const_iterator({this, tab.positions.begin()});
350 }
352 return const_iterator({this, tab.positions.end()});
353 }
354 };
355
356 Loop* get_loop();
357 bool ok() const { return !positions.empty(); }
358 size_t width() const { return positions.size(); }
359 size_t length() const;
360 size_t size() const { return length(); }
361 bool has_column(int n) const { return ok() && positions.at(n) >= 0; }
362 Row tags() { return Row{*this, -1}; }
363 Row operator[](int n) { return Row{*this, n}; }
364
365 void at_check(int& n) const {
366 if (n < 0)
367 n += length();
368 if (n < 0 || static_cast<size_t>(n) >= length())
369 throw std::out_of_range("No row with index " + std::to_string(n));
370 }
371 Row at(int n) {
372 at_check(n);
373 return (*this)[n];
374 }
375
377 if (length() != 1)
378 fail("Expected one value, found " + std::to_string(length()));
379 return (*this)[0];
380 }
381
382 std::string get_prefix() const {
383 for (int pos : positions)
384 if (pos >= 0)
385 return const_cast<Table*>(this)->tags()
386 .value_at(pos).substr(0, prefix_length);
387 fail("The table has no columns.");
388 }
389
390 Row find_row(const std::string& s);
391
392 template <typename T> void append_row(const T& new_values);
393 void append_row(std::initializer_list<std::string> new_values) {
395 }
396 void remove_row(int row_index) { remove_rows(row_index, row_index+1); }
397 void remove_rows(int start, int end);
398 Column column_at_pos(int pos);
399 Column column(int n) {
400 int pos = positions.at(n);
401 if (pos == -1)
402 fail("Cannot access absent column");
403 return column_at_pos(pos);
404 }
405
406 void move_row(int old_pos, int new_pos) {
409 if (Loop* loop = get_loop())
410 loop->move_row(old_pos, new_pos);
411 }
412
413 // prefix is optional
414 int find_column_position(const std::string& tag) const {
415 std::string lctag = gemmi::to_lower(tag);
416 Row tag_row = const_cast<Table*>(this)->tags();
417 for (int pos : positions) {
418 const std::string& v = tag_row.value_at_unsafe(pos);
419 if (v.length() == lctag.length() ? gemmi::iequal(v, lctag)
421 return pos;
422 }
423 fail("Column name not found: " + tag);
424 }
425
426 Column find_column(const std::string& tag) {
428 }
429
430 void erase();
431
433 void ensure_loop();
434
435 // It is not a proper input iterator, but just enough for using range-for.
436 struct iterator {
438 int index;
439 void operator++() { index++; }
440 bool operator==(const iterator& o) const { return index == o.index; }
441 bool operator!=(const iterator& o) const { return index != o.index; }
442 Row operator*() { return parent[index]; }
443 const std::string& get(int n) const { return parent[index].at(n); }
444 };
445 iterator begin() { return iterator{*this, 0}; }
446 iterator end() { return iterator{*this, (int)length()}; }
447};
448
449struct Block {
450 std::string name;
451 std::vector<Item> items;
452
453 explicit Block(const std::string& name_);
454 Block();
455
456 void swap(Block& o) noexcept { name.swap(o.name); items.swap(o.items); }
457 // access functions
458 const Item* find_pair_item(const std::string& tag) const;
459 const Pair* find_pair(const std::string& tag) const;
460 Column find_loop(const std::string& tag);
461 const Item* find_loop_item(const std::string& tag) const;
462 const std::string* find_value(const std::string& tag) const;
463 Column find_values(const std::string& tag);
464 bool has_tag(const std::string& tag) const {
465 return const_cast<Block*>(this)->find_values(tag).item() != nullptr;
466 }
467 bool has_any_value(const std::string& tag) const {
468 Column c = const_cast<Block*>(this)->find_values(tag);
469 return c.item() != nullptr && !std::all_of(c.begin(), c.end(), is_null);
470 }
471 Table find(const std::string& prefix,
472 const std::vector<std::string>& tags);
473 Table find(const std::vector<std::string>& tags) { return find({}, tags); }
474 Table find_any(const std::string& prefix,
475 const std::vector<std::string>& tags);
476 Table find_or_add(const std::string& prefix, std::vector<std::string> tags) {
477 Table t = find(prefix, tags);
478 if (!t.ok()) {
479 for (int i = 0; i != (int) tags.size(); ++i)
480 t.positions.push_back(i);
481 Table tab = find_any(prefix, tags);
482 t.loop_item = &setup_loop_item(std::move(tab), prefix, std::move(tags));
483 }
484 return t;
485 }
486 Block* find_frame(std::string name);
487 Table item_as_table(Item& item);
488
489 size_t get_index(const std::string& tag) const;
490
491 // modifying functions
492 void set_pair(const std::string& tag, const std::string& value);
493
494 Loop& init_loop(const std::string& prefix, std::vector<std::string> tags) {
495 Table tab = find_any(prefix, tags);
496 return setup_loop(std::move(tab), prefix, std::move(tags));
497 }
498
499 void move_item(int old_pos, int new_pos);
500
501 // mmCIF specific functions
502 std::vector<std::string> get_mmcif_category_names() const;
503 Table find_mmcif_category(std::string cat);
504 bool has_mmcif_category(std::string cat) const;
505
506 Loop& init_mmcif_loop(std::string cat, std::vector<std::string> tags) {
507 ensure_mmcif_category(cat); // modifies cat
508 return setup_loop(find_mmcif_category(cat), cat, std::move(tags));
509 }
510
511private:
512 Item& setup_loop_item(Table&& tab, const std::string& prefix,
513 std::vector<std::string>&& tags);
514 Loop& setup_loop(Table&& tab, const std::string& prefix,
515 std::vector<std::string>&& tags);
516};
517
518
519struct Item {
521 int line_number = -1;
522 union {
526 };
527
529 explicit Item(LoopArg)
530 : type{ItemType::Loop}, loop{} {}
531 explicit Item(std::string&& t)
532 : type{ItemType::Pair}, pair{{std::move(t), std::string()}} {}
533 Item(const std::string& t, const std::string& v)
534 : type{ItemType::Pair}, pair{{t, v}} {}
536 : type{ItemType::Frame}, frame(frame_arg.str) {}
537 explicit Item(CommentArg&& comment)
538 : type{ItemType::Comment}, pair{{std::string(), std::move(comment.str)}} {}
539
541 : type(o.type), line_number(o.line_number) {
542 move_value(std::move(o));
543 }
544 Item(const Item& o)
545 : type(o.type), line_number(o.line_number) {
546 copy_value(o);
547 }
548
549 Item& operator=(Item o) { set_value(std::move(o)); return *this; }
550
551 ~Item() { destruct(); }
552
553 void erase() {
554 destruct();
555 type = ItemType::Erased;
556 }
557
558 // case-insensitive, the prefix should be lower-case
559 bool has_prefix(const std::string& prefix) const {
560 return (type == ItemType::Pair && gemmi::istarts_with(pair[0], prefix)) ||
561 (type == ItemType::Loop && !loop.tags.empty() &&
562 gemmi::istarts_with(loop.tags[0], prefix));
563 }
564
565 void set_value(Item&& o) {
566 if (type == o.type) {
567 switch (type) {
568 case ItemType::Pair: pair = std::move(o.pair); break;
569 case ItemType::Loop: loop = std::move(o.loop); break;
570 case ItemType::Frame: frame = std::move(o.frame); break;
571 case ItemType::Comment: pair = std::move(o.pair); break;
572 case ItemType::Erased: break;
573 }
574 } else {
575 destruct();
576 type = o.type;
577 move_value(std::move(o));
578 }
579 }
580
581private:
582 void destruct() {
583 switch (type) {
584 case ItemType::Pair: pair.~Pair(); break;
585 case ItemType::Loop: loop.~Loop(); break;
586 case ItemType::Frame: frame.~Block(); break;
587 case ItemType::Comment: pair.~Pair(); break;
588 case ItemType::Erased: break;
589 }
590 }
591
592 void copy_value(const Item& o) {
593 if (o.type == ItemType::Pair || o.type == ItemType::Comment)
594 new (&pair) Pair(o.pair);
595 else if (o.type == ItemType::Loop)
596 new (&loop) Loop(o.loop);
597 else if (o.type == ItemType::Frame)
598 new (&frame) Block(o.frame);
599 }
600
601 void move_value(Item&& o) {
602 if (o.type == ItemType::Pair || o.type == ItemType::Comment)
603 new (&pair) Pair(std::move(o.pair));
604 else if (o.type == ItemType::Loop)
605 new (&loop) Loop(std::move(o.loop));
606 else if (o.type == ItemType::Frame)
607 new (&frame) Block(std::move(o.frame));
608 }
609};
610
611
612// ItemSpan is used to add tag-value pairs next to the same category tags.
613struct ItemSpan {
614 ItemSpan(std::vector<Item>& items)
615 : items_(items), begin_(0), end_(items.size()) {}
616 ItemSpan(std::vector<Item>& items, std::string prefix)
617 : ItemSpan(items) {
620 while (begin_ != end_ && !items_[begin_].has_prefix(prefix))
621 ++begin_;
622 if (begin_ != end_)
623 while (end_-1 != begin_ && !items_[end_-1].has_prefix(prefix))
624 --end_;
625 }
626 void set_pair(const std::string& tag, const std::string& value) {
627 assert_tag(tag);
628 std::string lctag = gemmi::to_lower(tag);
629 auto end = items_.begin() + end_;
630 for (auto i = items_.begin() + begin_; i != end; ++i) {
631 if (i->type == ItemType::Pair && gemmi::iequal(i->pair[0], lctag)) {
632 i->pair[0] = tag; // if letter case differs, the tag changes
633 i->pair[1] = value;
634 return;
635 }
636 if (i->type == ItemType::Loop && i->loop.find_tag_lc(lctag) != -1) {
637 i->set_value(Item(tag, value));
638 return;
639 }
640 }
641 items_.emplace(end, tag, value);
642 ++end_;
643 }
644private:
645 std::vector<Item>& items_;
646 size_t begin_, end_; // iterators would be invalidated by emplace()
647};
648
649
650inline void Loop::set_all_values(std::vector<std::vector<std::string>> columns){
651 size_t w = columns.size();
652 if (w != width())
653 fail(cat("set_all_values(): expected ", width(), " columns, got ", w));
654 if (w == 0)
655 return;
656 size_t h = columns[0].size();
657 for (auto& col : columns)
658 if (col.size() != h)
659 fail("set_all_values(): all columns must have the same length");
660 values.resize(w * h);
661 for (size_t i = 0; i != h; ++i)
662 for (size_t j = 0; j != w; ++j)
663 values[w * i + j] = std::move(columns[j][i]);
664}
665
666inline std::string* Column::get_tag() {
667 if (!item_)
668 return nullptr;
669 if (Loop* loop = get_loop())
670 return &loop->tags.at(col_);
671 return &item_->pair[0];
672}
673
674inline void Column::erase() {
675 if (Loop* loop = get_loop())
676 loop->remove_column_at(col_);
677 else if (item_)
678 item_->erase();
679}
680
681inline Loop* Column::get_loop() const {
682 return item_ && item_->type == ItemType::Loop ? &item_->loop : nullptr;
683}
684inline Column::iterator Column::begin() {
685 if (Loop* loop = get_loop())
686 return iterator({loop->values.data(), col_, (unsigned) loop->width()});
687 if (item_ && item_->type == ItemType::Pair)
688 return iterator({&item_->pair[1], 0, 1});
689 return iterator();
690}
691
692inline Column::iterator Column::end() {
693 if (Loop* loop = get_loop())
694 return iterator({loop->values.data() + loop->values.size(),
695 col_, (unsigned) loop->width()});
696 if (item_ && item_->type == ItemType::Pair)
697 return iterator({&item_->pair[1] + 1, 0, 1});
698 return iterator();
699}
700
701inline std::string& Column::operator[](int n) {
702 if (Loop* loop = get_loop())
703 return loop->values[n * loop->width() + col_];
704 return item_->pair[1];
705}
706
707inline std::string& Table::Row::operator[](size_t n) {
708 int pos = tab.positions[n];
709 if (Loop* loop = tab.get_loop()) {
710 if (row_index == -1) // tags
711 return loop->tags[pos];
712 return loop->values[loop->width() * row_index + pos];
713 }
714 return tab.bloc.items[pos].pair[row_index == -1 ? 0 : 1];
715}
716
717inline std::string& Table::Row::value_at_unsafe(int pos) {
718 Loop* loop = tab.get_loop();
719 if (row_index == -1) { // tags
720 if (loop)
721 return loop->tags.at(pos);
722 return tab.bloc.items[pos].pair[0];
723 }
724 if (loop)
725 return loop->values.at(loop->width() * row_index + pos);
726 return tab.bloc.items[pos].pair[1];
727}
728
729inline Loop* Table::get_loop() { // NOLINT(readability-make-member-function-const)
730 return loop_item ? &loop_item->loop : nullptr;
731}
732
733inline size_t Table::length() const {
734 return loop_item ? loop_item->loop.length() : (positions.empty() ? 0 : 1);
735}
736
737inline Table::Row Table::find_row(const std::string& s) {
738 int pos = positions.at(0);
739 if (const Loop* loop = get_loop()) {
740 for (size_t i = 0; i < loop->values.size(); i += loop->width())
741 if (as_string(loop->values[i + pos]) == s)
742 return Row{*this, static_cast<int>(i / loop->width())};
743 } else if (as_string(bloc.items[pos].pair[1]) == s) {
744 return Row{*this, 0};
745 }
746 fail("Not found in " + *column_at_pos(pos).get_tag() + ": " + s);
747}
748
749template <typename T> void Table::append_row(const T& new_values) {
750 if (!ok())
751 fail("append_row(): table not found");
752 if (new_values.size() != width())
753 fail("append_row(): wrong row length");
754 if (!loop_item)
755 fail("append_row(): data is not in loop, call ensure_loop() first");
756 Loop& loop = loop_item->loop;
757 size_t cur_size = loop.values.size();
758 loop.values.resize(cur_size + loop.width(), ".");
759 int n = 0;
760 for (const auto& value : new_values)
761 loop.values[cur_size + positions[n++]] = value;
762}
763
764inline void Table::remove_rows(int start, int end) {
765 if (!ok())
766 // this function is used mostly through remove_row()
767 fail("remove_row(): table not found");
768 ensure_loop(); // should we fail instead if we have pairs?
769 Loop& loop = loop_item->loop;
770 size_t start_pos = start * loop.width();
771 size_t end_pos = end * loop.width();
772 if (start_pos >= end_pos || end_pos > loop.values.size())
773 throw std::out_of_range("remove_row(): invalid index");
774 loop.values.erase(loop.values.begin() + start_pos,
775 loop.values.begin() + end_pos);
776}
777
778inline Column Table::column_at_pos(int pos) {
779 if (loop_item)
780 return Column(loop_item, pos);
781 return Column(&bloc.items[pos], 0);
782}
783
784inline void Table::erase() {
785 if (loop_item) {
786 loop_item->erase();
787 loop_item = nullptr;
788 } else {
789 for (int pos : positions)
790 if (pos >= 0)
791 bloc.items[pos].erase();
792 }
793 positions.clear();
794}
795
796inline void Table::ensure_loop() {
797 if (loop_item)
798 return;
800 new_item.loop.tags.resize(positions.size());
801 new_item.loop.values.resize(positions.size());
802 loop_item = &bloc.items.at(positions[0]);
803 for (size_t i = 0; i != positions.size(); ++i) {
804 Item& item = bloc.items[positions[i]];
805 new_item.loop.tags[i].swap(item.pair[0]);
806 new_item.loop.values[i].swap(item.pair[1]);
807 item.erase();
808 positions[i] = i;
809 }
810 loop_item->set_value(std::move(new_item));
811}
812
813inline Block::Block(const std::string& name_) : name(name_) {}
814inline Block::Block() {}
815
816inline const Item* Block::find_pair_item(const std::string& tag) const {
817 std::string lctag = gemmi::to_lower(tag);
818 for (const Item& i : items)
819 if (i.type == ItemType::Pair && gemmi::iequal(i.pair[0], lctag))
820 return &i;
821 return nullptr;
822}
823
824inline const Pair* Block::find_pair(const std::string& tag) const {
825 const Item* item = find_pair_item(tag);
826 return item ? &item->pair : nullptr;
827}
828
829inline Column Block::find_loop(const std::string& tag) {
830 Column c = find_values(tag);
831 return c.item() && c.item()->type == ItemType::Loop ? c : Column();
832}
833
834inline const Item* Block::find_loop_item(const std::string& tag) const {
835 for (const Item& i : items)
836 if (i.type == ItemType::Loop && i.loop.find_tag_lc(tag) != -1)
837 return &i;
838 return nullptr;
839}
840
841inline const std::string* Block::find_value(const std::string& tag) const {
842 std::string lctag = gemmi::to_lower(tag);
843 for (const Item& i : items)
844 if (i.type == ItemType::Pair && gemmi::iequal(i.pair[0], lctag))
845 return &i.pair[1];
846 for (const Item& i : items)
847 if (i.type == ItemType::Loop) {
848 int pos = i.loop.find_tag_lc(lctag);
849 if (pos != -1 && i.loop.tags.size() == i.loop.values.size())
850 return &i.loop.values[pos];
851 }
852 return nullptr;
853}
854
855inline Column Block::find_values(const std::string& tag) {
856 std::string lctag = gemmi::to_lower(tag);
857 for (Item& i : items)
858 if (i.type == ItemType::Loop) {
859 int pos = i.loop.find_tag_lc(lctag);
860 if (pos != -1)
861 return Column{&i, static_cast<size_t>(pos)};
862 } else if (i.type == ItemType::Pair) {
863 if (gemmi::iequal(i.pair[0], lctag))
864 return Column{&i, 0};
865 }
866 return Column{nullptr, 0};
867}
868
869inline Block* Block::find_frame(std::string frame_name) {
871 for (Item& i : items)
872 if (i.type == ItemType::Frame && gemmi::iequal(i.frame.name, frame_name))
873 return &i.frame;
874 return nullptr;
875}
876
878 if (item.type != ItemType::Loop)
879 fail("item_as_table: item is not Loop");
880 std::vector<int> indices(item.loop.tags.size());
881 for (size_t j = 0; j != indices.size(); ++j)
882 indices[j] = (int) j;
883 return Table{&item, *this, indices, 0};
884}
885
886inline size_t Block::get_index(const std::string& tag) const {
887 std::string lctag = gemmi::to_lower(tag);
888 for (size_t i = 0; i != items.size(); ++i) {
889 const Item& item = items[i];
890 if ((item.type == ItemType::Pair && gemmi::iequal(item.pair[0], lctag)) ||
891 (item.type == ItemType::Loop && item.loop.find_tag_lc(lctag) != -1))
892 return i;
893 }
894 fail(tag + " not found in block");
895}
896
897inline void Block::set_pair(const std::string& tag, const std::string& value) {
898 ItemSpan(items).set_pair(tag, value);
899}
900
901inline void Block::move_item(int old_pos, int new_pos) {
902 if (old_pos < 0)
903 old_pos += items.size();
904 if ((size_t) old_pos >= items.size())
905 fail("move_item: old_pos out of range");
906 if (new_pos < 0)
907 new_pos += items.size();
908 if ((size_t) new_pos >= items.size())
909 fail("move_item: new_pos out of range");
910 auto src = items.begin() + old_pos;
911 auto dst = items.begin() + new_pos;
912 if (src < dst)
913 std::rotate(src, src+1, dst+1);
914 else
915 std::rotate(dst, src, src+1);
916}
917
918inline std::vector<std::string> Block::get_mmcif_category_names() const {
919 std::vector<std::string> cats;
920 for (const Item& item : items) {
921 const std::string* tag = nullptr;
922 if (item.type == ItemType::Pair)
923 tag = &item.pair[0];
924 else if (item.type == ItemType::Loop && !item.loop.tags.empty())
925 tag = &item.loop.tags[0];
926 if (tag)
927 for (auto j = cats.rbegin(); j != cats.rend(); ++j)
928 if (gemmi::starts_with(*tag, *j)) {
929 tag = nullptr;
930 break;
931 }
932 if (tag) {
933 size_t dot = tag->find('.');
934 if (dot != std::string::npos)
935 cats.emplace_back(*tag, 0, dot + 1);
936 }
937 }
938 return cats;
939}
940
941inline Item& Block::setup_loop_item(Table&& tab, const std::string& prefix,
942 std::vector<std::string>&& tags) {
943 Item *item;
944 if (tab.loop_item) {
945 item = tab.loop_item;
946 item->loop.clear();
947 } else if (tab.ok()) {
948 item = &tab.bloc.items.at(tab.positions[0]);
949 tab.erase();
950 item->set_value(Item(LoopArg{}));
951 } else {
952 items.emplace_back(LoopArg{});
953 item = &items.back();
954 }
955 for (std::string& tag : tags) {
956 tag.insert(0, prefix);
957 assert_tag(tag);
958 }
959 item->loop.tags = std::move(tags);
960 return *item;
961}
962
963inline Loop& Block::setup_loop(Table&& tab, const std::string& prefix,
964 std::vector<std::string>&& tags) {
965 return setup_loop_item(std::move(tab), prefix, std::move(tags)).loop;
966}
967
968inline Table Block::find(const std::string& prefix,
969 const std::vector<std::string>& tags) {
970 Item* loop_item = nullptr;
971 if (!tags.empty()) {
972 if (tags[0][0] == '?')
973 fail("The first tag in find() cannot be ?optional.");
974 loop_item = find_loop(prefix + tags[0]).item();
975 }
976
977 std::vector<int> indices;
978 indices.reserve(tags.size());
979 if (loop_item) {
980 for (const std::string& tag : tags) {
981 std::string full_tag = prefix + (tag[0] != '?' ? tag : tag.substr(1));
982 int idx = loop_item->loop.find_tag(full_tag);
983 if (idx == -1 && tag[0] != '?') {
984 loop_item = nullptr;
985 indices.clear();
986 break;
987 }
988 indices.push_back(idx);
989 }
990 } else {
991 for (const std::string& tag : tags) {
992 std::string full_tag = prefix + (tag[0] != '?' ? tag : tag.substr(1));
993 if (const Item* p = find_pair_item(full_tag)) {
994 indices.push_back(p - items.data());
995 } else if (tag[0] == '?') {
996 indices.push_back(-1);
997 } else {
998 indices.clear();
999 break;
1000 }
1001 }
1002 }
1003 return Table{loop_item, *this, indices, prefix.length()};
1004}
1005
1006inline Table Block::find_any(const std::string& prefix,
1007 const std::vector<std::string>& tags) {
1008 std::vector<int> indices;
1009 for (auto tag = tags.begin(); tag != tags.end(); ++tag) {
1010 Column column = find_values(prefix + *tag);
1011 if (Item* item = column.item()) {
1012 if (item->type == ItemType::Loop) {
1013 indices.push_back(column.col());
1014 while (++tag != tags.end()) {
1015 int idx = item->loop.find_tag(prefix + *tag);
1016 if (idx != -1)
1017 indices.push_back(idx);
1018 }
1019 return Table{item, *this, indices, prefix.length()};
1020 }
1021 indices.push_back(item - items.data());
1022 while (++tag != tags.end())
1023 if (const Item* p = find_pair_item(prefix + *tag))
1024 indices.push_back(p - items.data());
1025 break;
1026 }
1027 }
1028 return Table{nullptr, *this, indices, prefix.length()};
1029}
1030
1034 std::vector<int> indices;
1035 for (Item& i : items)
1036 if (i.has_prefix(cat)) {
1037 if (i.type == ItemType::Loop) {
1038 indices.resize(i.loop.tags.size());
1039 for (size_t j = 0; j != indices.size(); ++j) {
1040 indices[j] = j;
1041 const std::string& tag = i.loop.tags[j];
1042 if (!istarts_with(tag, cat))
1043 fail("Tag ", tag, " in loop with ", cat);
1044 }
1045 return Table{&i, *this, indices, cat.length()};
1046 }
1047 indices.push_back(&i - items.data());
1048 }
1049 return Table{nullptr, *this, indices, cat.length()};
1050}
1051
1052inline bool Block::has_mmcif_category(std::string cat) const {
1055 for (const Item& i : items)
1056 if (i.has_prefix(cat))
1057 return true;
1058 return false;
1059}
1060
1061struct Document {
1062 std::string source;
1063 std::vector<Block> blocks;
1064
1065 // implementation detail: items of the currently parsed block or frame
1066 std::vector<Item>* items_ = nullptr;
1067
1068 Block& add_new_block(const std::string& name, int pos=-1) {
1069 if (find_block(name))
1070 fail("Block with such name already exists: " + name);
1071 if (pos > 0 && static_cast<size_t>(pos) > blocks.size())
1072 throw std::out_of_range("add_new_block(): invalid position");
1073 return *blocks.emplace(pos < 0 ? blocks.end() : blocks.begin() + pos, name);
1074 }
1075
1077 source.clear();
1078 blocks.clear();
1079 items_ = nullptr;
1080 }
1081
1082 // returns blocks[0] if the document has exactly one block (like mmCIF)
1084 if (blocks.size() > 1)
1085 fail("single data block expected, got " + std::to_string(blocks.size()));
1086 return blocks.at(0);
1087 }
1088 const Block& sole_block() const {
1089 return const_cast<Document*>(this)->sole_block();
1090 }
1091
1092 Block* find_block(const std::string& name) {
1093 for (Block& b : blocks)
1094 if (b.name == name)
1095 return &b;
1096 return nullptr;
1097 }
1098 const Block* find_block(const std::string& name) const {
1099 return const_cast<Document*>(this)->find_block(name);
1100 }
1101};
1102
1103
1104[[noreturn]]
1105inline void cif_fail(const std::string& source, const Block& b,
1106 const Item& item, const std::string& s) {
1107 fail(cat(source, ':', item.line_number, " in data_", b.name, ": ", s));
1108}
1109
1111 const std::string& source) {
1112 for (const Item& item : block.items) {
1113 if (item.type == ItemType::Pair) {
1114 if (item.pair[1].empty())
1115 cif_fail(source, block, item, item.pair[0] + " has no value");
1116 } else if (item.type == ItemType::Frame) {
1118 }
1119 }
1120}
1121
1122// Throw an error if any item (pair) value is missing
1124 for (const Block& block : d.blocks)
1125 check_for_missing_values_in_block(block, d.source);
1126}
1127
1128// Throw an error if any block name, frame name or tag is duplicated.
1129inline void check_for_duplicates(const Document& d) {
1130 // check for duplicate block names (except empty "" which is global_)
1131 std::unordered_set<std::string> names;
1132 for (const Block& block : d.blocks) {
1133 bool ok = names.insert(gemmi::to_lower(block.name)).second;
1134 if (!ok && !block.name.empty())
1135 fail(d.source + ": duplicate block name: ", block.name);
1136 }
1137 // check for dups inside each block
1138 std::unordered_set<std::string> frame_names;
1139 for (const Block& block : d.blocks) {
1140 names.clear();
1141 frame_names.clear();
1142 for (const Item& item : block.items) {
1143 if (item.type == ItemType::Pair) {
1144 bool ok = names.insert(gemmi::to_lower(item.pair[0])).second;
1145 if (!ok)
1146 cif_fail(d.source, block, item, "duplicate tag " + item.pair[0]);
1147 } else if (item.type == ItemType::Loop) {
1148 for (const std::string& t : item.loop.tags) {
1149 bool ok = names.insert(gemmi::to_lower(t)).second;
1150 if (!ok)
1151 cif_fail(d.source, block, item, "duplicate tag " + t);
1152 }
1153 } else if (item.type == ItemType::Frame) {
1154 bool ok = frame_names.insert(gemmi::to_lower(item.frame.name)).second;
1155 if (!ok)
1156 cif_fail(d.source, block, item, "duplicate save_" + item.frame.name);
1157 }
1158 }
1159 }
1160}
1161
1162// Empty loop is not a valid CIF syntax, but we parse it to accommodate
1163// some broken CIF files. Only check_level>=2 shows an error.
1164inline void check_empty_loops(const cif::Block& block, const std::string& source) {
1165 for (const cif::Item& item : block.items) {
1166 if (item.type == cif::ItemType::Loop) {
1167 if (item.loop.values.empty() && !item.loop.tags.empty())
1168 cif_fail(source, block, item, "empty loop with " + item.loop.tags[0]);
1169 } else if (item.type == cif::ItemType::Frame) {
1170 check_empty_loops(item.frame, source);
1171 }
1172 }
1173}
1174
1175inline bool is_text_field(const std::string& val) {
1176 size_t len = val.size();
1177 return len > 2 && val[0] == ';' && (val[len-2] == '\n' || val[len-2] == '\r');
1178}
1179
1180inline std::string quote(std::string v) {
1181 if (std::all_of(v.begin(), v.end(), [](char c) { return char_table(c) == 1; })
1182 && !v.empty() && !is_null(v))
1183 return v;
1184 char q = ';';
1185 if (std::memchr(v.c_str(), '\n', v.size()) == nullptr) {
1186 if (std::memchr(v.c_str(), '\'', v.size()) == nullptr)
1187 q = '\'';
1188 else if (std::memchr(v.c_str(), '"', v.size()) == nullptr)
1189 q = '"';
1190 }
1191 v.insert(v.begin(), q);
1192 if (q == ';')
1193 v += '\n';
1194 v += q;
1195 return v;
1196}
1197
1198#if defined(_MSC_VER)
1199#pragma warning(pop)
1200#endif
1201
1202} // namespace cif
1203} // namespace gemmi
1204#endif
Locale-independent functions that convert strings to integers, equivalents of standard isspace and is...
std::string * get_tag()
Definition cifdoc.hpp:666
std::string & operator[](int n)
Definition cifdoc.hpp:701
const_iterator end() const
Definition cifdoc.hpp:248
Loop * get_loop() const
Definition cifdoc.hpp:681
size_t col() const
Definition cifdoc.hpp:277
Column(Item *item, size_t col)
Definition cifdoc.hpp:242
int length() const
Definition cifdoc.hpp:255
std::string str(int n) const
Definition cifdoc.hpp:274
const std::string & at(int n) const
Definition cifdoc.hpp:270
iterator begin()
Definition cifdoc.hpp:684
const std::string * get_tag() const
Definition cifdoc.hpp:252
std::string & at(int n)
Definition cifdoc.hpp:262
const_iterator begin() const
Definition cifdoc.hpp:247
const Item * item() const
Definition cifdoc.hpp:275
iterator end()
Definition cifdoc.hpp:692
fail(), unreachable() and __declspec/__attribute__ macros
Bidirectional iterators (over elements of any container) that can filter, uniquify,...
char as_char(const std::string &value, char null)
Definition cifdoc.hpp:96
void check_for_missing_values(const Document &d)
Definition cifdoc.hpp:1123
void assert_tag(const std::string &tag)
Definition cifdoc.hpp:64
int as_any(const std::string &s, int null)
Definition cifdoc.hpp:116
std::string as_string(const std::string &value)
Definition cifdoc.hpp:80
void check_for_missing_values_in_block(const Block &block, const std::string &source)
Definition cifdoc.hpp:1110
std::array< std::string, 2 > Pair
Definition cifdoc.hpp:120
bool is_null(const std::string &value)
Definition cifdoc.hpp:76
std::string quote(std::string v)
Definition cifdoc.hpp:1180
bool is_text_field(const std::string &val)
Definition cifdoc.hpp:1175
void check_empty_loops(const cif::Block &block, const std::string &source)
Definition cifdoc.hpp:1164
void check_for_duplicates(const Document &d)
Definition cifdoc.hpp:1129
void ensure_mmcif_category(std::string &cat)
Definition cifdoc.hpp:69
void cif_fail(const std::string &source, const Block &b, const Item &item, const std::string &s)
Definition cifdoc.hpp:1105
int as_int(const std::string &str)
Definition cifdoc.hpp:107
uint8_t char_table(char c)
Definition cifdoc.hpp:44
int string_to_int(const char *p, bool checked, size_t length=0)
Definition atox.hpp:73
bool iequal_from(const std::string &str, size_t offset, const std::string &low)
Definition util.hpp:84
bool isame(char a, char b)
Definition util.hpp:78
bool istarts_with(const std::string &str, const std::string &prefix)
Definition util.hpp:94
std::string to_lower(std::string str)
Definition util.hpp:63
void vector_insert_columns(std::vector< T > &data, size_t old_width, size_t length, size_t n, size_t pos, const T &new_value)
Definition util.hpp:274
std::string cat(Args const &... args)
Definition util.hpp:33
bool starts_with(const std::string &str, const std::string &prefix)
Definition util.hpp:39
void fail(const std::string &msg)
Definition fail.hpp:59
void vector_remove_column(std::vector< T > &data, size_t new_width, size_t pos)
Definition util.hpp:293
bool iequal(const std::string &str, const std::string &low)
Definition util.hpp:90
const Item * find_pair_item(const std::string &tag) const
Definition cifdoc.hpp:816
void move_item(int old_pos, int new_pos)
Definition cifdoc.hpp:901
const Item * find_loop_item(const std::string &tag) const
Definition cifdoc.hpp:834
Table find(const std::vector< std::string > &tags)
Definition cifdoc.hpp:473
Column find_values(const std::string &tag)
Definition cifdoc.hpp:855
Column find_loop(const std::string &tag)
Definition cifdoc.hpp:829
Table find_any(const std::string &prefix, const std::vector< std::string > &tags)
Definition cifdoc.hpp:1006
Table find_or_add(const std::string &prefix, std::vector< std::string > tags)
Definition cifdoc.hpp:476
Table find(const std::string &prefix, const std::vector< std::string > &tags)
Definition cifdoc.hpp:968
std::vector< Item > items
Definition cifdoc.hpp:451
void set_pair(const std::string &tag, const std::string &value)
Definition cifdoc.hpp:897
Block * find_frame(std::string name)
Definition cifdoc.hpp:869
Table find_mmcif_category(std::string cat)
Definition cifdoc.hpp:1031
Loop & init_loop(const std::string &prefix, std::vector< std::string > tags)
Definition cifdoc.hpp:494
std::string name
Definition cifdoc.hpp:450
Loop & init_mmcif_loop(std::string cat, std::vector< std::string > tags)
Definition cifdoc.hpp:506
bool has_mmcif_category(std::string cat) const
Definition cifdoc.hpp:1052
void swap(Block &o) noexcept
Definition cifdoc.hpp:456
const std::string * find_value(const std::string &tag) const
Definition cifdoc.hpp:841
bool has_any_value(const std::string &tag) const
Definition cifdoc.hpp:467
Table item_as_table(Item &item)
Definition cifdoc.hpp:877
std::vector< std::string > get_mmcif_category_names() const
Definition cifdoc.hpp:918
const Pair * find_pair(const std::string &tag) const
Definition cifdoc.hpp:824
size_t get_index(const std::string &tag) const
Definition cifdoc.hpp:886
bool has_tag(const std::string &tag) const
Definition cifdoc.hpp:464
void clear() noexcept
Definition cifdoc.hpp:1076
const Block & sole_block() const
Definition cifdoc.hpp:1088
const Block * find_block(const std::string &name) const
Definition cifdoc.hpp:1098
Block & add_new_block(const std::string &name, int pos=-1)
Definition cifdoc.hpp:1068
std::string source
Definition cifdoc.hpp:1062
std::vector< Block > blocks
Definition cifdoc.hpp:1063
Block * find_block(const std::string &name)
Definition cifdoc.hpp:1092
std::string str
Definition cifdoc.hpp:124
ItemSpan(std::vector< Item > &items, std::string prefix)
Definition cifdoc.hpp:616
ItemSpan(std::vector< Item > &items)
Definition cifdoc.hpp:614
void set_pair(const std::string &tag, const std::string &value)
Definition cifdoc.hpp:626
Item(const Item &o)
Definition cifdoc.hpp:544
Item(Item &&o) noexcept
Definition cifdoc.hpp:540
Item(std::string &&t)
Definition cifdoc.hpp:531
Item(FrameArg &&frame_arg)
Definition cifdoc.hpp:535
Item(const std::string &t, const std::string &v)
Definition cifdoc.hpp:533
Item(CommentArg &&comment)
Definition cifdoc.hpp:537
void set_value(Item &&o)
Definition cifdoc.hpp:565
ItemType type
Definition cifdoc.hpp:520
bool has_prefix(const std::string &prefix) const
Definition cifdoc.hpp:559
Item & operator=(Item o)
Definition cifdoc.hpp:549
Item(LoopArg)
Definition cifdoc.hpp:529
void add_comment_and_row(std::initializer_list< std::string > ss)
Definition cifdoc.hpp:169
void remove_column(const std::string &column_name)
Definition cifdoc.hpp:205
void remove_column_at(size_t n)
Definition cifdoc.hpp:213
const std::string & val(size_t row, size_t col) const
Definition cifdoc.hpp:145
size_t width() const
Definition cifdoc.hpp:141
int find_tag(const std::string &tag) const
Definition cifdoc.hpp:137
std::vector< std::string > tags
Definition cifdoc.hpp:128
std::vector< std::string > values
Definition cifdoc.hpp:129
size_t length() const
Definition cifdoc.hpp:142
void add_values(std::initializer_list< std::string > new_values, int pos=-1)
Definition cifdoc.hpp:157
void add_values(T new_values, int pos=-1)
Definition cifdoc.hpp:151
std::string & val(size_t row, size_t col)
Definition cifdoc.hpp:144
void add_columns(const std::vector< std::string > &column_names, const std::string &value, int pos=-1)
Definition cifdoc.hpp:194
void set_all_values(std::vector< std::vector< std::string > > columns)
Definition cifdoc.hpp:650
void add_row(T new_values, int pos=-1)
Definition cifdoc.hpp:160
std::string common_prefix() const
Definition cifdoc.hpp:220
void move_row(int old_pos, int new_pos)
Definition cifdoc.hpp:183
bool has_tag(const std::string &tag) const
Definition cifdoc.hpp:140
int find_tag_lc(const std::string &lctag) const
Definition cifdoc.hpp:132
void add_row(std::initializer_list< std::string > new_values, int pos=-1)
Definition cifdoc.hpp:165
std::string & at(int n)
Definition cifdoc.hpp:310
const std::string & operator[](size_t n) const
Definition cifdoc.hpp:316
std::string * ptr_at(int n)
Definition cifdoc.hpp:320
IndirectIter< const Row, const std::string > const_iterator
Definition cifdoc.hpp:345
std::string str(int n) const
Definition cifdoc.hpp:342
const std::string & one_of(size_t n1, size_t n2) const
Definition cifdoc.hpp:331
bool has2(size_t n) const
Definition cifdoc.hpp:329
IndirectIter< Row, std::string > iterator
Definition cifdoc.hpp:344
const std::string * ptr_at(int n) const
Definition cifdoc.hpp:324
std::string & operator[](size_t n)
Definition cifdoc.hpp:707
std::string & value_at(int pos)
Definition cifdoc.hpp:301
const_iterator end() const
Definition cifdoc.hpp:351
size_t size() const
Definition cifdoc.hpp:340
const_iterator begin() const
Definition cifdoc.hpp:348
const std::string & at(int n) const
Definition cifdoc.hpp:313
bool has(size_t n) const
Definition cifdoc.hpp:328
std::string & value_at_unsafe(int pos)
Definition cifdoc.hpp:717
const std::string & value_at(int pos) const
Definition cifdoc.hpp:306
bool operator!=(const iterator &o) const
Definition cifdoc.hpp:441
bool operator==(const iterator &o) const
Definition cifdoc.hpp:440
const std::string & get(int n) const
Definition cifdoc.hpp:443
void ensure_loop()
if it's pairs, convert it to loop
Definition cifdoc.hpp:796
void at_check(int &n) const
Definition cifdoc.hpp:365
size_t prefix_length
Definition cifdoc.hpp:294
void move_row(int old_pos, int new_pos)
Definition cifdoc.hpp:406
size_t size() const
Definition cifdoc.hpp:360
std::vector< int > positions
Definition cifdoc.hpp:293
size_t width() const
Definition cifdoc.hpp:358
bool has_column(int n) const
Definition cifdoc.hpp:361
std::string get_prefix() const
Definition cifdoc.hpp:382
int find_column_position(const std::string &tag) const
Definition cifdoc.hpp:414
Row find_row(const std::string &s)
Definition cifdoc.hpp:737
size_t length() const
Definition cifdoc.hpp:733
bool ok() const
Definition cifdoc.hpp:357
void append_row(const T &new_values)
Definition cifdoc.hpp:749
Column find_column(const std::string &tag)
Definition cifdoc.hpp:426
iterator end()
Definition cifdoc.hpp:446
iterator begin()
Definition cifdoc.hpp:445
Row at(int n)
Definition cifdoc.hpp:371
void append_row(std::initializer_list< std::string > new_values)
Definition cifdoc.hpp:393
void remove_row(int row_index)
Definition cifdoc.hpp:396
Row operator[](int n)
Definition cifdoc.hpp:363
Loop * get_loop()
Definition cifdoc.hpp:729
void remove_rows(int start, int end)
Definition cifdoc.hpp:764
Column column_at_pos(int pos)
Definition cifdoc.hpp:778
Column column(int n)
Definition cifdoc.hpp:399
Utilities. Mostly for working with strings and vectors.