OpenStructure
Loading...
Searching...
No Matches
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>
29
30namespace{
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
68namespace ost { namespace io {
69
70class StarWriterObject;
71class StarWriterValue;
72class StarWriterDataItem;
73class StarWriterLoopDesc;
74class StarWriterLoop;
75typedef boost::shared_ptr<StarWriterObject> StarWriterObjectPtr;
76typedef boost::shared_ptr<StarWriterValue> StarWriterValuePtr;
77typedef boost::shared_ptr<StarWriterDataItem> StarWriterDataItemPtr;
78typedef boost::shared_ptr<StarWriterLoopDesc> StarWriterLoopDescPtr;
79typedef boost::shared_ptr<StarWriterLoop> StarWriterLoopPtr;
80
81
82class DLLEXPORT_OST_IO StarWriterObject {
83public:
84 virtual ~StarWriterObject() { }
85 virtual void ToStream(std::ostream& s) = 0;
86};
87
88
90public:
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_; }
242private:
243 String value_;
244};
245
246
247class DLLEXPORT_OST_IO StarWriterDataItem : public StarWriterObject {
248public:
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_; }
259private:
260 String category_;
261 String attribute_;
262 StarWriterValue value_;
263};
264
265
266class DLLEXPORT_OST_IO StarWriterLoopDesc : public StarWriterObject {
267public:
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_; }
295private:
296 String category_;
297 std::map<String, int> index_map_;
298};
299
300
301class DLLEXPORT_OST_IO StarWriterLoop: public StarWriterObject {
302public:
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
343private:
344 StarWriterLoopDesc desc_;
345 std::vector<StarWriterValue> data_;
346};
347
348
349class DLLEXPORT_OST_IO StarWriter {
350public:
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
359private:
360 std::vector<StarWriterObjectPtr> categories_to_write_;
361};
362
363}} // ns
364
365#endif
const String & GetAttribute() const
const StarWriterValue & GetValue() const
virtual void ToStream(std::ostream &s)
StarWriterDataItem(const String &category, const String &attribute, const StarWriterValue &value)
const String & GetCategory() const
void Write(const String &data_name, std::ostream &stream)
void Push(StarWriterObjectPtr obj)
void Write(const String &data_name, const String &filename)
StarWriterLoopDesc(const String &category)
void Add(const String &attribute)
int GetIndex(const String &attribute) const
virtual void ToStream(std::ostream &s)
const String & GetCategory() const
StarWriterLoop(const StarWriterLoopDesc &desc)
const std::vector< StarWriterValue > & GetData()
void AddData(const std::vector< StarWriterValue > &data)
virtual void ToStream(std::ostream &s)
const StarWriterLoopDesc & GetDesc()
virtual void ToStream(std::ostream &s)=0
const String & GetValue() const
static StarWriterValue FromInt(int int_value)
static StarWriterValue FromFloat(Real float_value, int decimals)
static StarWriterValue FromString(const String &string_value)
static bool HasNewline(const String &v)
static bool NeedsQuotes(const String &v)
#define DLLEXPORT_OST_IO
float Real
Definition base.hh:44
std::string String
Definition base.hh:54
boost::shared_ptr< StarWriterLoop > StarWriterLoopPtr
boost::shared_ptr< StarWriterDataItem > StarWriterDataItemPtr
boost::shared_ptr< StarWriterLoopDesc > StarWriterLoopDescPtr
boost::shared_ptr< StarWriterObject > StarWriterObjectPtr
boost::shared_ptr< StarWriterValue > StarWriterValuePtr
Definition base.dox:1