xref: /netbsd-src/external/bsd/kyua-cli/dist/utils/config/tree.cpp (revision 6b3a42af15b5e090c339512c790dd68f3d11a9d8)
1*6b3a42afSjmmv // Copyright 2012 Google Inc.
2*6b3a42afSjmmv // All rights reserved.
3*6b3a42afSjmmv //
4*6b3a42afSjmmv // Redistribution and use in source and binary forms, with or without
5*6b3a42afSjmmv // modification, are permitted provided that the following conditions are
6*6b3a42afSjmmv // met:
7*6b3a42afSjmmv //
8*6b3a42afSjmmv // * Redistributions of source code must retain the above copyright
9*6b3a42afSjmmv //   notice, this list of conditions and the following disclaimer.
10*6b3a42afSjmmv // * Redistributions in binary form must reproduce the above copyright
11*6b3a42afSjmmv //   notice, this list of conditions and the following disclaimer in the
12*6b3a42afSjmmv //   documentation and/or other materials provided with the distribution.
13*6b3a42afSjmmv // * Neither the name of Google Inc. nor the names of its contributors
14*6b3a42afSjmmv //   may be used to endorse or promote products derived from this software
15*6b3a42afSjmmv //   without specific prior written permission.
16*6b3a42afSjmmv //
17*6b3a42afSjmmv // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18*6b3a42afSjmmv // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19*6b3a42afSjmmv // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20*6b3a42afSjmmv // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21*6b3a42afSjmmv // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22*6b3a42afSjmmv // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23*6b3a42afSjmmv // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24*6b3a42afSjmmv // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25*6b3a42afSjmmv // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26*6b3a42afSjmmv // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27*6b3a42afSjmmv // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28*6b3a42afSjmmv 
29*6b3a42afSjmmv #include "utils/config/tree.ipp"
30*6b3a42afSjmmv 
31*6b3a42afSjmmv #include "utils/config/exceptions.hpp"
32*6b3a42afSjmmv #include "utils/config/keys.hpp"
33*6b3a42afSjmmv #include "utils/config/nodes.ipp"
34*6b3a42afSjmmv #include "utils/format/macros.hpp"
35*6b3a42afSjmmv 
36*6b3a42afSjmmv namespace config = utils::config;
37*6b3a42afSjmmv 
38*6b3a42afSjmmv 
39*6b3a42afSjmmv /// Constructor.
tree(void)40*6b3a42afSjmmv config::tree::tree(void) :
41*6b3a42afSjmmv     _root(new detail::static_inner_node())
42*6b3a42afSjmmv {
43*6b3a42afSjmmv }
44*6b3a42afSjmmv 
45*6b3a42afSjmmv 
46*6b3a42afSjmmv /// Constructor with a non-empty root.
47*6b3a42afSjmmv ///
48*6b3a42afSjmmv /// \param root The root to the tree to be owned by this instance.
tree(detail::static_inner_node * root)49*6b3a42afSjmmv config::tree::tree(detail::static_inner_node* root) :
50*6b3a42afSjmmv     _root(root)
51*6b3a42afSjmmv {
52*6b3a42afSjmmv }
53*6b3a42afSjmmv 
54*6b3a42afSjmmv 
55*6b3a42afSjmmv /// Destructor.
~tree(void)56*6b3a42afSjmmv config::tree::~tree(void)
57*6b3a42afSjmmv {
58*6b3a42afSjmmv }
59*6b3a42afSjmmv 
60*6b3a42afSjmmv 
61*6b3a42afSjmmv /// Generates a deep copy of the input tree.
62*6b3a42afSjmmv ///
63*6b3a42afSjmmv /// \return A new tree that is an exact copy of this tree.
64*6b3a42afSjmmv config::tree
deep_copy(void) const65*6b3a42afSjmmv config::tree::deep_copy(void) const
66*6b3a42afSjmmv {
67*6b3a42afSjmmv     detail::static_inner_node* new_root =
68*6b3a42afSjmmv         dynamic_cast< detail::static_inner_node* >(_root->deep_copy());
69*6b3a42afSjmmv     return config::tree(new_root);
70*6b3a42afSjmmv }
71*6b3a42afSjmmv 
72*6b3a42afSjmmv 
73*6b3a42afSjmmv /// Registers a node as being dynamic.
74*6b3a42afSjmmv ///
75*6b3a42afSjmmv /// This operation creates the given key as an inner node.  Further set
76*6b3a42afSjmmv /// operations that trespass this node will automatically create any missing
77*6b3a42afSjmmv /// keys.
78*6b3a42afSjmmv ///
79*6b3a42afSjmmv /// This method does not raise errors on invalid/unknown keys or other
80*6b3a42afSjmmv /// tree-related issues.  The reasons is that define() is a method that does not
81*6b3a42afSjmmv /// depend on user input: it is intended to pre-populate the tree with a
82*6b3a42afSjmmv /// specific structure, and that happens once at coding time.
83*6b3a42afSjmmv ///
84*6b3a42afSjmmv /// \param dotted_key The key to be registered in dotted representation.
85*6b3a42afSjmmv void
define_dynamic(const std::string & dotted_key)86*6b3a42afSjmmv config::tree::define_dynamic(const std::string& dotted_key)
87*6b3a42afSjmmv {
88*6b3a42afSjmmv     try {
89*6b3a42afSjmmv         const detail::tree_key key = detail::parse_key(dotted_key);
90*6b3a42afSjmmv         _root->define(key, 0, detail::new_node< detail::dynamic_inner_node >);
91*6b3a42afSjmmv     } catch (const error& e) {
92*6b3a42afSjmmv         UNREACHABLE_MSG("define() failing due to key errors is a programming "
93*6b3a42afSjmmv                         "mistake: " + std::string(e.what()));
94*6b3a42afSjmmv     }
95*6b3a42afSjmmv }
96*6b3a42afSjmmv 
97*6b3a42afSjmmv 
98*6b3a42afSjmmv /// Checks if a given node is set.
99*6b3a42afSjmmv ///
100*6b3a42afSjmmv /// \param dotted_key The key to be checked.
101*6b3a42afSjmmv ///
102*6b3a42afSjmmv /// \return True if the key is set to a specific value (not just defined).
103*6b3a42afSjmmv /// False if the key is not set or if the key does not exist.
104*6b3a42afSjmmv ///
105*6b3a42afSjmmv /// \throw invalid_key_error If the provided key has an invalid format.
106*6b3a42afSjmmv bool
is_set(const std::string & dotted_key) const107*6b3a42afSjmmv config::tree::is_set(const std::string& dotted_key) const
108*6b3a42afSjmmv {
109*6b3a42afSjmmv     const detail::tree_key key = detail::parse_key(dotted_key);
110*6b3a42afSjmmv     try {
111*6b3a42afSjmmv         const detail::base_node* raw_node = _root->lookup_ro(key, 0);
112*6b3a42afSjmmv         try {
113*6b3a42afSjmmv             const leaf_node& child = dynamic_cast< const leaf_node& >(
114*6b3a42afSjmmv                 *raw_node);
115*6b3a42afSjmmv             return child.is_set();
116*6b3a42afSjmmv         } catch (const std::bad_cast& unused_error) {
117*6b3a42afSjmmv             return false;
118*6b3a42afSjmmv         }
119*6b3a42afSjmmv     } catch (const unknown_key_error& unused_error) {
120*6b3a42afSjmmv         return false;
121*6b3a42afSjmmv     }
122*6b3a42afSjmmv }
123*6b3a42afSjmmv 
124*6b3a42afSjmmv 
125*6b3a42afSjmmv /// Pushes a leaf node's value onto the Lua stack.
126*6b3a42afSjmmv ///
127*6b3a42afSjmmv /// \param dotted_key The key to be pushed.
128*6b3a42afSjmmv /// \param state The Lua state into which to push the key's value.
129*6b3a42afSjmmv ///
130*6b3a42afSjmmv /// \throw invalid_key_error If the provided key has an invalid format.
131*6b3a42afSjmmv /// \throw unknown_key_error If the provided key is unknown.
132*6b3a42afSjmmv void
push_lua(const std::string & dotted_key,lutok::state & state) const133*6b3a42afSjmmv config::tree::push_lua(const std::string& dotted_key, lutok::state& state) const
134*6b3a42afSjmmv {
135*6b3a42afSjmmv     const detail::tree_key key = detail::parse_key(dotted_key);
136*6b3a42afSjmmv     const detail::base_node* raw_node = _root->lookup_ro(key, 0);
137*6b3a42afSjmmv     try {
138*6b3a42afSjmmv         const leaf_node& child = dynamic_cast< const leaf_node& >(*raw_node);
139*6b3a42afSjmmv         child.push_lua(state);
140*6b3a42afSjmmv     } catch (const std::bad_cast& unused_error) {
141*6b3a42afSjmmv         throw unknown_key_error(key);
142*6b3a42afSjmmv     }
143*6b3a42afSjmmv }
144*6b3a42afSjmmv 
145*6b3a42afSjmmv 
146*6b3a42afSjmmv /// Sets a leaf node's value from a value in the Lua stack.
147*6b3a42afSjmmv ///
148*6b3a42afSjmmv /// \param dotted_key The key to be set.
149*6b3a42afSjmmv /// \param state The Lua state from which to retrieve the value.
150*6b3a42afSjmmv /// \param value_index The position in the Lua stack holding the value.
151*6b3a42afSjmmv ///
152*6b3a42afSjmmv /// \throw invalid_key_error If the provided key has an invalid format.
153*6b3a42afSjmmv /// \throw unknown_key_error If the provided key is unknown.
154*6b3a42afSjmmv /// \throw value_error If the value mismatches the node type.
155*6b3a42afSjmmv void
set_lua(const std::string & dotted_key,lutok::state & state,const int value_index)156*6b3a42afSjmmv config::tree::set_lua(const std::string& dotted_key, lutok::state& state,
157*6b3a42afSjmmv                       const int value_index)
158*6b3a42afSjmmv {
159*6b3a42afSjmmv     const detail::tree_key key = detail::parse_key(dotted_key);
160*6b3a42afSjmmv     detail::base_node* raw_node = _root->lookup_rw(
161*6b3a42afSjmmv         key, 0, detail::new_node< string_node >);
162*6b3a42afSjmmv     try {
163*6b3a42afSjmmv         leaf_node& child = dynamic_cast< leaf_node& >(*raw_node);
164*6b3a42afSjmmv         child.set_lua(state, value_index);
165*6b3a42afSjmmv     } catch (const std::bad_cast& unused_error) {
166*6b3a42afSjmmv         throw value_error(F("Invalid value for key '%s'") %
167*6b3a42afSjmmv                           detail::flatten_key(key));
168*6b3a42afSjmmv     }
169*6b3a42afSjmmv }
170*6b3a42afSjmmv 
171*6b3a42afSjmmv 
172*6b3a42afSjmmv /// Gets the value of a node as a plain string.
173*6b3a42afSjmmv ///
174*6b3a42afSjmmv /// \param dotted_key The key to be looked up.
175*6b3a42afSjmmv ///
176*6b3a42afSjmmv /// \return The value of the located node as a string.
177*6b3a42afSjmmv ///
178*6b3a42afSjmmv /// \throw invalid_key_error If the provided key has an invalid format.
179*6b3a42afSjmmv /// \throw unknown_key_error If the provided key is unknown.
180*6b3a42afSjmmv std::string
lookup_string(const std::string & dotted_key) const181*6b3a42afSjmmv config::tree::lookup_string(const std::string& dotted_key) const
182*6b3a42afSjmmv {
183*6b3a42afSjmmv     const detail::tree_key key = detail::parse_key(dotted_key);
184*6b3a42afSjmmv     const detail::base_node* raw_node = _root->lookup_ro(key, 0);
185*6b3a42afSjmmv     try {
186*6b3a42afSjmmv         const leaf_node& child = dynamic_cast< const leaf_node& >(*raw_node);
187*6b3a42afSjmmv         return child.to_string();
188*6b3a42afSjmmv     } catch (const std::bad_cast& unused_error) {
189*6b3a42afSjmmv         throw unknown_key_error(key);
190*6b3a42afSjmmv     }
191*6b3a42afSjmmv }
192*6b3a42afSjmmv 
193*6b3a42afSjmmv 
194*6b3a42afSjmmv /// Sets the value of a leaf addressed by its key from a string value.
195*6b3a42afSjmmv ///
196*6b3a42afSjmmv /// This respects the native types of all the nodes that have been predefined.
197*6b3a42afSjmmv /// For new nodes under a dynamic subtree, this has no mechanism of determining
198*6b3a42afSjmmv /// what type they need to have, so they are created as plain string nodes.
199*6b3a42afSjmmv ///
200*6b3a42afSjmmv /// \param dotted_key The key to be registered in dotted representation.
201*6b3a42afSjmmv /// \param raw_value The string representation of the value to set the node to.
202*6b3a42afSjmmv ///
203*6b3a42afSjmmv /// \throw invalid_key_error If the provided key has an invalid format.
204*6b3a42afSjmmv /// \throw unknown_key_error If the provided key is unknown.
205*6b3a42afSjmmv /// \throw value_error If the value mismatches the node type.
206*6b3a42afSjmmv void
set_string(const std::string & dotted_key,const std::string & raw_value)207*6b3a42afSjmmv config::tree::set_string(const std::string& dotted_key,
208*6b3a42afSjmmv                          const std::string& raw_value)
209*6b3a42afSjmmv {
210*6b3a42afSjmmv     const detail::tree_key key = detail::parse_key(dotted_key);
211*6b3a42afSjmmv     detail::base_node* raw_node = _root->lookup_rw(
212*6b3a42afSjmmv         key, 0, detail::new_node< string_node >);
213*6b3a42afSjmmv     try {
214*6b3a42afSjmmv         leaf_node& child = dynamic_cast< leaf_node& >(*raw_node);
215*6b3a42afSjmmv         child.set_string(raw_value);
216*6b3a42afSjmmv     } catch (const std::bad_cast& unused_error) {
217*6b3a42afSjmmv         throw value_error(F("Invalid value for key '%s'") %
218*6b3a42afSjmmv                           detail::flatten_key(key));
219*6b3a42afSjmmv     }
220*6b3a42afSjmmv }
221*6b3a42afSjmmv 
222*6b3a42afSjmmv 
223*6b3a42afSjmmv /// Converts the tree to a collection of key/value string pairs.
224*6b3a42afSjmmv ///
225*6b3a42afSjmmv /// \param dotted_key Subtree from which to start the export.
226*6b3a42afSjmmv /// \param strip_key If true, remove the dotted_key prefix from the resulting
227*6b3a42afSjmmv ///     properties.
228*6b3a42afSjmmv ///
229*6b3a42afSjmmv /// \return A map of keys to values in their textual representation.
230*6b3a42afSjmmv ///
231*6b3a42afSjmmv /// \throw invalid_key_error If the provided key has an invalid format.
232*6b3a42afSjmmv /// \throw unknown_key_error If the provided key is unknown.
233*6b3a42afSjmmv /// \throw value_error If the provided key points to a leaf.
234*6b3a42afSjmmv config::properties_map
all_properties(const std::string & dotted_key,const bool strip_key) const235*6b3a42afSjmmv config::tree::all_properties(const std::string& dotted_key,
236*6b3a42afSjmmv                              const bool strip_key) const
237*6b3a42afSjmmv {
238*6b3a42afSjmmv     PRE(!strip_key || !dotted_key.empty());
239*6b3a42afSjmmv 
240*6b3a42afSjmmv     properties_map properties;
241*6b3a42afSjmmv 
242*6b3a42afSjmmv     detail::tree_key key;
243*6b3a42afSjmmv     const detail::base_node* raw_node;
244*6b3a42afSjmmv     if (dotted_key.empty()) {
245*6b3a42afSjmmv         raw_node = _root.get();
246*6b3a42afSjmmv     } else {
247*6b3a42afSjmmv         key = detail::parse_key(dotted_key);
248*6b3a42afSjmmv         raw_node = _root->lookup_ro(key, 0);
249*6b3a42afSjmmv     }
250*6b3a42afSjmmv     try {
251*6b3a42afSjmmv         const detail::inner_node& child =
252*6b3a42afSjmmv             dynamic_cast< const detail::inner_node& >(*raw_node);
253*6b3a42afSjmmv         child.all_properties(properties, key);
254*6b3a42afSjmmv     } catch (const std::bad_cast& unused_error) {
255*6b3a42afSjmmv         INV(!dotted_key.empty());
256*6b3a42afSjmmv         throw value_error(F("Cannot export properties from a leaf node; "
257*6b3a42afSjmmv                             "'%s' given") % dotted_key);
258*6b3a42afSjmmv     }
259*6b3a42afSjmmv 
260*6b3a42afSjmmv     if (strip_key) {
261*6b3a42afSjmmv         properties_map stripped;
262*6b3a42afSjmmv         for (properties_map::const_iterator iter = properties.begin();
263*6b3a42afSjmmv              iter != properties.end(); ++iter) {
264*6b3a42afSjmmv             stripped[(*iter).first.substr(dotted_key.length() + 1)] =
265*6b3a42afSjmmv                 (*iter).second;
266*6b3a42afSjmmv         }
267*6b3a42afSjmmv         properties = stripped;
268*6b3a42afSjmmv     }
269*6b3a42afSjmmv 
270*6b3a42afSjmmv     return properties;
271*6b3a42afSjmmv }
272*6b3a42afSjmmv 
273*6b3a42afSjmmv 
274*6b3a42afSjmmv /// Equality comparator.
275*6b3a42afSjmmv ///
276*6b3a42afSjmmv /// \param other The other object to compare this one to.
277*6b3a42afSjmmv ///
278*6b3a42afSjmmv /// \return True if this object and other are equal; false otherwise.
279*6b3a42afSjmmv bool
operator ==(const tree & other) const280*6b3a42afSjmmv config::tree::operator==(const tree& other) const
281*6b3a42afSjmmv {
282*6b3a42afSjmmv     // TODO(jmmv): Would be nicer to perform the comparison directly on the
283*6b3a42afSjmmv     // nodes, instead of exporting the values to strings first.
284*6b3a42afSjmmv     return _root == other._root || all_properties() == other.all_properties();
285*6b3a42afSjmmv }
286*6b3a42afSjmmv 
287*6b3a42afSjmmv 
288*6b3a42afSjmmv /// Inequality comparator.
289*6b3a42afSjmmv ///
290*6b3a42afSjmmv /// \param other The other object to compare this one to.
291*6b3a42afSjmmv ///
292*6b3a42afSjmmv /// \return True if this object and other are different; false otherwise.
293*6b3a42afSjmmv bool
operator !=(const tree & other) const294*6b3a42afSjmmv config::tree::operator!=(const tree& other) const
295*6b3a42afSjmmv {
296*6b3a42afSjmmv     return !(*this == other);
297*6b3a42afSjmmv }
298