1*6a6c8299Sjmmv// Copyright 2012 Google Inc.
2*6a6c8299Sjmmv// All rights reserved.
3*6a6c8299Sjmmv//
4*6a6c8299Sjmmv// Redistribution and use in source and binary forms, with or without
5*6a6c8299Sjmmv// modification, are permitted provided that the following conditions are
6*6a6c8299Sjmmv// met:
7*6a6c8299Sjmmv//
8*6a6c8299Sjmmv// * Redistributions of source code must retain the above copyright
9*6a6c8299Sjmmv//   notice, this list of conditions and the following disclaimer.
10*6a6c8299Sjmmv// * Redistributions in binary form must reproduce the above copyright
11*6a6c8299Sjmmv//   notice, this list of conditions and the following disclaimer in the
12*6a6c8299Sjmmv//   documentation and/or other materials provided with the distribution.
13*6a6c8299Sjmmv// * Neither the name of Google Inc. nor the names of its contributors
14*6a6c8299Sjmmv//   may be used to endorse or promote products derived from this software
15*6a6c8299Sjmmv//   without specific prior written permission.
16*6a6c8299Sjmmv//
17*6a6c8299Sjmmv// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18*6a6c8299Sjmmv// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19*6a6c8299Sjmmv// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20*6a6c8299Sjmmv// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21*6a6c8299Sjmmv// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22*6a6c8299Sjmmv// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23*6a6c8299Sjmmv// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24*6a6c8299Sjmmv// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25*6a6c8299Sjmmv// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26*6a6c8299Sjmmv// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27*6a6c8299Sjmmv// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28*6a6c8299Sjmmv
29*6a6c8299Sjmmv#include "utils/config/nodes.hpp"
30*6a6c8299Sjmmv
31*6a6c8299Sjmmv#if !defined(UTILS_CONFIG_NODES_IPP)
32*6a6c8299Sjmmv#define UTILS_CONFIG_NODES_IPP
33*6a6c8299Sjmmv
34*6a6c8299Sjmmv#include <memory>
35*6a6c8299Sjmmv#include <typeinfo>
36*6a6c8299Sjmmv
37*6a6c8299Sjmmv#include "utils/config/exceptions.hpp"
38*6a6c8299Sjmmv#include "utils/defs.hpp"
39*6a6c8299Sjmmv#include "utils/format/macros.hpp"
40*6a6c8299Sjmmv#include "utils/optional.ipp"
41*6a6c8299Sjmmv#include "utils/text/exceptions.hpp"
42*6a6c8299Sjmmv#include "utils/text/operations.ipp"
43*6a6c8299Sjmmv#include "utils/sanity.hpp"
44*6a6c8299Sjmmv
45*6a6c8299Sjmmvnamespace utils {
46*6a6c8299Sjmmv
47*6a6c8299Sjmmv
48*6a6c8299Sjmmvnamespace config {
49*6a6c8299Sjmmvnamespace detail {
50*6a6c8299Sjmmv
51*6a6c8299Sjmmv
52*6a6c8299Sjmmv/// Type of the new_node() family of functions.
53*6a6c8299Sjmmvtypedef base_node* (*new_node_hook)(void);
54*6a6c8299Sjmmv
55*6a6c8299Sjmmv
56*6a6c8299Sjmmv/// Creates a new leaf node of a given type.
57*6a6c8299Sjmmv///
58*6a6c8299Sjmmv/// \tparam NodeType The type of the leaf node to create.
59*6a6c8299Sjmmv///
60*6a6c8299Sjmmv/// \return A pointer to the newly-created node.
61*6a6c8299Sjmmvtemplate< class NodeType >
62*6a6c8299Sjmmvbase_node*
63*6a6c8299Sjmmvnew_node(void)
64*6a6c8299Sjmmv{
65*6a6c8299Sjmmv    return new NodeType();
66*6a6c8299Sjmmv}
67*6a6c8299Sjmmv
68*6a6c8299Sjmmv
69*6a6c8299Sjmmv/// Internal node of the tree.
70*6a6c8299Sjmmv///
71*6a6c8299Sjmmv/// This abstract base class provides the mechanism to implement both static and
72*6a6c8299Sjmmv/// dynamic nodes.  Ideally, the implementation would be split in subclasses and
73*6a6c8299Sjmmv/// this class would not include the knowledge of whether the node is dynamic or
74*6a6c8299Sjmmv/// not.  However, because the static/dynamic difference depends on the leaf
75*6a6c8299Sjmmv/// types, we need to declare template functions and these cannot be virtual.
76*6a6c8299Sjmmvclass inner_node : public base_node {
77*6a6c8299Sjmmv    /// Whether the node is dynamic or not.
78*6a6c8299Sjmmv    bool _dynamic;
79*6a6c8299Sjmmv
80*6a6c8299Sjmmvprotected:
81*6a6c8299Sjmmv    /// Type to represent the collection of children of this node.
82*6a6c8299Sjmmv    ///
83*6a6c8299Sjmmv    /// Note that these are one-level keys.  They cannot contain dots, and thus
84*6a6c8299Sjmmv    /// is why we use a string rather than a tree_key.
85*6a6c8299Sjmmv    typedef std::map< std::string, base_node* > children_map;
86*6a6c8299Sjmmv
87*6a6c8299Sjmmv    /// Mapping of keys to values that are descendants of this node.
88*6a6c8299Sjmmv    children_map _children;
89*6a6c8299Sjmmv
90*6a6c8299Sjmmv    void copy_into(inner_node* new_node) const;
91*6a6c8299Sjmmv
92*6a6c8299Sjmmvpublic:
93*6a6c8299Sjmmv    inner_node(const bool);
94*6a6c8299Sjmmv    virtual ~inner_node(void) = 0;
95*6a6c8299Sjmmv
96*6a6c8299Sjmmv    const base_node* lookup_ro(const tree_key&,
97*6a6c8299Sjmmv                               const tree_key::size_type) const;
98*6a6c8299Sjmmv    leaf_node* lookup_rw(const tree_key&, const tree_key::size_type,
99*6a6c8299Sjmmv                         new_node_hook);
100*6a6c8299Sjmmv
101*6a6c8299Sjmmv    void all_properties(properties_map&, const tree_key&) const;
102*6a6c8299Sjmmv};
103*6a6c8299Sjmmv
104*6a6c8299Sjmmv
105*6a6c8299Sjmmv/// Static internal node of the tree.
106*6a6c8299Sjmmv///
107*6a6c8299Sjmmv/// The direct children of this node must be pre-defined by calls to define().
108*6a6c8299Sjmmv/// Attempts to traverse this node and resolve a key that is not a pre-defined
109*6a6c8299Sjmmv/// children will result in an "unknown key" error.
110*6a6c8299Sjmmvclass static_inner_node : public config::detail::inner_node {
111*6a6c8299Sjmmvpublic:
112*6a6c8299Sjmmv    static_inner_node(void);
113*6a6c8299Sjmmv
114*6a6c8299Sjmmv    virtual base_node* deep_copy(void) const;
115*6a6c8299Sjmmv
116*6a6c8299Sjmmv    void define(const tree_key&, const tree_key::size_type, new_node_hook);
117*6a6c8299Sjmmv};
118*6a6c8299Sjmmv
119*6a6c8299Sjmmv
120*6a6c8299Sjmmv/// Dynamic internal node of the tree.
121*6a6c8299Sjmmv///
122*6a6c8299Sjmmv/// The children of this node need not be pre-defined.  Attempts to traverse
123*6a6c8299Sjmmv/// this node and resolve a key will result in such key being created.  Any
124*6a6c8299Sjmmv/// intermediate non-existent nodes of the traversal will be created as dynamic
125*6a6c8299Sjmmv/// inner nodes as well.
126*6a6c8299Sjmmvclass dynamic_inner_node : public config::detail::inner_node {
127*6a6c8299Sjmmvpublic:
128*6a6c8299Sjmmv    virtual base_node* deep_copy(void) const;
129*6a6c8299Sjmmv
130*6a6c8299Sjmmv    dynamic_inner_node(void);
131*6a6c8299Sjmmv};
132*6a6c8299Sjmmv
133*6a6c8299Sjmmv
134*6a6c8299Sjmmv}  // namespace detail
135*6a6c8299Sjmmv}  // namespace config
136*6a6c8299Sjmmv
137*6a6c8299Sjmmv
138*6a6c8299Sjmmv/// Constructor for a node with an undefined value.
139*6a6c8299Sjmmv///
140*6a6c8299Sjmmv/// This should only be called by the tree's define() method as a way to
141*6a6c8299Sjmmv/// register a node as known but undefined.  The node will then serve as a
142*6a6c8299Sjmmv/// placeholder for future values.
143*6a6c8299Sjmmvtemplate< typename ValueType >
144*6a6c8299Sjmmvconfig::typed_leaf_node< ValueType >::typed_leaf_node(void) :
145*6a6c8299Sjmmv    _value(none)
146*6a6c8299Sjmmv{
147*6a6c8299Sjmmv}
148*6a6c8299Sjmmv
149*6a6c8299Sjmmv
150*6a6c8299Sjmmv/// Checks whether the node has been set.
151*6a6c8299Sjmmv///
152*6a6c8299Sjmmv/// Remember that a node can exist before holding a value (i.e. when the node
153*6a6c8299Sjmmv/// has been defined as "known" but not yet set by the user).  This function
154*6a6c8299Sjmmv/// checks whether the node laready holds a value.
155*6a6c8299Sjmmv///
156*6a6c8299Sjmmv/// \return True if a value has been set in the node.
157*6a6c8299Sjmmvtemplate< typename ValueType >
158*6a6c8299Sjmmvbool
159*6a6c8299Sjmmvconfig::typed_leaf_node< ValueType >::is_set(void) const
160*6a6c8299Sjmmv{
161*6a6c8299Sjmmv    return static_cast< bool >(_value);
162*6a6c8299Sjmmv}
163*6a6c8299Sjmmv
164*6a6c8299Sjmmv
165*6a6c8299Sjmmv/// Gets the value stored in the node.
166*6a6c8299Sjmmv///
167*6a6c8299Sjmmv/// \pre The node must have a value.
168*6a6c8299Sjmmv///
169*6a6c8299Sjmmv/// \return The value in the node.
170*6a6c8299Sjmmvtemplate< typename ValueType >
171*6a6c8299Sjmmvconst typename config::typed_leaf_node< ValueType >::value_type&
172*6a6c8299Sjmmvconfig::typed_leaf_node< ValueType >::value(void) const
173*6a6c8299Sjmmv{
174*6a6c8299Sjmmv    PRE(is_set());
175*6a6c8299Sjmmv    return _value.get();
176*6a6c8299Sjmmv}
177*6a6c8299Sjmmv
178*6a6c8299Sjmmv
179*6a6c8299Sjmmv/// Gets the read-write value stored in the node.
180*6a6c8299Sjmmv///
181*6a6c8299Sjmmv/// \pre The node must have a value.
182*6a6c8299Sjmmv///
183*6a6c8299Sjmmv/// \return The value in the node.
184*6a6c8299Sjmmvtemplate< typename ValueType >
185*6a6c8299Sjmmvtypename config::typed_leaf_node< ValueType >::value_type&
186*6a6c8299Sjmmvconfig::typed_leaf_node< ValueType >::value(void)
187*6a6c8299Sjmmv{
188*6a6c8299Sjmmv    PRE(is_set());
189*6a6c8299Sjmmv    return _value.get();
190*6a6c8299Sjmmv}
191*6a6c8299Sjmmv
192*6a6c8299Sjmmv
193*6a6c8299Sjmmv/// Sets the value of the node.
194*6a6c8299Sjmmv///
195*6a6c8299Sjmmv/// \param value_ The new value to set the node to.
196*6a6c8299Sjmmv///
197*6a6c8299Sjmmv/// \throw value_error If the value is invalid, according to validate().
198*6a6c8299Sjmmvtemplate< typename ValueType >
199*6a6c8299Sjmmvvoid
200*6a6c8299Sjmmvconfig::typed_leaf_node< ValueType >::set(const value_type& value_)
201*6a6c8299Sjmmv{
202*6a6c8299Sjmmv    validate(value_);
203*6a6c8299Sjmmv    _value = optional< value_type >(value_);
204*6a6c8299Sjmmv}
205*6a6c8299Sjmmv
206*6a6c8299Sjmmv
207*6a6c8299Sjmmv/// Checks a given value for validity.
208*6a6c8299Sjmmv///
209*6a6c8299Sjmmv/// This is called internally by the node right before updating the recorded
210*6a6c8299Sjmmv/// value.  This method can be redefined by subclasses.
211*6a6c8299Sjmmv///
212*6a6c8299Sjmmv/// \param unused_new_value The value to validate.
213*6a6c8299Sjmmv///
214*6a6c8299Sjmmv/// \throw value_error If the value is not valid.
215*6a6c8299Sjmmvtemplate< typename ValueType >
216*6a6c8299Sjmmvvoid
217*6a6c8299Sjmmvconfig::typed_leaf_node< ValueType >::validate(
218*6a6c8299Sjmmv    const value_type& UTILS_UNUSED_PARAM(new_value)) const
219*6a6c8299Sjmmv{
220*6a6c8299Sjmmv}
221*6a6c8299Sjmmv
222*6a6c8299Sjmmv
223*6a6c8299Sjmmv/// Sets the value of the node from a raw string representation.
224*6a6c8299Sjmmv///
225*6a6c8299Sjmmv/// \param raw_value The value to set the node to.
226*6a6c8299Sjmmv///
227*6a6c8299Sjmmv/// \throw value_error If the value is invalid.
228*6a6c8299Sjmmvtemplate< typename ValueType >
229*6a6c8299Sjmmvvoid
230*6a6c8299Sjmmvconfig::native_leaf_node< ValueType >::set_string(const std::string& raw_value)
231*6a6c8299Sjmmv{
232*6a6c8299Sjmmv    try {
233*6a6c8299Sjmmv        typed_leaf_node< ValueType >::set(text::to_type< ValueType >(
234*6a6c8299Sjmmv            raw_value));
235*6a6c8299Sjmmv    } catch (const text::value_error& e) {
236*6a6c8299Sjmmv        throw config::value_error(F("Failed to convert string value '%s' to "
237*6a6c8299Sjmmv                                    "the node's type") % raw_value);
238*6a6c8299Sjmmv    }
239*6a6c8299Sjmmv}
240*6a6c8299Sjmmv
241*6a6c8299Sjmmv
242*6a6c8299Sjmmv/// Converts the contents of the node to a string.
243*6a6c8299Sjmmv///
244*6a6c8299Sjmmv/// \pre The node must have a value.
245*6a6c8299Sjmmv///
246*6a6c8299Sjmmv/// \return A string representation of the value held by the node.
247*6a6c8299Sjmmvtemplate< typename ValueType >
248*6a6c8299Sjmmvstd::string
249*6a6c8299Sjmmvconfig::native_leaf_node< ValueType >::to_string(void) const
250*6a6c8299Sjmmv{
251*6a6c8299Sjmmv    PRE(typed_leaf_node< ValueType >::is_set());
252*6a6c8299Sjmmv    return F("%s") % typed_leaf_node< ValueType >::value();
253*6a6c8299Sjmmv}
254*6a6c8299Sjmmv
255*6a6c8299Sjmmv
256*6a6c8299Sjmmv/// Constructor for a node with an undefined value.
257*6a6c8299Sjmmv///
258*6a6c8299Sjmmv/// This should only be called by the tree's define() method as a way to
259*6a6c8299Sjmmv/// register a node as known but undefined.  The node will then serve as a
260*6a6c8299Sjmmv/// placeholder for future values.
261*6a6c8299Sjmmvtemplate< typename ValueType >
262*6a6c8299Sjmmvconfig::base_set_node< ValueType >::base_set_node(void) :
263*6a6c8299Sjmmv    _value(none)
264*6a6c8299Sjmmv{
265*6a6c8299Sjmmv}
266*6a6c8299Sjmmv
267*6a6c8299Sjmmv
268*6a6c8299Sjmmv/// Checks whether the node has been set.
269*6a6c8299Sjmmv///
270*6a6c8299Sjmmv/// Remember that a node can exist before holding a value (i.e. when the node
271*6a6c8299Sjmmv/// has been defined as "known" but not yet set by the user).  This function
272*6a6c8299Sjmmv/// checks whether the node laready holds a value.
273*6a6c8299Sjmmv///
274*6a6c8299Sjmmv/// \return True if a value has been set in the node.
275*6a6c8299Sjmmvtemplate< typename ValueType >
276*6a6c8299Sjmmvbool
277*6a6c8299Sjmmvconfig::base_set_node< ValueType >::is_set(void) const
278*6a6c8299Sjmmv{
279*6a6c8299Sjmmv    return static_cast< bool >(_value);
280*6a6c8299Sjmmv}
281*6a6c8299Sjmmv
282*6a6c8299Sjmmv
283*6a6c8299Sjmmv/// Gets the value stored in the node.
284*6a6c8299Sjmmv///
285*6a6c8299Sjmmv/// \pre The node must have a value.
286*6a6c8299Sjmmv///
287*6a6c8299Sjmmv/// \return The value in the node.
288*6a6c8299Sjmmvtemplate< typename ValueType >
289*6a6c8299Sjmmvconst typename config::base_set_node< ValueType >::value_type&
290*6a6c8299Sjmmvconfig::base_set_node< ValueType >::value(void) const
291*6a6c8299Sjmmv{
292*6a6c8299Sjmmv    PRE(is_set());
293*6a6c8299Sjmmv    return _value.get();
294*6a6c8299Sjmmv}
295*6a6c8299Sjmmv
296*6a6c8299Sjmmv
297*6a6c8299Sjmmv/// Gets the read-write value stored in the node.
298*6a6c8299Sjmmv///
299*6a6c8299Sjmmv/// \pre The node must have a value.
300*6a6c8299Sjmmv///
301*6a6c8299Sjmmv/// \return The value in the node.
302*6a6c8299Sjmmvtemplate< typename ValueType >
303*6a6c8299Sjmmvtypename config::base_set_node< ValueType >::value_type&
304*6a6c8299Sjmmvconfig::base_set_node< ValueType >::value(void)
305*6a6c8299Sjmmv{
306*6a6c8299Sjmmv    PRE(is_set());
307*6a6c8299Sjmmv    return _value.get();
308*6a6c8299Sjmmv}
309*6a6c8299Sjmmv
310*6a6c8299Sjmmv
311*6a6c8299Sjmmv/// Sets the value of the node.
312*6a6c8299Sjmmv///
313*6a6c8299Sjmmv/// \param value_ The new value to set the node to.
314*6a6c8299Sjmmv///
315*6a6c8299Sjmmv/// \throw value_error If the value is invalid, according to validate().
316*6a6c8299Sjmmvtemplate< typename ValueType >
317*6a6c8299Sjmmvvoid
318*6a6c8299Sjmmvconfig::base_set_node< ValueType >::set(const value_type& value_)
319*6a6c8299Sjmmv{
320*6a6c8299Sjmmv    validate(value_);
321*6a6c8299Sjmmv    _value = optional< value_type >(value_);
322*6a6c8299Sjmmv}
323*6a6c8299Sjmmv
324*6a6c8299Sjmmv
325*6a6c8299Sjmmv/// Sets the value of the node from a raw string representation.
326*6a6c8299Sjmmv///
327*6a6c8299Sjmmv/// \param raw_value The value to set the node to.
328*6a6c8299Sjmmv///
329*6a6c8299Sjmmv/// \throw value_error If the value is invalid.
330*6a6c8299Sjmmvtemplate< typename ValueType >
331*6a6c8299Sjmmvvoid
332*6a6c8299Sjmmvconfig::base_set_node< ValueType >::set_string(const std::string& raw_value)
333*6a6c8299Sjmmv{
334*6a6c8299Sjmmv    std::set< ValueType > new_value;
335*6a6c8299Sjmmv
336*6a6c8299Sjmmv    const std::vector< std::string > words = text::split(raw_value, ' ');
337*6a6c8299Sjmmv    for (std::vector< std::string >::const_iterator iter = words.begin();
338*6a6c8299Sjmmv         iter != words.end(); ++iter) {
339*6a6c8299Sjmmv        if (!(*iter).empty())
340*6a6c8299Sjmmv            new_value.insert(parse_one(*iter));
341*6a6c8299Sjmmv    }
342*6a6c8299Sjmmv
343*6a6c8299Sjmmv    set(new_value);
344*6a6c8299Sjmmv}
345*6a6c8299Sjmmv
346*6a6c8299Sjmmv
347*6a6c8299Sjmmv/// Converts the contents of the node to a string.
348*6a6c8299Sjmmv///
349*6a6c8299Sjmmv/// \pre The node must have a value.
350*6a6c8299Sjmmv///
351*6a6c8299Sjmmv/// \return A string representation of the value held by the node.
352*6a6c8299Sjmmvtemplate< typename ValueType >
353*6a6c8299Sjmmvstd::string
354*6a6c8299Sjmmvconfig::base_set_node< ValueType >::to_string(void) const
355*6a6c8299Sjmmv{
356*6a6c8299Sjmmv    PRE(is_set());
357*6a6c8299Sjmmv    return text::join(_value.get(), " ");
358*6a6c8299Sjmmv}
359*6a6c8299Sjmmv
360*6a6c8299Sjmmv
361*6a6c8299Sjmmv/// Pushes the node's value onto the Lua stack.
362*6a6c8299Sjmmv///
363*6a6c8299Sjmmv/// \param unused_state The Lua state onto which to push the value.
364*6a6c8299Sjmmvtemplate< typename ValueType >
365*6a6c8299Sjmmvvoid
366*6a6c8299Sjmmvconfig::base_set_node< ValueType >::push_lua(
367*6a6c8299Sjmmv    lutok::state& UTILS_UNUSED_PARAM(state)) const
368*6a6c8299Sjmmv{
369*6a6c8299Sjmmv    UNREACHABLE;
370*6a6c8299Sjmmv}
371*6a6c8299Sjmmv
372*6a6c8299Sjmmv
373*6a6c8299Sjmmv/// Sets the value of the node from an entry in the Lua stack.
374*6a6c8299Sjmmv///
375*6a6c8299Sjmmv/// \param unused_state The Lua state from which to get the value.
376*6a6c8299Sjmmv/// \param unused_value_index The stack index in which the value resides.
377*6a6c8299Sjmmv///
378*6a6c8299Sjmmv/// \throw value_error If the value in state(value_index) cannot be
379*6a6c8299Sjmmv///     processed by this node.
380*6a6c8299Sjmmvtemplate< typename ValueType >
381*6a6c8299Sjmmvvoid
382*6a6c8299Sjmmvconfig::base_set_node< ValueType >::set_lua(
383*6a6c8299Sjmmv    lutok::state& UTILS_UNUSED_PARAM(state),
384*6a6c8299Sjmmv    const int UTILS_UNUSED_PARAM(value_index))
385*6a6c8299Sjmmv{
386*6a6c8299Sjmmv    UNREACHABLE;
387*6a6c8299Sjmmv}
388*6a6c8299Sjmmv
389*6a6c8299Sjmmv
390*6a6c8299Sjmmv/// Checks a given value for validity.
391*6a6c8299Sjmmv///
392*6a6c8299Sjmmv/// This is called internally by the node right before updating the recorded
393*6a6c8299Sjmmv/// value.  This method can be redefined by subclasses.
394*6a6c8299Sjmmv///
395*6a6c8299Sjmmv/// \param unused_new_value The value to validate.
396*6a6c8299Sjmmv///
397*6a6c8299Sjmmv/// \throw value_error If the value is not valid.
398*6a6c8299Sjmmvtemplate< typename ValueType >
399*6a6c8299Sjmmvvoid
400*6a6c8299Sjmmvconfig::base_set_node< ValueType >::validate(
401*6a6c8299Sjmmv    const value_type& UTILS_UNUSED_PARAM(new_value)) const
402*6a6c8299Sjmmv{
403*6a6c8299Sjmmv}
404*6a6c8299Sjmmv
405*6a6c8299Sjmmv
406*6a6c8299Sjmmv}  // namespace utils
407*6a6c8299Sjmmv
408*6a6c8299Sjmmv#endif  // !defined(UTILS_CONFIG_NODES_IPP)
409