The Battle for Wesnoth  1.13.4+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 - 2016 by Sytyi Nick <[email protected]>
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 twml_exception("Validation error occured",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_.push(cnt_map());
132  cache_.push(message_map());
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_.push(cnt_map());
198  cache_.push(message_map());
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].push_back(
233  message_info(MISSING_TAG,file,start_line,
234  tag->second.get_min(),tag->first,"",
235  name));
236  continue;
237  }
238  if (tag->second.get_max() < cnt){
239  cache_.top()[&cfg].push_back(
240  message_info(EXTRA_TAG,file,start_line,
241  tag->second.get_max(),tag->first,"",
242  name));
243  }
244  }
245  // Checking if all mandatory keys are present
246  class_tag::all_const_key_iterators k = stack_.top()->keys();
247  for (class_tag::const_key_iterator key = k.first;
248  key != k.second ; ++key){
249  if (key->second.is_mandatory()){
250  if (cfg.get(key->first) == nullptr){
251  cache_.top()[&cfg].push_back(
252  message_info(MISSING_KEY,file,start_line,0,
253  name,key->first ));
254  }
255  }
256  }
257  }
258 }
259 
260 
262  const std::string & name,
263  const std::string & value,
264  int start_line,
265  const std::string &file){
266  if (!stack_.empty() && stack_.top() && config_read_){
267  // checking existing keys
268  const class_key * key =stack_.top()->find_key(name);
269  if (key){
271  types_.find(key->get_type());
272  if (itt != types_.end()){
273  boost::smatch sub;
274  bool res = boost::regex_match(value,sub,itt->second);
275  if (!res ) {
276  cache_.top()[&cfg].push_back(
277  message_info(WRONG_VALUE,file,start_line,0,
278  stack_.top()->get_name(),
279  name,value));
280  }
281  }
282  }
283  else{
284  cache_.top()[&cfg].push_back(
285  message_info(EXTRA_KEY,file,start_line,0,
286  stack_.top()->get_name(),name));
287  }
288 
289  }
290 }
291 
293  switch (el.type){
294  case WRONG_TAG:
296  break;
297  case EXTRA_TAG:
299  break;
300  case MISSING_TAG:
301  missing_tag_error(el.file,el.line,el.tag,el.n,el.value,
303  break;
304  case EXTRA_KEY:
306  break;
307  case WRONG_VALUE:
308  wrong_value_error(el.file,el.line,el.tag,el.key,el.value,
310  break;
311  case MISSING_KEY:
313  }
314 }
315 }//namespace schema_validation{
std::string lineno_string(const std::string &lineno)
child_itors child_range(const std::string &key)
Definition: config.cpp:613
bool create_exceptions_
Controls the way to print errors.
static config_cache & instance()
Get reference to the singleton object.
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)
const char * what() const
Definition: exceptions.hpp:35
Add a special kind of assert to validate whether the input from WML doesn't contain any problems that...
GLuint GLuint GLsizei GLenum type
Definition: glew.h:1221
static l_noret error(LoadState *S, const char *why)
Definition: lundump.cpp:29
virtual void close_tag()
As far as parser is built on stack, some realizations can store stack too.
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:130
std::map< const config *, message_list > message_map
GLboolean GLboolean g
Definition: glew.h:7319
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:132
class_key is used to save the information about one key.
Definition: tag.hpp:37
bool config_read_
Shows, if validator is intialized with schema file;.
class_tag root_
Root of schema information.
std::map< std::string, preproc_define > preproc_map
GLuint GLuint stream
Definition: glew.h:5239
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)
static std::string sub(const std::string &s)
Private function to surround an argument with brackets.
schema_validator(const std::string &filename)
Initializes validator from file.
static text_list cache_
Definition: font.cpp:772
bool strict_validation_enabled
Definition: validator.cpp:19
GLsizei const GLfloat * value
Definition: glew.h:1817
static void print_output(const std::string &message, bool flag_exception=false)
GLfloat GLfloat p
Definition: glew.h:12766
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
std::map< std::string, counter > cnt_map
Counters are mapped by tag name.
GLuint res
Definition: glew.h:9258
bool read_config_file(const std::string &filename)
Reads config from input.
std::stack< const class_tag * > stack_
size_t i
Definition: function.cpp:1057
Declarations for File-IO.
void read(config &cfg, std::istream &in, abstract_validator *validator)
Definition: parser.cpp:400
GLuint const GLchar * name
Definition: glew.h:1782
const attribute_value * get(const std::string &key) const
Returns a pointer to the attribute with the given key or nullptr if it does not exist.
Definition: config.cpp:935
std::istream * preprocess_file(std::string const &fname, preproc_map *defines)
GLclampd n
Definition: glew.h:5903
Standard logging facilities (interface).
GLsizei GLenum GLuint GLuint GLsizei char * message
Definition: glew.h:2499
std::pair< const_tag_iterator, const_tag_iterator > all_const_tag_iterators
Definition: tag.hpp:139
#define e
#define ERR_VL
std::string::const_iterator iterator
Definition: tokenizer.hpp:21
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:83
Helper class, don't construct this directly.
static lg::log_domain log_validation("validation")
GLsizei const GLcharARB ** string
Definition: glew.h:4503
Stores information about tag.
Definition: tag.hpp:116
tag_map::const_iterator const_tag_iterator
Definition: tag.hpp:137
const std::string & get_type() const
Definition: tag.hpp:55
static void extra_tag_error(const std::string &file, int line, const std::string &name, int n, const std::string &parent, bool flag_exception)