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