xref: /freebsd/contrib/kyua/utils/config/nodes.cpp (revision e8d8bef961a50d4dc22501cde4fb9fb0be1b2532)
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::auto_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 treee");
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                 static_cast< base_node* >(new_node()) :
231                 static_cast< base_node* >(new dynamic_inner_node());
232             _children.insert(children_map::value_type(key[key_pos], child));
233             child_iter = _children.find(key[key_pos]);
234         } else {
235             throw unknown_key_error(key);
236         }
237     }
238 
239     if (key_pos == key.size() - 1) {
240         try {
241             leaf_node& child = dynamic_cast< leaf_node& >(
242                 *(*child_iter).second);
243             return &child;
244         } catch (const std::bad_cast& unused_error) {
245             throw invalid_key_value(key, "Type mismatch");
246         }
247     } else {
248         PRE(key_pos < key.size() - 1);
249         try {
250             inner_node& child = dynamic_cast< inner_node& >(
251                 *(*child_iter).second);
252             return child.lookup_rw(key, key_pos + 1, new_node);
253         } catch (const std::bad_cast& e) {
254             throw unknown_key_error(
255                 key, "Cannot address incomplete configuration property '%s'");
256         }
257     }
258 }
259 
260 
261 /// Converts the subtree to a collection of key/value string pairs.
262 ///
263 /// \param [out] properties The accumulator for the generated properties.  The
264 ///     contents of the map are only extended.
265 /// \param key The path to the current node.
266 void
267 config::detail::inner_node::all_properties(properties_map& properties,
268                                            const tree_key& key) const
269 {
270     for (children_map::const_iterator iter = _children.begin();
271          iter != _children.end(); ++iter) {
272         tree_key child_key = key;
273         child_key.push_back((*iter).first);
274         try {
275             leaf_node& child = dynamic_cast< leaf_node& >(*(*iter).second);
276             if (child.is_set())
277                 properties[flatten_key(child_key)] = child.to_string();
278         } catch (const std::bad_cast& unused_error) {
279             inner_node& child = dynamic_cast< inner_node& >(*(*iter).second);
280             child.all_properties(properties, child_key);
281         }
282     }
283 }
284 
285 
286 /// Constructor.
287 config::detail::static_inner_node::static_inner_node(void) :
288     inner_node(false)
289 {
290 }
291 
292 
293 /// Copies the node.
294 ///
295 /// \return A dynamically-allocated node.
296 config::detail::base_node*
297 config::detail::static_inner_node::deep_copy(void) const
298 {
299     std::auto_ptr< inner_node > new_node(new static_inner_node());
300     copy_into(new_node.get());
301     return new_node.release();
302 }
303 
304 
305 /// Combines this node with another one.
306 ///
307 /// \param key Key to this node.
308 /// \param other The node to combine with.
309 ///
310 /// \return A new node representing the combination.
311 ///
312 /// \throw bad_combination_error If the two nodes cannot be combined.
313 config::detail::base_node*
314 config::detail::static_inner_node::combine(const tree_key& key,
315                                            const base_node* other) const
316 {
317     std::auto_ptr< inner_node > new_node(new static_inner_node());
318     combine_into(key, other, new_node.get());
319     return new_node.release();
320 }
321 
322 
323 /// Registers a key as valid and having a specific type.
324 ///
325 /// This method does not raise errors on invalid/unknown keys or other
326 /// tree-related issues.  The reasons is that define() is a method that does not
327 /// depend on user input: it is intended to pre-populate the tree with a
328 /// specific structure, and that happens once at coding time.
329 ///
330 /// \param key The key to be registered.
331 /// \param key_pos The current level within the key to be examined.
332 /// \param new_node A function that returns a new leaf node of the desired
333 ///     type.
334 void
335 config::detail::static_inner_node::define(const tree_key& key,
336                                           const tree_key::size_type key_pos,
337                                           new_node_hook new_node)
338 {
339     PRE(key_pos < key.size());
340 
341     if (key_pos == key.size() - 1) {
342         PRE_MSG(_children.find(key[key_pos]) == _children.end(),
343                 "Key already defined");
344         _children.insert(children_map::value_type(key[key_pos], new_node()));
345     } else {
346         PRE(key_pos < key.size() - 1);
347         const children_map::const_iterator child_iter = _children.find(
348             key[key_pos]);
349 
350         if (child_iter == _children.end()) {
351             static_inner_node* const child_ptr = new static_inner_node();
352             _children.insert(children_map::value_type(key[key_pos], child_ptr));
353             child_ptr->define(key, key_pos + 1, new_node);
354         } else {
355             try {
356                 static_inner_node& child = dynamic_cast< static_inner_node& >(
357                     *(*child_iter).second);
358                 child.define(key, key_pos + 1, new_node);
359             } catch (const std::bad_cast& e) {
360                 UNREACHABLE;
361             }
362         }
363     }
364 }
365 
366 
367 /// Constructor.
368 config::detail::dynamic_inner_node::dynamic_inner_node(void) :
369     inner_node(true)
370 {
371 }
372 
373 
374 /// Copies the node.
375 ///
376 /// \return A dynamically-allocated node.
377 config::detail::base_node*
378 config::detail::dynamic_inner_node::deep_copy(void) const
379 {
380     std::auto_ptr< inner_node > new_node(new dynamic_inner_node());
381     copy_into(new_node.get());
382     return new_node.release();
383 }
384 
385 
386 /// Combines this node with another one.
387 ///
388 /// \param key Key to this node.
389 /// \param other The node to combine with.
390 ///
391 /// \return A new node representing the combination.
392 ///
393 /// \throw bad_combination_error If the two nodes cannot be combined.
394 config::detail::base_node*
395 config::detail::dynamic_inner_node::combine(const tree_key& key,
396                                             const base_node* other) const
397 {
398     std::auto_ptr< inner_node > new_node(new dynamic_inner_node());
399     combine_into(key, other, new_node.get());
400     return new_node.release();
401 }
402 
403 
404 /// Destructor.
405 config::leaf_node::~leaf_node(void)
406 {
407 }
408 
409 
410 /// Combines this node with another one.
411 ///
412 /// \param key Key to this node.
413 /// \param other_base The node to combine with.
414 ///
415 /// \return A new node representing the combination.
416 ///
417 /// \throw bad_combination_error If the two nodes cannot be combined.
418 config::detail::base_node*
419 config::leaf_node::combine(const detail::tree_key& key,
420                            const base_node* other_base) const
421 {
422     try {
423         const leaf_node& other = dynamic_cast< const leaf_node& >(*other_base);
424 
425         if (other.is_set()) {
426             return other.deep_copy();
427         } else {
428             return deep_copy();
429         }
430     } catch (const std::bad_cast& unused_e) {
431         throw config::bad_combination_error(
432             key, "'%s' is a leaf node in the base tree but an inner node in "
433             "the overrides treee");
434     }
435 }
436 
437 
438 /// Copies the node.
439 ///
440 /// \return A dynamically-allocated node.
441 config::detail::base_node*
442 config::bool_node::deep_copy(void) const
443 {
444     std::auto_ptr< bool_node > new_node(new bool_node());
445     new_node->_value = _value;
446     return new_node.release();
447 }
448 
449 
450 /// Pushes the node's value onto the Lua stack.
451 ///
452 /// \param state The Lua state onto which to push the value.
453 void
454 config::bool_node::push_lua(lutok::state& state) const
455 {
456     state.push_boolean(value());
457 }
458 
459 
460 /// Sets the value of the node from an entry in the Lua stack.
461 ///
462 /// \param state The Lua state from which to get the value.
463 /// \param value_index The stack index in which the value resides.
464 ///
465 /// \throw value_error If the value in state(value_index) cannot be
466 ///     processed by this node.
467 void
468 config::bool_node::set_lua(lutok::state& state, const int value_index)
469 {
470     if (state.is_boolean(value_index))
471         set(state.to_boolean(value_index));
472     else
473         throw value_error("Not a boolean");
474 }
475 
476 
477 /// Copies the node.
478 ///
479 /// \return A dynamically-allocated node.
480 config::detail::base_node*
481 config::int_node::deep_copy(void) const
482 {
483     std::auto_ptr< int_node > new_node(new int_node());
484     new_node->_value = _value;
485     return new_node.release();
486 }
487 
488 
489 /// Pushes the node's value onto the Lua stack.
490 ///
491 /// \param state The Lua state onto which to push the value.
492 void
493 config::int_node::push_lua(lutok::state& state) const
494 {
495     state.push_integer(value());
496 }
497 
498 
499 /// Sets the value of the node from an entry in the Lua stack.
500 ///
501 /// \param state The Lua state from which to get the value.
502 /// \param value_index The stack index in which the value resides.
503 ///
504 /// \throw value_error If the value in state(value_index) cannot be
505 ///     processed by this node.
506 void
507 config::int_node::set_lua(lutok::state& state, const int value_index)
508 {
509     if (state.is_number(value_index))
510         set(state.to_integer(value_index));
511     else
512         throw value_error("Not an integer");
513 }
514 
515 
516 /// Checks a given value for validity.
517 ///
518 /// \param new_value The value to validate.
519 ///
520 /// \throw value_error If the value is not valid.
521 void
522 config::positive_int_node::validate(const value_type& new_value) const
523 {
524     if (new_value <= 0)
525         throw value_error("Must be a positive integer");
526 }
527 
528 
529 /// Copies the node.
530 ///
531 /// \return A dynamically-allocated node.
532 config::detail::base_node*
533 config::string_node::deep_copy(void) const
534 {
535     std::auto_ptr< string_node > new_node(new string_node());
536     new_node->_value = _value;
537     return new_node.release();
538 }
539 
540 
541 /// Pushes the node's value onto the Lua stack.
542 ///
543 /// \param state The Lua state onto which to push the value.
544 void
545 config::string_node::push_lua(lutok::state& state) const
546 {
547     state.push_string(value());
548 }
549 
550 
551 /// Sets the value of the node from an entry in the Lua stack.
552 ///
553 /// \param state The Lua state from which to get the value.
554 /// \param value_index The stack index in which the value resides.
555 ///
556 /// \throw value_error If the value in state(value_index) cannot be
557 ///     processed by this node.
558 void
559 config::string_node::set_lua(lutok::state& state, const int value_index)
560 {
561     if (state.is_string(value_index))
562         set(state.to_string(value_index));
563     else
564         throw value_error("Not a string");
565 }
566 
567 
568 /// Copies the node.
569 ///
570 /// \return A dynamically-allocated node.
571 config::detail::base_node*
572 config::strings_set_node::deep_copy(void) const
573 {
574     std::auto_ptr< strings_set_node > new_node(new strings_set_node());
575     new_node->_value = _value;
576     return new_node.release();
577 }
578 
579 
580 /// Converts a single word to the native type.
581 ///
582 /// \param raw_value The value to parse.
583 ///
584 /// \return The parsed value.
585 std::string
586 config::strings_set_node::parse_one(const std::string& raw_value) const
587 {
588     return raw_value;
589 }
590