serialization/schema_validator.cpp

Go to the documentation of this file.
00001 /* $Id: schema_validator.cpp 52533 2012-01-07 02:35:17Z shadowmaster $ */
00002 /*
00003    Copyright (C) 2011 - 2012 by Sytyi Nick <nsytyi@gmail.com>
00004    Part of the Battle for Wesnoth Project http://www.wesnoth.org/
00005 
00006    This program is free software; you can redistribute it and/or modify
00007    it under the terms of the GNU General Public License as published by
00008    the Free Software Foundation; either version 2 of the License, or
00009    (at your option) any later version.
00010    This program is distributed in the hope that it will be useful,
00011    but WITHOUT ANY WARRANTY.
00012 
00013    See the COPYING file for more details.
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 // dirty hack to avoid "unused" error in case of compiling with definition on
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                 //@NOTE Don't know, maybe merging of roots needed.
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             // Need to check all type values in schema-generator
00159             }
00160         }
00161     }
00162 
00163     return true;
00164 }
00165 /*
00166  * Please, @Note that there is some magic in pushing and poping to/from stacks.
00167  * assume they all are on their place due to parser algorithm
00168  * and validation logic
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     //cache_ is cleared in another place.
00200 }
00201 
00202 void schema_validator::validate(const config & cfg, const std::string & name,
00203                                 int start_line,
00204                                 const std::string &file){
00205     //close previous errors and print them to output.
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     // clear cache
00215     cache_it = cache_.top().find(&cfg);
00216     if (cache_it != cache_.top().end()){
00217         cache_it->second.clear();
00218     }
00219     // Please note that validating unknown tag keys the result will be false
00220     // Checking all elements counters.
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         // Checking if all mandatory keys are present
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         // checking existing keys
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 }//namespace schema_validation{
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines

Generated by doxygen 1.7.1 on Fri May 25 2012 01:03:09 for The Battle for Wesnoth
Gna! | Forum | Wiki | CIA | devdocs