Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/contrib/kyua/utils/config/nodes.cpp
48081 views
1
// Copyright 2012 The Kyua Authors.
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/nodes.ipp"
30
31
#include <memory>
32
33
#include <lutok/state.ipp>
34
35
#include "utils/config/exceptions.hpp"
36
#include "utils/config/keys.hpp"
37
#include "utils/format/macros.hpp"
38
39
namespace config = utils::config;
40
41
42
/// Destructor.
43
config::detail::base_node::~base_node(void)
44
{
45
}
46
47
48
/// Constructor.
49
///
50
/// \param dynamic_ Whether the node is dynamic or not.
51
config::detail::inner_node::inner_node(const bool dynamic_) :
52
_dynamic(dynamic_)
53
{
54
}
55
56
57
/// Destructor.
58
config::detail::inner_node::~inner_node(void)
59
{
60
for (children_map::const_iterator iter = _children.begin();
61
iter != _children.end(); ++iter)
62
delete (*iter).second;
63
}
64
65
66
/// Fills the given node with a copy of this node's data.
67
///
68
/// \param node The node to fill. Should be the fresh return value of a
69
/// deep_copy() operation.
70
void
71
config::detail::inner_node::copy_into(inner_node* node) const
72
{
73
node->_dynamic = _dynamic;
74
for (children_map::const_iterator iter = _children.begin();
75
iter != _children.end(); ++iter) {
76
base_node* new_node = (*iter).second->deep_copy();
77
try {
78
node->_children[(*iter).first] = new_node;
79
} catch (...) {
80
delete new_node;
81
throw;
82
}
83
}
84
}
85
86
87
/// Combines two children sets, preferring the keys in the first set only.
88
///
89
/// This operation is not symmetrical on c1 and c2. The caller is responsible
90
/// for invoking this twice so that the two key sets are combined if they happen
91
/// to differ.
92
///
93
/// \param key Key to this node.
94
/// \param c1 First children set.
95
/// \param c2 First children set.
96
/// \param [in,out] node The node to combine into.
97
///
98
/// \throw bad_combination_error If the two nodes cannot be combined.
99
void
100
config::detail::inner_node::combine_children_into(
101
const tree_key& key,
102
const children_map& c1, const children_map& c2,
103
inner_node* node) const
104
{
105
for (children_map::const_iterator iter1 = c1.begin();
106
iter1 != c1.end(); ++iter1) {
107
const std::string& name = (*iter1).first;
108
109
if (node->_children.find(name) != node->_children.end()) {
110
continue;
111
}
112
113
std::unique_ptr< base_node > new_node;
114
115
children_map::const_iterator iter2 = c2.find(name);
116
if (iter2 == c2.end()) {
117
new_node.reset((*iter1).second->deep_copy());
118
} else {
119
tree_key child_key = key;
120
child_key.push_back(name);
121
new_node.reset((*iter1).second->combine(child_key,
122
(*iter2).second));
123
}
124
125
node->_children[name] = new_node.release();
126
}
127
}
128
129
130
/// Combines this inner node with another inner node onto a new node.
131
///
132
/// The "dynamic" property is inherited by the new node if either of the two
133
/// nodes are dynamic.
134
///
135
/// \param key Key to this node.
136
/// \param other_base The node to combine with.
137
/// \param [in,out] node The node to combine into.
138
///
139
/// \throw bad_combination_error If the two nodes cannot be combined.
140
void
141
config::detail::inner_node::combine_into(const tree_key& key,
142
const base_node* other_base,
143
inner_node* node) const
144
{
145
try {
146
const inner_node& other = dynamic_cast< const inner_node& >(
147
*other_base);
148
149
node->_dynamic = _dynamic || other._dynamic;
150
151
combine_children_into(key, _children, other._children, node);
152
combine_children_into(key, other._children, _children, node);
153
} catch (const std::bad_cast& unused_e) {
154
throw config::bad_combination_error(
155
key, "'%s' is an inner node in the base tree but a leaf node in "
156
"the overrides treee");
157
}
158
}
159
160
161
/// Finds a node without creating it if not found.
162
///
163
/// This recursive algorithm traverses the tree searching for a particular key.
164
/// The returned node is constant, so this can only be used for querying
165
/// purposes. For this reason, this algorithm does not create intermediate
166
/// nodes if they don't exist (as would be necessary to set a new node).
167
///
168
/// \param key The key to be queried.
169
/// \param key_pos The current level within the key to be examined.
170
///
171
/// \return A reference to the located node, if successful.
172
///
173
/// \throw unknown_key_error If the provided key is unknown.
174
const config::detail::base_node*
175
config::detail::inner_node::lookup_ro(const tree_key& key,
176
const tree_key::size_type key_pos) const
177
{
178
PRE(key_pos < key.size());
179
180
const children_map::const_iterator child_iter = _children.find(
181
key[key_pos]);
182
if (child_iter == _children.end())
183
throw unknown_key_error(key);
184
185
if (key_pos == key.size() - 1) {
186
return (*child_iter).second;
187
} else {
188
PRE(key_pos < key.size() - 1);
189
try {
190
const inner_node& child = dynamic_cast< const inner_node& >(
191
*(*child_iter).second);
192
return child.lookup_ro(key, key_pos + 1);
193
} catch (const std::bad_cast& e) {
194
throw unknown_key_error(
195
key, "Cannot address incomplete configuration property '%s'");
196
}
197
}
198
}
199
200
201
/// Finds a node and creates it if not found.
202
///
203
/// This recursive algorithm traverses the tree searching for a particular key,
204
/// creating any intermediate nodes if they do not already exist (for the case
205
/// of dynamic inner nodes). The returned node is non-constant, so this can be
206
/// used by the algorithms that set key values.
207
///
208
/// \param key The key to be queried.
209
/// \param key_pos The current level within the key to be examined.
210
/// \param new_node A function that returns a new leaf node of the desired
211
/// type. This is only called if the leaf cannot be found, but it has
212
/// already been defined.
213
///
214
/// \return A reference to the located node, if successful.
215
///
216
/// \throw invalid_key_value If the resulting node of the search would be an
217
/// inner node.
218
/// \throw unknown_key_error If the provided key is unknown.
219
config::leaf_node*
220
config::detail::inner_node::lookup_rw(const tree_key& key,
221
const tree_key::size_type key_pos,
222
new_node_hook new_node)
223
{
224
PRE(key_pos < key.size());
225
226
children_map::const_iterator child_iter = _children.find(key[key_pos]);
227
if (child_iter == _children.end()) {
228
if (_dynamic) {
229
base_node* const child = (key_pos == key.size() - 1) ?
230
static_cast< base_node* >(new_node()) :
231
static_cast< base_node* >(new dynamic_inner_node());
232
_children.insert(children_map::value_type(key[key_pos], child));
233
child_iter = _children.find(key[key_pos]);
234
} else {
235
throw unknown_key_error(key);
236
}
237
}
238
239
if (key_pos == key.size() - 1) {
240
try {
241
leaf_node& child = dynamic_cast< leaf_node& >(
242
*(*child_iter).second);
243
return &child;
244
} catch (const std::bad_cast& unused_error) {
245
throw invalid_key_value(key, "Type mismatch");
246
}
247
} else {
248
PRE(key_pos < key.size() - 1);
249
try {
250
inner_node& child = dynamic_cast< inner_node& >(
251
*(*child_iter).second);
252
return child.lookup_rw(key, key_pos + 1, new_node);
253
} catch (const std::bad_cast& e) {
254
throw unknown_key_error(
255
key, "Cannot address incomplete configuration property '%s'");
256
}
257
}
258
}
259
260
261
/// Converts the subtree to a collection of key/value string pairs.
262
///
263
/// \param [out] properties The accumulator for the generated properties. The
264
/// contents of the map are only extended.
265
/// \param key The path to the current node.
266
void
267
config::detail::inner_node::all_properties(properties_map& properties,
268
const tree_key& key) const
269
{
270
for (children_map::const_iterator iter = _children.begin();
271
iter != _children.end(); ++iter) {
272
tree_key child_key = key;
273
child_key.push_back((*iter).first);
274
try {
275
leaf_node& child = dynamic_cast< leaf_node& >(*(*iter).second);
276
if (child.is_set())
277
properties[flatten_key(child_key)] = child.to_string();
278
} catch (const std::bad_cast& unused_error) {
279
inner_node& child = dynamic_cast< inner_node& >(*(*iter).second);
280
child.all_properties(properties, child_key);
281
}
282
}
283
}
284
285
286
/// Constructor.
287
config::detail::static_inner_node::static_inner_node(void) :
288
inner_node(false)
289
{
290
}
291
292
293
/// Copies the node.
294
///
295
/// \return A dynamically-allocated node.
296
config::detail::base_node*
297
config::detail::static_inner_node::deep_copy(void) const
298
{
299
std::unique_ptr< inner_node > new_node(new static_inner_node());
300
copy_into(new_node.get());
301
return new_node.release();
302
}
303
304
305
/// Combines this node with another one.
306
///
307
/// \param key Key to this node.
308
/// \param other The node to combine with.
309
///
310
/// \return A new node representing the combination.
311
///
312
/// \throw bad_combination_error If the two nodes cannot be combined.
313
config::detail::base_node*
314
config::detail::static_inner_node::combine(const tree_key& key,
315
const base_node* other) const
316
{
317
std::unique_ptr< inner_node > new_node(new static_inner_node());
318
combine_into(key, other, new_node.get());
319
return new_node.release();
320
}
321
322
323
/// Registers a key as valid and having a specific type.
324
///
325
/// This method does not raise errors on invalid/unknown keys or other
326
/// tree-related issues. The reasons is that define() is a method that does not
327
/// depend on user input: it is intended to pre-populate the tree with a
328
/// specific structure, and that happens once at coding time.
329
///
330
/// \param key The key to be registered.
331
/// \param key_pos The current level within the key to be examined.
332
/// \param new_node A function that returns a new leaf node of the desired
333
/// type.
334
void
335
config::detail::static_inner_node::define(const tree_key& key,
336
const tree_key::size_type key_pos,
337
new_node_hook new_node)
338
{
339
PRE(key_pos < key.size());
340
341
if (key_pos == key.size() - 1) {
342
PRE_MSG(_children.find(key[key_pos]) == _children.end(),
343
"Key already defined");
344
_children.insert(children_map::value_type(key[key_pos], new_node()));
345
} else {
346
PRE(key_pos < key.size() - 1);
347
const children_map::const_iterator child_iter = _children.find(
348
key[key_pos]);
349
350
if (child_iter == _children.end()) {
351
static_inner_node* const child_ptr = new static_inner_node();
352
_children.insert(children_map::value_type(key[key_pos], child_ptr));
353
child_ptr->define(key, key_pos + 1, new_node);
354
} else {
355
try {
356
static_inner_node& child = dynamic_cast< static_inner_node& >(
357
*(*child_iter).second);
358
child.define(key, key_pos + 1, new_node);
359
} catch (const std::bad_cast& e) {
360
UNREACHABLE;
361
}
362
}
363
}
364
}
365
366
367
/// Constructor.
368
config::detail::dynamic_inner_node::dynamic_inner_node(void) :
369
inner_node(true)
370
{
371
}
372
373
374
/// Copies the node.
375
///
376
/// \return A dynamically-allocated node.
377
config::detail::base_node*
378
config::detail::dynamic_inner_node::deep_copy(void) const
379
{
380
std::unique_ptr< inner_node > new_node(new dynamic_inner_node());
381
copy_into(new_node.get());
382
return new_node.release();
383
}
384
385
386
/// Combines this node with another one.
387
///
388
/// \param key Key to this node.
389
/// \param other The node to combine with.
390
///
391
/// \return A new node representing the combination.
392
///
393
/// \throw bad_combination_error If the two nodes cannot be combined.
394
config::detail::base_node*
395
config::detail::dynamic_inner_node::combine(const tree_key& key,
396
const base_node* other) const
397
{
398
std::unique_ptr< inner_node > new_node(new dynamic_inner_node());
399
combine_into(key, other, new_node.get());
400
return new_node.release();
401
}
402
403
404
/// Destructor.
405
config::leaf_node::~leaf_node(void)
406
{
407
}
408
409
410
/// Combines this node with another one.
411
///
412
/// \param key Key to this node.
413
/// \param other_base The node to combine with.
414
///
415
/// \return A new node representing the combination.
416
///
417
/// \throw bad_combination_error If the two nodes cannot be combined.
418
config::detail::base_node*
419
config::leaf_node::combine(const detail::tree_key& key,
420
const base_node* other_base) const
421
{
422
try {
423
const leaf_node& other = dynamic_cast< const leaf_node& >(*other_base);
424
425
if (other.is_set()) {
426
return other.deep_copy();
427
} else {
428
return deep_copy();
429
}
430
} catch (const std::bad_cast& unused_e) {
431
throw config::bad_combination_error(
432
key, "'%s' is a leaf node in the base tree but an inner node in "
433
"the overrides treee");
434
}
435
}
436
437
438
/// Copies the node.
439
///
440
/// \return A dynamically-allocated node.
441
config::detail::base_node*
442
config::bool_node::deep_copy(void) const
443
{
444
std::unique_ptr< bool_node > new_node(new bool_node());
445
new_node->_value = _value;
446
return new_node.release();
447
}
448
449
450
/// Pushes the node's value onto the Lua stack.
451
///
452
/// \param state The Lua state onto which to push the value.
453
void
454
config::bool_node::push_lua(lutok::state& state) const
455
{
456
state.push_boolean(value());
457
}
458
459
460
/// Sets the value of the node from an entry in the Lua stack.
461
///
462
/// \param state The Lua state from which to get the value.
463
/// \param value_index The stack index in which the value resides.
464
///
465
/// \throw value_error If the value in state(value_index) cannot be
466
/// processed by this node.
467
void
468
config::bool_node::set_lua(lutok::state& state, const int value_index)
469
{
470
if (state.is_boolean(value_index))
471
set(state.to_boolean(value_index));
472
else
473
throw value_error("Not a boolean");
474
}
475
476
477
/// Copies the node.
478
///
479
/// \return A dynamically-allocated node.
480
config::detail::base_node*
481
config::int_node::deep_copy(void) const
482
{
483
std::unique_ptr< int_node > new_node(new int_node());
484
new_node->_value = _value;
485
return new_node.release();
486
}
487
488
489
/// Pushes the node's value onto the Lua stack.
490
///
491
/// \param state The Lua state onto which to push the value.
492
void
493
config::int_node::push_lua(lutok::state& state) const
494
{
495
state.push_integer(value());
496
}
497
498
499
/// Sets the value of the node from an entry in the Lua stack.
500
///
501
/// \param state The Lua state from which to get the value.
502
/// \param value_index The stack index in which the value resides.
503
///
504
/// \throw value_error If the value in state(value_index) cannot be
505
/// processed by this node.
506
void
507
config::int_node::set_lua(lutok::state& state, const int value_index)
508
{
509
if (state.is_number(value_index))
510
set(state.to_integer(value_index));
511
else
512
throw value_error("Not an integer");
513
}
514
515
516
/// Checks a given value for validity.
517
///
518
/// \param new_value The value to validate.
519
///
520
/// \throw value_error If the value is not valid.
521
void
522
config::positive_int_node::validate(const value_type& new_value) const
523
{
524
if (new_value <= 0)
525
throw value_error("Must be a positive integer");
526
}
527
528
529
/// Copies the node.
530
///
531
/// \return A dynamically-allocated node.
532
config::detail::base_node*
533
config::string_node::deep_copy(void) const
534
{
535
std::unique_ptr< string_node > new_node(new string_node());
536
new_node->_value = _value;
537
return new_node.release();
538
}
539
540
541
/// Pushes the node's value onto the Lua stack.
542
///
543
/// \param state The Lua state onto which to push the value.
544
void
545
config::string_node::push_lua(lutok::state& state) const
546
{
547
state.push_string(value());
548
}
549
550
551
/// Sets the value of the node from an entry in the Lua stack.
552
///
553
/// \param state The Lua state from which to get the value.
554
/// \param value_index The stack index in which the value resides.
555
///
556
/// \throw value_error If the value in state(value_index) cannot be
557
/// processed by this node.
558
void
559
config::string_node::set_lua(lutok::state& state, const int value_index)
560
{
561
if (state.is_string(value_index))
562
set(state.to_string(value_index));
563
else
564
throw value_error("Not a string");
565
}
566
567
568
/// Copies the node.
569
///
570
/// \return A dynamically-allocated node.
571
config::detail::base_node*
572
config::strings_set_node::deep_copy(void) const
573
{
574
std::unique_ptr< strings_set_node > new_node(new strings_set_node());
575
new_node->_value = _value;
576
return new_node.release();
577
}
578
579
580
/// Converts a single word to the native type.
581
///
582
/// \param raw_value The value to parse.
583
///
584
/// \return The parsed value.
585
std::string
586
config::strings_set_node::parse_one(const std::string& raw_value) const
587
{
588
return raw_value;
589
}
590
591