FEAT 3
Finite Element Analysis Toolbox
Loading...
Searching...
No Matches
property_map.cpp
1// FEAT3: Finite Element Analysis Toolbox, Version 3
2// Copyright (C) 2010 by Stefan Turek & the FEAT group
3// FEAT3 is released under the GNU General Public License version 3,
4// see the file 'copyright.txt' in the top level directory for details.
5
6#include <kernel/util/property_map.hpp>
7#include <kernel/util/dist_file_io.hpp>
8
9#include <fstream>
10#include <stack>
11
12#include <algorithm>
13
14namespace FEAT
15{
17 _parent(parent)
18 {
19 }
20
22 {
23 }
24
25 bool PropertyMap::add_entry(String key, String value, bool replace)
26 {
27 // try to insert the key-value-pair
28 std::pair<EntryMap::iterator, bool> rtn(_values.insert(std::make_pair(key,value)));
29 if(!rtn.second)
30 {
31 // insertion failed, i.e. there already exists a pair with that key - replace it?
32 if(!replace)
33 {
34 // Do not replace, so return false.
35 return false;
36 }
37 // okay, replace the existing value string
38 (*rtn.first).second = value;
39 }
40 // insertion / replacement successful
41 return true;
42 }
43
45 {
46 // try to find the entry
47 SectionMap::iterator it(_sections.find(name));
48
49 // if it has been found
50 if(it != _sections.end())
51 {
52 return ((*it).second).get();
53 }
54
55 // if it was not found, create a new section
56 return _sections.emplace(name, std::make_shared<PropertyMap>(this)).first->second.get();
57 }
58
60 {
61 EntryMap::iterator it(_values.find(key));
62 if(it != _values.end())
63 {
64 _values.erase(it);
65 return true;
66 }
67 return false;
68 }
69
71 {
72 SectionMap::iterator it(_sections.find(name));
73 if(it != _sections.end())
74 {
75 _sections.erase(it);
76 return true;
77 }
78 return false;
79 }
80
82 {
83 // We're too lazy to copy-&-paste the code below, so we'll do some casting...
84 return const_cast<PropertyMap*>(static_cast<const PropertyMap*>(this)->query_section(path));
85 }
86
88 {
89 // first of all, trim the input of leading and trailing slashes
90 path.trim_me("/");
91
92 // we need to split up the input path into:
93 // the leading section name
94 String sec_name(path);
95 // the remainder of the path
96 String rem_path("");
97
98 // try to find the first slash inside our path
99 std::size_t p = path.find('/');
100 if(p != path.npos)
101 {
102 // split our path at the separator
103 sec_name = path.substr(0, p).trim();
104 rem_path = path.substr(p+1).trim();
105 }
106
107 // try to find the leading section
108 const PropertyMap* lead = nullptr;
109 if(sec_name == "!")
110 lead = this->get_root();
111 else if(sec_name == "~")
112 lead = this->get_parent();
113 else
114 lead = this->get_sub_section(sec_name);
115
116 // do we have a remaining path?
117 if(rem_path.empty())
118 return lead;
119 else if(lead != nullptr)
120 return lead->query_section(rem_path);
121 else
122 return nullptr;
123 }
124
125 std::pair<String, bool> PropertyMap::query(String key_path) const
126 {
127 // first of all, trim the input of leadin and trailing slashes
128 key_path.trim_me("/");
129
130 // try to find the last slash inside our path
131 std::size_t p = key_path.rfind('/');
132
133 // no path separator found?
134 if(p == key_path.npos)
135 {
136 // interpret the path as a simple name then
137 return get_entry(key_path);
138 }
139
140 // okay, split the input path at the slash; this gives us
141 // the leading section path
142 String sec_path = key_path.substr(0, p).trim();
143 // the key name
144 String key_name = key_path.substr(p+1).trim();
145
146 // try to query the section
147 const PropertyMap* sec = query_section(sec_path);
148
149 // no luck finding the section?
150 if(sec == nullptr)
151 return std::make_pair(String(), false);
152 else
153 return sec->get_entry(key_name);
154 }
155
156 String PropertyMap::query(String key_path, String default_value) const
157 {
158 // use the other query function to search for the value
159 std::pair<String, bool> sb = this->query(key_path);
160
161 // if the key was found, return its value, otherwise return default value
162 return sb.second ? sb.first : default_value;
163 }
164
165 std::pair<String, bool> PropertyMap::get_entry(String key) const
166 {
167 EntryMap::const_iterator iter(_values.find(key));
168 if(iter == _values.end())
169 {
170 return std::make_pair("", false);
171 }
172 return std::make_pair(iter->second, true);
173 }
174
176 {
177 SectionMap::const_iterator iter(_sections.find(name));
178 if(iter == _sections.end())
179 {
180 return nullptr;
181 }
182 return (iter->second).get();
183 }
184
186 {
187 SectionMap::iterator iter(_sections.find(name));
188 if(iter == _sections.end())
189 {
190 return nullptr;
191 }
192 return (iter->second).get();
193 }
194
195 void PropertyMap::merge(const PropertyMap& section, bool replace)
196 {
197 EntryMap::const_iterator valiter(section._values.begin());
198 EntryMap::const_iterator valend(section._values.end());
199
200 // merging _values of the two sections
201 for(; valiter != valend; ++valiter)
202 {
203 add_entry(valiter->first, valiter->second, replace);
204 }
205
206 SectionMap::const_iterator seciter(section._sections.begin());
207 SectionMap::const_iterator secend(section._sections.end());
208
209 // merging _sections of the two PropertyMaps
210 for(; seciter != secend; ++seciter)
211 {
212 // get PropertyMap pointer to the section corresponding to iter and merge again
213 add_section(seciter->first)->merge(*seciter->second, replace);
214 }
215 }
216
217 void PropertyMap::read(const Dist::Comm& comm, String filename, bool replace)
218 {
219 std::stringstream is;
220 DistFileIO::read_common(is, filename, comm);
221 read(is, replace);
222 }
223
224 void PropertyMap::read(std::istream& ifs, bool replace)
225 {
226 // a stack to keep track of all currently open sections
227 std::stack<PropertyMap*> stack;
228
229 // a pointer to the currently used section; pushed onto the bottom of the stack
230 PropertyMap* current = this;
231 stack.push(current);
232
233 // the string containing the current line
234 String line;
235 line.reserve(256);
236
237 // other auxiliary variables
238 String key, value;
239 String::size_type found;
240 int cur_line = 0;
241
242 // an enumeration for keeping track what was read
243 enum LastRead
244 {
245 None, // nothing read until now
246 Entry, // last entity was a key-value pair
247 Section, // last entity was a section marker
248 BraceOpen, // last entity was an open curly brace
249 BraceClose, // last entity was a closed curly brace
250 Include // last entity was an include
251 };
252
253 // keep track of what was the last thing we read
254 LastRead last_read = None;
255
256 // loop over all lines until we reach the end of the file
257 while(!ifs.eof() && ifs.good())
258 {
259 // get a line
260 getline(ifs, line);
261 ++cur_line;
262
263 // trim whitespaces; continue with next line if the current one is empty
264 if(line.trim_me().empty())
265 {
266 continue;
267 }
268
269 // check for special keywords; these begin with an @ sign
270 /*if(line.front() == '@')
271 {
272 // if its an "@include "
273 if(line.compare(0, 9, "@include ") == 0)
274 {
275 // an include may appear anywhere in a file
276 last_read = Include;
277
278 // erase "@include "
279 line.erase(0, 9);
280
281 // parse the included file
282 current->parse(line.trim(), replace);
283
284 // okay
285 continue;
286 }
287 else
288 {
289 throw SyntaxError("Unknown keyword in line " + stringify(cur_line) + " : " + line);
290 }
291 }
292 else*/
293 if((found = line.find('#')) != std::string::npos)
294 {
295 // erase comment
296 line.erase(found);
297
298 // trim whitespaces; continue with next line if the current one is empty
299 if(line.trim_me().empty())
300 {
301 continue;
302 }
303 }
304
305 // if its a section
306 if((line.front() == '[') && (line.back() == ']'))
307 {
308 // a section may appear anywhere
309 last_read = Section;
310
311 // removing brackets and trimming
312 line.pop_front();
313 line.pop_back();
314
315 // the section name must not be empty
316 if(line.trim_me().empty())
317 {
318 throw SyntaxError("Missing section name in line " + stringify(cur_line));
319 }
320
321 // adding section
322 current = stack.top()->add_section(line);
323 }
324
325 // if its a key-value pair
326 else if((found = line.find('=')) != std::string::npos)
327 {
328 // an entry must not appear after a closing brace
329 if(last_read == BraceClose)
330 {
331 throw SyntaxError("Unexpected key-value pair in line " + stringify(cur_line) + " after '}'");
332 }
333 last_read = Entry;
334
335 // extract key
336 key = line.substr(0, found);
337
338 // the key must be not empty after trimming
339 if(key.trim_me().empty())
340 {
341 throw SyntaxError("Missing key in line " + stringify(cur_line));
342 }
343
344 // extract value string; add it if it's empty
345 value = String(line.substr(found + 1)).trim();
346 if(value.empty())
347 {
348 current->add_entry(key, value, replace);
349 continue;
350 }
351
352 // save current line number
353 int old_line = cur_line;
354
355 // check for line continuation
356 while(value.back() == '&')
357 {
358 // erase the '&' character
359 value.pop_back();
360
361 // read next non empty line
362 line.clear();
363 while(line.empty() && !ifs.eof() && ifs.good())
364 {
365 // read next line
366 getline(ifs, line);
367 ++cur_line;
368
369 // erase comments
370 if((found = line.find('#')) != std::string::npos)
371 {
372 line.erase(found);
373 }
374
375 // erase whitespaces
376 line.trim_me();
377 }
378
379 // do we have a non-empty string or an eof?
380 if(line.empty())
381 {
382 // file ended before we could find a line continuation
383 throw SyntaxError("Only empty lines from line " + stringify(old_line) + " on for line continuation");
384 }
385
386 // append line to value string
387 value += line;
388
389 // we'll continue the while-loop as the line might be further continued
390 }
391
392 // add the key-value pair
393 current->add_entry(key, value, replace);
394 }
395
396 else if(line == "{")
397 {
398 // an open curly brace is allowed only after a section marker
399 if(last_read == Section)
400 {
401 // push section onto the stack
402 stack.push(current);
403 last_read = BraceOpen;
404 }
405 // if it was not or there is something in the line above, that is not valid
406 else
407 {
408 throw SyntaxError("Unexpected '{' in line " + stringify(cur_line));
409 }
410 }
411
412 else if(line == "}")
413 {
414 // a closed curly brace is allowed anywhere except at the beginning of the file; in addition
415 // to that the stack must contain at least 2 entries as the root section always is the bottom
416 // entry of the stack and cannot be removed.
417 if((last_read != None) && (stack.size() > 1))
418 {
419 // remove top section from stack
420 stack.pop();
421 // and get the section underneath it
422 current = stack.top();
423 last_read = BraceClose;
424 }
425 else
426 {
427 throw SyntaxError("Unexpected '}' in line " + stringify(cur_line));
428 }
429 }
430
431 // if its something else
432 else
433 {
434 throw SyntaxError("Unknown format in line " +stringify(cur_line) + " : " + line);
435 }
436 } // end while
437
438 // in the end, the stack must contain nothing but the root section
439 if(stack.size() > 1)
440 {
441 throw SyntaxError("Missing '}' in line " + stringify(cur_line) + " or above!");
442 }
443 }
444
445 void PropertyMap::write(const Dist::Comm& comm, String filename) const
446 {
447 // only rank 0 writes
448 if(comm.rank() > 0)
449 return;
450
451 // open stream
452 std::ofstream ofs(filename.c_str(), std::ios_base::out | std::ios_base::trunc);
453 if(!ofs.is_open())
454 {
455 throw FileError("Failed to create '" + filename +"' for dumping!");
456 }
457
458 // dump into stream
459 write(ofs);
460
461 // close stream
462 ofs.close();
463 }
464
465 void PropertyMap::write(std::ostream& os, String::size_type indent) const
466 {
467 // prefix string: 2*indent spaces
468 String prefix(2*indent, ' ');
469
470 // dump values
471 EntryMap::const_iterator vit(_values.begin());
472 EntryMap::const_iterator vend(_values.end());
473 for( ; vit != vend ; ++vit)
474 {
475 os << prefix << (*vit).first << " = " << (*vit).second << "\n";
476 }
477
478 // dump subsections
479 SectionMap::const_iterator sit(_sections.begin());
480 SectionMap::const_iterator send(_sections.end());
481 for( ; sit != send ; ++sit)
482 {
483 // section name and opening brace
484 os << prefix << "[" << (*sit).first << "]\n";
485 os << prefix << "{\n";
486
487 // dump subsection with increased indent
488 (*sit).second->write(os, indent + 1);
489
490 // closing brace, including section name as comment
491 os << prefix << "} # end of [" << (*sit).first << "]\n";
492 }
493 }
494
495 PropertyMap* _add_sections(PropertyMap* out, const std::deque<String>& deq)
496 {
497 for(const auto& sec : deq)
498 {
499 out = out->add_section(sec);
500 }
501 return out;
502 }
503
504 void _treeify(PropertyMap* out, const PropertyMap* in)
505 {
506 for(auto iter = in->begin_section(); iter != in->end_section(); ++iter)
507 {
508 std::deque<String> key_deq = iter->first.split_by_charset("/");
509 auto curr_sec = _add_sections(out, key_deq);
510 for(auto iter_entry = iter->second->begin_entry(); iter_entry != iter->second->end_entry(); ++iter_entry)
511 {
512 curr_sec->add_entry(iter_entry->first, iter_entry->second);
513 }
514 _treeify(curr_sec, iter->second.get());
515 }
516 }
517
518 std::unique_ptr<PropertyMap> PropertyMap::treeify_structures() const
519 {
520 auto base_prop = std::make_unique<PropertyMap>();
521 PropertyMap* ptr = base_prop.get();
522
523 _treeify(ptr, this);
524 return base_prop;
525 }
526} //namespace FEAT
Communicator class.
Definition: dist.hpp:1349
int rank() const
Returns the rank of this process in this communicator.
Definition: dist.hpp:1494
static void read_common(std::stringstream &stream, const String &filename, const Dist::Comm &comm, int root_rank=0)
Reads a common text file for all ranks.
Base class for file related errors.
Definition: exception.hpp:173
A class organizing a tree of key-value pairs.
PropertyMap * add_section(String name)
Adds a new sub-section to this map.
bool erase_section(String name)
Erases a sub-section.
PropertyMap * get_parent()
Returns a pointer to the parent section.
PropertyMap * get_root()
Returns a pointer to the root section.
std::unique_ptr< PropertyMap > treeify_structures() const
Builds a new property map by inserting subsection based on section names.
PropertyMap * get_sub_section(String name)
Retrieves a sub-section by its name.
PropertyMap * query_section(String sec_path)
Queries a section by its section path.
std::pair< String, bool > query(String key_path) const
Queries a value by its key path.
EntryMap _values
a map storing the key-value-pairs
SectionMap _sections
a map storing the sub-sections
virtual ~PropertyMap()
Virtual Destructor.
bool erase_entry(String key)
Erases a key-value pair.
PropertyMap(PropertyMap *parent=nullptr)
Default Constructor.
bool add_entry(String key, String value, bool replace=true)
Adds a new key-value pair to this map.
void merge(const PropertyMap &section, bool replace=true)
Merges another PropertyMap into this.
std::pair< String, bool > get_entry(String key) const
Retrieves a value by its key.
void read(String filename, bool replace=true)
Parses a file in INI-format.
void write(String filename) const
Writes the property map into a file.
String class implementation.
Definition: string.hpp:46
void pop_front()
Removes the first character from the string.
Definition: string.hpp:239
String & trim_me(const String &charset)
Trims this string.
Definition: string.hpp:358
void pop_back()
Removes the last character from the string.
Definition: string.hpp:245
String trim(const String &charset) const
Trims the string.
Definition: string.hpp:327
Syntax Error exception class.
Definition: exception.hpp:250
@ iter
Plot every iteration (if applicable)
FEAT namespace.
Definition: adjactor.hpp:12
String stringify(const T_ &item)
Converts an item into a String.
Definition: string.hpp:944
@ value
specifies whether the space should supply basis function values