Geant4 Cross Reference |
1 // Copyright (C) 2010, Guy Barrand. All rights reserved. 2 // See the file tools.license for terms. 3 4 #ifndef toolx_xml_loader 5 #define toolx_xml_loader 6 7 #include <tools/xml/tree> 8 #include <tools/file> 9 #include <tools/mnmx> 10 11 #include <cstdio> 12 #include <cctype> //iscntrl 13 14 #include <expat.h> 15 16 #ifdef TOOLS_MEM 17 #include <tools/mem> 18 namespace toolx { 19 extern "C" { 20 inline void* xml_malloc(size_t a_size){ 21 tools::mem::increment(tools::s_malloc().c_str()); 22 return ::malloc(a_size); 23 } 24 inline void* xml_realloc(void* a_ptr,size_t a_size){ 25 if(a_ptr==NULL) tools::mem::increment(tools::s_malloc().c_str()); 26 return ::realloc(a_ptr,a_size); 27 } 28 inline void xml_free(void* a_ptr){ 29 if(a_ptr!=NULL) tools::mem::decrement(tools::s_malloc().c_str()); 30 ::free(a_ptr); 31 } 32 }} 33 #endif 34 35 namespace toolx { 36 namespace xml { 37 38 class loader { 39 #ifdef TOOLS_MEM 40 TOOLS_SCLASS(toolx::xml::loader) 41 #endif 42 public: 43 loader(tools::xml::factory& a_factory, 44 std::ostream& a_out,bool a_verbose = false) 45 :m_factory(a_factory) 46 ,m_out(a_out) 47 ,m_verbose(a_verbose) 48 ,m_take_cntrl(false) 49 50 ,m_errors(0) 51 ,m_top(0) // Used to cleanup in case XML parsing failed. 52 ,m_current(0) 53 ,m_compressed_reader(0) 54 ,m_depth(0) 55 ,m_abort(false) 56 { 57 #ifdef TOOLS_MEM 58 tools::mem::increment(s_class().c_str()); 59 #endif 60 } 61 62 virtual ~loader(){ 63 delete m_compressed_reader; 64 clear(); 65 #ifdef TOOLS_MEM 66 tools::mem::decrement(s_class().c_str()); 67 #endif 68 } 69 70 protected: 71 loader(const loader& a_from) 72 :m_factory(a_from.m_factory) 73 ,m_out(a_from.m_out) 74 ,m_verbose(a_from.m_verbose) 75 ,m_take_cntrl(a_from.m_take_cntrl) 76 77 ,m_errors(0) 78 ,m_top(0) // Used to cleanup in case XML parsing failed. 79 ,m_current(0) 80 ,m_compressed_reader(0) 81 ,m_depth(0) 82 ,m_abort(false) 83 { 84 #ifdef TOOLS_MEM 85 tools::mem::increment(s_class().c_str()); 86 #endif 87 } 88 loader& operator=(const loader& a_from){ 89 if(&a_from==this) return *this; 90 91 m_verbose = a_from.m_verbose; 92 m_take_cntrl = a_from.m_take_cntrl; 93 94 m_errors = 0; 95 m_top = 0; 96 m_current = 0; 97 m_compressed_reader = 0; 98 m_depth = 0; 99 m_abort = false; 100 101 return *this; 102 } 103 104 public: 105 virtual bool visit_end_element(tools::xml::tree&,bool& a_keep) { 106 a_keep = true; 107 return true; 108 } 109 110 public: 111 void set_take_cntrl_chars(bool a_value) {m_take_cntrl = a_value;} 112 113 std::ostream& out() const {return m_out;} 114 void set_compressed_reader(tools::file::reader* aReader){ 115 delete m_compressed_reader; 116 m_compressed_reader = aReader; //take ownership. 117 } 118 119 unsigned int errors() const {return m_errors;} 120 121 void set_tags(const std::vector<std::string>& a_tags){m_tags=a_tags;} 122 void add_tag(const std::string& a_tag){m_tags.push_back(a_tag);} 123 124 bool load_file(const std::string& a_file,bool a_compressed) { 125 clear(); 126 if(!parse_file(a_file, 127 (XML_StartElementHandler)start_element, 128 (XML_EndElementHandler)end_element, 129 this,a_compressed)) { 130 clear(); 131 return false; 132 } 133 if(m_current) m_current->set_file(a_file); 134 return true; 135 } 136 137 bool load_string(const std::string& a_string){ 138 clear(); 139 if(!parse_buffer(a_string.size(),a_string.c_str(), 140 (XML_StartElementHandler)start_element, 141 (XML_EndElementHandler)end_element, 142 this)) { 143 clear(); 144 return false; 145 } 146 return true; 147 } 148 149 bool load_buffer(size_t aSize,const char* aBuffer){ 150 clear(); 151 if(!parse_buffer(aSize,aBuffer, 152 (XML_StartElementHandler)start_element, 153 (XML_EndElementHandler)end_element, 154 this)) { 155 clear(); 156 return false; 157 } 158 return true; 159 } 160 161 const tools::xml::tree* top_item() const {return m_current;} 162 163 tools::xml::tree* top_item(){return m_current;} 164 165 void empty(){m_top = 0;m_current = 0;} 166 167 bool is_tag(const std::string& a_string) const { 168 size_t number = m_tags.size(); 169 for(size_t index=0;index<number;index++) { 170 if(a_string==m_tags[index]) return true; 171 } 172 return false; 173 } 174 175 protected: 176 void clear(){ 177 // In case of problem, deleting m_current is not sufficient. 178 delete m_top; 179 m_top = 0; 180 m_current = 0; 181 } 182 183 bool parse_buffer(size_t aSize,const char* aBuffer, 184 XML_StartElementHandler a_start,XML_EndElementHandler a_end, 185 void* a_tag){ 186 m_errors = 0; 187 if(!aSize) return true; //nothing to do. 188 m_depth = 0; 189 m_abort = false; 190 191 #ifdef TOOLS_MEM 192 XML_Memory_Handling_Suite mem; 193 mem.malloc_fcn = xml_malloc; 194 mem.realloc_fcn = xml_realloc; 195 mem.free_fcn = xml_free; 196 XML_Parser _parser = XML_ParserCreate_MM(NULL,&mem,NULL); 197 #else 198 XML_Parser _parser = XML_ParserCreate(NULL); 199 #endif 200 201 XML_SetUserData(_parser,a_tag); 202 XML_SetElementHandler(_parser,a_start,a_end); 203 XML_SetCharacterDataHandler(_parser,(XML_CharacterDataHandler)character_data_handler); 204 //XML_SetProcessingInstructionHandler(_parser,processingInstructionHandler); 205 char* buf = (char*)aBuffer; 206 size_t l = aSize; 207 int done = 0; 208 do { 209 size_t len = tools::mn<size_t>(l,BUFSIZ); //BUFSIZ in cstdio 210 done = len < BUFSIZ ? 1 : 0; 211 if(XML_Parse(_parser, buf, (int)len, done)==XML_STATUS_ERROR) { 212 m_out << "parse_buffer :" 213 << " " << XML_ErrorString(XML_GetErrorCode(_parser)) 214 << " at line " << (int)XML_GetCurrentLineNumber(_parser) 215 << " at byte index " << (int)XML_GetCurrentByteIndex(_parser) 216 << std::endl; 217 {XML_Index pos = XML_GetCurrentByteIndex(_parser); 218 XML_Index pmn = tools::mx<XML_Index>(pos-10,0); 219 XML_Index pmx = tools::mn<XML_Index>(pos+10,XML_Index(aSize)-1); 220 std::string c = " "; 221 {for(XML_Index p=pmn;p<=pmx;p++) {c[0] = *(aBuffer+p);m_out << c;} 222 m_out << std::endl;} 223 {for(XML_Index p=pmn;p<pos;p++) m_out << " "; 224 m_out << "^" << std::endl;}} 225 XML_ParserFree(_parser); 226 return false; 227 } 228 if(m_abort) { 229 XML_ParserFree(_parser); 230 return false; 231 } 232 buf += len; 233 l -= len; 234 } while (!done); 235 XML_ParserFree(_parser); 236 return true; 237 } 238 239 bool parse_file(const std::string& a_file, 240 XML_StartElementHandler a_start,XML_EndElementHandler a_end, 241 void* a_tag,bool a_compressed){ 242 if(m_verbose) { 243 m_out << "parse_file :" 244 << " parse file " << tools::sout(a_file) << "..." << std::endl; 245 } 246 m_errors = 0; 247 248 bool use_zlib = false; 249 if(a_compressed) { 250 if(m_verbose) { 251 m_out << "parse_file :" 252 << " uncompress requested for file " 253 << tools::sout(a_file) << "." 254 << std::endl; 255 } 256 use_zlib = true; 257 } else { 258 // may be compressed anyway : 259 bool compressed; 260 if(!tools::file::is_gzip(a_file,compressed)) { 261 m_out << "parse_file :" 262 << " tools::file::is_gzip() failed for " << a_file << "." 263 << std::endl; 264 return false; 265 } 266 if(compressed) use_zlib = true; 267 } 268 269 tools::file::reader* freader = 0; 270 bool delete_freader = false; 271 if(use_zlib) { 272 if(!m_compressed_reader) { 273 m_out << "parse_file :" 274 << " no compressed reader given." 275 << std::endl; 276 return false; 277 } 278 freader = m_compressed_reader; 279 } else { 280 freader = new tools::FILE_reader(); 281 delete_freader = true; 282 } 283 if(!freader->open(a_file)) { 284 m_out << "parse_file :" 285 << " can't open file " << a_file << std::endl; 286 if(delete_freader) delete freader; 287 return false; 288 } 289 290 m_depth = 0; 291 m_abort = false; 292 293 #ifdef TOOLS_MEM 294 XML_Memory_Handling_Suite mem; 295 mem.malloc_fcn = xml_malloc; 296 mem.realloc_fcn = xml_realloc; 297 mem.free_fcn = xml_free; 298 XML_Parser _parser = XML_ParserCreate_MM(NULL,&mem,NULL); 299 #else 300 XML_Parser _parser = XML_ParserCreate(NULL); 301 #endif 302 303 XML_SetUserData(_parser,a_tag); 304 XML_SetElementHandler(_parser,a_start,a_end); 305 XML_SetCharacterDataHandler(_parser,(XML_CharacterDataHandler)character_data_handler); 306 //XML_SetProcessingInstructionHandler(_parser, 307 // processingInstructionHandler); 308 309 310 //char buf[1024 * BUFSIZ]; 311 char buf[BUFSIZ]; 312 int done = 0; 313 do { 314 size_t len; 315 if(!freader->read(buf,sizeof(buf),len)) { 316 XML_ParserFree(_parser); 317 freader->close(); 318 if(delete_freader) delete freader; 319 return false; 320 } 321 done = len < sizeof(buf) ? 1 : 0; 322 if(XML_Parse(_parser, buf, (int)len, done)==XML_STATUS_ERROR) { 323 m_out << "parse_file :" 324 << " in file " << tools::sout(a_file) 325 << " " << XML_ErrorString(XML_GetErrorCode(_parser)) 326 << " at line " << (int)XML_GetCurrentLineNumber(_parser) 327 << std::endl; 328 XML_ParserFree(_parser); 329 freader->close(); 330 if(delete_freader) delete freader; 331 return false; 332 } 333 if(m_abort) { 334 XML_ParserFree(_parser); 335 freader->close(); 336 if(delete_freader) delete freader; 337 return false; 338 } 339 } while (!done); 340 XML_ParserFree(_parser); 341 freader->close(); 342 if(m_verbose) { 343 m_out << "parse_file :" 344 << " parse file " << tools::sout(a_file) << " done." << std::endl; 345 } 346 if(delete_freader) delete freader; 347 return true; 348 } 349 350 protected: 351 static void character_data_handler(void* aUserData,const XML_Char* a_string,int aLength){ 352 loader* This = (loader*)aUserData; 353 std::string _s; 354 _s.resize(aLength); 355 size_t count = 0; 356 char* p = (char*)a_string; 357 for (int i = 0; i < aLength; i++, p++) { 358 if(This->m_take_cntrl || (!iscntrl(*p))) { 359 _s[count] = *p; 360 count++; 361 } 362 } 363 if(count) { 364 _s.resize(count); 365 This->m_value += _s; 366 } 367 } 368 369 static void start_element(void* aUserData,const XML_Char* a_name,const XML_Char** a_atbs){ 370 loader* This = (loader*)aUserData; 371 if(This->m_abort) return; //Do nothing. 372 373 This->m_depth++; 374 This->m_value = ""; 375 376 std::string name = a_name; //Can't be empty 377 if(This->is_tag(name)) { 378 379 if(!This->m_current) { 380 if(This->m_depth==1) { 381 // Ok. Head. 382 } else { 383 This->m_out << "start_element :" 384 << " no tag with a depth of " << This->m_depth 385 << std::endl; 386 This->m_abort = true; 387 return; 388 } 389 } else { 390 int delta = This->m_current->depth() - This->m_depth; 391 if(delta>=1) { 392 This->m_out << "start_element :" 393 << " for element " << tools::sout(name) 394 << " tag with a delta depth of " << delta 395 << std::endl; 396 This->m_abort = true; 397 return; 398 } 399 } 400 401 std::vector<tools::xml::tree::atb> atbs; 402 {const XML_Char** a_atts = a_atbs; 403 while((*a_atts)&&(*(a_atts+1))) { 404 atbs.push_back(tools::xml::tree::atb(*a_atts,*(a_atts+1))); 405 a_atts+=2; 406 }} 407 408 tools::xml::tree* parent = This->m_current; 409 tools::xml::tree* _tree = This->m_factory.create(name,atbs,parent); 410 if(!_tree) { 411 This->m_out << "start_element :" 412 << " can't create a tree for tag " << tools::sout(name) 413 << std::endl; 414 This->m_abort = true; 415 return; 416 } 417 418 //out << "start_element :" << std::endl; 419 //_tree->print_xml(*(This->m_printer),"debug : "); 420 421 if(parent) parent->add_child(_tree); 422 423 /* 424 if(This->m_current && !This->m_current->parent()) { 425 This->m_out << "start_element :" 426 << " warning : current tree without parent." 427 << " Potential mem leak." 428 << std::endl; 429 } 430 */ 431 432 This->m_current = _tree; 433 _tree->set_depth(This->m_depth); // Internal only. 434 435 if(!This->m_top) This->m_top = _tree; 436 437 } else { 438 439 if(!This->m_current) { 440 441 // Can't be in a non-tag without a tag ! 442 This->m_out << "start_element :" 443 << " for element " << tools::sout(name) 444 << " non-tag without some parent tag." 445 << std::endl; 446 This->m_abort = true; 447 return; 448 449 } else { 450 451 int delta = This->m_depth - This->m_current->depth(); 452 if(delta>1) { 453 454 This->m_out << "start_element :" 455 << " for element " << tools::sout(name) 456 << " grand child of a tag." 457 << std::endl; 458 This->m_abort = true; 459 return; 460 461 } else if(delta==1) { //ok 462 463 This->m_atbs.clear(); 464 {const XML_Char** a_atts = a_atbs; 465 while((*a_atts)&&(*(a_atts+1))) { 466 This->m_atbs.push_back(tools::xml::tree::atb(*a_atts,*(a_atts+1))); 467 a_atts+=2; 468 }} 469 470 } else { 471 472 This->m_out << "start_element :" 473 << " for element " << tools::sout(name) 474 << " non-tag with a delta depth of " << delta 475 << std::endl; 476 This->m_abort = true; 477 return; 478 479 } 480 } 481 482 } 483 } 484 485 486 static void end_element(void* aUserData,const XML_Char* a_name){ 487 loader* This = (loader*)aUserData; 488 if(This->m_abort) return; //Do nothing. 489 490 if(This->m_current) { 491 492 tools::xml::tree* tr = This->m_current; 493 int delta = This->m_depth - tr->depth(); 494 if(delta==0) { //Back to a tag : 495 496 tools::xml::tree* parent = tr->parent(); 497 498 bool keep = false; 499 bool cont = This->visit_end_element(*tr,keep); 500 if(keep) { 501 if(parent) { 502 /* 503 if(!This->m_current->parent()) { 504 This->m_out << "end_element :" 505 << " warning : current tree without parent (1)." 506 << " Potential mem leak." 507 << std::endl; 508 } 509 */ 510 This->m_current = parent; 511 } 512 } else { 513 //FIXME : the top could be recreated ! 514 if(This->m_top==tr) This->m_top = 0; 515 516 if(parent) { 517 parent->remove_child(tr); //delete the tr 518 } else { 519 delete tr; 520 } 521 522 /* 523 if(!This->m_current->parent()) { 524 This->m_out << "end_element :" 525 << " warning : current tree without parent (2)." 526 << " Potential mem leak." 527 << std::endl; 528 } 529 */ 530 531 This->m_current = parent; //parent could be 0 : ok. 532 } 533 534 if(!cont) This->m_abort = true; 535 536 } else if(delta==1) { //Back to a child of tag : 537 538 //FIXME : correct m_value ? (Can we pick the one of a sub item ?) 539 tr->add_element(std::string(a_name),This->m_atbs,This->m_value); 540 //This->m_value = ""; 541 542 } else { 543 544 This->m_out << "end_element :" 545 << " problem for element " << tools::sout(std::string(a_name)) 546 << " : delta depth of " << delta 547 << std::endl; 548 This->m_abort = true; 549 550 } 551 552 } 553 554 555 This->m_depth--; 556 } 557 558 protected: 559 tools::xml::factory& m_factory; 560 std::ostream& m_out; 561 protected: 562 bool m_verbose; 563 bool m_take_cntrl; 564 unsigned int m_errors; 565 std::vector<std::string> m_tags; 566 tools::xml::tree* m_top; 567 tools::xml::tree* m_current; 568 //std::vector<tools::xml::tree::atb> m_atbs; 569 std::vector< std::pair<std::string,std::string> > m_atbs; 570 std::string m_value; 571 tools::file::reader* m_compressed_reader; 572 unsigned int m_depth; 573 bool m_abort; 574 }; 575 576 }} 577 578 #endif