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.
tree(void)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.
tree(detail::static_inner_node * root)49 config::tree::tree(detail::static_inner_node* root) :
50     _root(root)
51 {
52 }
53 
54 
55 /// Destructor.
~tree(void)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
deep_copy(void) const65 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
define_dynamic(const std::string & dotted_key)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
is_set(const std::string & dotted_key) const107 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
push_lua(const std::string & dotted_key,lutok::state & state) const133 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
set_lua(const std::string & dotted_key,lutok::state & state,const int value_index)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
lookup_string(const std::string & dotted_key) const181 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
set_string(const std::string & dotted_key,const std::string & raw_value)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
all_properties(const std::string & dotted_key,const bool strip_key) const235 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
operator ==(const tree & other) const280 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
operator !=(const tree & other) const294 config::tree::operator!=(const tree& other) const
295 {
296     return !(*this == other);
297 }
298