26 #include <boost/graph/adjacency_list.hpp>
33 #define ERR_VL LOG_STREAM(err, log_validation)
34 #define WRN_VL LOG_STREAM(warn, log_validation)
35 #define LOG_VL LOG_STREAM(info, log_validation)
37 static std::string
at(
const std::string& file,
int line)
39 std::ostringstream ss;
40 ss <<
line <<
" " << file;
44 static void print_output(
const std::string& message,
bool flag_exception =
false)
46 #ifndef VALIDATION_ERRORS_LOG
54 flag_exception =
true;
63 const std::string& name,
65 const std::string& parent,
68 std::ostringstream ss;
69 ss <<
"Extra tag [" << name <<
"]; there may only be " <<
n <<
" [" << name <<
"] in [" << parent <<
"]\n"
76 const std::string& file,
int line,
const std::string& name,
const std::string& parent,
bool flag_exception)
78 std::ostringstream ss;
79 ss <<
"Tag [" << name <<
"] may not be used in [" << parent <<
"]\n" <<
at(file,
line) <<
"\n";
86 const std::string& name,
88 const std::string& parent,
91 std::ostringstream ss;
92 ss <<
"Missing tag [" << name <<
"]; there must be " <<
n <<
" [" << name <<
"]s in [" << parent <<
"]\n"
99 const std::string& file,
int line,
const std::string&
tag,
const std::string& key,
bool flag_exception)
101 std::ostringstream ss;
102 ss <<
"Invalid key '" << key <<
"='";
104 ss <<
" in tag [" <<
tag <<
"]\n";
107 ss <<
at(file,
line) <<
"\n";
114 const std::string& file,
int line,
const std::string&
tag,
const std::string& key,
bool flag_exception)
116 std::ostringstream ss;
117 ss <<
"Missing key '" << key <<
"='";
119 ss <<
" in tag [" <<
tag <<
"]\n";
122 ss <<
at(file,
line) <<
"\n";
130 const std::string&
tag,
131 const std::string& key,
132 const std::string& value,
133 const std::string& expected,
136 std::ostringstream ss;
137 ss <<
"Invalid value '";
138 if(value.length() > 128)
139 ss << value.substr(0, 128) <<
"...";
141 ss <<
"' in key '" << key <<
"=' in tag [" <<
tag <<
"]\n" <<
" (expected value of type " << expected <<
") " <<
at(file,
line) <<
"\n";
148 const std::string&
tag,
149 const std::string& schema_name,
150 const std::string& value,
153 std::ostringstream ss;
154 ss <<
"Inheritance cycle from " <<
tag <<
" to " << value <<
" found\n"
155 <<
at(file,
line) <<
"\nwith schema " << schema_name <<
"\n";
162 const std::string&
tag,
163 const std::string& value,
166 std::ostringstream ss;
167 ss <<
"Link cycle from " <<
tag <<
" to " << value <<
" found\n"
175 const std::string&
tag,
176 const std::string& schema_name,
177 const std::string& super,
180 std::ostringstream ss;
181 ss <<
"Super " << super <<
" not found. Needed by " <<
tag <<
"\n"
182 <<
at(file,
line) <<
"\nwith schema " << schema_name <<
"\n";
189 const std::string&
tag,
190 const std::string& key,
191 const std::string& value,
194 std::ostringstream ss;
195 ss <<
"Unknown path reference '" << value <<
"' in key '" << key <<
"=' in tag [" <<
tag <<
"]\n" <<
at(file,
line) <<
"\n";
201 const std::string&
tag,
202 const std::string& pat,
203 const std::string& value,
206 std::ostringstream ss;
207 ss <<
"Duplicate or fully-overlapping tag definition '" << value <<
"' (which is also matched by '" << pat <<
"') in tag [" <<
tag <<
"]\n" <<
at(file,
line) <<
"\n";
213 const std::string&
tag,
214 const std::string& pat,
215 const std::string& value,
218 std::ostringstream ss;
219 ss <<
"Duplicate or fully-overlapping key definition '" << value <<
"' (which is also matched by '" << pat <<
"') in tag [" <<
tag <<
"]\n" <<
at(file,
line) <<
"\n";
225 const std::string&
tag,
226 const std::string& key,
227 const std::string& value,
231 std::ostringstream ss;
232 ss <<
"Inheritance loop " << key <<
"=" << value <<
" found (at offset " <<
index <<
") in tag [" <<
tag <<
"]\n" <<
at(file,
line) <<
"\n";
237 const std::string &
tag,
238 const std::string & key,
239 const std::string &
type,
242 std::ostringstream ss;
243 ss <<
"Invalid type '" <<
type <<
"' in key '" << key <<
"=' in tag [" <<
tag <<
"]\n" <<
at(file,
line) <<
"\n";
254 , config_read_(false)
255 , validate_schema_(validate_schema)
259 ERR_VL <<
"Schema file " << config_file_name <<
" was not read.";
266 LOG_VL <<
"Schema file " << config_file_name <<
" was read.";
267 LOG_VL <<
"Validator initialized";
275 std::unique_ptr<abstract_validator>
validator;
288 for(
const config& schema :
g.child_range(
"tag")) {
289 if(schema[
"name"].str() ==
"root") {
294 types_[
"t_string"] = std::make_shared<wml_type_tstring>();
298 }
catch(
const std::exception&) {
318 boost::depth_first_search(link_graph,
320 const auto source = std::find_if(link_map.begin(), link_map.end(),
321 [&](
const auto& link) { return link.second == boost::source(edge, link_graph); });
323 assert(source != link_map.end());
325 const auto target = std::find_if(link_map.begin(), link_map.end(),
326 [&](
const auto& link) { return link.second == boost::target(edge, link_graph); });
328 assert(target != link_map.end());
330 const auto& alias_type = link_graph[source->second];
331 const auto& link_type = link_graph[target->second];
339 auto it =
types_.find(alias->link());
345 for(
auto elem : composite->subtypes()) {
353 if (link_map.find(alias) == link_map.end()) {
356 boost::add_vertex(type_name, link_graph));
359 if (link_map.find(link) == link_map.end()) {
362 boost::add_vertex(alias->
link(), link_graph));
365 boost::add_edge(link_map[alias], link_map[link], link_graph);
367 for(
auto elem : composite->subtypes()) {
382 }
else if(!
stack_.empty()) {
426 for(
auto& m :
cache_.top()) {
427 for(
auto& list : m.second) {
442 auto cache_it =
cache_.top().find(&cfg);
443 if(cache_it !=
cache_.top().end()) {
444 cache_it->second.clear();
453 if(&active == &
root_) {
457 const auto super_tags = active.
super(cfg);
459 for(
const auto& [super_path, super_tag] : super_tags) {
476 for(
const auto& expected : super_expected) {
477 const auto super_exists = std::any_of(super_tags.begin(), super_tags.end(),
478 [&](
const std::pair<std::string, const wml_tag*>& super) { return super.first == expected; });
486 for(
const auto&
tag : active.
tags(cfg)) {
489 if(
tag.second.get_min() > cnt) {
494 if(
tag.second.get_max() < cnt) {
499 int total_cnt =
counter_.top()[
""].cnt;
502 }
else if(
active_tag().get_max_children() < total_cnt) {
513 auto visited = std::vector<const wml_tag*>();
518 const wml_tag*
tag,
const config& cfg, std::vector<const wml_tag*>& visited)
const
521 if(
std::find(visited.begin(), visited.end(),
tag) != visited.end()) {
522 return utils::nullopt;
525 visited.push_back(
tag);
527 auto mandatory_keys = std::map<std::string, wml_key>();
530 for(
const auto& [
_, super_tag] :
tag->super(cfg)) {
534 if(!super_mandatory_keys) {
535 return utils::nullopt;
538 super_mandatory_keys->merge(mandatory_keys);
539 mandatory_keys.swap(*super_mandatory_keys);
543 for(
const auto& key :
tag->keys(cfg)) {
544 if(key.second.is_mandatory() || mandatory_keys.find(key.first) != mandatory_keys.end()) {
545 mandatory_keys[key.first] = key.second;
549 return mandatory_keys;
553 const wml_tag*
tag,
const config& cfg,
const std::string& name,
int start_line,
const std::string& file)
558 if(!mandatory_keys) {
562 auto visited = std::vector<const wml_tag*>();
569 const std::string& name,
571 const std::string& file,
572 std::vector<const wml_tag*>& visited)
575 if(
std::find(visited.begin(), visited.end(),
tag) != visited.end()) {
579 visited.push_back(
tag);
582 for(
const auto& key : mandatory_keys) {
583 if(key.second.is_mandatory()) {
584 if(cfg.
get(key.first) ==
nullptr) {
596 [&](
const auto& derivation) { return derivation.second == boost::source(edge, derivation_graph_); });
601 [&](
const auto& derivation) { return derivation.second == boost::target(edge, derivation_graph_); });
621 bool matched =
false;
641 assert(
have_active_tag() &&
"Tried to get active tag name when there was none");
660 std::stack<const wml_tag*> temp =
stack_;
661 std::deque<std::string>
path;
662 while(!temp.empty()) {
663 path.push_front(temp.top()->get_name());
666 if(
path.front() ==
"root") {
705 , condition_nesting_()
718 if(name ==
"if" || name ==
"switch") {
720 }
else if(name ==
"tag") {
732 if(tag_name ==
"type") {
746 std::string suffix =
path.back();
748 while(!
path.empty()) {
750 auto link =
links_.find(prefix);
751 if(link !=
links_.end()) {
752 std::string new_path = link->second +
"/" + suffix;
757 suffix =
path.back();
761 if(supers.first != supers.second) {
763 for(
auto cur = supers.first ; cur != supers.second; ++cur) {
764 super_ref.
value_ = cur->second +
"/" + suffix;
773 std::string new_path = prefix +
"/" + suffix;
777 suffix =
path.back() +
"/" + suffix;
794 for(
const std::string& pattern : seen) {
795 for(
const std::string& key :
split) {
802 seen.push_back(name);
809 }
else if(name ==
"tag") {
810 bool first_tag =
true, first_key =
true;
811 std::vector<std::string> tag_names, key_names;
813 if(current_key ==
"tag" || current_key ==
"link") {
814 std::string tag_name = current_cfg[
"name"];
815 if(current_key ==
"link") {
816 tag_name.erase(0, tag_name.find_last_of(
'/') + 1);
819 tag_names.push_back(tag_name);
824 }
else if(current_key ==
"key") {
825 std::string key_name = current_cfg[
"name"];
827 key_names.push_back(key_name);
834 }
else if(name ==
"wml_schema") {
835 using namespace std::placeholders;
844 std::sort(missing_types.begin(), missing_types.end());
845 std::sort(missing_tags.begin(), missing_tags.end());
847 for(
auto& ref : missing_types) {
848 std::string tag_name;
849 if(ref.
tag_ ==
"key") {
856 for(
auto& ref : missing_tags) {
857 std::string tag_name;
858 if(ref.
tag_ ==
"tag") {
860 }
else if(ref.
tag_ ==
"link") {
876 if(tag_name ==
"key" && name ==
"type" ) {
880 }
else if((tag_name ==
"type" || tag_name ==
"element") && name ==
"link") {
882 }
else if(tag_name ==
"link" && name ==
"name") {
884 std::string link_name =
utils::split(cfg[
"name"].str(),
'/').back();
886 }
else if(tag_name ==
"tag" && name ==
"super") {
894 if(full_path == super) {
924 assert(cfg.has_attribute(
"super"));
927 "super", ref.value_);
934 std::deque<std::string>
path;
935 while(!temp.empty()) {
936 path.push_front(temp.top());
939 if(
path.front() ==
"root") {
947 return std::tie(file_, line_) < std::tie(other.
file_, other.
line_);
952 return with.count(value_) > 0;
958 return root.
find_tag(value_, root, cfg) !=
nullptr;
Used in parsing config file.
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.
auto all_children_view() const
In-order iteration over all children.
child_itors child_range(config_key_type key)
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.
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::stack< std::string > tag_stack_
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)
schema_derivation_graph_t schema_derivation_graph_
void detect_schema_derivation_cycles()
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::string current_path() const
std::vector< reference > referenced_types_
std::map< std::string, schema_derivation_graph_t::vertex_descriptor > schema_derivation_map_
std::set< std::string > defined_tag_paths_
static bool name_matches(const std::string &pattern, const std::string &name)
std::set< std::string > defined_types_
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.
boost::adjacency_list< boost::vecS, boost::vecS, boost::directedS, std::string > link_graph_t
utils::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.
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.
derivation_graph_t derivation_graph_
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...
void detect_derivation_cycles()
virtual ~schema_validator()
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.
std::string active_tag_path() const
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.
const wml_tag & active_tag() const
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_
virtual void print(message_info &)
bool have_active_tag() const
wml_key is used to save the information about one key.
const std::string & get_type() const
Stores information about tag.
boost::iterator_range< super_iterator > super(const config &cfg_match) const
int get_min_children() const
const std::string & get_name() const
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.
int get_max_children() const
boost::iterator_range< tag_iterator > tags(const config &cfg_match) const
const wml_key * find_key(const std::string &name, const config &match, bool ignore_super=false) const
Returns pointer to child key.
void expand_all(wml_tag &root)
Calls the expansion on each child.
const std::string & get_super() const
Stores information about a schema type.
const std::string & link() const
Stores information about a schema type.
Stores information about a schema type.
static std::shared_ptr< wml_type > from_config(const config &cfg)
std::shared_ptr< wml_type > ptr
A helper for boost::depth_first_search (DFS) usage with the purpose of detecting cycles.
Declarations for File-IO.
static std::string _(const char *str)
Standard logging facilities (interface).
void line(int from_x, int from_y, int to_x, int to_y)
Draw a line.
utils::optional< std::string > get_wml_location(const std::string &path, const utils::optional< std::string > ¤t_dir)
Returns a translated path to the actual file or directory, if it exists.
std::unique_ptr< std::istream > scoped_istream
std::string tag(const std::string &tag_name, Args &&... contents)
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 std::string missing_super_error(const std::string &file, int line, const std::string &tag, const std::string &schema_name, const std::string &super, 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 inheritance_cycle_error(const std::string &file, int line, const std::string &tag, const std::string &schema_name, 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)
static std::string link_cycle_error(const std::string &file, int line, const std::string &tag, const std::string &value, bool flag_exception)
std::size_t index(std::string_view str, const std::size_t index)
Codepoint index corresponding to the nth character in a UTF-8 string.
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,...
void erase_if(Container &container, const Predicate &predicate)
Convenience wrapper for using std::remove_if on a container.
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)
auto * find(Container &container, const Value &value)
Convenience wrapper for using find on a container without needing to comare to end()
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
One of the realizations of serialization/validator.hpp abstract validator.
void read(config &cfg, std::istream &in, abstract_validator *validator)
std::string filename
Filename.
Used to manage with not initialized validators Supposed to be thrown from the constructor.
bool match(const std::set< std::string > &with) const
bool can_find(const wml_tag &root, const config &cfg) const
bool operator<(const reference &other) const
Helper class, don't construct this directly.
static map_location::direction n
This file contains object "type", which is used to store information about types while annotation par...
bool strict_validation_enabled
Add a special kind of assert to validate whether the input from WML doesn't contain any problems that...