The Battle for Wesnoth  1.17.10+dev
schema_validator.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2011 - 2022
3  by Sytyi Nick <nsytyi@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 
17 
18 #include "filesystem.hpp"
19 #include "gettext.hpp"
20 #include "log.hpp"
23 #include "wml_exception.hpp"
24 #include <tuple>
25 
26 namespace schema_validation
27 {
28 static lg::log_domain log_validation("validation");
29 
30 #define ERR_VL LOG_STREAM(err, log_validation)
31 #define WRN_VL LOG_STREAM(warn, log_validation)
32 #define LOG_VL LOG_STREAM(info, log_validation)
33 
34 static std::string at(const std::string& file, int line)
35 {
36  std::ostringstream ss;
37  ss << line << " " << file;
38  return "at " + ::lineno_string(ss.str());
39 }
40 
41 static void print_output(const std::string& message, bool flag_exception = false)
42 {
43 #ifndef VALIDATION_ERRORS_LOG
44  if(flag_exception) {
45  throw wml_exception("Validation error occurred", message);
46  } else {
47  ERR_VL << message;
48  }
49 #else
50  // dirty hack to avoid "unused" error in case of compiling with definition on
51  flag_exception = true;
52  if(flag_exception) {
53  ERR_VL << message;
54  }
55 #endif
56 }
57 
58 static void extra_tag_error(const std::string& file,
59  int line,
60  const std::string& name,
61  int n,
62  const std::string& parent,
63  bool flag_exception)
64 {
65  std::ostringstream ss;
66  ss << "Extra tag [" << name << "]; there may only be " << n << " [" << name << "] in [" << parent << "]\n"
67  << at(file, line) << "\n";
68  print_output(ss.str(), flag_exception);
69 }
70 
71 static void wrong_tag_error(
72  const std::string& file, int line, const std::string& name, const std::string& parent, bool flag_exception)
73 {
74  std::ostringstream ss;
75  ss << "Tag [" << name << "] may not be used in [" << parent << "]\n" << at(file, line) << "\n";
76  print_output(ss.str(), flag_exception);
77 }
78 
79 static void missing_tag_error(const std::string& file,
80  int line,
81  const std::string& name,
82  int n,
83  const std::string& parent,
84  bool flag_exception)
85 {
86  std::ostringstream ss;
87  ss << "Missing tag [" << name << "]; there must be " << n << " [" << name << "]s in [" << parent << "]\n"
88  << at(file, line) << "\n";
89  print_output(ss.str(), flag_exception);
90 }
91 
92 static void extra_key_error(
93  const std::string& file, int line, const std::string& tag, const std::string& key, bool flag_exception)
94 {
95  std::ostringstream ss;
96  ss << "Invalid key '" << key << "=' in tag [" << tag << "]\n" << at(file, line) << "\n";
97  print_output(ss.str(), flag_exception);
98 }
99 
100 static void missing_key_error(
101  const std::string& file, int line, const std::string& tag, const std::string& key, bool flag_exception)
102 {
103  std::ostringstream ss;
104  ss << "Missing key '" << key << "=' in tag [" << tag << "]\n" << at(file, line) << "\n";
105  print_output(ss.str(), flag_exception);
106 }
107 
108 static void wrong_value_error(const std::string& file,
109  int line,
110  const std::string& tag,
111  const std::string& key,
112  const std::string& value,
113  const std::string& expected,
114  bool flag_exception)
115 {
116  std::ostringstream ss;
117  ss << "Invalid value '";
118  if(value.length() > 128)
119  ss << value.substr(0, 128) << "...";
120  else ss << value;
121  ss << "' in key '" << key << "=' in tag [" << tag << "]\n" << " (expected value of type " << expected << ") " << at(file, line) << "\n";
122  print_output(ss.str(), flag_exception);
123 }
124 
125 static void wrong_path_error(const std::string& file,
126  int line,
127  const std::string& tag,
128  const std::string& key,
129  const std::string& value,
130  bool flag_exception)
131 {
132  std::ostringstream ss;
133  ss << "Unknown path reference '" << value << "' in key '" << key << "=' in tag [" << tag << "]\n" << at(file, line) << "\n";
134  print_output(ss.str(), flag_exception);
135 }
136 
137 static void duplicate_tag_error(const std::string& file,
138  int line,
139  const std::string& tag,
140  const std::string& pat,
141  const std::string& value,
142  bool flag_exception)
143 {
144  std::ostringstream ss;
145  ss << "Duplicate or fully-overlapping tag definition '" << value << "' (which is also matched by '" << pat << "') in tag [" << tag << "]\n" << at(file, line) << "\n";
146  print_output(ss.str(), flag_exception);
147 }
148 
149 static void duplicate_key_error(const std::string& file,
150  int line,
151  const std::string& tag,
152  const std::string& pat,
153  const std::string& value,
154  bool flag_exception)
155 {
156  std::ostringstream ss;
157  ss << "Duplicate or fully-overlapping key definition '" << value << "' (which is also matched by '" << pat << "') in tag [" << tag << "]\n" << at(file, line) << "\n";
158  print_output(ss.str(), flag_exception);
159 }
160 
161 static void inheritance_loop_error(const std::string& file,
162  int line,
163  const std::string& tag,
164  const std::string& key,
165  const std::string& value,
166  int index,
167  bool flag_exception)
168 {
169  std::ostringstream ss;
170  ss << "Inheritance loop " << key << "=" << value << " found (at offset " << index << ") in tag [" << tag << "]\n" << at(file, line) << "\n";
171  print_output(ss.str(), flag_exception);
172 }
173 
174 static void wrong_type_error(const std::string & file, int line,
175  const std::string & tag,
176  const std::string & key,
177  const std::string & type,
178  bool flag_exception)
179 {
180  std::ostringstream ss;
181  ss << "Invalid type '" << type << "' in key '" << key << "=' in tag [" << tag << "]\n" << at(file, line) << "\n";
182  print_output(ss.str(), flag_exception);
183 }
184 
186 {
187 }
188 
189 schema_validator::schema_validator(const std::string& config_file_name, bool validate_schema)
190  : abstract_validator(config_file_name)
192  , config_read_(false)
193  , validate_schema_(validate_schema)
194 {
195  if(!read_config_file(config_file_name)) {
196  ERR_VL << "Schema file " << config_file_name << " was not read.";
197  throw abstract_validator::error("Schema file " + config_file_name + " was not read.\n");
198  } else {
199  stack_.push(&root_);
200  counter_.emplace();
201  cache_.emplace();
203  LOG_VL << "Schema file " << config_file_name << " was read.";
204  LOG_VL << "Validator initialized";
205  }
206 }
207 
208 bool schema_validator::read_config_file(const std::string& filename)
209 {
210  config cfg;
211  try {
212  std::unique_ptr<abstract_validator> validator;
213  if(validate_schema_) {
214  validator.reset(new schema_self_validator());
215  }
216  preproc_map preproc(game_config::config_cache::instance().get_preproc_map());
217  filesystem::scoped_istream stream = preprocess_file(filename, &preproc);
218  read(cfg, *stream, validator.get());
219  } catch(const config::error& e) {
220  ERR_VL << "Failed to read file " << filename << ":\n" << e.what();
221  return false;
222  }
223 
224  for(const config& g : cfg.child_range("wml_schema")) {
225  for(const config& schema : g.child_range("tag")) {
226  if(schema["name"].str() == "root") {
227  //@NOTE Don't know, maybe merging of roots needed.
228  root_ = wml_tag(schema);
229  }
230  }
231  types_["t_string"] = std::make_shared<wml_type_tstring>();
232  for(const config& type : g.child_range("type")) {
233  try {
234  types_[type["name"].str()] = wml_type::from_config(type);
235  } catch(const std::exception&) {
236  // Need to check all type values in schema-generator
237  }
238  }
239  }
240 
241  config_read_ = true;
242  return true;
243 }
244 
245 /*
246  * Please, @Note that there is some magic in pushing and poping to/from stacks.
247  * assume they all are on their place due to parser algorithm
248  * and validation logic
249  */
250 void schema_validator::open_tag(const std::string& name, const config& parent, int start_line, const std::string& file, bool addition)
251 {
252  if(name.empty()) {
253  // Opened the root tag; nothing special to do here
254  } else if(!stack_.empty()) {
255  const wml_tag* tag = nullptr;
256 
257  if(stack_.top()) {
258  tag = active_tag().find_tag(name, root_, parent);
259 
260  if(!tag) {
261  wrong_tag_error(file, start_line, name, stack_.top()->get_name(), create_exceptions_);
262  } else {
263  if(!addition) {
264  counter& cnt = counter_.top()[name];
265  ++cnt.cnt;
266  counter& total_cnt = counter_.top()[""];
267  ++total_cnt.cnt;
268  }
269  }
270  }
271 
272  stack_.push(tag);
273  } else {
274  stack_.push(nullptr);
275  }
276 
277  counter_.emplace();
278  cache_.emplace();
279 }
280 
282 {
283  stack_.pop();
284  counter_.pop();
285  // cache_ is cleared in another place.
286 }
287 
288 void schema_validator::validate(const config& cfg, const std::string& name, int start_line, const std::string& file)
289 {
290  // close previous errors and print them to output.
291  for(auto& m : cache_.top()) {
292  for(auto& list : m.second) {
293  print(list);
294  }
295  }
296 
297  cache_.pop();
298 
299  // clear cache
300  auto cache_it = cache_.top().find(&cfg);
301  if(cache_it != cache_.top().end()) {
302  cache_it->second.clear();
303  }
304 
305  // Please note that validating unknown tag keys the result will be false
306  // Checking all elements counters.
307  if(have_active_tag() && is_valid()) {
308  const wml_tag& active = active_tag();
309  for(const auto& tag : active.tags(cfg)) {
310  int cnt = counter_.top()[tag.first].cnt;
311 
312  if(tag.second.get_min() > cnt) {
313  queue_message(cfg, MISSING_TAG, file, start_line, tag.second.get_min(), tag.first, "", name);
314  continue;
315  }
316 
317  if(tag.second.get_max() < cnt) {
318  queue_message(cfg, EXTRA_TAG, file, start_line, tag.second.get_max(), tag.first, "", name);
319  }
320  }
321 
322  int total_cnt = counter_.top()[""].cnt;
323  if(active.get_min_children() > total_cnt) {
324  queue_message(cfg, MISSING_TAG, file, start_line, active.get_min_children(), "*", "", active.get_name());
325  } else if(active_tag().get_max_children() < total_cnt) {
326  queue_message(cfg, EXTRA_TAG, file, start_line, active.get_max_children(), "*", "", active.get_name());
327  }
328 
329  // Checking if all mandatory keys are present
330  for(const auto& key : active.keys(cfg)) {
331  if(key.second.is_mandatory()) {
332  if(cfg.get(key.first) == nullptr) {
333  queue_message(cfg, MISSING_KEY, file, start_line, 0, name, key.first);
334  }
335  }
336  }
337  }
338 }
339 
341  const config& cfg, const std::string& name, const config_attribute_value& value, int start_line, const std::string& file)
342 {
343  if(have_active_tag() && !active_tag().get_name().empty() && is_valid()) {
344  // checking existing keys
345  const wml_key* key = active_tag().find_key(name, cfg);
346  if(key) {
347  bool matched = false;
348  for(auto& possible_type : utils::split(key->get_type())) {
349  if(auto type = find_type(possible_type)) {
350  if(type->matches(value, types_)) {
351  matched = true;
352  break;
353  }
354  }
355  }
356  if(!matched) {
357  queue_message(cfg, WRONG_VALUE, file, start_line, 0, active_tag().get_name(), name, value, key->get_type());
358  }
359  } else {
360  queue_message(cfg, EXTRA_KEY, file, start_line, 0, active_tag().get_name(), name);
361  }
362  }
363 }
364 
366 {
367  assert(have_active_tag() && "Tried to get active tag name when there was none");
368  return *stack_.top();
369 }
370 
372 {
373  auto it = types_.find(type);
374  if(it == types_.end()) {
375  return nullptr;
376  }
377  return it->second;
378 }
379 
381 {
382  return !stack_.empty() && stack_.top();
383 }
384 
386  std::stack<const wml_tag*> temp = stack_;
387  std::deque<std::string> path;
388  while(!temp.empty()) {
389  path.push_front(temp.top()->get_name());
390  temp.pop();
391  }
392  if(path.front() == "root") {
393  path.pop_front();
394  }
395  return utils::join(path, "/");
396 }
397 
399 {
400  switch(el.type) {
401  case WRONG_TAG:
403  break;
404  case EXTRA_TAG:
405  extra_tag_error(el.file, el.line, el.tag, el.n, el.value, create_exceptions_);
406  break;
407  case MISSING_TAG:
408  missing_tag_error(el.file, el.line, el.tag, el.n, el.value, create_exceptions_);
409  break;
410  case EXTRA_KEY:
412  break;
413  case WRONG_VALUE:
415  break;
416  case MISSING_KEY:
418  break;
419  }
420 }
421 
423  : schema_validator(filesystem::get_wml_location("schema/schema.cfg"), false)
424  , type_nesting_()
425  , condition_nesting_()
426 {
427  defined_types_.insert("t_string");
428 }
429 
430 
431 void schema_self_validator::open_tag(const std::string& name, const config& parent, int start_line, const std::string& file, bool addition)
432 {
433  schema_validator::open_tag(name, parent, start_line, file, addition);
434  if(name == "type") {
435  type_nesting_++;
436  }
437  if(condition_nesting_ == 0) {
438  if(name == "if" || name == "switch") {
439  condition_nesting_ = 1;
440  } else if(name == "tag") {
441  tag_stack_.emplace();
442  }
443  } else {
445  }
446 }
447 
449 {
450  if(have_active_tag()) {
451  auto tag_name = active_tag().get_name();
452  if(tag_name == "type") {
453  type_nesting_--;
454  } else if(condition_nesting_ == 0 && tag_name == "tag") {
455  tag_stack_.pop();
456  }
457  }
458  if(condition_nesting_ > 0) {
460  }
462 }
463 
465  std::vector<std::string> path = utils::split(ref.value_, '/');
466  std::string suffix = path.back();
467  path.pop_back();
468  while(!path.empty()) {
469  std::string prefix = utils::join(path, "/");
470  auto link = links_.find(prefix);
471  if(link != links_.end()) {
472  std::string new_path = link->second + "/" + suffix;
473  if(defined_tag_paths_.count(new_path) > 0) {
474  return true;
475  }
476  path = utils::split(new_path, '/');
477  suffix = path.back();
478  //suffix = link->second + "/" + suffix;
479  } else {
480  const auto supers = derivations_.equal_range(prefix);
481  if(supers.first != supers.second) {
482  reference super_ref = ref;
483  for(auto cur = supers.first ; cur != supers.second; ++cur) {
484  super_ref.value_ = cur->second + "/" + suffix;
485  if(super_ref.value_.find(ref.value_) == 0) {
486  continue;
487  }
488  if(tag_path_exists(cfg, super_ref)) {
489  return true;
490  }
491  }
492  }
493  std::string new_path = prefix + "/" + suffix;
494  if(defined_tag_paths_.count(new_path) > 0) {
495  return true;
496  }
497  suffix = path.back() + "/" + suffix;
498  }
499  path.pop_back();
500  }
501  return false;
502 }
503 
504 bool schema_self_validator::name_matches(const std::string& pattern, const std::string& name)
505 {
506  for(const std::string& pat : utils::split(pattern)) {
507  if(utils::wildcard_string_match(name, pat)) return true;
508  }
509  return false;
510 }
511 
512 void schema_self_validator::check_for_duplicates(const std::string& name, std::vector<std::string>& seen, const config& cfg, message_type type, const std::string& file, int line, const std::string& tag) {
513  auto split = utils::split(name);
514  for(const std::string& pattern : seen) {
515  for(const std::string& key : split) {
516  if(name_matches(pattern, key)) {
517  queue_message(cfg, type, file, line, 0, tag, pattern, name);
518  continue;
519  }
520  }
521  }
522  seen.push_back(name);
523 }
524 
525 void schema_self_validator::validate(const config& cfg, const std::string& name, int start_line, const std::string& file)
526 {
527  if(type_nesting_ == 1 && name == "type") {
528  defined_types_.insert(cfg["name"]);
529  } else if(name == "tag") {
530  bool first_tag = true, first_key = true;
531  std::vector<std::string> tag_names, key_names;
532  for(auto current : cfg.all_children_range()) {
533  if(current.key == "tag" || current.key == "link") {
534  std::string tag_name = current.cfg["name"];
535  if(current.key == "link") {
536  tag_name.erase(0, tag_name.find_last_of('/') + 1);
537  }
538  if(first_tag) {
539  tag_names.push_back(tag_name);
540  first_tag = false;
541  continue;
542  }
543  check_for_duplicates(tag_name, tag_names, current.cfg, DUPLICATE_TAG, file, start_line, current.key);
544  } else if(current.key == "key") {
545  std::string key_name = current.cfg["name"];
546  if(first_key) {
547  key_names.push_back(key_name);
548  first_key = false;
549  continue;
550  }
551  check_for_duplicates(key_name, key_names, current.cfg, DUPLICATE_KEY, file, start_line, current.key);
552  }
553  }
554  } else if(name == "wml_schema") {
555  using namespace std::placeholders;
556  std::vector<reference> missing_types = referenced_types_, missing_tags = referenced_tag_paths_;
557  // Remove all the known types
558  missing_types.erase(std::remove_if(missing_types.begin(), missing_types.end(), std::bind(&reference::match, std::placeholders::_1, std::cref(defined_types_))), missing_types.end());
559  // Remove all the known tags. This is more complicated since links behave similar to a symbolic link.
560  // In other words, the presence of links means there may be more than one way to refer to a given tag.
561  // But that's not all! It's possible to refer to a tag through a derived tag even if it's actually defined in the base tag.
562  auto end = std::remove_if(missing_tags.begin(), missing_tags.end(), std::bind(&reference::match, std::placeholders::_1, std::cref(defined_tag_paths_)));
563  missing_tags.erase(std::remove_if(missing_tags.begin(), end, std::bind(&schema_self_validator::tag_path_exists, this, std::ref(cfg), std::placeholders::_1)), missing_tags.end());
564  std::sort(missing_types.begin(), missing_types.end());
565  std::sort(missing_tags.begin(), missing_tags.end());
566  static const config dummy;
567  for(auto& ref : missing_types) {
568  std::string tag_name;
569  if(ref.tag_ == "key") {
570  tag_name = "type";
571  } else {
572  tag_name = "link";
573  }
574  queue_message(dummy, WRONG_TYPE, ref.file_, ref.line_, 0, ref.tag_, tag_name, ref.value_);
575  }
576  for(auto& ref : missing_tags) {
577  std::string tag_name;
578  if(ref.tag_ == "tag") {
579  tag_name = "super";
580  } else if(ref.tag_ == "link") {
581  tag_name = "name";
582  }
583  queue_message(dummy, WRONG_PATH, ref.file_, ref.line_, 0, ref.tag_, tag_name, ref.value_);
584  }
585  }
586  schema_validator::validate(cfg, name, start_line, file);
587 }
588 
589 void schema_self_validator::validate_key(const config& cfg, const std::string& name, const config_attribute_value& value, int start_line, const std::string& file)
590 {
591  schema_validator::validate_key(cfg, name, value, start_line, file);
592  if(have_active_tag() && !active_tag().get_name().empty() && is_valid()) {
593  const std::string& tag_name = active_tag().get_name();
594  if(tag_name == "key" && name == "type" ) {
595  for(auto& possible_type : utils::split(cfg["type"])) {
596  referenced_types_.emplace_back(possible_type, file, start_line, tag_name);
597  }
598  } else if((tag_name == "type" || tag_name == "element") && name == "link") {
599  referenced_types_.emplace_back(cfg["link"], file, start_line, tag_name);
600  } else if(tag_name == "link" && name == "name") {
601  referenced_tag_paths_.emplace_back(cfg["name"], file, start_line, tag_name);
602  std::string link_name = utils::split(cfg["name"].str(), '/').back();
603  links_.emplace(current_path() + "/" + link_name, cfg["name"]);
604  } else if(tag_name == "tag" && name == "super") {
605  for(auto super : utils::split(cfg["super"])) {
606  referenced_tag_paths_.emplace_back(super, file, start_line, tag_name);
607  if(condition_nesting_ > 0) {
608  continue;
609  }
610  if(current_path() == super) {
611  queue_message(cfg, SUPER_LOOP, file, start_line, cfg["super"].str().find(super), tag_name, "super", super);
612  continue;
613  }
614  derivations_.emplace(current_path(), super);
615  }
616  } else if(condition_nesting_ == 0 && tag_name == "tag" && name == "name") {
617  tag_stack_.top() = value.str();
619  }
620  }
621 }
622 
624 {
625  std::stack<std::string> temp = tag_stack_;
626  std::deque<std::string> path;
627  while(!temp.empty()) {
628  path.push_front(temp.top());
629  temp.pop();
630  }
631  if(path.front() == "root") {
632  path.pop_front();
633  }
634  return utils::join(path, "/");
635 }
636 
638 {
639  return std::tie(file_, line_) < std::tie(other.file_, other.line_);
640 }
641 
642 bool schema_self_validator::reference::match(const std::set<std::string>& with)
643 {
644  return with.count(value_) > 0;
645 }
646 
648 {
649  // The problem is that the schema being validated is that of the schema!!!
650  return root.find_tag(value_, root, cfg) != nullptr;
651 }
652 
654 {
656  switch(el.type) {
657  case WRONG_TYPE:
659  break;
660  case WRONG_PATH:
662  break;
663  case DUPLICATE_TAG:
665  break;
666  case DUPLICATE_KEY:
668  break;
669  case SUPER_LOOP:
670  inheritance_loop_error(el.file, el.line, el.tag, el.key, el.value, el.n, create_exceptions_);
671  break;
672  }
673 }
674 
675 } // namespace schema_validation{
std::string lineno_string(const std::string &lineno)
std::stack< const wml_tag * > stack_
bool create_exceptions_
Controls the way to print errors.
static void inheritance_loop_error(const std::string &file, int line, const std::string &tag, const std::string &key, const std::string &value, int index, bool flag_exception)
static config_cache & instance()
Get reference to the singleton object.
bool match(const std::set< std::string > &with)
int get_min_children() const
Definition: tag.hpp:177
void check_for_duplicates(const std::string &name, std::vector< std::string > &seen, const config &cfg, message_type type, const std::string &file, int line, const std::string &tag)
const_all_children_itors all_children_range() const
In-order iteration over all children.
Definition: config.cpp:978
cnt_stack counter_
Contains number of children.
static void wrong_tag_error(const std::string &file, int line, const std::string &name, const std::string &parent, bool flag_exception)
std::string join(const T &v, const std::string &s=",")
Generates a new string joining container items in a list.
std::multimap< std::string, std::string > derivations_
virtual void open_tag(const std::string &name, const config &parent, int start_line=0, const std::string &file="", bool addition=false) override
Is called when parser opens tag.
bool tag_path_exists(const config &cfg, const reference &ref)
Variant for storing WML attributes.
Add a special kind of assert to validate whether the input from WML doesn&#39;t contain any problems that...
child_itors child_range(config_key_type key)
Definition: config.cpp:344
int get_max_children() const
Definition: tag.hpp:182
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:780
Stores information about tag.
Definition: tag.hpp:47
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.
void print(message_info &message) override
static void missing_tag_error(const std::string &file, int line, const std::string &name, int n, const std::string &parent, bool flag_exception)
wml_tag root_
Root of schema information.
bool config_read_
Shows, if validator is initialized with schema file.
const wml_key * find_key(const std::string &name, const config &match, bool ignore_super=false) const
Returns pointer to child key.
Definition: tag.cpp:109
boost::iterator_range< key_iterator > keys(const config &cfg_match) const
Definition: tag.hpp:306
One of the realizations of serialization/validator.hpp abstract validator.
Used in parsing config file.
Definition: validator.hpp:37
static void extra_key_error(const std::string &file, int line, const std::string &tag, const std::string &key, bool flag_exception)
std::stack< message_map > cache_
Caches error messages.
static void missing_key_error(const std::string &file, int line, const std::string &tag, const std::string &key, bool flag_exception)
static std::string at(const std::string &file, int line)
virtual void validate_key(const config &cfg, const std::string &name, const config_attribute_value &value, int start_line, const std::string &file) override
Checks if key is allowed and if its value is valid What exactly is validated depends on validator rea...
void read(config &cfg, std::istream &in, abstract_validator *validator)
Definition: parser.cpp:627
wml_type::map types_
Type validators.
Used to manage with not initialized validators Supposed to be thrown from the constructor.
Definition: validator.hpp:97
bool strict_validation_enabled
Definition: validator.cpp:21
const wml_tag * find_tag(const std::string &fullpath, const wml_tag &root, const config &match, bool ignore_super=false) const
Returns pointer to tag using full path to it.
Definition: tag.cpp:165
bool can_find(const wml_tag &root, const config &cfg)
static void wrong_value_error(const std::string &file, int line, const std::string &tag, const std::string &key, const std::string &value, const std::string &expected, bool flag_exception)
std::unique_ptr< std::istream > scoped_istream
Definition: filesystem.hpp:39
std::string path
Definition: game_config.cpp:39
static void print_output(const std::string &message, bool flag_exception=false)
const char * what() const noexcept
Definition: exceptions.hpp:36
static void duplicate_key_error(const std::string &file, int line, const std::string &tag, const std::string &pat, const std::string &value, bool flag_exception)
const std::string & get_type() const
Definition: key.hpp:64
static void duplicate_tag_error(const std::string &file, int line, const std::string &tag, const std::string &pat, const std::string &value, bool flag_exception)
#define LOG_VL
schema_validator(const std::string &filename, bool validate_schema=false)
Initializes validator from file.
Helper class, don&#39;t construct this directly.
boost::iterator_range< tag_iterator > tags(const config &cfg_match) const
Definition: tag.hpp:301
virtual void validate_key(const config &cfg, const std::string &name, const config_attribute_value &value, int start_line, const std::string &file) override
Checks if key is allowed and if its value is valid What exactly is validated depends on validator rea...
void line(int from_x, int from_y, int to_x, int to_y)
Draw a line.
Definition: draw.cpp:171
static std::shared_ptr< wml_type > from_config(const config &cfg)
Definition: type.cpp:52
std::string get_wml_location(const std::string &filename, const std::string &current_dir)
Returns a complete path to the actual WML file or directory or an empty string if the file isn&#39;t pres...
wml_key is used to save the information about one key.
Definition: key.hpp:36
bool read_config_file(const std::string &filename)
Reads config from input.
double g
Definition: astarsearch.cpp:65
virtual void close_tag() override
As far as parser is built on stack, some realizations can store stack too.
std::shared_ptr< wml_type > ptr
Definition: type.hpp:42
Declarations for File-IO.
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:72
void queue_message(const config &cfg, T &&... args)
void expand_all(wml_tag &root)
Calls the expansion on each child.
Definition: tag.cpp:243
std::vector< std::string > split(const config_attribute_value &val)
static void wrong_path_error(const std::string &file, int line, const std::string &tag, const std::string &key, const std::string &value, bool flag_exception)
virtual void validate(const config &cfg, const std::string &name, int start_line, const std::string &file) override
Validates config.
const std::string & get_name() const
Definition: tag.hpp:162
virtual void open_tag(const std::string &name, const config &parent, int start_line=0, const std::string &file="", bool addition=false) override
Is called when parser opens tag.
Standard logging facilities (interface).
virtual void close_tag() override
As far as parser is built on stack, some realizations can store stack too.
std::map< std::string, struct preproc_define > preproc_map
#define e
Realization of serialization/validator.hpp abstract validator.
wml_type::ptr find_type(const std::string &type) const
#define ERR_VL
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:60
filesystem::scoped_istream preprocess_file(const std::string &fname, preproc_map *defines)
Function to use the WML preprocessor on a file.
static void wrong_type_error(const std::string &file, int line, const std::string &tag, const std::string &key, const std::string &type, bool flag_exception)
static lg::log_domain log_validation("validation")
static map_location::DIRECTION n
static bool name_matches(const std::string &pattern, const std::string &name)
virtual void print(message_info &)
virtual void validate(const config &cfg, const std::string &name, int start_line, const std::string &file) override
Validates config.
std::map< std::string, std::string > links_
std::vector< reference > referenced_tag_paths_
std::string str(const std::string &fallback="") const
static void extra_tag_error(const std::string &file, int line, const std::string &name, int n, const std::string &parent, bool flag_exception)