xref: /freebsd/contrib/kyua/utils/config/nodes.cpp (revision 8a7df6c58d549bd2eaa5cad30b2ef93c3eca3c18)
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.
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.
51 config::detail::inner_node::inner_node(const bool dynamic_) :
52     _dynamic(dynamic_)
53 {
54 }
55 
56 
57 /// Destructor.
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
71 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
100 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
141 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*
175 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*
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
269 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.
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*
299 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*
316 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
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.
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*
380 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*
397 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.
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*
421 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*
444 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
456 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
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*
483 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
495 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
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
524 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*
535 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*
547 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
559 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
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*
586 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
600 config::strings_set_node::parse_one(const std::string& raw_value) const
601 {
602     return raw_value;
603 }
604