-
-
Notifications
You must be signed in to change notification settings - Fork 988
/
config.hpp
635 lines (520 loc) · 20.2 KB
/
config.hpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
/*
Copyright (C) 2003 - 2014 by David White <dave@whitevine.net>
Part of the Battle for Wesnoth Project http://www.wesnoth.org/
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY.
See the COPYING file for more details.
*/
/**
* @file
* Definitions for the interface to Wesnoth Markup Language (WML).
*
* This module defines the interface to Wesnoth Markup Language (WML). WML is
* a simple hierarchical text-based file format. The format is defined in
* Wiki, under BuildingScenariosWML
*
* All configuration files are stored in this format, and data is sent across
* the network in this format. It is thus used extensively throughout the
* game.
*/
#ifndef CONFIG_HPP_INCLUDED
#define CONFIG_HPP_INCLUDED
#include "global.hpp"
#include <map>
#include <iosfwd>
#include <vector>
#include <boost/exception/exception.hpp>
#include <boost/variant/apply_visitor.hpp>
#include <boost/variant/variant.hpp>
#include "game_errors.hpp"
#include "tstring.hpp"
class config;
struct tconfig_implementation;
class vconfig;
struct lua_State;
bool operator==(const config &, const config &);
inline bool operator!=(const config &a, const config &b) { return !operator==(a, b); }
std::ostream &operator << (std::ostream &, const config &);
/** A config object defines a single node in a WML file, with access to child nodes. */
class config
{
friend bool operator==(const config& a, const config& b);
friend struct tconfig_implementation;
static config invalid;
/**
* Raises an exception if @a this is not valid.
*/
void check_valid() const;
/**
* Raises an exception if @a this or @a cfg is not valid.
*/
void check_valid(const config &cfg) const;
#ifndef HAVE_CXX11
struct safe_bool_impl { void nonnull() {} };
/**
* Used as the return type of the conversion operator for boolean contexts.
* Needed, since the compiler would otherwise consider the following
* conversion (C legacy): cfg["abc"] -> "abc"[bool(cfg)] -> 'b'
*/
typedef void (safe_bool_impl::*safe_bool)();
#endif
public:
// Create an empty node.
config();
config(const config &);
config &operator=(const config &);
#ifdef HAVE_CXX11
config(config &&);
config &operator=(config &&);
#endif
/**
* Creates a config object with an empty child of name @a child.
*/
explicit config(const std::string &child);
~config();
#ifdef HAVE_CXX11
explicit operator bool() const
{ return this != &invalid; }
#else
operator safe_bool() const
{ return this != &invalid ? &safe_bool_impl::nonnull : NULL; }
#endif
typedef std::vector<config*> child_list;
typedef std::map<std::string,child_list> child_map;
struct const_child_iterator;
struct child_iterator
{
typedef config value_type;
typedef std::forward_iterator_tag iterator_category;
typedef int difference_type;
typedef config *pointer;
typedef config &reference;
typedef child_list::iterator Itor;
explicit child_iterator(const Itor &i): i_(i) {}
child_iterator &operator++() { ++i_; return *this; }
child_iterator operator++(int) { return child_iterator(i_++); }
child_iterator &operator--() { --i_; return *this; }
child_iterator operator--(int) { return child_iterator(i_--); }
config &operator*() const { return **i_; }
config *operator->() const { return &**i_; }
bool operator==(const child_iterator &i) const { return i_ == i.i_; }
bool operator!=(const child_iterator &i) const { return i_ != i.i_; }
private:
Itor i_;
friend struct const_child_iterator;
};
struct const_child_iterator
{
typedef config value_type;
typedef std::forward_iterator_tag iterator_category;
typedef int difference_type;
typedef const config *pointer;
typedef const config &reference;
typedef child_list::const_iterator Itor;
explicit const_child_iterator(const Itor &i): i_(i) {}
const_child_iterator(const child_iterator &i): i_(i.i_) {}
const_child_iterator &operator++() { ++i_; return *this; }
const_child_iterator operator++(int) { return const_child_iterator(i_++); }
const_child_iterator &operator--() { --i_; return *this; }
const_child_iterator operator--(int) { return const_child_iterator(i_--); }
const config &operator*() const { return **i_; }
const config *operator->() const { return &**i_; }
bool operator==(const const_child_iterator &i) const { return i_ == i.i_; }
bool operator!=(const const_child_iterator &i) const { return i_ != i.i_; }
private:
Itor i_;
};
typedef std::pair<child_iterator,child_iterator> child_itors;
typedef std::pair<const_child_iterator,const_child_iterator> const_child_itors;
/**
* Variant for storing WML attributes.
* The most efficient type is used when assigning a value. For instance,
* strings "yes", "no", "true", "false" will be detected and stored as boolean.
* @note The blank variant is only used when querying missing attributes.
* It is not stored in config objects.
*/
class attribute_value
{
/// A wrapper for bool to get the correct streaming ("true"/"false").
/// Most visitors can simply treat this as bool.
class true_false
{
bool value_;
public:
explicit true_false(bool value = false) : value_(value) {}
operator bool() const { return value_; }
const std::string & str() const
{ return value_ ? config::attribute_value::s_true :
config::attribute_value::s_false; }
};
friend std::ostream& operator<<(std::ostream &os, const true_false &v);
/// A wrapper for bool to get the correct streaming ("yes"/"no").
/// Most visitors can simply treat this as bool.
class yes_no
{
bool value_;
public:
explicit yes_no(bool value = false) : value_(value) {}
operator bool() const { return value_; }
const std::string & str() const
{ return value_ ? config::attribute_value::s_yes :
config::attribute_value::s_no; }
};
friend std::ostream& operator<<(std::ostream &os, const yes_no &v);
/// Visitor for checking equality.
class equality_visitor;
/// Visitor for converting a variant to a string.
class string_visitor;
// Data will be stored in a variant, allowing for the possibility of
// boolean, numeric, and translatable data in addition to basic string
// data. For most purposes, int is the preferred type for numeric data
// as it is fast (often natural word size). While it is desirable to
// use few types (to keep the overhead low), we do have use cases for
// fractions (double) and huge numbers (up to the larger of LLONG_MAX
// and SIZE_MAX).
typedef boost::variant<boost::blank,
true_false, yes_no,
int, unsigned long long, double,
std::string, t_string
> value_type;
/// The stored value will always use the first type from the variant
/// definition that can represent it and that can be streamed to the
/// correct string representation (if applicable).
/// This is enforced upon assignment.
value_type value_;
public:
/// Default implementation, but defined out-of-line for efficiency reasons.
attribute_value();
/// Default implementation, but defined out-of-line for efficiency reasons.
~attribute_value();
/// Default implementation, but defined out-of-line for efficiency reasons.
attribute_value(const attribute_value &);
/// Default implementation, but defined out-of-line for efficiency reasons.
attribute_value &operator=(const attribute_value &);
// Numeric assignments:
attribute_value &operator=(bool v);
attribute_value &operator=(int v);
attribute_value &operator=(long v) { return operator=(static_cast<long long>(v)); }
attribute_value &operator=(long long v);
attribute_value &operator=(unsigned v) { return operator=(static_cast<unsigned long long>(v)); }
attribute_value &operator=(unsigned long v) { return operator=(static_cast<unsigned long long>(v)); }
attribute_value &operator=(unsigned long long v);
attribute_value &operator=(double v);
// String assignments:
attribute_value &operator=(const char *v) { return operator=(std::string(v)); }
attribute_value &operator=(const std::string &v);
attribute_value &operator=(const t_string &v);
// Extracting as a specific type:
bool to_bool(bool def = false) const;
int to_int(int def = 0) const;
long long to_long_long(long long def = 0) const;
unsigned to_unsigned(unsigned def = 0) const;
size_t to_size_t(size_t def = 0) const;
time_t to_time_t(time_t def = 0) const;
double to_double(double def = 0.) const;
std::string str() const;
t_string t_str() const;
// Implicit conversions:
operator int() const { return to_int(); }
operator std::string() const { return str(); }
operator t_string() const { return t_str(); }
/// Tests for an attribute that was never set.
bool blank() const;
/// Tests for an attribute that either was never set or was set to "".
bool empty() const;
// Comparisons:
bool operator==(const attribute_value &other) const;
bool operator!=(const attribute_value &other) const
{ return !operator==(other); }
// Streaming:
friend std::ostream& operator<<(std::ostream &os, const attribute_value &v);
// Visitor support:
/// Applies a visitor to the underlying variant.
/// (See the documentation for Boost.Variant.)
template <typename V>
typename V::result_type apply_visitor(const V & visitor) const
{ return boost::apply_visitor(visitor, value_); }
// Special strings.
static const std::string s_yes, s_no;
static const std::string s_true, s_false;
};
typedef std::map<std::string, attribute_value> attribute_map;
typedef attribute_map::value_type attribute;
struct const_attribute_iterator
{
typedef attribute value_type;
typedef std::forward_iterator_tag iterator_category;
typedef int difference_type;
typedef const attribute *pointer;
typedef const attribute &reference;
typedef attribute_map::const_iterator Itor;
explicit const_attribute_iterator(const Itor &i): i_(i) {}
const_attribute_iterator &operator++() { ++i_; return *this; }
const_attribute_iterator operator++(int) { return const_attribute_iterator(i_++); }
const attribute &operator*() const { return *i_; }
const attribute *operator->() const { return &*i_; }
bool operator==(const const_attribute_iterator &i) const { return i_ == i.i_; }
bool operator!=(const const_attribute_iterator &i) const { return i_ != i.i_; }
private:
Itor i_;
};
typedef std::pair<const_attribute_iterator,const_attribute_iterator> const_attr_itors;
child_itors child_range(const std::string& key);
const_child_itors child_range(const std::string& key) const;
unsigned child_count(const std::string &key) const;
unsigned all_children_count() const;
/**
* Determine whether a config has a child or not.
*
* @param key The key of the child to find.
*
* @returns Whether a child is available.
*/
bool has_child(const std::string& key) const;
/**
* Returns the first child with the given @a key, or an empty config if there is none.
*/
const config & child_or_empty(const std::string &key) const;
/**
* Returns the nth child with the given @a key, or
* a reference to an invalid config if there is none.
* @note A negative @a n accesses from the end of the object.
* For instance, -1 is the index of the last child.
*/
config &child(const std::string& key, int n = 0);
/**
* Returns the nth child with the given @a key, or
* a reference to an invalid config if there is none.
* @note A negative @a n accesses from the end of the object.
* For instance, -1 is the index of the last child.
*/
const config &child(const std::string& key, int n = 0) const
{ return const_cast<config *>(this)->child(key, n); }
/**
* Returns a mandatory child node.
*
* If the child is not found a @ref wml_exception is thrown.
*
* @pre parent[0] == '['
* @pre parent[parent.size() - 1] == ']'
*
* @param key The key of the child item to return.
* @param parent The section in which the child should reside.
* This is only used for error reporting.
*
* @returns The wanted child node.
*/
config& child(const std::string& key, const std::string& parent);
/**
* Returns a mandatory child node.
*
* If the child is not found a @ref wml_exception is thrown.
*
* @pre parent[0] == '['
* @pre parent[parent.size() - 1] == ']'
*
* @param key The key of the child item to return.
* @param parent The section in which the child should reside.
* This is only used for error reporting.
*
* @returns The wanted child node.
*/
const config& child(
const std::string& key
, const std::string& parent) const;
config& add_child(const std::string& key);
config& add_child(const std::string& key, const config& val);
config& add_child_at(const std::string &key, const config &val, unsigned index);
#ifdef HAVE_CXX11
config &add_child(const std::string &key, config &&val);
#endif
/**
* Returns a reference to the attribute with the given @a key.
* Creates it if it does not exist.
*/
attribute_value &operator[](const std::string &key);
/**
* Returns a reference to the attribute with the given @a key
* or to a dummy empty attribute if it does not exist.
*/
const attribute_value &operator[](const std::string &key) const;
/**
* Returns a pointer to the attribute with the given @a key
* or NULL if it does not exist.
*/
const attribute_value *get(const std::string &key) const;
/**
* Function to handle backward compatibility
* Get the value of key and if missing try old_key
* and log msg as a WML error (if not empty)
*/
const attribute_value &get_old_attribute(const std::string &key, const std::string &old_key, const std::string& msg = "") const;
/**
* Returns a reference to the first child with the given @a key.
* Creates the child if it does not yet exist.
*/
config &child_or_add(const std::string &key);
bool has_attribute(const std::string &key) const;
/**
* Function to handle backward compatibility
* Check if has key or old_key
* and log msg as a WML error (if not empty)
*/
bool has_old_attribute(const std::string &key, const std::string &old_key, const std::string& msg = "") const;
void remove_attribute(const std::string &key);
void merge_attributes(const config &);
const_attr_itors attribute_range() const;
/**
* Returns the first child of tag @a key with a @a name attribute
* containing @a value.
*/
config &find_child(const std::string &key, const std::string &name,
const std::string &value);
const config &find_child(const std::string &key, const std::string &name,
const std::string &value) const
{ return const_cast<config *>(this)->find_child(key, name, value); }
void clear_children(const std::string& key);
/**
* Moves all the children with tag @a key from @a src to this.
*/
void splice_children(config &src, const std::string &key);
void remove_child(const std::string &key, unsigned index);
void recursive_clear_value(const std::string& key);
void clear();
bool empty() const;
std::string debug() const;
std::string hash() const;
struct error : public game::error, public boost::exception {
error(const std::string& message) : game::error(message) {}
};
struct child_pos
{
child_pos(child_map::iterator p, unsigned i) : pos(p), index(i) {}
child_map::iterator pos;
unsigned index;
bool operator==(const child_pos& o) const { return pos == o.pos && index == o.index; }
bool operator!=(const child_pos& o) const { return !operator==(o); }
};
struct any_child
{
const child_map::key_type &key;
const config &cfg;
any_child(const child_map::key_type *k, const config *c): key(*k), cfg(*c) {}
};
struct all_children_iterator
{
struct arrow_helper
{
any_child data;
arrow_helper(const all_children_iterator &i): data(*i) {}
const any_child *operator->() const { return &data; }
};
typedef any_child value_type;
typedef std::forward_iterator_tag iterator_category;
typedef int difference_type;
typedef const arrow_helper pointer;
typedef const any_child reference;
typedef std::vector<child_pos>::const_iterator Itor;
explicit all_children_iterator(const Itor &i): i_(i) {}
all_children_iterator &operator++() { ++i_; return *this; }
all_children_iterator operator++(int) { return all_children_iterator(i_++); }
reference operator*() const;
pointer operator->() const { return *this; }
bool operator==(const all_children_iterator &i) const { return i_ == i.i_; }
bool operator!=(const all_children_iterator &i) const { return i_ != i.i_; }
private:
Itor i_;
friend class config;
};
typedef std::pair<all_children_iterator, all_children_iterator> all_children_itors;
/** In-order iteration over all children. */
all_children_itors all_children_range() const;
all_children_iterator ordered_begin() const;
all_children_iterator ordered_end() const;
all_children_iterator erase(const all_children_iterator& i);
/**
* A function to get the differences between this object,
* and 'c', as another config object.
* I.e. calling cfg2.apply_diff(cfg1.get_diff(cfg2))
* will make cfg1 identical to cfg2.
*/
config get_diff(const config& c) const;
void get_diff(const config& c, config& res) const;
/**
* The name of the attribute used for tracking diff changes
*/
static const char* diff_track_attribute;
/**
* A function to apply a diff config onto this config object.
*
* If the "track" parameter is true, the changes made will be marked in a
* magic attribute (defined above) of this and child nodes of this config,
* with "new" value indicating an added child, "modified" a modified one,
* and "deleted" for the deleted items, *which will not be actually
* deleted* (so calling code can easily see what they are).
* Use clear_diff_track with the same diff object to clear the tracking
* info and actually delete the nodes.
*/
void apply_diff(const config& diff, bool track = false); //throw error
/**
* Clear any tracking info from a previous apply_diff call with tracking.
* This also removes the nodes that are to be deleted, in effect making
* apply_diff(c, true); clear_diff_tracking(c);
* equivalent to apply_diff(c, false);
*/
void clear_diff_track(const config& diff);
/**
* Merge config 'c' into this config, overwriting this config's values.
*/
void merge_with(const config& c);
/**
* Merge config 'c' into this config, preserving this config's values.
*/
void inherit_from(const config& c);
bool matches(const config &filter) const;
/**
* Append data from another config object to this one.
* Attributes in the latter config object will clobber attributes in this one.
*/
void append(const config& cfg);
/**
* Adds children from @a cfg.
*/
void append_children(const config &cfg);
/**
* All children with the given key will be merged
* into the first element with that key.
*/
void merge_children(const std::string& key);
/**
* All children with the given key and with equal values
* of the specified attribute will be merged into the
* element with that key and that value of the attribute
*/
void merge_children_by_attribute(const std::string& key, const std::string& attribute);
//this is a cheap O(1) operation
void swap(config& cfg);
private:
/**
* Removes the child at position @a pos of @a l.
*/
std::vector<child_pos>::iterator remove_child(const child_map::iterator &l, unsigned pos);
/** All the attributes of this node. */
attribute_map values;
/** A list of all children of this node. */
child_map children;
std::vector<child_pos> ordered_children;
};
class variable_set
{
public:
virtual ~variable_set() {}
virtual config::attribute_value get_variable_const(const std::string &id) const = 0;
};
inline std::ostream &operator<<(std::ostream &os, const config::attribute_value::true_false &v) { return os << v.str(); }
inline std::ostream &operator<<(std::ostream &os, const config::attribute_value::yes_no &v) { return os << v.str(); }
#endif