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