1 2 // 3 // XML storage C++ classes version 1.3 4 // 5 // Copyright (c) 2004, 2005, 2006, 2007, 2008, 2009, 2010 Martin Fuchs <martin-fuchs@gmx.net> 6 // 7 8 /// \file xmlstorage.cpp 9 /// XMLStorage implementation file 10 11 12 /* 13 14 All rights reserved. 15 16 Redistribution and use in source and binary forms, with or without 17 modification, are permitted provided that the following conditions are met: 18 19 * Redistributions of source code must retain the above copyright 20 notice, this list of conditions and the following disclaimer. 21 * Redistributions in binary form must reproduce the above copyright 22 notice, this list of conditions and the following disclaimer in 23 the documentation and/or other materials provided with the 24 distribution. 25 26 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 27 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 28 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 29 ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 30 LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 31 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 32 SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 33 INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 34 CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 35 ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 36 POSSIBILITY OF SUCH DAMAGE. 37 38 */ 39 40 #include <precomp.h> 41 42 #ifndef XS_NO_COMMENT 43 #define XS_NO_COMMENT // no #pragma comment(lib, ...) statements in .lib files to enable static linking 44 #endif 45 46 //#include "xmlstorage.h" 47 48 49 namespace XMLStorage { 50 51 52 // work around GCC's wide string constant bug 53 #ifdef __GNUC__ 54 const LPCXSSTR XS_EMPTY = XS_EMPTY_STR; 55 const LPCXSSTR XS_TRUE = XS_TRUE_STR; 56 const LPCXSSTR XS_FALSE = XS_FALSE_STR; 57 const LPCXSSTR XS_INTFMT = XS_INTFMT_STR; 58 const LPCXSSTR XS_FLOATFMT = XS_FLOATFMT_STR; 59 #endif 60 61 const XS_String XS_KEY = XS_KEY_STR; 62 const XS_String XS_VALUE = XS_VALUE_STR; 63 const XS_String XS_PROPERTY = XS_PROPERTY_STR; 64 65 66 /// remove escape characters from zero terminated string 67 static std::string unescape(const char* s, char b, char e) 68 { 69 const char* end = s + strlen(s); 70 71 // if (*s == b) 72 // ++s; 73 // 74 // if (end>s && end[-1]==e) 75 // --end; 76 77 if (*s == b) 78 if (end>s && end[-1]==e) 79 ++s, --end; 80 81 return std::string(s, end-s); 82 } 83 84 inline std::string unescape(const char* s) 85 { 86 return unescape(s, '"', '"'); 87 } 88 89 /// remove escape characters from string with specified length 90 static std::string unescape(const char* s, size_t l, char b, char e) 91 { 92 const char* end = s + l; 93 94 // if (*s == b) 95 // ++s; 96 // 97 // if (end>s && end[-1]==e) 98 // --end; 99 100 if (*s == b) 101 if (end>s && end[-1]==e) 102 ++s, --end; 103 104 return std::string(s, end-s); 105 } 106 107 inline std::string unescape(const char* s, size_t l) 108 { 109 return unescape(s, l, '"', '"'); 110 } 111 112 113 /// move to the position defined by xpath in XML tree 114 bool XMLPos::go(const XPath& xpath) 115 { 116 XMLNode* node = xpath._absolute? _root: _cur; 117 118 node = node->find_relative(xpath); 119 120 if (node) { 121 go_to(node); 122 return true; 123 } else 124 return false; 125 } 126 127 /// move to the position defined by xpath in XML tree 128 bool const_XMLPos::go(const XPath& xpath) 129 { 130 const XMLNode* node = xpath._absolute? _root: _cur; 131 132 node = node->find_relative(xpath); 133 134 if (node) { 135 go_to(node); 136 return true; 137 } else 138 return false; 139 } 140 141 142 const char* XPathElement::parse(const char* path) 143 { 144 const char* slash = strchr(path, '/'); 145 if (slash == path) 146 return NULL; 147 148 size_t l = slash? slash-path: strlen(path); 149 std::string comp(path, l); 150 path += l; 151 152 // look for [n] and [@attr_name="attr_value"] expressions in path components 153 const char* bracket = strchr(comp.c_str(), '['); 154 l = bracket? bracket-comp.c_str(): comp.length(); 155 _child_name.assign(comp.c_str(), l); 156 157 int n = 0; 158 if (bracket) { 159 std::string expr = unescape(bracket, '[', ']'); 160 const char* p = expr.c_str(); 161 162 n = atoi(p); // read index number 163 164 if (n) 165 _child_idx = n - 1; // convert into zero based index 166 167 const char* at = strchr(p, '@'); 168 169 if (at) { 170 p = at + 1; 171 const char* equal = strchr(p, '='); 172 173 // read attribute name and value 174 if (equal) { 175 _attr_name = unescape(p, equal-p); 176 _attr_value = unescape(equal+1); 177 } 178 } 179 } 180 181 return path; 182 } 183 184 XMLNode* XPathElement::find(XMLNode* node) const 185 { 186 int n = 0; 187 188 for(XMLNode::Children::const_iterator it=node->_children.begin(); it!=node->_children.end(); ++it) 189 if (matches(**it, n)) 190 return *it; 191 192 return NULL; 193 } 194 195 const XMLNode* XPathElement::const_find(const XMLNode* node) const 196 { 197 int n = 0; 198 199 for(XMLNode::Children::const_iterator it=node->_children.begin(); it!=node->_children.end(); ++it) 200 if (matches(**it, n)) 201 return *it; 202 203 return NULL; 204 } 205 206 bool XPathElement::matches(const XMLNode& node, int& n) const 207 { 208 if (node != _child_name) 209 if (_child_name != XS_TEXT("*")) // use asterisk as wildcard 210 return false; 211 212 if (!_attr_name.empty()) 213 if (node.get(_attr_name) != _attr_value) 214 return false; 215 216 if (_child_idx == -1) 217 return true; 218 else if (n++ == _child_idx) 219 return true; 220 else 221 return false; 222 } 223 224 225 void XPath::init(const char* path) 226 { 227 // Is this an absolute path? 228 if (*path == '/') { 229 _absolute = true; 230 ++path; 231 } else 232 _absolute = false; 233 234 // parse path 235 while(*path) { 236 XPathElement elem; 237 238 path = elem.parse(path); 239 240 if (!path) 241 break; 242 243 if (*path == '/') 244 ++path; 245 246 push_back(elem); 247 } 248 } 249 250 251 const XMLNode* XMLNode::find_relative(const XPath& xpath) const 252 { 253 const XMLNode* node = this; 254 255 for(XPath::const_iterator it=xpath.begin(); it!=xpath.end(); ++it) { 256 node = it->const_find(node); 257 258 if (!node) 259 return NULL; 260 } 261 262 return node; 263 } 264 265 XMLNode* XMLNode::find_relative(const XPath& xpath) 266 { 267 XMLNode* node = this; 268 269 for(XPath::const_iterator it=xpath.begin(); it!=xpath.end(); ++it) { 270 node = it->find(node); 271 272 if (!node) 273 return NULL; 274 } 275 276 return node; 277 } 278 279 XMLNode* XMLNode::create_relative(const XPath& xpath) 280 { 281 XMLNode* node = this; 282 283 for(XPath::const_iterator it=xpath.begin(); it!=xpath.end(); ++it) { 284 XMLNode* child = it->find(node); 285 286 if (!child) { 287 child = new XMLNode(it->_child_name); 288 node->add_child(child); 289 290 if (!it->_attr_name.empty()) 291 (*this)[it->_attr_name] = it->_attr_value; 292 } 293 294 node = child; 295 } 296 297 return node; 298 } 299 300 /// count the nodes matching the given relative XPath expression 301 int XMLNode::count(XPath::const_iterator from, const XPath::const_iterator& to) const 302 { 303 const XPathElement& elem = *from++; 304 int cnt = 0; 305 int n = 0; 306 307 for(XMLNode::Children::const_iterator it=_children.begin(); it!=_children.end(); ++it) 308 if (elem.matches(**it, n)) { 309 if (from != to) 310 // iterate deeper 311 cnt += (*it)->count(from, to); 312 else 313 // increment match counter 314 ++cnt; 315 } 316 317 return cnt; 318 } 319 320 /// copy matching tree nodes using the given XPath filter expression 321 bool XMLNode::filter(const XPath& xpath, XMLNode& target) const 322 { 323 XMLNode* ret = filter(xpath.begin(), xpath.end()); 324 325 if (ret) { 326 // move returned nodes to target node 327 target._children.move(ret->_children); 328 target._attributes = ret->_attributes; 329 330 delete ret; 331 332 return true; 333 } else 334 return false; 335 } 336 337 /// create a new node tree using the given XPath filter expression 338 XMLNode* XMLNode::filter(XPath::const_iterator from, const XPath::const_iterator& to) const 339 { 340 XMLNode* copy = NULL; 341 342 const XPathElement& elem = *from++; 343 int cnt = 0; 344 int n = 0; 345 346 for(XMLNode::Children::const_iterator it=_children.begin(); it!=_children.end(); ++it) 347 if (elem.matches(**it, n)) { 348 if (!copy) 349 copy = new XMLNode(*this, XMLNode::COPY_NOCHILDREN); 350 351 if (from != to) { 352 XMLNode* ret = (*it)->filter(from, to); 353 354 if (ret) { 355 copy->add_child(ret); 356 ++cnt; 357 } 358 } else { 359 copy->add_child(new XMLNode(**it, XMLNode::COPY_NOCHILDREN)); 360 ++cnt; 361 } 362 } 363 364 if (cnt > 0) { 365 return copy; 366 } else { 367 delete copy; 368 return NULL; 369 } 370 } 371 372 373 /// encode XML string literals 374 std::string EncodeXMLString(const XS_String& str, bool cdata) 375 { 376 LPCXSSTR s = str.c_str(); 377 size_t l = XS_len(s); 378 379 if (cdata) { 380 // encode the whole string in a CDATA section 381 std::string ret = CDATA_START; 382 383 #ifdef XS_STRING_UTF8 384 ret += str; 385 #else 386 ret += get_utf8(str); 387 #endif 388 389 ret += CDATA_END; 390 391 return ret; 392 } else if (l <= BUFFER_LEN) { 393 LPXSSTR buffer = (LPXSSTR)alloca(6*sizeof(XS_CHAR)*XS_len(s)); // worst case """ / "'" 394 LPXSSTR o = buffer; 395 396 for(LPCXSSTR p=s; *p; ++p) 397 switch(*p) { 398 case '&': 399 *o++ = '&'; *o++ = 'a'; *o++ = 'm'; *o++ = 'p'; *o++ = ';'; // "&" 400 break; 401 402 case '<': 403 *o++ = '&'; *o++ = 'l'; *o++ = 't'; *o++ = ';'; // "<" 404 break; 405 406 case '>': 407 *o++ = '&'; *o++ = 'g'; *o++ = 't'; *o++ = ';'; // ">" 408 break; 409 410 case '"': 411 *o++ = '&'; *o++ = 'q'; *o++ = 'u'; *o++ = 'o'; *o++ = 't'; *o++ = ';'; // """ 412 break; 413 414 case '\'': 415 *o++ = '&'; *o++ = 'a'; *o++ = 'p'; *o++ = 'o'; *o++ = 's'; *o++ = ';'; // "'" 416 break; 417 418 default: 419 if ((unsigned)*p<0x20 && *p!='\t' && *p!='\r' && *p!='\n') { 420 char b[16]; 421 sprintf(b, "&#%d;", (unsigned)*p); 422 for(const char*q=b; *q; ) 423 *o++ = *q++; 424 } else 425 *o++ = *p; 426 } 427 428 #ifdef XS_STRING_UTF8 429 return XS_String(buffer, o-buffer); 430 #else 431 return get_utf8(buffer, o-buffer); 432 #endif 433 } else { // l > BUFFER_LEN 434 // alternative code for larger strings using ostringstream 435 // and avoiding to use alloca() for preallocated memory 436 fast_ostringstream out; 437 438 LPCXSSTR s = str.c_str(); 439 440 for(LPCXSSTR p=s; *p; ++p) 441 switch(*p) { 442 case '&': 443 out << "&"; 444 break; 445 446 case '<': 447 out << "<"; 448 break; 449 450 case '>': 451 out << ">"; 452 break; 453 454 case '"': 455 out << """; 456 break; 457 458 case '\'': 459 out << "'"; 460 break; 461 462 default: 463 if ((unsigned)*p<0x20 && *p!='\t' && *p!='\r' && *p!='\n') 464 out << "&#" << (unsigned)*p << ";"; 465 else 466 out << *p; 467 } 468 469 #ifdef XS_STRING_UTF8 470 return XS_String(out.str()); 471 #else 472 return get_utf8(out.str()); 473 #endif 474 } 475 } 476 477 /// decode XML string literals 478 XS_String DecodeXMLString(const std::string& str) 479 { 480 #ifdef XS_STRING_UTF8 481 const XS_String& str_utf8 = str; 482 #else 483 XS_String str_utf8; 484 assign_utf8(str_utf8, str.c_str(), str.length()); 485 #endif 486 487 LPCXSSTR s = str_utf8.c_str(); 488 LPXSSTR buffer = (LPXSSTR)alloca(sizeof(XS_CHAR)*XS_len(s)); 489 LPXSSTR o = buffer; 490 491 for(LPCXSSTR p=s; *p; ++p) 492 if (*p == '&') { 493 if (!XS_nicmp(p+1, XS_TEXT("lt;"), 3)) { 494 *o++ = '<'; 495 p += 3; 496 } else if (!XS_nicmp(p+1, XS_TEXT("gt;"), 3)) { 497 *o++ = '>'; 498 p += 3; 499 } else if (!XS_nicmp(p+1, XS_TEXT("amp;"), 4)) { 500 *o++ = '&'; 501 p += 4; 502 } else if (!XS_nicmp(p+1, XS_TEXT("quot;"), 5)) { 503 *o++ = '"'; 504 p += 5; 505 } else if (!XS_nicmp(p+1, XS_TEXT("apos;"), 5)) { 506 *o++ = '\''; 507 p += 5; 508 } else //@@ maybe decode "&#xx;" special characters 509 *o++ = *p; 510 } else if (*p=='<' && !XS_nicmp(p+1,XS_TEXT("![CDATA["),8)) { 511 LPCXSSTR e = XS_strstr(p+9, XS_TEXT(CDATA_END)); 512 if (e) { 513 p += 9; 514 size_t l = e - p; 515 memcpy(o, p, l); 516 o += l; 517 p = e + 2; 518 } else 519 *o++ = *p; 520 } else 521 *o++ = *p; 522 523 return XS_String(buffer, o-buffer); 524 } 525 526 527 /// write node with children tree to output stream using original white space 528 void XMLNode::original_write_worker(std::ostream& out) const 529 { 530 out << _leading << '<' << EncodeXMLString(*this); 531 532 for(AttributeMap::const_iterator it=_attributes.begin(); it!=_attributes.end(); ++it) 533 out << ' ' << EncodeXMLString(it->first) << "=\"" << EncodeXMLString(it->second) << "\""; 534 535 if (!_children.empty() || !_content.empty()) { 536 out << '>'; 537 538 if (_cdata_content) 539 out << CDATA_START << _content << CDATA_END; 540 else 541 out << _content; 542 543 for(Children::const_iterator it=_children.begin(); it!=_children.end(); ++it) 544 (*it)->original_write_worker(out); 545 546 out << _end_leading << "</" << EncodeXMLString(*this) << '>'; 547 } else 548 out << "/>"; 549 550 out << _trailing; 551 } 552 553 554 /// print node without any white space 555 void XMLNode::plain_write_worker(std::ostream& out) const 556 { 557 out << '<' << EncodeXMLString(*this); 558 559 for(AttributeMap::const_iterator it=_attributes.begin(); it!=_attributes.end(); ++it) 560 out << ' ' << EncodeXMLString(it->first) << "=\"" << EncodeXMLString(it->second) << "\""; 561 562 // strip leading white space from content 563 const char* content = _content.c_str(); 564 while(isspace((unsigned char)*content)) ++content; 565 566 if (!_children.empty() || *content) { 567 out << ">" << content; 568 569 for(Children::const_iterator it=_children.begin(); it!=_children.end(); ++it) 570 (*it)->plain_write_worker(out); 571 572 out << "</" << EncodeXMLString(*this) << ">"; 573 } else 574 out << "/>"; 575 } 576 577 578 /// pretty print node with children tree to output stream 579 void XMLNode::pretty_write_worker(std::ostream& out, const XMLFormat& format, int indent) const 580 { 581 for(int i=indent; i--; ) 582 out << XML_INDENT_SPACE; 583 584 out << '<' << EncodeXMLString(*this); 585 586 for(AttributeMap::const_iterator it=_attributes.begin(); it!=_attributes.end(); ++it) 587 out << ' ' << EncodeXMLString(it->first) << "=\"" << EncodeXMLString(it->second) << "\""; 588 589 // strip leading white space from content 590 const char* content = _content.c_str(); 591 while(isspace((unsigned char)*content)) ++content; 592 593 if (!_children.empty() || *content) { 594 out << '>' << content; 595 596 if (!_children.empty()) 597 out << format._endl; 598 599 for(Children::const_iterator it=_children.begin(); it!=_children.end(); ++it) 600 (*it)->pretty_write_worker(out, format, indent+1); 601 602 for(int i=indent; i--; ) 603 out << XML_INDENT_SPACE; 604 605 out << "</" << EncodeXMLString(*this) << '>' << format._endl; 606 } else 607 out << "/>" << format._endl; 608 } 609 610 611 /// write node with children tree to output stream using smart formating 612 void XMLNode::smart_write_worker(std::ostream& out, const XMLFormat& format, int indent) const 613 { 614 // strip the first line feed from _leading 615 const char* leading = _leading.c_str(); 616 if (*leading == '\n') ++leading; 617 618 if (!*leading) 619 for(int i=indent; i--; ) 620 out << XML_INDENT_SPACE; 621 else 622 out << leading; 623 624 out << '<' << EncodeXMLString(*this); 625 626 for(AttributeMap::const_iterator it=_attributes.begin(); it!=_attributes.end(); ++it) 627 out << ' ' << EncodeXMLString(it->first) << "=\"" << EncodeXMLString(it->second) << "\""; 628 629 // strip leading white space from content 630 const char* content = _content.c_str(); 631 while(isspace((unsigned char)*content)) ++content; 632 633 if (_children.empty() && !*content) 634 out << "/>"; 635 else { 636 out << '>'; 637 638 if (_cdata_content) 639 out << CDATA_START << _content << CDATA_END; 640 else if (!*content) 641 out << format._endl; 642 else 643 out << content; 644 645 Children::const_iterator it = _children.begin(); 646 647 if (it != _children.end()) { 648 for(; it!=_children.end(); ++it) 649 (*it)->smart_write_worker(out, format, indent+1); 650 651 // strip the first line feed from _end_leading 652 const char* end_leading = _end_leading.c_str(); 653 if (*end_leading == '\n') ++end_leading; 654 655 if (!*end_leading) 656 for(int i=indent; i--; ) 657 out << XML_INDENT_SPACE; 658 else 659 out << end_leading; 660 } else 661 out << _end_leading; 662 663 out << "</" << EncodeXMLString(*this) << '>'; 664 } 665 666 if (_trailing.empty()) 667 out << format._endl; 668 else 669 out << _trailing; 670 } 671 672 673 std::ostream& operator<<(std::ostream& out, const XMLError& err) 674 { 675 out << err._systemId << "(" << err._line << ") [column " << err._column << "] : " 676 << err._message; 677 678 return out; 679 } 680 681 682 const char* get_xmlsym_end_utf8(const char* p) 683 { 684 for(; *p; ++p) { 685 char c = *p; 686 687 // NameChar ::= Letter | Digit | '.' | '-' | '_' | ':' | CombiningChar | Extender 688 if (c == '\xC3') // UTF-8 escape character 689 ++p; //TODO only continue on umlaut characters 690 else if (!isalnum(c) && c!='.' && c!='-' && c!='_' && c!=':') 691 break; 692 } 693 694 return p; 695 } 696 697 698 void DocType::parse(const char* p) 699 { 700 while(isspace((unsigned char)*p)) ++p; 701 702 const char* start = p; 703 p = get_xmlsym_end_utf8(p); 704 _name.assign(start, p-start); 705 706 while(isspace((unsigned char)*p)) ++p; 707 708 start = p; 709 p = get_xmlsym_end_utf8(p); 710 std::string keyword(p, p-start); // "PUBLIC" or "SYSTEM" 711 712 while(isspace((unsigned char)*p)) ++p; 713 714 if (*p=='"' || *p=='\'') { 715 char delim = *p; 716 717 start = ++p; 718 while(*p && *p!=delim) ++p; 719 720 if (*p == delim) 721 _public.assign(start, p++-start); 722 } else 723 _public.erase(); 724 725 while(isspace((unsigned char)*p)) ++p; 726 727 if (*p=='"' || *p=='\'') { 728 char delim = *p; 729 730 start = ++p; 731 while(*p && *p!=delim) ++p; 732 733 if (*p == delim) 734 _system.assign(start, p++-start); 735 } else 736 _system.erase(); 737 } 738 739 740 void XMLFormat::print_header(std::ostream& out, bool lf) const 741 { 742 out << "<?xml version=\"" << _version << "\" encoding=\"" << _encoding << "\""; 743 744 if (_standalone != -1) 745 out << " standalone=\"yes\""; 746 747 out << "?>"; 748 749 if (lf) 750 out << _endl; 751 752 if (!_doctype.empty()) { 753 out << "<!DOCTYPE " << _doctype._name; 754 755 if (!_doctype._public.empty()) { 756 out << " PUBLIC \"" << _doctype._public << '"'; 757 758 if (lf) 759 out << _endl; 760 761 out << " \"" << _doctype._system << '"'; 762 } else if (!_doctype._system.empty()) 763 out << " SYSTEM \"" << _doctype._system << '"'; 764 765 out << "?>"; 766 767 if (lf) 768 out << _endl; 769 } 770 771 for(StyleSheetList::const_iterator it=_stylesheets.begin(); it!=_stylesheets.end(); ++it) { 772 it->print(out); 773 774 if (lf) 775 out << _endl; 776 } 777 778 /* if (!_additional.empty()) { 779 out << _additional; 780 781 if (lf) 782 out << _endl; 783 } */ 784 } 785 786 void StyleSheet::print(std::ostream& out) const 787 { 788 out << "<?xml-stylesheet" 789 " href=\"" << _href << "\"" 790 " type=\"" << _type << "\""; 791 792 if (!_title.empty()) 793 out << " title=\"" << _title << "\""; 794 795 if (!_media.empty()) 796 out << " media=\"" << _media << "\""; 797 798 if (!_charset.empty()) 799 out << " charset=\"" << _charset << "\""; 800 801 if (_alternate) 802 out << " alternate=\"yes\""; 803 804 out << "?>"; 805 } 806 807 808 /// return formated error message 809 std::string XMLError::str() const 810 { 811 std::ostringstream out; 812 813 out << *this; 814 815 return out.str(); 816 } 817 818 819 /// return merged error strings 820 XS_String XMLErrorList::str() const 821 { 822 std::ostringstream out; 823 824 for(const_iterator it=begin(); it!=end(); ++it) 825 out << *it << std::endl; 826 827 return out.str(); 828 } 829 830 831 void XMLReaderBase::finish_read() 832 { 833 if (_pos->_children.empty()) 834 _pos->_trailing.append(_content); 835 else 836 _pos->_children.back()->_trailing.append(_content); 837 838 _content.erase(); 839 } 840 841 842 /// store XML version and encoding into XML reader 843 void XMLReaderBase::XmlDeclHandler(const char* version, const char* encoding, int standalone) 844 { 845 if (version) 846 _format._version = version; 847 848 if (encoding) 849 _format._encoding = encoding; 850 851 _format._standalone = standalone; 852 } 853 854 855 /// notifications about XML start tag 856 void XMLReaderBase::StartElementHandler(const XS_String& name, const XMLNode::AttributeMap& attributes) 857 { 858 const char* s = _content.c_str(); 859 const char* e = s + _content.length(); 860 const char* p = s; 861 862 // search for content end leaving only white space for leading 863 for(p=e; p>s; --p) 864 if (!isspace((unsigned char)p[-1])) 865 break; 866 867 if (p != s) { 868 if (_pos->_children.empty()) { // no children in last node? 869 if (_last_tag == TAG_START) 870 _pos->_content.append(s, p-s); 871 else if (_last_tag == TAG_END) 872 _pos->_trailing.append(s, p-s); 873 else // TAG_NONE at root node 874 p = s; 875 } else 876 _pos->_children.back()->_trailing.append(s, p-s); 877 } 878 879 std::string leading; 880 881 if (p != e) 882 leading.assign(p, e-p); 883 884 XMLNode* node = new XMLNode(name, leading); 885 886 _pos.add_down(node); 887 888 #ifdef XMLNODE_LOCATION 889 node->_location = get_location(); 890 #endif 891 892 node->_attributes = attributes; 893 894 _last_tag = TAG_START; 895 _content.erase(); 896 } 897 898 /// notifications about XML end tag 899 void XMLReaderBase::EndElementHandler() 900 { 901 const char* s = _content.c_str(); 902 const char* e = s + _content.length(); 903 const char* p; 904 905 if (!strncmp(s,CDATA_START,9) && !strncmp(e-3,CDATA_END,3)) { 906 s += 9; 907 p = (e-=3); 908 909 _pos->_cdata_content = true; 910 } else { 911 // search for content end leaving only white space for _end_leading 912 for(p=e; p>s; --p) 913 if (!isspace((unsigned char)p[-1])) 914 break; 915 916 _pos->_cdata_content = false; 917 } 918 919 if (p != s) { 920 if (_pos->_children.empty()) // no children in current node? 921 _pos->_content.append(s, p-s); 922 else if (_last_tag == TAG_START) 923 _pos->_content.append(s, p-s); 924 else 925 _pos->_children.back()->_trailing.append(s, p-s); 926 } 927 928 if (p != e) 929 _pos->_end_leading.assign(p, e-p); 930 931 _pos.back(); 932 933 _last_tag = TAG_END; 934 _content.erase(); 935 } 936 937 #if defined(XS_USE_XERCES) || defined(XS_USE_EXPAT) 938 /// store content, white space and comments 939 void XMLReaderBase::DefaultHandler(const XML_Char* s, int len) 940 { 941 #if defined(XML_UNICODE) || defined(XS_USE_XERCES) 942 _content.append(String_from_XML_Char(s, len)); 943 #else 944 _content.append(s, len); 945 #endif 946 } 947 #endif 948 949 950 XS_String XMLWriter::s_empty_attr; 951 952 void XMLWriter::create(const XS_String& name) 953 { 954 if (!_stack.empty()) { 955 StackEntry& last = _stack.top(); 956 957 if (last._state < PRE_CLOSED) { 958 write_attributes(last); 959 close_pre(last); 960 } 961 962 ++last._children; 963 } 964 965 StackEntry entry; 966 entry._node_name = name; 967 _stack.push(entry); 968 969 write_pre(entry); 970 } 971 972 bool XMLWriter::back() 973 { 974 if (!_stack.empty()) { 975 write_post(_stack.top()); 976 977 _stack.pop(); 978 return true; 979 } else 980 return false; 981 } 982 983 void XMLWriter::close_pre(StackEntry& entry) 984 { 985 _out << '>'; 986 987 entry._state = PRE_CLOSED; 988 } 989 990 void XMLWriter::write_pre(StackEntry& entry) 991 { 992 if (_format._pretty >= PRETTY_LINEFEED) 993 _out << _format._endl; 994 995 if (_format._pretty == PRETTY_INDENT) { 996 for(size_t i=_stack.size(); --i>0; ) 997 _out << XML_INDENT_SPACE; 998 } 999 1000 _out << '<' << EncodeXMLString(entry._node_name); 1001 //entry._state = PRE; 1002 } 1003 1004 void XMLWriter::write_attributes(StackEntry& entry) 1005 { 1006 for(AttrMap::const_iterator it=entry._attributes.begin(); it!=entry._attributes.end(); ++it) 1007 _out << ' ' << EncodeXMLString(it->first) << "=\"" << EncodeXMLString(it->second) << "\""; 1008 1009 entry._state = ATTRIBUTES; 1010 } 1011 1012 void XMLWriter::write_post(StackEntry& entry) 1013 { 1014 if (entry._state < ATTRIBUTES) 1015 write_attributes(entry); 1016 1017 if (entry._children || !entry._content.empty()) { 1018 if (entry._state < PRE_CLOSED) 1019 close_pre(entry); 1020 1021 _out << entry._content; 1022 //entry._state = CONTENT; 1023 1024 if (_format._pretty>=PRETTY_LINEFEED && entry._content.empty()) 1025 _out << _format._endl; 1026 1027 if (_format._pretty==PRETTY_INDENT && entry._content.empty()) { 1028 for(size_t i=_stack.size(); --i>0; ) 1029 _out << XML_INDENT_SPACE; 1030 } 1031 1032 _out << "</" << EncodeXMLString(entry._node_name) << ">"; 1033 } else { 1034 _out << "/>"; 1035 } 1036 1037 entry._state = POST; 1038 } 1039 1040 1041 } // namespace XMLStorage 1042