The Battle for Wesnoth  1.19.0+dev
schema_validator.hpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2011 - 2024
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 
16 #pragma once
17 
18 #include "config_cache.hpp"
19 #include "serialization/parser.hpp"
23 
24 #include <boost/graph/adjacency_list.hpp>
25 #include <optional>
26 #include <stack>
27 #include <string>
28 
29 
30 /** @file
31  * One of the realizations of serialization/validator.hpp abstract validator.
32  */
33 namespace schema_validation
34 {
35 /**
36  * Realization of serialization/validator.hpp abstract validator.
37  * Based on stack. Uses some stacks to store different info.
38  */
40 {
41 public:
42  virtual ~schema_validator();
43 
44  /**
45  * Initializes validator from file.
46  * Throws abstract_validator::error if any error.
47  */
48  schema_validator(const std::string& filename, bool validate_schema = false);
49 
50  void set_create_exceptions(bool value)
51  {
52  create_exceptions_ = value;
53  }
54 
55  const std::vector<std::string>& get_errors() const
56  {
57  return errors_;
58  }
59 
60  virtual void open_tag(const std::string& name, const config& parent, int start_line = 0, const std::string& file = "", bool addition = false) override;
61  virtual void close_tag() override;
62  virtual void validate(const config& cfg, const std::string& name, int start_line, const std::string& file) override;
63  virtual void validate_key(const config& cfg, const std::string& name, const config_attribute_value& value, int start_line, const std::string& file) override;
64 
65 private:
66  // types section
67  // Just some magic to ensure zero initialization.
68  struct counter
69  {
70  int cnt;
72  : cnt(0)
73  {
74  }
75  };
76 
77  /** Counters are mapped by tag name. */
78  typedef std::map<std::string, counter> cnt_map;
79 
80  /** And counter maps are organize in stack. */
81  typedef std::stack<cnt_map> cnt_stack;
82 
83 protected:
84  using message_type = int;
86 
87  /**
88  * Messages are cached.
89  * The reason is next: in file where [tag]...[/tag][+tag]...[/tag]
90  * config object will be validated each [/tag]. So message can be as wrong
91  * (when [+tag] section contains missed elements) as duplicated.
92  *
93  * Messages are mapped by config*. That ensures uniqueness.
94  * Also message-maps are organized in stack to avoid memory leaks.
95  */
96  struct message_info
97  {
99  std::string file;
100  int line;
101  int n;
102  std::string tag;
103  std::string key;
104  std::string value;
105  std::string expected;
106 
107  message_info(message_type t, const std::string& file, int line = 0, int n = 0, const std::string& tag = "", const std::string& key = "", const std::string& value = "", const std::string& expected = "")
108  : type(t)
109  , file(file)
110  , line(line)
111  , n(n)
112  , tag(tag)
113  , key(key)
114  , value(value)
115  , expected(expected)
116  {
117  }
118  };
119 
120  /** Controls the way to print errors. */
122 
123  virtual void print(message_info&);
124 private:
125  void print_cache();
126 
127  typedef std::deque<message_info> message_list;
128  typedef std::map<const config*, message_list> message_map;
129 
130  /** Reads config from input. */
131  bool read_config_file(const std::string& filename);
132 
133  /** Shows, if validator is initialized with schema file. */
135 
136  /** Root of schema information. */
138 
139  std::stack<const wml_tag*> stack_;
140 
141  /** Contains number of children. */
143 
144  /** Caches error messages. */
145  std::stack<message_map> cache_;
146 
147  /** Type validators. */
149 
151 
152  std::vector<std::string> errors_;
153 
154 protected:
155  template<typename... T>
156  void queue_message(const config& cfg, T&&... args)
157  {
158  cache_.top()[&cfg].emplace_back(std::forward<T>(args)...);
159  }
160 
161  const wml_tag& active_tag() const;
162  std::string active_tag_path() const;
163  bool have_active_tag() const;
164  bool is_valid() const {return config_read_;}
165  wml_type::ptr find_type(const std::string& type) const;
166 
167 private:
168  using derivation_graph_t = boost::adjacency_list<boost::vecS,
169  boost::vecS,
170  boost::directedS,
171  std::pair<const wml_tag*, std::string>,
172  std::tuple<config, std::string, int>>;
173 
175  std::map<const wml_tag*, derivation_graph_t::vertex_descriptor> derivation_map_;
176 
178 
179  /**
180  * Collects all mandatory keys for a tag, including the super keys and overrides.
181  *
182  * The returned map can contain non-mandatory keys if they are overriden, please check the is_mandatory() result.
183  */
184  std::optional<std::map<std::string, wml_key>> find_mandatory_keys(const wml_tag* tag, const config& cfg) const;
185  std::optional<std::map<std::string, wml_key>> find_mandatory_keys(
186  const wml_tag* tag, const config& cfg, std::vector<const wml_tag*>& visited) const;
187 
188  /**
189  * Validates that all mandatory keys for a tag are present.
190  */
192  const wml_tag* tag, const config& cfg, const std::string& name, int start_line, const std::string& file);
193  void validate_mandatory_keys(const std::map<std::string, wml_key>& mandatory_keys,
194  const wml_tag* tag,
195  const config& cfg,
196  const std::string& name,
197  int start_line,
198  const std::string& file,
199  std::vector<const wml_tag*>& visited);
200 
201  using link_graph_t = boost::adjacency_list<boost::vecS,
202  boost::vecS,
203  boost::directedS,
204  std::string>;
205 
206  using link_graph_map_t = std::map<const wml_type_alias*,
207  link_graph_t::vertex_descriptor>;
208 
209  void detect_link_cycles(const std::string& filename);
210  void collect_link_source(link_graph_t& link_graph, link_graph_map_t& link_map, const std::string& type_name, const wml_type* type);
211  void collect_link_target(link_graph_t& link_graph, link_graph_map_t& link_map, const std::string& type_name, const wml_type* type, const wml_type_alias* alias);
212 };
213 
214 // A validator specifically designed for validating a schema
215 // In addition to the usual, it verifies that references within the schema are valid, for example via the [link] tag
217 {
218 public:
220  virtual void open_tag(const std::string& name, const config& parent, int start_line = 0, const std::string& file = "", bool addition = false) override;
221  virtual void close_tag() override;
222  virtual void validate(const config& cfg, const std::string& name, int start_line, const std::string& file) override;
223  virtual void validate_key(const config& cfg, const std::string& name, const config_attribute_value& value, int start_line, const std::string& file) override;
224 private:
225  struct reference {
226  reference(const std::string& value, const std::string& file, int line, const std::string& tag)
227  : value_(value)
228  , file_(file)
229  , tag_(tag)
230  , line_(line)
231  {}
232  std::string value_, file_, tag_;
233  int line_;
234  bool match(const std::set<std::string>& with);
235  bool can_find(const wml_tag& root, const config& cfg);
236  bool operator<(const reference& other) const;
237  };
238  std::string current_path() const;
239  std::set<std::string> defined_types_, defined_tag_paths_;
240  std::vector<reference> referenced_types_, referenced_tag_paths_;
241  std::stack<std::string> tag_stack_;
242  std::map<std::string, std::string> links_;
243  std::multimap<std::string, std::string> derivations_;
245  bool tag_path_exists(const config& cfg, const reference& ref);
246  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);
247  static bool name_matches(const std::string& pattern, const std::string& name);
248 
249  void print(message_info& message) override;
251 
253  = boost::adjacency_list<boost::vecS, boost::vecS, boost::directedS, std::string, std::pair<config, reference>>;
254 
256  std::map<std::string, schema_derivation_graph_t::vertex_descriptor> schema_derivation_map_;
257 
259 };
260 
261 } // namespace schema_validation{
double t
Definition: astarsearch.cpp:63
Used in parsing config file.
Definition: validator.hpp:38
Variant for storing WML attributes.
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:159
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_
boost::adjacency_list< boost::vecS, boost::vecS, boost::directedS, std::string, std::pair< config, reference > > schema_derivation_graph_t
bool tag_path_exists(const config &cfg, const reference &ref)
schema_derivation_graph_t schema_derivation_graph_
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)
std::map< std::string, schema_derivation_graph_t::vertex_descriptor > schema_derivation_map_
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.
const std::vector< std::string > & get_errors() const
wml_type::map types_
Type validators.
wml_tag root_
Root of schema information.
std::optional< std::map< std::string, wml_key > > find_mandatory_keys(const wml_tag *tag, const config &cfg) const
Collects all mandatory keys for a tag, including the super keys and overrides.
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.
boost::adjacency_list< boost::vecS, boost::vecS, boost::directedS, std::string > link_graph_t
boost::adjacency_list< boost::vecS, boost::vecS, boost::directedS, std::pair< const wml_tag *, std::string >, std::tuple< config, std::string, int > > derivation_graph_t
wml_type::ptr find_type(const std::string &type) const
void collect_link_source(link_graph_t &link_graph, link_graph_map_t &link_map, const std::string &type_name, const wml_type *type)
std::stack< message_map > cache_
Caches error messages.
std::map< const config *, message_list > message_map
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...
std::stack< cnt_map > cnt_stack
And counter maps are organize in stack.
std::deque< message_info > message_list
std::map< const wml_type_alias *, link_graph_t::vertex_descriptor > link_graph_map_t
bool config_read_
Shows, if validator is initialized with schema file.
void detect_link_cycles(const std::string &filename)
virtual void close_tag() override
As far as parser is built on stack, some realizations can store stack too.
void validate_mandatory_keys(const wml_tag *tag, const config &cfg, const std::string &name, int start_line, const std::string &file)
Validates that all mandatory keys for a tag are present.
void collect_link_target(link_graph_t &link_graph, link_graph_map_t &link_map, const std::string &type_name, const wml_type *type, const wml_type_alias *alias)
std::map< const wml_tag *, derivation_graph_t::vertex_descriptor > derivation_map_
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_
std::map< std::string, counter > cnt_map
Counters are mapped by tag name.
virtual void print(message_info &)
Stores information about tag.
Definition: tag.hpp:48
Stores information about a schema type.
Definition: type.hpp:66
Stores information about a schema type.
Definition: type.hpp:36
std::map< std::string, ptr > map
Definition: type.hpp:43
std::shared_ptr< wml_type > ptr
Definition: type.hpp:42
void line(int from_x, int from_y, int to_x, int to_y)
Draw a line.
Definition: draw.cpp:180
reference(const std::string &value, const std::string &file, int line, const std::string &tag)
bool match(const std::set< std::string > &with)
bool can_find(const wml_tag &root, const config &cfg)
message_info(message_type t, const std::string &file, int line=0, int n=0, const std::string &tag="", const std::string &key="", const std::string &value="", const std::string &expected="")
This file contains object "tag", which is used to store information about tags while annotation parsi...
This file contains object "type", which is used to store information about types while annotation par...
This file contains information about validation abstract level interface.