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