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/nodes.ipp"
30
31 #include <memory>
32
33 #include <lutok/state.ipp>
34
35 #include "utils/config/exceptions.hpp"
36 #include "utils/config/keys.hpp"
37 #include "utils/format/macros.hpp"
38
39 namespace config = utils::config;
40
41
42 /// Destructor.
~base_node(void)43 config::detail::base_node::~base_node(void)
44 {
45 }
46
47
48 /// Constructor.
49 ///
50 /// \param dynamic_ Whether the node is dynamic or not.
inner_node(const bool dynamic_)51 config::detail::inner_node::inner_node(const bool dynamic_) :
52 _dynamic(dynamic_)
53 {
54 }
55
56
57 /// Destructor.
~inner_node(void)58 config::detail::inner_node::~inner_node(void)
59 {
60 for (children_map::const_iterator iter = _children.begin();
61 iter != _children.end(); ++iter)
62 delete (*iter).second;
63 }
64
65
66 /// Fills the given node with a copy of this node's data.
67 ///
68 /// \param node The node to fill. Should be the fresh return value of a
69 /// deep_copy() operation.
70 void
copy_into(inner_node * node) const71 config::detail::inner_node::copy_into(inner_node* node) const
72 {
73 node->_dynamic = _dynamic;
74 for (children_map::const_iterator iter = _children.begin();
75 iter != _children.end(); ++iter) {
76 base_node* new_node = (*iter).second->deep_copy();
77 try {
78 node->_children[(*iter).first] = new_node;
79 } catch (...) {
80 delete new_node;
81 throw;
82 }
83 }
84 }
85
86
87 /// Combines two children sets, preferring the keys in the first set only.
88 ///
89 /// This operation is not symmetrical on c1 and c2. The caller is responsible
90 /// for invoking this twice so that the two key sets are combined if they happen
91 /// to differ.
92 ///
93 /// \param key Key to this node.
94 /// \param c1 First children set.
95 /// \param c2 First children set.
96 /// \param [in,out] node The node to combine into.
97 ///
98 /// \throw bad_combination_error If the two nodes cannot be combined.
99 void
combine_children_into(const tree_key & key,const children_map & c1,const children_map & c2,inner_node * node) const100 config::detail::inner_node::combine_children_into(
101 const tree_key& key,
102 const children_map& c1, const children_map& c2,
103 inner_node* node) const
104 {
105 for (children_map::const_iterator iter1 = c1.begin();
106 iter1 != c1.end(); ++iter1) {
107 const std::string& name = (*iter1).first;
108
109 if (node->_children.find(name) != node->_children.end()) {
110 continue;
111 }
112
113 std::unique_ptr< base_node > new_node;
114
115 children_map::const_iterator iter2 = c2.find(name);
116 if (iter2 == c2.end()) {
117 new_node.reset((*iter1).second->deep_copy());
118 } else {
119 tree_key child_key = key;
120 child_key.push_back(name);
121 new_node.reset((*iter1).second->combine(child_key,
122 (*iter2).second));
123 }
124
125 node->_children[name] = new_node.release();
126 }
127 }
128
129
130 /// Combines this inner node with another inner node onto a new node.
131 ///
132 /// The "dynamic" property is inherited by the new node if either of the two
133 /// nodes are dynamic.
134 ///
135 /// \param key Key to this node.
136 /// \param other_base The node to combine with.
137 /// \param [in,out] node The node to combine into.
138 ///
139 /// \throw bad_combination_error If the two nodes cannot be combined.
140 void
combine_into(const tree_key & key,const base_node * other_base,inner_node * node) const141 config::detail::inner_node::combine_into(const tree_key& key,
142 const base_node* other_base,
143 inner_node* node) const
144 {
145 try {
146 const inner_node& other = dynamic_cast< const inner_node& >(
147 *other_base);
148
149 node->_dynamic = _dynamic || other._dynamic;
150
151 combine_children_into(key, _children, other._children, node);
152 combine_children_into(key, other._children, _children, node);
153 } catch (const std::bad_cast& unused_e) {
154 throw config::bad_combination_error(
155 key, "'%s' is an inner node in the base tree but a leaf node in "
156 "the overrides tree");
157 }
158 }
159
160
161 /// Finds a node without creating it if not found.
162 ///
163 /// This recursive algorithm traverses the tree searching for a particular key.
164 /// The returned node is constant, so this can only be used for querying
165 /// purposes. For this reason, this algorithm does not create intermediate
166 /// nodes if they don't exist (as would be necessary to set a new node).
167 ///
168 /// \param key The key to be queried.
169 /// \param key_pos The current level within the key to be examined.
170 ///
171 /// \return A reference to the located node, if successful.
172 ///
173 /// \throw unknown_key_error If the provided key is unknown.
174 const config::detail::base_node*
lookup_ro(const tree_key & key,const tree_key::size_type key_pos) const175 config::detail::inner_node::lookup_ro(const tree_key& key,
176 const tree_key::size_type key_pos) const
177 {
178 PRE(key_pos < key.size());
179
180 const children_map::const_iterator child_iter = _children.find(
181 key[key_pos]);
182 if (child_iter == _children.end())
183 throw unknown_key_error(key);
184
185 if (key_pos == key.size() - 1) {
186 return (*child_iter).second;
187 } else {
188 PRE(key_pos < key.size() - 1);
189 try {
190 const inner_node& child = dynamic_cast< const inner_node& >(
191 *(*child_iter).second);
192 return child.lookup_ro(key, key_pos + 1);
193 } catch (const std::bad_cast& e) {
194 throw unknown_key_error(
195 key, "Cannot address incomplete configuration property '%s'");
196 }
197 }
198 }
199
200
201 /// Finds a node and creates it if not found.
202 ///
203 /// This recursive algorithm traverses the tree searching for a particular key,
204 /// creating any intermediate nodes if they do not already exist (for the case
205 /// of dynamic inner nodes). The returned node is non-constant, so this can be
206 /// used by the algorithms that set key values.
207 ///
208 /// \param key The key to be queried.
209 /// \param key_pos The current level within the key to be examined.
210 /// \param new_node A function that returns a new leaf node of the desired
211 /// type. This is only called if the leaf cannot be found, but it has
212 /// already been defined.
213 ///
214 /// \return A reference to the located node, if successful.
215 ///
216 /// \throw invalid_key_value If the resulting node of the search would be an
217 /// inner node.
218 /// \throw unknown_key_error If the provided key is unknown.
219 config::leaf_node*
lookup_rw(const tree_key & key,const tree_key::size_type key_pos,new_node_hook new_node)220 config::detail::inner_node::lookup_rw(const tree_key& key,
221 const tree_key::size_type key_pos,
222 new_node_hook new_node)
223 {
224 PRE(key_pos < key.size());
225
226 children_map::const_iterator child_iter = _children.find(key[key_pos]);
227 if (child_iter == _children.end()) {
228 if (_dynamic) {
229 base_node* const child = (key_pos == key.size() - 1) ?
230 dynamic_cast< base_node* >(new_node()) :
231 dynamic_cast< base_node* >(new dynamic_inner_node());
232 // The types for `new_node`
233 INV_MSG(child != nullptr, "check the return type for the function called");
234 _children.insert(children_map::value_type(key[key_pos], child));
235 child_iter = _children.find(key[key_pos]);
236 } else {
237 throw unknown_key_error(key);
238 }
239 }
240
241 if (key_pos == key.size() - 1) {
242 try {
243 leaf_node& child = dynamic_cast< leaf_node& >(
244 *(*child_iter).second);
245 return &child;
246 } catch (const std::bad_cast& unused_error) {
247 throw invalid_key_value(key, "Type mismatch");
248 }
249 } else {
250 PRE(key_pos < key.size() - 1);
251 try {
252 inner_node& child = dynamic_cast< inner_node& >(
253 *(*child_iter).second);
254 return child.lookup_rw(key, key_pos + 1, new_node);
255 } catch (const std::bad_cast& e) {
256 throw unknown_key_error(
257 key, "Cannot address incomplete configuration property '%s'");
258 }
259 }
260 }
261
262
263 /// Converts the subtree to a collection of key/value string pairs.
264 ///
265 /// \param [out] properties The accumulator for the generated properties. The
266 /// contents of the map are only extended.
267 /// \param key The path to the current node.
268 void
all_properties(properties_map & properties,const tree_key & key) const269 config::detail::inner_node::all_properties(properties_map& properties,
270 const tree_key& key) const
271 {
272 for (children_map::const_iterator iter = _children.begin();
273 iter != _children.end(); ++iter) {
274 tree_key child_key = key;
275 child_key.push_back((*iter).first);
276 try {
277 leaf_node& child = dynamic_cast< leaf_node& >(*(*iter).second);
278 if (child.is_set())
279 properties[flatten_key(child_key)] = child.to_string();
280 } catch (const std::bad_cast& unused_error) {
281 inner_node& child = dynamic_cast< inner_node& >(*(*iter).second);
282 child.all_properties(properties, child_key);
283 }
284 }
285 }
286
287
288 /// Constructor.
static_inner_node(void)289 config::detail::static_inner_node::static_inner_node(void) :
290 inner_node(false)
291 {
292 }
293
294
295 /// Copies the node.
296 ///
297 /// \return A dynamically-allocated node.
298 config::detail::base_node*
deep_copy(void) const299 config::detail::static_inner_node::deep_copy(void) const
300 {
301 std::unique_ptr< inner_node > new_node(new static_inner_node());
302 copy_into(new_node.get());
303 return new_node.release();
304 }
305
306
307 /// Combines this node with another one.
308 ///
309 /// \param key Key to this node.
310 /// \param other The node to combine with.
311 ///
312 /// \return A new node representing the combination.
313 ///
314 /// \throw bad_combination_error If the two nodes cannot be combined.
315 config::detail::base_node*
combine(const tree_key & key,const base_node * other) const316 config::detail::static_inner_node::combine(const tree_key& key,
317 const base_node* other) const
318 {
319 std::unique_ptr< inner_node > new_node(new static_inner_node());
320 combine_into(key, other, new_node.get());
321 return new_node.release();
322 }
323
324
325 /// Registers a key as valid and having a specific type.
326 ///
327 /// This method does not raise errors on invalid/unknown keys or other
328 /// tree-related issues. The reasons is that define() is a method that does not
329 /// depend on user input: it is intended to pre-populate the tree with a
330 /// specific structure, and that happens once at coding time.
331 ///
332 /// \param key The key to be registered.
333 /// \param key_pos The current level within the key to be examined.
334 /// \param new_node A function that returns a new leaf node of the desired
335 /// type.
336 void
define(const tree_key & key,const tree_key::size_type key_pos,new_node_hook new_node)337 config::detail::static_inner_node::define(const tree_key& key,
338 const tree_key::size_type key_pos,
339 new_node_hook new_node)
340 {
341 PRE(key_pos < key.size());
342
343 if (key_pos == key.size() - 1) {
344 PRE_MSG(_children.find(key[key_pos]) == _children.end(),
345 "Key already defined");
346 _children.insert(children_map::value_type(key[key_pos], new_node()));
347 } else {
348 PRE(key_pos < key.size() - 1);
349 const children_map::const_iterator child_iter = _children.find(
350 key[key_pos]);
351
352 if (child_iter == _children.end()) {
353 static_inner_node* const child_ptr = new static_inner_node();
354 _children.insert(children_map::value_type(key[key_pos], child_ptr));
355 child_ptr->define(key, key_pos + 1, new_node);
356 } else {
357 try {
358 static_inner_node& child = dynamic_cast< static_inner_node& >(
359 *(*child_iter).second);
360 child.define(key, key_pos + 1, new_node);
361 } catch (const std::bad_cast& e) {
362 UNREACHABLE;
363 }
364 }
365 }
366 }
367
368
369 /// Constructor.
dynamic_inner_node(void)370 config::detail::dynamic_inner_node::dynamic_inner_node(void) :
371 inner_node(true)
372 {
373 }
374
375
376 /// Copies the node.
377 ///
378 /// \return A dynamically-allocated node.
379 config::detail::base_node*
deep_copy(void) const380 config::detail::dynamic_inner_node::deep_copy(void) const
381 {
382 std::unique_ptr< inner_node > new_node(new dynamic_inner_node());
383 copy_into(new_node.get());
384 return new_node.release();
385 }
386
387
388 /// Combines this node with another one.
389 ///
390 /// \param key Key to this node.
391 /// \param other The node to combine with.
392 ///
393 /// \return A new node representing the combination.
394 ///
395 /// \throw bad_combination_error If the two nodes cannot be combined.
396 config::detail::base_node*
combine(const tree_key & key,const base_node * other) const397 config::detail::dynamic_inner_node::combine(const tree_key& key,
398 const base_node* other) const
399 {
400 std::unique_ptr< inner_node > new_node(new dynamic_inner_node());
401 combine_into(key, other, new_node.get());
402 return new_node.release();
403 }
404
405
406 /// Destructor.
~leaf_node(void)407 config::leaf_node::~leaf_node(void)
408 {
409 }
410
411
412 /// Combines this node with another one.
413 ///
414 /// \param key Key to this node.
415 /// \param other_base The node to combine with.
416 ///
417 /// \return A new node representing the combination.
418 ///
419 /// \throw bad_combination_error If the two nodes cannot be combined.
420 config::detail::base_node*
combine(const detail::tree_key & key,const base_node * other_base) const421 config::leaf_node::combine(const detail::tree_key& key,
422 const base_node* other_base) const
423 {
424 try {
425 const leaf_node& other = dynamic_cast< const leaf_node& >(*other_base);
426
427 if (other.is_set()) {
428 return other.deep_copy();
429 } else {
430 return deep_copy();
431 }
432 } catch (const std::bad_cast& unused_e) {
433 throw config::bad_combination_error(
434 key, "'%s' is a leaf node in the base tree but an inner node in "
435 "the overrides treee");
436 }
437 }
438
439
440 /// Copies the node.
441 ///
442 /// \return A dynamically-allocated node.
443 config::detail::base_node*
deep_copy(void) const444 config::bool_node::deep_copy(void) const
445 {
446 std::unique_ptr< bool_node > new_node(new bool_node());
447 new_node->_value = _value;
448 return new_node.release();
449 }
450
451
452 /// Pushes the node's value onto the Lua stack.
453 ///
454 /// \param state The Lua state onto which to push the value.
455 void
push_lua(lutok::state & state) const456 config::bool_node::push_lua(lutok::state& state) const
457 {
458 state.push_boolean(value());
459 }
460
461
462 /// Sets the value of the node from an entry in the Lua stack.
463 ///
464 /// \param state The Lua state from which to get the value.
465 /// \param value_index The stack index in which the value resides.
466 ///
467 /// \throw value_error If the value in state(value_index) cannot be
468 /// processed by this node.
469 void
set_lua(lutok::state & state,const int value_index)470 config::bool_node::set_lua(lutok::state& state, const int value_index)
471 {
472 if (state.is_boolean(value_index))
473 set(state.to_boolean(value_index));
474 else
475 throw value_error("Not a boolean");
476 }
477
478
479 /// Copies the node.
480 ///
481 /// \return A dynamically-allocated node.
482 config::detail::base_node*
deep_copy(void) const483 config::int_node::deep_copy(void) const
484 {
485 std::unique_ptr< int_node > new_node(new int_node());
486 new_node->_value = _value;
487 return new_node.release();
488 }
489
490
491 /// Pushes the node's value onto the Lua stack.
492 ///
493 /// \param state The Lua state onto which to push the value.
494 void
push_lua(lutok::state & state) const495 config::int_node::push_lua(lutok::state& state) const
496 {
497 state.push_integer(value());
498 }
499
500
501 /// Sets the value of the node from an entry in the Lua stack.
502 ///
503 /// \param state The Lua state from which to get the value.
504 /// \param value_index The stack index in which the value resides.
505 ///
506 /// \throw value_error If the value in state(value_index) cannot be
507 /// processed by this node.
508 void
set_lua(lutok::state & state,const int value_index)509 config::int_node::set_lua(lutok::state& state, const int value_index)
510 {
511 if (state.is_number(value_index))
512 set(state.to_integer(value_index));
513 else
514 throw value_error("Not an integer");
515 }
516
517
518 /// Checks a given value for validity.
519 ///
520 /// \param new_value The value to validate.
521 ///
522 /// \throw value_error If the value is not valid.
523 void
validate(const value_type & new_value) const524 config::positive_int_node::validate(const value_type& new_value) const
525 {
526 if (new_value <= 0)
527 throw value_error("Must be a positive integer");
528 }
529
530
531 /// Copies the node.
532 ///
533 /// \return A dynamically-allocated node.
534 config::detail::base_node*
deep_copy(void) const535 config::positive_int_node::deep_copy(void) const
536 {
537 std::unique_ptr< positive_int_node > new_node(new positive_int_node());
538 new_node->_value = _value;
539 return new_node.release();
540 }
541
542
543 /// Copies the node.
544 ///
545 /// \return A dynamically-allocated node.
546 config::detail::base_node*
deep_copy(void) const547 config::string_node::deep_copy(void) const
548 {
549 std::unique_ptr< string_node > new_node(new string_node());
550 new_node->_value = _value;
551 return new_node.release();
552 }
553
554
555 /// Pushes the node's value onto the Lua stack.
556 ///
557 /// \param state The Lua state onto which to push the value.
558 void
push_lua(lutok::state & state) const559 config::string_node::push_lua(lutok::state& state) const
560 {
561 state.push_string(value());
562 }
563
564
565 /// Sets the value of the node from an entry in the Lua stack.
566 ///
567 /// \param state The Lua state from which to get the value.
568 /// \param value_index The stack index in which the value resides.
569 ///
570 /// \throw value_error If the value in state(value_index) cannot be
571 /// processed by this node.
572 void
set_lua(lutok::state & state,const int value_index)573 config::string_node::set_lua(lutok::state& state, const int value_index)
574 {
575 if (state.is_string(value_index))
576 set(state.to_string(value_index));
577 else
578 throw value_error("Not a string");
579 }
580
581
582 /// Copies the node.
583 ///
584 /// \return A dynamically-allocated node.
585 config::detail::base_node*
deep_copy(void) const586 config::strings_set_node::deep_copy(void) const
587 {
588 std::unique_ptr< strings_set_node > new_node(new strings_set_node());
589 new_node->_value = _value;
590 return new_node.release();
591 }
592
593
594 /// Converts a single word to the native type.
595 ///
596 /// \param raw_value The value to parse.
597 ///
598 /// \return The parsed value.
599 std::string
parse_one(const std::string & raw_value) const600 config::strings_set_node::parse_one(const std::string& raw_value) const
601 {
602 return raw_value;
603 }
604