votca 2024-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 throw std::ios_base::failure(
267 filename + ": Parse error at line " +
268 boost::lexical_cast<string>(XML_GetCurrentLineNumber(parser)) + "\n" +
269 XML_ErrorString(XML_GetErrorCode(parser)));
270 }
271 }
272 fl.close();
273 XML_ParserFree(parser);
274}
275
276void PrintNodeTXT(std::ostream &out, const Property &p, const Index start_level,
277 Index level = 0, string prefix = "", string offset = "") {
278 if ((p.value() != "") || p.HasChildren()) {
279 if (level >= start_level) {
280 if ((p.value()).find_first_not_of("\t\n ") != std::string::npos) {
281 out << offset << prefix << " = " << p.value() << endl;
282 }
283 } else {
284 prefix = "";
285 }
286 }
287
288 for (const Property &prop : p) {
289 level++;
290 if (prefix == "") {
291 PrintNodeTXT(out, prop, start_level, level, prefix + prop.name(), offset);
292 } else {
293 PrintNodeTXT(out, prop, start_level, level, prefix + "." + prop.name(),
294 offset);
295 }
296 level--;
297 }
298}
299
300void PrintNodeXML(std::ostream &out, const Property &p,
301 PropertyIOManipulator *piom, Index level = 0,
302 string offset = "") {
304 bool linebreak = true;
305 bool has_value = false;
306
307 const ColorSchemeBase *color = &DEFAULT_COLORS;
308
309 string indent("");
310 Index start_level(0);
311
312 if (piom) {
313 start_level = piom->getLevel();
314 indent = piom->getIndentation();
315 color = piom->getColorScheme();
316 }
317
318 string cKey = color->Magenta();
319 string cAttribute = color->Blue();
320 string cAttributeValue = color->Green();
321 string cReset = color->Reset();
322
323 // print starting only from the start_level (the first node (level 0) can be
324 // <> </>)
325 if (level >= start_level) {
326 // print the node name
327 out << indent << offset << "<" << cKey << p.name() << cReset;
328 // print the node attributes
329 for (ia = p.firstAttribute(); ia != p.lastAttribute(); ++ia) {
330 out << " " << cAttribute << ia->first << cReset << "=\""
331 << cAttributeValue << ia->second << cReset << "\"";
332 }
333 // print node value if it is not empty
334 has_value = ((p.value()).find_first_not_of("\t\n ") != std::string::npos);
335 if (has_value || p.HasChildren()) {
336 out << ">";
337 } else {
338 out << "/>" << std::endl;
339 }
340 if (has_value) {
341 out << cAttributeValue << p.value() << cReset;
342 linebreak = false;
343 }
344
345 // check if we need the end of the line or not
346 if (!has_value && p.HasChildren()) {
347 out << std::endl;
348 }
349 if (!has_value && !p.HasChildren()) {
350 linebreak = false;
351 }
352 }
353
354 // continue iteratively through the rest of the nodes
355 for (const Property &prop : p) {
356 level++;
357 if (level > start_level) {
358 offset += "\t";
359 }
360 PrintNodeXML(out, prop, piom, level, offset);
361 if (level > start_level) {
362 offset.resize(offset.size() - 1);
363 }
364 level--;
365 }
366
367 if (level >= start_level) {
368 if (linebreak) {
369 out << indent << offset << "</" << cKey << p.name() << cReset << ">"
370 << std::endl;
371 } else if (has_value) {
372 out << "</" << cKey << p.name() << cReset << ">" << std::endl;
373 }
374 }
375}
376
377void PrintNodeHLP(std::ostream &out, const Property &p,
378 const Index start_level = 0, Index level = 0,
379 const string &prefix = "", const string &offset = "") {
380
381 Color<csRGB> RGB; // Using RGB palette
382 string fmt = "t|%1%%|15t|" + string(RGB.Blue()) + "%2%" +
383 string(RGB.Green()) + "%|40t|%3%%|55t|" + string(RGB.Reset()) +
384 "%4%\n";
385
386 Index leveloffset = level;
387 string help("");
388 // if this is the head node, print the header
389 if (level == start_level) {
390 string head_name = string(RGB.Magenta()) + p.name();
391 if (p.hasAttribute("help")) {
392 if (p.hasAttribute("help")) {
393 help = string(RGB.Red()) + p.getAttribute<string>("help");
394 }
395 out << boost::format(" %1%: %|18t| %2%" + string(RGB.Reset()) + "\n") %
396 head_name % help;
397 }
398 leveloffset = 0;
399 out << boost::format("%|3" + fmt) % "OPTION" % "DEFAULT" % "UNIT" %
400 "DESCRIPTION";
401 }
402
403 if (level > start_level) {
404 string ofmt = "%|" + boost::lexical_cast<string>(leveloffset) + fmt;
405 string unit("");
406 if (p.hasAttribute("unit")) {
407 unit = p.getAttribute<string>("unit");
408 }
409 string defaults("");
410 if (p.hasAttribute("default")) {
411 defaults = p.getAttribute<string>("default");
412 }
413 if (p.hasAttribute("help")) {
414 help = p.getAttribute<string>("help");
415 }
416 if (!unit.empty()) {
417 unit = "[" + unit + "]";
418 }
419 if (!defaults.empty()) {
420 defaults = "(" + defaults + ")";
421 }
422
423 string name = p.name();
424
425 out << boost::format(ofmt) % name % defaults % unit % help;
426 }
427
428 for (const Property &pp : p) {
429 level++;
430 if (prefix == "") {
431 PrintNodeHLP(out, pp, start_level, level, pp.name(), offset);
432 } else {
433 PrintNodeHLP(out, pp, start_level, level, prefix + "." + pp.name(),
434 offset);
435 }
436 level--;
437 }
438}
439
440std::ostream &operator<<(std::ostream &out, const Property &p) {
441 if (!out.good()) {
442 return out;
443 }
444
445 std::ostream::sentry sentry(out);
446
447 if (sentry) {
448 // get the property format object attached to the stream
450 (PropertyIOManipulator *)out.pword(int(Property::getIOindex()));
451
452 string indentation("");
453 Index level = 0;
454
456 if (pm) {
457 indentation = pm->getIndentation();
458 level = pm->getLevel();
459 type = pm->getType();
460 // check if we > or >> to a file and remove color codes
461 // if ( out.tellp() != -1 ) - not suitable for pipes
462 if (!isatty(STDOUT_FILENO) || !isatty(STDERR_FILENO)) {
464 }
465 }
466
467 switch (type) {
468 default:
469 PrintNodeTXT(out, p, level);
470 break;
472 PrintNodeXML(out, p, pm);
473 break;
475 PrintNodeTXT(out, p, level, 0, "", indentation);
476 break;
478 PrintNodeHLP(out, p, level, 0, "", indentation);
479 break;
480 }
481 }
482
483 return out;
484}
485} // namespace tools
486} // 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:308
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:314
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
STL namespace.
void PrintNodeXML(std::ostream &out, const Property &p, PropertyIOManipulator *piom, Index level=0, string offset="")
Definition property.cc:300
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:276
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:377
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
base class for all analysis tools
Definition basebead.h:33
Eigen::Index Index
Definition types.h:26