Version: SMASH-1.5
configuration.cc
Go to the documentation of this file.
1 /*
2  *
3  * Copyright (c) 2014-2018
4  * SMASH Team
5  *
6  * GNU General Public License (GPLv3 or later)
7  *
8  */
9 
10 #include "smash/configuration.h"
11 
12 #include <cstdio>
13 #include <string>
14 #include <vector>
15 
16 #include <yaml-cpp/yaml.h> // NOLINT(build/include_order)
17 #include <boost/filesystem.hpp>
18 #include <boost/filesystem/fstream.hpp>
19 
21 #include "smash/inputfunctions.h"
22 
23 namespace smash {
24 
25 // internal helper functions
26 namespace {
36 YAML::Node find_node_at(YAML::Node node,
37  std::initializer_list<const char *> keys) {
38  assert(keys.size() > 0);
39  for (auto key : keys) {
40  // see comment in take on Node::reset
41  node.reset(node[key]);
42  }
43  return node;
44 }
45 
52 YAML::Node remove_empty_maps(YAML::Node root) {
53  if (root.IsMap()) {
54  std::vector<std::string> to_remove(root.size());
55  for (auto n : root) {
56  remove_empty_maps(n.second);
57  if ((n.second.IsMap() || n.second.IsSequence()) && n.second.size() == 0) {
58  to_remove.emplace_back(n.first.Scalar());
59  }
60  }
61  for (const auto &key : to_remove) {
62  root.remove(key);
63  }
64  }
65  return root;
66 }
67 
75 YAML::Node operator|=(YAML::Node a, const YAML::Node &b) {
76  if (b.IsMap()) {
77  for (auto n0 : b) {
78  a[n0.first.Scalar()] |= n0.second;
79  }
80  } else {
81  a = b;
82  }
83  return a;
84 }
85 
86 #ifndef DOXYGEN
87 namespace particles_txt {
88 #include <particles.txt.h>
89 } // namespace particles_txt
90 namespace decaymodes_txt {
91 #include <decaymodes.txt.h>
92 } // namespace decaymodes_txt
93 #endif
94 
95 } // unnamed namespace
96 
97 // Default constructor
98 Configuration::Configuration(const bf::path &path)
99  : Configuration(path, "config.yaml") {}
100 
101 // Constructor checking for validity of input
102 Configuration::Configuration(const bf::path &path, const bf::path &filename) {
103  const auto file_path = path / filename;
104  if (!bf::exists(file_path)) {
105  throw FileDoesNotExist("The configuration file was expected at '" +
106  file_path.native() +
107  "', but the file does not exist.");
108  }
109  if (has_crlf_line_ending(read_all(bf::ifstream((file_path))))) {
110  throw std::runtime_error(
111  "The configuration file has CR LF line endings. Please use LF "
112  "line endings.");
113  }
114  try {
115  root_node_ = YAML::LoadFile(file_path.native());
116  } catch (YAML::ParserException &e) {
117  if (e.msg == "illegal map value" || e.msg == "end of map not found") {
118  const auto line = std::to_string(e.mark.line + 1);
119  throw ParseError("YAML parse error at\n" + file_path.native() + ':' +
120  line + ": " + e.msg +
121  " (check that the indentation of map keys matches)");
122  }
123  throw;
124  }
125 
126  if (!root_node_["decaymodes"].IsDefined()) {
127  root_node_["decaymodes"] = decaymodes_txt::data;
128  }
129  if (!root_node_["particles"].IsDefined()) {
130  root_node_["particles"] = particles_txt::data;
131  }
132 }
133 
134 void Configuration::merge_yaml(const std::string &yaml) {
135  try {
136  root_node_ |= YAML::Load(yaml);
137  } catch (YAML::ParserException &e) {
138  if (e.msg == "illegal map value" || e.msg == "end of map not found") {
139  const auto line = std::to_string(e.mark.line + 1);
140  throw ParseError("YAML parse error in:\n" + yaml + "\nat line " + line +
141  ": " + e.msg +
142  " (check that the indentation of map keys matches)");
143  }
144  throw;
145  }
146 }
147 
148 std::vector<std::string> Configuration::list_upmost_nodes() {
149  std::vector<std::string> r;
150  for (auto i : root_node_) {
151  r.emplace_back(i.first.Scalar());
152  }
153  return r;
154 }
155 
157  std::initializer_list<const char *> keys) {
158  assert(keys.size() > 0);
159  auto node = root_node_;
160  auto keyIt = begin(keys);
161  std::size_t i = 0;
162  for (; i < keys.size() - 1; ++i, ++keyIt) {
163  // Node::reset does what you might expect Node::operator= to do. But
164  // operator= assigns a value to the node. So
165  // node = node[*keyIt]
166  // leads to modification of the data structure, not simple traversal.
167  node.reset(node[*keyIt]);
168  }
169  const auto r = node[*keyIt];
170  node.remove(*keyIt);
171  return {r, keys.begin()[keys.size() - 1]};
172 }
173 
175  std::initializer_list<const char *> keys) const {
176  return {find_node_at(root_node_, keys), keys.begin()[keys.size() - 1]};
177 }
178 
179 void Configuration::remove_all_but(const std::string &key) {
180  std::vector<std::string> to_remove;
181  for (auto i : root_node_) {
182  if (i.first.Scalar() != key) {
183  to_remove.push_back(i.first.Scalar());
184  }
185  }
186  for (auto i : to_remove) {
187  root_node_.remove(i);
188  }
189 }
190 
192  std::initializer_list<const char *> keys) const {
193  const auto n = find_node_at(root_node_, keys);
194  return n.IsDefined();
195 }
196 
197 bool Configuration::has_value(std::initializer_list<const char *> keys) const {
198  const auto n = find_node_at(root_node_, keys);
199  return n.IsDefined() && (!n.IsNull());
200 }
201 
203  std::stringstream s;
205  return s.str();
206 }
207 
208 std::string Configuration::to_string() const {
209  std::stringstream s;
210  s << root_node_;
211  return s.str();
212 }
213 
214 } // namespace smash
Value read(std::initializer_list< const char *> keys) const
Additional interface for SMASH to read configuration values without removing them.
std::string unused_values_report() const
Returns a string listing the key/value pairs that have not been taken yet.
Thrown if the file does not exist.
Return type of Configuration::take that automatically determines the target type. ...
YAML::Node operator|=(YAML::Node a, const YAML::Node &b)
Merge two YAML::Nodes.
Configuration(const bf::path &path)
Reads config.yaml from the specified path.
Interface to the SMASH configuration files.
YAML::Node remove_empty_maps(YAML::Node root)
Removes all empty maps of a YAML::Node.
Thrown for YAML parse errors.
std::vector< std::string > list_upmost_nodes()
Lists all YAML::Nodes from the configuration setup.
bool has_value(std::initializer_list< const char *> keys) const
Returns whether there is a non-empty value behind the requested keys.
void merge_yaml(const std::string &yaml)
Merge the configuration in yaml into the existing tree.
void remove_all_but(const std::string &key)
Removes all entries in the map except for key.
bool has_crlf_line_ending(const std::string in)
Check if a line in the string ends with \r\n.
bool has_value_including_empty(std::initializer_list< const char *> keys) const
Returns if there is a (maybe empty) value behind the requested keys.
YAML::Node find_node_at(YAML::Node node, std::initializer_list< const char *> keys)
Finds a node, copies its structure and replaces the previous keys by the newly provided keys...
Value take(std::initializer_list< const char *> keys)
The default interface for SMASH to read configuration values.
YAML::Node root_node_
the general_config.yaml contents - fully parsed
constexpr int n
Neutron.
std::string to_string() const
Returns a YAML string of the current tree.
Definition: action.h:24
std::string read_all(std::istream &&input)
Utility function to read a complete input stream (e.g.