votca 2026-dev
Loading...
Searching...
No Matches
property.cc
Go to the documentation of this file.
1/*
2 * Copyright 2009-2020 The VOTCA Development Team (http://www.votca.org)
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 *
16 */
17
18// Standard includes
19#include <algorithm>
20#include <cstring>
21#include <fstream>
22#include <iomanip>
23#include <iostream>
24#include <iterator>
25#include <stack>
26#include <stdexcept>
27#include <string>
28
29// Third party includes
30#include <boost/algorithm/string.hpp>
31#include <boost/format.hpp>
32#include <expat.h>
33#include <unistd.h>
34#include <vector>
35
36// Local VOTCA includes
37#include "votca/tools/colors.h"
41
42namespace votca {
43namespace tools {
44using namespace std;
45// ostream modifier defines the output format, level, indentation
46const Index Property::IOindex = std::ios_base::xalloc();
47
48const Property &Property::get(const string &key) const {
49 Tokenizer tok(key, ".");
50 Tokenizer::iterator n = tok.begin();
51 if (n == tok.end()) {
52 return *this;
53 }
54
55 const Property *p;
56 map<string, std::vector<Index>>::const_iterator iter;
57 if (*n == "") {
58 p = this;
59 } else {
60 iter = map_.find(*n);
61 if (iter == map_.end()) {
62 throw std::runtime_error("property not found: " + key);
63 }
64 p = &properties_[iter->second.back()];
65 }
66 ++n;
67 try {
68 for (; n != tok.end(); ++n) {
69 p = &p->get(*n);
70 }
71 } catch (std::runtime_error &) { // catch here to get full key in
72 // exception
73 throw std::runtime_error("property not found: " + key);
74 }
75
76 return *p;
77}
78
79Property &Property::get(const string &key) {
80 return const_cast<Property &>(static_cast<const Property &>(*this).get(key));
81}
82
83Property &Property::set(const std::string &key, const std::string &value) {
84 Property &p = get(key);
85 p.value() = value;
86 return p;
87}
88
89Property &Property::addTree(const std::string &key, const std::string &value) {
90 return addTree(Tokenizer(key, ".").ToVector(), value);
91}
92
93Property &Property::addTree(const std::vector<std::string> &key,
94 const std::string &value) {
95
96 if (key.size() == 1) {
97 return add(key[0], value);
98 } else {
99 std::vector<std::string> subkey(key.begin() + 1, key.end());
100 if (this->exists(key[0])) {
101 return this->get(key[0]).addTree(subkey, value);
102 } else {
103 return this->add(key[0], "").addTree(subkey, value);
104 }
105 }
106}
107
108Property &Property::add(const std::string &key, const std::string &value) {
109 std::string path = path_;
110 if (path != "") {
111 path = path + ".";
112 }
113 properties_.push_back(Property(key, value, path + name_));
114 map_[key].push_back(Index(properties_.size()) - 1);
115 return properties_.back();
116}
117
118bool Property::hasAttribute(const std::string &attribute) const {
119 return attributes_.find(attribute) != attributes_.end();
120}
121
122bool Property::exists(const std::string &key) const {
123 try {
124 get(key);
125 } catch (std::exception &) {
126 return false;
127 }
128 return true;
129}
130
131void FixPath(tools::Property &prop, std::string path) {
132 prop.path() = path;
133 if (path != "") {
134 path += ".";
135 }
136 path += prop.name();
137 for (tools::Property &child : prop) {
138 FixPath(child, path);
139 }
140}
141
143
144 properties_.push_back(other);
145 map_[other.name()].push_back((properties_.size()) - 1);
146
147 std::string path = path_;
148 if (path != "") {
149 path += ".";
150 }
151 path += name_;
152 FixPath(properties_.back(), path);
153 return properties_.back();
154}
155
156Property &Property::getOradd(const std::string &key) {
157 if (exists(key)) {
158 return get(key);
159 } else {
160 return addTree(key, "");
161 }
162}
163
164std::vector<const Property *> Property::Select(const string &filter) const {
165 Tokenizer tok(filter, ".");
166 std::vector<const Property *> selection;
167 if (tok.begin() == tok.end()) {
168 return selection;
169 }
170 selection.push_back(this);
171 for (const auto &n : tok) {
172 std::vector<const Property *> selected;
173 for (const Property *p : selection) {
174 for (const Property &child : *p) {
175 if (wildcmp(n, child.name())) {
176 selected.push_back(&child);
177 }
178 }
179 }
180 selection = selected;
181 }
182 return selection;
183}
184
185std::vector<Property *> Property::Select(const string &filter) {
186 Tokenizer tok(filter, ".");
187 std::vector<Property *> selection;
188 if (tok.begin() == tok.end()) {
189 return selection;
190 }
191 selection.push_back(this);
192 for (const auto &n : tok) {
193 std::vector<Property *> selected;
194 for (Property *p : selection) {
195 for (Property &child : *p) {
196 if (wildcmp(n, child.name())) {
197 selected.push_back(&child);
198 }
199 }
200 }
201 selection = selected;
202 }
203 return selection;
204}
205
206void Property::deleteAttribute(const std::string &attribute) {
207 attributes_.erase(attribute);
208}
209
210static void start_hndl(void *data, const char *el, const char **attr) {
211 stack<Property *> *property_stack =
212 (stack<Property *> *)XML_GetUserData((XML_Parser *)data);
213
214 Property *cur = property_stack->top();
215 Property &np = cur->add(el, "");
216
217 for (Index i = 0; attr[i]; i += 2) {
218 np.setAttribute(attr[i], attr[i + 1]);
219 }
220
221 property_stack->push(&np);
222}
223
224static void end_hndl(void *data, const char *) {
225 stack<Property *> *property_stack =
226 (stack<Property *> *)XML_GetUserData((XML_Parser *)data);
227 property_stack->pop();
228}
229
230void char_hndl(void *data, const char *txt, int txtlen) {
231 stack<Property *> *property_stack =
232 (stack<Property *> *)XML_GetUserData((XML_Parser *)data);
233
234 Property *cur = property_stack->top();
235 cur->value().append(txt, txtlen);
236}
237
238void Property::LoadFromXML(string filename) {
239 ifstream fl;
240 fl.open(filename);
241 if (!fl.is_open()) {
242 throw std::ios_base::failure("Error on open xml file: " + filename);
243 }
244
245 XML_Parser parser = XML_ParserCreate(nullptr);
246 if (!parser) {
247 throw std::runtime_error("Couldn't allocate memory for xml parser");
248 }
249
250 XML_UseParserAsHandlerArg(parser);
251 XML_SetElementHandler(parser, start_hndl, end_hndl);
252 XML_SetCharacterDataHandler(parser, char_hndl);
253
254 stack<Property *> pstack;
255 pstack.push(this);
256
257 XML_SetUserData(parser, (void *)&pstack);
258 while (!fl.eof()) {
259 string line;
260 tools::getline(fl, line);
261 line = line + "\n";
262 if (line.length() > (size_t)std::numeric_limits<int>::max()) {
263 throw std::runtime_error("Property::LoadFromXML: line is too long");
264 }
265 if (!XML_Parse(parser, line.c_str(), (int)line.length(), fl.eof())) {
266 // expat 2.9 deprecates XML_GetCurrentLineNumber for the 64-bit variant
267#if XML_MAJOR_VERSION > 2 || (XML_MAJOR_VERSION == 2 && XML_MINOR_VERSION >= 9)
268 const auto line_number = XML_GetCurrentLineNumber64(parser);
269#else
270 const auto line_number = XML_GetCurrentLineNumber(parser);
271#endif
272 throw std::ios_base::failure(filename + ": Parse error at line " +
273 boost::lexical_cast<string>(line_number) +
274 "\n" +
275 XML_ErrorString(XML_GetErrorCode(parser)));
276 }
277 }
278 fl.close();
279 XML_ParserFree(parser);
280}
281
282void PrintNodeTXT(std::ostream &out, const Property &p, const Index start_level,
283 Index level = 0, string prefix = "", string offset = "") {
284 if ((p.value() != "") || p.HasChildren()) {
285 if (level >= start_level) {
286 if ((p.value()).find_first_not_of("\t\n ") != std::string::npos) {
287 out << offset << prefix << " = " << p.value() << endl;
288 }
289 } else {
290 prefix = "";
291 }
292 }
293
294 for (const Property &prop : p) {
295 level++;
296 if (prefix == "") {
297 PrintNodeTXT(out, prop, start_level, level, prefix + prop.name(), offset);
298 } else {
299 PrintNodeTXT(out, prop, start_level, level, prefix + "." + prop.name(),
300 offset);
301 }
302 level--;
303 }
304}
305
306void PrintNodeXML(std::ostream &out, const Property &p,
307 PropertyIOManipulator *piom, Index level = 0,
308 string offset = "") {
310 bool linebreak = true;
311 bool has_value = false;
312
313 const ColorSchemeBase *color = &DEFAULT_COLORS;
314
315 string indent("");
316 Index start_level(0);
317
318 if (piom) {
319 start_level = piom->getLevel();
320 indent = piom->getIndentation();
321 color = piom->getColorScheme();
322 }
323
324 string cKey = color->Magenta();
325 string cAttribute = color->Blue();
326 string cAttributeValue = color->Green();
327 string cReset = color->Reset();
328
329 // print starting only from the start_level (the first node (level 0) can be
330 // <> </>)
331 if (level >= start_level) {
332 // print the node name
333 out << indent << offset << "<" << cKey << p.name() << cReset;
334 // print the node attributes
335 for (ia = p.firstAttribute(); ia != p.lastAttribute(); ++ia) {
336 out << " " << cAttribute << ia->first << cReset << "=\""
337 << cAttributeValue << ia->second << cReset << "\"";
338 }
339 // print node value if it is not empty
340 has_value = ((p.value()).find_first_not_of("\t\n ") != std::string::npos);
341 if (has_value || p.HasChildren()) {
342 out << ">";
343 } else {
344 out << "/>" << std::endl;
345 }
346 if (has_value) {
347 out << cAttributeValue << p.value() << cReset;
348 linebreak = false;
349 }
350
351 // check if we need the end of the line or not
352 if (!has_value && p.HasChildren()) {
353 out << std::endl;
354 }
355 if (!has_value && !p.HasChildren()) {
356 linebreak = false;
357 }
358 }
359
360 // continue iteratively through the rest of the nodes
361 for (const Property &prop : p) {
362 level++;
363 if (level > start_level) {
364 offset += "\t";
365 }
366 PrintNodeXML(out, prop, piom, level, offset);
367 if (level > start_level) {
368 offset.resize(offset.size() - 1);
369 }
370 level--;
371 }
372
373 if (level >= start_level) {
374 if (linebreak) {
375 out << indent << offset << "</" << cKey << p.name() << cReset << ">"
376 << std::endl;
377 } else if (has_value) {
378 out << "</" << cKey << p.name() << cReset << ">" << std::endl;
379 }
380 }
381}
382
383void PrintNodeHLP(std::ostream &out, const Property &p,
384 const Index start_level = 0, Index level = 0,
385 const string &prefix = "", const string &offset = "") {
386
387 Color<csRGB> RGB; // Using RGB palette
388 string fmt = "t|%1%%|15t|" + string(RGB.Blue()) + "%2%" +
389 string(RGB.Green()) + "%|40t|%3%%|55t|" + string(RGB.Reset()) +
390 "%4%\n";
391
392 Index leveloffset = level;
393 string help("");
394 // if this is the head node, print the header
395 if (level == start_level) {
396 string head_name = string(RGB.Magenta()) + p.name();
397 if (p.hasAttribute("help")) {
398 if (p.hasAttribute("help")) {
399 help = string(RGB.Red()) + p.getAttribute<string>("help");
400 }
401 out << boost::format(" %1%: %|18t| %2%" + string(RGB.Reset()) + "\n") %
402 head_name % help;
403 }
404 leveloffset = 0;
405 out << boost::format("%|3" + fmt) % "OPTION" % "DEFAULT" % "UNIT" %
406 "DESCRIPTION";
407 }
408
409 if (level > start_level) {
410 string ofmt = "%|" + boost::lexical_cast<string>(leveloffset) + fmt;
411 string unit("");
412 if (p.hasAttribute("unit")) {
413 unit = p.getAttribute<string>("unit");
414 }
415 string defaults("");
416 if (p.hasAttribute("default")) {
417 defaults = p.getAttribute<string>("default");
418 }
419 if (p.hasAttribute("help")) {
420 help = p.getAttribute<string>("help");
421 }
422 if (!unit.empty()) {
423 unit = "[" + unit + "]";
424 }
425 if (!defaults.empty()) {
426 defaults = "(" + defaults + ")";
427 }
428
429 string name = p.name();
430
431 out << boost::format(ofmt) % name % defaults % unit % help;
432 }
433
434 for (const Property &pp : p) {
435 level++;
436 if (prefix == "") {
437 PrintNodeHLP(out, pp, start_level, level, pp.name(), offset);
438 } else {
439 PrintNodeHLP(out, pp, start_level, level, prefix + "." + pp.name(),
440 offset);
441 }
442 level--;
443 }
444}
445
446std::ostream &operator<<(std::ostream &out, const Property &p) {
447 if (!out.good()) {
448 return out;
449 }
450
451 std::ostream::sentry sentry(out);
452
453 if (sentry) {
454 // get the property format object attached to the stream
456 (PropertyIOManipulator *)out.pword(int(Property::getIOindex()));
457
458 string indentation("");
459 Index level = 0;
460
462 if (pm) {
463 indentation = pm->getIndentation();
464 level = pm->getLevel();
465 type = pm->getType();
466 // check if we > or >> to a file and remove color codes
467 // if ( out.tellp() != -1 ) - not suitable for pipes
468 if (!isatty(STDOUT_FILENO) || !isatty(STDERR_FILENO)) {
470 }
471 }
472
473 switch (type) {
474 default:
475 PrintNodeTXT(out, p, level);
476 break;
478 PrintNodeXML(out, p, pm);
479 break;
481 PrintNodeTXT(out, p, level, 0, "", indentation);
482 break;
484 PrintNodeHLP(out, p, level, 0, "", indentation);
485 break;
486 }
487 }
488
489 return out;
490}
491} // namespace tools
492} // namespace votca
virtual const char * Green() const =0
virtual const char * Blue() const =0
virtual const char * Reset() const =0
virtual const char * Magenta() const =0
const char * Reset() const
Definition colors.h:96
const char * Red() const
Definition colors.h:98
const char * Magenta() const
Definition colors.h:102
const char * Green() const
Definition colors.h:99
const char * Blue() const
Definition colors.h:101
Manipulates the format state of the output stream.
class to manage program options with xml serialization functionality
Definition property.h:55
Property & add(const std::string &key, const std::string &value)
add a new property to structure
Definition property.cc:108
std::map< std::string, std::vector< Index > > map_
Definition property.h:271
std::map< std::string, std::string >::const_iterator const_AttributeIterator
Definition property.h:234
Property & set(const std::string &key, const std::string &value)
set value of existing property
Definition property.cc:83
std::string & path()
full path of property (including parents)
Definition property.h:167
Property & get(const std::string &key)
get existing property
Definition property.cc:79
void deleteAttribute(const std::string &attribute)
deletes an attribute
Definition property.cc:206
std::string & value()
reference to value of property
Definition property.h:153
bool exists(const std::string &key) const
check whether property exists
Definition property.cc:122
bool hasAttribute(const std::string &attribute) const
return true if an attribute exists
Definition property.cc:118
static Index getIOindex()
Definition property.h:268
Property & getOradd(const std::string &key)
adds new or gets existing property
Definition property.cc:156
T getAttribute(const std::string &attribute) const
return attribute as type
Definition property.h:319
std::map< std::string, std::string > attributes_
Definition property.h:272
bool HasChildren() const
does the property have children?
Definition property.h:182
std::string & name()
name of property
Definition property.h:159
Property & addTree(const std::string &key, const std::string &value)
add a new property tree to structure
Definition property.cc:89
std::vector< Property > properties_
Definition property.h:273
AttributeIterator firstAttribute()
returns an iterator to the first attribute
Definition property.h:247
std::vector< Property * > Select(const std::string &filter)
select property based on a filter
Definition property.cc:185
std::vector< Property >::const_iterator const_iterator
Definition property.h:186
void setAttribute(const std::string &attribute, const T &value)
set an attribute
Definition property.h:325
AttributeIterator lastAttribute()
returns an iterator to the last attribute
Definition property.h:252
void LoadFromXML(std::string filename)
Definition property.cc:238
static const Index IOindex
Definition property.h:279
break string into words
Definition tokenizer.h:72
iterator end()
end iterator
Definition tokenizer.h:102
iterator begin()
iterator to first element
Definition tokenizer.h:97
boost::tokenizer< boost::char_separator< char > >::iterator iterator
Definition tokenizer.h:74
void PrintNodeXML(std::ostream &out, const Property &p, PropertyIOManipulator *piom, Index level=0, string offset="")
Definition property.cc:306
void char_hndl(void *data, const char *txt, int txtlen)
Definition property.cc:230
static void end_hndl(void *data, const char *)
Definition property.cc:224
int wildcmp(const char *wild, const char *string)
Definition tokenizer.cc:28
void PrintNodeTXT(std::ostream &out, const Property &p, const Index start_level, Index level=0, string prefix="", string offset="")
Definition property.cc:282
void PrintNodeHLP(std::ostream &out, const Property &p, const Index start_level=0, Index level=0, const string &prefix="", const string &offset="")
Definition property.cc:383
Color< csDefault > DEFAULT_COLORS
Definition colors.cc:24
std::ostream & operator<<(std::ostream &out, const Correlate &c)
Definition correlate.h:53
static void start_hndl(void *data, const char *el, const char **attr)
Definition property.cc:210
void FixPath(tools::Property &prop, std::string path)
Definition property.cc:131
std::istream & getline(std::istream &is, std::string &str)
Wrapper for a getline function.
Definition getline.h:35
Provides a means for comparing floating point numbers.
Definition basebead.h:33
Eigen::Index Index
Definition types.h:26