]>
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 /////////////////////////////////////////////////////////////////////////////
12 #pragma implementation "xml.h"
15 // For compilers that support precompilation, includes "wx.h".
16 #include "wx/wxprec.h"
22 #include "wx/xml/xml.h"
26 #include "wx/wfstream.h"
27 #include "wx/datstrm.h"
28 #include "wx/zstream.h"
31 #include "wx/strconv.h"
33 #include "expat.h" // from Expat
35 //-----------------------------------------------------------------------------
37 //-----------------------------------------------------------------------------
39 wxXmlNode::wxXmlNode(wxXmlNode
*parent
,wxXmlNodeType type
,
40 const wxString
& name
, const wxString
& content
,
41 wxXmlProperty
*props
, wxXmlNode
*next
)
42 : m_type(type
), m_name(name
), m_content(content
),
43 m_properties(props
), m_parent(parent
),
44 m_children(NULL
), m_next(next
)
48 if (m_parent
->m_children
)
50 m_next
= m_parent
->m_children
;
51 m_parent
->m_children
= this;
54 m_parent
->m_children
= this;
58 wxXmlNode::wxXmlNode(wxXmlNodeType type
, const wxString
& name
,
59 const wxString
& content
)
60 : m_type(type
), m_name(name
), m_content(content
),
61 m_properties(NULL
), m_parent(NULL
),
62 m_children(NULL
), m_next(NULL
)
65 wxXmlNode::wxXmlNode(const wxXmlNode
& node
)
72 wxXmlNode::~wxXmlNode()
75 for (c
= m_children
; c
; c
= c2
)
81 wxXmlProperty
*p
, *p2
;
82 for (p
= m_properties
; p
; p
= p2
)
89 wxXmlNode
& wxXmlNode::operator=(const wxXmlNode
& node
)
91 wxDELETE(m_properties
);
97 void wxXmlNode::DoCopy(const wxXmlNode
& node
)
100 m_name
= node
.m_name
;
101 m_content
= node
.m_content
;
104 wxXmlNode
*n
= node
.m_children
;
107 AddChild(new wxXmlNode(*n
));
112 wxXmlProperty
*p
= node
.m_properties
;
115 AddProperty(p
->GetName(), p
->GetValue());
120 bool wxXmlNode::HasProp(const wxString
& propName
) const
122 wxXmlProperty
*prop
= GetProperties();
126 if (prop
->GetName() == propName
) return TRUE
;
127 prop
= prop
->GetNext();
133 bool wxXmlNode::GetPropVal(const wxString
& propName
, wxString
*value
) const
135 wxXmlProperty
*prop
= GetProperties();
139 if (prop
->GetName() == propName
)
141 *value
= prop
->GetValue();
144 prop
= prop
->GetNext();
150 wxString
wxXmlNode::GetPropVal(const wxString
& propName
, const wxString
& defaultVal
) const
153 if (GetPropVal(propName
, &tmp
))
159 void wxXmlNode::AddChild(wxXmlNode
*child
)
161 if (m_children
== NULL
)
165 wxXmlNode
*ch
= m_children
;
166 while (ch
->m_next
) ch
= ch
->m_next
;
169 child
->m_next
= NULL
;
170 child
->m_parent
= this;
173 void wxXmlNode::InsertChild(wxXmlNode
*child
, wxXmlNode
*before_node
)
175 wxASSERT_MSG(before_node
->GetParent() == this, wxT("wxXmlNode::InsertChild - the node has incorrect parent"));
177 if (m_children
== before_node
)
181 wxXmlNode
*ch
= m_children
;
182 while (ch
->m_next
!= before_node
) ch
= ch
->m_next
;
186 child
->m_parent
= this;
187 child
->m_next
= before_node
;
190 bool wxXmlNode::RemoveChild(wxXmlNode
*child
)
192 if (m_children
== NULL
)
194 else if (m_children
== child
)
196 m_children
= child
->m_next
;
197 child
->m_parent
= NULL
;
198 child
->m_next
= NULL
;
203 wxXmlNode
*ch
= m_children
;
206 if (ch
->m_next
== child
)
208 ch
->m_next
= child
->m_next
;
209 child
->m_parent
= NULL
;
210 child
->m_next
= NULL
;
219 void wxXmlNode::AddProperty(const wxString
& name
, const wxString
& value
)
221 AddProperty(new wxXmlProperty(name
, value
, NULL
));
224 void wxXmlNode::AddProperty(wxXmlProperty
*prop
)
226 if (m_properties
== NULL
)
230 wxXmlProperty
*p
= m_properties
;
231 while (p
->GetNext()) p
= p
->GetNext();
236 bool wxXmlNode::DeleteProperty(const wxString
& name
)
240 if (m_properties
== NULL
)
243 else if (m_properties
->GetName() == name
)
246 m_properties
= prop
->GetNext();
254 wxXmlProperty
*p
= m_properties
;
257 if (p
->GetNext()->GetName() == name
)
260 p
->SetNext(prop
->GetNext());
273 //-----------------------------------------------------------------------------
275 //-----------------------------------------------------------------------------
277 wxXmlDocument::wxXmlDocument()
278 : m_version(wxT("1.0")), m_fileEncoding(wxT("utf-8")), m_root(NULL
)
281 m_encoding
= wxT("UTF-8");
285 wxXmlDocument::wxXmlDocument(const wxString
& filename
, const wxString
& encoding
)
286 : wxObject(), m_root(NULL
)
288 if ( !Load(filename
, encoding
) )
294 wxXmlDocument::wxXmlDocument(wxInputStream
& stream
, const wxString
& encoding
)
295 : wxObject(), m_root(NULL
)
297 if ( !Load(stream
, encoding
) )
303 wxXmlDocument::wxXmlDocument(const wxXmlDocument
& doc
)
308 wxXmlDocument
& wxXmlDocument::operator=(const wxXmlDocument
& doc
)
315 void wxXmlDocument::DoCopy(const wxXmlDocument
& doc
)
317 m_version
= doc
.m_version
;
319 m_encoding
= doc
.m_encoding
;
321 m_fileEncoding
= doc
.m_fileEncoding
;
322 m_root
= new wxXmlNode(*doc
.m_root
);
325 bool wxXmlDocument::Load(const wxString
& filename
, const wxString
& encoding
)
327 wxFileInputStream
stream(filename
);
328 return Load(stream
, encoding
);
331 bool wxXmlDocument::Save(const wxString
& filename
) const
333 wxFileOutputStream
stream(filename
);
339 //-----------------------------------------------------------------------------
340 // wxXmlDocument loading routines
341 //-----------------------------------------------------------------------------
345 - process all elements, including CDATA
348 // converts Expat-produced string in UTF-8 into wxString.
349 inline static wxString
CharToString(wxMBConv
*conv
,
350 const char *s
, size_t len
= wxSTRING_MAXLEN
)
354 return wxString(s
, wxConvUTF8
, len
);
358 size_t nLen
= (len
!= wxSTRING_MAXLEN
) ? len
:
359 nLen
= wxConvUTF8
.MB2WC((wchar_t*) NULL
, s
, 0);
361 wchar_t *buf
= new wchar_t[nLen
+1];
362 wxConvUTF8
.MB2WC(buf
, s
, nLen
);
364 wxString
str(buf
, *conv
, len
);
369 return wxString(s
, len
);
373 struct wxXmlParsingContext
378 wxXmlNode
*lastAsText
;
383 static void StartElementHnd(void *userData
, const char *name
, const char **atts
)
385 wxXmlParsingContext
*ctx
= (wxXmlParsingContext
*)userData
;
386 wxXmlNode
*node
= new wxXmlNode(wxXML_ELEMENT_NODE
, CharToString(ctx
->conv
, name
));
387 const char **a
= atts
;
390 node
->AddProperty(CharToString(ctx
->conv
, a
[0]), CharToString(ctx
->conv
, a
[1]));
393 if (ctx
->root
== NULL
)
396 ctx
->node
->AddChild(node
);
398 ctx
->lastAsText
= NULL
;
401 static void EndElementHnd(void *userData
, const char* WXUNUSED(name
))
403 wxXmlParsingContext
*ctx
= (wxXmlParsingContext
*)userData
;
405 ctx
->node
= ctx
->node
->GetParent();
406 ctx
->lastAsText
= NULL
;
409 static void TextHnd(void *userData
, const char *s
, int len
)
411 wxXmlParsingContext
*ctx
= (wxXmlParsingContext
*)userData
;
412 char *buf
= new char[len
+ 1];
415 memcpy(buf
, s
, (size_t)len
);
419 ctx
->lastAsText
->SetContent(ctx
->lastAsText
->GetContent() +
420 CharToString(ctx
->conv
, buf
));
424 bool whiteOnly
= TRUE
;
425 for (char *c
= buf
; *c
!= '\0'; c
++)
426 if (*c
!= ' ' && *c
!= '\t' && *c
!= '\n' && *c
!= '\r')
433 ctx
->lastAsText
= new wxXmlNode(wxXML_TEXT_NODE
, wxT("text"),
434 CharToString(ctx
->conv
, buf
));
435 ctx
->node
->AddChild(ctx
->lastAsText
);
442 static void CommentHnd(void *userData
, const char *data
)
444 wxXmlParsingContext
*ctx
= (wxXmlParsingContext
*)userData
;
448 // VS: ctx->node == NULL happens if there is a comment before
449 // the root element (e.g. wxDesigner's output). We ignore such
450 // comments, no big deal...
451 ctx
->node
->AddChild(new wxXmlNode(wxXML_COMMENT_NODE
,
452 wxT("comment"), CharToString(ctx
->conv
, data
)));
454 ctx
->lastAsText
= NULL
;
457 static void DefaultHnd(void *userData
, const char *s
, int len
)
460 if (len
> 6 && memcmp(s
, "<?xml ", 6) == 0)
462 wxXmlParsingContext
*ctx
= (wxXmlParsingContext
*)userData
;
464 wxString buf
= CharToString(ctx
->conv
, s
, (size_t)len
);
466 pos
= buf
.Find(wxT("encoding="));
467 if (pos
!= wxNOT_FOUND
)
468 ctx
->encoding
= buf
.Mid(pos
+ 10).BeforeFirst(buf
[(size_t)pos
+9]);
469 pos
= buf
.Find(wxT("version="));
470 if (pos
!= wxNOT_FOUND
)
471 ctx
->version
= buf
.Mid(pos
+ 9).BeforeFirst(buf
[(size_t)pos
+8]);
475 static int UnknownEncodingHnd(void * WXUNUSED(encodingHandlerData
),
476 const XML_Char
*name
, XML_Encoding
*info
)
478 // We must build conversion table for expat. The easiest way to do so
479 // is to let wxCSConv convert as string containing all characters to
480 // wide character representation:
481 wxCSConv
conv(wxString(name
, wxConvLibc
));
488 for (i
= 0; i
< 255; i
++)
490 mbBuf
[0] = (char)(i
+1);
491 if (conv
.MB2WC(wcBuf
, mbBuf
, 2) == (size_t)-1)
493 // invalid/undefined byte in the encoding:
496 info
->map
[i
+1] = (int)wcBuf
[0];
500 info
->convert
= NULL
;
501 info
->release
= NULL
;
506 bool wxXmlDocument::Load(wxInputStream
& stream
, const wxString
& encoding
)
511 m_encoding
= encoding
;
514 const size_t BUFSIZE
= 1024;
516 wxXmlParsingContext ctx
;
518 XML_Parser parser
= XML_ParserCreate(NULL
);
520 ctx
.root
= ctx
.node
= NULL
;
521 ctx
.encoding
= wxT("UTF-8"); // default in absence of encoding=""
524 if ( encoding
!= wxT("UTF-8") && encoding
!= wxT("utf-8") )
525 ctx
.conv
= new wxCSConv(encoding
);
528 XML_SetUserData(parser
, (void*)&ctx
);
529 XML_SetElementHandler(parser
, StartElementHnd
, EndElementHnd
);
530 XML_SetCharacterDataHandler(parser
, TextHnd
);
531 XML_SetCommentHandler(parser
, CommentHnd
);
532 XML_SetDefaultHandler(parser
, DefaultHnd
);
533 XML_SetUnknownEncodingHandler(parser
, UnknownEncodingHnd
, NULL
);
538 size_t len
= stream
.Read(buf
, BUFSIZE
).LastRead();
539 done
= (len
< BUFSIZE
);
540 if (!XML_Parse(parser
, buf
, len
, done
))
542 wxLogError(_("XML parsing error: '%s' at line %d"),
543 XML_ErrorString(XML_GetErrorCode(parser
)),
544 XML_GetCurrentLineNumber(parser
));
552 SetVersion(ctx
.version
);
553 SetFileEncoding(ctx
.encoding
);
557 XML_ParserFree(parser
);
569 //-----------------------------------------------------------------------------
570 // wxXmlDocument saving routines
571 //-----------------------------------------------------------------------------
573 // write string to output:
574 inline static void OutputString(wxOutputStream
& stream
, const wxString
& str
,
575 wxMBConv
*convMem
, wxMBConv
*convFile
)
577 if (str
.IsEmpty()) return;
579 const wxWX2MBbuf
buf(str
.mb_str(convFile
? *convFile
: wxConvUTF8
));
580 stream
.Write((const char*)buf
, strlen((const char*)buf
));
582 if ( convFile
== NULL
)
583 stream
.Write(str
.mb_str(), str
.Len());
586 wxString
str2(str
.wc_str(*convMem
), *convFile
);
587 stream
.Write(str2
.mb_str(), str2
.Len());
592 // Same as above, but create entities first.
593 // Translates '<' to "<", '>' to ">" and '&' to "&"
594 static void OutputStringEnt(wxOutputStream
& stream
, const wxString
& str
,
595 wxMBConv
*convMem
, wxMBConv
*convFile
)
603 for (i
= 0; i
< len
; i
++)
606 if (c
== wxT('<') || c
== wxT('>') ||
607 (c
== wxT('&') && str
.Mid(i
+1, 4) != wxT("amp;")))
609 OutputString(stream
, str
.Mid(last
, i
- last
), convMem
, convFile
);
613 OutputString(stream
, wxT("<"), NULL
, NULL
);
616 OutputString(stream
, wxT(">"), NULL
, NULL
);
619 OutputString(stream
, wxT("&"), NULL
, NULL
);
626 OutputString(stream
, str
.Mid(last
, i
- last
), convMem
, convFile
);
629 inline static void OutputIndentation(wxOutputStream
& stream
, int indent
)
631 wxString str
= wxT("\n");
632 for (int i
= 0; i
< indent
; i
++)
633 str
<< wxT(' ') << wxT(' ');
634 OutputString(stream
, str
, NULL
, NULL
);
637 static void OutputNode(wxOutputStream
& stream
, wxXmlNode
*node
, int indent
,
638 wxMBConv
*convMem
, wxMBConv
*convFile
)
643 switch (node
->GetType())
645 case wxXML_TEXT_NODE
:
646 OutputStringEnt(stream
, node
->GetContent(), convMem
, convFile
);
649 case wxXML_ELEMENT_NODE
:
650 OutputString(stream
, wxT("<"), NULL
, NULL
);
651 OutputString(stream
, node
->GetName(), NULL
, NULL
);
653 prop
= node
->GetProperties();
656 OutputString(stream
, wxT(" ") + prop
->GetName() +
657 wxT("=\"") + prop
->GetValue() + wxT("\""),
659 // FIXME - what if prop contains '"'?
660 prop
= prop
->GetNext();
663 if (node
->GetChildren())
665 OutputString(stream
, wxT(">"), NULL
, NULL
);
667 n
= node
->GetChildren();
670 if (n
&& n
->GetType() != wxXML_TEXT_NODE
)
671 OutputIndentation(stream
, indent
+ 1);
672 OutputNode(stream
, n
, indent
+ 1, convMem
, convFile
);
676 if (prev
&& prev
->GetType() != wxXML_TEXT_NODE
)
677 OutputIndentation(stream
, indent
);
678 OutputString(stream
, wxT("</"), NULL
, NULL
);
679 OutputString(stream
, node
->GetName(), NULL
, NULL
);
680 OutputString(stream
, wxT(">"), NULL
, NULL
);
683 OutputString(stream
, wxT("/>"), NULL
, NULL
);
686 case wxXML_COMMENT_NODE
:
687 OutputString(stream
, wxT("<!--"), NULL
, NULL
);
688 OutputString(stream
, node
->GetContent(), convMem
, convFile
);
689 OutputString(stream
, wxT("-->"), NULL
, NULL
);
693 wxFAIL_MSG(wxT("unsupported node type"));
697 bool wxXmlDocument::Save(wxOutputStream
& stream
) const
704 wxMBConv
*convMem
= NULL
, *convFile
= NULL
;
706 convFile
= new wxCSConv(GetFileEncoding());
708 if ( GetFileEncoding() != GetEncoding() )
710 convFile
= new wxCSConv(GetFileEncoding());
711 convMem
= new wxCSConv(GetEncoding());
715 s
.Printf(wxT("<?xml version=\"%s\" encoding=\"%s\"?>\n"),
716 GetVersion().c_str(), GetFileEncoding().c_str());
717 OutputString(stream
, s
, NULL
, NULL
);
719 OutputNode(stream
, GetRoot(), 0, convMem
, convFile
);
720 OutputString(stream
, wxT("\n"), NULL
, NULL
);