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 http://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 "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[0] == '[');
91  assert(parent[parent.size() - 1] == ']');
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 
545 namespace
546 {
547 struct remove_ordered
548 {
549  remove_ordered(const config::child_map::iterator& iter)
550  : iter_(iter)
551  {
552  }
553 
554  bool operator()(const config::child_pos& pos) const
555  {
556  return pos.pos == iter_;
557  }
558 
559 private:
561 };
562 } // end anon namespace
563 
565 {
566  check_valid();
567 
568  child_map::iterator i = children_.find(key);
569  if(i == children_.end())
570  return;
571 
572  ordered_children.erase(
573  std::remove_if(ordered_children.begin(), ordered_children.end(), remove_ordered(i)),
574  ordered_children.end());
575 
576  children_.erase(i);
577 }
578 
579 void config::splice_children(config& src, const std::string& key)
580 {
581  check_valid(src);
582 
583  child_map::iterator i_src = src.children_.find(key);
584  if(i_src == src.children_.end()) {
585  return;
586  }
587 
588  src.ordered_children.erase(
589  std::remove_if(src.ordered_children.begin(), src.ordered_children.end(), remove_ordered(i_src)),
590  src.ordered_children.end());
591 
592  child_list& dst = map_get(children_, key);
593  child_map::iterator i_dst = children_.find(key);
594 
595  unsigned before = dst.size();
596  dst.insert(dst.end(), std::make_move_iterator(i_src->second.begin()), std::make_move_iterator(i_src->second.end()));
597  src.children_.erase(i_src);
598  // key might be a reference to i_src->first, so it is no longer usable.
599 
600  for(unsigned j = before; j < dst.size(); ++j) {
601  ordered_children.emplace_back(i_dst, j);
602  }
603 }
604 
606 {
607  check_valid();
608 
609  map_erase_key(values_, key);
610 
611  for(std::pair<const std::string, child_list>& p : children_) {
612  for(auto& cfg : p.second) {
613  cfg->recursive_clear_value(key);
614  }
615  }
616 }
617 
619 {
620  /* Find the position with the correct index and decrement all the
621  indices in the ordering that are above this index. */
622  unsigned found = 0;
623  for(child_pos& p : ordered_children) {
624  if(p.pos != pos) {
625  continue;
626  }
627 
628  if(p.index == index) {
629  found = &p - &ordered_children.front();
630  } else if(p.index > index) {
631  --p.index;
632  }
633  }
634 
635  // Remove from the child map.
636  pos->second.erase(pos->second.begin() + index);
637 
638  // Erase from the ordering and return the next position.
639  return ordered_children.erase(ordered_children.begin() + found);
640 }
641 
643 {
644  return all_children_iterator(remove_child(i.i_->pos, i.i_->index));
645 }
646 
648 {
649  check_valid();
650 
651  child_map::iterator i = children_.find(key);
652  if(i == children_.end() || index >= i->second.size()) {
653  ERR_CF << "Error: attempting to delete non-existing child: " << key << "[" << index << "]\n";
654  return;
655  }
656 
657  remove_child(i, index);
658 }
659 
660 void config::remove_children(config_key_type key, std::function<bool(const config&)> p)
661 {
662  check_valid();
663 
664  child_map::iterator pos = children_.find(key);
665  if(pos == children_.end()) {
666  return;
667  }
668 
669  const auto predicate = [p](const std::unique_ptr<config>& child)
670  {
671  return p(*child);
672  };
673 
674  auto child_it = std::find_if(pos->second.begin(), pos->second.end(), predicate);
675  while(child_it != pos->second.end()) {
676  unsigned index = std::distance(pos->second.begin(), child_it);
677  remove_child(pos, index);
678  child_it = std::find_if(pos->second.begin() + index, pos->second.end(), predicate);
679  }
680 }
681 
683 {
684  check_valid();
685 
686  const attribute_map::const_iterator i = values_.find(key);
687  if(i != values_.end()) {
688  return i->second;
689  }
690 
691  static const attribute_value empty_attribute;
692  return empty_attribute;
693 }
694 
696 {
697  check_valid();
698  attribute_map::const_iterator i = values_.find(key);
699  return i != values_.end() ? &i->second : nullptr;
700 }
701 
703 {
704  check_valid();
705 
706  auto res = values_.lower_bound(key);
707 
708  if(res == values_.end() || key != res->first) {
709  res = values_.emplace_hint(res, std::piecewise_construct, std::forward_as_tuple(key), std::tuple<>());
710  }
711 
712  return res->second;
713 }
714 
716  config_key_type key, const std::string& old_key, const std::string& in_tag) const
717 {
718  check_valid();
719 
720  attribute_map::const_iterator i = values_.find(key);
721  if(i != values_.end()) {
722  return i->second;
723  }
724 
725  i = values_.find(old_key);
726  if(i != values_.end()) {
727  if(!in_tag.empty()) {
728  const std::string what = formatter() << "[" << in_tag << "]" << old_key << "=";
729  const std::string msg = formatter() << "Use " << key << "= instead.";
731  lg::wml_error() << msg;
732  }
733 
734  return i->second;
735  }
736 
737  static const attribute_value empty_attribute;
738  return empty_attribute;
739 }
740 
742 {
743  check_valid(cfg);
744 
745  assert(this != &cfg);
746  for(const attribute& v : cfg.values_) {
747  std::string key = v.first;
748  if(key.substr(0, 7) == "add_to_") {
749  std::string add_to = key.substr(7);
750  values_[add_to] = values_[add_to].to_double() + v.second.to_double();
751  } else if(key.substr(0, 10) == "concat_to_") {
752  std::string concat_to = key.substr(10);
753  // TODO: Only use t_string if one or both are actually translatable?
754  // That probably requires using a visitor though.
755  values_[concat_to] = values_[concat_to].t_str() + v.second.t_str();
756  } else {
757  values_[v.first] = v.second;
758  }
759  }
760 }
761 
763 {
764  check_valid();
765 
767 
768  // Ensure the first element is not blank, as a few places assume this
769  while(range.begin() != range.end() && range.begin()->second.blank()) {
770  range.pop_front();
771  }
772 
773  return range;
774 }
775 
777 {
778  check_valid();
780 
781  // Ensure the first element is not blank, as a few places assume this
782  while(range.begin() != range.end() && range.begin()->second.blank()) {
783  range.pop_front();
784  }
785 
786  return range;
787 }
788 
789 config& config::find_child(config_key_type key, const std::string& name, const std::string& value)
790 {
791  check_valid();
792 
793  const child_map::iterator i = children_.find(key);
794  if(i == children_.end()) {
795  DBG_CF << "Key »" << name << "« value »" << value << "« pair not found as child of key »" << key << "«.\n";
796 
797  return invalid;
798  }
799 
800  const child_list::iterator j = std::find_if(i->second.begin(), i->second.end(),
801  [&](const std::unique_ptr<config>& pcfg) {
802  const config& cfg = *pcfg;
803  return cfg[name] == value;
804  }
805  );
806 
807  if(j != i->second.end()) {
808  return **j;
809  }
810 
811  DBG_CF << "Key »" << name << "« value »" << value << "« pair not found as child of key »" << key << "«.\n";
812 
813  return invalid;
814 }
815 
817 {
818  // No validity check for this function.
819  children_.clear();
820  values_.clear();
821  ordered_children.clear();
822 }
823 
825 {
826  // No validity check for this function.
827  children_.clear();
828  ordered_children.clear();
829 }
830 
832 {
833  // No validity check for this function.
834  values_.clear();
835 }
836 
837 bool config::empty() const
838 {
839  check_valid();
840 
841  return children_.empty() && values_.empty();
842 }
843 
845 {
846  return any_child(&i_->pos->first, i_->pos->second[i_->index].get());
847 }
848 
850 {
851  return any_child(&i_->pos->first, i_->pos->second[i_->index].get());
852 }
853 
855 {
857 }
858 
860 {
862 }
863 
865 {
867 }
868 
870 {
872 }
873 
875 {
879  );
880 }
881 
883 {
884  return all_children_iterator(ordered_children.begin());
885 }
886 
888 {
890 }
891 
893 {
894  return all_children_itors(
897  );
898 }
899 
901 {
902  check_valid(c);
903 
904  config res;
905  get_diff(c, res);
906  return res;
907 }
908 
909 void config::get_diff(const config& c, config& res) const
910 {
911  check_valid(c);
912  check_valid(res);
913 
914  config* inserts = nullptr;
915 
916  for(const auto& v : values_) {
917  if(v.second.blank()) {
918  continue;
919  }
920 
921  const attribute_map::const_iterator j = c.values_.find(v.first);
922  if(j == c.values_.end() || (v.second != j->second && !v.second.blank())) {
923  if(inserts == nullptr) {
924  inserts = &res.add_child("insert");
925  }
926 
927  (*inserts)[v.first] = v.second;
928  }
929  }
930 
931  config* deletes = nullptr;
932 
933  for(const auto& v : c.values_) {
934  if(v.second.blank()) {
935  continue;
936  }
937 
938  const attribute_map::const_iterator itor = values_.find(v.first);
939  if(itor == values_.end() || itor->second.blank()) {
940  if(deletes == nullptr) {
941  deletes = &res.add_child("delete");
942  }
943 
944  (*deletes)[v.first] = "x";
945  }
946  }
947 
948  std::vector<std::string> entities;
949 
950  for(const auto& child : children_) {
951  entities.push_back(child.first);
952  }
953 
954  for(const auto& child : c.children_) {
955  if(children_.count(child.first) == 0) {
956  entities.push_back(child.first);
957  }
958  }
959 
960  for(const std::string& entity : entities) {
961  const child_map::const_iterator itor_a = children_.find(entity);
962  const child_map::const_iterator itor_b = c.children_.find(entity);
963 
964  static const child_list dummy;
965 
966  // Get the two child lists. 'b' has to be modified to look like 'a'.
967  const child_list& a = itor_a != children_.end() ? itor_a->second : dummy;
968  const child_list& b = itor_b != c.children_.end() ? itor_b->second : dummy;
969 
970  std::size_t ndeletes = 0;
971  std::size_t ai = 0, bi = 0;
972  while(ai != a.size() || bi != b.size()) {
973  // If the two elements are the same, nothing needs to be done.
974  if(ai < a.size() && bi < b.size() && *a[ai] == *b[bi]) {
975  ++ai;
976  ++bi;
977  } else {
978  // We have to work out what the most appropriate operation --
979  // delete, insert, or change is the best to get b[bi] looking like a[ai].
980  std::stringstream buf;
981 
982  // If b has more elements than a, then we assume this element
983  // is an element that needs deleting.
984  if(b.size() - bi > a.size() - ai) {
985  config& new_delete = res.add_child("delete_child");
986  buf << bi - ndeletes;
987  new_delete.values_["index"] = buf.str();
988  new_delete.add_child(entity);
989 
990  ++ndeletes;
991  ++bi;
992  }
993 
994  // If b has less elements than a, then we assume this element
995  // is an element that needs inserting.
996  else if(b.size() - bi < a.size() - ai) {
997  config& new_insert = res.add_child("insert_child");
998  buf << ai;
999  new_insert.values_["index"] = buf.str();
1000  new_insert.add_child(entity, *a[ai]);
1001 
1002  ++ai;
1003  }
1004 
1005  // Otherwise, they have the same number of elements,
1006  // so try just changing this element to match.
1007  else {
1008  config& new_change = res.add_child("change_child");
1009  buf << bi;
1010  new_change.values_["index"] = buf.str();
1011  new_change.add_child(entity, a[ai]->get_diff(*b[bi]));
1012 
1013  ++ai;
1014  ++bi;
1015  }
1016  }
1017  }
1018  }
1019 }
1020 
1021 void config::apply_diff(const config& diff, bool track /* = false */)
1022 {
1023  check_valid(diff);
1024 
1025  if(track) {
1026  values_[diff_track_attribute] = "modified";
1027  }
1028 
1029  if(const config& inserts = diff.child("insert")) {
1030  for(const attribute& v : inserts.attribute_range()) {
1031  values_[v.first] = v.second;
1032  }
1033  }
1034 
1035  if(const config& deletes = diff.child("delete")) {
1036  for(const attribute& v : deletes.attribute_range()) {
1037  values_.erase(v.first);
1038  }
1039  }
1040 
1041  for(const config& i : diff.child_range("change_child")) {
1042  const std::size_t index = lexical_cast<std::size_t>(i["index"].str());
1043  for(const any_child& item : i.all_children_range()) {
1044  if(item.key.empty()) {
1045  continue;
1046  }
1047 
1048  const child_map::iterator itor = children_.find(item.key);
1049  if(itor == children_.end() || index >= itor->second.size()) {
1050  throw error("error in diff: could not find element '" + item.key + "'");
1051  }
1052 
1053  itor->second[index]->apply_diff(item.cfg, track);
1054  }
1055  }
1056 
1057  for(const config& i : diff.child_range("insert_child")) {
1058  const std::size_t index = lexical_cast<std::size_t>(i["index"].str());
1059  for(const any_child& item : i.all_children_range()) {
1060  config& inserted = add_child_at(item.key, item.cfg, index);
1061  if(track) {
1062  inserted[diff_track_attribute] = "new";
1063  }
1064  }
1065  }
1066 
1067  for(const config& i : diff.child_range("delete_child")) {
1068  const std::size_t index = lexical_cast<std::size_t>(i["index"].str());
1069  for(const any_child& item : i.all_children_range()) {
1070  if(!track) {
1071  remove_child(item.key, index);
1072  } else {
1073  const child_map::iterator itor = children_.find(item.key);
1074  if(itor == children_.end() || index >= itor->second.size()) {
1075  throw error("error in diff: could not find element '" + item.key + "'");
1076  }
1077 
1078  itor->second[index]->values_[diff_track_attribute] = "deleted";
1079  }
1080  }
1081  }
1082 }
1083 
1085 {
1087  for(const config& i : diff.child_range("delete_child")) {
1088  const std::size_t index = lexical_cast<std::size_t>(i["index"].str());
1089  for(const any_child& item : i.all_children_range()) {
1090  remove_child(item.key, index);
1091  }
1092  }
1093 
1094  for(const config& i : diff.child_range("change_child")) {
1095  const std::size_t index = lexical_cast<std::size_t>(i["index"].str());
1096  for(const any_child& item : i.all_children_range()) {
1097  if(item.key.empty()) {
1098  continue;
1099  }
1100 
1101  const child_map::iterator itor = children_.find(item.key);
1102  if(itor == children_.end() || index >= itor->second.size()) {
1103  throw error("error in diff: could not find element '" + item.key + "'");
1104  }
1105 
1106  itor->second[index]->clear_diff_track(item.cfg);
1107  }
1108  }
1109 
1110  for(std::pair<const std::string, child_list>& p : children_) {
1111  for(auto& cfg : p.second) {
1112  cfg->remove_attribute(diff_track_attribute);
1113  }
1114  }
1115 }
1116 
1117 /**
1118  * Merge config 'c' into this config, overwriting this config's values.
1119  */
1121 {
1122  check_valid(c);
1123 
1124  std::vector<child_pos> to_remove;
1125  std::map<std::string, unsigned> visitations;
1126 
1127  // Merge attributes first
1128  merge_attributes(c);
1129 
1130  // Now merge shared tags
1132  for(i = ordered_children.begin(); i != i_end; ++i) {
1133  const std::string& tag = i->pos->first;
1134  const child_map::const_iterator j = c.children_.find(tag);
1135 
1136  if(j != c.children_.end()) {
1137  unsigned& visits = visitations[tag];
1138 
1139  if(visits < j->second.size()) {
1140  // Get a const config so we do not add attributes.
1141  const config& merge_child = *j->second[visits++];
1142 
1143  if(merge_child["__remove"].to_bool()) {
1144  to_remove.push_back(*i);
1145  } else {
1146  (i->pos->second[i->index])->merge_with(merge_child);
1147  }
1148  }
1149  }
1150  }
1151 
1152  // Now add any unvisited tags
1153  for(const auto& pair : c.children_) {
1154  const std::string& tag = pair.first;
1155  unsigned& visits = visitations[tag];
1156  while(visits < pair.second.size()) {
1157  add_child(tag, *pair.second[visits++]);
1158  }
1159  }
1160 
1161  // Remove those marked so
1162  std::map<std::string, unsigned> removals;
1163  for(const child_pos& pos : to_remove) {
1164  const std::string& tag = pos.pos->first;
1165  unsigned& removes = removals[tag];
1166  remove_child(tag, pos.index - removes++);
1167  }
1168 }
1169 
1170 /**
1171  * Merge config 'c' into this config, preserving this config's values.
1172  */
1174 {
1175  // Using a scratch config and merge_with() seems to execute about as fast
1176  // as direct coding of this merge.
1177  config scratch(c);
1178  scratch.merge_with(*this);
1179  swap(scratch);
1180 }
1181 
1182 bool config::matches(const config& filter) const
1183 {
1184  check_valid(filter);
1185 
1186  bool result = true;
1187 
1188  for(const attribute& i : filter.attribute_range()) {
1189  if(i.first.compare(0, 8, "glob_on_") == 0) {
1190  const attribute_value* v = get(i.first.substr(8));
1191  if(!v || !utils::wildcard_string_match(v->str(), i.second.str())) {
1192  result = false;
1193  break;
1194  }
1195  } else {
1196  const attribute_value* v = get(i.first);
1197  if(!v || *v != i.second) {
1198  result = false;
1199  break;
1200  }
1201  }
1202  }
1203 
1204  for(const any_child& i : filter.all_children_range()) {
1205  if(i.key == "not") {
1206  result = result && !matches(i.cfg);
1207  continue;
1208  } else if(i.key == "and") {
1209  result = result && matches(i.cfg);
1210  continue;
1211  } else if(i.key == "or") {
1212  result = result || matches(i.cfg);
1213  continue;
1214  }
1215 
1216  bool found = false;
1217  for(const config& j : child_range(i.key)) {
1218  if(j.matches(i.cfg)) {
1219  found = true;
1220  break;
1221  }
1222  }
1223 
1224  result = result && found;
1225  }
1226 
1227  return result;
1228 }
1229 
1230 std::string config::debug() const
1231 {
1232  check_valid();
1233 
1234  std::ostringstream outstream;
1235  outstream << *this;
1236  return outstream.str();
1237 }
1238 
1239 std::ostream& operator<<(std::ostream& outstream, const config& cfg)
1240 {
1241  static int i = 0;
1242  i++;
1243 
1244  for(const config::attribute& val : cfg.attribute_range()) {
1245  if(val.second.blank()) {
1246  continue;
1247  }
1248 
1249  for(int j = 0; j < i - 1; j++) {
1250  outstream << char(9);
1251  }
1252 
1253  outstream << val.first << " = " << val.second << '\n';
1254  }
1255 
1256  for(const config::any_child& child : cfg.all_children_range()) {
1257  for(int j = 0; j < i - 1; ++j) {
1258  outstream << char(9);
1259  }
1260 
1261  outstream << "[" << child.key << "]\n";
1262  outstream << child.cfg;
1263 
1264  for(int j = 0; j < i - 1; ++j) {
1265  outstream << char(9);
1266  }
1267 
1268  outstream << "[/" << child.key << "]\n";
1269  }
1270 
1271  i--;
1272  return outstream;
1273 }
1274 
1275 std::string config::hash() const
1276 {
1277  check_valid();
1278 
1279  static const unsigned int hash_length = 128;
1280  static const char hash_string[] = "+-,.<>0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
1281  char hash_str[hash_length + 1];
1282 
1283  unsigned int i;
1284  for(i = 0; i != hash_length; ++i) {
1285  hash_str[i] = 'a';
1286  }
1287 
1288  hash_str[hash_length] = 0;
1289 
1290  i = 0;
1291  for(const attribute& val : values_) {
1292  if(val.second.blank()) {
1293  continue;
1294  }
1295 
1296  for(char c : val.first) {
1297  hash_str[i] ^= c;
1298  if(++i == hash_length) {
1299  i = 0;
1300  }
1301  }
1302 
1303  std::string base_str = val.second.t_str().base_str();
1304  for(const char c : base_str) {
1305  hash_str[i] ^= c;
1306  if(++i == hash_length) {
1307  i = 0;
1308  }
1309  }
1310  }
1311 
1312  for(const any_child& ch : all_children_range()) {
1313  std::string child_hash = ch.cfg.hash();
1314  for(char c : child_hash) {
1315  hash_str[i] ^= c;
1316  ++i;
1317  if(i == hash_length) {
1318  i = 0;
1319  }
1320  }
1321  }
1322 
1323  for(i = 0; i != hash_length; ++i) {
1324  hash_str[i] = hash_string[static_cast<unsigned>(hash_str[i]) % strlen(hash_string)];
1325  }
1326 
1327  return std::string(hash_str);
1328 }
1329 
1331 {
1332  check_valid(cfg);
1333 
1334  values_.swap(cfg.values_);
1335  children_.swap(cfg.children_);
1337 }
1338 
1339 void swap(config& lhs, config& rhs)
1340 {
1341  lhs.swap(rhs);
1342 }
1343 
1345 {
1346  return std::all_of(children_.begin(), children_.end(), [](const child_map::value_type& pair)
1347  {
1348  return valid_tag(pair.first) &&
1349  std::all_of(pair.second.begin(), pair.second.end(),
1350  [](const std::unique_ptr<config>& c) { return c->validate_wml(); });
1351  });
1352 }
1353 
1354 bool operator==(const config& a, const config& b)
1355 {
1356  a.check_valid(b);
1357 
1358  if(a.values_ != b.values_) {
1359  return false;
1360  }
1361 
1363  for(; !x.empty() && !y.empty(); x.pop_front(), y.pop_front()) {
1364  if(x.front().key != y.front().key || x.front().cfg != y.front().cfg) {
1365  return false;
1366  }
1367  }
1368 
1369  return x.empty() && y.empty();
1370 }
std::vector< child_pos >::iterator Itor
Definition: config.hpp:540
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:510
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:1182
const_all_children_iterator ordered_cbegin() const
Definition: config.cpp:859
const_all_children_itors all_children_range() const
In-order iteration over all children.
Definition: config.cpp:874
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:605
int dummy
Definition: lstrlib.cpp:1125
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:844
boost::iterator_range< const_all_children_iterator > const_all_children_itors
Definition: config.hpp:631
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:789
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
friend bool operator==(const config &a, const config &b)
Definition: config.cpp:1354
child_itors child_range(config_key_type key)
Definition: config.cpp:366
attribute_map::value_type attribute
Definition: config.hpp:226
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:695
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.
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:715
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:816
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:1084
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:762
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:900
#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:1120
void inherit_from(const config &c)
Merge config &#39;c&#39; into this config, preserving this config&#39;s values.
Definition: config.cpp:1173
static config invalid
Definition: config.hpp:73
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:864
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:1330
void splice_children(config &src, const std::string &key)
Moves all the children with tag key from src to this.
Definition: config.cpp:579
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:564
std::ostringstream wrapper.
Definition: formatter.hpp:38
config & add_child_at(config_key_type key, const config &val, unsigned index)
Definition: config.cpp:511
bool validate_wml() const
Returns true if this object represents valid WML, i.e.
Definition: config.cpp:1344
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:752
config()
Definition: config.cpp:132
boost::iterator_range< const_attribute_iterator > const_attr_itors
Definition: config.hpp:286
void apply_diff(const config &diff, bool track=false)
A function to apply a diff config onto this config object.
Definition: config.cpp:1021
all_children_iterator erase(const all_children_iterator &i)
Definition: config.cpp:642
boost::iterator_range< const_child_iterator > const_child_itors
Definition: config.hpp:210
attribute_value & operator[](config_key_type key)
Returns a reference to the attribute with the given key.
Definition: config.cpp:702
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:747
mock_party p
boost::iterator_range< attribute_iterator > attr_itors
Definition: config.hpp:287
std::ostream & operator<<(std::ostream &outstream, const config &cfg)
Definition: config.cpp:1239
boost::iterator_range< all_children_iterator > all_children_itors
Definition: config.hpp:630
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:119
config & add_child(config_key_type key)
Definition: config.cpp:479
void merge_attributes(const config &)
Definition: config.cpp:741
const_all_children_iterator ordered_begin() const
Definition: config.cpp:854
static const char * diff_track_attribute
The name of the attribute used for tracking diff changes.
Definition: config.hpp:657
static lg::log_domain log_config("config")
void clear_all_children()
Definition: config.cpp:824
boost::string_ref config_key_type
Definition: config.hpp:57
boost::iterator_range< child_iterator > child_itors
Definition: config.hpp:209
#define DBG_CF
Definition: config.cpp:46
Standard logging facilities (interface).
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:831
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:68
mock_char c
Interfaces for manipulating version numbers of engine, add-ons, etc.
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:647
std::string hash() const
Definition: config.cpp:1275
const_all_children_iterator ordered_cend() const
Definition: config.cpp:869
std::string::const_iterator iterator
Definition: tokenizer.hpp:24
bool empty() const
Definition: config.cpp:837
child_map children_
A list of all children of this node.
Definition: config.hpp:750
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:1230
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:660
std::string str(const std::string &fallback="") const