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/tree.ipp" 30 31 #include "utils/config/exceptions.hpp" 32 #include "utils/config/keys.hpp" 33 #include "utils/config/nodes.ipp" 34 #include "utils/format/macros.hpp" 35 36 namespace config = utils::config; 37 38 39 /// Constructor. 40 /// 41 /// \param strict Whether keys must be validated at "set" time. 42 config::tree::tree(const bool strict) : 43 _strict(strict), _root(new detail::static_inner_node()) 44 { 45 } 46 47 48 /// Constructor with a non-empty root. 49 /// 50 /// \param strict Whether keys must be validated at "set" time. 51 /// \param root The root to the tree to be owned by this instance. 52 config::tree::tree(const bool strict, detail::static_inner_node* root) : 53 _strict(strict), _root(root) 54 { 55 } 56 57 58 /// Destructor. 59 config::tree::~tree(void) 60 { 61 } 62 63 64 /// Generates a deep copy of the input tree. 65 /// 66 /// \return A new tree that is an exact copy of this tree. 67 config::tree 68 config::tree::deep_copy(void) const 69 { 70 detail::static_inner_node* new_root = 71 dynamic_cast< detail::static_inner_node* >(_root->deep_copy()); 72 return config::tree(_strict, new_root); 73 } 74 75 76 /// Combines two trees. 77 /// 78 /// By combination we understand a new tree that contains the full key space of 79 /// the two input trees and, for the keys that match, respects the value of the 80 /// right-hand side (aka "other") tree. 81 /// 82 /// Any nodes marked as dynamic "win" over non-dynamic nodes and the resulting 83 /// tree will have the dynamic property set on those. 84 /// 85 /// \param overrides The tree to use as value overrides. 86 /// 87 /// \return The combined tree. 88 /// 89 /// \throw bad_combination_error If the two trees cannot be combined; for 90 /// example, if a single key represents an inner node in one tree but a leaf 91 /// node in the other one. 92 config::tree 93 config::tree::combine(const tree& overrides) const 94 { 95 const detail::static_inner_node* other_root = 96 dynamic_cast< const detail::static_inner_node * >( 97 overrides._root.get()); 98 99 detail::static_inner_node* new_root = 100 dynamic_cast< detail::static_inner_node* >( 101 _root->combine(detail::tree_key(), other_root)); 102 return config::tree(_strict, new_root); 103 } 104 105 106 /// Registers a node as being dynamic. 107 /// 108 /// This operation creates the given key as an inner node. Further set 109 /// operations that trespass this node will automatically create any missing 110 /// keys. 111 /// 112 /// This method does not raise errors on invalid/unknown keys or other 113 /// tree-related issues. The reasons is that define() is a method that does not 114 /// depend on user input: it is intended to pre-populate the tree with a 115 /// specific structure, and that happens once at coding time. 116 /// 117 /// \param dotted_key The key to be registered in dotted representation. 118 void 119 config::tree::define_dynamic(const std::string& dotted_key) 120 { 121 try { 122 const detail::tree_key key = detail::parse_key(dotted_key); 123 _root->define(key, 0, detail::new_node< detail::dynamic_inner_node >); 124 } catch (const error& e) { 125 UNREACHABLE_MSG("define() failing due to key errors is a programming " 126 "mistake: " + std::string(e.what())); 127 } 128 } 129 130 131 /// Checks if a given node is set. 132 /// 133 /// \param dotted_key The key to be checked. 134 /// 135 /// \return True if the key is set to a specific value (not just defined). 136 /// False if the key is not set or if the key does not exist. 137 /// 138 /// \throw invalid_key_error If the provided key has an invalid format. 139 bool 140 config::tree::is_set(const std::string& dotted_key) const 141 { 142 const detail::tree_key key = detail::parse_key(dotted_key); 143 try { 144 const detail::base_node* raw_node = _root->lookup_ro(key, 0); 145 try { 146 const leaf_node& child = dynamic_cast< const leaf_node& >( 147 *raw_node); 148 return child.is_set(); 149 } catch (const std::bad_cast& unused_error) { 150 return false; 151 } 152 } catch (const unknown_key_error& unused_error) { 153 return false; 154 } 155 } 156 157 158 /// Pushes a leaf node's value onto the Lua stack. 159 /// 160 /// \param dotted_key The key to be pushed. 161 /// \param state The Lua state into which to push the key's value. 162 /// 163 /// \throw invalid_key_error If the provided key has an invalid format. 164 /// \throw unknown_key_error If the provided key is unknown. 165 void 166 config::tree::push_lua(const std::string& dotted_key, lutok::state& state) const 167 { 168 const detail::tree_key key = detail::parse_key(dotted_key); 169 const detail::base_node* raw_node = _root->lookup_ro(key, 0); 170 try { 171 const leaf_node& child = dynamic_cast< const leaf_node& >(*raw_node); 172 child.push_lua(state); 173 } catch (const std::bad_cast& unused_error) { 174 throw unknown_key_error(key); 175 } 176 } 177 178 179 /// Sets a leaf node's value from a value in the Lua stack. 180 /// 181 /// \param dotted_key The key to be set. 182 /// \param state The Lua state from which to retrieve the value. 183 /// \param value_index The position in the Lua stack holding the value. 184 /// 185 /// \throw invalid_key_error If the provided key has an invalid format. 186 /// \throw invalid_key_value If the value mismatches the node type. 187 /// \throw unknown_key_error If the provided key is unknown. 188 void 189 config::tree::set_lua(const std::string& dotted_key, lutok::state& state, 190 const int value_index) 191 { 192 const detail::tree_key key = detail::parse_key(dotted_key); 193 try { 194 detail::base_node* raw_node = _root->lookup_rw( 195 key, 0, detail::new_node< string_node >); 196 leaf_node& child = dynamic_cast< leaf_node& >(*raw_node); 197 child.set_lua(state, value_index); 198 } catch (const unknown_key_error& e) { 199 if (_strict) 200 throw e; 201 } catch (const value_error& e) { 202 throw invalid_key_value(key, e.what()); 203 } catch (const std::bad_cast& unused_error) { 204 throw invalid_key_value(key, "Type mismatch"); 205 } 206 } 207 208 209 /// Gets the value of a node as a plain string. 210 /// 211 /// \param dotted_key The key to be looked up. 212 /// 213 /// \return The value of the located node as a string. 214 /// 215 /// \throw invalid_key_error If the provided key has an invalid format. 216 /// \throw unknown_key_error If the provided key is unknown. 217 std::string 218 config::tree::lookup_string(const std::string& dotted_key) const 219 { 220 const detail::tree_key key = detail::parse_key(dotted_key); 221 const detail::base_node* raw_node = _root->lookup_ro(key, 0); 222 try { 223 const leaf_node& child = dynamic_cast< const leaf_node& >(*raw_node); 224 return child.to_string(); 225 } catch (const std::bad_cast& unused_error) { 226 throw unknown_key_error(key); 227 } 228 } 229 230 231 /// Sets the value of a leaf addressed by its key from a string value. 232 /// 233 /// This respects the native types of all the nodes that have been predefined. 234 /// For new nodes under a dynamic subtree, this has no mechanism of determining 235 /// what type they need to have, so they are created as plain string nodes. 236 /// 237 /// \param dotted_key The key to be registered in dotted representation. 238 /// \param raw_value The string representation of the value to set the node to. 239 /// 240 /// \throw invalid_key_error If the provided key has an invalid format. 241 /// \throw invalid_key_value If the value mismatches the node type. 242 /// \throw unknown_key_error If the provided key is unknown. 243 void 244 config::tree::set_string(const std::string& dotted_key, 245 const std::string& raw_value) 246 { 247 const detail::tree_key key = detail::parse_key(dotted_key); 248 try { 249 detail::base_node* raw_node = _root->lookup_rw( 250 key, 0, detail::new_node< string_node >); 251 leaf_node& child = dynamic_cast< leaf_node& >(*raw_node); 252 child.set_string(raw_value); 253 } catch (const unknown_key_error& e) { 254 if (_strict) 255 throw e; 256 } catch (const value_error& e) { 257 throw invalid_key_value(key, e.what()); 258 } catch (const std::bad_cast& unused_error) { 259 throw invalid_key_value(key, "Type mismatch"); 260 } 261 } 262 263 264 /// Converts the tree to a collection of key/value string pairs. 265 /// 266 /// \param dotted_key Subtree from which to start the export. 267 /// \param strip_key If true, remove the dotted_key prefix from the resulting 268 /// properties. 269 /// 270 /// \return A map of keys to values in their textual representation. 271 /// 272 /// \throw invalid_key_error If the provided key has an invalid format. 273 /// \throw unknown_key_error If the provided key is unknown. 274 /// \throw value_error If the provided key points to a leaf. 275 config::properties_map 276 config::tree::all_properties(const std::string& dotted_key, 277 const bool strip_key) const 278 { 279 PRE(!strip_key || !dotted_key.empty()); 280 281 properties_map properties; 282 283 detail::tree_key key; 284 const detail::base_node* raw_node; 285 if (dotted_key.empty()) { 286 raw_node = _root.get(); 287 } else { 288 key = detail::parse_key(dotted_key); 289 raw_node = _root->lookup_ro(key, 0); 290 } 291 try { 292 const detail::inner_node& child = 293 dynamic_cast< const detail::inner_node& >(*raw_node); 294 child.all_properties(properties, key); 295 } catch (const std::bad_cast& unused_error) { 296 INV(!dotted_key.empty()); 297 throw value_error(F("Cannot export properties from a leaf node; " 298 "'%s' given") % dotted_key); 299 } 300 301 if (strip_key) { 302 properties_map stripped; 303 for (properties_map::const_iterator iter = properties.begin(); 304 iter != properties.end(); ++iter) { 305 stripped[(*iter).first.substr(dotted_key.length() + 1)] = 306 (*iter).second; 307 } 308 properties = stripped; 309 } 310 311 return properties; 312 } 313 314 315 /// Equality comparator. 316 /// 317 /// \param other The other object to compare this one to. 318 /// 319 /// \return True if this object and other are equal; false otherwise. 320 bool 321 config::tree::operator==(const tree& other) const 322 { 323 // TODO(jmmv): Would be nicer to perform the comparison directly on the 324 // nodes, instead of exporting the values to strings first. 325 return _root == other._root || all_properties() == other.all_properties(); 326 } 327 328 329 /// Inequality comparator. 330 /// 331 /// \param other The other object to compare this one to. 332 /// 333 /// \return True if this object and other are different; false otherwise. 334 bool 335 config::tree::operator!=(const tree& other) const 336 { 337 return !(*this == other); 338 } 339