The Battle for Wesnoth  1.13.11+dev
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
schema_validator.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2011 - 2018 by Sytyi Nick <nsytyi@gmail.com>
3  Part of the Battle for Wesnoth Project http://www.wesnoth.org/
4 
5  This program is free software; you can redistribute it and/or modify
6  it under the terms of the GNU General Public License as published by
7  the Free Software Foundation; either version 2 of the License, or
8  (at your option) any later version.
9  This program is distributed in the hope that it will be useful,
10  but WITHOUT ANY WARRANTY.
11 
12  See the COPYING file for more details.
13 */
14 
16 
17 
18 #include "filesystem.hpp"
19 #include "gettext.hpp"
20 #include "log.hpp"
22 #include "wml_exception.hpp"
23 
25 
26 static lg::log_domain log_validation("validation");
27 
28 #define ERR_VL LOG_STREAM(err, log_validation)
29 #define WRN_VL LOG_STREAM(warn, log_validation)
30 #define LOG_VL LOG_STREAM(info, log_validation)
31 
32 static std::string at(const std::string & file, int line){
33  std::ostringstream ss;
34  ss << line << " " << file;
35  return "at " + ::lineno_string(ss.str());
36 }
37 
38 static void print_output(const std::string & message,bool flag_exception = false ){
39 #ifndef VALIDATION_ERRORS_LOG
40  if(flag_exception){
41  throw wml_exception("Validation error occurred",message);
42  }else{
43  ERR_VL << message;
44 }
45 #else
46 // dirty hack to avoid "unused" error in case of compiling with definition on
47  flag_exception = true;
48  if (flag_exception){ ERR_VL << message;}
49 #endif
50 }
51 
52 static void extra_tag_error(const std::string & file, int line,
53  const std::string & name,int n,
54  const std::string & parent, bool flag_exception){
55  std::ostringstream ss;
56  ss << "Extra tag [" << name << "]; there may only be "
57  << n << " [" << name << "] in [" << parent << "]\n"
58  << at(file, line) << "\n";
59  print_output (ss.str (),flag_exception);
60 }
61 
62 static void wrong_tag_error(const std::string & file, int line,
63  const std::string & name,const std::string & parent,
64  bool flag_exception){
65  std::ostringstream ss;
66  ss << "Tag [" << name << "] may not be used in ["
67  << parent << "]\n"
68  << at(file, line) << "\n";
69  print_output (ss.str (),flag_exception);
70 }
71 
72 static void missing_tag_error(const std::string & file, int line,
73  const std::string & name,int n,
74  const std::string & parent, bool flag_exception){
75  std::ostringstream ss;
76  ss << "Missing tag [" << name << "]; there must be "
77  << n << " [" << name << "]s in [" << parent << "]\n"
78  << at(file, line) << "\n";
79  print_output (ss.str (),flag_exception);
80 }
81 
82 static void extra_key_error(const std::string & file, int line,
83  const std::string & tag,const std::string & key,
84  bool flag_exception){
85  std::ostringstream ss;
86  ss << "Invalid key '" << key << "=' in tag [" << tag
87  << "]\n"
88  << at(file, line) << "\n";
89  print_output (ss.str (),flag_exception);
90 }
91 
92 static void missing_key_error(const std::string & file, int line,
93  const std::string & tag,const std::string & key,
94  bool flag_exception){
95  std::ostringstream ss;
96  ss << "Missing key '" << key << "=' in tag [" << tag
97  << "]\n"
98  << at(file, line) << "\n";
99  print_output (ss.str (),flag_exception);
100 }
101 
102 static void wrong_value_error(const std::string & file, int line,
103  const std::string & tag,const std::string & key,
104  const std::string & value,bool flag_exception){
105  std::ostringstream ss;
106  ss << "Invalid value '" << value << "' in key '" << key
107  << "=' in tag [" << tag << "]\n"
108  << at(file, line) << "\n";
109  print_output (ss.str (),flag_exception);
110 }
111 
112 
113 
115 
117  : config_read_(false)
118  , create_exceptions_(strict_validation_enabled)
119  , root_()
120  , stack_()
121  , counter_()
122  , cache_()
123  , types_()
124 {
125  if ( !read_config_file(config_file_name) ) {
126  ERR_VL << "Schema file "<< config_file_name << " was not read." << std::endl;
127  throw abstract_validator::error("Schema file "+ config_file_name
128  + " was not read.\n");
129  }else{
130  stack_.push(&root_);
131  counter_.emplace();
132  cache_.emplace();
134  LOG_VL << "Schema file "<< config_file_name << " was read.\n"
135  << "Validator initialized\n";
136  }
137 }
138 
140  config cfg;
141  try {
142  preproc_map preproc(
143  game_config::config_cache::instance().get_preproc_map());
144  filesystem::scoped_istream stream = preprocess_file(filename, &preproc);
145  read(cfg, *stream);
146  } catch(config::error& e) {
147  ERR_VL << "Failed to read file "<< filename << ":\n" << e.what() << "\n";
148  return false;
149  }
150  for(const config &g : cfg.child_range("wml_schema")) {
151  for(const config &schema : g.child_range("tag")) {
152  if (schema["name"].str() == "root"){
153  //@NOTE Don't know, maybe merging of roots needed.
154  root_ = class_tag (schema);
155  }
156  }
157  for(const config &type : g.child_range("type")) {
158  try{
159  types_[type["name"].str()] = boost::regex( type["value"].str());
160  }
161  catch (std::exception&){
162  // Need to check all type values in schema-generator
163  }
164  }
165  }
166 
167  config_read_ = true;
168  return true;
169 }
170 /*
171  * Please, @Note that there is some magic in pushing and poping to/from stacks.
172  * assume they all are on their place due to parser algorithm
173  * and validation logic
174  */
176  int start_line,
177  const std::string &file,
178  bool addittion){
179  if (! stack_.empty()){
180  const class_tag * tag = nullptr;
181  if (stack_.top()){
182  tag = stack_.top()->find_tag(name,root_);
183  if (! tag){
184  wrong_tag_error(file,start_line,name,stack_.top()->get_name(),
186  }else{
187  if (! addittion){
188  counter & cnt = counter_.top()[name];
189  ++ cnt.cnt;
190  }
191  }
192  }
193  stack_.push(tag);
194  }else{
195  stack_.push(nullptr);
196  }
197  counter_.emplace();
198  cache_.emplace();
199 }
200 
202  stack_.pop();
203  counter_.pop();
204  //cache_ is cleared in another place.
205 }
206 
208  int start_line,
209  const std::string &file){
210  //close previous errors and print them to output.
211  message_map::iterator cache_it = cache_.top().begin();
212  for (;cache_it != cache_.top().end();++cache_it){
213  for (message_list::iterator i = cache_it->second.begin();
214  i != cache_it->second.end(); ++i){
215  print(*i);
216  }
217  }
218  cache_.pop();
219  // clear cache
220  cache_it = cache_.top().find(&cfg);
221  if (cache_it != cache_.top().end()){
222  cache_it->second.clear();
223  }
224  // Please note that validating unknown tag keys the result will be false
225  // Checking all elements counters.
226  if (!stack_.empty() && stack_.top() && config_read_){
228  for (class_tag::const_tag_iterator tag = p.first;
229  tag != p.second ; ++tag){
230  int cnt = counter_.top()[tag->first].cnt;
231  if (tag->second.get_min() > cnt){
232  cache_.top()[&cfg].emplace_back(
233  MISSING_TAG,file,start_line,tag->second.get_min(),tag->first,"",name);
234  continue;
235  }
236  if (tag->second.get_max() < cnt){
237  cache_.top()[&cfg].emplace_back(
238  EXTRA_TAG,file,start_line,tag->second.get_max(),tag->first,"",name);
239  }
240  }
241  // Checking if all mandatory keys are present
242  class_tag::all_const_key_iterators k = stack_.top()->keys();
243  for (class_tag::const_key_iterator key = k.first;
244  key != k.second ; ++key){
245  if (key->second.is_mandatory()){
246  if (cfg.get(key->first) == nullptr){
247  cache_.top()[&cfg].emplace_back(
248  MISSING_KEY,file,start_line,0,name,key->first );
249  }
250  }
251  }
252  }
253 }
254 
255 
257  const std::string & name,
258  const std::string & value,
259  int start_line,
260  const std::string &file){
261  if (!stack_.empty() && stack_.top() && config_read_){
262  // checking existing keys
263  const class_key * key =stack_.top()->find_key(name);
264  if (key){
266  types_.find(key->get_type());
267  if (itt != types_.end()){
268  boost::smatch sub;
269  bool res = boost::regex_match(value,sub,itt->second);
270  if (!res ) {
271  cache_.top()[&cfg].emplace_back(
272  WRONG_VALUE,file,start_line,0,stack_.top()->get_name(),name,value);
273  }
274  }
275  }
276  else{
277  cache_.top()[&cfg].emplace_back(
278  EXTRA_KEY,file,start_line,0, stack_.top()->get_name(),name);
279  }
280 
281  }
282 }
283 
285  switch (el.type){
286  case WRONG_TAG:
288  break;
289  case EXTRA_TAG:
291  break;
292  case MISSING_TAG:
293  missing_tag_error(el.file,el.line,el.tag,el.n,el.value,
295  break;
296  case EXTRA_KEY:
298  break;
299  case WRONG_VALUE:
300  wrong_value_error(el.file,el.line,el.tag,el.key,el.value,
302  break;
303  case MISSING_KEY:
305  }
306 }
307 }//namespace schema_validation{
std::string lineno_string(const std::string &lineno)
bool create_exceptions_
Controls the way to print errors.
static config_cache & instance()
Get reference to the singleton object.
const char * what() const NOEXCEPT
Definition: exceptions.hpp:37
std::vector< char_t > string
static void wrong_value_error(const std::string &file, int line, const std::string &tag, const std::string &key, const std::string &value, bool flag_exception)
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)
Add a special kind of assert to validate whether the input from WML doesn't contain any problems that...
static l_noret error(LoadState *S, const char *why)
Definition: lundump.cpp:39
virtual void close_tag()
As far as parser is built on stack, some realizations can store stack too.
child_itors child_range(config_key_type key)
Definition: config.cpp:360
virtual void validate(const config &cfg, const std::string &name, int start_line, const std::string &file)
Validates config.
std::map< std::string, boost::regex > types_
Type validators.
key_map::const_iterator const_key_iterator
Definition: tag.hpp:129
static void missing_tag_error(const std::string &file, int line, const std::string &name, int n, const std::string &parent, bool flag_exception)
std::pair< const_key_iterator, const_key_iterator > all_const_key_iterators
Definition: tag.hpp:131
class_key is used to save the information about one key.
Definition: tag.hpp:36
bool config_read_
Shows, if validator is initialized with schema file;.
class_tag root_
Root of schema information.
One of the realizations of serialization/validator.hpp abstract validator.
void expand_all(class_tag &root)
Calls the expansion on each child.
Definition: tag.cpp:152
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)
void read(config &cfg, std::istream &in, abstract_validator *validator)
Definition: parser.cpp:612
schema_validator(const std::string &filename)
Initializes validator from file.
bool strict_validation_enabled
Definition: validator.cpp:19
std::unique_ptr< std::istream > scoped_istream
Definition: filesystem.hpp:37
static void print_output(const std::string &message, bool flag_exception=false)
virtual void validate_key(const config &cfg, const std::string &name, const std::string &value, int start_line, const std::string &file)
Checks if key is allowed and if its value is valid What exactly is validated depends on validator rea...
virtual void open_tag(const std::string &name, int start_line=0, const std::string &file="", bool addittion=false)
Is called when parser opens tag.
#define LOG_VL
Helper class, don't construct this directly.
mock_party p
bool read_config_file(const std::string &filename)
Reads config from input.
std::stack< const class_tag * > stack_
double g
Definition: astarsearch.cpp:64
size_t i
Definition: function.cpp:933
Declarations for File-IO.
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:689
Standard logging facilities (interface).
static const char * name(const std::vector< SDL_Joystick * > &joysticks, const size_t index)
Definition: joystick.cpp:48
std::pair< const_tag_iterator, const_tag_iterator > all_const_tag_iterators
Definition: tag.hpp:138
std::map< std::string, struct preproc_define > preproc_map
#define e
#define ERR_VL
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:93
filesystem::scoped_istream preprocess_file(const std::string &fname, preproc_map *defines)
static lg::log_domain log_validation("validation")
static map_location::DIRECTION n
std::string::const_iterator iterator
Definition: tokenizer.hpp:24
Stores information about tag.
Definition: tag.hpp:115
tag_map::const_iterator const_tag_iterator
Definition: tag.hpp:136
const std::string & get_type() const
Definition: tag.hpp:54
static void extra_tag_error(const std::string &file, int line, const std::string &name, int n, const std::string &parent, bool flag_exception)