Some schema code reformatting as requested by @celticminstrel

This commit is contained in:
Charles Dang 2018-03-05 16:45:14 +11:00
parent 579bbc464f
commit 596edaac00
4 changed files with 663 additions and 546 deletions

View file

@ -14,106 +14,112 @@
#include "serialization/schema_validator.hpp"
#include "filesystem.hpp"
#include "gettext.hpp"
#include "log.hpp"
#include "serialization/preprocessor.hpp"
#include "wml_exception.hpp"
namespace schema_validation{
namespace schema_validation
{
static lg::log_domain log_validation("validation");
#define ERR_VL LOG_STREAM(err, log_validation)
#define WRN_VL LOG_STREAM(warn, log_validation)
#define LOG_VL LOG_STREAM(info, log_validation)
static std::string at(const std::string & file, int line){
static std::string at(const std::string& file, int line)
{
std::ostringstream ss;
ss << line << " " << file;
return "at " + ::lineno_string(ss.str());
}
static void print_output(const std::string & message,bool flag_exception = false ){
static void print_output(const std::string& message, bool flag_exception = false)
{
#ifndef VALIDATION_ERRORS_LOG
if(flag_exception){
throw wml_exception("Validation error occurred",message);
}else{
ERR_VL << message;
}
if(flag_exception) {
throw wml_exception("Validation error occurred", message);
} else {
ERR_VL << message;
}
#else
// dirty hack to avoid "unused" error in case of compiling with definition on
// dirty hack to avoid "unused" error in case of compiling with definition on
flag_exception = true;
if (flag_exception){ ERR_VL << message;}
if(flag_exception) {
ERR_VL << message;
}
#endif
}
static void extra_tag_error(const std::string & file, int line,
const std::string & name,int n,
const std::string & parent, bool flag_exception){
static void extra_tag_error(const std::string& file,
int line,
const std::string& name,
int n,
const std::string& parent,
bool flag_exception)
{
std::ostringstream ss;
ss << "Extra tag [" << name << "]; there may only be "
<< n << " [" << name << "] in [" << parent << "]\n"
ss << "Extra tag [" << name << "]; there may only be " << n << " [" << name << "] in [" << parent << "]\n"
<< at(file, line) << "\n";
print_output (ss.str (),flag_exception);
print_output(ss.str(), flag_exception);
}
static void wrong_tag_error(const std::string & file, int line,
const std::string & name,const std::string & parent,
bool flag_exception){
static void wrong_tag_error(
const std::string& file, int line, const std::string& name, const std::string& parent, bool flag_exception)
{
std::ostringstream ss;
ss << "Tag [" << name << "] may not be used in ["
<< parent << "]\n"
<< at(file, line) << "\n";
print_output (ss.str (),flag_exception);
ss << "Tag [" << name << "] may not be used in [" << parent << "]\n" << at(file, line) << "\n";
print_output(ss.str(), flag_exception);
}
static void missing_tag_error(const std::string & file, int line,
const std::string & name,int n,
const std::string & parent, bool flag_exception){
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::ostringstream ss;
ss << "Missing tag [" << name << "]; there must be "
<< n << " [" << name << "]s in [" << parent << "]\n"
ss << "Missing tag [" << name << "]; there must be " << n << " [" << name << "]s in [" << parent << "]\n"
<< at(file, line) << "\n";
print_output (ss.str (),flag_exception);
print_output(ss.str(), flag_exception);
}
static void extra_key_error(const std::string & file, int line,
const std::string & tag,const std::string & key,
bool flag_exception){
static void extra_key_error(
const std::string& file, int line, const std::string& tag, const std::string& key, bool flag_exception)
{
std::ostringstream ss;
ss << "Invalid key '" << key << "=' in tag [" << tag
<< "]\n"
<< at(file, line) << "\n";
print_output (ss.str (),flag_exception);
ss << "Invalid key '" << key << "=' in tag [" << tag << "]\n" << at(file, line) << "\n";
print_output(ss.str(), flag_exception);
}
static void missing_key_error(const std::string & file, int line,
const std::string & tag,const std::string & key,
bool flag_exception){
static void missing_key_error(
const std::string& file, int line, const std::string& tag, const std::string& key, bool flag_exception)
{
std::ostringstream ss;
ss << "Missing key '" << key << "=' in tag [" << tag
<< "]\n"
<< at(file, line) << "\n";
print_output (ss.str (),flag_exception);
ss << "Missing key '" << key << "=' in tag [" << tag << "]\n" << at(file, line) << "\n";
print_output(ss.str(), flag_exception);
}
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){
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)
{
std::ostringstream ss;
ss << "Invalid value '" << value << "' in key '" << key
<< "=' in tag [" << tag << "]\n"
<< at(file, line) << "\n";
print_output (ss.str (),flag_exception);
ss << "Invalid value '" << value << "' in key '" << key << "=' in tag [" << tag << "]\n" << at(file, line) << "\n";
print_output(ss.str(), flag_exception);
}
schema_validator::~schema_validator()
{
}
schema_validator::~schema_validator(){}
schema_validator::schema_validator(const std::string & config_file_name)
schema_validator::schema_validator(const std::string& config_file_name)
: config_read_(false)
, create_exceptions_(strict_validation_enabled)
, root_()
@ -122,44 +128,44 @@ schema_validator::schema_validator(const std::string & config_file_name)
, cache_()
, types_()
{
if ( !read_config_file(config_file_name) ) {
ERR_VL << "Schema file "<< config_file_name << " was not read." << std::endl;
throw abstract_validator::error("Schema file "+ config_file_name
+ " was not read.\n");
}else{
if(!read_config_file(config_file_name)) {
ERR_VL << "Schema file " << config_file_name << " was not read." << std::endl;
throw abstract_validator::error("Schema file " + config_file_name + " was not read.\n");
} else {
stack_.push(&root_);
counter_.emplace();
cache_.emplace();
root_.expand_all(root_);
LOG_VL << "Schema file "<< config_file_name << " was read.\n"
<< "Validator initialized\n";
LOG_VL << "Schema file " << config_file_name << " was read.\n"
<< "Validator initialized\n";
}
}
bool schema_validator::read_config_file(const std::string &filename){
bool schema_validator::read_config_file(const std::string& filename)
{
config cfg;
try {
preproc_map preproc(
game_config::config_cache::instance().get_preproc_map());
preproc_map preproc(game_config::config_cache::instance().get_preproc_map());
filesystem::scoped_istream stream = preprocess_file(filename, &preproc);
read(cfg, *stream);
} catch(config::error& e) {
ERR_VL << "Failed to read file "<< filename << ":\n" << e.what() << "\n";
ERR_VL << "Failed to read file " << filename << ":\n" << e.what() << "\n";
return false;
}
for(const config &g : cfg.child_range("wml_schema")) {
for(const config &schema : g.child_range("tag")) {
if (schema["name"].str() == "root"){
for(const config& g : cfg.child_range("wml_schema")) {
for(const config& schema : g.child_range("tag")) {
if(schema["name"].str() == "root") {
//@NOTE Don't know, maybe merging of roots needed.
root_ = class_tag (schema);
root_ = class_tag(schema);
}
}
for(const config &type : g.child_range("type")) {
try{
types_[type["name"].str()] = boost::regex( type["value"].str());
}
catch (std::exception&){
// Need to check all type values in schema-generator
for(const config& type : g.child_range("type")) {
try {
types_[type["name"].str()] = boost::regex(type["value"].str());
} catch(std::exception&) {
// Need to check all type values in schema-generator
}
}
}
@ -167,141 +173,140 @@ bool schema_validator::read_config_file(const std::string &filename){
config_read_ = true;
return true;
}
/*
* Please, @Note that there is some magic in pushing and poping to/from stacks.
* assume they all are on their place due to parser algorithm
* and validation logic
*/
void schema_validator::open_tag(const std::string & name,
int start_line,
const std::string &file,
bool addittion){
if (! stack_.empty()){
const class_tag * tag = nullptr;
if (stack_.top()){
tag = stack_.top()->find_tag(name,root_);
if (! tag){
wrong_tag_error(file,start_line,name,stack_.top()->get_name(),
create_exceptions_);
}else{
if (! addittion){
counter & cnt = counter_.top()[name];
++ cnt.cnt;
void schema_validator::open_tag(const std::string& name, int start_line, const std::string& file, bool addittion)
{
if(!stack_.empty()) {
const class_tag* tag = nullptr;
if(stack_.top()) {
tag = stack_.top()->find_tag(name, root_);
if(!tag) {
wrong_tag_error(file, start_line, name, stack_.top()->get_name(), create_exceptions_);
} else {
if(!addittion) {
counter& cnt = counter_.top()[name];
++cnt.cnt;
}
}
}
stack_.push(tag);
}else{
} else {
stack_.push(nullptr);
}
counter_.emplace();
cache_.emplace();
}
void schema_validator::close_tag(){
void schema_validator::close_tag()
{
stack_.pop();
counter_.pop();
//cache_ is cleared in another place.
// cache_ is cleared in another place.
}
void schema_validator::validate(const config & cfg, const std::string & name,
int start_line,
const std::string &file){
//close previous errors and print them to output.
void schema_validator::validate(const config& cfg, const std::string& name, int start_line, const std::string& file)
{
// close previous errors and print them to output.
message_map::iterator cache_it = cache_.top().begin();
for (;cache_it != cache_.top().end();++cache_it){
for (message_list::iterator i = cache_it->second.begin();
i != cache_it->second.end(); ++i){
for(; cache_it != cache_.top().end(); ++cache_it) {
for(message_list::iterator i = cache_it->second.begin(); i != cache_it->second.end(); ++i) {
print(*i);
}
}
cache_.pop();
// clear cache
cache_it = cache_.top().find(&cfg);
if (cache_it != cache_.top().end()){
if(cache_it != cache_.top().end()) {
cache_it->second.clear();
}
// Please note that validating unknown tag keys the result will be false
// Checking all elements counters.
if (!stack_.empty() && stack_.top() && config_read_){
if(!stack_.empty() && stack_.top() && config_read_) {
class_tag::all_const_tag_iterators p = stack_.top()->tags();
for (class_tag::const_tag_iterator tag = p.first;
tag != p.second ; ++tag){
for(class_tag::const_tag_iterator tag = p.first; tag != p.second; ++tag) {
int cnt = counter_.top()[tag->first].cnt;
if (tag->second.get_min() > cnt){
if(tag->second.get_min() > cnt) {
cache_.top()[&cfg].emplace_back(
MISSING_TAG,file,start_line,tag->second.get_min(),tag->first,"",name);
MISSING_TAG, file, start_line, tag->second.get_min(), tag->first, "", name);
continue;
}
if (tag->second.get_max() < cnt){
if(tag->second.get_max() < cnt) {
cache_.top()[&cfg].emplace_back(
EXTRA_TAG,file,start_line,tag->second.get_max(),tag->first,"",name);
EXTRA_TAG, file, start_line, tag->second.get_max(), tag->first, "", name);
}
}
// Checking if all mandatory keys are present
class_tag::all_const_key_iterators k = stack_.top()->keys();
for (class_tag::const_key_iterator key = k.first;
key != k.second ; ++key){
if (key->second.is_mandatory()){
if (cfg.get(key->first) == nullptr){
cache_.top()[&cfg].emplace_back(
MISSING_KEY,file,start_line,0,name,key->first );
for(class_tag::const_key_iterator key = k.first; key != k.second; ++key) {
if(key->second.is_mandatory()) {
if(cfg.get(key->first) == nullptr) {
cache_.top()[&cfg].emplace_back(MISSING_KEY, file, start_line, 0, name, key->first);
}
}
}
}
}
void schema_validator::validate_key(const config & cfg,
const std::string & name,
const std::string & value,
int start_line,
const std::string &file){
if (!stack_.empty() && stack_.top() && config_read_){
void schema_validator::validate_key(
const config& cfg, const std::string& name, const std::string& value, int start_line, const std::string& file)
{
if(!stack_.empty() && stack_.top() && config_read_) {
// checking existing keys
const class_key * key =stack_.top()->find_key(name);
if (key){
std::map<std::string,boost::regex>::iterator itt =
types_.find(key->get_type());
if (itt != types_.end()){
const class_key* key = stack_.top()->find_key(name);
if(key) {
std::map<std::string, boost::regex>::iterator itt = types_.find(key->get_type());
if(itt != types_.end()) {
boost::smatch sub;
bool res = boost::regex_match(value,sub,itt->second);
if (!res ) {
bool res = boost::regex_match(value, sub, itt->second);
if(!res) {
cache_.top()[&cfg].emplace_back(
WRONG_VALUE,file,start_line,0,stack_.top()->get_name(),name,value);
WRONG_VALUE, file, start_line, 0, stack_.top()->get_name(), name, value);
}
}
} else {
cache_.top()[&cfg].emplace_back(EXTRA_KEY, file, start_line, 0, stack_.top()->get_name(), name);
}
else{
cache_.top()[&cfg].emplace_back(
EXTRA_KEY,file,start_line,0, stack_.top()->get_name(),name);
}
}
}
void schema_validator::print(message_info & el){
switch (el.type){
void schema_validator::print(message_info& el)
{
switch(el.type) {
case WRONG_TAG:
wrong_tag_error(el.file,el.line,el.tag,el.value,create_exceptions_);
wrong_tag_error(el.file, el.line, el.tag, el.value, create_exceptions_);
break;
case EXTRA_TAG:
extra_tag_error(el.file,el.line,el.tag,el.n,el.value,create_exceptions_);
extra_tag_error(el.file, el.line, el.tag, el.n, el.value, create_exceptions_);
break;
case MISSING_TAG:
missing_tag_error(el.file,el.line,el.tag,el.n,el.value,
create_exceptions_);
missing_tag_error(el.file, el.line, el.tag, el.n, el.value, create_exceptions_);
break;
case EXTRA_KEY:
extra_key_error(el.file,el.line,el.tag,el.key,create_exceptions_);
extra_key_error(el.file, el.line, el.tag, el.key, create_exceptions_);
break;
case WRONG_VALUE:
wrong_value_error(el.file,el.line,el.tag,el.key,el.value,
create_exceptions_);
wrong_value_error(el.file, el.line, el.tag, el.key, el.value, create_exceptions_);
break;
case MISSING_KEY:
missing_key_error(el.file,el.line,el.tag,el.key,create_exceptions_);
missing_key_error(el.file, el.line, el.tag, el.key, create_exceptions_);
}
}
}//namespace schema_validation{
} // namespace schema_validation{

View file

@ -14,85 +14,87 @@
#pragma once
#include "serialization/validator.hpp"
#include "serialization/tag.hpp"
#include "config_cache.hpp"
#include "serialization/parser.hpp"
#include "serialization/tag.hpp"
#include "serialization/validator.hpp"
#include <boost/regex.hpp>
#include <iostream>
#include <queue>
#include <string>
#include <stack>
#include <string>
class config;
/** @file
* One of the realizations of serialization/validator.hpp abstract validator.
*/
namespace schema_validation{
namespace schema_validation
{
/**
* Realization of serialization/validator.hpp abstract validator.
* Based on stack. Uses some stacks to store different info.
*/
class schema_validator : public abstract_validator{
class schema_validator : public abstract_validator
{
public:
virtual ~schema_validator();
/**
* Initializes validator from file.
* Throws abstract_validator::error if any error.
*/
schema_validator(const std::string & filename);
void set_create_exceptions(bool value){
schema_validator(const std::string& filename);
void set_create_exceptions(bool value)
{
create_exceptions_ = value;
}
virtual void open_tag(const std::string & name,
int start_line=0,
const std::string &file="",
bool addittion = false);
virtual void open_tag(
const std::string& name, int start_line = 0, const std::string& file = "", bool addittion = false);
virtual void close_tag();
virtual void validate(const config & cfg,
const std::string & name,
int start_line,
const std::string &file);
virtual void validate_key(const config & cfg,
const std::string & name,
const std::string & value,
int start_line,
const std::string &file);
private:
// types section
// Just some magic to ensure zero initialization.
struct counter{
int cnt;
counter(): cnt(0){}
};
/**
* Counters are mapped by tag name
*/
typedef std::map<std::string,counter> cnt_map;
virtual void validate(const config& cfg, const std::string& name, int start_line, const std::string& file);
virtual void validate_key(const config& cfg,
const std::string& name,
const std::string& value,
int start_line,
const std::string& file);
/**
* And counter maps are organize in stack.
*/
private:
// types section
// Just some magic to ensure zero initialization.
struct counter
{
int cnt;
counter()
: cnt(0)
{
}
};
/** Counters are mapped by tag name. */
typedef std::map<std::string, counter> cnt_map;
/** And counter maps are organize in stack. */
typedef std::stack<cnt_map> cnt_stack;
enum message_type{WRONG_TAG,EXTRA_TAG,MISSING_TAG,
EXTRA_KEY,MISSING_KEY,WRONG_VALUE};
//error_cache
enum message_type { WRONG_TAG, EXTRA_TAG, MISSING_TAG, EXTRA_KEY, MISSING_KEY, WRONG_VALUE };
// error_cache
/**
* Messages are cached.
* The reason is next: in file where [tag]...[/tag][+tag]...[/tag]
* config object will be validated each [/tag]. So message can be as wrong
* (when [+tag] section contains missed elements) as duplicated.
*
* Messages are mapped by config*. That ensures uniqueness.
* Also message-maps are organized in stack to avoid memory leaks.
*/
struct message_info{
* Messages are cached.
* The reason is next: in file where [tag]...[/tag][+tag]...[/tag]
* config object will be validated each [/tag]. So message can be as wrong
* (when [+tag] section contains missed elements) as duplicated.
*
* Messages are mapped by config*. That ensures uniqueness.
* Also message-maps are organized in stack to avoid memory leaks.
*/
struct message_info
{
message_type type;
std::string file;
int line;
@ -100,49 +102,51 @@ private:
std::string tag;
std::string key;
std::string value;
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 = "")
:type(t),file(file),line(line),n(n),tag(tag),key(key),
value(value){}
};
typedef std::deque<message_info> message_list;
typedef std::map<const config *, message_list> message_map;
void print(message_info &);
/**
* Reads config from input.
*/
bool read_config_file(const std::string & filename);
/**
* Shows, if validator is initialized with schema file;
*/
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 = "")
: type(t)
, file(file)
, line(line)
, n(n)
, tag(tag)
, key(key)
, value(value)
{
}
};
typedef std::deque<message_info> message_list;
typedef std::map<const config*, message_list> message_map;
void print(message_info&);
/** Reads config from input. */
bool read_config_file(const std::string& filename);
/** Shows, if validator is initialized with schema file. */
bool config_read_;
/**
* Controls the way to print errors.
*/
/** Controls the way to print errors. */
bool create_exceptions_;
/**
* Root of schema information
*/
/** Root of schema information. */
class_tag root_;
std::stack<const class_tag *> stack_;
/**
* Contains number of children
*/
std::stack<const class_tag*> stack_;
/** Contains number of children. */
cnt_stack counter_;
/**
* Caches error messages.
*/
/** Caches error messages. */
std::stack<message_map> cache_;
/**
* Type validators.
*/
std::map<std::string,boost::regex> types_;
/** Type validators. */
std::map<std::string, boost::regex> types_;
};
}//namespace schema_validation{
} // namespace schema_validation{

View file

@ -21,8 +21,8 @@
#include "config.hpp"
namespace schema_validation{
namespace schema_validation
{
/*WIKI
* @begin{parent}{name="wml_schema/tag/"}
* @begin{tag}{name="key"}{min=0}{max=-1}
@ -36,36 +36,40 @@ namespace schema_validation{
* @end{parent}{name="wml_schema/tag/"}
*/
class_key::class_key(const config & cfg)
: name_(cfg["name"].str())
class_key::class_key(const config& cfg)
: name_(cfg["name"].str())
, type_(cfg["type"].str())
, default_()
, mandatory_(false)
{
if (cfg.has_attribute("mandatory")){
mandatory_ = cfg["mandatory"].to_bool();
}else{
if (cfg.has_attribute("default")){
default_= cfg["default"].str();
}
}
}
void class_key::print(std::ostream& os,int level) const {
{
if(cfg.has_attribute("mandatory")) {
mandatory_ = cfg["mandatory"].to_bool();
} else {
if(cfg.has_attribute("default")) {
default_ = cfg["default"].str();
}
}
}
void class_key::print(std::ostream& os, int level) const
{
std::string s;
for (int j=0;j<level;j++){
for(int j = 0; j < level; j++) {
s.append(" ");
}
os << s << "[key]\n"
<< s << " name=\""<< name_ <<"\"\n"
<< s << " type=\""<< type_ <<"\"\n";
if (is_mandatory()){
os << s << "[key]\n" << s << " name=\"" << name_ << "\"\n" << s << " type=\"" << type_ << "\"\n";
if(is_mandatory()) {
os << s << " mandatory=\"true\"\n";
}else{
os << s <<" default="<< default_ <<"\n";
} else {
os << s << " default=" << default_ << "\n";
}
os << s << "[/key]\n";
}
class_tag::class_tag(const config & cfg)
class_tag::class_tag(const config& cfg)
: name_(cfg["name"].str())
, min_(cfg["min"].to_int())
, max_(cfg["max"].to_int())
@ -74,89 +78,108 @@ class_tag::class_tag(const config & cfg)
, keys_()
, links_()
{
if (max_ < 0){
max_ = INT_MAX;
}
if (cfg.has_attribute("super")){
super_ = cfg["super"].str();
}
for (const config &child : cfg.child_range("tag")) {
class_tag child_tag (child);
add_tag(child_tag);
}
for (const config &child : cfg.child_range("key")) {
class_key child_key (child);
add_key(child_key);
}
for (const config &link : cfg.child_range("link")) {
std::string link_name = link["name"].str();
add_link(link_name);
}
if(max_ < 0) {
max_ = INT_MAX;
}
if(cfg.has_attribute("super")) {
super_ = cfg["super"].str();
}
for(const config& child : cfg.child_range("tag")) {
class_tag child_tag(child);
add_tag(child_tag);
}
for(const config& child : cfg.child_range("key")) {
class_key child_key(child);
add_key(child_key);
}
for(const config& link : cfg.child_range("link")) {
std::string link_name = link["name"].str();
add_link(link_name);
}
}
void class_tag::print(std::ostream& os){
printl(os,4,4);
void class_tag::print(std::ostream& os)
{
printl(os, 4, 4);
}
void class_tag::add_link(const std::string &link){
void class_tag::add_link(const std::string& link)
{
std::string::size_type pos_last = link.rfind('/');
//if (pos_last == std::string::npos) return;
std::string name_link = link.substr(pos_last+1,link.length());
// if (pos_last == std::string::npos) return;
std::string name_link = link.substr(pos_last + 1, link.length());
links_.emplace(name_link, link);
}
const class_key * class_tag::find_key(const std::string &name) const{
const class_key* class_tag::find_key(const std::string& name) const
{
key_map::const_iterator it_keys = keys_.find(name);
if ( it_keys!= keys_.end() ){
if(it_keys != keys_.end()) {
return &(it_keys->second);
}
return nullptr;
}
const std::string * class_tag::find_link(const std::string &name) const{
const std::string* class_tag::find_link(const std::string& name) const
{
link_map::const_iterator it_links = links_.find(name);
if ( it_links!= links_.end() ){
if(it_links != links_.end()) {
return &(it_links->second);
}
return nullptr;
}
const class_tag * class_tag::find_tag(const std::string &fullpath,
const class_tag &root) const{
if (fullpath.empty()) return nullptr;
std::string::size_type pos = fullpath.find('/');
std::string name;
std::string next_path;
if (pos != std::string::npos) {
name = fullpath.substr(0,pos);
next_path = fullpath.substr(pos+1,fullpath.length());
}else{
name = fullpath;
}
tag_map::const_iterator it_tags = tags_.find(name);
if (it_tags != tags_.end()){
if (next_path.empty()){
return &(it_tags->second);
}else{
return it_tags->second.find_tag(next_path,root);
}
}
link_map::const_iterator it_links = links_.find(name);
if (it_links != links_.end()){
return root.find_tag(it_links->second + "/" +next_path,root);
}
return nullptr;
const class_tag* class_tag::find_tag(const std::string& fullpath, const class_tag& root) const
{
if(fullpath.empty()) {
return nullptr;
}
}
std::string::size_type pos = fullpath.find('/');
std::string name;
std::string next_path;
void class_tag::expand_all(class_tag &root){
for (tag_map::iterator i = tags_.begin(); i!= tags_.end(); ++i){
if(pos != std::string::npos) {
name = fullpath.substr(0, pos);
next_path = fullpath.substr(pos + 1, fullpath.length());
} else {
name = fullpath;
}
tag_map::const_iterator it_tags = tags_.find(name);
if(it_tags != tags_.end()) {
if(next_path.empty()) {
return &(it_tags->second);
} else {
return it_tags->second.find_tag(next_path, root);
}
}
link_map::const_iterator it_links = links_.find(name);
if(it_links != links_.end()) {
return root.find_tag(it_links->second + "/" + next_path, root);
}
return nullptr;
}
void class_tag::expand_all(class_tag& root)
{
for(tag_map::iterator i = tags_.begin(); i != tags_.end(); ++i) {
i->second.expand(root);
i->second.expand_all(root);
}
}
void class_tag::remove_keys_by_type (const std::string & type){
key_iterator i= keys_.begin ();
void class_tag::remove_keys_by_type(const std::string& type)
{
key_iterator i = keys_.begin();
while(i != keys_.end()) {
if(i->second.get_type() == type) {
keys_.erase(i++);
@ -164,7 +187,8 @@ void class_tag::remove_keys_by_type (const std::string & type){
++i;
}
}
for (tag_iterator t = tags_.begin(); t!=tags_.end();++t){
for(tag_iterator t = tags_.begin(); t != tags_.end(); ++t) {
t->second.remove_keys_by_type(type);
}
}
@ -194,134 +218,158 @@ void class_tag::remove_keys_by_type (const std::string & type){
* @end{tag}{name="type"}
* @end{parent}{name="wml_schema/"}
*/
void class_tag::printl(std::ostream &os,int level, int step){
void class_tag::printl(std::ostream& os, int level, int step)
{
std::string s;
for (int j=0;j<level;j++){
for(int j = 0; j < level; j++) {
s.append(" ");
}
os << s << "[tag]\n"
<< s <<" name=\""<< name_ <<"\"\n"
<< s <<" min=\""<< min_ <<"\"\n"
<< s <<" max=\""<< max_ <<"\"\n";
if (! super_.empty() ){
os<< s <<" super=\""<< super_ <<"\"\n";
<< s << " name=\"" << name_ << "\"\n"
<< s << " min=\"" << min_ << "\"\n"
<< s << " max=\"" << max_ << "\"\n";
if(!super_.empty()) {
os << s << " super=\"" << super_ << "\"\n";
}
for (tag_map::iterator i = tags_.begin();
i != tags_.end(); ++i ){
i->second.printl(os,level+step,step);
for(tag_map::iterator i = tags_.begin(); i != tags_.end(); ++i) {
i->second.printl(os, level + step, step);
}
for (link_map::iterator i = links_.begin();
i != links_.end(); ++i ){
os << s << "" << "[link]\n"
<< s << "" << " name=\"" <<i->second << "\"\n"
<< s << "" << "[/link]\n";
for(link_map::iterator i = links_.begin(); i != links_.end(); ++i) {
os << s << ""
<< "[link]\n"
<< s << ""
<< " name=\"" << i->second << "\"\n"
<< s << ""
<< "[/link]\n";
}
for (key_map::iterator i = keys_.begin();
i != keys_.end(); ++i ){
i->second.print(os,level+step);
for(key_map::iterator i = keys_.begin(); i != keys_.end(); ++i) {
i->second.print(os, level + step);
}
os<< s << "[/tag]\n";
os << s << "[/tag]\n";
}
class_tag * class_tag::find_tag(const std::string &fullpath,
class_tag &root) {
if (fullpath.empty()) return nullptr;
std::string::size_type pos = fullpath.find('/');
std::string name;
std::string next_path;
if (pos != std::string::npos) {
name = fullpath.substr(0,pos);
next_path = fullpath.substr(pos+1,fullpath.length());
}else{
name = fullpath;
}
class_tag* class_tag::find_tag(const std::string& fullpath, class_tag& root)
{
if(fullpath.empty()) {
return nullptr;
}
tag_map::iterator it_tags = tags_.find(name);
if (it_tags != tags_.end()){
if (next_path.empty()){
return &(it_tags->second);
}else{
return it_tags->second.find_tag(next_path,root);
}
}
link_map::iterator it_links = links_.find(name);
if (it_links != links_.end()){
return root.find_tag(it_links->second +"/" +next_path,root);
}
return nullptr;
std::string::size_type pos = fullpath.find('/');
std::string name;
std::string next_path;
}
// class_tag & class_tag::operator= (const class_tag& t){
// if (&t != this){
// name_ = t.name_;
// min_ = t.min_;
// max_ = t.max_;
// super_ = t.super_;
// tags_ = t.tags_;
// keys_ = t.keys_;
// links_ = t.links_;
// }
// return *this;
// }
if(pos != std::string::npos) {
name = fullpath.substr(0, pos);
next_path = fullpath.substr(pos + 1, fullpath.length());
} else {
name = fullpath;
}
void class_tag::add_tag(const std::string &path, const class_tag &tag,
class_tag &root){
if ( path.empty() || path == "/" ){
tag_map::iterator it_tags = tags_.find(name);
if(it_tags != tags_.end()) {
if(next_path.empty()) {
return &(it_tags->second);
} else {
return it_tags->second.find_tag(next_path, root);
}
}
link_map::iterator it_links = links_.find(name);
if(it_links != links_.end()) {
return root.find_tag(it_links->second + "/" + next_path, root);
}
return nullptr;
}
#if 0
class_tag& class_tag::operator=(const class_tag& t)
{
if(&t != this) {
name_ = t.name_;
min_ = t.min_;
max_ = t.max_;
super_ = t.super_;
tags_ = t.tags_;
keys_ = t.keys_;
links_ = t.links_;
}
return *this;
}
#endif
void class_tag::add_tag(const std::string& path, const class_tag& tag, class_tag& root)
{
if(path.empty() || path == "/") {
tag_map::iterator it = tags_.find(tag.name_);
if (it == tags_.end()){
if(it == tags_.end()) {
tags_.emplace(tag.name_, tag);
}else{
} else {
it->second.set_min(tag.min_);
it->second.set_max(tag.max_);
it->second.add_tags(tag.tags_);
it->second.add_keys(tag.keys_);
it->second.add_links(tag.links_);
}
links_.erase (tag.get_name ());
return ;
}
std::string::size_type pos = path.find('/');
std::string name = path.substr(0,pos);
std::string next_path = path.substr(pos+1,path.length());
link_map::const_iterator it_links= links_.find(name);
if (it_links != links_.end()){
root.add_tag(it_links->second + "/" + next_path,tag,root);
links_.erase(tag.get_name());
return;
}
std::string::size_type pos = path.find('/');
std::string name = path.substr(0, pos);
std::string next_path = path.substr(pos + 1, path.length());
link_map::const_iterator it_links = links_.find(name);
if(it_links != links_.end()) {
root.add_tag(it_links->second + "/" + next_path, tag, root);
}
tag_map::iterator it_tags = tags_.find(name);
if (it_tags == tags_.end()){
if(it_tags == tags_.end()) {
class_tag subtag;
subtag.set_name(name);
subtag.add_tag(next_path,tag,root);
subtag.add_tag(next_path, tag, root);
tags_.emplace(name, subtag);
return;
}
it_tags->second.add_tag(next_path,tag,root);
it_tags->second.add_tag(next_path, tag, root);
}
void class_tag::append_super(const class_tag &tag,const std::string & path){
void class_tag::append_super(const class_tag& tag, const std::string& path)
{
add_keys(tag.keys_);
add_links(tag.links_);
for (tag_map::const_iterator i = tag.tags_.begin();i!=tag.tags_.end();++i){
for(tag_map::const_iterator i = tag.tags_.begin(); i != tag.tags_.end(); ++i) {
links_.erase(i->first);
add_link(path + "/" + i->first);
}
}
void class_tag::expand(class_tag &root){
if (! super_.empty()){
class_tag * super_tag = root.find_tag(super_,root);
if (super_tag){
if (super_tag != this){
void class_tag::expand(class_tag& root)
{
if(!super_.empty()) {
class_tag* super_tag = root.find_tag(super_, root);
if(super_tag) {
if(super_tag != this) {
super_tag->expand(root);
append_super(*super_tag,super_);
append_super(*super_tag, super_);
super_.clear();
}else{
std::cerr << "the same" << super_tag->name_ <<"\n";
} else {
std::cerr << "the same" << super_tag->name_ << "\n";
}
}
}
}
}//namespace schema_generator
} // namespace schema_validation

View file

@ -28,54 +28,76 @@
class config;
namespace schema_validation{
namespace schema_validation
{
/**
* class_key is used to save the information about one key.
* Key has next info: name, type, default value or key is mandatory.
*/
class class_key{
* class_key is used to save the information about one key.
* Key has next info: name, type, default value or key is mandatory.
*/
class class_key
{
public:
class_key():name_(""),type_(""),default_("\"\""),mandatory_(false)
{ }
class_key(const std::string & name,
const std::string &type,
const std::string &def="\"\"")
class_key()
: name_("")
, type_("")
, default_("\"\"")
, mandatory_(false)
{
}
class_key(const std::string& name, const std::string& type, const std::string& def = "\"\"")
: name_(name)
, type_(type)
, default_(def)
, mandatory_(def.empty())
{
}
class_key(const config&);
const std::string & get_name() const{
return name_ ;
}
const std::string & get_type() const{
return type_ ;
}
const std::string & get_default() const{
return default_;
}
bool is_mandatory () const{
return mandatory_ ;
const std::string& get_name() const
{
return name_;
}
void set_name(const std::string& name){
const std::string& get_type() const
{
return type_;
}
const std::string& get_default() const
{
return default_;
}
bool is_mandatory() const
{
return mandatory_;
}
void set_name(const std::string& name)
{
name_ = name;
}
void set_type(const std::string& type){
void set_type(const std::string& type)
{
type_ = type;
}
void set_default(const std::string& def){
void set_default(const std::string& def)
{
default_ = def;
if (def.empty()){
if(def.empty()) {
mandatory_ = true;
}
}
void set_mandatory(bool mandatory){
void set_mandatory(bool mandatory)
{
mandatory_ = mandatory;
}
/** is used to print key info
* the format is next
* [key]
@ -84,65 +106,66 @@ public:
* default="default"
* mandatory="true/false"
* [/key]
*/
void print(std::ostream& os,int level) const;
/**
*Compares keys by name. Used in std::sort, i.e.
*/
bool operator < ( const class_key& k) const{
void print(std::ostream& os, int level) const;
/** Compares keys by name. Used in std::sort, i.e. */
bool operator<(const class_key& k) const
{
return (get_name() < k.get_name());
}
private:
/** Name of key*/
/** Name of key. */
std::string name_;
/** Type of key*/
/** Type of key. */
std::string type_;
/** Default value*/
/** Default value. */
std::string default_;
/** Shows, if key is a mandatory key.*/
/** Shows, if key is a mandatory key. */
bool mandatory_;
};
/**
* Stores information about tag.
* Each tags is an element of great tag tree. This tree is close to filesystem:
* you can use links and special include directory global/
* Normally root is not mentioned in path.
* Each tag has name, minimum and maximum occasions number,
* and lists of subtags, keys and links.
*/
class class_tag{
* Stores information about tag.
* Each tags is an element of great tag tree. This tree is close to filesystem:
* you can use links and special include directory global/
* Normally root is not mentioned in path.
* Each tag has name, minimum and maximum occasions number,
* and lists of subtags, keys and links.
*/
class class_tag
{
public:
typedef std::map<std::string,class_tag> tag_map;
typedef std::pair<std::string,class_tag> tag_map_value;
typedef std::map<std::string, class_tag> tag_map;
typedef std::pair<std::string, class_tag> tag_map_value;
typedef std::map<std::string,class_key> key_map;
typedef std::pair<std::string,class_key> key_map_value;
typedef std::map<std::string, class_key> key_map;
typedef std::pair<std::string, class_key> key_map_value;
typedef std::map<std::string,std::string> link_map;
typedef std::pair<std::string,std::string> link_map_value;
typedef std::map<std::string, std::string> link_map;
typedef std::pair<std::string, std::string> link_map_value;
typedef key_map::iterator key_iterator;
typedef std::pair<key_iterator,key_iterator> all_key_iterators;
typedef std::pair<key_iterator, key_iterator> all_key_iterators;
typedef key_map::const_iterator const_key_iterator;
typedef std::pair<const_key_iterator,const_key_iterator>
all_const_key_iterators;
typedef std::pair<const_key_iterator, const_key_iterator> all_const_key_iterators;
typedef tag_map::iterator tag_iterator;
typedef std::pair<tag_iterator,tag_iterator> all_tag_iterators;
typedef std::pair<tag_iterator, tag_iterator> all_tag_iterators;
typedef tag_map::const_iterator const_tag_iterator;
typedef std::pair<const_tag_iterator,const_tag_iterator>
all_const_tag_iterators;
typedef std::pair<const_tag_iterator, const_tag_iterator> all_const_tag_iterators;
typedef link_map::iterator link_iterator;
typedef std::pair<link_iterator,link_iterator> all_link_iterators;
typedef std::pair<link_iterator, link_iterator> all_link_iterators;
typedef link_map::const_iterator const_link_iterator;
typedef std::pair<const_link_iterator,const_link_iterator>
all_const_link_iterators;
typedef std::pair<const_link_iterator, const_link_iterator> all_const_link_iterators;
class_tag()
: name_("")
@ -155,11 +178,7 @@ public:
{
}
class_tag(const std::string & name,
int min,
int max,
const std::string & super=""
)
class_tag(const std::string& name, int min, int max, const std::string& super = "")
: name_(name)
, min_(min)
, max_(max)
@ -169,8 +188,12 @@ public:
, links_()
{
}
class_tag(const config&);
~class_tag(){ }
~class_tag()
{
}
/** Prints information about tag to outputstream, recursively
* is used to print tag info
@ -182,56 +205,81 @@ public:
* min="min"
* max="max"
* [/tag]
*/
*/
void print(std::ostream& os);
const std::string & get_name() const{
return name_ ;
}
int get_min() const{
return min_;
}
int get_max() const{
return max_;
}
const std::string & get_super() const{
return super_ ;
}
bool is_extension() const{
return ! super_.empty();
const std::string& get_name() const
{
return name_;
}
void set_name(const std::string& name){
int get_min() const
{
return min_;
}
int get_max() const
{
return max_;
}
const std::string& get_super() const
{
return super_;
}
bool is_extension() const
{
return !super_.empty();
}
void set_name(const std::string& name)
{
name_ = name;
}
void set_min(int o){
void set_min(int o)
{
min_ = o;
}
void set_max(int o){
void set_max(int o)
{
max_ = o;
}
void set_min(const std::string& s){
void set_min(const std::string& s)
{
std::istringstream i(s);
if (!(i >> min_)){
if(!(i >> min_)) {
min_ = 0;
}
}
void set_max(const std::string& s){
void set_max(const std::string& s)
{
std::istringstream i(s);
if (!(i >> max_)){
if(!(i >> max_)) {
max_ = 0;
}
}
void set_super(const std::string& s){
super_= s;
void set_super(const std::string& s)
{
super_ = s;
}
void add_key(const class_key& new_key){
void add_key(const class_key& new_key)
{
keys_.emplace(new_key.get_name(), new_key);
}
void add_tag(const class_tag& new_tag){
void add_tag(const class_tag& new_tag)
{
tags_.emplace(new_tag.name_, new_tag);
}
void add_link(const std::string & link);
void add_link(const std::string& link);
/**
* Tags are usually organized in tree.
@ -242,50 +290,55 @@ public:
* Path is getting shotter and shoter with each call.
* Path should look like tag1/tag2/parent/ Slash at end is mandatory.
*/
void add_tag (const std::string & path,const class_tag & tag,
class_tag &root);
void add_tag(const std::string& path, const class_tag& tag, class_tag& root);
bool operator < ( const class_tag& t) const{
bool operator<(const class_tag& t) const
{
return name_ < t.name_;
}
bool operator == (const class_tag & other) const {
bool operator==(const class_tag& other) const
{
return name_ == other.name_;
}
/**
* Returns pointer to child key
*/
const class_key * find_key(const std::string & name) const;
/**
* Returns pointer to child link
*/
const std::string * find_link(const std::string & name) const;
/** Returns pointer to child key. */
const class_key* find_key(const std::string& name) const;
/** Returns pointer to child link. */
const std::string* find_link(const std::string& name) const;
/**
* Returns pointer to tag using full path to it.
* Also work with links
*/
const class_tag * find_tag(const std::string & fullpath,
const class_tag & root) const;
/**
* Calls the expansion on each child
*/
void expand_all(class_tag &root);
const class_tag* find_tag(const std::string& fullpath, const class_tag& root) const;
all_const_tag_iterators tags() const{
return all_const_tag_iterators(tags_.begin(),tags_.end());
}
all_const_key_iterators keys() const{
return all_const_key_iterators(keys_.begin(),keys_.end());
}
all_const_link_iterators links() const{
return all_const_link_iterators(links_.begin(),links_.end());
/** Calls the expansion on each child. */
void expand_all(class_tag& root);
all_const_tag_iterators tags() const
{
return all_const_tag_iterators(tags_.begin(), tags_.end());
}
void remove_key_by_name(const std::string &name){
keys_.erase (name);
all_const_key_iterators keys() const
{
return all_const_key_iterators(keys_.begin(), keys_.end());
}
all_const_link_iterators links() const
{
return all_const_link_iterators(links_.begin(), links_.end());
}
void remove_key_by_name(const std::string& name)
{
keys_.erase(name);
}
/** Removes all keys with this type. Works recursively */
void remove_keys_by_type(const std::string &type);
void remove_keys_by_type(const std::string& type);
#ifdef _MSC_VER
// MSVC throws an error if this method is private.
@ -293,19 +346,21 @@ public:
// error C2248: 'schema_validation::class_tag::find_tag' : cannot
// access private member declared in class 'schema_validation::class_tag'
class_tag * find_tag(const std::string & fullpath,
class_tag & root) ;
class_tag* find_tag(const std::string& fullpath, class_tag& root);
#endif
// class_tag & operator= (const class_tag&);
// class_tag & operator= (const class_tag&);
private:
/** name of tag*/
/** name of tag. */
std::string name_;
/** number of minimum occasions*/
/** number of minimum occasions. */
int min_;
/** number of maximum occasions*/
/** number of maximum occasions. */
int max_;
/**
* name of tag to extend "super-tag"
* Extension is smth like inheritance and is used in case
@ -314,12 +369,16 @@ private:
* so just linking that tag wouldn't help at all.
*/
std::string super_;
/** children tags*/
tag_map tags_;
/** keys*/
key_map keys_;
/** links to possible children. */
link_map links_;
/**
* the same as class_tag::print(std::ostream&)
* but indents different levels with step space.
@ -327,31 +386,32 @@ private:
* @param level current level of indentation
* @param step step to next indent
*/
void printl(std::ostream &os,int level, int step = 4);
void printl(std::ostream& os, int level, int step = 4);
#ifndef _MSC_VER
// this is complementary with the above #ifdef for the MSVC error
class_tag * find_tag(const std::string & fullpath,
class_tag & root) ;
class_tag* find_tag(const std::string& fullpath, class_tag& root);
#endif
void add_tags (const tag_map & list){
tags_.insert(list.begin(),list.end());
void add_tags(const tag_map& list)
{
tags_.insert(list.begin(), list.end());
}
void add_keys (const key_map & list){
keys_.insert(list.begin(),list.end());
}
void add_links (const link_map & list){
links_.insert(list.begin(),list.end());
}
/**
* Copies tags, keys and links of tag to this
*/
void append_super(const class_tag & tag,const std::string & super);
/**
* Expands all "super" copying their data to this.
*/
void expand(class_tag & root);
};
void add_keys(const key_map& list)
{
keys_.insert(list.begin(), list.end());
}
void add_links(const link_map& list)
{
links_.insert(list.begin(), list.end());
}
/** Copies tags, keys and links of tag to this. */
void append_super(const class_tag& tag, const std::string& super);
/** Expands all "super" copying their data to this. */
void expand(class_tag& root);
};
}