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