1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/xml/xml.cpp
3 // Purpose: wxXmlDocument - XML parser & data holder class
4 // Author: Vaclav Slavik
7 // Copyright: (c) 2000 Vaclav Slavik
8 // Licence: wxWindows licence
9 /////////////////////////////////////////////////////////////////////////////
11 // For compilers that support precompilation, includes "wx.h".
12 #include "wx/wxprec.h"
20 #include "wx/xml/xml.h"
28 #include "wx/wfstream.h"
29 #include "wx/datstrm.h"
30 #include "wx/zstream.h"
31 #include "wx/strconv.h"
32 #include "wx/scopedptr.h"
33 #include "wx/versioninfo.h"
35 #include "expat.h" // from Expat
37 // DLL options compatibility check:
38 WX_CHECK_BUILD_OPTIONS("wxXML")
41 IMPLEMENT_CLASS(wxXmlDocument
, wxObject
)
44 // a private utility used by wxXML
45 static bool wxIsWhiteOnly(const wxString
& buf
);
48 //-----------------------------------------------------------------------------
50 //-----------------------------------------------------------------------------
52 wxXmlNode::wxXmlNode(wxXmlNode
*parent
,wxXmlNodeType type
,
53 const wxString
& name
, const wxString
& content
,
54 wxXmlAttribute
*attrs
, wxXmlNode
*next
, int lineNo
)
55 : m_type(type
), m_name(name
), m_content(content
),
56 m_attrs(attrs
), m_parent(parent
),
57 m_children(NULL
), m_next(next
),
61 wxASSERT_MSG ( type
!= wxXML_ELEMENT_NODE
|| content
.empty(), "element nodes can't have content" );
65 if (m_parent
->m_children
)
67 m_next
= m_parent
->m_children
;
68 m_parent
->m_children
= this;
71 m_parent
->m_children
= this;
75 wxXmlNode::wxXmlNode(wxXmlNodeType type
, const wxString
& name
,
76 const wxString
& content
,
78 : m_type(type
), m_name(name
), m_content(content
),
79 m_attrs(NULL
), m_parent(NULL
),
80 m_children(NULL
), m_next(NULL
),
81 m_lineNo(lineNo
), m_noConversion(false)
83 wxASSERT_MSG ( type
!= wxXML_ELEMENT_NODE
|| content
.empty(), "element nodes can't have content" );
86 wxXmlNode::wxXmlNode(const wxXmlNode
& node
)
93 wxXmlNode::~wxXmlNode()
96 for (c
= m_children
; c
; c
= c2
)
102 wxXmlAttribute
*p
, *p2
;
103 for (p
= m_attrs
; p
; p
= p2
)
110 wxXmlNode
& wxXmlNode::operator=(const wxXmlNode
& node
)
113 wxDELETE(m_children
);
118 void wxXmlNode::DoCopy(const wxXmlNode
& node
)
120 m_type
= node
.m_type
;
121 m_name
= node
.m_name
;
122 m_content
= node
.m_content
;
123 m_lineNo
= node
.m_lineNo
;
124 m_noConversion
= node
.m_noConversion
;
127 wxXmlNode
*n
= node
.m_children
;
130 AddChild(new wxXmlNode(*n
));
135 wxXmlAttribute
*p
= node
.m_attrs
;
138 AddAttribute(p
->GetName(), p
->GetValue());
143 bool wxXmlNode::HasAttribute(const wxString
& attrName
) const
145 wxXmlAttribute
*attr
= GetAttributes();
149 if (attr
->GetName() == attrName
) return true;
150 attr
= attr
->GetNext();
156 bool wxXmlNode::GetAttribute(const wxString
& attrName
, wxString
*value
) const
158 wxCHECK_MSG( value
, false, "value argument must not be NULL" );
160 wxXmlAttribute
*attr
= GetAttributes();
164 if (attr
->GetName() == attrName
)
166 *value
= attr
->GetValue();
169 attr
= attr
->GetNext();
175 wxString
wxXmlNode::GetAttribute(const wxString
& attrName
, const wxString
& defaultVal
) const
178 if (GetAttribute(attrName
, &tmp
))
184 void wxXmlNode::AddChild(wxXmlNode
*child
)
186 if (m_children
== NULL
)
190 wxXmlNode
*ch
= m_children
;
191 while (ch
->m_next
) ch
= ch
->m_next
;
194 child
->m_next
= NULL
;
195 child
->m_parent
= this;
198 // inserts a new node in front of 'followingNode'
199 bool wxXmlNode::InsertChild(wxXmlNode
*child
, wxXmlNode
*followingNode
)
201 wxCHECK_MSG( child
, false, "cannot insert a NULL node!" );
202 wxCHECK_MSG( child
->m_parent
== NULL
, false, "node already has a parent" );
203 wxCHECK_MSG( child
->m_next
== NULL
, false, "node already has m_next" );
204 wxCHECK_MSG( followingNode
== NULL
|| followingNode
->GetParent() == this,
206 "wxXmlNode::InsertChild - followingNode has incorrect parent" );
208 // this is for backward compatibility, NULL was allowed here thanks to
209 // the confusion about followingNode's meaning
210 if ( followingNode
== NULL
)
211 followingNode
= m_children
;
213 if ( m_children
== followingNode
)
215 child
->m_next
= m_children
;
220 wxXmlNode
*ch
= m_children
;
221 while ( ch
&& ch
->m_next
!= followingNode
)
225 wxFAIL_MSG( "followingNode has this node as parent, but couldn't be found among children" );
229 child
->m_next
= followingNode
;
233 child
->m_parent
= this;
237 // inserts a new node right after 'precedingNode'
238 bool wxXmlNode::InsertChildAfter(wxXmlNode
*child
, wxXmlNode
*precedingNode
)
240 wxCHECK_MSG( child
, false, "cannot insert a NULL node!" );
241 wxCHECK_MSG( child
->m_parent
== NULL
, false, "node already has a parent" );
242 wxCHECK_MSG( child
->m_next
== NULL
, false, "node already has m_next" );
243 wxCHECK_MSG( precedingNode
== NULL
|| precedingNode
->m_parent
== this, false,
244 "precedingNode has wrong parent" );
248 child
->m_next
= precedingNode
->m_next
;
249 precedingNode
->m_next
= child
;
251 else // precedingNode == NULL
253 wxCHECK_MSG( m_children
== NULL
, false,
254 "NULL precedingNode only makes sense when there are no children" );
256 child
->m_next
= m_children
;
260 child
->m_parent
= this;
264 bool wxXmlNode::RemoveChild(wxXmlNode
*child
)
266 if (m_children
== NULL
)
268 else if (m_children
== child
)
270 m_children
= child
->m_next
;
271 child
->m_parent
= NULL
;
272 child
->m_next
= NULL
;
277 wxXmlNode
*ch
= m_children
;
280 if (ch
->m_next
== child
)
282 ch
->m_next
= child
->m_next
;
283 child
->m_parent
= NULL
;
284 child
->m_next
= NULL
;
293 void wxXmlNode::AddAttribute(const wxString
& name
, const wxString
& value
)
295 AddProperty(name
, value
);
298 void wxXmlNode::AddAttribute(wxXmlAttribute
*attr
)
303 bool wxXmlNode::DeleteAttribute(const wxString
& name
)
305 return DeleteProperty(name
);
308 void wxXmlNode::AddProperty(const wxString
& name
, const wxString
& value
)
310 AddProperty(new wxXmlAttribute(name
, value
, NULL
));
313 void wxXmlNode::AddProperty(wxXmlAttribute
*attr
)
319 wxXmlAttribute
*p
= m_attrs
;
320 while (p
->GetNext()) p
= p
->GetNext();
325 bool wxXmlNode::DeleteProperty(const wxString
& name
)
327 wxXmlAttribute
*attr
;
332 else if (m_attrs
->GetName() == name
)
335 m_attrs
= attr
->GetNext();
343 wxXmlAttribute
*p
= m_attrs
;
346 if (p
->GetNext()->GetName() == name
)
349 p
->SetNext(attr
->GetNext());
360 wxString
wxXmlNode::GetNodeContent() const
362 wxXmlNode
*n
= GetChildren();
366 if (n
->GetType() == wxXML_TEXT_NODE
||
367 n
->GetType() == wxXML_CDATA_SECTION_NODE
)
368 return n
->GetContent();
371 return wxEmptyString
;
374 int wxXmlNode::GetDepth(wxXmlNode
*grandparent
) const
376 const wxXmlNode
*n
= this;
383 if (n
== grandparent
)
391 bool wxXmlNode::IsWhitespaceOnly() const
393 return wxIsWhiteOnly(m_content
);
398 //-----------------------------------------------------------------------------
400 //-----------------------------------------------------------------------------
402 wxXmlDocument::wxXmlDocument()
403 : m_version(wxS("1.0")), m_fileEncoding(wxS("UTF-8")), m_docNode(NULL
)
406 m_encoding
= wxS("UTF-8");
410 wxXmlDocument::wxXmlDocument(const wxString
& filename
, const wxString
& encoding
)
411 :wxObject(), m_docNode(NULL
)
413 if ( !Load(filename
, encoding
) )
419 wxXmlDocument::wxXmlDocument(wxInputStream
& stream
, const wxString
& encoding
)
420 :wxObject(), m_docNode(NULL
)
422 if ( !Load(stream
, encoding
) )
428 wxXmlDocument::wxXmlDocument(const wxXmlDocument
& doc
)
434 wxXmlDocument
& wxXmlDocument::operator=(const wxXmlDocument
& doc
)
441 void wxXmlDocument::DoCopy(const wxXmlDocument
& doc
)
443 m_version
= doc
.m_version
;
445 m_encoding
= doc
.m_encoding
;
447 m_fileEncoding
= doc
.m_fileEncoding
;
450 m_docNode
= new wxXmlNode(*doc
.m_docNode
);
455 bool wxXmlDocument::Load(const wxString
& filename
, const wxString
& encoding
, int flags
)
457 wxFileInputStream
stream(filename
);
460 return Load(stream
, encoding
, flags
);
463 bool wxXmlDocument::Save(const wxString
& filename
, int indentstep
) const
465 wxFileOutputStream
stream(filename
);
468 return Save(stream
, indentstep
);
471 wxXmlNode
*wxXmlDocument::GetRoot() const
473 wxXmlNode
*node
= m_docNode
;
476 node
= m_docNode
->GetChildren();
477 while (node
!= NULL
&& node
->GetType() != wxXML_ELEMENT_NODE
)
478 node
= node
->GetNext();
483 wxXmlNode
*wxXmlDocument::DetachRoot()
485 wxXmlNode
*node
= m_docNode
;
488 node
= m_docNode
->GetChildren();
489 wxXmlNode
*prev
= NULL
;
490 while (node
!= NULL
&& node
->GetType() != wxXML_ELEMENT_NODE
)
493 node
= node
->GetNext();
497 if (node
== m_docNode
->GetChildren())
498 m_docNode
->SetChildren(node
->GetNext());
501 prev
->SetNext(node
->GetNext());
503 node
->SetParent(NULL
);
510 void wxXmlDocument::SetRoot(wxXmlNode
*root
)
514 wxASSERT_MSG( root
->GetType() == wxXML_ELEMENT_NODE
,
515 "Can only set an element type node as root" );
518 wxXmlNode
*node
= m_docNode
;
521 node
= m_docNode
->GetChildren();
522 wxXmlNode
*prev
= NULL
;
523 while (node
!= NULL
&& node
->GetType() != wxXML_ELEMENT_NODE
)
526 node
= node
->GetNext();
530 root
->SetNext( node
->GetNext() );
536 m_docNode
->SetChildren(root
);
540 m_docNode
= new wxXmlNode(wxXML_DOCUMENT_NODE
, wxEmptyString
);
541 m_docNode
->SetChildren(root
);
544 root
->SetParent(m_docNode
);
547 void wxXmlDocument::AppendToProlog(wxXmlNode
*node
)
550 m_docNode
= new wxXmlNode(wxXML_DOCUMENT_NODE
, wxEmptyString
);
552 m_docNode
->InsertChild( node
, GetRoot() );
554 m_docNode
->AddChild( node
);
557 //-----------------------------------------------------------------------------
558 // wxXmlDocument loading routines
559 //-----------------------------------------------------------------------------
561 // converts Expat-produced string in UTF-8 into wxString using the specified
562 // conv or keep in UTF-8 if conv is NULL
563 static wxString
CharToString(wxMBConv
*conv
,
564 const char *s
, size_t len
= wxString::npos
)
569 // there can be no embedded NULs in this string so we don't need the
570 // output length, it will be NUL-terminated
571 const wxWCharBuffer
wbuf(
572 wxConvUTF8
.cMB2WC(s
, len
== wxString::npos
? wxNO_LEN
: len
, NULL
));
574 return wxString(wbuf
, *conv
);
576 // else: the string is wanted in UTF-8
577 #endif // !wxUSE_UNICODE
580 return wxString::FromUTF8Unchecked(s
, len
);
583 // returns true if the given string contains only whitespaces
584 bool wxIsWhiteOnly(const wxString
& buf
)
586 for ( wxString::const_iterator i
= buf
.begin(); i
!= buf
.end(); ++i
)
589 if ( c
!= wxS(' ') && c
!= wxS('\t') && c
!= wxS('\n') && c
!= wxS('\r'))
596 struct wxXmlParsingContext
598 wxXmlParsingContext()
603 removeWhiteOnlyNodes(false)
608 wxXmlNode
*node
; // the node being parsed
609 wxXmlNode
*lastChild
; // the last child of "node"
610 wxXmlNode
*lastAsText
; // the last _text_ child of "node"
613 bool removeWhiteOnlyNodes
;
616 // checks that ctx->lastChild is in consistent state
617 #define ASSERT_LAST_CHILD_OK(ctx) \
618 wxASSERT( ctx->lastChild == NULL || \
619 ctx->lastChild->GetNext() == NULL ); \
620 wxASSERT( ctx->lastChild == NULL || \
621 ctx->lastChild->GetParent() == ctx->node )
624 static void StartElementHnd(void *userData
, const char *name
, const char **atts
)
626 wxXmlParsingContext
*ctx
= (wxXmlParsingContext
*)userData
;
627 wxXmlNode
*node
= new wxXmlNode(wxXML_ELEMENT_NODE
,
628 CharToString(ctx
->conv
, name
),
630 XML_GetCurrentLineNumber(ctx
->parser
));
631 const char **a
= atts
;
633 // add node attributes
636 node
->AddAttribute(CharToString(ctx
->conv
, a
[0]), CharToString(ctx
->conv
, a
[1]));
640 ASSERT_LAST_CHILD_OK(ctx
);
641 ctx
->node
->InsertChildAfter(node
, ctx
->lastChild
);
642 ctx
->lastAsText
= NULL
;
643 ctx
->lastChild
= NULL
; // our new node "node" has no children yet
648 static void EndElementHnd(void *userData
, const char* WXUNUSED(name
))
650 wxXmlParsingContext
*ctx
= (wxXmlParsingContext
*)userData
;
652 // we're exiting the last children of ctx->node->GetParent() and going
653 // back one level up, so current value of ctx->node points to the last
654 // child of ctx->node->GetParent()
655 ctx
->lastChild
= ctx
->node
;
657 ctx
->node
= ctx
->node
->GetParent();
658 ctx
->lastAsText
= NULL
;
661 static void TextHnd(void *userData
, const char *s
, int len
)
663 wxXmlParsingContext
*ctx
= (wxXmlParsingContext
*)userData
;
664 wxString str
= CharToString(ctx
->conv
, s
, len
);
668 ctx
->lastAsText
->SetContent(ctx
->lastAsText
->GetContent() + str
);
672 bool whiteOnly
= false;
673 if (ctx
->removeWhiteOnlyNodes
)
674 whiteOnly
= wxIsWhiteOnly(str
);
678 wxXmlNode
*textnode
=
679 new wxXmlNode(wxXML_TEXT_NODE
, wxS("text"), str
,
680 XML_GetCurrentLineNumber(ctx
->parser
));
682 ASSERT_LAST_CHILD_OK(ctx
);
683 ctx
->node
->InsertChildAfter(textnode
, ctx
->lastChild
);
684 ctx
->lastChild
= ctx
->lastAsText
= textnode
;
689 static void StartCdataHnd(void *userData
)
691 wxXmlParsingContext
*ctx
= (wxXmlParsingContext
*)userData
;
693 wxXmlNode
*textnode
=
694 new wxXmlNode(wxXML_CDATA_SECTION_NODE
, wxS("cdata"), wxS(""),
695 XML_GetCurrentLineNumber(ctx
->parser
));
697 ASSERT_LAST_CHILD_OK(ctx
);
698 ctx
->node
->InsertChildAfter(textnode
, ctx
->lastChild
);
699 ctx
->lastChild
= ctx
->lastAsText
= textnode
;
702 static void EndCdataHnd(void *userData
)
704 wxXmlParsingContext
*ctx
= (wxXmlParsingContext
*)userData
;
706 // we need to reset this pointer so that subsequent text nodes don't append
707 // their contents to this one but create new wxXML_TEXT_NODE objects (or
708 // not create anything at all if only white space follows the CDATA section
709 // and wxXMLDOC_KEEP_WHITESPACE_NODES is not used as is commonly the case)
710 ctx
->lastAsText
= NULL
;
713 static void CommentHnd(void *userData
, const char *data
)
715 wxXmlParsingContext
*ctx
= (wxXmlParsingContext
*)userData
;
717 wxXmlNode
*commentnode
=
718 new wxXmlNode(wxXML_COMMENT_NODE
,
719 wxS("comment"), CharToString(ctx
->conv
, data
),
720 XML_GetCurrentLineNumber(ctx
->parser
));
722 ASSERT_LAST_CHILD_OK(ctx
);
723 ctx
->node
->InsertChildAfter(commentnode
, ctx
->lastChild
);
724 ctx
->lastChild
= commentnode
;
725 ctx
->lastAsText
= NULL
;
728 static void PIHnd(void *userData
, const char *target
, const char *data
)
730 wxXmlParsingContext
*ctx
= (wxXmlParsingContext
*)userData
;
733 new wxXmlNode(wxXML_PI_NODE
, CharToString(ctx
->conv
, target
),
734 CharToString(ctx
->conv
, data
),
735 XML_GetCurrentLineNumber(ctx
->parser
));
737 ASSERT_LAST_CHILD_OK(ctx
);
738 ctx
->node
->InsertChildAfter(pinode
, ctx
->lastChild
);
739 ctx
->lastChild
= pinode
;
740 ctx
->lastAsText
= NULL
;
743 static void DefaultHnd(void *userData
, const char *s
, int len
)
746 if (len
> 6 && memcmp(s
, "<?xml ", 6) == 0)
748 wxXmlParsingContext
*ctx
= (wxXmlParsingContext
*)userData
;
750 wxString buf
= CharToString(ctx
->conv
, s
, (size_t)len
);
752 pos
= buf
.Find(wxS("encoding="));
753 if (pos
!= wxNOT_FOUND
)
754 ctx
->encoding
= buf
.Mid(pos
+ 10).BeforeFirst(buf
[(size_t)pos
+9]);
755 pos
= buf
.Find(wxS("version="));
756 if (pos
!= wxNOT_FOUND
)
757 ctx
->version
= buf
.Mid(pos
+ 9).BeforeFirst(buf
[(size_t)pos
+8]);
761 static int UnknownEncodingHnd(void * WXUNUSED(encodingHandlerData
),
762 const XML_Char
*name
, XML_Encoding
*info
)
764 // We must build conversion table for expat. The easiest way to do so
765 // is to let wxCSConv convert as string containing all characters to
766 // wide character representation:
774 for (i
= 0; i
< 255; i
++)
776 mbBuf
[0] = (char)(i
+1);
777 if (conv
.MB2WC(wcBuf
, mbBuf
, 2) == (size_t)-1)
779 // invalid/undefined byte in the encoding:
782 info
->map
[i
+1] = (int)wcBuf
[0];
786 info
->convert
= NULL
;
787 info
->release
= NULL
;
794 bool wxXmlDocument::Load(wxInputStream
& stream
, const wxString
& encoding
, int flags
)
799 m_encoding
= encoding
;
802 const size_t BUFSIZE
= 1024;
804 wxXmlParsingContext ctx
;
806 XML_Parser parser
= XML_ParserCreate(NULL
);
807 wxXmlNode
*root
= new wxXmlNode(wxXML_DOCUMENT_NODE
, wxEmptyString
);
809 ctx
.encoding
= wxS("UTF-8"); // default in absence of encoding=""
812 if ( encoding
.CmpNoCase(wxS("UTF-8")) != 0 )
813 ctx
.conv
= new wxCSConv(encoding
);
815 ctx
.removeWhiteOnlyNodes
= (flags
& wxXMLDOC_KEEP_WHITESPACE_NODES
) == 0;
819 XML_SetUserData(parser
, (void*)&ctx
);
820 XML_SetElementHandler(parser
, StartElementHnd
, EndElementHnd
);
821 XML_SetCharacterDataHandler(parser
, TextHnd
);
822 XML_SetCdataSectionHandler(parser
, StartCdataHnd
, EndCdataHnd
);;
823 XML_SetCommentHandler(parser
, CommentHnd
);
824 XML_SetProcessingInstructionHandler(parser
, PIHnd
);
825 XML_SetDefaultHandler(parser
, DefaultHnd
);
826 XML_SetUnknownEncodingHandler(parser
, UnknownEncodingHnd
, NULL
);
831 size_t len
= stream
.Read(buf
, BUFSIZE
).LastRead();
832 done
= (len
< BUFSIZE
);
833 if (!XML_Parse(parser
, buf
, len
, done
))
835 wxString
error(XML_ErrorString(XML_GetErrorCode(parser
)),
837 wxLogError(_("XML parsing error: '%s' at line %d"),
839 (int)XML_GetCurrentLineNumber(parser
));
847 if (!ctx
.version
.empty())
848 SetVersion(ctx
.version
);
849 if (!ctx
.encoding
.empty())
850 SetFileEncoding(ctx
.encoding
);
851 SetDocumentNode(root
);
858 XML_ParserFree(parser
);
870 //-----------------------------------------------------------------------------
871 // wxXmlDocument saving routines
872 //-----------------------------------------------------------------------------
874 // helpers for XML generation
878 // write string to output:
879 bool OutputString(wxOutputStream
& stream
,
888 wxUnusedVar(convMem
);
890 convFile
= &wxConvUTF8
;
892 const wxScopedCharBuffer
buf(str
.mb_str(*convFile
));
895 // conversion failed, can't write this string in an XML file in this
896 // (presumably non-UTF-8) encoding
900 stream
.Write(buf
, buf
.length());
901 #else // !wxUSE_UNICODE
902 if ( convFile
&& convMem
)
904 wxString
str2(str
.wc_str(*convMem
), *convFile
);
905 stream
.Write(str2
.mb_str(), str2
.length());
907 else // no conversions to do
909 stream
.Write(str
.mb_str(), str
.length());
911 #endif // wxUSE_UNICODE/!wxUSE_UNICODE
913 return stream
.IsOk();
922 // Same as above, but create entities first.
923 // Translates '<' to "<", '>' to ">" and so on, according to the spec:
924 // http://www.w3.org/TR/2000/WD-xml-c14n-20000119.html#charescaping
925 bool OutputEscapedString(wxOutputStream
& stream
,
932 escaped
.reserve(str
.length());
934 for ( wxString::const_iterator i
= str
.begin(); i
!= str
.end(); ++i
)
941 escaped
.append(wxS("<"));
944 escaped
.append(wxS(">"));
947 escaped
.append(wxS("&"));
950 escaped
.append(wxS("
"));
953 if ( mode
== Escape_Attribute
)
958 escaped
.append(wxS("""));
961 escaped
.append(wxS("	"));
964 escaped
.append(wxS("
"));
978 return OutputString(stream
, escaped
, convMem
, convFile
);
981 bool OutputIndentation(wxOutputStream
& stream
,
986 wxString
str(wxS("\n"));
987 str
+= wxString(indent
, wxS(' '));
988 return OutputString(stream
, str
, convMem
, convFile
);
991 bool OutputNode(wxOutputStream
& stream
,
999 switch (node
->GetType())
1001 case wxXML_CDATA_SECTION_NODE
:
1002 rc
= OutputString(stream
, wxS("<![CDATA["), convMem
, convFile
) &&
1003 OutputString(stream
, node
->GetContent(), convMem
, convFile
) &&
1004 OutputString(stream
, wxS("]]>"), convMem
, convFile
);
1007 case wxXML_TEXT_NODE
:
1008 if (node
->GetNoConversion())
1010 stream
.Write(node
->GetContent().c_str(), node
->GetContent().Length());
1014 rc
= OutputEscapedString(stream
, node
->GetContent(),
1019 case wxXML_ELEMENT_NODE
:
1020 rc
= OutputString(stream
, wxS("<"), convMem
, convFile
) &&
1021 OutputString(stream
, node
->GetName(), convMem
, convFile
);
1025 for ( wxXmlAttribute
*attr
= node
->GetAttributes();
1027 attr
= attr
->GetNext() )
1029 rc
= OutputString(stream
,
1030 wxS(" ") + attr
->GetName() + wxS("=\""),
1031 convMem
, convFile
) &&
1032 OutputEscapedString(stream
, attr
->GetValue(),
1034 Escape_Attribute
) &&
1035 OutputString(stream
, wxS("\""), convMem
, convFile
);
1039 if ( node
->GetChildren() )
1041 rc
= OutputString(stream
, wxS(">"), convMem
, convFile
);
1043 wxXmlNode
*prev
= NULL
;
1044 for ( wxXmlNode
*n
= node
->GetChildren();
1048 if ( indentstep
>= 0 && n
->GetType() != wxXML_TEXT_NODE
)
1050 rc
= OutputIndentation(stream
, indent
+ indentstep
,
1055 rc
= OutputNode(stream
, n
, indent
+ indentstep
,
1056 convMem
, convFile
, indentstep
);
1061 if ( rc
&& indentstep
>= 0 &&
1062 prev
&& prev
->GetType() != wxXML_TEXT_NODE
)
1064 rc
= OutputIndentation(stream
, indent
, convMem
, convFile
);
1069 rc
= OutputString(stream
, wxS("</"), convMem
, convFile
) &&
1070 OutputString(stream
, node
->GetName(),
1071 convMem
, convFile
) &&
1072 OutputString(stream
, wxS(">"), convMem
, convFile
);
1075 else // no children, output "<foo/>"
1077 rc
= OutputString(stream
, wxS("/>"), convMem
, convFile
);
1081 case wxXML_COMMENT_NODE
:
1082 rc
= OutputString(stream
, wxS("<!--"), convMem
, convFile
) &&
1083 OutputString(stream
, node
->GetContent(), convMem
, convFile
) &&
1084 OutputString(stream
, wxS("-->"), convMem
, convFile
);
1088 rc
= OutputString(stream
, wxT("<?"), convMem
, convFile
) &&
1089 OutputString(stream
, node
->GetName(), convMem
, convFile
) &&
1090 OutputString(stream
, wxT(" "), convMem
, convFile
) &&
1091 OutputString(stream
, node
->GetContent(), convMem
, convFile
) &&
1092 OutputString(stream
, wxT("?>"), convMem
, convFile
);
1096 wxFAIL_MSG("unsupported node type");
1103 } // anonymous namespace
1105 bool wxXmlDocument::Save(wxOutputStream
& stream
, int indentstep
) const
1110 wxScopedPtr
<wxMBConv
> convMem
, convFile
;
1113 convFile
.reset(new wxCSConv(GetFileEncoding()));
1115 if ( GetFileEncoding().CmpNoCase(GetEncoding()) != 0 )
1117 convFile
.reset(new wxCSConv(GetFileEncoding()));
1118 convMem
.reset(new wxCSConv(GetEncoding()));
1120 //else: file and in-memory encodings are the same, no conversion needed
1123 wxString dec
= wxString::Format(
1124 wxS("<?xml version=\"%s\" encoding=\"%s\"?>\n"),
1125 GetVersion(), GetFileEncoding()
1127 bool rc
= OutputString(stream
, dec
, convMem
.get(), convFile
.get());
1129 wxXmlNode
*node
= GetDocumentNode();
1131 node
= node
->GetChildren();
1135 rc
= OutputNode(stream
, node
, 0, convMem
.get(),
1136 convFile
.get(), indentstep
) &&
1137 OutputString(stream
, wxS("\n"), convMem
.get(), convFile
.get());
1138 node
= node
->GetNext();
1143 /*static*/ wxVersionInfo
wxXmlDocument::GetLibraryVersionInfo()
1145 return wxVersionInfo("expat",