Path: blob/main/contrib/kyua/utils/config/nodes.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/nodes.ipp"2930#include <memory>3132#include <lutok/state.ipp>3334#include "utils/config/exceptions.hpp"35#include "utils/config/keys.hpp"36#include "utils/format/macros.hpp"3738namespace config = utils::config;394041/// Destructor.42config::detail::base_node::~base_node(void)43{44}454647/// Constructor.48///49/// \param dynamic_ Whether the node is dynamic or not.50config::detail::inner_node::inner_node(const bool dynamic_) :51_dynamic(dynamic_)52{53}545556/// Destructor.57config::detail::inner_node::~inner_node(void)58{59for (children_map::const_iterator iter = _children.begin();60iter != _children.end(); ++iter)61delete (*iter).second;62}636465/// Fills the given node with a copy of this node's data.66///67/// \param node The node to fill. Should be the fresh return value of a68/// deep_copy() operation.69void70config::detail::inner_node::copy_into(inner_node* node) const71{72node->_dynamic = _dynamic;73for (children_map::const_iterator iter = _children.begin();74iter != _children.end(); ++iter) {75base_node* new_node = (*iter).second->deep_copy();76try {77node->_children[(*iter).first] = new_node;78} catch (...) {79delete new_node;80throw;81}82}83}848586/// Combines two children sets, preferring the keys in the first set only.87///88/// This operation is not symmetrical on c1 and c2. The caller is responsible89/// for invoking this twice so that the two key sets are combined if they happen90/// to differ.91///92/// \param key Key to this node.93/// \param c1 First children set.94/// \param c2 First children set.95/// \param [in,out] node The node to combine into.96///97/// \throw bad_combination_error If the two nodes cannot be combined.98void99config::detail::inner_node::combine_children_into(100const tree_key& key,101const children_map& c1, const children_map& c2,102inner_node* node) const103{104for (children_map::const_iterator iter1 = c1.begin();105iter1 != c1.end(); ++iter1) {106const std::string& name = (*iter1).first;107108if (node->_children.find(name) != node->_children.end()) {109continue;110}111112std::unique_ptr< base_node > new_node;113114children_map::const_iterator iter2 = c2.find(name);115if (iter2 == c2.end()) {116new_node.reset((*iter1).second->deep_copy());117} else {118tree_key child_key = key;119child_key.push_back(name);120new_node.reset((*iter1).second->combine(child_key,121(*iter2).second));122}123124node->_children[name] = new_node.release();125}126}127128129/// Combines this inner node with another inner node onto a new node.130///131/// The "dynamic" property is inherited by the new node if either of the two132/// nodes are dynamic.133///134/// \param key Key to this node.135/// \param other_base The node to combine with.136/// \param [in,out] node The node to combine into.137///138/// \throw bad_combination_error If the two nodes cannot be combined.139void140config::detail::inner_node::combine_into(const tree_key& key,141const base_node* other_base,142inner_node* node) const143{144try {145const inner_node& other = dynamic_cast< const inner_node& >(146*other_base);147148node->_dynamic = _dynamic || other._dynamic;149150combine_children_into(key, _children, other._children, node);151combine_children_into(key, other._children, _children, node);152} catch (const std::bad_cast& unused_e) {153throw config::bad_combination_error(154key, "'%s' is an inner node in the base tree but a leaf node in "155"the overrides treee");156}157}158159160/// Finds a node without creating it if not found.161///162/// This recursive algorithm traverses the tree searching for a particular key.163/// The returned node is constant, so this can only be used for querying164/// purposes. For this reason, this algorithm does not create intermediate165/// nodes if they don't exist (as would be necessary to set a new node).166///167/// \param key The key to be queried.168/// \param key_pos The current level within the key to be examined.169///170/// \return A reference to the located node, if successful.171///172/// \throw unknown_key_error If the provided key is unknown.173const config::detail::base_node*174config::detail::inner_node::lookup_ro(const tree_key& key,175const tree_key::size_type key_pos) const176{177PRE(key_pos < key.size());178179const children_map::const_iterator child_iter = _children.find(180key[key_pos]);181if (child_iter == _children.end())182throw unknown_key_error(key);183184if (key_pos == key.size() - 1) {185return (*child_iter).second;186} else {187PRE(key_pos < key.size() - 1);188try {189const inner_node& child = dynamic_cast< const inner_node& >(190*(*child_iter).second);191return child.lookup_ro(key, key_pos + 1);192} catch (const std::bad_cast& e) {193throw unknown_key_error(194key, "Cannot address incomplete configuration property '%s'");195}196}197}198199200/// Finds a node and creates it if not found.201///202/// This recursive algorithm traverses the tree searching for a particular key,203/// creating any intermediate nodes if they do not already exist (for the case204/// of dynamic inner nodes). The returned node is non-constant, so this can be205/// used by the algorithms that set key values.206///207/// \param key The key to be queried.208/// \param key_pos The current level within the key to be examined.209/// \param new_node A function that returns a new leaf node of the desired210/// type. This is only called if the leaf cannot be found, but it has211/// already been defined.212///213/// \return A reference to the located node, if successful.214///215/// \throw invalid_key_value If the resulting node of the search would be an216/// inner node.217/// \throw unknown_key_error If the provided key is unknown.218config::leaf_node*219config::detail::inner_node::lookup_rw(const tree_key& key,220const tree_key::size_type key_pos,221new_node_hook new_node)222{223PRE(key_pos < key.size());224225children_map::const_iterator child_iter = _children.find(key[key_pos]);226if (child_iter == _children.end()) {227if (_dynamic) {228base_node* const child = (key_pos == key.size() - 1) ?229static_cast< base_node* >(new_node()) :230static_cast< base_node* >(new dynamic_inner_node());231_children.insert(children_map::value_type(key[key_pos], child));232child_iter = _children.find(key[key_pos]);233} else {234throw unknown_key_error(key);235}236}237238if (key_pos == key.size() - 1) {239try {240leaf_node& child = dynamic_cast< leaf_node& >(241*(*child_iter).second);242return &child;243} catch (const std::bad_cast& unused_error) {244throw invalid_key_value(key, "Type mismatch");245}246} else {247PRE(key_pos < key.size() - 1);248try {249inner_node& child = dynamic_cast< inner_node& >(250*(*child_iter).second);251return child.lookup_rw(key, key_pos + 1, new_node);252} catch (const std::bad_cast& e) {253throw unknown_key_error(254key, "Cannot address incomplete configuration property '%s'");255}256}257}258259260/// Converts the subtree to a collection of key/value string pairs.261///262/// \param [out] properties The accumulator for the generated properties. The263/// contents of the map are only extended.264/// \param key The path to the current node.265void266config::detail::inner_node::all_properties(properties_map& properties,267const tree_key& key) const268{269for (children_map::const_iterator iter = _children.begin();270iter != _children.end(); ++iter) {271tree_key child_key = key;272child_key.push_back((*iter).first);273try {274leaf_node& child = dynamic_cast< leaf_node& >(*(*iter).second);275if (child.is_set())276properties[flatten_key(child_key)] = child.to_string();277} catch (const std::bad_cast& unused_error) {278inner_node& child = dynamic_cast< inner_node& >(*(*iter).second);279child.all_properties(properties, child_key);280}281}282}283284285/// Constructor.286config::detail::static_inner_node::static_inner_node(void) :287inner_node(false)288{289}290291292/// Copies the node.293///294/// \return A dynamically-allocated node.295config::detail::base_node*296config::detail::static_inner_node::deep_copy(void) const297{298std::unique_ptr< inner_node > new_node(new static_inner_node());299copy_into(new_node.get());300return new_node.release();301}302303304/// Combines this node with another one.305///306/// \param key Key to this node.307/// \param other The node to combine with.308///309/// \return A new node representing the combination.310///311/// \throw bad_combination_error If the two nodes cannot be combined.312config::detail::base_node*313config::detail::static_inner_node::combine(const tree_key& key,314const base_node* other) const315{316std::unique_ptr< inner_node > new_node(new static_inner_node());317combine_into(key, other, new_node.get());318return new_node.release();319}320321322/// Registers a key as valid and having a specific type.323///324/// This method does not raise errors on invalid/unknown keys or other325/// tree-related issues. The reasons is that define() is a method that does not326/// depend on user input: it is intended to pre-populate the tree with a327/// specific structure, and that happens once at coding time.328///329/// \param key The key to be registered.330/// \param key_pos The current level within the key to be examined.331/// \param new_node A function that returns a new leaf node of the desired332/// type.333void334config::detail::static_inner_node::define(const tree_key& key,335const tree_key::size_type key_pos,336new_node_hook new_node)337{338PRE(key_pos < key.size());339340if (key_pos == key.size() - 1) {341PRE_MSG(_children.find(key[key_pos]) == _children.end(),342"Key already defined");343_children.insert(children_map::value_type(key[key_pos], new_node()));344} else {345PRE(key_pos < key.size() - 1);346const children_map::const_iterator child_iter = _children.find(347key[key_pos]);348349if (child_iter == _children.end()) {350static_inner_node* const child_ptr = new static_inner_node();351_children.insert(children_map::value_type(key[key_pos], child_ptr));352child_ptr->define(key, key_pos + 1, new_node);353} else {354try {355static_inner_node& child = dynamic_cast< static_inner_node& >(356*(*child_iter).second);357child.define(key, key_pos + 1, new_node);358} catch (const std::bad_cast& e) {359UNREACHABLE;360}361}362}363}364365366/// Constructor.367config::detail::dynamic_inner_node::dynamic_inner_node(void) :368inner_node(true)369{370}371372373/// Copies the node.374///375/// \return A dynamically-allocated node.376config::detail::base_node*377config::detail::dynamic_inner_node::deep_copy(void) const378{379std::unique_ptr< inner_node > new_node(new dynamic_inner_node());380copy_into(new_node.get());381return new_node.release();382}383384385/// Combines this node with another one.386///387/// \param key Key to this node.388/// \param other The node to combine with.389///390/// \return A new node representing the combination.391///392/// \throw bad_combination_error If the two nodes cannot be combined.393config::detail::base_node*394config::detail::dynamic_inner_node::combine(const tree_key& key,395const base_node* other) const396{397std::unique_ptr< inner_node > new_node(new dynamic_inner_node());398combine_into(key, other, new_node.get());399return new_node.release();400}401402403/// Destructor.404config::leaf_node::~leaf_node(void)405{406}407408409/// Combines this node with another one.410///411/// \param key Key to this node.412/// \param other_base The node to combine with.413///414/// \return A new node representing the combination.415///416/// \throw bad_combination_error If the two nodes cannot be combined.417config::detail::base_node*418config::leaf_node::combine(const detail::tree_key& key,419const base_node* other_base) const420{421try {422const leaf_node& other = dynamic_cast< const leaf_node& >(*other_base);423424if (other.is_set()) {425return other.deep_copy();426} else {427return deep_copy();428}429} catch (const std::bad_cast& unused_e) {430throw config::bad_combination_error(431key, "'%s' is a leaf node in the base tree but an inner node in "432"the overrides treee");433}434}435436437/// Copies the node.438///439/// \return A dynamically-allocated node.440config::detail::base_node*441config::bool_node::deep_copy(void) const442{443std::unique_ptr< bool_node > new_node(new bool_node());444new_node->_value = _value;445return new_node.release();446}447448449/// Pushes the node's value onto the Lua stack.450///451/// \param state The Lua state onto which to push the value.452void453config::bool_node::push_lua(lutok::state& state) const454{455state.push_boolean(value());456}457458459/// Sets the value of the node from an entry in the Lua stack.460///461/// \param state The Lua state from which to get the value.462/// \param value_index The stack index in which the value resides.463///464/// \throw value_error If the value in state(value_index) cannot be465/// processed by this node.466void467config::bool_node::set_lua(lutok::state& state, const int value_index)468{469if (state.is_boolean(value_index))470set(state.to_boolean(value_index));471else472throw value_error("Not a boolean");473}474475476/// Copies the node.477///478/// \return A dynamically-allocated node.479config::detail::base_node*480config::int_node::deep_copy(void) const481{482std::unique_ptr< int_node > new_node(new int_node());483new_node->_value = _value;484return new_node.release();485}486487488/// Pushes the node's value onto the Lua stack.489///490/// \param state The Lua state onto which to push the value.491void492config::int_node::push_lua(lutok::state& state) const493{494state.push_integer(value());495}496497498/// Sets the value of the node from an entry in the Lua stack.499///500/// \param state The Lua state from which to get the value.501/// \param value_index The stack index in which the value resides.502///503/// \throw value_error If the value in state(value_index) cannot be504/// processed by this node.505void506config::int_node::set_lua(lutok::state& state, const int value_index)507{508if (state.is_number(value_index))509set(state.to_integer(value_index));510else511throw value_error("Not an integer");512}513514515/// Checks a given value for validity.516///517/// \param new_value The value to validate.518///519/// \throw value_error If the value is not valid.520void521config::positive_int_node::validate(const value_type& new_value) const522{523if (new_value <= 0)524throw value_error("Must be a positive integer");525}526527528/// Copies the node.529///530/// \return A dynamically-allocated node.531config::detail::base_node*532config::string_node::deep_copy(void) const533{534std::unique_ptr< string_node > new_node(new string_node());535new_node->_value = _value;536return new_node.release();537}538539540/// Pushes the node's value onto the Lua stack.541///542/// \param state The Lua state onto which to push the value.543void544config::string_node::push_lua(lutok::state& state) const545{546state.push_string(value());547}548549550/// Sets the value of the node from an entry in the Lua stack.551///552/// \param state The Lua state from which to get the value.553/// \param value_index The stack index in which the value resides.554///555/// \throw value_error If the value in state(value_index) cannot be556/// processed by this node.557void558config::string_node::set_lua(lutok::state& state, const int value_index)559{560if (state.is_string(value_index))561set(state.to_string(value_index));562else563throw value_error("Not a string");564}565566567/// Copies the node.568///569/// \return A dynamically-allocated node.570config::detail::base_node*571config::strings_set_node::deep_copy(void) const572{573std::unique_ptr< strings_set_node > new_node(new strings_set_node());574new_node->_value = _value;575return new_node.release();576}577578579/// Converts a single word to the native type.580///581/// \param raw_value The value to parse.582///583/// \return The parsed value.584std::string585config::strings_set_node::parse_one(const std::string& raw_value) const586{587return raw_value;588}589590591