1 // Copyright 2012 Google Inc.
2 // All rights reserved.
4 // Redistribution and use in source and binary forms, with or without
5 // modification, are permitted provided that the following conditions are
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.
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.
29 #include "utils/config/tree.ipp"
31 #include "utils/config/exceptions.hpp"
32 #include "utils/config/keys.hpp"
33 #include "utils/config/nodes.ipp"
34 #include "utils/format/macros.hpp"
36 namespace config
= utils::config
;
40 config::tree::tree(void) :
41 _root(new detail::static_inner_node())
46 /// Constructor with a non-empty root.
48 /// \param root The root to the tree to be owned by this instance.
49 config::tree::tree(detail::static_inner_node
* root
) :
56 config::tree::~tree(void)
61 /// Generates a deep copy of the input tree.
63 /// \return A new tree that is an exact copy of this tree.
65 config::tree::deep_copy(void) const
67 detail::static_inner_node
* new_root
=
68 dynamic_cast< detail::static_inner_node
* >(_root
->deep_copy());
69 return config::tree(new_root
);
73 /// Registers a node as being dynamic.
75 /// This operation creates the given key as an inner node. Further set
76 /// operations that trespass this node will automatically create any missing
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.
84 /// \param dotted_key The key to be registered in dotted representation.
86 config::tree::define_dynamic(const std::string
& dotted_key
)
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()));
98 /// Checks if a given node is set.
100 /// \param dotted_key The key to be checked.
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.
105 /// \throw invalid_key_error If the provided key has an invalid format.
107 config::tree::is_set(const std::string
& dotted_key
) const
109 const detail::tree_key key
= detail::parse_key(dotted_key
);
111 const detail::base_node
* raw_node
= _root
->lookup_ro(key
, 0);
113 const leaf_node
& child
= dynamic_cast< const leaf_node
& >(
115 return child
.is_set();
116 } catch (const std::bad_cast
& unused_error
) {
119 } catch (const unknown_key_error
& unused_error
) {
125 /// Pushes a leaf node's value onto the Lua stack.
127 /// \param dotted_key The key to be pushed.
128 /// \param state The Lua state into which to push the key's value.
130 /// \throw invalid_key_error If the provided key has an invalid format.
131 /// \throw unknown_key_error If the provided key is unknown.
133 config::tree::push_lua(const std::string
& dotted_key
, lutok::state
& state
) const
135 const detail::tree_key key
= detail::parse_key(dotted_key
);
136 const detail::base_node
* raw_node
= _root
->lookup_ro(key
, 0);
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
);
146 /// Sets a leaf node's value from a value in the Lua stack.
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.
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.
156 config::tree::set_lua(const std::string
& dotted_key
, lutok::state
& state
,
157 const int value_index
)
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
>);
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
));
172 /// Gets the value of a node as a plain string.
174 /// \param dotted_key The key to be looked up.
176 /// \return The value of the located node as a string.
178 /// \throw invalid_key_error If the provided key has an invalid format.
179 /// \throw unknown_key_error If the provided key is unknown.
181 config::tree::lookup_string(const std::string
& dotted_key
) const
183 const detail::tree_key key
= detail::parse_key(dotted_key
);
184 const detail::base_node
* raw_node
= _root
->lookup_ro(key
, 0);
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
);
194 /// Sets the value of a leaf addressed by its key from a string value.
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.
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.
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.
207 config::tree::set_string(const std::string
& dotted_key
,
208 const std::string
& raw_value
)
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
>);
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
));
223 /// Converts the tree to a collection of key/value string pairs.
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
229 /// \return A map of keys to values in their textual representation.
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
238 PRE(!strip_key
|| !dotted_key
.empty());
240 properties_map properties
;
242 detail::tree_key key
;
243 const detail::base_node
* raw_node
;
244 if (dotted_key
.empty()) {
245 raw_node
= _root
.get();
247 key
= detail::parse_key(dotted_key
);
248 raw_node
= _root
->lookup_ro(key
, 0);
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
);
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)] =
267 properties
= stripped
;
274 /// Equality comparator.
276 /// \param other The other object to compare this one to.
278 /// \return True if this object and other are equal; false otherwise.
280 config::tree::operator==(const tree
& other
) const
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();
288 /// Inequality comparator.
290 /// \param other The other object to compare this one to.
292 /// \return True if this object and other are different; false otherwise.
294 config::tree::operator!=(const tree
& other
) const
296 return !(*this == other
);