1 /*
2 Copyright (C) 2003 by David White <dave@whitevine.net>
3 Copyright (C) 2005 - 2018 by Guillaume Melquiond <guillaume.melquiond@gmail.com>
4 Part of the Battle for Wesnoth Project https://www.wesnoth.org/
5
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY.
12
13 See the COPYING file for more details.
14 */
15
16 /**
17 * @file
18 * Routines related to configuration-files / WML.
19 */
20
21 #include "config.hpp"
22
23 #include "formatter.hpp"
24 #include "lexical_cast.hpp"
25 #include "log.hpp"
26 #include "utils/const_clone.hpp"
27 #include "utils/functional.hpp"
28 #include "deprecation.hpp"
29 #include "game_version.hpp"
30
31 #include <algorithm>
32 #include <cstdlib>
33 #include <cstring>
34 #include <deque>
35 #include <istream>
36 #include <locale>
37
38 #include <boost/variant/apply_visitor.hpp>
39 #include <boost/variant/get.hpp>
40 #include <boost/variant/static_visitor.hpp>
41 #include <boost/variant/variant.hpp>
42
43 static lg::log_domain log_config("config");
44 #define ERR_CF LOG_STREAM(err, log_config)
45 #define DBG_CF LOG_STREAM(debug, log_config)
46
47 namespace
48 {
49 // std::map::operator[] does not support heterogeneous lookup so we need this to work around.
50 template<typename Map, typename Key>
map_get(Map & map,Key && key)51 typename Map::mapped_type& map_get(Map& map, Key&& key)
52 {
53 auto res = map.lower_bound(key);
54
55 if(res == map.end() || key != res->first) {
56 res = map.emplace_hint(res, std::piecewise_construct, std::forward_as_tuple(key), std::tuple<>());
57 }
58
59 return res->second;
60 }
61
62 // std::map::erase does not support heterogeneous lookup so we need this to work around.
63 template<typename Map, typename Key>
map_erase_key(Map & map,Key && key)64 int map_erase_key(Map& map, Key&& key)
65 {
66 auto i = map.find(key);
67 if(i != map.end()) {
68 map.erase(i);
69 return 1;
70 }
71 return 0;
72 }
73 }
74
75 struct config_implementation
76 {
77 /**
78 * Implementation for the wrappers for
79 * [const] config& child(const std::string& key, const std::string& parent);
80 *
81 * @tparam T A pointer to the config.
82 */
83 template<class T>
childconfig_implementation84 static utils::const_clone_ref<config, T> child(T config, config_key_type key, const std::string& parent)
85 {
86 config->check_valid();
87
88 assert(!parent.empty());
89 assert(parent.front() == '[');
90 assert(parent.back() == ']');
91
92 if(config->has_child(key)) {
93 return *(config->children_.find(key)->second.front());
94 }
95
96 /**
97 * @todo Implement a proper wml_exception here.
98 *
99 * at the moment there seem to be dependency issues, which i don't want
100 * to fix right now.
101 */
102 // FAIL(missing_mandatory_wml_section(parent, key));
103
104 std::stringstream sstr;
105 sstr << "Mandatory WML child »[" << key << "]« missing in »" << parent << "«. Please report this bug.";
106
107 throw config::error(sstr.str());
108 }
109 };
110
111 /* ** config implementation ** */
112
113 config config::invalid;
114
115 const char* config::diff_track_attribute = "__diff_track";
116
check_valid() const117 void config::check_valid() const
118 {
119 if(!*this) {
120 throw error("Mandatory WML child missing yet untested for. Please report.");
121 }
122 }
123
check_valid(const config & cfg) const124 void config::check_valid(const config& cfg) const
125 {
126 if(!*this || !cfg) {
127 throw error("Mandatory WML child missing yet untested for. Please report.");
128 }
129 }
130
config()131 config::config()
132 : values_()
133 , children_()
134 , ordered_children()
135 {
136 }
137
config(const config & cfg)138 config::config(const config& cfg)
139 : values_(cfg.values_)
140 , children_()
141 , ordered_children()
142 {
143 append_children(cfg);
144 }
145
config(config_key_type child)146 config::config(config_key_type child)
147 : values_()
148 , children_()
149 , ordered_children()
150 {
151 add_child(child);
152 }
153
~config()154 config::~config()
155 {
156 clear();
157 }
158
operator =(const config & cfg)159 config& config::operator=(const config& cfg)
160 {
161 if(this == &cfg) {
162 return *this;
163 }
164
165 config tmp(cfg);
166 swap(tmp);
167 return *this;
168 }
169
config(config && cfg)170 config::config(config&& cfg)
171 : values_(std::move(cfg.values_))
172 , children_(std::move(cfg.children_))
173 , ordered_children(std::move(cfg.ordered_children))
174 {
175 }
176
operator =(config && cfg)177 config& config::operator=(config&& cfg)
178 {
179 clear();
180 swap(cfg);
181 return *this;
182 }
183
valid_tag(config_key_type name)184 bool config::valid_tag(config_key_type name)
185 {
186 if(name == "") {
187 // Empty strings not allowed
188 return false;
189 } else if(name == "_") {
190 // A lone underscore isn't a valid tag name
191 return false;
192 } else {
193 return std::all_of(name.begin(), name.end(), [](const char& c)
194 {
195 // Only alphanumeric ASCII characters and underscores are allowed
196 return std::isalnum(c, std::locale::classic()) || c == '_';
197 });
198 }
199 }
200
valid_attribute(config_key_type name)201 bool config::valid_attribute(config_key_type name)
202 {
203 if(name.empty()) {
204 return false;
205 }
206
207 for(char c : name) {
208 if(!std::isalnum(c, std::locale::classic()) && c != '_') {
209 return false;
210 }
211 }
212
213 return true;
214 }
215
has_attribute(config_key_type key) const216 bool config::has_attribute(config_key_type key) const
217 {
218 check_valid();
219 return values_.find(key) != values_.end();
220 }
221
has_old_attribute(config_key_type key,const std::string & old_key,const std::string & msg) const222 bool config::has_old_attribute(config_key_type key, const std::string& old_key, const std::string& msg) const
223 {
224 check_valid();
225 if(values_.find(key) != values_.end()) {
226 return true;
227 } else if(values_.find(old_key) != values_.end()) {
228 if(!msg.empty()) {
229 lg::wml_error() << msg;
230 }
231
232 return true;
233 }
234
235 return false;
236 }
237
remove_attribute(config_key_type key)238 void config::remove_attribute(config_key_type key)
239 {
240 check_valid();
241 map_erase_key(values_, key);
242 }
243
append_children(const config & cfg)244 void config::append_children(const config& cfg)
245 {
246 check_valid(cfg);
247
248 for(const any_child& value : cfg.all_children_range()) {
249 add_child(value.key, value.cfg);
250 }
251 }
252
append_children(config && cfg)253 void config::append_children(config&& cfg)
254 {
255 check_valid(cfg);
256
257 #if 0
258 //For some unknown reason this doesn't compile.
259 if(children_.empty()) {
260 //optimisation
261 children_ = std::move(cfg.children_);
262 ordered_children = std::move(cfg.ordered_children);
263 cfg.clear_all_children();
264 return;
265 }
266 #endif
267 for(const any_child& value : cfg.all_children_range()) {
268 add_child(value.key, std::move(value.cfg));
269 }
270 cfg.clear_all_children();
271 }
272
append_attributes(const config & cfg)273 void config::append_attributes(const config& cfg)
274 {
275 check_valid(cfg);
276 for(const attribute& v : cfg.values_) {
277 values_[v.first] = v.second;
278 }
279 }
280
append_children(const config & cfg,const std::string & key)281 void config::append_children(const config& cfg, const std::string& key)
282 {
283 check_valid(cfg);
284
285 for(const config& value : cfg.child_range(key)) {
286 add_child(key, value);
287 }
288 }
289
append(const config & cfg)290 void config::append(const config& cfg)
291 {
292 append_children(cfg);
293 for(const attribute& v : cfg.values_) {
294 values_[v.first] = v.second;
295 }
296 }
297
append(config && cfg)298 void config::append(config&& cfg)
299 {
300 append_children(std::move(cfg));
301
302 if(values_.empty()) {
303 //optimisation.
304 values_ = std::move(cfg.values_);
305 }
306 else {
307 for(const attribute& v : cfg.values_) {
308 //TODO: move the attributes as well?
309 values_[v.first] = v.second;
310 }
311 }
312 cfg.clear_attributes();
313 }
314
append_children_by_move(config & cfg,const std::string & key)315 void config::append_children_by_move(config& cfg, const std::string& key)
316 {
317 check_valid(cfg);
318
319 // DO note this leaves the tags empty in the source config. Not sure if
320 // that should be changed.
321 for(config& value : cfg.child_range(key)) {
322 add_child(key, std::move(value));
323 }
324
325 cfg.clear_children_impl(key);
326 }
327
merge_children(const std::string & key)328 void config::merge_children(const std::string& key)
329 {
330 check_valid();
331
332 if(child_count(key) < 2) {
333 return;
334 }
335
336 config merged_children;
337 for(config& cfg : child_range(key)) {
338 merged_children.append(std::move(cfg));
339 }
340
341 clear_children_impl(key);
342 add_child(key, std::move(merged_children));
343 }
344
merge_children_by_attribute(const std::string & key,const std::string & attribute)345 void config::merge_children_by_attribute(const std::string& key, const std::string& attribute)
346 {
347 check_valid();
348
349 if(child_count(key) < 2) {
350 return;
351 }
352
353 typedef std::map<std::string, config> config_map;
354 config_map merged_children_map;
355 for(config& cfg : child_range(key)) {
356 merged_children_map[cfg[attribute]].append(std::move(cfg));
357 }
358
359 clear_children_impl(key);
360 for(const config_map::value_type& i : merged_children_map) {
361 add_child(key, i.second); // TODO: can we use std::move?
362 }
363 }
364
child_range(config_key_type key)365 config::child_itors config::child_range(config_key_type key)
366 {
367 check_valid();
368
369 child_map::iterator i = children_.find(key);
370 static child_list dummy;
371 child_list* p = &dummy;
372 if(i != children_.end()) {
373 p = &i->second;
374 }
375
376 return child_itors(child_iterator(p->begin()), child_iterator(p->end()));
377 }
378
child_range(config_key_type key) const379 config::const_child_itors config::child_range(config_key_type key) const
380 {
381 check_valid();
382
383 child_map::const_iterator i = children_.find(key);
384 static child_list dummy;
385 const child_list* p = &dummy;
386 if(i != children_.end()) {
387 p = &i->second;
388 }
389
390 return const_child_itors(const_child_iterator(p->begin()), const_child_iterator(p->end()));
391 }
392
child_count(config_key_type key) const393 unsigned config::child_count(config_key_type key) const
394 {
395 check_valid();
396
397 child_map::const_iterator i = children_.find(key);
398 if(i != children_.end()) {
399 return i->second.size();
400 }
401
402 return 0;
403 }
404
all_children_count() const405 unsigned config::all_children_count() const
406 {
407 return ordered_children.size();
408 }
409
attribute_count() const410 unsigned config::attribute_count() const
411 {
412 return std::count_if(values_.begin(), values_.end(), [](const attribute& v) { return !v.second.blank(); });
413 }
414
has_child(config_key_type key) const415 bool config::has_child(config_key_type key) const
416 {
417 check_valid();
418
419 return children_.find(key) != children_.end();
420 }
421
child(config_key_type key,int n)422 config& config::child(config_key_type key, int n)
423 {
424 check_valid();
425
426 const child_map::const_iterator i = children_.find(key);
427 if(i == children_.end()) {
428 DBG_CF << "The config object has no child named »" << key << "«.\n";
429
430 return invalid;
431 }
432
433 if(n < 0)
434 n = i->second.size() + n;
435 if(size_t(n) < i->second.size()) {
436 return *i->second[n];
437 } else {
438 DBG_CF << "The config object has only »" << i->second.size() << "« children named »" << key
439 << "«; request for the index »" << n << "« cannot be honored.\n";
440
441 return invalid;
442 }
443 }
444
child(config_key_type key,const std::string & parent)445 config& config::child(config_key_type key, const std::string& parent)
446 {
447 return config_implementation::child(this, key, parent);
448 }
449
child(config_key_type key,const std::string & parent) const450 const config& config::child(config_key_type key, const std::string& parent) const
451 {
452 return config_implementation::child(this, key, parent);
453 }
454
child_or_empty(config_key_type key) const455 const config& config::child_or_empty(config_key_type key) const
456 {
457 static const config empty_cfg;
458 check_valid();
459
460 child_map::const_iterator i = children_.find(key);
461 if(i != children_.end() && !i->second.empty()) {
462 return *i->second.front();
463 }
464
465 return empty_cfg;
466 }
467
child_or_add(config_key_type key)468 config& config::child_or_add(config_key_type key)
469 {
470 child_map::const_iterator i = children_.find(key);
471 if(i != children_.end() && !i->second.empty()) {
472 return *i->second.front();
473 }
474
475 return add_child(key);
476 }
477
add_child(config_key_type key)478 config& config::add_child(config_key_type key)
479 {
480 check_valid();
481
482 child_list& v = map_get(children_, key);
483 v.emplace_back(new config());
484 ordered_children.emplace_back(children_.find(key), v.size() - 1);
485 return *v.back();
486 }
487
add_child(config_key_type key,const config & val)488 config& config::add_child(config_key_type key, const config& val)
489 {
490 check_valid(val);
491
492 child_list& v = map_get(children_, key);
493 v.emplace_back(new config(val));
494 ordered_children.emplace_back(children_.find(key), v.size() - 1);
495
496 return *v.back();
497 }
498
add_child(config_key_type key,config && val)499 config& config::add_child(config_key_type key, config&& val)
500 {
501 check_valid(val);
502
503 child_list& v = map_get(children_, key);
504 v.emplace_back(new config(std::move(val)));
505 ordered_children.emplace_back(children_.find(key), v.size() - 1);
506
507 return *v.back();
508 }
509
add_child_at(config_key_type key,const config & val,unsigned index)510 config& config::add_child_at(config_key_type key, const config& val, unsigned index)
511 {
512 check_valid(val);
513
514 child_list& v = map_get(children_, key);
515 if(index > v.size()) {
516 throw error("illegal index to add child at");
517 }
518
519 v.emplace(v.begin() + index, new config(val));
520
521 bool inserted = false;
522
523 const child_pos value(children_.find(key), index);
524
525 std::vector<child_pos>::iterator ord = ordered_children.begin();
526 for(; ord != ordered_children.end(); ++ord) {
527 if(ord->pos != value.pos)
528 continue;
529 if(!inserted && ord->index == index) {
530 ord = ordered_children.insert(ord, value);
531 inserted = true;
532 } else if(ord->index >= index) {
533 ord->index++;
534 }
535 }
536
537 if(!inserted) {
538 ordered_children.push_back(value);
539 }
540
541 return *v[index];
542 }
543
544 namespace
545 {
546 struct remove_ordered
547 {
remove_ordered__anon13feaf990411::remove_ordered548 remove_ordered(const config::child_map::iterator& iter)
549 : iter_(iter)
550 {
551 }
552
operator ()__anon13feaf990411::remove_ordered553 bool operator()(const config::child_pos& pos) const
554 {
555 return pos.pos == iter_;
556 }
557
558 private:
559 config::child_map::iterator iter_;
560 };
561 } // end anon namespace
562
clear_children_impl(config_key_type key)563 void config::clear_children_impl(config_key_type key)
564 {
565 check_valid();
566
567 child_map::iterator i = children_.find(key);
568 if(i == children_.end())
569 return;
570
571 ordered_children.erase(
572 std::remove_if(ordered_children.begin(), ordered_children.end(), remove_ordered(i)),
573 ordered_children.end());
574
575 children_.erase(i);
576 }
577
splice_children(config & src,const std::string & key)578 void config::splice_children(config& src, const std::string& key)
579 {
580 check_valid(src);
581
582 child_map::iterator i_src = src.children_.find(key);
583 if(i_src == src.children_.end()) {
584 return;
585 }
586
587 src.ordered_children.erase(
588 std::remove_if(src.ordered_children.begin(), src.ordered_children.end(), remove_ordered(i_src)),
589 src.ordered_children.end());
590
591 child_list& dst = map_get(children_, key);
592 child_map::iterator i_dst = children_.find(key);
593
594 unsigned before = dst.size();
595 dst.insert(dst.end(), std::make_move_iterator(i_src->second.begin()), std::make_move_iterator(i_src->second.end()));
596 src.children_.erase(i_src);
597 // key might be a reference to i_src->first, so it is no longer usable.
598
599 for(unsigned j = before; j < dst.size(); ++j) {
600 ordered_children.emplace_back(i_dst, j);
601 }
602 }
603
recursive_clear_value(config_key_type key)604 void config::recursive_clear_value(config_key_type key)
605 {
606 check_valid();
607
608 map_erase_key(values_, key);
609
610 for(std::pair<const std::string, child_list>& p : children_) {
611 for(auto& cfg : p.second) {
612 cfg->recursive_clear_value(key);
613 }
614 }
615 }
616
remove_child(const child_map::iterator & pos,unsigned index)617 std::vector<config::child_pos>::iterator config::remove_child(const child_map::iterator& pos, unsigned index)
618 {
619 /* Find the position with the correct index and decrement all the
620 indices in the ordering that are above this index. */
621 unsigned found = 0;
622 for(child_pos& p : ordered_children) {
623 if(p.pos != pos) {
624 continue;
625 }
626
627 if(p.index == index) {
628 found = &p - &ordered_children.front();
629 } else if(p.index > index) {
630 --p.index;
631 }
632 }
633
634 // Remove from the child map.
635 pos->second.erase(pos->second.begin() + index);
636
637 // Erase from the ordering and return the next position.
638 return ordered_children.erase(ordered_children.begin() + found);
639 }
640
erase(const config::all_children_iterator & i)641 config::all_children_iterator config::erase(const config::all_children_iterator& i)
642 {
643 return all_children_iterator(remove_child(i.i_->pos, i.i_->index));
644 }
645
remove_child(config_key_type key,unsigned index)646 void config::remove_child(config_key_type key, unsigned index)
647 {
648 check_valid();
649
650 child_map::iterator i = children_.find(key);
651 if(i == children_.end() || index >= i->second.size()) {
652 ERR_CF << "Error: attempting to delete non-existing child: " << key << "[" << index << "]\n";
653 return;
654 }
655
656 remove_child(i, index);
657 }
658
remove_children(config_key_type key,std::function<bool (const config &)> p)659 void config::remove_children(config_key_type key, std::function<bool(const config&)> p)
660 {
661 check_valid();
662
663 child_map::iterator pos = children_.find(key);
664 if(pos == children_.end()) {
665 return;
666 }
667
668 const auto predicate = [p](const std::unique_ptr<config>& child)
669 {
670 return p(*child);
671 };
672
673 auto child_it = std::find_if(pos->second.begin(), pos->second.end(), predicate);
674 while(child_it != pos->second.end()) {
675 unsigned index = std::distance(pos->second.begin(), child_it);
676 remove_child(pos, index);
677 child_it = std::find_if(pos->second.begin() + index, pos->second.end(), predicate);
678 }
679 }
680
operator [](config_key_type key) const681 const config::attribute_value& config::operator[](config_key_type key) const
682 {
683 check_valid();
684
685 const attribute_map::const_iterator i = values_.find(key);
686 if(i != values_.end()) {
687 return i->second;
688 }
689
690 static const attribute_value empty_attribute;
691 return empty_attribute;
692 }
693
get(config_key_type key) const694 const config::attribute_value* config::get(config_key_type key) const
695 {
696 check_valid();
697 attribute_map::const_iterator i = values_.find(key);
698 return i != values_.end() ? &i->second : nullptr;
699 }
700
operator [](config_key_type key)701 config::attribute_value& config::operator[](config_key_type key)
702 {
703 check_valid();
704
705 auto res = values_.lower_bound(key);
706
707 if(res == values_.end() || key != res->first) {
708 res = values_.emplace_hint(res, std::piecewise_construct, std::forward_as_tuple(key), std::tuple<>());
709 }
710
711 return res->second;
712 }
713
get_old_attribute(config_key_type key,const std::string & old_key,const std::string & in_tag) const714 const config::attribute_value& config::get_old_attribute(
715 config_key_type key, const std::string& old_key, const std::string& in_tag) const
716 {
717 check_valid();
718
719 attribute_map::const_iterator i = values_.find(key);
720 if(i != values_.end()) {
721 return i->second;
722 }
723
724 i = values_.find(old_key);
725 if(i != values_.end()) {
726 if(!in_tag.empty()) {
727 const std::string what = formatter() << "[" << in_tag << "]" << old_key << "=";
728 const std::string msg = formatter() << "Use " << key << "= instead.";
729 deprecated_message(what, DEP_LEVEL::INDEFINITE, "", msg);
730 lg::wml_error() << msg;
731 }
732
733 return i->second;
734 }
735
736 static const attribute_value empty_attribute;
737 return empty_attribute;
738 }
739
merge_attributes(const config & cfg)740 void config::merge_attributes(const config& cfg)
741 {
742 check_valid(cfg);
743
744 assert(this != &cfg);
745 for(const attribute& v : cfg.values_) {
746 std::string key = v.first;
747 if(key.substr(0, 7) == "add_to_") {
748 std::string add_to = key.substr(7);
749 values_[add_to] = values_[add_to].to_double() + v.second.to_double();
750 } else if(key.substr(0, 10) == "concat_to_") {
751 std::string concat_to = key.substr(10);
752 // TODO: Only use t_string if one or both are actually translatable?
753 // That probably requires using a visitor though.
754 values_[concat_to] = values_[concat_to].t_str() + v.second.t_str();
755 } else {
756 values_[v.first] = v.second;
757 }
758 }
759 }
760
attribute_range() const761 config::const_attr_itors config::attribute_range() const
762 {
763 check_valid();
764
765 const_attr_itors range(const_attribute_iterator(values_.begin()), const_attribute_iterator(values_.end()));
766
767 // Ensure the first element is not blank, as a few places assume this
768 while(range.begin() != range.end() && range.begin()->second.blank()) {
769 range.pop_front();
770 }
771
772 return range;
773 }
774
attribute_range()775 config::attr_itors config::attribute_range()
776 {
777 check_valid();
778 attr_itors range(attribute_iterator(values_.begin()), attribute_iterator(values_.end()));
779
780 // Ensure the first element is not blank, as a few places assume this
781 while(range.begin() != range.end() && range.begin()->second.blank()) {
782 range.pop_front();
783 }
784
785 return range;
786 }
787
find_child(config_key_type key,const std::string & name,const std::string & value)788 config& config::find_child(config_key_type key, const std::string& name, const std::string& value)
789 {
790 check_valid();
791
792 const child_map::iterator i = children_.find(key);
793 if(i == children_.end()) {
794 DBG_CF << "Key »" << name << "« value »" << value << "« pair not found as child of key »" << key << "«.\n";
795
796 return invalid;
797 }
798
799 const child_list::iterator j = std::find_if(i->second.begin(), i->second.end(),
800 [&](const std::unique_ptr<config>& pcfg) {
801 const config& cfg = *pcfg;
802 return cfg[name] == value;
803 }
804 );
805
806 if(j != i->second.end()) {
807 return **j;
808 }
809
810 DBG_CF << "Key »" << name << "« value »" << value << "« pair not found as child of key »" << key << "«.\n";
811
812 return invalid;
813 }
814
clear()815 void config::clear()
816 {
817 // No validity check for this function.
818 children_.clear();
819 values_.clear();
820 ordered_children.clear();
821 }
822
clear_all_children()823 void config::clear_all_children()
824 {
825 // No validity check for this function.
826 children_.clear();
827 ordered_children.clear();
828 }
829
clear_attributes()830 void config::clear_attributes()
831 {
832 // No validity check for this function.
833 values_.clear();
834 }
835
empty() const836 bool config::empty() const
837 {
838 check_valid();
839
840 return children_.empty() && values_.empty();
841 }
842
operator *() const843 config::all_children_iterator::reference config::all_children_iterator::operator*() const
844 {
845 return any_child(&i_->pos->first, i_->pos->second[i_->index].get());
846 }
847
operator *() const848 config::const_all_children_iterator::reference config::const_all_children_iterator::operator*() const
849 {
850 return any_child(&i_->pos->first, i_->pos->second[i_->index].get());
851 }
852
ordered_begin() const853 config::const_all_children_iterator config::ordered_begin() const
854 {
855 return const_all_children_iterator(ordered_children.cbegin());
856 }
857
ordered_cbegin() const858 config::const_all_children_iterator config::ordered_cbegin() const
859 {
860 return const_all_children_iterator(ordered_children.cbegin());
861 }
862
ordered_end() const863 config::const_all_children_iterator config::ordered_end() const
864 {
865 return const_all_children_iterator(ordered_children.cend());
866 }
867
ordered_cend() const868 config::const_all_children_iterator config::ordered_cend() const
869 {
870 return const_all_children_iterator(ordered_children.cend());
871 }
872
all_children_range() const873 config::const_all_children_itors config::all_children_range() const
874 {
875 return const_all_children_itors(
876 const_all_children_iterator(ordered_children.cbegin()),
877 const_all_children_iterator(ordered_children.cend())
878 );
879 }
880
ordered_begin()881 config::all_children_iterator config::ordered_begin()
882 {
883 return all_children_iterator(ordered_children.begin());
884 }
885
ordered_end()886 config::all_children_iterator config::ordered_end()
887 {
888 return all_children_iterator(ordered_children.end());
889 }
890
all_children_range()891 config::all_children_itors config::all_children_range()
892 {
893 return all_children_itors(
894 all_children_iterator(ordered_children.begin()),
895 all_children_iterator(ordered_children.end())
896 );
897 }
898
get_diff(const config & c) const899 config config::get_diff(const config& c) const
900 {
901 check_valid(c);
902
903 config res;
904 get_diff(c, res);
905 return res;
906 }
907
get_diff(const config & c,config & res) const908 void config::get_diff(const config& c, config& res) const
909 {
910 check_valid(c);
911 check_valid(res);
912
913 config* inserts = nullptr;
914
915 for(const auto& v : values_) {
916 if(v.second.blank()) {
917 continue;
918 }
919
920 const attribute_map::const_iterator j = c.values_.find(v.first);
921 if(j == c.values_.end() || (v.second != j->second && !v.second.blank())) {
922 if(inserts == nullptr) {
923 inserts = &res.add_child("insert");
924 }
925
926 (*inserts)[v.first] = v.second;
927 }
928 }
929
930 config* deletes = nullptr;
931
932 for(const auto& v : c.values_) {
933 if(v.second.blank()) {
934 continue;
935 }
936
937 const attribute_map::const_iterator itor = values_.find(v.first);
938 if(itor == values_.end() || itor->second.blank()) {
939 if(deletes == nullptr) {
940 deletes = &res.add_child("delete");
941 }
942
943 (*deletes)[v.first] = "x";
944 }
945 }
946
947 std::vector<std::string> entities;
948
949 for(const auto& child : children_) {
950 entities.push_back(child.first);
951 }
952
953 for(const auto& child : c.children_) {
954 if(children_.count(child.first) == 0) {
955 entities.push_back(child.first);
956 }
957 }
958
959 for(const std::string& entity : entities) {
960 const child_map::const_iterator itor_a = children_.find(entity);
961 const child_map::const_iterator itor_b = c.children_.find(entity);
962
963 static const child_list dummy;
964
965 // Get the two child lists. 'b' has to be modified to look like 'a'.
966 const child_list& a = itor_a != children_.end() ? itor_a->second : dummy;
967 const child_list& b = itor_b != c.children_.end() ? itor_b->second : dummy;
968
969 size_t ndeletes = 0;
970 size_t ai = 0, bi = 0;
971 while(ai != a.size() || bi != b.size()) {
972 // If the two elements are the same, nothing needs to be done.
973 if(ai < a.size() && bi < b.size() && *a[ai] == *b[bi]) {
974 ++ai;
975 ++bi;
976 } else {
977 // We have to work out what the most appropriate operation --
978 // delete, insert, or change is the best to get b[bi] looking like a[ai].
979 std::stringstream buf;
980
981 // If b has more elements than a, then we assume this element
982 // is an element that needs deleting.
983 if(b.size() - bi > a.size() - ai) {
984 config& new_delete = res.add_child("delete_child");
985 buf << bi - ndeletes;
986 new_delete.values_["index"] = buf.str();
987 new_delete.add_child(entity);
988
989 ++ndeletes;
990 ++bi;
991 }
992
993 // If b has less elements than a, then we assume this element
994 // is an element that needs inserting.
995 else if(b.size() - bi < a.size() - ai) {
996 config& new_insert = res.add_child("insert_child");
997 buf << ai;
998 new_insert.values_["index"] = buf.str();
999 new_insert.add_child(entity, *a[ai]);
1000
1001 ++ai;
1002 }
1003
1004 // Otherwise, they have the same number of elements,
1005 // so try just changing this element to match.
1006 else {
1007 config& new_change = res.add_child("change_child");
1008 buf << bi;
1009 new_change.values_["index"] = buf.str();
1010 new_change.add_child(entity, a[ai]->get_diff(*b[bi]));
1011
1012 ++ai;
1013 ++bi;
1014 }
1015 }
1016 }
1017 }
1018 }
1019
apply_diff(const config & diff,bool track)1020 void config::apply_diff(const config& diff, bool track /* = false */)
1021 {
1022 check_valid(diff);
1023
1024 if(track) {
1025 values_[diff_track_attribute] = "modified";
1026 }
1027
1028 if(const config& inserts = diff.child("insert")) {
1029 for(const attribute& v : inserts.attribute_range()) {
1030 values_[v.first] = v.second;
1031 }
1032 }
1033
1034 if(const config& deletes = diff.child("delete")) {
1035 for(const attribute& v : deletes.attribute_range()) {
1036 values_.erase(v.first);
1037 }
1038 }
1039
1040 for(const config& i : diff.child_range("change_child")) {
1041 const size_t index = lexical_cast<size_t>(i["index"].str());
1042 for(const any_child& item : i.all_children_range()) {
1043 if(item.key.empty()) {
1044 continue;
1045 }
1046
1047 const child_map::iterator itor = children_.find(item.key);
1048 if(itor == children_.end() || index >= itor->second.size()) {
1049 throw error("error in diff: could not find element '" + item.key + "'");
1050 }
1051
1052 itor->second[index]->apply_diff(item.cfg, track);
1053 }
1054 }
1055
1056 for(const config& i : diff.child_range("insert_child")) {
1057 const size_t index = lexical_cast<size_t>(i["index"].str());
1058 for(const any_child& item : i.all_children_range()) {
1059 config& inserted = add_child_at(item.key, item.cfg, index);
1060 if(track) {
1061 inserted[diff_track_attribute] = "new";
1062 }
1063 }
1064 }
1065
1066 for(const config& i : diff.child_range("delete_child")) {
1067 const size_t index = lexical_cast<size_t>(i["index"].str());
1068 for(const any_child& item : i.all_children_range()) {
1069 if(!track) {
1070 remove_child(item.key, index);
1071 } else {
1072 const child_map::iterator itor = children_.find(item.key);
1073 if(itor == children_.end() || index >= itor->second.size()) {
1074 throw error("error in diff: could not find element '" + item.key + "'");
1075 }
1076
1077 itor->second[index]->values_[diff_track_attribute] = "deleted";
1078 }
1079 }
1080 }
1081 }
1082
clear_diff_track(const config & diff)1083 void config::clear_diff_track(const config& diff)
1084 {
1085 remove_attribute(diff_track_attribute);
1086 for(const config& i : diff.child_range("delete_child")) {
1087 const size_t index = lexical_cast<size_t>(i["index"].str());
1088 for(const any_child& item : i.all_children_range()) {
1089 remove_child(item.key, index);
1090 }
1091 }
1092
1093 for(const config& i : diff.child_range("change_child")) {
1094 const size_t index = lexical_cast<size_t>(i["index"].str());
1095 for(const any_child& item : i.all_children_range()) {
1096 if(item.key.empty()) {
1097 continue;
1098 }
1099
1100 const child_map::iterator itor = children_.find(item.key);
1101 if(itor == children_.end() || index >= itor->second.size()) {
1102 throw error("error in diff: could not find element '" + item.key + "'");
1103 }
1104
1105 itor->second[index]->clear_diff_track(item.cfg);
1106 }
1107 }
1108
1109 for(std::pair<const std::string, child_list>& p : children_) {
1110 for(auto& cfg : p.second) {
1111 cfg->remove_attribute(diff_track_attribute);
1112 }
1113 }
1114 }
1115
1116 /**
1117 * Merge config 'c' into this config, overwriting this config's values.
1118 */
merge_with(const config & c)1119 void config::merge_with(const config& c)
1120 {
1121 check_valid(c);
1122
1123 std::vector<child_pos> to_remove;
1124 std::map<std::string, unsigned> visitations;
1125
1126 // Merge attributes first
1127 merge_attributes(c);
1128
1129 // Now merge shared tags
1130 all_children_iterator::Itor i, i_end = ordered_children.end();
1131 for(i = ordered_children.begin(); i != i_end; ++i) {
1132 const std::string& tag = i->pos->first;
1133 const child_map::const_iterator j = c.children_.find(tag);
1134
1135 if(j != c.children_.end()) {
1136 unsigned& visits = visitations[tag];
1137
1138 if(visits < j->second.size()) {
1139 // Get a const config so we do not add attributes.
1140 const config& merge_child = *j->second[visits++];
1141
1142 if(merge_child["__remove"].to_bool()) {
1143 to_remove.push_back(*i);
1144 } else {
1145 (i->pos->second[i->index])->merge_with(merge_child);
1146 }
1147 }
1148 }
1149 }
1150
1151 // Now add any unvisited tags
1152 for(const auto& pair : c.children_) {
1153 const std::string& tag = pair.first;
1154 unsigned& visits = visitations[tag];
1155 while(visits < pair.second.size()) {
1156 add_child(tag, *pair.second[visits++]);
1157 }
1158 }
1159
1160 // Remove those marked so
1161 std::map<std::string, unsigned> removals;
1162 for(const child_pos& pos : to_remove) {
1163 const std::string& tag = pos.pos->first;
1164 unsigned& removes = removals[tag];
1165 remove_child(tag, pos.index - removes++);
1166 }
1167 }
1168
1169 /**
1170 * Merge config 'c' into this config, preserving this config's values.
1171 */
inherit_from(const config & c)1172 void config::inherit_from(const config& c)
1173 {
1174 // Using a scratch config and merge_with() seems to execute about as fast
1175 // as direct coding of this merge.
1176 config scratch(c);
1177 scratch.merge_with(*this);
1178 swap(scratch);
1179 }
1180
matches(const config & filter) const1181 bool config::matches(const config& filter) const
1182 {
1183 check_valid(filter);
1184
1185 for(const attribute& i : filter.attribute_range()) {
1186 const attribute_value* v = get(i.first);
1187 if(!v || *v != i.second) {
1188 return false;
1189 }
1190 }
1191
1192 for(const any_child& i : filter.all_children_range()) {
1193 if(i.key == "not") {
1194 if(matches(i.cfg)) {
1195 return false;
1196 }
1197
1198 continue;
1199 }
1200
1201 bool found = false;
1202 for(const config& j : child_range(i.key)) {
1203 if(j.matches(i.cfg)) {
1204 found = true;
1205 break;
1206 }
1207 }
1208
1209 if(!found) {
1210 return false;
1211 }
1212 }
1213
1214 return true;
1215 }
1216
debug() const1217 std::string config::debug() const
1218 {
1219 check_valid();
1220
1221 std::ostringstream outstream;
1222 outstream << *this;
1223 return outstream.str();
1224 }
1225
operator <<(std::ostream & outstream,const config & cfg)1226 std::ostream& operator<<(std::ostream& outstream, const config& cfg)
1227 {
1228 static int i = 0;
1229 i++;
1230
1231 for(const config::attribute& val : cfg.attribute_range()) {
1232 if(val.second.blank()) {
1233 continue;
1234 }
1235
1236 for(int j = 0; j < i - 1; j++) {
1237 outstream << '\t';
1238 }
1239
1240 outstream << val.first << " = " << val.second << '\n';
1241 }
1242
1243 for(const config::any_child& child : cfg.all_children_range()) {
1244 for(int j = 0; j < i - 1; ++j) {
1245 outstream << '\t';
1246 }
1247
1248 outstream << "[" << child.key << "]\n";
1249 outstream << child.cfg;
1250
1251 for(int j = 0; j < i - 1; ++j) {
1252 outstream << '\t';
1253 }
1254
1255 outstream << "[/" << child.key << "]\n";
1256 }
1257
1258 i--;
1259 return outstream;
1260 }
1261
hash() const1262 std::string config::hash() const
1263 {
1264 check_valid();
1265
1266 static const unsigned int hash_length = 128;
1267 static const char hash_string[] = "+-,.<>0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
1268 char hash_str[hash_length + 1];
1269
1270 unsigned int i;
1271 for(i = 0; i != hash_length; ++i) {
1272 hash_str[i] = 'a';
1273 }
1274
1275 hash_str[hash_length] = 0;
1276
1277 i = 0;
1278 for(const attribute& val : values_) {
1279 if(val.second.blank()) {
1280 continue;
1281 }
1282
1283 for(char c : val.first) {
1284 hash_str[i] ^= c;
1285 if(++i == hash_length) {
1286 i = 0;
1287 }
1288 }
1289
1290 std::string base_str = val.second.t_str().base_str();
1291 for(const char c : base_str) {
1292 hash_str[i] ^= c;
1293 if(++i == hash_length) {
1294 i = 0;
1295 }
1296 }
1297 }
1298
1299 for(const any_child& ch : all_children_range()) {
1300 std::string child_hash = ch.cfg.hash();
1301 for(char c : child_hash) {
1302 hash_str[i] ^= c;
1303 ++i;
1304 if(i == hash_length) {
1305 i = 0;
1306 }
1307 }
1308 }
1309
1310 for(i = 0; i != hash_length; ++i) {
1311 hash_str[i] = hash_string[static_cast<unsigned>(hash_str[i]) % strlen(hash_string)];
1312 }
1313
1314 return std::string(hash_str);
1315 }
1316
swap(config & cfg)1317 void config::swap(config& cfg)
1318 {
1319 check_valid(cfg);
1320
1321 values_.swap(cfg.values_);
1322 children_.swap(cfg.children_);
1323 ordered_children.swap(cfg.ordered_children);
1324 }
1325
swap(config & lhs,config & rhs)1326 void swap(config& lhs, config& rhs)
1327 {
1328 lhs.swap(rhs);
1329 }
1330
validate_wml() const1331 bool config::validate_wml() const
1332 {
1333 return std::all_of(children_.begin(), children_.end(), [](const child_map::value_type& pair)
1334 {
1335 return valid_tag(pair.first) &&
1336 std::all_of(pair.second.begin(), pair.second.end(),
1337 [](const std::unique_ptr<config>& c) { return c->validate_wml(); });
1338 });
1339 }
1340
operator ==(const config & a,const config & b)1341 bool operator==(const config& a, const config& b)
1342 {
1343 a.check_valid(b);
1344
1345 if(a.values_ != b.values_) {
1346 return false;
1347 }
1348
1349 config::const_all_children_itors x = a.all_children_range(), y = b.all_children_range();
1350 for(; !x.empty() && !y.empty(); x.pop_front(), y.pop_front()) {
1351 if(x.front().key != y.front().key || x.front().cfg != y.front().cfg) {
1352 return false;
1353 }
1354 }
1355
1356 return x.empty() && y.empty();
1357 }
1358