The Battle for Wesnoth  1.17.14+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 normally cleared in another place.
286  // However, if we're closing the root tag, clear it now
287  if(stack_.empty()) {
288  print_cache();
289  }
290 }
291 
293 {
294  for(auto& m : cache_.top()) {
295  for(auto& list : m.second) {
296  print(list);
297  }
298  }
299 
300  cache_.pop();
301 }
302 
303 void schema_validator::validate(const config& cfg, const std::string& name, int start_line, const std::string& file)
304 {
305  // close previous errors and print them to output.
306  print_cache();
307 
308  // clear cache
309  auto cache_it = cache_.top().find(&cfg);
310  if(cache_it != cache_.top().end()) {
311  cache_it->second.clear();
312  }
313 
314  // Please note that validating unknown tag keys the result will be false
315  // Checking all elements counters.
316  if(have_active_tag() && is_valid()) {
317  const wml_tag& active = active_tag();
318  for(const auto& tag : active.tags(cfg)) {
319  int cnt = counter_.top()[tag.first].cnt;
320 
321  if(tag.second.get_min() > cnt) {
322  queue_message(cfg, MISSING_TAG, file, start_line, tag.second.get_min(), tag.first, "", name);
323  continue;
324  }
325 
326  if(tag.second.get_max() < cnt) {
327  queue_message(cfg, EXTRA_TAG, file, start_line, tag.second.get_max(), tag.first, "", name);
328  }
329  }
330 
331  int total_cnt = counter_.top()[""].cnt;
332  if(active.get_min_children() > total_cnt) {
333  queue_message(cfg, MISSING_TAG, file, start_line, active.get_min_children(), "*", "", active.get_name());
334  } else if(active_tag().get_max_children() < total_cnt) {
335  queue_message(cfg, EXTRA_TAG, file, start_line, active.get_max_children(), "*", "", active.get_name());
336  }
337 
338  // Checking if all mandatory keys are present
339  for(const auto& key : active.keys(cfg)) {
340  if(key.second.is_mandatory()) {
341  if(cfg.get(key.first) == nullptr) {
342  queue_message(cfg, MISSING_KEY, file, start_line, 0, name, key.first);
343  }
344  }
345  }
346  }
347 }
348 
350  const config& cfg, const std::string& name, const config_attribute_value& value, int start_line, const std::string& file)
351 {
352  if(have_active_tag() && !active_tag().get_name().empty() && is_valid()) {
353  // checking existing keys
354  const wml_key* key = active_tag().find_key(name, cfg);
355  if(key) {
356  bool matched = false;
357  for(auto& possible_type : utils::split(key->get_type())) {
358  if(auto type = find_type(possible_type)) {
359  if(type->matches(value, types_)) {
360  matched = true;
361  break;
362  }
363  }
364  }
365  if(!matched) {
366  queue_message(cfg, WRONG_VALUE, file, start_line, 0, active_tag().get_name(), name, value, key->get_type());
367  }
368  } else {
369  queue_message(cfg, EXTRA_KEY, file, start_line, 0, active_tag().get_name(), name);
370  }
371  }
372 }
373 
375 {
376  assert(have_active_tag() && "Tried to get active tag name when there was none");
377  return *stack_.top();
378 }
379 
381 {
382  auto it = types_.find(type);
383  if(it == types_.end()) {
384  return nullptr;
385  }
386  return it->second;
387 }
388 
390 {
391  return !stack_.empty() && stack_.top();
392 }
393 
395  std::stack<const wml_tag*> temp = stack_;
396  std::deque<std::string> path;
397  while(!temp.empty()) {
398  path.push_front(temp.top()->get_name());
399  temp.pop();
400  }
401  if(path.front() == "root") {
402  path.pop_front();
403  }
404  return utils::join(path, "/");
405 }
406 
408 {
409  switch(el.type) {
410  case WRONG_TAG:
412  break;
413  case EXTRA_TAG:
414  extra_tag_error(el.file, el.line, el.tag, el.n, el.value, create_exceptions_);
415  break;
416  case MISSING_TAG:
417  missing_tag_error(el.file, el.line, el.tag, el.n, el.value, create_exceptions_);
418  break;
419  case EXTRA_KEY:
421  break;
422  case WRONG_VALUE:
424  break;
425  case MISSING_KEY:
427  break;
428  }
429 }
430 
432  : schema_validator(filesystem::get_wml_location("schema/schema.cfg"), false)
433  , type_nesting_()
434  , condition_nesting_()
435 {
436  defined_types_.insert("t_string");
437 }
438 
439 
440 void schema_self_validator::open_tag(const std::string& name, const config& parent, int start_line, const std::string& file, bool addition)
441 {
442  schema_validator::open_tag(name, parent, start_line, file, addition);
443  if(name == "type") {
444  type_nesting_++;
445  }
446  if(condition_nesting_ == 0) {
447  if(name == "if" || name == "switch") {
448  condition_nesting_ = 1;
449  } else if(name == "tag") {
450  tag_stack_.emplace();
451  }
452  } else {
454  }
455 }
456 
458 {
459  if(have_active_tag()) {
460  auto tag_name = active_tag().get_name();
461  if(tag_name == "type") {
462  type_nesting_--;
463  } else if(condition_nesting_ == 0 && tag_name == "tag") {
464  tag_stack_.pop();
465  }
466  }
467  if(condition_nesting_ > 0) {
469  }
471 }
472 
474  std::vector<std::string> path = utils::split(ref.value_, '/');
475  std::string suffix = path.back();
476  path.pop_back();
477  while(!path.empty()) {
478  std::string prefix = utils::join(path, "/");
479  auto link = links_.find(prefix);
480  if(link != links_.end()) {
481  std::string new_path = link->second + "/" + suffix;
482  if(defined_tag_paths_.count(new_path) > 0) {
483  return true;
484  }
485  path = utils::split(new_path, '/');
486  suffix = path.back();
487  //suffix = link->second + "/" + suffix;
488  } else {
489  const auto supers = derivations_.equal_range(prefix);
490  if(supers.first != supers.second) {
491  reference super_ref = ref;
492  for(auto cur = supers.first ; cur != supers.second; ++cur) {
493  super_ref.value_ = cur->second + "/" + suffix;
494  if(super_ref.value_.find(ref.value_) == 0) {
495  continue;
496  }
497  if(tag_path_exists(cfg, super_ref)) {
498  return true;
499  }
500  }
501  }
502  std::string new_path = prefix + "/" + suffix;
503  if(defined_tag_paths_.count(new_path) > 0) {
504  return true;
505  }
506  suffix = path.back() + "/" + suffix;
507  }
508  path.pop_back();
509  }
510  return false;
511 }
512 
513 bool schema_self_validator::name_matches(const std::string& pattern, const std::string& name)
514 {
515  for(const std::string& pat : utils::split(pattern)) {
516  if(utils::wildcard_string_match(name, pat)) return true;
517  }
518  return false;
519 }
520 
521 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) {
522  auto split = utils::split(name);
523  for(const std::string& pattern : seen) {
524  for(const std::string& key : split) {
525  if(name_matches(pattern, key)) {
526  queue_message(cfg, type, file, line, 0, tag, pattern, name);
527  continue;
528  }
529  }
530  }
531  seen.push_back(name);
532 }
533 
534 void schema_self_validator::validate(const config& cfg, const std::string& name, int start_line, const std::string& file)
535 {
536  if(type_nesting_ == 1 && name == "type") {
537  defined_types_.insert(cfg["name"]);
538  } else if(name == "tag") {
539  bool first_tag = true, first_key = true;
540  std::vector<std::string> tag_names, key_names;
541  for(auto current : cfg.all_children_range()) {
542  if(current.key == "tag" || current.key == "link") {
543  std::string tag_name = current.cfg["name"];
544  if(current.key == "link") {
545  tag_name.erase(0, tag_name.find_last_of('/') + 1);
546  }
547  if(first_tag) {
548  tag_names.push_back(tag_name);
549  first_tag = false;
550  continue;
551  }
552  check_for_duplicates(tag_name, tag_names, current.cfg, DUPLICATE_TAG, file, start_line, current.key);
553  } else if(current.key == "key") {
554  std::string key_name = current.cfg["name"];
555  if(first_key) {
556  key_names.push_back(key_name);
557  first_key = false;
558  continue;
559  }
560  check_for_duplicates(key_name, key_names, current.cfg, DUPLICATE_KEY, file, start_line, current.key);
561  }
562  }
563  } else if(name == "wml_schema") {
564  using namespace std::placeholders;
565  std::vector<reference> missing_types = referenced_types_, missing_tags = referenced_tag_paths_;
566  // Remove all the known types
567  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());
568  // Remove all the known tags. This is more complicated since links behave similar to a symbolic link.
569  // In other words, the presence of links means there may be more than one way to refer to a given tag.
570  // 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.
571  auto end = std::remove_if(missing_tags.begin(), missing_tags.end(), std::bind(&reference::match, std::placeholders::_1, std::cref(defined_tag_paths_)));
572  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());
573  std::sort(missing_types.begin(), missing_types.end());
574  std::sort(missing_tags.begin(), missing_tags.end());
575  static const config dummy;
576  for(auto& ref : missing_types) {
577  std::string tag_name;
578  if(ref.tag_ == "key") {
579  tag_name = "type";
580  } else {
581  tag_name = "link";
582  }
583  queue_message(dummy, WRONG_TYPE, ref.file_, ref.line_, 0, ref.tag_, tag_name, ref.value_);
584  }
585  for(auto& ref : missing_tags) {
586  std::string tag_name;
587  if(ref.tag_ == "tag") {
588  tag_name = "super";
589  } else if(ref.tag_ == "link") {
590  tag_name = "name";
591  }
592  queue_message(dummy, WRONG_PATH, ref.file_, ref.line_, 0, ref.tag_, tag_name, ref.value_);
593  }
594  }
595  schema_validator::validate(cfg, name, start_line, file);
596 }
597 
598 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)
599 {
600  schema_validator::validate_key(cfg, name, value, start_line, file);
601  if(have_active_tag() && !active_tag().get_name().empty() && is_valid()) {
602  const std::string& tag_name = active_tag().get_name();
603  if(tag_name == "key" && name == "type" ) {
604  for(auto& possible_type : utils::split(cfg["type"])) {
605  referenced_types_.emplace_back(possible_type, file, start_line, tag_name);
606  }
607  } else if((tag_name == "type" || tag_name == "element") && name == "link") {
608  referenced_types_.emplace_back(cfg["link"], file, start_line, tag_name);
609  } else if(tag_name == "link" && name == "name") {
610  referenced_tag_paths_.emplace_back(cfg["name"], file, start_line, tag_name);
611  std::string link_name = utils::split(cfg["name"].str(), '/').back();
612  links_.emplace(current_path() + "/" + link_name, cfg["name"]);
613  } else if(tag_name == "tag" && name == "super") {
614  for(auto super : utils::split(cfg["super"])) {
615  referenced_tag_paths_.emplace_back(super, file, start_line, tag_name);
616  if(condition_nesting_ > 0) {
617  continue;
618  }
619  if(current_path() == super) {
620  queue_message(cfg, SUPER_LOOP, file, start_line, cfg["super"].str().find(super), tag_name, "super", super);
621  continue;
622  }
623  derivations_.emplace(current_path(), super);
624  }
625  } else if(condition_nesting_ == 0 && tag_name == "tag" && name == "name") {
626  tag_stack_.top() = value.str();
628  }
629  }
630 }
631 
633 {
634  std::stack<std::string> temp = tag_stack_;
635  std::deque<std::string> path;
636  while(!temp.empty()) {
637  path.push_front(temp.top());
638  temp.pop();
639  }
640  if(path.front() == "root") {
641  path.pop_front();
642  }
643  return utils::join(path, "/");
644 }
645 
647 {
648  return std::tie(file_, line_) < std::tie(other.file_, other.line_);
649 }
650 
651 bool schema_self_validator::reference::match(const std::set<std::string>& with)
652 {
653  return with.count(value_) > 0;
654 }
655 
657 {
658  // The problem is that the schema being validated is that of the schema!!!
659  return root.find_tag(value_, root, cfg) != nullptr;
660 }
661 
663 {
665  switch(el.type) {
666  case WRONG_TYPE:
668  break;
669  case WRONG_PATH:
671  break;
672  case DUPLICATE_TAG:
674  break;
675  case DUPLICATE_KEY:
677  break;
678  case SUPER_LOOP:
679  inheritance_loop_error(el.file, el.line, el.tag, el.key, el.value, el.n, create_exceptions_);
680  break;
681  }
682 }
683 
684 } // 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)