1 // Copyright 2012 The Kyua Authors.
2 // All rights reserved.
3 //
4 // Redistribution and use in source and binary forms, with or without
5 // modification, are permitted provided that the following conditions are
6 // met:
7 //
8 // * Redistributions of source code must retain the above copyright
9 // notice, this list of conditions and the following disclaimer.
10 // * Redistributions in binary form must reproduce the above copyright
11 // notice, this list of conditions and the following disclaimer in the
12 // documentation and/or other materials provided with the distribution.
13 // * Neither the name of Google Inc. nor the names of its contributors
14 // may be used to endorse or promote products derived from this software
15 // without specific prior written permission.
16 //
17 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28
29 #include "utils/config/tree.ipp"
30
31 #include "utils/config/exceptions.hpp"
32 #include "utils/config/keys.hpp"
33 #include "utils/config/nodes.ipp"
34 #include "utils/format/macros.hpp"
35
36 namespace config = utils::config;
37
38
39 /// Constructor.
40 ///
41 /// \param strict Whether keys must be validated at "set" time.
tree(const bool strict)42 config::tree::tree(const bool strict) :
43 _strict(strict), _root(new detail::static_inner_node())
44 {
45 }
46
47
48 /// Constructor with a non-empty root.
49 ///
50 /// \param strict Whether keys must be validated at "set" time.
51 /// \param root The root to the tree to be owned by this instance.
tree(const bool strict,detail::static_inner_node * root)52 config::tree::tree(const bool strict, detail::static_inner_node* root) :
53 _strict(strict), _root(root)
54 {
55 }
56
57
58 /// Destructor.
~tree(void)59 config::tree::~tree(void)
60 {
61 }
62
63
64 /// Generates a deep copy of the input tree.
65 ///
66 /// \return A new tree that is an exact copy of this tree.
67 config::tree
deep_copy(void) const68 config::tree::deep_copy(void) const
69 {
70 detail::static_inner_node* new_root =
71 dynamic_cast< detail::static_inner_node* >(_root->deep_copy());
72 return config::tree(_strict, new_root);
73 }
74
75
76 /// Combines two trees.
77 ///
78 /// By combination we understand a new tree that contains the full key space of
79 /// the two input trees and, for the keys that match, respects the value of the
80 /// right-hand side (aka "other") tree.
81 ///
82 /// Any nodes marked as dynamic "win" over non-dynamic nodes and the resulting
83 /// tree will have the dynamic property set on those.
84 ///
85 /// \param overrides The tree to use as value overrides.
86 ///
87 /// \return The combined tree.
88 ///
89 /// \throw bad_combination_error If the two trees cannot be combined; for
90 /// example, if a single key represents an inner node in one tree but a leaf
91 /// node in the other one.
92 config::tree
combine(const tree & overrides) const93 config::tree::combine(const tree& overrides) const
94 {
95 const detail::static_inner_node* other_root =
96 dynamic_cast< const detail::static_inner_node * >(
97 overrides._root.get());
98
99 detail::static_inner_node* new_root =
100 dynamic_cast< detail::static_inner_node* >(
101 _root->combine(detail::tree_key(), other_root));
102 return config::tree(_strict, new_root);
103 }
104
105
106 /// Registers a node as being dynamic.
107 ///
108 /// This operation creates the given key as an inner node. Further set
109 /// operations that trespass this node will automatically create any missing
110 /// keys.
111 ///
112 /// This method does not raise errors on invalid/unknown keys or other
113 /// tree-related issues. The reasons is that define() is a method that does not
114 /// depend on user input: it is intended to pre-populate the tree with a
115 /// specific structure, and that happens once at coding time.
116 ///
117 /// \param dotted_key The key to be registered in dotted representation.
118 void
define_dynamic(const std::string & dotted_key)119 config::tree::define_dynamic(const std::string& dotted_key)
120 {
121 try {
122 const detail::tree_key key = detail::parse_key(dotted_key);
123 _root->define(key, 0, detail::new_node< detail::dynamic_inner_node >);
124 } catch (const error& e) {
125 UNREACHABLE_MSG("define() failing due to key errors is a programming "
126 "mistake: " + std::string(e.what()));
127 }
128 }
129
130
131 /// Checks if a given node is set.
132 ///
133 /// \param dotted_key The key to be checked.
134 ///
135 /// \return True if the key is set to a specific value (not just defined).
136 /// False if the key is not set or if the key does not exist.
137 ///
138 /// \throw invalid_key_error If the provided key has an invalid format.
139 bool
is_set(const std::string & dotted_key) const140 config::tree::is_set(const std::string& dotted_key) const
141 {
142 const detail::tree_key key = detail::parse_key(dotted_key);
143 try {
144 const detail::base_node* raw_node = _root->lookup_ro(key, 0);
145 try {
146 const leaf_node& child = dynamic_cast< const leaf_node& >(
147 *raw_node);
148 return child.is_set();
149 } catch (const std::bad_cast& unused_error) {
150 return false;
151 }
152 } catch (const unknown_key_error& unused_error) {
153 return false;
154 }
155 }
156
157
158 /// Pushes a leaf node's value onto the Lua stack.
159 ///
160 /// \param dotted_key The key to be pushed.
161 /// \param state The Lua state into which to push the key's value.
162 ///
163 /// \throw invalid_key_error If the provided key has an invalid format.
164 /// \throw unknown_key_error If the provided key is unknown.
165 void
push_lua(const std::string & dotted_key,lutok::state & state) const166 config::tree::push_lua(const std::string& dotted_key, lutok::state& state) const
167 {
168 const detail::tree_key key = detail::parse_key(dotted_key);
169 const detail::base_node* raw_node = _root->lookup_ro(key, 0);
170 try {
171 const leaf_node& child = dynamic_cast< const leaf_node& >(*raw_node);
172 child.push_lua(state);
173 } catch (const std::bad_cast& unused_error) {
174 throw unknown_key_error(key);
175 }
176 }
177
178
179 /// Sets a leaf node's value from a value in the Lua stack.
180 ///
181 /// \param dotted_key The key to be set.
182 /// \param state The Lua state from which to retrieve the value.
183 /// \param value_index The position in the Lua stack holding the value.
184 ///
185 /// \throw invalid_key_error If the provided key has an invalid format.
186 /// \throw invalid_key_value If the value mismatches the node type.
187 /// \throw unknown_key_error If the provided key is unknown.
188 void
set_lua(const std::string & dotted_key,lutok::state & state,const int value_index)189 config::tree::set_lua(const std::string& dotted_key, lutok::state& state,
190 const int value_index)
191 {
192 const detail::tree_key key = detail::parse_key(dotted_key);
193 try {
194 detail::base_node* raw_node = _root->lookup_rw(
195 key, 0, detail::new_node< string_node >);
196 leaf_node& child = dynamic_cast< leaf_node& >(*raw_node);
197 child.set_lua(state, value_index);
198 } catch (const unknown_key_error& e) {
199 if (_strict)
200 throw e;
201 } catch (const value_error& e) {
202 throw invalid_key_value(key, e.what());
203 } catch (const std::bad_cast& unused_error) {
204 throw invalid_key_value(key, "Type mismatch");
205 }
206 }
207
208
209 /// Gets the value of a node as a plain string.
210 ///
211 /// \param dotted_key The key to be looked up.
212 ///
213 /// \return The value of the located node as a string.
214 ///
215 /// \throw invalid_key_error If the provided key has an invalid format.
216 /// \throw unknown_key_error If the provided key is unknown.
217 std::string
lookup_string(const std::string & dotted_key) const218 config::tree::lookup_string(const std::string& dotted_key) const
219 {
220 const detail::tree_key key = detail::parse_key(dotted_key);
221 const detail::base_node* raw_node = _root->lookup_ro(key, 0);
222 try {
223 const leaf_node& child = dynamic_cast< const leaf_node& >(*raw_node);
224 return child.to_string();
225 } catch (const std::bad_cast& unused_error) {
226 throw unknown_key_error(key);
227 }
228 }
229
230
231 /// Sets the value of a leaf addressed by its key from a string value.
232 ///
233 /// This respects the native types of all the nodes that have been predefined.
234 /// For new nodes under a dynamic subtree, this has no mechanism of determining
235 /// what type they need to have, so they are created as plain string nodes.
236 ///
237 /// \param dotted_key The key to be registered in dotted representation.
238 /// \param raw_value The string representation of the value to set the node to.
239 ///
240 /// \throw invalid_key_error If the provided key has an invalid format.
241 /// \throw invalid_key_value If the value mismatches the node type.
242 /// \throw unknown_key_error If the provided key is unknown.
243 void
set_string(const std::string & dotted_key,const std::string & raw_value)244 config::tree::set_string(const std::string& dotted_key,
245 const std::string& raw_value)
246 {
247 const detail::tree_key key = detail::parse_key(dotted_key);
248 try {
249 detail::base_node* raw_node = _root->lookup_rw(
250 key, 0, detail::new_node< string_node >);
251 leaf_node& child = dynamic_cast< leaf_node& >(*raw_node);
252 child.set_string(raw_value);
253 } catch (const unknown_key_error& e) {
254 if (_strict)
255 throw e;
256 } catch (const value_error& e) {
257 throw invalid_key_value(key, e.what());
258 } catch (const std::bad_cast& unused_error) {
259 throw invalid_key_value(key, "Type mismatch");
260 }
261 }
262
263
264 /// Converts the tree to a collection of key/value string pairs.
265 ///
266 /// \param dotted_key Subtree from which to start the export.
267 /// \param strip_key If true, remove the dotted_key prefix from the resulting
268 /// properties.
269 ///
270 /// \return A map of keys to values in their textual representation.
271 ///
272 /// \throw invalid_key_error If the provided key has an invalid format.
273 /// \throw unknown_key_error If the provided key is unknown.
274 /// \throw value_error If the provided key points to a leaf.
275 config::properties_map
all_properties(const std::string & dotted_key,const bool strip_key) const276 config::tree::all_properties(const std::string& dotted_key,
277 const bool strip_key) const
278 {
279 PRE(!strip_key || !dotted_key.empty());
280
281 properties_map properties;
282
283 detail::tree_key key;
284 const detail::base_node* raw_node;
285 if (dotted_key.empty()) {
286 raw_node = _root.get();
287 } else {
288 key = detail::parse_key(dotted_key);
289 raw_node = _root->lookup_ro(key, 0);
290 }
291 try {
292 const detail::inner_node& child =
293 dynamic_cast< const detail::inner_node& >(*raw_node);
294 child.all_properties(properties, key);
295 } catch (const std::bad_cast& unused_error) {
296 INV(!dotted_key.empty());
297 throw value_error(F("Cannot export properties from a leaf node; "
298 "'%s' given") % dotted_key);
299 }
300
301 if (strip_key) {
302 properties_map stripped;
303 for (properties_map::const_iterator iter = properties.begin();
304 iter != properties.end(); ++iter) {
305 stripped[(*iter).first.substr(dotted_key.length() + 1)] =
306 (*iter).second;
307 }
308 properties = stripped;
309 }
310
311 return properties;
312 }
313
314
315 /// Equality comparator.
316 ///
317 /// \param other The other object to compare this one to.
318 ///
319 /// \return True if this object and other are equal; false otherwise.
320 bool
operator ==(const tree & other) const321 config::tree::operator==(const tree& other) const
322 {
323 // TODO(jmmv): Would be nicer to perform the comparison directly on the
324 // nodes, instead of exporting the values to strings first.
325 return _root == other._root || all_properties() == other.all_properties();
326 }
327
328
329 /// Inequality comparator.
330 ///
331 /// \param other The other object to compare this one to.
332 ///
333 /// \return True if this object and other are different; false otherwise.
334 bool
operator !=(const tree & other) const335 config::tree::operator!=(const tree& other) const
336 {
337 return !(*this == other);
338 }
339