00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020 #include "tools/schema/sourceparser.hpp"
00021
00022 #include "boost/regex.hpp"
00023
00024 #include <stack>
00025
00026 namespace schema_validation{
00027
00028
00029
00030
00031 const std::string valid = "^\\s*\\*\\s*";
00032
00033 const std::string wiki_begin ="^\\s*/\\*(?:WIKI|SCHEMA)";
00034
00035 const std::string space ="\\s*";
00036
00037 const std::string equals ="=";
00038
00039 const std::string quote_symbol ="\"?";
00040
00041 const std::string block_begin="@begin" ;
00042
00043 const std::string block_end ="@end";
00044
00045 const std::string allow="@allow";
00046
00047 const std::string remove="@remove";
00048
00049 const std::string property_open="\\{";
00050
00051 const std::string property_close= "\\}";
00052
00053 const std::string name_type= "[a-z][a-zA-Z0-9_-]*" ;
00054
00055 const std::string parent_type= "/|(?:[a-z][a-zA-Z0-9_-]*/)+";
00056
00057 const std::string link_type="(?:[a-z][a-zA-Z0-9_-]*/)*(?:[a-z][a-zA-Z0-9_-]*)";
00058
00059 const std::string number="\\d*";
00060
00061 const std::string sign="-?";
00062
00063
00064
00065
00066
00067
00068
00069 const std::string eol=".*$";
00070
00071
00072
00073
00074 static std::string sub (const std::string & s){
00075 return "(" + s + ")";
00076 }
00077
00078
00079
00080
00081 static std::string quote(const std::string & s){
00082 return quote_symbol + s + quote_symbol ;
00083 }
00084
00085
00086
00087
00088
00089
00090
00091
00092 static std::string property(const std::string & name,
00093 const std::string & value = ""){
00094 if (value.empty()){
00095 return property_open + name + property_close;
00096 }
00097 return property_open + name + equals + quote(value) + property_close;
00098 }
00099
00100
00101 const std::string & get_valid() {
00102 return valid;
00103 }
00104
00105 const std::string & get_wiki() {
00106 static std::string wiki = wiki_begin + eol;
00107 return wiki;
00108 }
00109
00110 const std::string & get_parent_begin() {
00111 static std::string parent_begin = valid + block_begin
00112 + property("parent")
00113 + property("name",sub(parent_type))
00114 + eol;
00115 return parent_begin;
00116 }
00117
00118 const std::string & get_parent_end() {
00119 static std::string parent_end = valid + block_end + property("parent")
00120 + property("name",sub(parent_type))+ eol;
00121 return parent_end;
00122 }
00123
00124 const std::string & get_tag_begin() {
00125 static std::string tag_begin = valid + block_begin + property("tag")
00126 + property("name",sub(name_type))
00127 + property("min",sub(number))
00128 + property("max",sub(sign + number))
00129 + sub(property("super",sub(link_type)))
00130 +"?" + eol;
00131
00132
00133
00134 return tag_begin;
00135 }
00136
00137
00138 const std::string & get_tag_end() {
00139 static std::string tag_end = valid + block_end + property("tag")
00140 + property("name",sub(name_type)) + eol;
00141 return tag_end;
00142 }
00143
00144
00145 const std::string & get_allow_link(){
00146 static std::string allow_link = valid + allow + property("link")
00147 +property("name",sub(link_type)) + eol;
00148 return allow_link;
00149 }
00150
00151 const std::string & get_allow_global(){
00152 static std::string global_link = valid + allow + property("global")
00153 +property("name",sub(name_type)) + eol;
00154 return global_link;
00155 }
00156
00157 static const std::string & get_allow_type(){
00158 static std::string allow_type = valid + allow + property("type")
00159 +property("name",sub(name_type))
00160 +property("value",sub("\\^.+\\$"))
00161 + eol;
00162 return allow_type;
00163 }
00164 static const std::string & get_remove_type(){
00165 static std::string remove_type = valid + remove + property("type")
00166 +property("name",sub(name_type))
00167 + eol;
00168 return remove_type;
00169 }
00170 static const std::string & get_remove_key(){
00171 static std::string remove_key = valid + remove + property("key")
00172 +property("name",sub(name_type))
00173 + eol;
00174 return remove_key;
00175 }
00176
00177 const std::string & get_table_key_begin() {
00178 static std::string keys_begin = valid + block_begin + property("table")
00179 + property("config")+ eol;
00180 return keys_begin;
00181 }
00182
00183 const std::string & get_table_end() {
00184 static std::string table_end = valid + block_end + property("table")
00185 + eol;
00186 return table_end;
00187 }
00188
00189
00190 const std::string & get_key_value(){
00191 static std::string key_value = valid + sub("[a-zA-z][a-zA-Z\\d_+-]*")
00192 + "\\s*&\\s*"+sub(name_type)
00193 +"\\s*&\\s?"+ sub(quote("[a-zA-Z._0-9+-]*"))
00194 +"\\s&"+ eol;
00195 return key_value;
00196 }
00197
00198 void test_regex( std::ostream & f ){
00199 f << get_valid() << "\n"
00200 << get_wiki() << "\n"
00201 << get_parent_begin() << "\n"
00202 << get_parent_end() << "\n"
00203 << get_tag_begin() << "\n"
00204 << get_tag_end() << "\n"
00205 << get_allow_link() << "\n"
00206 << get_allow_global() << "\n"
00207 << get_table_key_begin() << "\n"
00208 << get_table_end() << "\n"
00209 << get_key_value() << "\n"
00210 << get_allow_type() << "\n"
00211 << std::endl;
00212 }
00213
00214 bool class_source_parser::save_schema(){
00215
00216 std::fstream out;
00217 if (output_.empty()){
00218 return false;
00219 }
00220 out.open(output_.c_str(),std::ios::out|std::ios::trunc);
00221 if (out.fail()){
00222 errors_.add_simple_error("Can not open file "+output_+
00223 "\n Output woulfd not be stored\n");
00224 return false;
00225 }
00226
00227 for (std::vector<std::string>::const_iterator i= forbidden_.begin ();
00228 i != forbidden_.end (); ++i){
00229 root_.remove_keys_by_type (*i);
00230 types_.erase (*i);
00231 }
00232 out << "[wml_schema]\n";
00233 for (std::map<std::string,std::string>::iterator i=types_.begin();
00234 i!= types_.end();++i){
00235 out << " [type]\n"
00236 << " name=" << i->first << "\n"
00237 << " value=\""<< i->second << "\"\n"
00238 <<" [/type]\n";
00239 }
00240 root_.print(out);
00241 out << "[/wml_schema]\n";
00242 out.close();
00243 return true;
00244 }
00245
00246
00247 bool class_source_parser::getline(std::string &s){
00248 if (f_.fail()){
00249 errors_.add_read_error(input_,line_);
00250 return false;
00251 }
00252 std::getline(f_,s);
00253 line_ ++;
00254 if (! f_.eof()){
00255 if (f_.fail()){
00256 errors_.add_read_error(input_,line_);
00257 return false;
00258 }
00259 }
00260 return true;
00261 }
00262
00263 void class_source_parser::add_open_tag_error(int i = INT_MAX){
00264 std::vector<class_tag>::iterator it;
00265 for (it = current_.begin(); it != current_.end() && i > 0; ++it){
00266 errors_.add_opened_entity_error(input_,line_,it->get_name());
00267 --i;
00268 }
00269 }
00270
00271 void class_source_parser::close_opened_tags(int i = INT_MAX){
00272 if (current_.empty()){
00273 return;
00274 }
00275 std::stack<std::string> error_cache ;
00276 while (current_.size() > 1){
00277 if (i==0){
00278 break;
00279 }
00280 class_tag tag (current_.back());
00281 current_.pop_back();
00282 current_.back().add_tag(tag);
00283 error_cache.push(tag.get_name());
00284 i--;
00285 }
00286 if (i!=0){
00287
00288 if (parent_name_.empty()) {
00289 orphan_tags_.push_back(current_.back());
00290 errors_.add_orphan_error(input_,line_,current_.back().get_name());
00291 }else{
00292 error_cache.push(current_.back().get_name());
00293 root_.add_tag(parent_name_,current_.back(),root_);
00294 }
00295 current_.pop_back();
00296 }
00297 std::string name_to_remove_from_cache = parent_name_;
00298 for (std::vector<class_tag>::const_iterator ii = current_.begin();
00299 ii!= current_.end();++ii){
00300 name_to_remove_from_cache += ii->get_name() + "/";
00301 }
00302 while (! error_cache.empty()){
00303 name_to_remove_from_cache += error_cache.top();
00304 errors_.remove_link_errors(name_to_remove_from_cache);
00305 error_cache.pop();
00306 name_to_remove_from_cache += "/";
00307 }
00308
00309 }
00310
00311
00312 bool class_source_parser::parse_source(){
00313 if (input_.empty()){
00314 errors_.add_simple_error("File was not defined\n");
00315
00316
00317 return false;
00318 }
00319
00320 f_.open(input_.c_str(),std::ios::in);
00321 if (f_.fail()){
00322 errors_.add_simple_error("File "+input_ + " can not be opened\n");
00323 return false;
00324 }
00325 line_ = 0;
00326 bool result = true;
00327 while (!f_.eof()){
00328 std::string line;
00329 if (! getline(line) ) {
00330 f_.close();
00331 f_.clear();
00332
00333 close_opened_tags();
00334 parent_name_.clear();
00335 return false;
00336 }
00337
00338 if (check_wiki(line)) {
00339 result = parse_block();
00340 if (! result) {
00341 break;
00342 }
00343 }
00344 }
00345
00346 f_.close();
00347
00348
00349
00350 f_.clear();
00351
00352 close_opened_tags();
00353 parent_name_.clear();
00354 return result;
00355 }
00356
00357
00358 bool class_source_parser::parse_block(){
00359 while (!f_.eof()){
00360 std::string line;
00361 if (! getline(line) ) { return false; }
00362 if ( check_valid(line)) {
00363 if (check_allow_type(line)) continue;
00364 if (check_remove_type(line)) continue;
00365 if (check_parent_begin(line)) continue;
00366
00367 if (check_tag_begin(line)){
00368 parse_tag();
00369 continue;
00370 }
00371 check_parent_end(line);
00372 }
00373 else{
00374
00375 if (!current_.empty()){
00376 add_open_tag_error();
00377 close_opened_tags();
00378
00379 }
00380
00381 return true;
00382
00383 }
00384 }
00385 return false;
00386 }
00387
00388 bool class_source_parser::parse_tag(){
00389 while (!f_.eof()){
00390 std::string line;
00391 if (! getline(line) ) { return false; }
00392 if (check_valid(line)) {
00393 if (check_tag_begin(line)){
00394 parse_tag();
00395 }
00396 if (check_tag_end(line)){
00397 return true;
00398 }
00399 if (check_keys_begin(line)){
00400 parse_keys();
00401 }
00402 if (check_allow_link(line)){
00403 continue;
00404 }
00405 if (check_allow_global(line)){
00406 continue;
00407 }
00408 if (check_remove_key(line)){
00409 }
00410 }else{
00411 if (!current_.empty()){
00412
00413 add_open_tag_error();
00414 close_opened_tags();
00415 }
00416 return true;
00417 }
00418 }
00419 return true;
00420 }
00421
00422
00423 bool class_source_parser::parse_keys(){
00424 std::string line;
00425 do{
00426 if (! getline(line) ) { return false; }
00427 if (! check_valid(line)) {
00428 errors_.add_opened_entity_error(input_,line_,"Table config");
00429 add_open_tag_error();
00430 close_opened_tags();
00431 return false;
00432 }
00433 static const boost::regex value (get_key_value() );
00434 boost::smatch sub;
00435 bool res = boost::regex_match(line,sub,value);
00436 if (res){
00437 std::string type = sub[2];
00438 class_key key (sub[1],type,sub[3]);
00439 current_.back().add_key(key);
00440 if (types_.find(type) == types_.end()){
00441 errors_.add_type_error(input_,line_,type);
00442 }
00443 }
00444 }while (! check_keys_end(line));
00445 return true;
00446 }
00447
00448
00449 bool class_source_parser::check_valid(const std::string &s){
00450
00451 static const boost::regex valid (get_valid());
00452 return boost::regex_search(s,valid);
00453 }
00454
00455 bool class_source_parser::check_wiki(const std::string& s){
00456 boost::regex wiki (get_wiki());
00457 return boost::regex_match(s,wiki);
00458 }
00459
00460 bool class_source_parser::check_tag_begin(const std::string &s){
00461
00462 static boost::regex tag (get_tag_begin());
00463 boost::smatch sub;
00464 bool res = boost::regex_match(s,sub,tag);
00465 if (res){
00466 std::string link = sub[5];
00467 class_tag new_tag;
00468 new_tag.set_name(sub[1]);
00469 new_tag.set_min(sub[2]);
00470 new_tag.set_max(sub[3]);
00471 new_tag.set_super(link);
00472 current_.push_back(new_tag);
00473 if (! link.empty() &&
00474 ! static_cast<const class_tag>(root_).find_tag(link,root_)){
00475 errors_.add_link_error(input_,line_,link);
00476 }
00477 return true;
00478 }
00479 return false;
00480 }
00481
00482 bool class_source_parser::check_tag_end(const std::string &s){
00483 static const boost::regex endtag (get_tag_end());
00484 boost::smatch sub;
00485 bool res = boost::regex_match(s,sub,endtag);
00486 if (res){
00487 std::string name = sub[1];
00488 if (current_.empty()){
00489 errors_.add_unopened_entity_error(input_,line_,name);
00490 return false;
00491 }
00492 std::vector<class_tag>::iterator ii = current_.end();
00493 int count_opened = 0;
00494 do{
00495 --ii;
00496 if (ii->get_name() == name){
00497 add_open_tag_error(count_opened);
00498 close_opened_tags(++count_opened);
00499 return true;
00500 }else{
00501 count_opened ++;
00502 }
00503 }while (ii != current_.begin()) ;
00504 }
00505 return false;
00506 }
00507
00508 bool class_source_parser::check_allow_link(const std::string &s){
00509 static const boost::regex allow_link (get_allow_link());
00510 boost::smatch sub;
00511 bool res = boost::regex_match(s,sub,allow_link);
00512 if (res){
00513 if (!current_.empty()){
00514 std::string link = sub[1];
00515 current_.back().add_link(link);
00516 if (static_cast<const class_tag>(root_).find_tag(link,root_) == NULL){
00517 errors_.add_link_error(input_,line_,link);
00518 }
00519 }
00520 }
00521 return res;
00522 }
00523
00524 bool class_source_parser::check_allow_global(const std::string &s){
00525 static const boost::regex allow_global (get_allow_global());
00526 boost::smatch sub;
00527 bool res = boost::regex_match(s,sub,allow_global);
00528 if (res){
00529 if (!current_.empty()){
00530 current_.back().add_link("global/"+sub[1]);
00531 }
00532 }
00533 return res;
00534 }
00535
00536 bool class_source_parser::check_parent_begin(const std::string &s){
00537 static const boost::regex parent (get_parent_begin());
00538 boost::smatch sub;
00539 bool res = boost::regex_match(s,sub,parent);
00540 if (res){
00541 std::string name = sub[1];
00542 if (!parent_name_.empty()) {
00543 errors_.add_second_parent_error(input_,line_,parent_name_,name);
00544 }
00545 parent_name_ = name;
00546 }
00547 return res;
00548 }
00549
00550 bool class_source_parser::check_parent_end(const std::string &s){
00551 static const boost::regex parent (get_parent_end());
00552 boost::smatch sub;
00553 bool res = boost::regex_match(s,sub,parent);
00554 if (res){
00555 std::string name = sub[1];
00556 if (parent_name_ == name) {
00557 parent_name_.clear();
00558 }else{
00559 errors_.add_unopened_entity_error(input_,line_,name);
00560 }
00561 }
00562 return true;
00563 }
00564
00565 bool class_source_parser::check_keys_begin(const std::string &s){
00566 static const boost::regex keys (get_table_key_begin());
00567 return boost::regex_match(s,keys);
00568
00569 }
00570
00571 bool class_source_parser::check_keys_end(const std::string &s){
00572 static const boost::regex endkeys (get_table_end());
00573 bool res = boost::regex_match(s,endkeys);
00574 return res;
00575 }
00576
00577 bool class_source_parser::check_allow_type(const std::string &s){
00578 static const boost::regex allow_type (get_allow_type());
00579 boost::smatch sub;
00580 bool res = boost::regex_match(s,sub,allow_type);
00581 if (res){
00582 std::string name = sub[1];
00583 std::string value = sub[2];
00584 try{
00585 boost::regex tmp (value);
00586 }catch(std::exception ){
00587 errors_.wrong_type_error(input_,line_,name,value);
00588 return true;
00589 }
00590 if(types_.find(name)!=types_.end()){
00591 errors_.overriding_type_error(input_,line_,name);
00592 }
00593 types_[name]=value;
00594 errors_.remove_type_errors(name);
00595 }
00596 return res;
00597 }
00598 bool class_source_parser::check_remove_type(const std::string &s){
00599 static const boost::regex remove_type (get_remove_type());
00600 boost::smatch sub;
00601 bool res = boost::regex_match(s,sub,remove_type);
00602 if (res){
00603 std::string name = sub[1];
00604 types_[name]="";
00605 forbidden_.push_back (name);
00606 errors_.remove_type_errors (name);
00607 }
00608 return res;
00609 }
00610 bool class_source_parser::check_remove_key(const std::string &s){
00611 static const boost::regex remove_key (get_remove_key());
00612 boost::smatch sub;
00613 bool res = boost::regex_match(s,sub,remove_key);
00614 if (res){
00615 if (! current_.empty ()){
00616 current_.back ().remove_key_by_name(sub[1]);
00617 }
00618 }
00619 return res;
00620 }
00621 }