37 #define ERR_CF LOG_STREAM(err, log_config)
38 #define DBG_CF LOG_STREAM(debug, log_config)
41 #define ERR_WML LOG_STREAM(err, log_wml)
46 template<
typename Map,
typename Key>
49 auto res = map.lower_bound(key);
51 if(res == map.end() || key != res->first) {
52 res = map.emplace_hint(res, std::piecewise_construct, std::forward_as_tuple(key), std::tuple<>());
59 template<
typename Map,
typename Key>
60 int map_erase_key(Map& map, Key&& key)
62 auto i = map.find(key);
84 : values_(cfg.values_)
116 : values_(std::move(cfg.values_))
117 , children_(std::move(cfg.children_))
118 , ordered_children(std::move(cfg.ordered_children))
134 }
else if(name ==
"_") {
138 return std::all_of(name.begin(), name.end(), [](
const char&
c)
146 return (c >=
'A' && c <=
'Z') || (c >=
'a' && c <=
'z') ||
147 (c >=
'0' && c <=
'9') || (c ==
'_');
176 for(
const auto& [key, value] : cfg.
values_) {
191 for(
const auto& [key, value] : cfg.
values_) {
202 cfg.clear_all_children();
205 for(
const auto [child_key, child_value] : cfg.all_children_view()) {
206 add_child(child_key, std::move(child_value));
208 cfg.clear_all_children();
213 values_ = std::move(cfg.values_);
216 for(
const auto& [key, value] : cfg.values_) {
221 cfg.clear_attributes();
243 merged_children.
append(std::move(cfg));
247 add_child(key, std::move(merged_children));
256 typedef std::map<std::string, config> config_map;
257 config_map merged_children_map;
259 merged_children_map[cfg[
attribute]].append(std::move(cfg));
263 for(
const config_map::value_type&
i : merged_children_map) {
282 child_map::const_iterator
i =
children_.find(key);
294 child_map::const_iterator
i =
children_.find(key);
296 return i->second.size();
314 child_map::const_iterator
i =
children_.find(key);
319 template<
class Tchildren>
323 auto i = children.find(key);
324 if(
i == children.end()) {
325 DBG_CF <<
"The config object has no child named ‘" << key <<
"’.";
326 return utils::nullopt;
330 n =
static_cast<int>(
i->second.size()) +
n;
334 return *
i->second.at(
n);
335 }
catch(
const std::out_of_range&) {
336 DBG_CF <<
"The config object has only ‘" <<
i->second.size() <<
"’ children named ‘" << key
337 <<
"’; request for the index ‘" <<
n <<
"’ cannot be honored.";
338 return utils::nullopt;
346 if(
auto res = get_child_impl(
children_, key, 0)) {
349 throw error(
"Mandatory WML child ‘[" + std::string(key) +
"]’ missing in ‘" + parent +
"’. Please report this bug.");
355 if(
auto res = get_child_impl(
children_, key, 0)) {
358 throw error(
"Mandatory WML child ‘[" + std::string(key) +
"]’ missing in ‘" + parent +
"’. Please report this bug.");
364 if(
auto res = get_child_impl(
children_, key,
n)) {
367 throw error(
"Child [" + std::string(key) +
"] at index " + std::to_string(
n) +
" not found");
373 if(
auto res = get_child_impl(
children_, key,
n)) {
376 throw error(
"Child [" + std::string(key) +
"] at index " + std::to_string(
n) +
" not found");
392 static const config empty_cfg;
393 child_map::const_iterator
i =
children_.find(key);
395 return *
i->second.front();
403 child_map::const_iterator
i =
children_.find(key);
405 return *
i->second.front();
414 const std::string what =
formatter() <<
"[" << in_tag <<
"][" << old_key <<
"]";
419 return utils::nullopt;
428 const std::string what =
formatter() <<
"[" << in_tag <<
"][" << old_key <<
"]";
440 v.emplace_back(
new config());
449 v.emplace_back(
new config(val));
459 v.emplace_back(
new config(std::move(val)));
469 if(
index > v.size()) {
470 throw error(
"illegal index to add child at");
475 bool inserted =
false;
481 if(ord->pos != value.
pos)
483 if(!inserted && ord->index ==
index) {
486 }
else if(ord->index >=
index) {
501 const size_t npos =
static_cast<size_t>(-1);
522 auto next = std::find_if(pos_it, end,[&](
const child_pos&
p){
return p.pos->first == key; });
533 const auto index = next->index;
536 for(
auto ord = next; ord != end; ++ord) {
550 struct remove_ordered
559 return pos.
pos == iter_;
580 if(i_src ==
src.children_.end()) {
589 const auto before =
dst.
size();
590 dst.insert(
dst.end(), std::make_move_iterator(i_src->second.begin()), std::make_move_iterator(i_src->second.end()));
591 src.children_.erase(i_src);
594 for(std::size_t j = before; j <
dst.
size(); ++j) {
603 for(std::pair<const std::string, child_list>&
p :
children_) {
604 for(
auto& cfg :
p.second) {
605 cfg->recursive_clear_value(key);
614 std::size_t found = 0;
622 }
else if(
p.index >
index) {
628 pos->second.erase(pos->second.begin() +
index);
643 ERR_CF <<
"Error: attempting to delete non-existing child: " << key <<
"[" <<
index <<
"]";
657 const auto predicate = [
p](
const std::unique_ptr<config>& child)
659 return !
p ||
p(*child);
662 auto child_it = std::find_if(pos->second.begin(), pos->second.end(), predicate);
663 while(child_it != pos->second.end()) {
664 const auto index = std::distance(pos->second.begin(), child_it);
666 child_it = std::find_if(pos->second.begin() +
index, pos->second.end(), predicate);
672 const attribute_map::const_iterator
i =
values_.find(key);
678 return empty_attribute;
683 attribute_map::const_iterator
i =
values_.find(key);
684 return i !=
values_.end() ? &
i->second :
nullptr;
695 auto res =
values_.lower_bound(key);
697 if(res ==
values_.end() || key != res->first) {
698 res =
values_.emplace_hint(res, std::piecewise_construct, std::forward_as_tuple(key), std::tuple<>());
707 const std::string what =
formatter() <<
"[" << in_tag <<
"]" << old_key <<
"=";
708 const std::string
msg =
formatter() <<
"Use " << key <<
"= instead. " << message;
712 attribute_map::const_iterator
i =
values_.find(key);
723 return empty_attribute;
729 const std::string what =
formatter() <<
"[" << in_tag <<
"]" << old_key <<
"=";
735 return empty_attribute;
740 assert(
this != &cfg);
741 for(
const auto& [key, value] : cfg.
values_) {
742 if(key.substr(0, 7) ==
"add_to_") {
743 std::string add_to = key.substr(7);
744 values_[add_to] =
values_[add_to].to_double() + value.to_double();
745 }
else if(key.substr(0, 10) ==
"concat_to_") {
746 std::string concat_to = key.substr(10);
761 while(range.begin() != range.end() && range.begin()->second.blank()) {
773 while(range.begin() != range.end() && range.begin()->second.blank()) {
784 DBG_CF <<
"Key ‘" << name <<
"’ value ‘" << value <<
"’ pair not found as child of key ‘" << key <<
"’.";
787 return utils::nullopt;
791 [&](
const std::unique_ptr<config>& pcfg) {
792 const config& cfg = *pcfg;
793 return cfg[name] == value;
797 if(j !=
i->second.end()) {
801 DBG_CF <<
"Key ‘" << name <<
"’ value ‘" << value <<
"’ pair not found as child of key ‘" << key <<
"’.";
803 return utils::nullopt;
812 throw error(
"Cannot find child [" + std::string(key) +
"] with " + name +
"=" + value);
821 throw error(
"Cannot find child [" + std::string(key) +
"] with " + name +
"=" + value);
857 return any_child(&i_->pos->first, i_->pos->second[i_->index].get());
915 config* inserts =
nullptr;
917 for(
const auto& [key, value] :
values_) {
922 const attribute_map::const_iterator j =
c.values_.find(key);
923 if(j ==
c.values_.end() || (value != j->second && !value.blank())) {
924 if(inserts ==
nullptr) {
928 (*inserts)[key] = value;
932 config* deletes =
nullptr;
934 for(
const auto& [key, value] :
c.values_) {
939 const attribute_map::const_iterator itor =
values_.find(key);
940 if(itor ==
values_.end() || itor->second.blank()) {
941 if(deletes ==
nullptr) {
945 (*deletes)[key] =
"x";
949 std::vector<std::string> entities;
952 entities.push_back(child.first);
955 for(
const auto& child :
c.children_) {
957 entities.push_back(child.first);
961 for(
const std::string& entity : entities) {
962 const child_map::const_iterator itor_a =
children_.find(entity);
963 const child_map::const_iterator itor_b =
c.children_.find(entity);
971 std::size_t ndeletes = 0;
972 std::size_t
ai = 0, bi = 0;
973 while(
ai != a.size() || bi !=
b.size()) {
975 if(
ai < a.size() && bi <
b.size() && *a[
ai] == *
b[bi]) {
981 std::stringstream buf;
985 if(
b.size() - bi > a.size() -
ai) {
987 buf << bi - ndeletes;
988 new_delete.
values_[
"index"] = buf.str();
997 else if(
b.size() - bi < a.size() -
ai) {
1000 new_insert.
values_[
"index"] = buf.str();
1011 new_change.
values_[
"index"] = buf.str();
1029 for(
const auto& [key, value] : inserts->attribute_range()) {
1035 for(
const attribute& v : deletes->attribute_range()) {
1041 const std::size_t
index = lexical_cast<std::size_t>(
i[
"index"].str());
1042 for(
const auto [key, cfg] :
i.all_children_view()) {
1049 throw error(
"error in diff: could not find element '" + key +
"'");
1052 itor->second[
index]->apply_diff(cfg, track);
1057 const auto index = lexical_cast<std::size_t>(
i[
"index"].str());
1058 for(
const auto [key, cfg] :
i.all_children_view()) {
1067 const auto index = lexical_cast<std::size_t>(
i[
"index"].str());
1068 for(
const auto [key, cfg] :
i.all_children_view()) {
1074 throw error(
"error in diff: could not find element '" + key +
"'");
1087 const auto index = lexical_cast<std::size_t>(
i[
"index"].str());
1088 for(
const auto [key, cfg] :
i.all_children_view()) {
1094 const std::size_t
index = lexical_cast<std::size_t>(
i[
"index"].str());
1095 for(
const auto [key, cfg] :
i.all_children_view()) {
1102 throw error(
"error in diff: could not find element '" + key +
"'");
1105 itor->second[
index]->clear_diff_track(cfg);
1109 for(std::pair<const std::string, child_list>&
p :
children_) {
1110 for(
auto& cfg :
p.second) {
1121 std::vector<child_pos> to_remove;
1122 std::map<std::string, unsigned> visitations;
1130 const std::string&
tag =
i->pos->first;
1131 const child_map::const_iterator j =
c.children_.find(
tag);
1133 if(j !=
c.children_.end()) {
1134 unsigned& visits = visitations[
tag];
1136 if(visits < j->second.size()) {
1138 const config& merge_child = *j->second[visits++];
1140 if(merge_child[
"__remove"].to_bool()) {
1141 to_remove.push_back(*
i);
1150 for(
const auto& [
tag, list] :
c.children_) {
1151 unsigned& visits = visitations[
tag];
1152 while(visits < list.size()) {
1158 std::map<std::string, std::size_t> removals;
1160 const std::string&
tag = pos.pos->first;
1161 auto& removes = removals[
tag];
1183 for(
const auto& [key, value] : cfg.
values_) {
1194 for(
const auto& [key, value] :
filter.attribute_range()) {
1195 if(key.compare(0, 8,
"glob_on_") == 0) {
1203 if(!v || *v != value) {
1210 for(
const auto [key, cfg] :
filter.all_children_view()) {
1212 result = result && !
matches(cfg);
1214 }
else if(key ==
"and") {
1215 result = result &&
matches(cfg);
1217 }
else if(key ==
"or") {
1218 result = result ||
matches(cfg);
1224 if(j.matches(cfg)) {
1230 result = result && found;
1238 std::ostringstream outstream;
1240 return outstream.str();
1253 for(
int j = 0; j <
i - 1; j++) {
1257 outstream << key <<
" = " << value <<
'\n';
1261 for(
int j = 0; j <
i - 1; ++j) {
1265 outstream <<
"[" << key <<
"]\n";
1268 for(
int j = 0; j <
i - 1; ++j) {
1272 outstream <<
"[/" << key <<
"]\n";
1281 static const unsigned int hash_length = 128;
1282 static const char hash_string[] =
"+-,.<>0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
1286 for(
i = 0;
i != hash_length; ++
i) {
1293 for(
const auto& [key, value] :
values_) {
1300 if(++
i == hash_length) {
1305 std::string base_str = value.t_str().base_str();
1306 for(
const char c : base_str) {
1308 if(++
i == hash_length) {
1315 std::string child_hash = cfg.hash();
1316 for(
char c : child_hash) {
1319 if(
i == hash_length) {
1325 for(
i = 0;
i != hash_length; ++
i) {
1348 return valid_tag(pair.first) &&
1349 std::all_of(pair.second.begin(), pair.second.end(),
1350 [](const auto& c) { return c->validate_wml(); });
1365 for(; !x.empty() && !y.empty(); x.pop_front(), y.pop_front()) {
1366 if(x.front().key != y.front().key || x.front().cfg != y.front().cfg) {
1371 return x.empty() && y.empty();
static unsigned int hash_str(const std::string &str)
Variant for storing WML attributes.
std::string str(const std::string &fallback="") const
bool blank() const
Tests for an attribute that was never set.
A config object defines a single node in a WML file, with access to child nodes.
void merge_children_by_attribute(config_key_type key, config_key_type attribute)
All children with the given key and with equal values of the specified attribute will be merged into ...
const attribute_value & get_old_attribute(config_key_type key, const std::string &old_key, const std::string &in_tag, const std::string &message="") const
Function to handle backward compatibility Get the value of key and if missing try old_key and log a d...
void append(const config &cfg)
Append data from another config object to this one.
const attribute_value & get_or(const config_key_type key, const config_key_type default_key) const
Chooses a value.
all_children_iterator erase(const all_children_iterator &i)
const_all_children_iterator ordered_begin() const
boost::iterator_range< const_all_children_iterator > const_all_children_itors
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.
std::size_t attribute_count() const
Count the number of non-blank attributes.
config & mandatory_child(config_key_type key, int n=0)
Returns the nth child with the given key, or throws an error if there is none.
attribute_value & operator[](config_key_type key)
Returns a reference to the attribute with the given key.
void recursive_clear_value(config_key_type key)
bool matches(const config &filter) const
void remove_child(config_key_type key, std::size_t index)
void merge_children(config_key_type key)
All children with the given key will be merged into the first element with that key.
const_attr_itors attribute_range() const
std::size_t child_count(config_key_type key) const
attribute_map values_
All the attributes of this node.
bool validate_wml() const
Returns true if this object represents valid WML, i.e.
optional_config_impl< const config > get_deprecated_child(config_key_type old_key, const std::string &in_tag, DEP_LEVEL level, const std::string &message) const
Get a deprecated child and log a deprecation message.
auto all_children_view() const
In-order iteration over all children.
void merge_attributes(const config &)
const_all_children_iterator ordered_end() const
boost::iterator_range< child_iterator > child_itors
config & add_child_at(config_key_type key, const config &val, std::size_t index)
config & find_mandatory_child(config_key_type key, const std::string &name, const std::string &value)
const attribute_value & get_deprecated_attribute(config_key_type old_key, const std::string &in_tag, DEP_LEVEL level, const std::string &message) const
Get a deprecated attribute without a direct substitute, and log a deprecation message.
std::vector< std::unique_ptr< config > > child_list
optional_config_impl< 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.
static bool valid_tag(config_key_type name)
boost::iterator_range< attribute_iterator > attr_itors
const_all_children_iterator ordered_cbegin() const
bool has_child(config_key_type key) const
Determine whether a config has a child or not.
bool has_attribute(config_key_type key) const
void merge_with(const config &c)
Merge config 'c' into this config, overwriting this config's values.
void clear_children_impl(config_key_type key)
const_child_itors get_deprecated_child_range(config_key_type old_key, const std::string &in_tag, DEP_LEVEL level, const std::string &message) const
Get a deprecated child range and log a deprecation message.
void remove_children(config_key_type key, const std::function< bool(const config &)> &p={})
Removes all children with tag key for which p returns true.
void clear_all_children()
void inherit_from(const config &c)
Merge config 'c' into this config, preserving this config's values.
const_all_children_itors all_children_range() const
In-order iteration over all children.
static const char * diff_track_attribute
The name of the attribute used for tracking diff changes.
boost::iterator_range< all_children_iterator > all_children_itors
child_itors child_range(config_key_type key)
void append_attributes(const config &cfg)
Adds attributes from cfg.
config & child_or_add(config_key_type key)
Returns a reference to the first child with the given key.
boost::iterator_range< const_attribute_iterator > const_attr_itors
void apply_diff(const config &diff, bool track=false)
A function to apply a diff config onto this config object.
std::size_t all_children_count() const
child_map children_
A list of all children of this node.
const_all_children_iterator ordered_cend() const
config & add_child_at_total(config_key_type key, const config &val, std::size_t pos)
void remove_attribute(config_key_type key)
static bool valid_attribute(config_key_type name)
config get_diff(const config &c) const
A function to get the differences between this object, and 'c', as another config object.
std::string debug() const
attribute_map::value_type attribute
void inherit_attributes(const config &c)
Merge the attributes of config 'c' into this config, preserving this config's values.
std::size_t find_total_first_of(config_key_type key, std::size_t start=0)
std::vector< child_pos > ordered_children
void clear_diff_track(const config &diff)
Clear any tracking info from a previous apply_diff call with tracking.
void append_children_by_move(config &cfg, config_key_type key)
Moves children with the given name from the given config to this one.
boost::iterator_range< const_child_iterator > const_child_itors
void append_children(const config &cfg)
Adds children from cfg.
void splice_children(config &src, config_key_type key)
Moves all the children with tag key from src to this.
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.
friend bool operator==(const config &a, const config &b)
optional_config_impl< config > optional_child(config_key_type key, int n=0)
Equivalent to mandatory_child, but returns an empty optional if the nth child was not found.
config & operator=(const config &)
config & add_child(config_key_type key)
std::ostream & operator<<(std::ostream &outstream, const config &cfg)
static lg::log_domain log_wml("wml")
static lg::log_domain log_config("config")
Definitions for the interface to Wesnoth Markup Language (WML).
std::string_view config_key_type
std::string deprecated_message(const std::string &elem_name, DEP_LEVEL level, const version_info &version, const std::string &detail)
DEP_LEVEL
See https://wiki.wesnoth.org/CompatibilityStandards for more info.
Interfaces for manipulating version numbers of engine, add-ons, etc.
New lexcical_cast header.
Standard logging facilities (interface).
A small explanation about what's going on here: Each action has access to two game_info objects First...
EXIT_STATUS start(bool clear_id, const std::string &filename, bool take_screenshot, const std::string &screenshot_filename)
Main interface for launching the editor from the title screen.
std::string tag(std::string_view tag, Args &&... data)
Wraps the given data in the specified formatting tag.
std::size_t index(std::string_view str, const std::size_t index)
Codepoint index corresponding to the nth character in a UTF-8 string.
bool wildcard_string_match(const std::string &str, const std::string &match)
Match using '*' as any number of characters (including none), '+' as one or more characters,...
void erase_if(Container &container, const Predicate &predicate)
Convenience wrapper for using std::remove_if on a container.
std::string::const_iterator iterator
static void msg(const char *act, debug_info &i, const char *to="", const char *result="")
rect dst
Location on the final composed sheet.
rect src
Non-transparent portion of the surface to compose.
std::vector< child_pos >::iterator Itor
reference operator*() const
const child_map::key_type & key
reference operator*() const
constexpr point size() const
static map_location::direction n