Path: blob/main/contrib/kyua/utils/config/tree.cpp
48081 views
// Copyright 2012 The Kyua Authors.1// All rights reserved.2//3// Redistribution and use in source and binary forms, with or without4// modification, are permitted provided that the following conditions are5// met:6//7// * Redistributions of source code must retain the above copyright8// notice, this list of conditions and the following disclaimer.9// * Redistributions in binary form must reproduce the above copyright10// notice, this list of conditions and the following disclaimer in the11// documentation and/or other materials provided with the distribution.12// * Neither the name of Google Inc. nor the names of its contributors13// may be used to endorse or promote products derived from this software14// without specific prior written permission.15//16// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS17// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT18// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR19// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT20// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,21// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT22// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,23// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY24// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT25// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE26// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.2728#include "utils/config/tree.ipp"2930#include "utils/config/exceptions.hpp"31#include "utils/config/keys.hpp"32#include "utils/config/nodes.ipp"33#include "utils/format/macros.hpp"3435namespace config = utils::config;363738/// Constructor.39///40/// \param strict Whether keys must be validated at "set" time.41config::tree::tree(const bool strict) :42_strict(strict), _root(new detail::static_inner_node())43{44}454647/// Constructor with a non-empty root.48///49/// \param strict Whether keys must be validated at "set" time.50/// \param root The root to the tree to be owned by this instance.51config::tree::tree(const bool strict, detail::static_inner_node* root) :52_strict(strict), _root(root)53{54}555657/// Destructor.58config::tree::~tree(void)59{60}616263/// Generates a deep copy of the input tree.64///65/// \return A new tree that is an exact copy of this tree.66config::tree67config::tree::deep_copy(void) const68{69detail::static_inner_node* new_root =70dynamic_cast< detail::static_inner_node* >(_root->deep_copy());71return config::tree(_strict, new_root);72}737475/// Combines two trees.76///77/// By combination we understand a new tree that contains the full key space of78/// the two input trees and, for the keys that match, respects the value of the79/// right-hand side (aka "other") tree.80///81/// Any nodes marked as dynamic "win" over non-dynamic nodes and the resulting82/// tree will have the dynamic property set on those.83///84/// \param overrides The tree to use as value overrides.85///86/// \return The combined tree.87///88/// \throw bad_combination_error If the two trees cannot be combined; for89/// example, if a single key represents an inner node in one tree but a leaf90/// node in the other one.91config::tree92config::tree::combine(const tree& overrides) const93{94const detail::static_inner_node* other_root =95dynamic_cast< const detail::static_inner_node * >(96overrides._root.get());9798detail::static_inner_node* new_root =99dynamic_cast< detail::static_inner_node* >(100_root->combine(detail::tree_key(), other_root));101return config::tree(_strict, new_root);102}103104105/// Registers a node as being dynamic.106///107/// This operation creates the given key as an inner node. Further set108/// operations that trespass this node will automatically create any missing109/// keys.110///111/// This method does not raise errors on invalid/unknown keys or other112/// tree-related issues. The reasons is that define() is a method that does not113/// depend on user input: it is intended to pre-populate the tree with a114/// specific structure, and that happens once at coding time.115///116/// \param dotted_key The key to be registered in dotted representation.117void118config::tree::define_dynamic(const std::string& dotted_key)119{120try {121const detail::tree_key key = detail::parse_key(dotted_key);122_root->define(key, 0, detail::new_node< detail::dynamic_inner_node >);123} catch (const error& e) {124UNREACHABLE_MSG("define() failing due to key errors is a programming "125"mistake: " + std::string(e.what()));126}127}128129130/// Checks if a given node is set.131///132/// \param dotted_key The key to be checked.133///134/// \return True if the key is set to a specific value (not just defined).135/// False if the key is not set or if the key does not exist.136///137/// \throw invalid_key_error If the provided key has an invalid format.138bool139config::tree::is_set(const std::string& dotted_key) const140{141const detail::tree_key key = detail::parse_key(dotted_key);142try {143const detail::base_node* raw_node = _root->lookup_ro(key, 0);144try {145const leaf_node& child = dynamic_cast< const leaf_node& >(146*raw_node);147return child.is_set();148} catch (const std::bad_cast& unused_error) {149return false;150}151} catch (const unknown_key_error& unused_error) {152return false;153}154}155156157/// Pushes a leaf node's value onto the Lua stack.158///159/// \param dotted_key The key to be pushed.160/// \param state The Lua state into which to push the key's value.161///162/// \throw invalid_key_error If the provided key has an invalid format.163/// \throw unknown_key_error If the provided key is unknown.164void165config::tree::push_lua(const std::string& dotted_key, lutok::state& state) const166{167const detail::tree_key key = detail::parse_key(dotted_key);168const detail::base_node* raw_node = _root->lookup_ro(key, 0);169try {170const leaf_node& child = dynamic_cast< const leaf_node& >(*raw_node);171child.push_lua(state);172} catch (const std::bad_cast& unused_error) {173throw unknown_key_error(key);174}175}176177178/// Sets a leaf node's value from a value in the Lua stack.179///180/// \param dotted_key The key to be set.181/// \param state The Lua state from which to retrieve the value.182/// \param value_index The position in the Lua stack holding the value.183///184/// \throw invalid_key_error If the provided key has an invalid format.185/// \throw invalid_key_value If the value mismatches the node type.186/// \throw unknown_key_error If the provided key is unknown.187void188config::tree::set_lua(const std::string& dotted_key, lutok::state& state,189const int value_index)190{191const detail::tree_key key = detail::parse_key(dotted_key);192try {193detail::base_node* raw_node = _root->lookup_rw(194key, 0, detail::new_node< string_node >);195leaf_node& child = dynamic_cast< leaf_node& >(*raw_node);196child.set_lua(state, value_index);197} catch (const unknown_key_error& e) {198if (_strict)199throw e;200} catch (const value_error& e) {201throw invalid_key_value(key, e.what());202} catch (const std::bad_cast& unused_error) {203throw invalid_key_value(key, "Type mismatch");204}205}206207208/// Gets the value of a node as a plain string.209///210/// \param dotted_key The key to be looked up.211///212/// \return The value of the located node as a string.213///214/// \throw invalid_key_error If the provided key has an invalid format.215/// \throw unknown_key_error If the provided key is unknown.216std::string217config::tree::lookup_string(const std::string& dotted_key) const218{219const detail::tree_key key = detail::parse_key(dotted_key);220const detail::base_node* raw_node = _root->lookup_ro(key, 0);221try {222const leaf_node& child = dynamic_cast< const leaf_node& >(*raw_node);223return child.to_string();224} catch (const std::bad_cast& unused_error) {225throw unknown_key_error(key);226}227}228229230/// Sets the value of a leaf addressed by its key from a string value.231///232/// This respects the native types of all the nodes that have been predefined.233/// For new nodes under a dynamic subtree, this has no mechanism of determining234/// what type they need to have, so they are created as plain string nodes.235///236/// \param dotted_key The key to be registered in dotted representation.237/// \param raw_value The string representation of the value to set the node to.238///239/// \throw invalid_key_error If the provided key has an invalid format.240/// \throw invalid_key_value If the value mismatches the node type.241/// \throw unknown_key_error If the provided key is unknown.242void243config::tree::set_string(const std::string& dotted_key,244const std::string& raw_value)245{246const detail::tree_key key = detail::parse_key(dotted_key);247try {248detail::base_node* raw_node = _root->lookup_rw(249key, 0, detail::new_node< string_node >);250leaf_node& child = dynamic_cast< leaf_node& >(*raw_node);251child.set_string(raw_value);252} catch (const unknown_key_error& e) {253if (_strict)254throw e;255} catch (const value_error& e) {256throw invalid_key_value(key, e.what());257} catch (const std::bad_cast& unused_error) {258throw invalid_key_value(key, "Type mismatch");259}260}261262263/// Converts the tree to a collection of key/value string pairs.264///265/// \param dotted_key Subtree from which to start the export.266/// \param strip_key If true, remove the dotted_key prefix from the resulting267/// properties.268///269/// \return A map of keys to values in their textual representation.270///271/// \throw invalid_key_error If the provided key has an invalid format.272/// \throw unknown_key_error If the provided key is unknown.273/// \throw value_error If the provided key points to a leaf.274config::properties_map275config::tree::all_properties(const std::string& dotted_key,276const bool strip_key) const277{278PRE(!strip_key || !dotted_key.empty());279280properties_map properties;281282detail::tree_key key;283const detail::base_node* raw_node;284if (dotted_key.empty()) {285raw_node = _root.get();286} else {287key = detail::parse_key(dotted_key);288raw_node = _root->lookup_ro(key, 0);289}290try {291const detail::inner_node& child =292dynamic_cast< const detail::inner_node& >(*raw_node);293child.all_properties(properties, key);294} catch (const std::bad_cast& unused_error) {295INV(!dotted_key.empty());296throw value_error(F("Cannot export properties from a leaf node; "297"'%s' given") % dotted_key);298}299300if (strip_key) {301properties_map stripped;302for (properties_map::const_iterator iter = properties.begin();303iter != properties.end(); ++iter) {304stripped[(*iter).first.substr(dotted_key.length() + 1)] =305(*iter).second;306}307properties = stripped;308}309310return properties;311}312313314/// Equality comparator.315///316/// \param other The other object to compare this one to.317///318/// \return True if this object and other are equal; false otherwise.319bool320config::tree::operator==(const tree& other) const321{322// TODO(jmmv): Would be nicer to perform the comparison directly on the323// nodes, instead of exporting the values to strings first.324return _root == other._root || all_properties() == other.all_properties();325}326327328/// Inequality comparator.329///330/// \param other The other object to compare this one to.331///332/// \return True if this object and other are different; false otherwise.333bool334config::tree::operator!=(const tree& other) const335{336return !(*this == other);337}338339340