diff options
Diffstat (limited to 'utils/config/tree.cpp')
-rw-r--r-- | utils/config/tree.cpp | 338 |
1 files changed, 338 insertions, 0 deletions
diff --git a/utils/config/tree.cpp b/utils/config/tree.cpp new file mode 100644 index 0000000000000..1aa2d85b89cdc --- /dev/null +++ b/utils/config/tree.cpp @@ -0,0 +1,338 @@ +// Copyright 2012 The Kyua Authors. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of Google Inc. nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "utils/config/tree.ipp" + +#include "utils/config/exceptions.hpp" +#include "utils/config/keys.hpp" +#include "utils/config/nodes.ipp" +#include "utils/format/macros.hpp" + +namespace config = utils::config; + + +/// Constructor. +/// +/// \param strict Whether keys must be validated at "set" time. +config::tree::tree(const bool strict) : + _strict(strict), _root(new detail::static_inner_node()) +{ +} + + +/// Constructor with a non-empty root. +/// +/// \param strict Whether keys must be validated at "set" time. +/// \param root The root to the tree to be owned by this instance. +config::tree::tree(const bool strict, detail::static_inner_node* root) : + _strict(strict), _root(root) +{ +} + + +/// Destructor. +config::tree::~tree(void) +{ +} + + +/// Generates a deep copy of the input tree. +/// +/// \return A new tree that is an exact copy of this tree. +config::tree +config::tree::deep_copy(void) const +{ + detail::static_inner_node* new_root = + dynamic_cast< detail::static_inner_node* >(_root->deep_copy()); + return config::tree(_strict, new_root); +} + + +/// Combines two trees. +/// +/// By combination we understand a new tree that contains the full key space of +/// the two input trees and, for the keys that match, respects the value of the +/// right-hand side (aka "other") tree. +/// +/// Any nodes marked as dynamic "win" over non-dynamic nodes and the resulting +/// tree will have the dynamic property set on those. +/// +/// \param overrides The tree to use as value overrides. +/// +/// \return The combined tree. +/// +/// \throw bad_combination_error If the two trees cannot be combined; for +/// example, if a single key represents an inner node in one tree but a leaf +/// node in the other one. +config::tree +config::tree::combine(const tree& overrides) const +{ + const detail::static_inner_node* other_root = + dynamic_cast< const detail::static_inner_node * >( + overrides._root.get()); + + detail::static_inner_node* new_root = + dynamic_cast< detail::static_inner_node* >( + _root->combine(detail::tree_key(), other_root)); + return config::tree(_strict, new_root); +} + + +/// Registers a node as being dynamic. +/// +/// This operation creates the given key as an inner node. Further set +/// operations that trespass this node will automatically create any missing +/// keys. +/// +/// This method does not raise errors on invalid/unknown keys or other +/// tree-related issues. The reasons is that define() is a method that does not +/// depend on user input: it is intended to pre-populate the tree with a +/// specific structure, and that happens once at coding time. +/// +/// \param dotted_key The key to be registered in dotted representation. +void +config::tree::define_dynamic(const std::string& dotted_key) +{ + try { + const detail::tree_key key = detail::parse_key(dotted_key); + _root->define(key, 0, detail::new_node< detail::dynamic_inner_node >); + } catch (const error& e) { + UNREACHABLE_MSG("define() failing due to key errors is a programming " + "mistake: " + std::string(e.what())); + } +} + + +/// Checks if a given node is set. +/// +/// \param dotted_key The key to be checked. +/// +/// \return True if the key is set to a specific value (not just defined). +/// False if the key is not set or if the key does not exist. +/// +/// \throw invalid_key_error If the provided key has an invalid format. +bool +config::tree::is_set(const std::string& dotted_key) const +{ + const detail::tree_key key = detail::parse_key(dotted_key); + try { + const detail::base_node* raw_node = _root->lookup_ro(key, 0); + try { + const leaf_node& child = dynamic_cast< const leaf_node& >( + *raw_node); + return child.is_set(); + } catch (const std::bad_cast& unused_error) { + return false; + } + } catch (const unknown_key_error& unused_error) { + return false; + } +} + + +/// Pushes a leaf node's value onto the Lua stack. +/// +/// \param dotted_key The key to be pushed. +/// \param state The Lua state into which to push the key's value. +/// +/// \throw invalid_key_error If the provided key has an invalid format. +/// \throw unknown_key_error If the provided key is unknown. +void +config::tree::push_lua(const std::string& dotted_key, lutok::state& state) const +{ + const detail::tree_key key = detail::parse_key(dotted_key); + const detail::base_node* raw_node = _root->lookup_ro(key, 0); + try { + const leaf_node& child = dynamic_cast< const leaf_node& >(*raw_node); + child.push_lua(state); + } catch (const std::bad_cast& unused_error) { + throw unknown_key_error(key); + } +} + + +/// Sets a leaf node's value from a value in the Lua stack. +/// +/// \param dotted_key The key to be set. +/// \param state The Lua state from which to retrieve the value. +/// \param value_index The position in the Lua stack holding the value. +/// +/// \throw invalid_key_error If the provided key has an invalid format. +/// \throw invalid_key_value If the value mismatches the node type. +/// \throw unknown_key_error If the provided key is unknown. +void +config::tree::set_lua(const std::string& dotted_key, lutok::state& state, + const int value_index) +{ + const detail::tree_key key = detail::parse_key(dotted_key); + try { + detail::base_node* raw_node = _root->lookup_rw( + key, 0, detail::new_node< string_node >); + leaf_node& child = dynamic_cast< leaf_node& >(*raw_node); + child.set_lua(state, value_index); + } catch (const unknown_key_error& e) { + if (_strict) + throw e; + } catch (const value_error& e) { + throw invalid_key_value(key, e.what()); + } catch (const std::bad_cast& unused_error) { + throw invalid_key_value(key, "Type mismatch"); + } +} + + +/// Gets the value of a node as a plain string. +/// +/// \param dotted_key The key to be looked up. +/// +/// \return The value of the located node as a string. +/// +/// \throw invalid_key_error If the provided key has an invalid format. +/// \throw unknown_key_error If the provided key is unknown. +std::string +config::tree::lookup_string(const std::string& dotted_key) const +{ + const detail::tree_key key = detail::parse_key(dotted_key); + const detail::base_node* raw_node = _root->lookup_ro(key, 0); + try { + const leaf_node& child = dynamic_cast< const leaf_node& >(*raw_node); + return child.to_string(); + } catch (const std::bad_cast& unused_error) { + throw unknown_key_error(key); + } +} + + +/// Sets the value of a leaf addressed by its key from a string value. +/// +/// This respects the native types of all the nodes that have been predefined. +/// For new nodes under a dynamic subtree, this has no mechanism of determining +/// what type they need to have, so they are created as plain string nodes. +/// +/// \param dotted_key The key to be registered in dotted representation. +/// \param raw_value The string representation of the value to set the node to. +/// +/// \throw invalid_key_error If the provided key has an invalid format. +/// \throw invalid_key_value If the value mismatches the node type. +/// \throw unknown_key_error If the provided key is unknown. +void +config::tree::set_string(const std::string& dotted_key, + const std::string& raw_value) +{ + const detail::tree_key key = detail::parse_key(dotted_key); + try { + detail::base_node* raw_node = _root->lookup_rw( + key, 0, detail::new_node< string_node >); + leaf_node& child = dynamic_cast< leaf_node& >(*raw_node); + child.set_string(raw_value); + } catch (const unknown_key_error& e) { + if (_strict) + throw e; + } catch (const value_error& e) { + throw invalid_key_value(key, e.what()); + } catch (const std::bad_cast& unused_error) { + throw invalid_key_value(key, "Type mismatch"); + } +} + + +/// Converts the tree to a collection of key/value string pairs. +/// +/// \param dotted_key Subtree from which to start the export. +/// \param strip_key If true, remove the dotted_key prefix from the resulting +/// properties. +/// +/// \return A map of keys to values in their textual representation. +/// +/// \throw invalid_key_error If the provided key has an invalid format. +/// \throw unknown_key_error If the provided key is unknown. +/// \throw value_error If the provided key points to a leaf. +config::properties_map +config::tree::all_properties(const std::string& dotted_key, + const bool strip_key) const +{ + PRE(!strip_key || !dotted_key.empty()); + + properties_map properties; + + detail::tree_key key; + const detail::base_node* raw_node; + if (dotted_key.empty()) { + raw_node = _root.get(); + } else { + key = detail::parse_key(dotted_key); + raw_node = _root->lookup_ro(key, 0); + } + try { + const detail::inner_node& child = + dynamic_cast< const detail::inner_node& >(*raw_node); + child.all_properties(properties, key); + } catch (const std::bad_cast& unused_error) { + INV(!dotted_key.empty()); + throw value_error(F("Cannot export properties from a leaf node; " + "'%s' given") % dotted_key); + } + + if (strip_key) { + properties_map stripped; + for (properties_map::const_iterator iter = properties.begin(); + iter != properties.end(); ++iter) { + stripped[(*iter).first.substr(dotted_key.length() + 1)] = + (*iter).second; + } + properties = stripped; + } + + return properties; +} + + +/// Equality comparator. +/// +/// \param other The other object to compare this one to. +/// +/// \return True if this object and other are equal; false otherwise. +bool +config::tree::operator==(const tree& other) const +{ + // TODO(jmmv): Would be nicer to perform the comparison directly on the + // nodes, instead of exporting the values to strings first. + return _root == other._root || all_properties() == other.all_properties(); +} + + +/// Inequality comparator. +/// +/// \param other The other object to compare this one to. +/// +/// \return True if this object and other are different; false otherwise. +bool +config::tree::operator!=(const tree& other) const +{ + return !(*this == other); +} |