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::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
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 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*
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 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
all_properties(properties_map & properties,const tree_key & key) const267 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.
static_inner_node(void)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*
deep_copy(void) const297 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*
combine(const tree_key & key,const base_node * other) const314 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
define(const tree_key & key,const tree_key::size_type key_pos,new_node_hook new_node)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.
dynamic_inner_node(void)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*
deep_copy(void) const378 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*
combine(const tree_key & key,const base_node * other) const395 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.
~leaf_node(void)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*
combine(const detail::tree_key & key,const base_node * other_base) const419 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*
deep_copy(void) const442 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
push_lua(lutok::state & state) const454 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
set_lua(lutok::state & state,const int value_index)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*
deep_copy(void) const481 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
push_lua(lutok::state & state) const493 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
set_lua(lutok::state & state,const int value_index)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
validate(const value_type & new_value) const522 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*
deep_copy(void) const533 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
push_lua(lutok::state & state) const545 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
set_lua(lutok::state & state,const int value_index)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*
deep_copy(void) const572 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
parse_one(const std::string & raw_value) const586 config::strings_set_node::parse_one(const std::string& raw_value) const
587 {
588 return raw_value;
589 }
590