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 39namespace config = utils::config; 40 41 42/// Destructor. 43config::detail::base_node::~base_node(void) 44{ 45} 46 47 48/// Constructor. 49/// 50/// \param dynamic_ Whether the node is dynamic or not. 51config::detail::inner_node::inner_node(const bool dynamic_) : 52 _dynamic(dynamic_) 53{ 54} 55 56 57/// Destructor. 58config::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. 70void 71config::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. 99void 100config::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. 140void 141config::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. 174const config::detail::base_node* 175config::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. 219config::leaf_node* 220config::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. 266void 267config::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. 287config::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. 296config::detail::base_node* 297config::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. 313config::detail::base_node* 314config::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. 334void 335config::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. 368config::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. 377config::detail::base_node* 378config::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. 394config::detail::base_node* 395config::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. 405config::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. 418config::detail::base_node* 419config::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. 441config::detail::base_node* 442config::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. 453void 454config::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. 467void 468config::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. 480config::detail::base_node* 481config::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. 492void 493config::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. 506void 507config::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. 521void 522config::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. 532config::detail::base_node* 533config::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. 544void 545config::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. 558void 559config::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. 571config::detail::base_node* 572config::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. 585std::string 586config::strings_set_node::parse_one(const std::string& raw_value) const 587{ 588 return raw_value; 589} 590