Add a validator subclass specialized for validating WML schemas against the WML schema schema
This commit is contained in:
parent
4674e97eb6
commit
52c4dc0e3d
12 changed files with 473 additions and 24 deletions
|
@ -39,7 +39,6 @@
|
|||
[/tag]
|
||||
[tag]
|
||||
name="$generic_lua_component"
|
||||
engine=lua
|
||||
{SIMPLE_KEY code string}
|
||||
{DATA_TAG args 0 1}
|
||||
[/tag]
|
||||
|
|
|
@ -238,7 +238,7 @@
|
|||
{SIMPLE_KEY begin s_real}
|
||||
{SIMPLE_KEY end s_real}
|
||||
{SIMPLE_KEY spend_all_gold s_int}
|
||||
{SIMPLE_KEY save_on_negative_income sbool}
|
||||
{SIMPLE_KEY save_on_negative_income s_bool}
|
||||
)}
|
||||
|
||||
{AI_FACET_TAG recruitment_instructions (
|
||||
|
|
|
@ -164,10 +164,10 @@
|
|||
{AI_MODIFY_MATCH_ASPECT villages_per_scout int}
|
||||
{AI_MODIFY_MATCH_ASPECT attack_depth int}
|
||||
{AI_MODIFY_MATCH_ASPECT recruitment_randomness int}
|
||||
{AI_MODIFY_MATCH_ASPECT grouping ai_grouping}
|
||||
{AI_MODIFY_MATCH_ASPECT advancements string_list}
|
||||
{AI_MODIFY_MATCH_ASPECT recruitment_more string_list}
|
||||
{AI_MODIFY_MATCH_ASPECT recruitment_pattern string_list}
|
||||
{AI_MODIFY_MATCH_ASPECT grouping grouping}
|
||||
{AI_MODIFY_MATCH_ASPECT advancements string}
|
||||
{AI_MODIFY_MATCH_ASPECT recruitment_more string}
|
||||
{AI_MODIFY_MATCH_ASPECT recruitment_pattern string}
|
||||
{AI_MODIFY_MATCH_ASPECT avoid avoid}
|
||||
{AI_MODIFY_MATCH_ASPECT leader_goal leader_goal}
|
||||
{AI_MODIFY_MATCH_ASPECT recruitment_instructions recruitment_instructions}
|
||||
|
|
|
@ -72,8 +72,8 @@
|
|||
[tag]
|
||||
name="fog_override"
|
||||
max=infinite
|
||||
{SIMPLE_KEY x int_list}
|
||||
{SIMPLE_KEY y int_list}
|
||||
{SIMPLE_KEY x range_list}
|
||||
{SIMPLE_KEY y range_list}
|
||||
[/tag]
|
||||
[tag]
|
||||
name="ai"
|
||||
|
@ -289,7 +289,7 @@
|
|||
{SIMPLE_KEY map_data map_data}
|
||||
{DEFAULT_KEY turns turns unlimited}
|
||||
{DEFAULT_KEY turn_at int 1}
|
||||
{DEFAULT_KEY random_start_time bool,int_list no} # Note: Is it random_start_time or random_starting_time? (There's some uses of the latter)
|
||||
{DEFAULT_KEY random_start_time bool,int_list no}
|
||||
{DEPRECATED_KEY music string}
|
||||
{SIMPLE_KEY defeat_music string}
|
||||
{SIMPLE_KEY victory_music string}
|
||||
|
|
|
@ -47,7 +47,7 @@
|
|||
{LINK_TAG "lua"}
|
||||
[tag]
|
||||
name="and,or,not"
|
||||
super="$condition_wml"
|
||||
super="$conditional_wml"
|
||||
[/tag]
|
||||
any_tag=yes
|
||||
[/tag]
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
|
||||
[tag]
|
||||
name="editor_group"
|
||||
max=unlimited
|
||||
max=infinite
|
||||
{REQUIRED_KEY id string}
|
||||
{SIMPLE_KEY name t_string}
|
||||
{SIMPLE_KEY icon t_string}
|
||||
|
@ -10,11 +10,11 @@
|
|||
|
||||
[tag]
|
||||
name="item_group"
|
||||
max=unlimited
|
||||
max=infinite
|
||||
super="editor_group"
|
||||
[tag]
|
||||
name="item"
|
||||
max=unlimited
|
||||
max=infinite
|
||||
super="scenario/item"
|
||||
{REQUIRED_KEY id string}
|
||||
{SIMPLE_KEY name t_string}
|
||||
|
|
|
@ -90,6 +90,14 @@
|
|||
[/element]
|
||||
[/list]
|
||||
[/type]
|
||||
[type]
|
||||
name=int_list
|
||||
[list]
|
||||
[element]
|
||||
link=unsigned
|
||||
[/element]
|
||||
[/list]
|
||||
[/type]
|
||||
[type]
|
||||
name=prog_string
|
||||
value=".*"
|
||||
|
|
210
data/schema/schema.cfg
Normal file
210
data/schema/schema.cfg
Normal file
|
@ -0,0 +1,210 @@
|
|||
{./macros.cfg}
|
||||
[wml_schema]
|
||||
[type]
|
||||
name="regex"
|
||||
value=".*"
|
||||
[/type]
|
||||
[type]
|
||||
name="glob"
|
||||
value=".*"
|
||||
[/type]
|
||||
[type]
|
||||
name="string"
|
||||
value=".*"
|
||||
[/type]
|
||||
[type]
|
||||
name="id"
|
||||
value="[a-zA-Z0-9_~$]+"
|
||||
[/type]
|
||||
[type]
|
||||
name="path"
|
||||
[list]
|
||||
min=1
|
||||
split="/"
|
||||
[element]
|
||||
link="id"
|
||||
[/element]
|
||||
[/list]
|
||||
[/type]
|
||||
[type]
|
||||
name="path_list"
|
||||
[list]
|
||||
min=1
|
||||
[element]
|
||||
link="path"
|
||||
[/element]
|
||||
[/list]
|
||||
[/type]
|
||||
[type]
|
||||
name="id_list"
|
||||
[list]
|
||||
min=1
|
||||
[element]
|
||||
link="id"
|
||||
[/element]
|
||||
[/list]
|
||||
[/type]
|
||||
[type]
|
||||
name="int"
|
||||
value="\d+"
|
||||
[/type]
|
||||
[type]
|
||||
name="inf"
|
||||
value="infinite"
|
||||
[/type]
|
||||
[type]
|
||||
name="bool"
|
||||
value="yes|no|true|false"
|
||||
[/type]
|
||||
[tag]
|
||||
name="root"
|
||||
min=1
|
||||
[tag]
|
||||
name="wml_schema"
|
||||
min=1
|
||||
[tag]
|
||||
name="type"
|
||||
max=infinite
|
||||
super="type"
|
||||
{REQUIRED_KEY name id}
|
||||
[if]
|
||||
[union]
|
||||
[/union]
|
||||
[then]
|
||||
[tag]
|
||||
name="union"
|
||||
min=1
|
||||
[tag]
|
||||
name="type"
|
||||
max=infinite
|
||||
super="wml_schema/type"
|
||||
# Override the required name with an optional name
|
||||
# Technically name is not really allowed here at all,
|
||||
# but the schema can't override a supertag key with its absence.
|
||||
{SIMPLE_KEY name string}
|
||||
[/tag]
|
||||
[/tag]
|
||||
[/then]
|
||||
[elseif]
|
||||
[intersection]
|
||||
[/intersection]
|
||||
[then]
|
||||
[tag]
|
||||
name="intersection"
|
||||
min=1
|
||||
{LINK_TAG "wml_schema/type"}
|
||||
[tag]
|
||||
name="type"
|
||||
max=infinite
|
||||
super="wml_schema/type"
|
||||
# Override the required name with an optional name
|
||||
# Technically name is not really allowed here at all,
|
||||
# but the schema can't override a supertag key with its absence.
|
||||
{SIMPLE_KEY name string}
|
||||
[/tag]
|
||||
[/tag]
|
||||
[/then]
|
||||
[/elseif]
|
||||
[elseif]
|
||||
[list]
|
||||
[/list]
|
||||
[then]
|
||||
[tag]
|
||||
name="list"
|
||||
min=1
|
||||
{DEFAULT_KEY min int 0}
|
||||
{DEFAULT_KEY max int,inf infinite}
|
||||
{DEFAULT_KEY split regex "\s*,\s*"}
|
||||
[tag]
|
||||
name="element"
|
||||
max=infinite
|
||||
super="wml_schema/type"
|
||||
# Override the required name with an optional name
|
||||
# Technically name is not really allowed here at all,
|
||||
# but the schema can't override a supertag key with its absence.
|
||||
{SIMPLE_KEY name string}
|
||||
[/tag]
|
||||
[/tag]
|
||||
[/then]
|
||||
[/elseif]
|
||||
[elseif]
|
||||
glob_on_value=*
|
||||
[then]
|
||||
{SIMPLE_KEY value regex}
|
||||
[/then]
|
||||
[/elseif]
|
||||
[else]
|
||||
{SIMPLE_KEY link id}
|
||||
[/else]
|
||||
[/if]
|
||||
[/tag]
|
||||
[tag]
|
||||
name="tag"
|
||||
min=1
|
||||
{REQUIRED_KEY name glob}
|
||||
{DEFAULT_KEY min int 0}
|
||||
{DEFAULT_KEY max int,inf 1}
|
||||
{SIMPLE_KEY super path_list}
|
||||
{DEFAULT_KEY any_tag bool no}
|
||||
{DEFAULT_KEY deprecated bool no}
|
||||
[tag]
|
||||
name="key"
|
||||
max=infinite
|
||||
{REQUIRED_KEY name glob}
|
||||
{REQUIRED_KEY type id_list}
|
||||
{DEFAULT_KEY mandatory bool no}
|
||||
{SIMPLE_KEY default string}
|
||||
{DEFAULT_KEY deprecated bool no}
|
||||
[/tag]
|
||||
{LINK_TAG "wml_schema/tag"}
|
||||
[tag]
|
||||
name="link"
|
||||
max=infinite
|
||||
{REQUIRED_KEY name path}
|
||||
[/tag]
|
||||
[tag]
|
||||
name="switch"
|
||||
max=infinite
|
||||
{REQUIRED_KEY key id}
|
||||
[tag]
|
||||
name="case"
|
||||
max=infinite
|
||||
super="wml_schema/tag"
|
||||
{REQUIRED_KEY value string}
|
||||
{DEFAULT_KEY trigger_if_missing bool no}
|
||||
[/tag]
|
||||
[tag]
|
||||
name="else"
|
||||
super="wml_schema/tag"
|
||||
[/tag]
|
||||
[/tag]
|
||||
[tag]
|
||||
name="if"
|
||||
max="infinite"
|
||||
any_tag=yes
|
||||
{ANY_KEY string}
|
||||
[tag]
|
||||
name="then"
|
||||
super="wml_schema/tag"
|
||||
[/tag]
|
||||
[tag]
|
||||
name="elseif"
|
||||
max=infinite
|
||||
super="wml_schema/tag"
|
||||
any_tag=yes
|
||||
{ANY_KEY string}
|
||||
[tag]
|
||||
name="then"
|
||||
min=1
|
||||
super="wml_schema/tag"
|
||||
[/tag]
|
||||
[/tag]
|
||||
[tag]
|
||||
name="else"
|
||||
super="wml_schema/tag"
|
||||
[/tag]
|
||||
[/tag]
|
||||
[/tag]
|
||||
[/tag]
|
||||
[/tag]
|
||||
[/wml_schema]
|
|
@ -40,7 +40,13 @@
|
|||
|
||||
[type]
|
||||
name=color
|
||||
value="(?:2[0-5][0-5]|[01]?\d?\d)[.,]\s*(?:2[0-5][0-5]|[01]?\d?\d)[.,]\s*(?:2[0-5][0-5]|[01]?\d?\d)[.,]\s*(?:2[0-5][0-5]|[01]?\d?\d)"
|
||||
[list]
|
||||
min=3
|
||||
max=4
|
||||
[element]
|
||||
value="(?:2[0-5][0-5]|[01]?\d?\d)\s*"
|
||||
[/element]
|
||||
[/list]
|
||||
[/type]
|
||||
|
||||
[type]
|
||||
|
|
|
@ -31,7 +31,7 @@
|
|||
{SIMPLE_KEY advances_to string_list}
|
||||
{SIMPLE_KEY race string}
|
||||
{SIMPLE_KEY undead_variation string}
|
||||
{SIMPLE_KEY usage usage}
|
||||
{SIMPLE_KEY usage ai_usage}
|
||||
{SIMPLE_KEY zoc s_bool}
|
||||
{SIMPLE_KEY ellipse string}
|
||||
{DEPRECATED_KEY ai_special string} # Not documented
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
#include "serialization/preprocessor.hpp"
|
||||
#include "serialization/string_utils.hpp"
|
||||
#include "wml_exception.hpp"
|
||||
#include <tuple>
|
||||
|
||||
namespace schema_validation
|
||||
{
|
||||
|
@ -115,6 +116,18 @@ static void wrong_value_error(const std::string& file,
|
|||
print_output(ss.str(), 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)
|
||||
{
|
||||
std::ostringstream ss;
|
||||
ss << "Unknown path reference '" << value << "' in key '" << key << "=' in tag [" << tag << "]\n" << at(file, line) << "\n";
|
||||
print_output(ss.str(), flag_exception);
|
||||
}
|
||||
|
||||
static void wrong_type_error(const std::string & file, int line,
|
||||
const std::string & tag,
|
||||
const std::string & key,
|
||||
|
@ -130,14 +143,10 @@ schema_validator::~schema_validator()
|
|||
{
|
||||
}
|
||||
|
||||
schema_validator::schema_validator(const std::string& config_file_name)
|
||||
schema_validator::schema_validator(const std::string& config_file_name, bool validate_schema)
|
||||
: config_read_(false)
|
||||
, create_exceptions_(strict_validation_enabled)
|
||||
, root_()
|
||||
, stack_()
|
||||
, counter_()
|
||||
, cache_()
|
||||
, types_()
|
||||
, validate_schema_(validate_schema)
|
||||
{
|
||||
if(!read_config_file(config_file_name)) {
|
||||
ERR_VL << "Schema file " << config_file_name << " was not read." << std::endl;
|
||||
|
@ -156,9 +165,13 @@ bool schema_validator::read_config_file(const std::string& filename)
|
|||
{
|
||||
config cfg;
|
||||
try {
|
||||
std::unique_ptr<abstract_validator> validator;
|
||||
if(validate_schema_) {
|
||||
validator.reset(new schema_self_validator());
|
||||
}
|
||||
preproc_map preproc(game_config::config_cache::instance().get_preproc_map());
|
||||
filesystem::scoped_istream stream = preprocess_file(filename, &preproc);
|
||||
read(cfg, *stream);
|
||||
read(cfg, *stream, validator.get());
|
||||
} catch(const config::error& e) {
|
||||
ERR_VL << "Failed to read file " << filename << ":\n" << e.what() << "\n";
|
||||
return false;
|
||||
|
@ -353,6 +366,182 @@ void schema_validator::print(message_info& el)
|
|||
case WRONG_TYPE:
|
||||
wrong_type_error(el.file, el.line, el.tag, el.key, el.value, create_exceptions_);
|
||||
break;
|
||||
case WRONG_PATH:
|
||||
wrong_path_error(el.file, el.line, el.tag, el.key, el.value, create_exceptions_);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
schema_self_validator::schema_self_validator()
|
||||
: schema_validator(filesystem::get_wml_location("schema/schema.cfg"), false)
|
||||
, type_nesting_()
|
||||
, condition_nesting_()
|
||||
{}
|
||||
|
||||
void schema_self_validator::open_tag(const std::string& name, const config& parent, int start_line, const std::string& file, bool addition)
|
||||
{
|
||||
schema_validator::open_tag(name, parent, start_line, file, addition);
|
||||
if(name == "type") {
|
||||
type_nesting_++;
|
||||
}
|
||||
if(condition_nesting_ == 0) {
|
||||
if(name == "if" || name == "switch") {
|
||||
condition_nesting_ = 1;
|
||||
} else if(name == "tag") {
|
||||
tag_stack_.emplace();
|
||||
}
|
||||
} else {
|
||||
condition_nesting_++;
|
||||
}
|
||||
}
|
||||
|
||||
void schema_self_validator::close_tag()
|
||||
{
|
||||
if(have_active_tag()) {
|
||||
auto tag_name = active_tag().get_name();
|
||||
if(tag_name == "type") {
|
||||
type_nesting_--;
|
||||
} else if(condition_nesting_ == 0 && tag_name == "tag") {
|
||||
tag_stack_.pop();
|
||||
}
|
||||
}
|
||||
if(condition_nesting_ > 0) {
|
||||
condition_nesting_--;
|
||||
}
|
||||
schema_validator::close_tag();
|
||||
}
|
||||
|
||||
bool schema_self_validator::tag_path_exists(const config& cfg, const reference& ref) {
|
||||
std::vector<std::string> path = utils::split(ref.value_, '/');
|
||||
std::string suffix = path.back();
|
||||
path.pop_back();
|
||||
while(!path.empty()) {
|
||||
std::string prefix = utils::join(path, "/");
|
||||
auto link = links_.find(prefix);
|
||||
if(link != links_.end()) {
|
||||
std::string new_path = link->second + "/" + suffix;
|
||||
if(defined_tag_paths_.count(new_path) > 0) {
|
||||
return true;
|
||||
}
|
||||
path = utils::split(new_path, '/');
|
||||
suffix = path.back();
|
||||
//suffix = link->second + "/" + suffix;
|
||||
} else {
|
||||
auto supers = derivations_.equal_range(prefix);
|
||||
if(supers.first != supers.second) {
|
||||
reference super_ref = ref;
|
||||
for( ; supers.first != supers.second; ++supers.first) {
|
||||
super_ref.value_ = supers.first->second + "/" + suffix;
|
||||
if(tag_path_exists(cfg, super_ref)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
std::string new_path = prefix + "/" + suffix;
|
||||
if(defined_tag_paths_.count(new_path) > 0) {
|
||||
return true;
|
||||
}
|
||||
suffix = path.back() + "/" + suffix;
|
||||
}
|
||||
path.pop_back();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void schema_self_validator::validate(const config& cfg, const std::string& name, int start_line, const std::string& file)
|
||||
{
|
||||
if(type_nesting_ == 1 && name == "type") {
|
||||
defined_types_.insert(cfg["name"]);
|
||||
} else if(name == "wml_schema") {
|
||||
using namespace std::placeholders;
|
||||
std::vector<reference> missing_types = referenced_types_, missing_tags = referenced_tag_paths_;
|
||||
// Remove all the known types
|
||||
missing_types.erase(std::remove_if(missing_types.begin(), missing_types.end(), std::bind(&reference::match, _1, std::cref(defined_types_))), missing_types.end());
|
||||
// Remove all the known tags. This is more complicated since links behave similar to a symbolic link.
|
||||
// In other words, the presence of links means there may be more than one way to refer to a given tag.
|
||||
// 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.
|
||||
auto end = std::remove_if(missing_tags.begin(), missing_tags.end(), std::bind(&reference::match, _1, std::cref(defined_tag_paths_)));
|
||||
missing_tags.erase(std::remove_if(missing_tags.begin(), end, std::bind(&schema_self_validator::tag_path_exists, this, std::ref(cfg), _1)), missing_tags.end());
|
||||
std::sort(missing_types.begin(), missing_types.end());
|
||||
std::sort(missing_tags.begin(), missing_tags.end());
|
||||
static const config dummy;
|
||||
for(auto& ref : missing_types) {
|
||||
std::string name;
|
||||
if(ref.tag_ == "key") {
|
||||
name = "type";
|
||||
} else {
|
||||
name = "link";
|
||||
}
|
||||
queue_message(dummy, WRONG_TYPE, ref.file_, ref.line_, 0, ref.tag_, name, ref.value_);
|
||||
}
|
||||
for(auto& ref : missing_tags) {
|
||||
std::string name;
|
||||
if(ref.tag_ == "tag") {
|
||||
name = "super";
|
||||
} else if(ref.tag_ == "link") {
|
||||
name = "name";
|
||||
}
|
||||
queue_message(dummy, WRONG_PATH, ref.file_, ref.line_, 0, ref.tag_, name, ref.value_);
|
||||
}
|
||||
}
|
||||
schema_validator::validate(cfg, name, start_line, file);
|
||||
}
|
||||
|
||||
void schema_self_validator::validate_key(const config& cfg, const std::string& name, const std::string& value, int start_line, const std::string& file)
|
||||
{
|
||||
schema_validator::validate_key(cfg, name, value, start_line, file);
|
||||
if(have_active_tag() && !active_tag().get_name().empty() && is_valid()) {
|
||||
const std::string& tag_name = active_tag().get_name();
|
||||
if(tag_name == "key" && name == "type" ) {
|
||||
for(auto& possible_type : utils::split(cfg["type"])) {
|
||||
referenced_types_.emplace_back(possible_type, file, start_line, tag_name);
|
||||
}
|
||||
} else if((tag_name == "type" || tag_name == "element") && name == "link") {
|
||||
referenced_types_.emplace_back(cfg["link"], file, start_line, tag_name);
|
||||
} else if(tag_name == "link" && name == "name") {
|
||||
referenced_tag_paths_.emplace_back(cfg["name"], file, start_line, tag_name);
|
||||
std::string link_name = utils::split(cfg["name"], '/').back();
|
||||
links_.emplace(current_path() + "/" + link_name, cfg["name"]);
|
||||
} else if(tag_name == "tag" && name == "super") {
|
||||
for(auto super : utils::split(cfg["super"])) {
|
||||
referenced_tag_paths_.emplace_back(super, file, start_line, tag_name);
|
||||
derivations_.emplace(std::make_pair(current_path(), super));
|
||||
}
|
||||
} else if(condition_nesting_ == 0 && tag_name == "tag" && name == "name") {
|
||||
tag_stack_.top() = value;
|
||||
defined_tag_paths_.insert(current_path());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::string schema_self_validator::current_path() const
|
||||
{
|
||||
std::stack<std::string> temp = tag_stack_;
|
||||
std::deque<std::string> path;
|
||||
while(!temp.empty()) {
|
||||
path.push_front(temp.top());
|
||||
temp.pop();
|
||||
}
|
||||
if(path.front() == "root") {
|
||||
path.pop_front();
|
||||
}
|
||||
return utils::join(path, "/");
|
||||
}
|
||||
|
||||
bool schema_self_validator::reference::operator<(const reference& other)
|
||||
{
|
||||
return std::make_tuple(file_, line_) < std::make_tuple(other.file_, other.line_);
|
||||
}
|
||||
|
||||
bool schema_self_validator::reference::match(const std::set<std::string>& with)
|
||||
{
|
||||
return with.count(value_) > 0;
|
||||
}
|
||||
|
||||
bool schema_self_validator::reference::can_find(const class_tag& root, const config& cfg)
|
||||
{
|
||||
// The problem is that the schema being validated is that of the schema!!!
|
||||
return root.find_tag(value_, root, cfg) != nullptr;
|
||||
}
|
||||
|
||||
} // namespace schema_validation{
|
||||
|
|
|
@ -44,7 +44,7 @@ public:
|
|||
* Initializes validator from file.
|
||||
* Throws abstract_validator::error if any error.
|
||||
*/
|
||||
schema_validator(const std::string& filename);
|
||||
schema_validator(const std::string& filename, bool validate_schema = false);
|
||||
|
||||
void set_create_exceptions(bool value)
|
||||
{
|
||||
|
@ -75,7 +75,7 @@ private:
|
|||
typedef std::stack<cnt_map> cnt_stack;
|
||||
|
||||
protected:
|
||||
enum message_type{ WRONG_TAG, EXTRA_TAG, MISSING_TAG, EXTRA_KEY, MISSING_KEY, WRONG_VALUE, WRONG_TYPE };
|
||||
enum message_type{ WRONG_TAG, EXTRA_TAG, MISSING_TAG, EXTRA_KEY, MISSING_KEY, WRONG_VALUE, WRONG_TYPE, WRONG_PATH };
|
||||
private:
|
||||
// error_cache
|
||||
|
||||
|
@ -137,6 +137,8 @@ private:
|
|||
|
||||
/** Type validators. */
|
||||
class_type::map types_;
|
||||
|
||||
bool validate_schema_;
|
||||
protected:
|
||||
void queue_message(const config& cfg, 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 class_tag& active_tag() const;
|
||||
|
@ -145,4 +147,39 @@ protected:
|
|||
bool is_valid() const {return config_read_;}
|
||||
class_type::ptr find_type(const std::string& type) const;
|
||||
};
|
||||
|
||||
// A validator specifically designed for validating a schema
|
||||
// In addition to the usual, it verifies that references within the schema are valid, for example via the [link] tag
|
||||
class schema_self_validator : public schema_validator
|
||||
{
|
||||
public:
|
||||
schema_self_validator();
|
||||
virtual void open_tag(const std::string& name, const config& parent, int start_line = 0, const std::string& file = "", bool addition = false) override;
|
||||
virtual void close_tag() override;
|
||||
virtual void validate(const config& cfg, const std::string& name, int start_line, const std::string& file) override;
|
||||
virtual void validate_key(const config& cfg, const std::string& name, const std::string& value, int start_line, const std::string& file) override;
|
||||
private:
|
||||
struct reference {
|
||||
reference(const std::string& value, const std::string& file, int line, const std::string& tag)
|
||||
: value_(value)
|
||||
, file_(file)
|
||||
, tag_(tag)
|
||||
, line_(line)
|
||||
{}
|
||||
std::string value_, file_, tag_;
|
||||
int line_;
|
||||
bool match(const std::set<std::string>& with);
|
||||
bool can_find(const class_tag& root, const config& cfg);
|
||||
bool operator<(const reference& other);
|
||||
};
|
||||
std::string current_path() const;
|
||||
std::set<std::string> defined_types_, defined_tag_paths_;
|
||||
std::vector<reference> referenced_types_, referenced_tag_paths_;
|
||||
std::stack<std::string> tag_stack_;
|
||||
std::map<std::string, std::string> links_;
|
||||
std::multimap<std::string, std::string> derivations_;
|
||||
int type_nesting_, condition_nesting_;
|
||||
bool tag_path_exists(const config& cfg, const reference& ref);
|
||||
};
|
||||
|
||||
} // namespace schema_validation{
|
||||
|
|
Loading…
Add table
Reference in a new issue