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 == "_") {
191  // A lone underscore isn't a valid tag name
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 
198  We're using a manual check mainly for performance. @gfgtdf measured
199  that a manual check can be up to 30 times faster than std::isalnum().
200 
201  - Jyrki, 2019-01-19 */
202  return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') ||
203  (c >= '0' && c <= '9') || (c == '_');
204  });
205  }
206 }
207 
209 {
210  return valid_tag(name);
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 
312 void config::append_children_by_move(config& cfg, const std::string& key)
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 
325 void config::merge_children(const std::string& key)
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 
342 void config::merge_children_by_attribute(const std::string& key, const std::string& attribute)
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  child_map::const_iterator i = children_.find(key);
417  return i != children_.end() && !i->second.empty();
418 }
419 
421 {
422  check_valid();
423 
424  const child_map::const_iterator i = children_.find(key);
425  if(i == children_.end()) {
426  DBG_CF << "The config object has no child named »" << key << "«.\n";
427 
428  return invalid;
429  }
430 
431  if(n < 0)
432  n = i->second.size() + n;
433  if(std::size_t(n) < i->second.size()) {
434  return *i->second[n];
435  } else {
436  DBG_CF << "The config object has only »" << i->second.size() << "« children named »" << key
437  << "«; request for the index »" << n << "« cannot be honored.\n";
438 
439  return invalid;
440  }
441 }
442 
443 config& config::child(config_key_type key, const std::string& parent)
444 {
445  return config_implementation::child(this, key, parent);
446 }
447 
448 const config& config::child(config_key_type key, const std::string& parent) const
449 {
450  return config_implementation::child(this, key, parent);
451 }
452 
454 {
455  static const config empty_cfg;
456  check_valid();
457 
458  child_map::const_iterator i = children_.find(key);
459  if(i != children_.end() && !i->second.empty()) {
460  return *i->second.front();
461  }
462 
463  return empty_cfg;
464 }
465 
467 {
468  child_map::const_iterator i = children_.find(key);
469  if(i != children_.end() && !i->second.empty()) {
470  return *i->second.front();
471  }
472 
473  return add_child(key);
474 }
475 
477 {
478  check_valid();
479 
480  child_list& v = map_get(children_, key);
481  v.emplace_back(new config());
482  ordered_children.emplace_back(children_.find(key), v.size() - 1);
483  return *v.back();
484 }
485 
487 {
488  check_valid(val);
489 
490  child_list& v = map_get(children_, key);
491  v.emplace_back(new config(val));
492  ordered_children.emplace_back(children_.find(key), v.size() - 1);
493 
494  return *v.back();
495 }
496 
498 {
499  check_valid(val);
500 
501  child_list& v = map_get(children_, key);
502  v.emplace_back(new config(std::move(val)));
503  ordered_children.emplace_back(children_.find(key), v.size() - 1);
504 
505  return *v.back();
506 }
507 
509 {
510  check_valid(val);
511 
512  child_list& v = map_get(children_, key);
513  if(index > v.size()) {
514  throw error("illegal index to add child at");
515  }
516 
517  v.emplace(v.begin() + index, new config(val));
518 
519  bool inserted = false;
520 
521  const child_pos value(children_.find(key), index);
522 
524  for(; ord != ordered_children.end(); ++ord) {
525  if(ord->pos != value.pos)
526  continue;
527  if(!inserted && ord->index == index) {
528  ord = ordered_children.insert(ord, value);
529  inserted = true;
530  } else if(ord->index >= index) {
531  ord->index++;
532  }
533  }
534 
535  if(!inserted) {
536  ordered_children.push_back(value);
537  }
538 
539  return *v[index];
540 }
541 
543 {
544  assert(start <= ordered_children.size());
545  const size_t npos = static_cast<size_t>(-1);
546 
547  auto pos = std::find_if(ordered_begin() + start, ordered_end(), [&](const config::any_child& can){ return can.key == key; });
548 
549  if(pos == ordered_end()) {
550  return npos;
551  }
552 
553  return static_cast<size_t>(pos - ordered_begin());
554 }
555 
557 {
558  assert(pos <= ordered_children.size());
559  if(pos == ordered_children.size()) {
560  //optimisation
561  config::add_child(key, val);
562  }
563 
564  auto end = ordered_children.end();
565  auto pos_it = ordered_children.begin() + pos;
566  auto next = std::find_if(pos_it, end,[&](const child_pos& p){ return p.pos->first == key; });
567 
568  if(next == end) {
569  config& res = config::add_child(key, val);
570  //rotate the just inserted element to position pos.
571  std::rotate(ordered_children.begin() + pos, ordered_children.end() - 1, ordered_children.end());
572  return res;
573  }
574 
575  auto pl = next->pos;
576  child_list& l = pl->second;
577  unsigned int index = next->index;
578  config& res = **(l.emplace(l.begin() + index, new config(val)));
579 
580  for(auto ord = next; ord != end; ++ord) {
581  //this changes next->index and all later refernces to that tag.
582  if(ord->pos == pl) {
583  ++ord->index;
584  }
585  }
586 
587  //finally insert our new child in ordered_children.
588  ordered_children.insert(pos_it, { pl, index });
589  return res;
590 }
591 
592 namespace
593 {
594 struct remove_ordered
595 {
596  remove_ordered(const config::child_map::iterator& iter)
597  : iter_(iter)
598  {
599  }
600 
601  bool operator()(const config::child_pos& pos) const
602  {
603  return pos.pos == iter_;
604  }
605 
606 private:
608 };
609 } // end anon namespace
610 
612 {
613  check_valid();
614 
615  child_map::iterator i = children_.find(key);
616  if(i == children_.end())
617  return;
618 
619  ordered_children.erase(
620  std::remove_if(ordered_children.begin(), ordered_children.end(), remove_ordered(i)),
621  ordered_children.end());
622 
623  children_.erase(i);
624 }
625 
626 void config::splice_children(config& src, const std::string& key)
627 {
628  check_valid(src);
629 
630  child_map::iterator i_src = src.children_.find(key);
631  if(i_src == src.children_.end()) {
632  return;
633  }
634 
635  src.ordered_children.erase(
636  std::remove_if(src.ordered_children.begin(), src.ordered_children.end(), remove_ordered(i_src)),
637  src.ordered_children.end());
638 
639  child_list& dst = map_get(children_, key);
640  child_map::iterator i_dst = children_.find(key);
641 
642  unsigned before = dst.size();
643  dst.insert(dst.end(), std::make_move_iterator(i_src->second.begin()), std::make_move_iterator(i_src->second.end()));
644  src.children_.erase(i_src);
645  // key might be a reference to i_src->first, so it is no longer usable.
646 
647  for(unsigned j = before; j < dst.size(); ++j) {
648  ordered_children.emplace_back(i_dst, j);
649  }
650 }
651 
653 {
654  check_valid();
655 
656  map_erase_key(values_, key);
657 
658  for(std::pair<const std::string, child_list>& p : children_) {
659  for(auto& cfg : p.second) {
660  cfg->recursive_clear_value(key);
661  }
662  }
663 }
664 
666 {
667  /* Find the position with the correct index and decrement all the
668  indices in the ordering that are above this index. */
669  unsigned found = 0;
670  for(child_pos& p : ordered_children) {
671  if(p.pos != pos) {
672  continue;
673  }
674 
675  if(p.index == index) {
676  found = &p - &ordered_children.front();
677  } else if(p.index > index) {
678  --p.index;
679  }
680  }
681 
682  // Remove from the child map.
683  pos->second.erase(pos->second.begin() + index);
684 
685  // Erase from the ordering and return the next position.
686  return ordered_children.erase(ordered_children.begin() + found);
687 }
688 
690 {
691  return all_children_iterator(remove_child(i.i_->pos, i.i_->index));
692 }
693 
695 {
696  check_valid();
697 
698  child_map::iterator i = children_.find(key);
699  if(i == children_.end() || index >= i->second.size()) {
700  ERR_CF << "Error: attempting to delete non-existing child: " << key << "[" << index << "]\n";
701  return;
702  }
703 
704  remove_child(i, index);
705 }
706 
707 void config::remove_children(config_key_type key, std::function<bool(const config&)> p)
708 {
709  check_valid();
710 
711  child_map::iterator pos = children_.find(key);
712  if(pos == children_.end()) {
713  return;
714  }
715 
716  const auto predicate = [p](const std::unique_ptr<config>& child)
717  {
718  return p(*child);
719  };
720 
721  auto child_it = std::find_if(pos->second.begin(), pos->second.end(), predicate);
722  while(child_it != pos->second.end()) {
723  unsigned index = std::distance(pos->second.begin(), child_it);
724  remove_child(pos, index);
725  child_it = std::find_if(pos->second.begin() + index, pos->second.end(), predicate);
726  }
727 }
728 
730 {
731  check_valid();
732 
733  const attribute_map::const_iterator i = values_.find(key);
734  if(i != values_.end()) {
735  return i->second;
736  }
737 
738  static const attribute_value empty_attribute;
739  return empty_attribute;
740 }
741 
743 {
744  check_valid();
745  attribute_map::const_iterator i = values_.find(key);
746  return i != values_.end() ? &i->second : nullptr;
747 }
748 
750 {
751  check_valid();
752 
753  auto res = values_.lower_bound(key);
754 
755  if(res == values_.end() || key != res->first) {
756  res = values_.emplace_hint(res, std::piecewise_construct, std::forward_as_tuple(key), std::tuple<>());
757  }
758 
759  return res->second;
760 }
761 
763  config_key_type key, const std::string& old_key, const std::string& in_tag) const
764 {
765  check_valid();
766 
767  attribute_map::const_iterator i = values_.find(key);
768  if(i != values_.end()) {
769  return i->second;
770  }
771 
772  i = values_.find(old_key);
773  if(i != values_.end()) {
774  if(!in_tag.empty()) {
775  const std::string what = formatter() << "[" << in_tag << "]" << old_key << "=";
776  const std::string msg = formatter() << "Use " << key << "= instead.";
778  lg::wml_error() << msg;
779  }
780 
781  return i->second;
782  }
783 
784  static const attribute_value empty_attribute;
785  return empty_attribute;
786 }
787 
789 {
790  check_valid(cfg);
791 
792  assert(this != &cfg);
793  for(const attribute& v : cfg.values_) {
794  std::string key = v.first;
795  if(key.substr(0, 7) == "add_to_") {
796  std::string add_to = key.substr(7);
797  values_[add_to] = values_[add_to].to_double() + v.second.to_double();
798  } else if(key.substr(0, 10) == "concat_to_") {
799  std::string concat_to = key.substr(10);
800  // TODO: Only use t_string if one or both are actually translatable?
801  // That probably requires using a visitor though.
802  values_[concat_to] = values_[concat_to].t_str() + v.second.t_str();
803  } else {
804  values_[v.first] = v.second;
805  }
806  }
807 }
808 
810 {
811  check_valid();
812 
814 
815  // Ensure the first element is not blank, as a few places assume this
816  while(range.begin() != range.end() && range.begin()->second.blank()) {
817  range.pop_front();
818  }
819 
820  return range;
821 }
822 
824 {
825  check_valid();
827 
828  // Ensure the first element is not blank, as a few places assume this
829  while(range.begin() != range.end() && range.begin()->second.blank()) {
830  range.pop_front();
831  }
832 
833  return range;
834 }
835 
836 config& config::find_child(config_key_type key, const std::string& name, const std::string& value)
837 {
838  check_valid();
839 
840  const child_map::iterator i = children_.find(key);
841  if(i == children_.end()) {
842  DBG_CF << "Key »" << name << "« value »" << value << "« pair not found as child of key »" << key << "«.\n";
843 
844  return invalid;
845  }
846 
847  const child_list::iterator j = std::find_if(i->second.begin(), i->second.end(),
848  [&](const std::unique_ptr<config>& pcfg) {
849  const config& cfg = *pcfg;
850  return cfg[name] == value;
851  }
852  );
853 
854  if(j != i->second.end()) {
855  return **j;
856  }
857 
858  DBG_CF << "Key »" << name << "« value »" << value << "« pair not found as child of key »" << key << "«.\n";
859 
860  return invalid;
861 }
862 
864 {
865  // No validity check for this function.
866  children_.clear();
867  values_.clear();
868  ordered_children.clear();
869 }
870 
872 {
873  // No validity check for this function.
874  children_.clear();
875  ordered_children.clear();
876 }
877 
879 {
880  // No validity check for this function.
881  values_.clear();
882 }
883 
884 bool config::empty() const
885 {
886  check_valid();
887 
888  return children_.empty() && values_.empty();
889 }
890 
892 {
893  return any_child(&i_->pos->first, i_->pos->second[i_->index].get());
894 }
895 
897 {
898  return any_child(&i_->pos->first, i_->pos->second[i_->index].get());
899 }
900 
902 {
904 }
905 
907 {
909 }
910 
912 {
914 }
915 
917 {
919 }
920 
922 {
926  );
927 }
928 
930 {
931  return all_children_iterator(ordered_children.begin());
932 }
933 
935 {
937 }
938 
940 {
941  return all_children_itors(
944  );
945 }
946 
948 {
949  check_valid(c);
950 
951  config res;
952  get_diff(c, res);
953  return res;
954 }
955 
956 void config::get_diff(const config& c, config& res) const
957 {
958  check_valid(c);
959  check_valid(res);
960 
961  config* inserts = nullptr;
962 
963  for(const auto& v : values_) {
964  if(v.second.blank()) {
965  continue;
966  }
967 
968  const attribute_map::const_iterator j = c.values_.find(v.first);
969  if(j == c.values_.end() || (v.second != j->second && !v.second.blank())) {
970  if(inserts == nullptr) {
971  inserts = &res.add_child("insert");
972  }
973 
974  (*inserts)[v.first] = v.second;
975  }
976  }
977 
978  config* deletes = nullptr;
979 
980  for(const auto& v : c.values_) {
981  if(v.second.blank()) {
982  continue;
983  }
984 
985  const attribute_map::const_iterator itor = values_.find(v.first);
986  if(itor == values_.end() || itor->second.blank()) {
987  if(deletes == nullptr) {
988  deletes = &res.add_child("delete");
989  }
990 
991  (*deletes)[v.first] = "x";
992  }
993  }
994 
995  std::vector<std::string> entities;
996 
997  for(const auto& child : children_) {
998  entities.push_back(child.first);
999  }
1000 
1001  for(const auto& child : c.children_) {
1002  if(children_.count(child.first) == 0) {
1003  entities.push_back(child.first);
1004  }
1005  }
1006 
1007  for(const std::string& entity : entities) {
1008  const child_map::const_iterator itor_a = children_.find(entity);
1009  const child_map::const_iterator itor_b = c.children_.find(entity);
1010 
1011  static const child_list dummy;
1012 
1013  // Get the two child lists. 'b' has to be modified to look like 'a'.
1014  const child_list& a = itor_a != children_.end() ? itor_a->second : dummy;
1015  const child_list& b = itor_b != c.children_.end() ? itor_b->second : dummy;
1016 
1017  std::size_t ndeletes = 0;
1018  std::size_t ai = 0, bi = 0;
1019  while(ai != a.size() || bi != b.size()) {
1020  // If the two elements are the same, nothing needs to be done.
1021  if(ai < a.size() && bi < b.size() && *a[ai] == *b[bi]) {
1022  ++ai;
1023  ++bi;
1024  } else {
1025  // We have to work out what the most appropriate operation --
1026  // delete, insert, or change is the best to get b[bi] looking like a[ai].
1027  std::stringstream buf;
1028 
1029  // If b has more elements than a, then we assume this element
1030  // is an element that needs deleting.
1031  if(b.size() - bi > a.size() - ai) {
1032  config& new_delete = res.add_child("delete_child");
1033  buf << bi - ndeletes;
1034  new_delete.values_["index"] = buf.str();
1035  new_delete.add_child(entity);
1036 
1037  ++ndeletes;
1038  ++bi;
1039  }
1040 
1041  // If b has less elements than a, then we assume this element
1042  // is an element that needs inserting.
1043  else if(b.size() - bi < a.size() - ai) {
1044  config& new_insert = res.add_child("insert_child");
1045  buf << ai;
1046  new_insert.values_["index"] = buf.str();
1047  new_insert.add_child(entity, *a[ai]);
1048 
1049  ++ai;
1050  }
1051 
1052  // Otherwise, they have the same number of elements,
1053  // so try just changing this element to match.
1054  else {
1055  config& new_change = res.add_child("change_child");
1056  buf << bi;
1057  new_change.values_["index"] = buf.str();
1058  new_change.add_child(entity, a[ai]->get_diff(*b[bi]));
1059 
1060  ++ai;
1061  ++bi;
1062  }
1063  }
1064  }
1065  }
1066 }
1067 
1068 void config::apply_diff(const config& diff, bool track /* = false */)
1069 {
1070  check_valid(diff);
1071 
1072  if(track) {
1073  values_[diff_track_attribute] = "modified";
1074  }
1075 
1076  if(const config& inserts = diff.child("insert")) {
1077  for(const attribute& v : inserts.attribute_range()) {
1078  values_[v.first] = v.second;
1079  }
1080  }
1081 
1082  if(const config& deletes = diff.child("delete")) {
1083  for(const attribute& v : deletes.attribute_range()) {
1084  values_.erase(v.first);
1085  }
1086  }
1087 
1088  for(const config& i : diff.child_range("change_child")) {
1089  const std::size_t index = lexical_cast<std::size_t>(i["index"].str());
1090  for(const any_child& item : i.all_children_range()) {
1091  if(item.key.empty()) {
1092  continue;
1093  }
1094 
1095  const child_map::iterator itor = children_.find(item.key);
1096  if(itor == children_.end() || index >= itor->second.size()) {
1097  throw error("error in diff: could not find element '" + item.key + "'");
1098  }
1099 
1100  itor->second[index]->apply_diff(item.cfg, track);
1101  }
1102  }
1103 
1104  for(const config& i : diff.child_range("insert_child")) {
1105  const std::size_t index = lexical_cast<std::size_t>(i["index"].str());
1106  for(const any_child& item : i.all_children_range()) {
1107  config& inserted = add_child_at(item.key, item.cfg, index);
1108  if(track) {
1109  inserted[diff_track_attribute] = "new";
1110  }
1111  }
1112  }
1113 
1114  for(const config& i : diff.child_range("delete_child")) {
1115  const std::size_t index = lexical_cast<std::size_t>(i["index"].str());
1116  for(const any_child& item : i.all_children_range()) {
1117  if(!track) {
1118  remove_child(item.key, index);
1119  } else {
1120  const child_map::iterator itor = children_.find(item.key);
1121  if(itor == children_.end() || index >= itor->second.size()) {
1122  throw error("error in diff: could not find element '" + item.key + "'");
1123  }
1124 
1125  itor->second[index]->values_[diff_track_attribute] = "deleted";
1126  }
1127  }
1128  }
1129 }
1130 
1132 {
1134  for(const config& i : diff.child_range("delete_child")) {
1135  const std::size_t index = lexical_cast<std::size_t>(i["index"].str());
1136  for(const any_child& item : i.all_children_range()) {
1137  remove_child(item.key, index);
1138  }
1139  }
1140 
1141  for(const config& i : diff.child_range("change_child")) {
1142  const std::size_t index = lexical_cast<std::size_t>(i["index"].str());
1143  for(const any_child& item : i.all_children_range()) {
1144  if(item.key.empty()) {
1145  continue;
1146  }
1147 
1148  const child_map::iterator itor = children_.find(item.key);
1149  if(itor == children_.end() || index >= itor->second.size()) {
1150  throw error("error in diff: could not find element '" + item.key + "'");
1151  }
1152 
1153  itor->second[index]->clear_diff_track(item.cfg);
1154  }
1155  }
1156 
1157  for(std::pair<const std::string, child_list>& p : children_) {
1158  for(auto& cfg : p.second) {
1159  cfg->remove_attribute(diff_track_attribute);
1160  }
1161  }
1162 }
1163 
1164 /**
1165  * Merge config 'c' into this config, overwriting this config's values.
1166  */
1168 {
1169  check_valid(c);
1170 
1171  std::vector<child_pos> to_remove;
1172  std::map<std::string, unsigned> visitations;
1173 
1174  // Merge attributes first
1175  merge_attributes(c);
1176 
1177  // Now merge shared tags
1179  for(i = ordered_children.begin(); i != i_end; ++i) {
1180  const std::string& tag = i->pos->first;
1181  const child_map::const_iterator j = c.children_.find(tag);
1182 
1183  if(j != c.children_.end()) {
1184  unsigned& visits = visitations[tag];
1185 
1186  if(visits < j->second.size()) {
1187  // Get a const config so we do not add attributes.
1188  const config& merge_child = *j->second[visits++];
1189 
1190  if(merge_child["__remove"].to_bool()) {
1191  to_remove.push_back(*i);
1192  } else {
1193  (i->pos->second[i->index])->merge_with(merge_child);
1194  }
1195  }
1196  }
1197  }
1198 
1199  // Now add any unvisited tags
1200  for(const auto& pair : c.children_) {
1201  const std::string& tag = pair.first;
1202  unsigned& visits = visitations[tag];
1203  while(visits < pair.second.size()) {
1204  add_child(tag, *pair.second[visits++]);
1205  }
1206  }
1207 
1208  // Remove those marked so
1209  std::map<std::string, unsigned> removals;
1210  for(const child_pos& pos : to_remove) {
1211  const std::string& tag = pos.pos->first;
1212  unsigned& removes = removals[tag];
1213  remove_child(tag, pos.index - removes++);
1214  }
1215 }
1216 
1217 /**
1218  * Merge config 'c' into this config, preserving this config's values.
1219  */
1221 {
1222  // Using a scratch config and merge_with() seems to execute about as fast
1223  // as direct coding of this merge.
1224  config scratch(c);
1225  scratch.merge_with(*this);
1226  swap(scratch);
1227 }
1228 
1229 bool config::matches(const config& filter) const
1230 {
1231  check_valid(filter);
1232 
1233  bool result = true;
1234 
1235  for(const attribute& i : filter.attribute_range()) {
1236  if(i.first.compare(0, 8, "glob_on_") == 0) {
1237  const attribute_value* v = get(i.first.substr(8));
1238  if(!v || !utils::wildcard_string_match(v->str(), i.second.str())) {
1239  result = false;
1240  break;
1241  }
1242  } else {
1243  const attribute_value* v = get(i.first);
1244  if(!v || *v != i.second) {
1245  result = false;
1246  break;
1247  }
1248  }
1249  }
1250 
1251  for(const any_child& i : filter.all_children_range()) {
1252  if(i.key == "not") {
1253  result = result && !matches(i.cfg);
1254  continue;
1255  } else if(i.key == "and") {
1256  result = result && matches(i.cfg);
1257  continue;
1258  } else if(i.key == "or") {
1259  result = result || matches(i.cfg);
1260  continue;
1261  }
1262 
1263  bool found = false;
1264  for(const config& j : child_range(i.key)) {
1265  if(j.matches(i.cfg)) {
1266  found = true;
1267  break;
1268  }
1269  }
1270 
1271  result = result && found;
1272  }
1273 
1274  return result;
1275 }
1276 
1277 std::string config::debug() const
1278 {
1279  check_valid();
1280 
1281  std::ostringstream outstream;
1282  outstream << *this;
1283  return outstream.str();
1284 }
1285 
1286 std::ostream& operator<<(std::ostream& outstream, const config& cfg)
1287 {
1288  static int i = 0;
1289  i++;
1290 
1291  for(const config::attribute& val : cfg.attribute_range()) {
1292  if(val.second.blank()) {
1293  continue;
1294  }
1295 
1296  for(int j = 0; j < i - 1; j++) {
1297  outstream << '\t';
1298  }
1299 
1300  outstream << val.first << " = " << val.second << '\n';
1301  }
1302 
1303  for(const config::any_child& child : cfg.all_children_range()) {
1304  for(int j = 0; j < i - 1; ++j) {
1305  outstream << '\t';
1306  }
1307 
1308  outstream << "[" << child.key << "]\n";
1309  outstream << child.cfg;
1310 
1311  for(int j = 0; j < i - 1; ++j) {
1312  outstream << '\t';
1313  }
1314 
1315  outstream << "[/" << child.key << "]\n";
1316  }
1317 
1318  i--;
1319  return outstream;
1320 }
1321 
1322 std::string config::hash() const
1323 {
1324  check_valid();
1325 
1326  static const unsigned int hash_length = 128;
1327  static const char hash_string[] = "+-,.<>0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
1328  char hash_str[hash_length + 1];
1329 
1330  unsigned int i;
1331  for(i = 0; i != hash_length; ++i) {
1332  hash_str[i] = 'a';
1333  }
1334 
1335  hash_str[hash_length] = 0;
1336 
1337  i = 0;
1338  for(const attribute& val : values_) {
1339  if(val.second.blank()) {
1340  continue;
1341  }
1342 
1343  for(char c : val.first) {
1344  hash_str[i] ^= c;
1345  if(++i == hash_length) {
1346  i = 0;
1347  }
1348  }
1349 
1350  std::string base_str = val.second.t_str().base_str();
1351  for(const char c : base_str) {
1352  hash_str[i] ^= c;
1353  if(++i == hash_length) {
1354  i = 0;
1355  }
1356  }
1357  }
1358 
1359  for(const any_child& ch : all_children_range()) {
1360  std::string child_hash = ch.cfg.hash();
1361  for(char c : child_hash) {
1362  hash_str[i] ^= c;
1363  ++i;
1364  if(i == hash_length) {
1365  i = 0;
1366  }
1367  }
1368  }
1369 
1370  for(i = 0; i != hash_length; ++i) {
1371  hash_str[i] = hash_string[static_cast<unsigned>(hash_str[i]) % strlen(hash_string)];
1372  }
1373 
1374  return std::string(hash_str);
1375 }
1376 
1378 {
1379  check_valid(cfg);
1380 
1381  values_.swap(cfg.values_);
1382  children_.swap(cfg.children_);
1384 }
1385 
1386 void swap(config& lhs, config& rhs)
1387 {
1388  lhs.swap(rhs);
1389 }
1390 
1392 {
1393  return std::all_of(children_.begin(), children_.end(), [](const auto& pair)
1394  {
1395  return valid_tag(pair.first) &&
1396  std::all_of(pair.second.begin(), pair.second.end(),
1397  [](const auto& c) { return c->validate_wml(); });
1398  }) &&
1399  std::all_of(values_.begin(), values_.end(), [](const auto& pair)
1400  {
1401  return valid_attribute(pair.first);
1402  });
1403 }
1404 
1405 bool operator==(const config& a, const config& b)
1406 {
1407  a.check_valid(b);
1408 
1409  if(a.values_ != b.values_) {
1410  return false;
1411  }
1412 
1414  for(; !x.empty() && !y.empty(); x.pop_front(), y.pop_front()) {
1415  if(x.front().key != y.front().key || x.front().cfg != y.front().cfg) {
1416  return false;
1417  }
1418  }
1419 
1420  return x.empty() && y.empty();
1421 }
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:420
bool matches(const config &filter) const
Definition: config.cpp:1229
const_all_children_iterator ordered_cbegin() const
Definition: config.cpp:906
const_all_children_itors all_children_range() const
In-order iteration over all children.
Definition: config.cpp:921
void append_attributes(const config &cfg)
Adds attributes from cfg.
Definition: config.cpp:270
void recursive_clear_value(config_key_type key)
Definition: config.cpp:652
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: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
reference operator*() const
Definition: config.cpp:891
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:836
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:325
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:213
#define a
bool has_child(config_key_type key) const
Determine whether a config has a child or not.
Definition: config.cpp:412
unsigned child_count(config_key_type key) const
Definition: config.cpp:390
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:556
friend bool operator==(const config &a, const config &b)
Definition: config.cpp:1405
child_itors child_range(config_key_type key)
Definition: config.cpp:362
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:742
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:542
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:762
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:208
config & child_or_add(config_key_type key)
Definition: config.cpp:466
void clear()
Definition: config.cpp:863
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:1131
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:62
const_attr_itors attribute_range() const
Definition: config.cpp:809
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:947
#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:1167
void inherit_from(const config &c)
Merge config &#39;c&#39; into this config, preserving this config&#39;s values.
Definition: config.cpp:1220
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:407
const_all_children_iterator ordered_end() const
Definition: config.cpp:911
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:1377
void splice_children(config &src, const std::string &key)
Moves all the children with tag key from src to this.
Definition: config.cpp:626
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:611
std::ostringstream wrapper.
Definition: formatter.hpp:38
config & add_child_at(config_key_type key, const config &val, unsigned index)
Definition: config.cpp:508
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:1391
unsigned all_children_count() const
Definition: config.cpp:402
#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:1068
all_children_iterator erase(const all_children_iterator &i)
Definition: config.cpp:689
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:749
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:1286
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:476
void merge_attributes(const config &)
Definition: config.cpp:788
const_all_children_iterator ordered_begin() const
Definition: config.cpp:901
#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:871
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:219
void clear_attributes()
Definition: config.cpp:878
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:453
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:694
std::string hash() const
Definition: config.cpp:1322
const_all_children_iterator ordered_cend() const
Definition: config.cpp:916
std::string::const_iterator iterator
Definition: tokenizer.hpp:24
bool empty() const
Definition: config.cpp:884
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:1277
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:707
std::string str(const std::string &fallback="") const