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