]>
git.saurik.com Git - wxWidgets.git/blob - src/xml/xml.cpp
1 /////////////////////////////////////////////////////////////////////////////
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"
18 #include "wx/xml/xml.h"
22 #include "wx/wfstream.h"
23 #include "wx/datstrm.h"
24 #include "wx/zstream.h"
27 #include "wx/strconv.h"
29 #include "expat.h" // from Expat
31 // DLL options compatibility check:
33 WX_CHECK_BUILD_OPTIONS("wxXML")
36 IMPLEMENT_CLASS(wxXmlDocument
, wxObject
)
40 //-----------------------------------------------------------------------------
42 //-----------------------------------------------------------------------------
44 wxXmlNode::wxXmlNode(wxXmlNode
*parent
,wxXmlNodeType type
,
45 const wxString
& name
, const wxString
& content
,
46 wxXmlProperty
*props
, wxXmlNode
*next
)
47 : m_type(type
), m_name(name
), m_content(content
),
48 m_properties(props
), m_parent(parent
),
49 m_children(NULL
), m_next(next
)
53 if (m_parent
->m_children
)
55 m_next
= m_parent
->m_children
;
56 m_parent
->m_children
= this;
59 m_parent
->m_children
= this;
63 wxXmlNode::wxXmlNode(wxXmlNodeType type
, const wxString
& name
,
64 const wxString
& content
)
65 : m_type(type
), m_name(name
), m_content(content
),
66 m_properties(NULL
), m_parent(NULL
),
67 m_children(NULL
), m_next(NULL
)
70 wxXmlNode::wxXmlNode(const wxXmlNode
& node
)
77 wxXmlNode::~wxXmlNode()
80 for (c
= m_children
; c
; c
= c2
)
86 wxXmlProperty
*p
, *p2
;
87 for (p
= m_properties
; p
; p
= p2
)
94 wxXmlNode
& wxXmlNode::operator=(const wxXmlNode
& node
)
96 wxDELETE(m_properties
);
102 void wxXmlNode::DoCopy(const wxXmlNode
& node
)
104 m_type
= node
.m_type
;
105 m_name
= node
.m_name
;
106 m_content
= node
.m_content
;
109 wxXmlNode
*n
= node
.m_children
;
112 AddChild(new wxXmlNode(*n
));
117 wxXmlProperty
*p
= node
.m_properties
;
120 AddProperty(p
->GetName(), p
->GetValue());
125 bool wxXmlNode::HasProp(const wxString
& propName
) const
127 wxXmlProperty
*prop
= GetProperties();
131 if (prop
->GetName() == propName
) return true;
132 prop
= prop
->GetNext();
138 bool wxXmlNode::GetPropVal(const wxString
& propName
, wxString
*value
) const
140 wxXmlProperty
*prop
= GetProperties();
144 if (prop
->GetName() == propName
)
146 *value
= prop
->GetValue();
149 prop
= prop
->GetNext();
155 wxString
wxXmlNode::GetPropVal(const wxString
& propName
, const wxString
& defaultVal
) const
158 if (GetPropVal(propName
, &tmp
))
164 void wxXmlNode::AddChild(wxXmlNode
*child
)
166 if (m_children
== NULL
)
170 wxXmlNode
*ch
= m_children
;
171 while (ch
->m_next
) ch
= ch
->m_next
;
174 child
->m_next
= NULL
;
175 child
->m_parent
= this;
178 void wxXmlNode::InsertChild(wxXmlNode
*child
, wxXmlNode
*before_node
)
180 wxASSERT_MSG(before_node
->GetParent() == this, wxT("wxXmlNode::InsertChild - the node has incorrect parent"));
182 if (m_children
== before_node
)
186 wxXmlNode
*ch
= m_children
;
187 while (ch
->m_next
!= before_node
) ch
= ch
->m_next
;
191 child
->m_parent
= this;
192 child
->m_next
= before_node
;
195 bool wxXmlNode::RemoveChild(wxXmlNode
*child
)
197 if (m_children
== NULL
)
199 else if (m_children
== child
)
201 m_children
= child
->m_next
;
202 child
->m_parent
= NULL
;
203 child
->m_next
= NULL
;
208 wxXmlNode
*ch
= m_children
;
211 if (ch
->m_next
== child
)
213 ch
->m_next
= child
->m_next
;
214 child
->m_parent
= NULL
;
215 child
->m_next
= NULL
;
224 void wxXmlNode::AddProperty(const wxString
& name
, const wxString
& value
)
226 AddProperty(new wxXmlProperty(name
, value
, NULL
));
229 void wxXmlNode::AddProperty(wxXmlProperty
*prop
)
231 if (m_properties
== NULL
)
235 wxXmlProperty
*p
= m_properties
;
236 while (p
->GetNext()) p
= p
->GetNext();
241 bool wxXmlNode::DeleteProperty(const wxString
& name
)
245 if (m_properties
== NULL
)
248 else if (m_properties
->GetName() == name
)
251 m_properties
= prop
->GetNext();
259 wxXmlProperty
*p
= m_properties
;
262 if (p
->GetNext()->GetName() == name
)
265 p
->SetNext(prop
->GetNext());
276 wxString
wxXmlNode::GetNodeContent() const
278 wxXmlNode
*n
= GetChildren();
282 if (n
->GetType() == wxXML_TEXT_NODE
||
283 n
->GetType() == wxXML_CDATA_SECTION_NODE
)
284 return n
->GetContent();
287 return wxEmptyString
;
292 //-----------------------------------------------------------------------------
294 //-----------------------------------------------------------------------------
296 wxXmlDocument::wxXmlDocument()
297 : m_version(wxT("1.0")), m_fileEncoding(wxT("utf-8")), m_root(NULL
)
300 m_encoding
= wxT("UTF-8");
304 wxXmlDocument::wxXmlDocument(const wxString
& filename
, const wxString
& encoding
)
305 :wxObject(), m_root(NULL
)
307 if ( !Load(filename
, encoding
) )
313 wxXmlDocument::wxXmlDocument(wxInputStream
& stream
, const wxString
& encoding
)
314 :wxObject(), m_root(NULL
)
316 if ( !Load(stream
, encoding
) )
322 wxXmlDocument::wxXmlDocument(const wxXmlDocument
& doc
)
328 wxXmlDocument
& wxXmlDocument::operator=(const wxXmlDocument
& doc
)
335 void wxXmlDocument::DoCopy(const wxXmlDocument
& doc
)
337 m_version
= doc
.m_version
;
339 m_encoding
= doc
.m_encoding
;
341 m_fileEncoding
= doc
.m_fileEncoding
;
342 m_root
= new wxXmlNode(*doc
.m_root
);
345 bool wxXmlDocument::Load(const wxString
& filename
, const wxString
& encoding
)
347 wxFileInputStream
stream(filename
);
348 return Load(stream
, encoding
);
351 bool wxXmlDocument::Save(const wxString
& filename
) const
353 wxFileOutputStream
stream(filename
);
359 //-----------------------------------------------------------------------------
360 // wxXmlDocument loading routines
361 //-----------------------------------------------------------------------------
365 - process all elements, including CDATA
368 // converts Expat-produced string in UTF-8 into wxString.
369 inline static wxString
CharToString(wxMBConv
*conv
,
370 const char *s
, size_t len
= wxSTRING_MAXLEN
)
374 return wxString(s
, wxConvUTF8
, len
);
378 size_t nLen
= (len
!= wxSTRING_MAXLEN
) ? len
:
379 wxConvUTF8
.MB2WC((wchar_t*) NULL
, s
, 0);
381 wchar_t *buf
= new wchar_t[nLen
+1];
382 wxConvUTF8
.MB2WC(buf
, s
, nLen
);
384 wxString
str(buf
, *conv
, len
);
389 return wxString(s
, len
!= wxSTRING_MAXLEN
? len
: strlen(s
));
393 struct wxXmlParsingContext
398 wxXmlNode
*lastAsText
;
404 static void StartElementHnd(void *userData
, const char *name
, const char **atts
)
406 wxXmlParsingContext
*ctx
= (wxXmlParsingContext
*)userData
;
407 wxXmlNode
*node
= new wxXmlNode(wxXML_ELEMENT_NODE
, CharToString(ctx
->conv
, name
));
408 const char **a
= atts
;
411 node
->AddProperty(CharToString(ctx
->conv
, a
[0]), CharToString(ctx
->conv
, a
[1]));
414 if (ctx
->root
== NULL
)
417 ctx
->node
->AddChild(node
);
419 ctx
->lastAsText
= NULL
;
424 static void EndElementHnd(void *userData
, const char* WXUNUSED(name
))
426 wxXmlParsingContext
*ctx
= (wxXmlParsingContext
*)userData
;
428 ctx
->node
= ctx
->node
->GetParent();
429 ctx
->lastAsText
= NULL
;
434 static void TextHnd(void *userData
, const char *s
, int len
)
436 wxXmlParsingContext
*ctx
= (wxXmlParsingContext
*)userData
;
437 char *buf
= new char[len
+ 1];
440 memcpy(buf
, s
, (size_t)len
);
444 ctx
->lastAsText
->SetContent(ctx
->lastAsText
->GetContent() +
445 CharToString(ctx
->conv
, buf
));
449 bool whiteOnly
= true;
450 for (char *c
= buf
; *c
!= '\0'; c
++)
451 if (*c
!= ' ' && *c
!= '\t' && *c
!= '\n' && *c
!= '\r')
458 ctx
->lastAsText
= new wxXmlNode(wxXML_TEXT_NODE
, wxT("text"),
459 CharToString(ctx
->conv
, buf
));
460 ctx
->node
->AddChild(ctx
->lastAsText
);
469 static void CommentHnd(void *userData
, const char *data
)
471 wxXmlParsingContext
*ctx
= (wxXmlParsingContext
*)userData
;
475 // VS: ctx->node == NULL happens if there is a comment before
476 // the root element (e.g. wxDesigner's output). We ignore such
477 // comments, no big deal...
478 ctx
->node
->AddChild(new wxXmlNode(wxXML_COMMENT_NODE
,
479 wxT("comment"), CharToString(ctx
->conv
, data
)));
481 ctx
->lastAsText
= NULL
;
486 static void DefaultHnd(void *userData
, const char *s
, int len
)
489 if (len
> 6 && memcmp(s
, "<?xml ", 6) == 0)
491 wxXmlParsingContext
*ctx
= (wxXmlParsingContext
*)userData
;
493 wxString buf
= CharToString(ctx
->conv
, s
, (size_t)len
);
495 pos
= buf
.Find(wxT("encoding="));
496 if (pos
!= wxNOT_FOUND
)
497 ctx
->encoding
= buf
.Mid(pos
+ 10).BeforeFirst(buf
[(size_t)pos
+9]);
498 pos
= buf
.Find(wxT("version="));
499 if (pos
!= wxNOT_FOUND
)
500 ctx
->version
= buf
.Mid(pos
+ 9).BeforeFirst(buf
[(size_t)pos
+8]);
506 static int UnknownEncodingHnd(void * WXUNUSED(encodingHandlerData
),
507 const XML_Char
*name
, XML_Encoding
*info
)
509 // We must build conversion table for expat. The easiest way to do so
510 // is to let wxCSConv convert as string containing all characters to
511 // wide character representation:
512 wxString
str(name
, wxConvLibc
);
520 for (i
= 0; i
< 255; i
++)
522 mbBuf
[0] = (char)(i
+1);
523 if (conv
.MB2WC(wcBuf
, mbBuf
, 2) == (size_t)-1)
525 // invalid/undefined byte in the encoding:
528 info
->map
[i
+1] = (int)wcBuf
[0];
532 info
->convert
= NULL
;
533 info
->release
= NULL
;
539 bool wxXmlDocument::Load(wxInputStream
& stream
, const wxString
& encoding
)
544 m_encoding
= encoding
;
547 const size_t BUFSIZE
= 1024;
549 wxXmlParsingContext ctx
;
551 XML_Parser parser
= XML_ParserCreate(NULL
);
553 ctx
.root
= ctx
.node
= NULL
;
554 ctx
.encoding
= wxT("UTF-8"); // default in absence of encoding=""
557 if ( encoding
!= wxT("UTF-8") && encoding
!= wxT("utf-8") )
558 ctx
.conv
= new wxCSConv(encoding
);
561 XML_SetUserData(parser
, (void*)&ctx
);
562 XML_SetElementHandler(parser
, StartElementHnd
, EndElementHnd
);
563 XML_SetCharacterDataHandler(parser
, TextHnd
);
564 XML_SetCommentHandler(parser
, CommentHnd
);
565 XML_SetDefaultHandler(parser
, DefaultHnd
);
566 XML_SetUnknownEncodingHandler(parser
, UnknownEncodingHnd
, NULL
);
571 size_t len
= stream
.Read(buf
, BUFSIZE
).LastRead();
572 done
= (len
< BUFSIZE
);
573 if (!XML_Parse(parser
, buf
, len
, done
))
575 wxString
error(XML_ErrorString(XML_GetErrorCode(parser
)),
577 wxLogError(_("XML parsing error: '%s' at line %d"),
579 XML_GetCurrentLineNumber(parser
));
587 if (!ctx
.version
.empty())
588 SetVersion(ctx
.version
);
589 if (!ctx
.encoding
.empty())
590 SetFileEncoding(ctx
.encoding
);
598 XML_ParserFree(parser
);
610 //-----------------------------------------------------------------------------
611 // wxXmlDocument saving routines
612 //-----------------------------------------------------------------------------
614 // write string to output:
615 inline static void OutputString(wxOutputStream
& stream
, const wxString
& str
,
616 wxMBConv
*convMem
= NULL
,
617 wxMBConv
*convFile
= NULL
)
623 wxUnusedVar(convMem
);
625 const wxWX2MBbuf
buf(str
.mb_str(*(convFile
? convFile
: &wxConvUTF8
)));
626 stream
.Write((const char*)buf
, strlen((const char*)buf
));
627 #else // !wxUSE_UNICODE
628 if ( convFile
&& convMem
)
630 wxString
str2(str
.wc_str(*convMem
), *convFile
);
631 stream
.Write(str2
.mb_str(), str2
.Len());
633 else // no conversions to do
635 stream
.Write(str
.mb_str(), str
.Len());
637 #endif // wxUSE_UNICODE/!wxUSE_UNICODE
640 // flags for OutputStringEnt()
643 XML_ESCAPE_QUOTES
= 1
646 // Same as above, but create entities first.
647 // Translates '<' to "<", '>' to ">" and '&' to "&"
648 static void OutputStringEnt(wxOutputStream
& stream
, const wxString
& str
,
649 wxMBConv
*convMem
= NULL
,
650 wxMBConv
*convFile
= NULL
,
659 for (i
= 0; i
< len
; i
++)
662 if (c
== wxT('<') || c
== wxT('>') ||
663 (c
== wxT('&') && str
.Mid(i
+1, 4) != wxT("amp;")) ||
664 ((flags
& XML_ESCAPE_QUOTES
) && c
== wxT('"')))
666 OutputString(stream
, str
.Mid(last
, i
- last
), convMem
, convFile
);
670 OutputString(stream
, wxT("<"));
673 OutputString(stream
, wxT(">"));
676 OutputString(stream
, wxT("&"));
679 OutputString(stream
, wxT("""));
687 OutputString(stream
, str
.Mid(last
, i
- last
), convMem
, convFile
);
690 inline static void OutputIndentation(wxOutputStream
& stream
, int indent
)
692 wxString str
= wxT("\n");
693 for (int i
= 0; i
< indent
; i
++)
694 str
<< wxT(' ') << wxT(' ');
695 OutputString(stream
, str
);
698 static void OutputNode(wxOutputStream
& stream
, wxXmlNode
*node
, int indent
,
699 wxMBConv
*convMem
, wxMBConv
*convFile
)
704 switch (node
->GetType())
706 case wxXML_TEXT_NODE
:
707 OutputStringEnt(stream
, node
->GetContent(), convMem
, convFile
);
710 case wxXML_ELEMENT_NODE
:
711 OutputString(stream
, wxT("<"));
712 OutputString(stream
, node
->GetName());
714 prop
= node
->GetProperties();
717 OutputString(stream
, wxT(" ") + prop
->GetName() + wxT("=\""));
718 OutputStringEnt(stream
, prop
->GetValue(), convMem
, convFile
,
720 OutputString(stream
, wxT("\""));
721 prop
= prop
->GetNext();
724 if (node
->GetChildren())
726 OutputString(stream
, wxT(">"));
728 n
= node
->GetChildren();
731 if (n
&& n
->GetType() != wxXML_TEXT_NODE
)
732 OutputIndentation(stream
, indent
+ 1);
733 OutputNode(stream
, n
, indent
+ 1, convMem
, convFile
);
737 if (prev
&& prev
->GetType() != wxXML_TEXT_NODE
)
738 OutputIndentation(stream
, indent
);
739 OutputString(stream
, wxT("</"));
740 OutputString(stream
, node
->GetName());
741 OutputString(stream
, wxT(">"));
744 OutputString(stream
, wxT("/>"));
747 case wxXML_COMMENT_NODE
:
748 OutputString(stream
, wxT("<!--"));
749 OutputString(stream
, node
->GetContent(), convMem
, convFile
);
750 OutputString(stream
, wxT("-->"));
754 wxFAIL_MSG(wxT("unsupported node type"));
758 bool wxXmlDocument::Save(wxOutputStream
& stream
) const
765 wxMBConv
*convMem
= NULL
;
768 wxMBConv
*convFile
= new wxCSConv(GetFileEncoding());
770 wxMBConv
*convFile
= NULL
;
771 if ( GetFileEncoding() != GetEncoding() )
773 convFile
= new wxCSConv(GetFileEncoding());
774 convMem
= new wxCSConv(GetEncoding());
778 s
.Printf(wxT("<?xml version=\"%s\" encoding=\"%s\"?>\n"),
779 GetVersion().c_str(), GetFileEncoding().c_str());
780 OutputString(stream
, s
);
782 OutputNode(stream
, GetRoot(), 0, convMem
, convFile
);
783 OutputString(stream
, wxT("\n"));