OpenStructure
star_writer.hh
Go to the documentation of this file.
1 //------------------------------------------------------------------------------
2 // This file is part of the OpenStructure project <www.openstructure.org>
3 //
4 // Copyright (C) 2008-2023 by the OpenStructure authors
5 //
6 // This library is free software; you can redistribute it and/or modify it under
7 // the terms of the GNU Lesser General Public License as published by the Free
8 // Software Foundation; either version 3.0 of the License, or (at your option)
9 // any later version.
10 // This library is distributed in the hope that it will be useful, but WITHOUT
11 // ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
12 // FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
13 // details.
14 //
15 // You should have received a copy of the GNU Lesser General Public License
16 // along with this library; if not, write to the Free Software Foundation, Inc.,
17 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18 //------------------------------------------------------------------------------
19 #ifndef OST_IO_STAR_WRITER_HH
20 #define OST_IO_STAR_WRITER_HH
21 
22 #include <map>
23 #include <fstream>
24 #include <boost/iostreams/filtering_stream.hpp>
25 #include <boost/iostreams/filter/gzip.hpp>
26 #include <boost/shared_ptr.hpp>
27 #include <ost/string_ref.hh>
28 #include <ost/io/io_exception.hh>
29 
30 namespace{
31  // float to string with specified number of decimals
32  void fts(Real f, int decimals, String& s) {
33  char data[20];
34  size_t len;
35  switch(decimals){
36  case 0:
37  len = std::snprintf(data, sizeof(data), "%.0f", f);
38  break;
39  case 1:
40  len = std::snprintf(data, sizeof(data), "%.1f", f);
41  break;
42  case 2:
43  len = std::snprintf(data, sizeof(data), "%.2f", f);
44  break;
45  case 3:
46  len = std::snprintf(data, sizeof(data), "%.3f", f);
47  break;
48  case 4:
49  len = std::snprintf(data, sizeof(data), "%.4f", f);
50  break;
51  case 5:
52  len = std::snprintf(data, sizeof(data), "%.5f", f);
53  break;
54  case 6:
55  len = std::snprintf(data, sizeof(data), "%.6f", f);
56  break;
57  default:
58  throw ost::io::IOException("Max decimals in float conversion: 6");
59  }
60 
61  if(len < 0 || len > 20) {
62  throw ost::io::IOException("float conversion failed");
63  }
64  s.assign(data, len);
65  }
66 }
67 
68 namespace ost { namespace io {
69 
70 class StarWriterObject;
71 class StarWriterValue;
72 class StarWriterDataItem;
73 class StarWriterLoopDesc;
74 class StarWriterLoop;
75 typedef boost::shared_ptr<StarWriterObject> StarWriterObjectPtr;
76 typedef boost::shared_ptr<StarWriterValue> StarWriterValuePtr;
77 typedef boost::shared_ptr<StarWriterDataItem> StarWriterDataItemPtr;
78 typedef boost::shared_ptr<StarWriterLoopDesc> StarWriterLoopDescPtr;
79 typedef boost::shared_ptr<StarWriterLoop> StarWriterLoopPtr;
80 
81 
82 class DLLEXPORT_OST_IO StarWriterObject {
83 public:
84  virtual ~StarWriterObject() { }
85  virtual void ToStream(std::ostream& s) = 0;
86 };
87 
88 
90 public:
91 
93 
94  static StarWriterValue FromInt(int int_value) {
95  StarWriterValue value;
96  value.value_ = std::to_string(int_value);
97  return value;
98  }
99  static StarWriterValue FromFloat(Real float_value, int decimals) {
100  StarWriterValue value;
101  fts(float_value, decimals, value.value_);
102  return value;
103  }
104  static StarWriterValue FromString(const String& string_value) {
105  StarWriterValue value;
106 
107  if(string_value == "") {
108  value.value_ = "?";
109  } else {
110  if(StarWriterValue::HasNewline(string_value)) {
111  value.value_ = "\n;" + string_value + "\n;\n";
112  }
113  else if(StarWriterValue::NeedsQuotes(string_value)) {
114  value.value_ = "\"" + string_value + "\"";
115  }
116  else {
117  value.value_ = string_value;
118  }
119  }
120  return value;
121  }
122 
123  static bool NeedsQuotes(const String& v) {
124 
125  // string requires quotes if any of the following is True
126  // information from https://www.iucr.org/resources/cif/spec/version1.1/cifsyntax
127  // * space in string
128  // * any string that starts with any of the following strings
129  // * _
130  // * #
131  // * $
132  // * '
133  // * "
134  // * [
135  // * ]
136  // * ;
137  // * data_ (case insensitive)
138  // * save_ (case insensitive)
139  // * any string that is equal to any of the following reserved words
140  // * loop_ (case insensitive)
141  // * stop_ (case insensitive)
142  // * global_ (case insensitive)
143 
144  // space in string
145  for(char c: v) {
146  if(isspace(c) && c != '\n') {
147  // linebreaks evaluate to true in isspace but do not need quotes
148  // they are handled with semi-colons
149  return true;
150  }
151  }
152 
153  // any string that starts with any of the special single characters
154  switch(v[0]) {
155  case '_': {
156  return true;
157  }
158  case '#': {
159  return true;
160  }
161  case '$': {
162  return true;
163  }
164  case '\'': {
165  return true;
166  }
167  case '\"': {
168  return true;
169  }
170  case '[': {
171  return true;
172  }
173  case ']': {
174  return true;
175  }
176  case ';': {
177  return true;
178  }
179  default: {
180  break;
181  }
182  }
183 
184  // any string that starts with any of the special multi character thingies
185  if(v.size() >= 5 && v[4] == '_') {
186  // need to do case insensitive checking
187  if((v[0] == 'd' || v[0] == 'D') &&
188  (v[1] == 'a' || v[1] == 'A') &&
189  (v[2] == 't' || v[2] == 'T') &&
190  (v[3] == 'a' || v[3] == 'A')) {
191  return true;
192  }
193  if((v[0] == 's' || v[0] == 'S') &&
194  (v[1] == 'a' || v[1] == 'A') &&
195  (v[2] == 'v' || v[2] == 'V') &&
196  (v[3] == 'e' || v[3] == 'E')) {
197  return true;
198  }
199  }
200 
201  // any string that is exactly one of the reserved words
202  if(v.size() == 5 && v[4] == '_') {
203  // need to do case insensitive checking
204  if((v[0] == 'l' || v[0] == 'L') &&
205  (v[1] == 'o' || v[1] == 'O') &&
206  (v[2] == 'o' || v[2] == 'O') &&
207  (v[3] == 'p' || v[3] == 'P')) {
208  return true;
209  }
210  if((v[0] == 's' || v[0] == 'S') &&
211  (v[1] == 't' || v[1] == 'T') &&
212  (v[2] == 'o' || v[2] == 'O') &&
213  (v[3] == 'p' || v[3] == 'P')) {
214  return true;
215  }
216  }
217 
218  if(v.size() == 7 && v[6] == '_') {
219  // need to do case insensitive checking
220  if((v[0] == 'g' || v[0] == 'G') &&
221  (v[1] == 'l' || v[1] == 'L') &&
222  (v[2] == 'o' || v[2] == 'O') &&
223  (v[3] == 'b' || v[3] == 'B') &&
224  (v[4] == 'a' || v[4] == 'A') &&
225  (v[5] == 'l' || v[5] == 'L')) {
226  return true;
227  }
228  }
229  return false;
230  }
231 
232  static bool HasNewline(const String& v) {
233  for(char c: v) {
234  if(c == '\n') {
235  return true;
236  }
237  }
238  return false;
239  }
240 
241  const String& GetValue() const { return value_; }
242 private:
243  String value_;
244 };
245 
246 
247 class DLLEXPORT_OST_IO StarWriterDataItem : public StarWriterObject {
248 public:
249  StarWriterDataItem(const String& category, const String& attribute,
250  const StarWriterValue& value): category_(category),
251  attribute_(attribute),
252  value_(value) { }
253  virtual void ToStream(std::ostream& s) {
254  s << category_ << '.' << attribute_ << ' ' << value_.GetValue() << std::endl;
255  }
256  const String& GetCategory() const { return category_; }
257  const String& GetAttribute() const { return attribute_; }
258  const StarWriterValue& GetValue() const { return value_; }
259 private:
260  String category_;
261  String attribute_;
262  StarWriterValue value_;
263 };
264 
265 
266 class DLLEXPORT_OST_IO StarWriterLoopDesc : public StarWriterObject {
267 public:
268  StarWriterLoopDesc(const String& category): category_(category) { }
269 
270  int GetIndex(const String& attribute) const {
271  std::map<String, int>::const_iterator i=index_map_.find(attribute);
272  return i==index_map_.end() ? -1 : i->second;
273  }
274 
275  void Add(const String& attribute) {
276  index_map_.insert(std::make_pair(attribute, index_map_.size()));
277  }
278 
279  size_t GetSize() const {
280  return index_map_.size();
281  }
282 
283  virtual void ToStream(std::ostream& s) {
284  std::vector<std::pair<int, String> > tmp;
285  for(auto it = index_map_.begin(); it != index_map_.end(); ++it) {
286  tmp.push_back(std::make_pair(it->second, it->first));
287  }
288  std::sort(tmp.begin(), tmp.end());
289  for(auto it = tmp.begin(); it != tmp.end(); ++it) {
290  s << category_ << "." << it->second << std::endl;
291  }
292  }
293 
294  const String& GetCategory() const { return category_; }
295 private:
296  String category_;
297  std::map<String, int> index_map_;
298 };
299 
300 
301 class DLLEXPORT_OST_IO StarWriterLoop: public StarWriterObject {
302 public:
303 
304  StarWriterLoop(const StarWriterLoopDesc& desc): desc_(desc) { }
305 
306  const StarWriterLoopDesc& GetDesc() { return desc_; }
307 
308  void AddData(const std::vector<StarWriterValue>& data) {
309  if(data.size() != desc_.GetSize()) {
310  throw ost::io::IOException("Invalid data size when adding to StarLoop");
311  }
312  data_.insert(data_.end(), data.begin(), data.end());
313  }
314 
315  const std::vector<StarWriterValue>& GetData() { return data_; }
316 
317  int GetN() {
318  return data_.size() / desc_.GetSize();
319  }
320 
321  virtual void ToStream(std::ostream& s) {
322  if(data_.empty()) {
323  return; // skip loop, including header
324  }
325  s << "loop_" << std::endl;
326  desc_.ToStream(s);
327  int desc_size = desc_.GetSize();
328  for(size_t i = 0; i < data_.size(); ++i) {
329  const String& v = data_[i].GetValue();
330  s << v;
331  bool ends_with_newline = v.back() == '\n';
332  if((i+1) % desc_size == 0) {
333  // no need for newline if the last item ended with a newline anyways
334  if(!ends_with_newline) {
335  s << std::endl;
336  }
337  } else {
338  s << ' ';
339  }
340  }
341  }
342 
343 private:
344  StarWriterLoopDesc desc_;
345  std::vector<StarWriterValue> data_;
346 };
347 
348 
349 class DLLEXPORT_OST_IO StarWriter {
350 public:
352  virtual ~StarWriter() { }
353 
354  void Push(StarWriterObjectPtr obj) { categories_to_write_.push_back(obj); }
355 
356  void Write(const String& data_name, const String& filename);
357  void Write(const String& data_name, std::ostream& stream);
358 
359 private:
360  std::vector<StarWriterObjectPtr> categories_to_write_;
361 };
362 
363 }} // ns
364 
365 #endif
const String & GetAttribute() const
Definition: star_writer.hh:257
virtual void ToStream(std::ostream &s)
Definition: star_writer.hh:253
StarWriterDataItem(const String &category, const String &attribute, const StarWriterValue &value)
Definition: star_writer.hh:249
const StarWriterValue & GetValue() const
Definition: star_writer.hh:258
const String & GetCategory() const
Definition: star_writer.hh:256
void Write(const String &data_name, std::ostream &stream)
void Push(StarWriterObjectPtr obj)
Definition: star_writer.hh:354
void Write(const String &data_name, const String &filename)
StarWriterLoopDesc(const String &category)
Definition: star_writer.hh:268
void Add(const String &attribute)
Definition: star_writer.hh:275
int GetIndex(const String &attribute) const
Definition: star_writer.hh:270
virtual void ToStream(std::ostream &s)
Definition: star_writer.hh:283
const String & GetCategory() const
Definition: star_writer.hh:294
StarWriterLoop(const StarWriterLoopDesc &desc)
Definition: star_writer.hh:304
const std::vector< StarWriterValue > & GetData()
Definition: star_writer.hh:315
const StarWriterLoopDesc & GetDesc()
Definition: star_writer.hh:306
void AddData(const std::vector< StarWriterValue > &data)
Definition: star_writer.hh:308
virtual void ToStream(std::ostream &s)
Definition: star_writer.hh:321
virtual void ToStream(std::ostream &s)=0
const String & GetValue() const
Definition: star_writer.hh:241
static StarWriterValue FromInt(int int_value)
Definition: star_writer.hh:94
static StarWriterValue FromFloat(Real float_value, int decimals)
Definition: star_writer.hh:99
static StarWriterValue FromString(const String &string_value)
Definition: star_writer.hh:104
static bool HasNewline(const String &v)
Definition: star_writer.hh:232
static bool NeedsQuotes(const String &v)
Definition: star_writer.hh:123
#define DLLEXPORT_OST_IO
float Real
Definition: base.hh:44
std::string String
Definition: base.hh:54
boost::shared_ptr< StarWriterLoop > StarWriterLoopPtr
Definition: star_writer.hh:79
boost::shared_ptr< StarWriterDataItem > StarWriterDataItemPtr
Definition: star_writer.hh:77
boost::shared_ptr< StarWriterLoopDesc > StarWriterLoopDescPtr
Definition: star_writer.hh:78
boost::shared_ptr< StarWriterObject > StarWriterObjectPtr
Definition: star_writer.hh:74
boost::shared_ptr< StarWriterValue > StarWriterValuePtr
Definition: star_writer.hh:76
Definition: base.dox:1