The Battle for Wesnoth  1.17.0-dev
schema_validator.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2011 - 2021
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 wrong_type_error(const std::string & file, int line,
162  const std::string & tag,
163  const std::string & key,
164  const std::string & type,
165  bool flag_exception)
166 {
167  std::ostringstream ss;
168  ss << "Invalid type '" << type << "' in key '" << key << "=' in tag [" << tag << "]\n" << at(file, line) << "\n";
169  print_output(ss.str(), flag_exception);
170 }
171 
173 {
174 }
175 
176 schema_validator::schema_validator(const std::string& config_file_name, bool validate_schema)
177  : abstract_validator(config_file_name)
179  , config_read_(false)
180  , validate_schema_(validate_schema)
181 {
182  if(!read_config_file(config_file_name)) {
183  ERR_VL << "Schema file " << config_file_name << " was not read." << std::endl;
184  throw abstract_validator::error("Schema file " + config_file_name + " was not read.\n");
185  } else {
186  stack_.push(&root_);
187  counter_.emplace();
188  cache_.emplace();
190  LOG_VL << "Schema file " << config_file_name << " was read.\n"
191  << "Validator initialized\n";
192  }
193 }
194 
195 bool schema_validator::read_config_file(const std::string& filename)
196 {
197  config cfg;
198  try {
199  std::unique_ptr<abstract_validator> validator;
200  if(validate_schema_) {
201  validator.reset(new schema_self_validator());
202  }
203  preproc_map preproc(game_config::config_cache::instance().get_preproc_map());
204  filesystem::scoped_istream stream = preprocess_file(filename, &preproc);
205  read(cfg, *stream, validator.get());
206  } catch(const config::error& e) {
207  ERR_VL << "Failed to read file " << filename << ":\n" << e.what() << "\n";
208  return false;
209  }
210 
211  for(const config& g : cfg.child_range("wml_schema")) {
212  for(const config& schema : g.child_range("tag")) {
213  if(schema["name"].str() == "root") {
214  //@NOTE Don't know, maybe merging of roots needed.
215  root_ = wml_tag(schema);
216  }
217  }
218  types_["t_string"] = std::make_shared<wml_type_tstring>();
219  for(const config& type : g.child_range("type")) {
220  try {
221  types_[type["name"].str()] = wml_type::from_config(type);
222  } catch(const std::exception&) {
223  // Need to check all type values in schema-generator
224  }
225  }
226  }
227 
228  config_read_ = true;
229  return true;
230 }
231 
232 /*
233  * Please, @Note that there is some magic in pushing and poping to/from stacks.
234  * assume they all are on their place due to parser algorithm
235  * and validation logic
236  */
237 void schema_validator::open_tag(const std::string& name, const config& parent, int start_line, const std::string& file, bool addition)
238 {
239  if(name.empty()) {
240  // Opened the root tag; nothing special to do here
241  } else if(!stack_.empty()) {
242  const wml_tag* tag = nullptr;
243 
244  if(stack_.top()) {
245  tag = active_tag().find_tag(name, root_, parent);
246 
247  if(!tag) {
248  wrong_tag_error(file, start_line, name, stack_.top()->get_name(), create_exceptions_);
249  } else {
250  if(!addition) {
251  counter& cnt = counter_.top()[name];
252  ++cnt.cnt;
253  counter& total_cnt = counter_.top()[""];
254  ++total_cnt.cnt;
255  }
256  }
257  }
258 
259  stack_.push(tag);
260  } else {
261  stack_.push(nullptr);
262  }
263 
264  counter_.emplace();
265  cache_.emplace();
266 }
267 
269 {
270  stack_.pop();
271  counter_.pop();
272  // cache_ is cleared in another place.
273 }
274 
275 void schema_validator::validate(const config& cfg, const std::string& name, int start_line, const std::string& file)
276 {
277  // close previous errors and print them to output.
278  for(auto& m : cache_.top()) {
279  for(auto& list : m.second) {
280  print(list);
281  }
282  }
283 
284  cache_.pop();
285 
286  // clear cache
287  auto cache_it = cache_.top().find(&cfg);
288  if(cache_it != cache_.top().end()) {
289  cache_it->second.clear();
290  }
291 
292  // Please note that validating unknown tag keys the result will be false
293  // Checking all elements counters.
294  if(have_active_tag() && is_valid()) {
295  const wml_tag& active = active_tag();
296  for(const auto& tag : active.tags(cfg)) {
297  int cnt = counter_.top()[tag.first].cnt;
298 
299  if(tag.second.get_min() > cnt) {
300  queue_message(cfg, MISSING_TAG, file, start_line, tag.second.get_min(), tag.first, "", name);
301  continue;
302  }
303 
304  if(tag.second.get_max() < cnt) {
305  queue_message(cfg, EXTRA_TAG, file, start_line, tag.second.get_max(), tag.first, "", name);
306  }
307  }
308 
309  int total_cnt = counter_.top()[""].cnt;
310  if(active.get_min_children() > total_cnt) {
311  queue_message(cfg, MISSING_TAG, file, start_line, active.get_min_children(), "*", "", active.get_name());
312  } else if(active_tag().get_max_children() < total_cnt) {
313  queue_message(cfg, EXTRA_TAG, file, start_line, active.get_max_children(), "*", "", active.get_name());
314  }
315 
316  // Checking if all mandatory keys are present
317  for(const auto& key : active.keys(cfg)) {
318  if(key.second.is_mandatory()) {
319  if(cfg.get(key.first) == nullptr) {
320  queue_message(cfg, MISSING_KEY, file, start_line, 0, name, key.first);
321  }
322  }
323  }
324  }
325 }
326 
328  const config& cfg, const std::string& name, const config_attribute_value& value, int start_line, const std::string& file)
329 {
330  if(have_active_tag() && !active_tag().get_name().empty() && is_valid()) {
331  // checking existing keys
332  const wml_key* key = active_tag().find_key(name, cfg);
333  if(key) {
334  bool matched = false;
335  for(auto& possible_type : utils::split(key->get_type())) {
336  if(auto type = find_type(possible_type)) {
337  if(type->matches(value, types_)) {
338  matched = true;
339  break;
340  }
341  }
342  }
343  if(!matched) {
344  queue_message(cfg, WRONG_VALUE, file, start_line, 0, active_tag().get_name(), name, value, key->get_type());
345  }
346  } else {
347  queue_message(cfg, EXTRA_KEY, file, start_line, 0, active_tag().get_name(), name);
348  }
349  }
350 }
351 
353 {
354  assert(have_active_tag() && "Tried to get active tag name when there was none");
355  return *stack_.top();
356 }
357 
359 {
360  auto it = types_.find(type);
361  if(it == types_.end()) {
362  return nullptr;
363  }
364  return it->second;
365 }
366 
368 {
369  return !stack_.empty() && stack_.top();
370 }
371 
373  std::stack<const wml_tag*> temp = stack_;
374  std::deque<std::string> path;
375  while(!temp.empty()) {
376  path.push_front(temp.top()->get_name());
377  temp.pop();
378  }
379  if(path.front() == "root") {
380  path.pop_front();
381  }
382  return utils::join(path, "/");
383 }
384 
386 {
387  switch(el.type) {
388  case WRONG_TAG:
390  break;
391  case EXTRA_TAG:
392  extra_tag_error(el.file, el.line, el.tag, el.n, el.value, create_exceptions_);
393  break;
394  case MISSING_TAG:
395  missing_tag_error(el.file, el.line, el.tag, el.n, el.value, create_exceptions_);
396  break;
397  case EXTRA_KEY:
399  break;
400  case WRONG_VALUE:
402  break;
403  case MISSING_KEY:
405  break;
406  }
407 }
408 
410  : schema_validator(filesystem::get_wml_location("schema/schema.cfg"), false)
411  , type_nesting_()
412  , condition_nesting_()
413 {
414  defined_types_.insert("t_string");
415 }
416 
417 
418 void schema_self_validator::open_tag(const std::string& name, const config& parent, int start_line, const std::string& file, bool addition)
419 {
420  schema_validator::open_tag(name, parent, start_line, file, addition);
421  if(name == "type") {
422  type_nesting_++;
423  }
424  if(condition_nesting_ == 0) {
425  if(name == "if" || name == "switch") {
426  condition_nesting_ = 1;
427  } else if(name == "tag") {
428  tag_stack_.emplace();
429  }
430  } else {
432  }
433 }
434 
436 {
437  if(have_active_tag()) {
438  auto tag_name = active_tag().get_name();
439  if(tag_name == "type") {
440  type_nesting_--;
441  } else if(condition_nesting_ == 0 && tag_name == "tag") {
442  tag_stack_.pop();
443  }
444  }
445  if(condition_nesting_ > 0) {
447  }
449 }
450 
452  std::vector<std::string> path = utils::split(ref.value_, '/');
453  std::string suffix = path.back();
454  path.pop_back();
455  while(!path.empty()) {
456  std::string prefix = utils::join(path, "/");
457  auto link = links_.find(prefix);
458  if(link != links_.end()) {
459  std::string new_path = link->second + "/" + suffix;
460  if(defined_tag_paths_.count(new_path) > 0) {
461  return true;
462  }
463  path = utils::split(new_path, '/');
464  suffix = path.back();
465  //suffix = link->second + "/" + suffix;
466  } else {
467  auto supers = derivations_.equal_range(prefix);
468  if(supers.first != supers.second) {
469  reference super_ref = ref;
470  for( ; supers.first != supers.second; ++supers.first) {
471  super_ref.value_ = supers.first->second + "/" + suffix;
472  if(tag_path_exists(cfg, super_ref)) {
473  return true;
474  }
475  }
476  }
477  std::string new_path = prefix + "/" + suffix;
478  if(defined_tag_paths_.count(new_path) > 0) {
479  return true;
480  }
481  suffix = path.back() + "/" + suffix;
482  }
483  path.pop_back();
484  }
485  return false;
486 }
487 
488 bool schema_self_validator::name_matches(const std::string& pattern, const std::string& name)
489 {
490  for(const std::string& pat : utils::split(pattern)) {
491  if(utils::wildcard_string_match(name, pat)) return true;
492  }
493  return false;
494 }
495 
496 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) {
497  auto split = utils::split(name);
498  for(const std::string& pattern : seen) {
499  for(const std::string& key : split) {
500  if(name_matches(pattern, key)) {
501  queue_message(cfg, type, file, line, 0, tag, pattern, name);
502  continue;
503  }
504  }
505  }
506  seen.push_back(name);
507 }
508 
509 void schema_self_validator::validate(const config& cfg, const std::string& name, int start_line, const std::string& file)
510 {
511  if(type_nesting_ == 1 && name == "type") {
512  defined_types_.insert(cfg["name"]);
513  } else if(name == "tag") {
514  bool first_tag = true, first_key = true;
515  std::vector<std::string> tag_names, key_names;
516  for(auto current : cfg.all_children_range()) {
517  if(current.key == "tag" || current.key == "link") {
518  std::string tag_name = current.cfg["name"];
519  if(current.key == "link") {
520  tag_name.erase(0, tag_name.find_last_of('/') + 1);
521  }
522  if(first_tag) {
523  tag_names.push_back(tag_name);
524  first_tag = false;
525  continue;
526  }
527  check_for_duplicates(tag_name, tag_names, current.cfg, DUPLICATE_TAG, file, start_line, current.key);
528  } else if(current.key == "key") {
529  std::string key_name = current.cfg["name"];
530  if(first_key) {
531  key_names.push_back(key_name);
532  first_key = false;
533  continue;
534  }
535  check_for_duplicates(key_name, key_names, current.cfg, DUPLICATE_KEY, file, start_line, current.key);
536  }
537  }
538  } else if(name == "wml_schema") {
539  using namespace std::placeholders;
540  std::vector<reference> missing_types = referenced_types_, missing_tags = referenced_tag_paths_;
541  // Remove all the known types
542  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());
543  // Remove all the known tags. This is more complicated since links behave similar to a symbolic link.
544  // In other words, the presence of links means there may be more than one way to refer to a given tag.
545  // 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.
546  auto end = std::remove_if(missing_tags.begin(), missing_tags.end(), std::bind(&reference::match, std::placeholders::_1, std::cref(defined_tag_paths_)));
547  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());
548  std::sort(missing_types.begin(), missing_types.end());
549  std::sort(missing_tags.begin(), missing_tags.end());
550  static const config dummy;
551  for(auto& ref : missing_types) {
552  std::string tag_name;
553  if(ref.tag_ == "key") {
554  tag_name = "type";
555  } else {
556  tag_name = "link";
557  }
558  queue_message(dummy, WRONG_TYPE, ref.file_, ref.line_, 0, ref.tag_, tag_name, ref.value_);
559  }
560  for(auto& ref : missing_tags) {
561  std::string tag_name;
562  if(ref.tag_ == "tag") {
563  tag_name = "super";
564  } else if(ref.tag_ == "link") {
565  tag_name = "name";
566  }
567  queue_message(dummy, WRONG_PATH, ref.file_, ref.line_, 0, ref.tag_, tag_name, ref.value_);
568  }
569  }
570  schema_validator::validate(cfg, name, start_line, file);
571 }
572 
573 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)
574 {
575  schema_validator::validate_key(cfg, name, value, start_line, file);
576  if(have_active_tag() && !active_tag().get_name().empty() && is_valid()) {
577  const std::string& tag_name = active_tag().get_name();
578  if(tag_name == "key" && name == "type" ) {
579  for(auto& possible_type : utils::split(cfg["type"])) {
580  referenced_types_.emplace_back(possible_type, file, start_line, tag_name);
581  }
582  } else if((tag_name == "type" || tag_name == "element") && name == "link") {
583  referenced_types_.emplace_back(cfg["link"], file, start_line, tag_name);
584  } else if(tag_name == "link" && name == "name") {
585  referenced_tag_paths_.emplace_back(cfg["name"], file, start_line, tag_name);
586  std::string link_name = utils::split(cfg["name"].str(), '/').back();
587  links_.emplace(current_path() + "/" + link_name, cfg["name"]);
588  } else if(tag_name == "tag" && name == "super") {
589  for(auto super : utils::split(cfg["super"])) {
590  referenced_tag_paths_.emplace_back(super, file, start_line, tag_name);
591  derivations_.emplace(current_path(), super);
592  }
593  } else if(condition_nesting_ == 0 && tag_name == "tag" && name == "name") {
594  tag_stack_.top() = value.str();
596  }
597  }
598 }
599 
601 {
602  std::stack<std::string> temp = tag_stack_;
603  std::deque<std::string> path;
604  while(!temp.empty()) {
605  path.push_front(temp.top());
606  temp.pop();
607  }
608  if(path.front() == "root") {
609  path.pop_front();
610  }
611  return utils::join(path, "/");
612 }
613 
615 {
616  return std::tie(file_, line_) < std::tie(other.file_, other.line_);
617 }
618 
619 bool schema_self_validator::reference::match(const std::set<std::string>& with)
620 {
621  return with.count(value_) > 0;
622 }
623 
625 {
626  // The problem is that the schema being validated is that of the schema!!!
627  return root.find_tag(value_, root, cfg) != nullptr;
628 }
629 
631 {
633  switch(el.type) {
634  case WRONG_TYPE:
636  break;
637  case WRONG_PATH:
639  break;
640  case DUPLICATE_TAG:
642  break;
643  case DUPLICATE_KEY:
645  break;
646  }
647 }
648 
649 } // 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 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)
int dummy
Definition: lstrlib.cpp:1347
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...
static l_noret error(LoadState *S, const char *why)
Definition: lundump.cpp:40
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.
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...
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.
static int sort(lua_State *L)
Definition: ltablib.cpp:397
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:61
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)