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