1///////////////////////////////////////////////////////////////////////////// 2// Name: src/xml/xml.cpp 3// Purpose: wxXmlDocument - XML parser & data holder class 4// Author: Vaclav Slavik 5// Created: 2000/03/05 6// RCS-ID: $Id: xml.cpp 65726 2010-10-02 15:47:41Z TIK $ 7// Copyright: (c) 2000 Vaclav Slavik 8// Licence: wxWindows licence 9///////////////////////////////////////////////////////////////////////////// 10 11// For compilers that support precompilation, includes "wx.h". 12#include "wx/wxprec.h" 13 14#ifdef __BORLANDC__ 15 #pragma hdrstop 16#endif 17 18#if wxUSE_XML 19 20#include "wx/xml/xml.h" 21 22#ifndef WX_PRECOMP 23 #include "wx/intl.h" 24 #include "wx/log.h" 25 #include "wx/app.h" 26#endif 27 28#include "wx/wfstream.h" 29#include "wx/datstrm.h" 30#include "wx/zstream.h" 31#include "wx/strconv.h" 32 33#include "expat.h" // from Expat 34 35// DLL options compatibility check: 36WX_CHECK_BUILD_OPTIONS("wxXML") 37 38 39IMPLEMENT_CLASS(wxXmlDocument, wxObject) 40 41 42// a private utility used by wxXML 43static bool wxIsWhiteOnly(const wxChar *buf); 44 45 46//----------------------------------------------------------------------------- 47// wxXmlNode 48//----------------------------------------------------------------------------- 49 50wxXmlNode::wxXmlNode(wxXmlNode *parent,wxXmlNodeType type, 51 const wxString& name, const wxString& content, 52 wxXmlProperty *props, wxXmlNode *next) 53 : m_type(type), m_name(name), m_content(content), 54 m_properties(props), m_parent(parent), 55 m_children(NULL), m_next(next) 56{ 57 if (m_parent) 58 { 59 if (m_parent->m_children) 60 { 61 m_next = m_parent->m_children; 62 m_parent->m_children = this; 63 } 64 else 65 m_parent->m_children = this; 66 } 67} 68 69wxXmlNode::wxXmlNode(wxXmlNodeType type, const wxString& name, 70 const wxString& content) 71 : m_type(type), m_name(name), m_content(content), 72 m_properties(NULL), m_parent(NULL), 73 m_children(NULL), m_next(NULL) 74{} 75 76wxXmlNode::wxXmlNode(const wxXmlNode& node) 77{ 78 m_next = NULL; 79 m_parent = NULL; 80 DoCopy(node); 81} 82 83wxXmlNode::~wxXmlNode() 84{ 85 wxXmlNode *c, *c2; 86 for (c = m_children; c; c = c2) 87 { 88 c2 = c->m_next; 89 delete c; 90 } 91 92 wxXmlProperty *p, *p2; 93 for (p = m_properties; p; p = p2) 94 { 95 p2 = p->GetNext(); 96 delete p; 97 } 98} 99 100wxXmlNode& wxXmlNode::operator=(const wxXmlNode& node) 101{ 102 wxDELETE(m_properties); 103 wxDELETE(m_children); 104 DoCopy(node); 105 return *this; 106} 107 108void wxXmlNode::DoCopy(const wxXmlNode& node) 109{ 110 m_type = node.m_type; 111 m_name = node.m_name; 112 m_content = node.m_content; 113 m_children = NULL; 114 115 wxXmlNode *n = node.m_children; 116 while (n) 117 { 118 AddChild(new wxXmlNode(*n)); 119 n = n->GetNext(); 120 } 121 122 m_properties = NULL; 123 wxXmlProperty *p = node.m_properties; 124 while (p) 125 { 126 AddProperty(p->GetName(), p->GetValue()); 127 p = p->GetNext(); 128 } 129} 130 131bool wxXmlNode::HasProp(const wxString& propName) const 132{ 133 wxXmlProperty *prop = GetProperties(); 134 135 while (prop) 136 { 137 if (prop->GetName() == propName) return true; 138 prop = prop->GetNext(); 139 } 140 141 return false; 142} 143 144bool wxXmlNode::GetPropVal(const wxString& propName, wxString *value) const 145{ 146 wxCHECK_MSG( value, false, wxT("value argument must not be NULL") ); 147 148 wxXmlProperty *prop = GetProperties(); 149 150 while (prop) 151 { 152 if (prop->GetName() == propName) 153 { 154 *value = prop->GetValue(); 155 return true; 156 } 157 prop = prop->GetNext(); 158 } 159 160 return false; 161} 162 163wxString wxXmlNode::GetPropVal(const wxString& propName, const wxString& defaultVal) const 164{ 165 wxString tmp; 166 if (GetPropVal(propName, &tmp)) 167 return tmp; 168 169 return defaultVal; 170} 171 172void wxXmlNode::AddChild(wxXmlNode *child) 173{ 174 if (m_children == NULL) 175 m_children = child; 176 else 177 { 178 wxXmlNode *ch = m_children; 179 while (ch->m_next) ch = ch->m_next; 180 ch->m_next = child; 181 } 182 child->m_next = NULL; 183 child->m_parent = this; 184} 185 186bool wxXmlNode::InsertChild(wxXmlNode *child, wxXmlNode *before_node) 187{ 188 wxCHECK_MSG(before_node == NULL || before_node->GetParent() == this, false, 189 wxT("wxXmlNode::InsertChild - the node has incorrect parent")); 190 wxCHECK_MSG(child, false, wxT("Cannot insert a NULL pointer!")); 191 192 if (m_children == before_node) 193 m_children = child; 194 else if (m_children == NULL) 195 { 196 if (before_node != NULL) 197 return false; // we have no children so we don't need to search 198 m_children = child; 199 } 200 else if (before_node == NULL) 201 { 202 // prepend child 203 child->m_parent = this; 204 child->m_next = m_children; 205 m_children = child; 206 return true; 207 } 208 else 209 { 210 wxXmlNode *ch = m_children; 211 while (ch && ch->m_next != before_node) ch = ch->m_next; 212 if (!ch) 213 return false; // before_node not found 214 ch->m_next = child; 215 } 216 217 child->m_parent = this; 218 child->m_next = before_node; 219 return true; 220} 221 222// inserts a new node right after 'precedingNode' 223bool wxXmlNode::InsertChildAfter(wxXmlNode *child, wxXmlNode *precedingNode) 224{ 225 wxCHECK_MSG( child, false, wxT("cannot insert a NULL node!") ); 226 wxCHECK_MSG( child->m_parent == NULL, false, wxT("node already has a parent") ); 227 wxCHECK_MSG( child->m_next == NULL, false, wxT("node already has m_next") ); 228 wxCHECK_MSG( precedingNode == NULL || precedingNode->m_parent == this, false, 229 wxT("precedingNode has wrong parent") ); 230 231 if ( precedingNode ) 232 { 233 child->m_next = precedingNode->m_next; 234 precedingNode->m_next = child; 235 } 236 else // precedingNode == NULL 237 { 238 wxCHECK_MSG( m_children == NULL, false, 239 wxT("NULL precedingNode only makes sense when there are no children") ); 240 241 child->m_next = m_children; 242 m_children = child; 243 } 244 245 child->m_parent = this; 246 return true; 247} 248 249 250bool wxXmlNode::RemoveChild(wxXmlNode *child) 251{ 252 if (m_children == NULL) 253 return false; 254 else if (m_children == child) 255 { 256 m_children = child->m_next; 257 child->m_parent = NULL; 258 child->m_next = NULL; 259 return true; 260 } 261 else 262 { 263 wxXmlNode *ch = m_children; 264 while (ch->m_next) 265 { 266 if (ch->m_next == child) 267 { 268 ch->m_next = child->m_next; 269 child->m_parent = NULL; 270 child->m_next = NULL; 271 return true; 272 } 273 ch = ch->m_next; 274 } 275 return false; 276 } 277} 278 279void wxXmlNode::AddProperty(const wxString& name, const wxString& value) 280{ 281 AddProperty(new wxXmlProperty(name, value, NULL)); 282} 283 284void wxXmlNode::AddProperty(wxXmlProperty *prop) 285{ 286 if (m_properties == NULL) 287 m_properties = prop; 288 else 289 { 290 wxXmlProperty *p = m_properties; 291 while (p->GetNext()) p = p->GetNext(); 292 p->SetNext(prop); 293 } 294} 295 296bool wxXmlNode::DeleteProperty(const wxString& name) 297{ 298 wxXmlProperty *prop; 299 300 if (m_properties == NULL) 301 return false; 302 303 else if (m_properties->GetName() == name) 304 { 305 prop = m_properties; 306 m_properties = prop->GetNext(); 307 prop->SetNext(NULL); 308 delete prop; 309 return true; 310 } 311 312 else 313 { 314 wxXmlProperty *p = m_properties; 315 while (p->GetNext()) 316 { 317 if (p->GetNext()->GetName() == name) 318 { 319 prop = p->GetNext(); 320 p->SetNext(prop->GetNext()); 321 prop->SetNext(NULL); 322 delete prop; 323 return true; 324 } 325 p = p->GetNext(); 326 } 327 return false; 328 } 329} 330 331wxString wxXmlNode::GetNodeContent() const 332{ 333 wxXmlNode *n = GetChildren(); 334 335 while (n) 336 { 337 if (n->GetType() == wxXML_TEXT_NODE || 338 n->GetType() == wxXML_CDATA_SECTION_NODE) 339 return n->GetContent(); 340 n = n->GetNext(); 341 } 342 return wxEmptyString; 343} 344 345int wxXmlNode::GetDepth(wxXmlNode *grandparent) const 346{ 347 const wxXmlNode *n = this; 348 int ret = -1; 349 350 do 351 { 352 ret++; 353 n = n->GetParent(); 354 if (n == grandparent) 355 return ret; 356 357 } while (n); 358 359 return wxNOT_FOUND; 360} 361 362bool wxXmlNode::IsWhitespaceOnly() const 363{ 364 return wxIsWhiteOnly(m_content); 365} 366 367 368 369//----------------------------------------------------------------------------- 370// wxXmlDocument 371//----------------------------------------------------------------------------- 372 373wxXmlDocument::wxXmlDocument() 374 : m_version(wxT("1.0")), m_fileEncoding(wxT("utf-8")), m_root(NULL) 375{ 376#if !wxUSE_UNICODE 377 m_encoding = wxT("UTF-8"); 378#endif 379} 380 381wxXmlDocument::wxXmlDocument(const wxString& filename, const wxString& encoding) 382 :wxObject(), m_root(NULL) 383{ 384 if ( !Load(filename, encoding) ) 385 { 386 wxDELETE(m_root); 387 } 388} 389 390wxXmlDocument::wxXmlDocument(wxInputStream& stream, const wxString& encoding) 391 :wxObject(), m_root(NULL) 392{ 393 if ( !Load(stream, encoding) ) 394 { 395 wxDELETE(m_root); 396 } 397} 398 399wxXmlDocument::wxXmlDocument(const wxXmlDocument& doc) 400 :wxObject() 401{ 402 DoCopy(doc); 403} 404 405wxXmlDocument& wxXmlDocument::operator=(const wxXmlDocument& doc) 406{ 407 wxDELETE(m_root); 408 DoCopy(doc); 409 return *this; 410} 411 412void wxXmlDocument::DoCopy(const wxXmlDocument& doc) 413{ 414 m_version = doc.m_version; 415#if !wxUSE_UNICODE 416 m_encoding = doc.m_encoding; 417#endif 418 m_fileEncoding = doc.m_fileEncoding; 419 420 if (doc.m_root) 421 m_root = new wxXmlNode(*doc.m_root); 422 else 423 m_root = NULL; 424} 425 426bool wxXmlDocument::Load(const wxString& filename, const wxString& encoding, int flags) 427{ 428 wxFileInputStream stream(filename); 429 if (!stream.Ok()) 430 return false; 431 return Load(stream, encoding, flags); 432} 433 434bool wxXmlDocument::Save(const wxString& filename, int indentstep) const 435{ 436 wxFileOutputStream stream(filename); 437 if (!stream.Ok()) 438 return false; 439 return Save(stream, indentstep); 440} 441 442 443 444//----------------------------------------------------------------------------- 445// wxXmlDocument loading routines 446//----------------------------------------------------------------------------- 447 448// converts Expat-produced string in UTF-8 into wxString using the specified 449// conv or keep in UTF-8 if conv is NULL 450static wxString CharToString(wxMBConv *conv, 451 const char *s, size_t len = wxString::npos) 452{ 453#if wxUSE_UNICODE 454 wxUnusedVar(conv); 455 456 return wxString(s, wxConvUTF8, len); 457#else // !wxUSE_UNICODE 458 if ( conv ) 459 { 460 // there can be no embedded NULs in this string so we don't need the 461 // output length, it will be NUL-terminated 462 const wxWCharBuffer wbuf( 463 wxConvUTF8.cMB2WC(s, len == wxString::npos ? wxNO_LEN : len, NULL)); 464 465 return wxString(wbuf, *conv); 466 } 467 else // already in UTF-8, no conversion needed 468 { 469 return wxString(s, len != wxString::npos ? len : strlen(s)); 470 } 471#endif // wxUSE_UNICODE/!wxUSE_UNICODE 472} 473 474// returns true if the given string contains only whitespaces 475bool wxIsWhiteOnly(const wxChar *buf) 476{ 477 for (const wxChar *c = buf; *c != wxT('\0'); c++) 478 if (*c != wxT(' ') && *c != wxT('\t') && *c != wxT('\n') && *c != wxT('\r')) 479 return false; 480 return true; 481} 482 483 484struct wxXmlParsingContext 485{ 486 wxXmlParsingContext() 487 : conv(NULL), 488 root(NULL), 489 node(NULL), 490 lastChild(NULL), 491 lastAsText(NULL), 492 removeWhiteOnlyNodes(false) 493 {} 494 495 wxMBConv *conv; 496 wxXmlNode *root; 497 wxXmlNode *node; // the node being parsed 498 wxXmlNode *lastChild; // the last child of "node" 499 wxXmlNode *lastAsText; // the last _text_ child of "node" 500 wxString encoding; 501 wxString version; 502 bool removeWhiteOnlyNodes; 503}; 504 505// checks that ctx->lastChild is in consistent state 506#define ASSERT_LAST_CHILD_OK(ctx) \ 507 wxASSERT( ctx->lastChild == NULL || \ 508 ctx->lastChild->GetNext() == NULL ); \ 509 wxASSERT( ctx->lastChild == NULL || \ 510 ctx->lastChild->GetParent() == ctx->node ) 511 512extern "C" { 513static void StartElementHnd(void *userData, const char *name, const char **atts) 514{ 515 wxXmlParsingContext *ctx = (wxXmlParsingContext*)userData; 516 wxXmlNode *node = new wxXmlNode(wxXML_ELEMENT_NODE, CharToString(ctx->conv, name)); 517 const char **a = atts; 518 while (*a) 519 { 520 node->AddProperty(CharToString(ctx->conv, a[0]), CharToString(ctx->conv, a[1])); 521 a += 2; 522 } 523 if (ctx->root == NULL) 524 { 525 ctx->root = node; 526 } 527 else 528 { 529 ASSERT_LAST_CHILD_OK(ctx); 530 ctx->node->InsertChildAfter(node, ctx->lastChild); 531 } 532 533 ctx->lastAsText = NULL; 534 ctx->lastChild = NULL; // our new node "node" has no children yet 535 536 ctx->node = node; 537} 538} 539 540extern "C" { 541static void EndElementHnd(void *userData, const char* WXUNUSED(name)) 542{ 543 wxXmlParsingContext *ctx = (wxXmlParsingContext*)userData; 544 545 // we're exiting the last children of ctx->node->GetParent() and going 546 // back one level up, so current value of ctx->node points to the last 547 // child of ctx->node->GetParent() 548 ctx->lastChild = ctx->node; 549 550 ctx->node = ctx->node->GetParent(); 551 ctx->lastAsText = NULL; 552} 553} 554 555extern "C" { 556static void TextHnd(void *userData, const char *s, int len) 557{ 558 wxXmlParsingContext *ctx = (wxXmlParsingContext*)userData; 559 wxString str = CharToString(ctx->conv, s, len); 560 561 if (ctx->lastAsText) 562 { 563 ctx->lastAsText->SetContent(ctx->lastAsText->GetContent() + str); 564 } 565 else 566 { 567 bool whiteOnly = false; 568 if (ctx->removeWhiteOnlyNodes) 569 whiteOnly = wxIsWhiteOnly(str); 570 571 if (!whiteOnly) 572 { 573 wxXmlNode *textnode = 574 new wxXmlNode(wxXML_TEXT_NODE, wxT("text"), str); 575 576 ASSERT_LAST_CHILD_OK(ctx); 577 ctx->node->InsertChildAfter(textnode, ctx->lastChild); 578 ctx->lastChild= ctx->lastAsText = textnode; 579 } 580 } 581} 582} 583 584extern "C" { 585static void StartCdataHnd(void *userData) 586{ 587 wxXmlParsingContext *ctx = (wxXmlParsingContext*)userData; 588 589 wxXmlNode *textnode = 590 new wxXmlNode(wxXML_CDATA_SECTION_NODE, wxT("cdata"),wxT("")); 591 592 ASSERT_LAST_CHILD_OK(ctx); 593 ctx->node->InsertChildAfter(textnode, ctx->lastChild); 594 ctx->lastChild= ctx->lastAsText = textnode; 595} 596} 597 598extern "C" { 599static void CommentHnd(void *userData, const char *data) 600{ 601 wxXmlParsingContext *ctx = (wxXmlParsingContext*)userData; 602 603 if (ctx->node) 604 { 605 // VS: ctx->node == NULL happens if there is a comment before 606 // the root element (e.g. wxDesigner's output). We ignore such 607 // comments, no big deal... 608 wxXmlNode *commentnode = 609 new wxXmlNode(wxXML_COMMENT_NODE, 610 wxT("comment"), CharToString(ctx->conv, data)); 611 ASSERT_LAST_CHILD_OK(ctx); 612 ctx->node->InsertChildAfter(commentnode, ctx->lastChild); 613 ctx->lastChild = commentnode; 614 } 615 ctx->lastAsText = NULL; 616} 617} 618 619extern "C" { 620static void DefaultHnd(void *userData, const char *s, int len) 621{ 622 // XML header: 623 if (len > 6 && memcmp(s, "<?xml ", 6) == 0) 624 { 625 wxXmlParsingContext *ctx = (wxXmlParsingContext*)userData; 626 627 wxString buf = CharToString(ctx->conv, s, (size_t)len); 628 int pos; 629 pos = buf.Find(wxT("encoding=")); 630 if (pos != wxNOT_FOUND) 631 ctx->encoding = buf.Mid(pos + 10).BeforeFirst(buf[(size_t)pos+9]); 632 pos = buf.Find(wxT("version=")); 633 if (pos != wxNOT_FOUND) 634 ctx->version = buf.Mid(pos + 9).BeforeFirst(buf[(size_t)pos+8]); 635 } 636} 637} 638 639extern "C" { 640static int UnknownEncodingHnd(void * WXUNUSED(encodingHandlerData), 641 const XML_Char *name, XML_Encoding *info) 642{ 643 // We must build conversion table for expat. The easiest way to do so 644 // is to let wxCSConv convert as string containing all characters to 645 // wide character representation: 646 wxString str(name, wxConvLibc); 647 wxCSConv conv(str); 648 char mbBuf[2]; 649 wchar_t wcBuf[10]; 650 size_t i; 651 652 mbBuf[1] = 0; 653 info->map[0] = 0; 654 for (i = 0; i < 255; i++) 655 { 656 mbBuf[0] = (char)(i+1); 657 if (conv.MB2WC(wcBuf, mbBuf, 2) == (size_t)-1) 658 { 659 // invalid/undefined byte in the encoding: 660 info->map[i+1] = -1; 661 } 662 info->map[i+1] = (int)wcBuf[0]; 663 } 664 665 info->data = NULL; 666 info->convert = NULL; 667 info->release = NULL; 668 669 return 1; 670} 671} 672 673bool wxXmlDocument::Load(wxInputStream& stream, const wxString& encoding, int flags) 674{ 675#if wxUSE_UNICODE 676 (void)encoding; 677#else 678 m_encoding = encoding; 679#endif 680 681 const size_t BUFSIZE = 1024; 682 char buf[BUFSIZE]; 683 wxXmlParsingContext ctx; 684 bool done; 685 XML_Parser parser = XML_ParserCreate(NULL); 686 687 ctx.root = ctx.node = NULL; 688 ctx.encoding = wxT("UTF-8"); // default in absence of encoding="" 689 ctx.conv = NULL; 690#if !wxUSE_UNICODE 691 if ( encoding.CmpNoCase(wxT("UTF-8")) != 0 ) 692 ctx.conv = new wxCSConv(encoding); 693#endif 694 ctx.removeWhiteOnlyNodes = (flags & wxXMLDOC_KEEP_WHITESPACE_NODES) == 0; 695 696 XML_SetUserData(parser, (void*)&ctx); 697 XML_SetElementHandler(parser, StartElementHnd, EndElementHnd); 698 XML_SetCharacterDataHandler(parser, TextHnd); 699 XML_SetStartCdataSectionHandler(parser, StartCdataHnd); 700 XML_SetCommentHandler(parser, CommentHnd); 701 XML_SetDefaultHandler(parser, DefaultHnd); 702 XML_SetUnknownEncodingHandler(parser, UnknownEncodingHnd, NULL); 703 704 bool ok = true; 705 do 706 { 707 size_t len = stream.Read(buf, BUFSIZE).LastRead(); 708 done = (len < BUFSIZE); 709 if (!XML_Parse(parser, buf, len, done)) 710 { 711 wxString error(XML_ErrorString(XML_GetErrorCode(parser)), 712 *wxConvCurrent); 713 wxLogError(_("XML parsing error: '%s' at line %d"), 714 error.c_str(), 715 XML_GetCurrentLineNumber(parser)); 716 ok = false; 717 break; 718 } 719 } while (!done); 720 721 if (ok) 722 { 723 if (!ctx.version.empty()) 724 SetVersion(ctx.version); 725 if (!ctx.encoding.empty()) 726 SetFileEncoding(ctx.encoding); 727 SetRoot(ctx.root); 728 } 729 else 730 { 731 delete ctx.root; 732 } 733 734 XML_ParserFree(parser); 735#if !wxUSE_UNICODE 736 if ( ctx.conv ) 737 delete ctx.conv; 738#endif 739 740 return ok; 741 742} 743 744 745 746//----------------------------------------------------------------------------- 747// wxXmlDocument saving routines 748//----------------------------------------------------------------------------- 749 750// write string to output: 751inline static void OutputString(wxOutputStream& stream, const wxString& str, 752 wxMBConv *convMem = NULL, 753 wxMBConv *convFile = NULL) 754{ 755 if (str.empty()) 756 return; 757 758#if wxUSE_UNICODE 759 wxUnusedVar(convMem); 760 761 const wxWX2MBbuf buf(str.mb_str(*(convFile ? convFile : &wxConvUTF8))); 762 if ( !buf ) 763 return; 764 stream.Write((const char*)buf, strlen((const char*)buf)); 765#else // !wxUSE_UNICODE 766 if ( convFile && convMem ) 767 { 768 wxString str2(str.wc_str(*convMem), *convFile); 769 stream.Write(str2.mb_str(), str2.Len()); 770 } 771 else // no conversions to do 772 { 773 stream.Write(str.mb_str(), str.Len()); 774 } 775#endif // wxUSE_UNICODE/!wxUSE_UNICODE 776} 777 778 779enum EscapingMode 780{ 781 Escape_Text, 782 Escape_Attribute 783}; 784 785// Same as above, but create entities first. 786// Translates '<' to "<", '>' to ">" and so on, according to the spec: 787// http://www.w3.org/TR/2000/WD-xml-c14n-20000119.html#charescaping 788static void OutputEscapedString(wxOutputStream& stream, 789 const wxString& str, 790 wxMBConv *convMem, 791 wxMBConv *convFile, 792 EscapingMode mode) 793{ 794 const size_t len = str.Len(); 795 796 wxString escaped; 797 escaped.reserve( len ); 798 799 for (size_t i = 0; i < len; i++) 800 { 801 const wxChar c = str.GetChar(i); 802 803 switch ( c ) 804 { 805 case '<': 806 escaped.append(wxT("<")); 807 break; 808 case '>': 809 escaped.append(wxT(">")); 810 break; 811 case '&': 812 escaped.append(wxT("&")); 813 break; 814 case '\r': 815 escaped.append(wxT("
")); 816 break; 817 default: 818 if ( mode == Escape_Attribute ) 819 { 820 switch ( c ) 821 { 822 case '"': 823 escaped.append(wxT(""")); 824 break; 825 case '\t': 826 escaped.append(wxT("	")); 827 break; 828 case '\n': 829 escaped.append(wxT("
")); 830 break; 831 default: 832 escaped.append(c); 833 } 834 } 835 else 836 { 837 escaped.append(c); 838 } 839 } 840 } 841 OutputString(stream, escaped, convMem, convFile); 842} 843 844inline static void OutputIndentation(wxOutputStream& stream, int indent) 845{ 846 wxString str = wxT("\n"); 847 for (int i = 0; i < indent; i++) 848 str << wxT(' ') << wxT(' '); 849 OutputString(stream, str); 850} 851 852static void OutputNode(wxOutputStream& stream, wxXmlNode *node, int indent, 853 wxMBConv *convMem, wxMBConv *convFile, int indentstep) 854{ 855 wxXmlNode *n, *prev; 856 wxXmlProperty *prop; 857 858 switch (node->GetType()) 859 { 860 case wxXML_CDATA_SECTION_NODE: 861 OutputString( stream, wxT("<![CDATA[")); 862 OutputString( stream, node->GetContent() ); 863 OutputString( stream, wxT("]]>") ); 864 break; 865 866 case wxXML_TEXT_NODE: 867 OutputEscapedString(stream, node->GetContent(), 868 convMem, convFile, 869 Escape_Text); 870 break; 871 872 case wxXML_ELEMENT_NODE: 873 OutputString(stream, wxT("<")); 874 OutputString(stream, node->GetName()); 875 876 prop = node->GetProperties(); 877 while (prop) 878 { 879 OutputString(stream, wxT(" ") + prop->GetName() + wxT("=\"")); 880 OutputEscapedString(stream, prop->GetValue(), 881 convMem, convFile, 882 Escape_Attribute); 883 OutputString(stream, wxT("\"")); 884 prop = prop->GetNext(); 885 } 886 887 if (node->GetChildren()) 888 { 889 OutputString(stream, wxT(">")); 890 prev = NULL; 891 n = node->GetChildren(); 892 while (n) 893 { 894 if (indentstep >= 0 && n && n->GetType() != wxXML_TEXT_NODE) 895 OutputIndentation(stream, indent + indentstep); 896 OutputNode(stream, n, indent + indentstep, convMem, convFile, indentstep); 897 prev = n; 898 n = n->GetNext(); 899 } 900 if (indentstep >= 0 && prev && prev->GetType() != wxXML_TEXT_NODE) 901 OutputIndentation(stream, indent); 902 OutputString(stream, wxT("</")); 903 OutputString(stream, node->GetName()); 904 OutputString(stream, wxT(">")); 905 } 906 else 907 OutputString(stream, wxT("/>")); 908 break; 909 910 case wxXML_COMMENT_NODE: 911 OutputString(stream, wxT("<!--")); 912 OutputString(stream, node->GetContent(), convMem, convFile); 913 OutputString(stream, wxT("-->")); 914 break; 915 916 default: 917 wxFAIL_MSG(wxT("unsupported node type")); 918 } 919} 920 921bool wxXmlDocument::Save(wxOutputStream& stream, int indentstep) const 922{ 923 if ( !IsOk() ) 924 return false; 925 926 wxString s; 927 928 wxMBConv *convMem = NULL, 929 *convFile; 930 931#if wxUSE_UNICODE 932 convFile = new wxCSConv(GetFileEncoding()); 933 convMem = NULL; 934#else 935 if ( GetFileEncoding().CmpNoCase(GetEncoding()) != 0 ) 936 { 937 convFile = new wxCSConv(GetFileEncoding()); 938 convMem = new wxCSConv(GetEncoding()); 939 } 940 else // file and in-memory encodings are the same, no conversion needed 941 { 942 convFile = 943 convMem = NULL; 944 } 945#endif 946 947 s.Printf(wxT("<?xml version=\"%s\" encoding=\"%s\"?>\n"), 948 GetVersion().c_str(), GetFileEncoding().c_str()); 949 OutputString(stream, s); 950 951 OutputNode(stream, GetRoot(), 0, convMem, convFile, indentstep); 952 OutputString(stream, wxT("\n")); 953 954 delete convFile; 955 delete convMem; 956 957 return true; 958} 959 960#endif // wxUSE_XML 961