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