00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016 #include "serialization/schema_validator.hpp"
00017
00018
00019 #include "filesystem.hpp"
00020 #include "foreach.hpp"
00021 #include "gettext.hpp"
00022 #include "log.hpp"
00023 #include "serialization/preprocessor.hpp"
00024 #include "wml_exception.hpp"
00025
00026 namespace schema_validation{
00027
00028 static lg::log_domain log_validation("validation");
00029
00030 #define ERR_VL LOG_STREAM(err, log_validation)
00031 #define WRN_VL LOG_STREAM(warn, log_validation)
00032 #define LOG_VL LOG_STREAM(info, log_validation)
00033
00034 static std::string at(const std::string & file, int line){
00035 std::ostringstream ss;
00036 ss << line << " " << file;
00037 return ::lineno_string(ss.str());
00038 }
00039
00040 static void print_output(const std::string & message,bool flag_exception = false ){
00041 #ifndef VALIDATION_ERRORS_LOG
00042 if(flag_exception){
00043 throw twml_exception("Validation error occured",message);
00044 }else{
00045 ERR_VL << message;
00046 }
00047 #else
00048
00049 flag_exception = true;
00050 if (flag_exception){ ERR_VL << message;}
00051 #endif
00052 }
00053
00054 static void extra_tag_error(const std::string & file, int line,
00055 const std::string & name,int n,
00056 const std::string & parent, bool flag_exception){
00057 std::ostringstream ss;
00058 ss <<at(file,line) << ": extra tag [" << name << "]; there may only be "
00059 << n << " ["<< name <<"] in [" << parent <<"]\n";
00060 print_output (ss.str (),flag_exception);
00061 }
00062
00063 static void wrong_tag_error(const std::string & file, int line,
00064 const std::string & name,const std::string & parent,
00065 bool flag_exception){
00066 std::ostringstream ss;
00067 ss <<at(file,line) << ": tag [" << name << "] may not be used in [" <<
00068 parent <<"]\n";
00069 print_output (ss.str (),flag_exception);
00070 }
00071
00072 static void missing_tag_error(const std::string & file, int line,
00073 const std::string & name,int n,
00074 const std::string & parent, bool flag_exception){
00075 std::ostringstream ss;
00076 ss <<at(file,line) << ": missing tag [" << name << "]; there must be "
00077 << n << " ["<< name <<"]s in [" << parent <<"]\n";
00078 print_output (ss.str (),flag_exception);
00079 }
00080
00081 static void extra_key_error(const std::string & file, int line,
00082 const std::string & tag,const std::string & key,
00083 bool flag_exception){
00084 std::ostringstream ss;
00085 ss << at(file,line) << ": Invalid key '"<< key <<"=' in tag ["<< tag
00086 << "] on line " << line << "\n";
00087 print_output (ss.str (),flag_exception);
00088 }
00089
00090 static void missing_key_error(const std::string & file, int line,
00091 const std::string & tag,const std::string & key,
00092 bool flag_exception){
00093 std::ostringstream ss;
00094 ss << at(file,line) << ": In tag "<< tag
00095 << " which begins here, " << " missing key "<< key << "\n";
00096 print_output (ss.str (),flag_exception);
00097 }
00098
00099 static void wrong_value_error(const std::string & file, int line,
00100 const std::string & tag,const std::string & key,
00101 const std::string & value,bool flag_exception){
00102 std::ostringstream ss;
00103 ss << at(file,line) << ": Invalid value '"<< value << "' in key '" << key <<
00104 "=' in tag ["<< tag <<"] on line " << line << "'\n";
00105 print_output (ss.str (),flag_exception);
00106 }
00107
00108
00109
00110 schema_validator::~schema_validator(){}
00111
00112 schema_validator::schema_validator(const std::string & config_file_name)
00113 : config_read_ (false)
00114 , create_exceptions_(strict_validation_enabled)
00115 , root_()
00116 , stack_()
00117 , counter_()
00118 , cache_()
00119 , types_()
00120 {
00121 config_read_ = read_config_file(config_file_name);
00122 if (! config_read_) {
00123 ERR_VL << "Schema file "<< config_file_name << " was not read.\n";
00124 throw abstract_validator::error("Schema file "+ config_file_name
00125 + " was not read.\n");
00126 }else{
00127 stack_.push(&root_);
00128 counter_.push(cnt_map());
00129 cache_.push(message_map());
00130 root_.expand_all(root_);
00131 LOG_VL << "Schema file "<< config_file_name << " was read.\n"
00132 << "Validator initialized\n";
00133 }
00134 }
00135
00136 bool schema_validator::read_config_file(const std::string &filename){
00137 config cfg;
00138 try {
00139 preproc_map preproc(
00140 game_config::config_cache::instance().get_preproc_map());
00141 scoped_istream stream = preprocess_file(filename, &preproc);
00142 read(cfg, *stream);
00143 } catch(config::error&) {
00144 return false;
00145 }
00146 foreach (const config &g, cfg.child_range("wml_schema")) {
00147 foreach (const config &schema, g.child_range("tag")) {
00148 if (schema["name"].str() == "root"){
00149
00150 root_ = class_tag (schema);
00151 }
00152 }
00153 foreach (const config &type, g.child_range("type")) {
00154 try{
00155 types_[type["name"].str()] = boost::regex( type["value"].str());
00156 }
00157 catch (std::exception){
00158
00159 }
00160 }
00161 }
00162
00163 return true;
00164 }
00165
00166
00167
00168
00169
00170 void schema_validator::open_tag(const std::string & name,
00171 int start_line,
00172 const std::string &file,
00173 bool addittion){
00174 if (! stack_.empty()){
00175 const class_tag * tag = NULL;
00176 if (stack_.top()){
00177 tag = stack_.top()->find_tag(name,root_);
00178 if (! tag){
00179 wrong_tag_error(file,start_line,name,stack_.top()->get_name(),
00180 create_exceptions_);
00181 }else{
00182 if (! addittion){
00183 counter & cnt = counter_.top()[name];
00184 ++ cnt.cnt;
00185 }
00186 }
00187 }
00188 stack_.push(tag);
00189 }else{
00190 stack_.push(NULL);
00191 }
00192 counter_.push(cnt_map());
00193 cache_.push(message_map());
00194 }
00195
00196 void schema_validator::close_tag(){
00197 stack_.pop();
00198 counter_.pop();
00199
00200 }
00201
00202 void schema_validator::validate(const config & cfg, const std::string & name,
00203 int start_line,
00204 const std::string &file){
00205
00206 message_map::iterator cache_it = cache_.top().begin();
00207 for (;cache_it != cache_.top().end();++cache_it){
00208 for (message_list::iterator i = cache_it->second.begin();
00209 i != cache_it->second.end(); ++i){
00210 print(*i);
00211 }
00212 }
00213 cache_.pop();
00214
00215 cache_it = cache_.top().find(&cfg);
00216 if (cache_it != cache_.top().end()){
00217 cache_it->second.clear();
00218 }
00219
00220
00221 if (!stack_.empty() && stack_.top() && config_read_){
00222 class_tag::all_const_tag_iterators p = stack_.top()->tags();
00223 for (class_tag::const_tag_iterator tag = p.first;
00224 tag != p.second ; ++tag){
00225 int cnt = counter_.top()[tag->first].cnt;
00226 if (tag->second.get_min() > cnt){
00227 cache_.top()[&cfg].push_back(
00228 message_info(MISSING_TAG,file,start_line,
00229 tag->second.get_min(),tag->first,"",
00230 name));
00231 continue;
00232 }
00233 if (tag->second.get_max() < cnt){
00234 cache_.top()[&cfg].push_back(
00235 message_info(EXTRA_TAG,file,start_line,
00236 tag->second.get_max(),tag->first,"",
00237 name));
00238 }
00239 }
00240
00241 class_tag::all_const_key_iterators k = stack_.top()->keys();
00242 for (class_tag::const_key_iterator key = k.first;
00243 key != k.second ; ++key){
00244 if (key->second.is_mandatory()){
00245 if (cfg.get(key->first) == NULL){
00246 cache_.top()[&cfg].push_back(
00247 message_info(MISSING_KEY,file,start_line,0,
00248 name,key->first ));
00249 }
00250 }
00251 }
00252 }
00253 }
00254
00255
00256 void schema_validator::validate_key(const config & cfg,
00257 const std::string & name,
00258 const std::string & value,
00259 int start_line,
00260 const std::string &file){
00261 if (!stack_.empty() && stack_.top() && config_read_){
00262
00263 const class_key * key =stack_.top()->find_key(name);
00264 if (key){
00265 std::map<std::string,boost::regex>::iterator itt =
00266 types_.find(key->get_type());
00267 if (itt != types_.end()){
00268 boost::smatch sub;
00269 bool res = boost::regex_match(value,sub,itt->second);
00270 if (!res ) {
00271 cache_.top()[&cfg].push_back(
00272 message_info(WRONG_VALUE,file,start_line,0,
00273 stack_.top()->get_name(),
00274 name,value));
00275 }
00276 }
00277 }
00278 else{
00279 cache_.top()[&cfg].push_back(
00280 message_info(EXTRA_KEY,file,start_line,0,
00281 stack_.top()->get_name(),name));
00282 }
00283
00284 }
00285 }
00286
00287 void schema_validator::print(message_info & el){
00288 switch (el.type){
00289 case WRONG_TAG:
00290 wrong_tag_error(el.file,el.line,el.tag,el.value,create_exceptions_);
00291 break;
00292 case EXTRA_TAG:
00293 extra_tag_error(el.file,el.line,el.tag,el.n,el.value,create_exceptions_);
00294 break;
00295 case MISSING_TAG:
00296 missing_tag_error(el.file,el.line,el.tag,el.n,el.value,
00297 create_exceptions_);
00298 break;
00299 case EXTRA_KEY:
00300 extra_key_error(el.file,el.line,el.tag,el.key,create_exceptions_);
00301 break;
00302 case WRONG_VALUE:
00303 wrong_value_error(el.file,el.line,el.tag,el.key,el.value,
00304 create_exceptions_);
00305 break;
00306 case MISSING_KEY:
00307 missing_key_error(el.file,el.line,el.tag,el.key,create_exceptions_);
00308 }
00309 }
00310 }