]>
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 #if defined(__GNUG__) && !defined(NO_GCC_PRAGMA) 
  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 // DLL options compatibility check: 
  37 WX_CHECK_BUILD_OPTIONS("wxXML") 
  39 //----------------------------------------------------------------------------- 
  41 //----------------------------------------------------------------------------- 
  43 wxXmlNode::wxXmlNode(wxXmlNode 
*parent
,wxXmlNodeType type
, 
  44                      const wxString
& name
, const wxString
& content
, 
  45                      wxXmlProperty 
*props
, wxXmlNode 
*next
) 
  46     : m_type(type
), m_name(name
), m_content(content
), 
  47       m_properties(props
), m_parent(parent
), 
  48       m_children(NULL
), m_next(next
) 
  52         if (m_parent
->m_children
) 
  54             m_next 
= m_parent
->m_children
; 
  55             m_parent
->m_children 
= this; 
  58             m_parent
->m_children 
= this; 
  62 wxXmlNode::wxXmlNode(wxXmlNodeType type
, const wxString
& name
, 
  63                      const wxString
& content
) 
  64     : m_type(type
), m_name(name
), m_content(content
), 
  65       m_properties(NULL
), m_parent(NULL
), 
  66       m_children(NULL
), m_next(NULL
) 
  69 wxXmlNode::wxXmlNode(const wxXmlNode
& node
) 
  76 wxXmlNode::~wxXmlNode() 
  79     for (c 
= m_children
; c
; c 
= c2
) 
  85     wxXmlProperty 
*p
, *p2
; 
  86     for (p 
= m_properties
; p
; p 
= p2
) 
  93 wxXmlNode
& wxXmlNode::operator=(const wxXmlNode
& node
) 
  95     wxDELETE(m_properties
); 
 101 void wxXmlNode::DoCopy(const wxXmlNode
& node
) 
 103     m_type 
= node
.m_type
; 
 104     m_name 
= node
.m_name
; 
 105     m_content 
= node
.m_content
; 
 108     wxXmlNode 
*n 
= node
.m_children
; 
 111         AddChild(new wxXmlNode(*n
)); 
 116     wxXmlProperty 
*p 
= node
.m_properties
; 
 119        AddProperty(p
->GetName(), p
->GetValue()); 
 124 bool wxXmlNode::HasProp(const wxString
& propName
) const 
 126     wxXmlProperty 
*prop 
= GetProperties(); 
 130         if (prop
->GetName() == propName
) return true; 
 131         prop 
= prop
->GetNext(); 
 137 bool wxXmlNode::GetPropVal(const wxString
& propName
, wxString 
*value
) const 
 139     wxXmlProperty 
*prop 
= GetProperties(); 
 143         if (prop
->GetName() == propName
) 
 145             *value 
= prop
->GetValue(); 
 148         prop 
= prop
->GetNext(); 
 154 wxString 
wxXmlNode::GetPropVal(const wxString
& propName
, const wxString
& defaultVal
) const 
 157     if (GetPropVal(propName
, &tmp
)) 
 163 void wxXmlNode::AddChild(wxXmlNode 
*child
) 
 165     if (m_children 
== NULL
) 
 169         wxXmlNode 
*ch 
= m_children
; 
 170         while (ch
->m_next
) ch 
= ch
->m_next
; 
 173     child
->m_next 
= NULL
; 
 174     child
->m_parent 
= this; 
 177 void wxXmlNode::InsertChild(wxXmlNode 
*child
, wxXmlNode 
*before_node
) 
 179     wxASSERT_MSG(before_node
->GetParent() == this, wxT("wxXmlNode::InsertChild - the node has incorrect parent")); 
 181     if (m_children 
== before_node
) 
 185         wxXmlNode 
*ch 
= m_children
; 
 186         while (ch
->m_next 
!= before_node
) ch 
= ch
->m_next
; 
 190     child
->m_parent 
= this; 
 191     child
->m_next 
= before_node
; 
 194 bool wxXmlNode::RemoveChild(wxXmlNode 
*child
) 
 196     if (m_children 
== NULL
) 
 198     else if (m_children 
== child
) 
 200         m_children 
= child
->m_next
; 
 201         child
->m_parent 
= NULL
; 
 202         child
->m_next 
= NULL
; 
 207         wxXmlNode 
*ch 
= m_children
; 
 210             if (ch
->m_next 
== child
) 
 212                 ch
->m_next 
= child
->m_next
; 
 213                 child
->m_parent 
= NULL
; 
 214                 child
->m_next 
= NULL
; 
 223 void wxXmlNode::AddProperty(const wxString
& name
, const wxString
& value
) 
 225     AddProperty(new wxXmlProperty(name
, value
, NULL
)); 
 228 void wxXmlNode::AddProperty(wxXmlProperty 
*prop
) 
 230     if (m_properties 
== NULL
) 
 234         wxXmlProperty 
*p 
= m_properties
; 
 235         while (p
->GetNext()) p 
= p
->GetNext(); 
 240 bool wxXmlNode::DeleteProperty(const wxString
& name
) 
 244     if (m_properties 
== NULL
) 
 247     else if (m_properties
->GetName() == name
) 
 250         m_properties 
= prop
->GetNext(); 
 258         wxXmlProperty 
*p 
= m_properties
; 
 261             if (p
->GetNext()->GetName() == name
) 
 264                 p
->SetNext(prop
->GetNext()); 
 277 //----------------------------------------------------------------------------- 
 279 //----------------------------------------------------------------------------- 
 281 wxXmlDocument::wxXmlDocument() 
 282     : m_version(wxT("1.0")), m_fileEncoding(wxT("utf-8")), m_root(NULL
) 
 285     m_encoding 
= wxT("UTF-8"); 
 289 wxXmlDocument::wxXmlDocument(const wxString
& filename
, const wxString
& encoding
) 
 290               :wxObject(), m_root(NULL
) 
 292     if ( !Load(filename
, encoding
) ) 
 298 wxXmlDocument::wxXmlDocument(wxInputStream
& stream
, const wxString
& encoding
) 
 299               :wxObject(), m_root(NULL
) 
 301     if ( !Load(stream
, encoding
) ) 
 307 wxXmlDocument::wxXmlDocument(const wxXmlDocument
& doc
) 
 313 wxXmlDocument
& wxXmlDocument::operator=(const wxXmlDocument
& doc
) 
 320 void wxXmlDocument::DoCopy(const wxXmlDocument
& doc
) 
 322     m_version 
= doc
.m_version
; 
 324     m_encoding 
= doc
.m_encoding
; 
 326     m_fileEncoding 
= doc
.m_fileEncoding
; 
 327     m_root 
= new wxXmlNode(*doc
.m_root
); 
 330 bool wxXmlDocument::Load(const wxString
& filename
, const wxString
& encoding
) 
 332     wxFileInputStream 
stream(filename
); 
 333     return Load(stream
, encoding
); 
 336 bool wxXmlDocument::Save(const wxString
& filename
) const 
 338     wxFileOutputStream 
stream(filename
); 
 344 //----------------------------------------------------------------------------- 
 345 //  wxXmlDocument loading routines 
 346 //----------------------------------------------------------------------------- 
 350        - process all elements, including CDATA 
 353 // converts Expat-produced string in UTF-8 into wxString. 
 354 inline static wxString 
CharToString(wxMBConv 
*conv
, 
 355                                     const char *s
, size_t len 
= wxSTRING_MAXLEN
) 
 359     return wxString(s
, wxConvUTF8
, len
); 
 363         size_t nLen 
= (len 
!= wxSTRING_MAXLEN
) ? len 
: 
 364                           wxConvUTF8
.MB2WC((wchar_t*) NULL
, s
, 0); 
 366         wchar_t *buf 
= new wchar_t[nLen
+1]; 
 367         wxConvUTF8
.MB2WC(buf
, s
, nLen
); 
 369         wxString 
str(buf
, *conv
, len
); 
 374         return wxString(s
, len 
!= wxSTRING_MAXLEN 
? len 
: strlen(s
)); 
 378 struct wxXmlParsingContext
 
 383     wxXmlNode 
*lastAsText
; 
 389 static void StartElementHnd(void *userData
, const char *name
, const char **atts
) 
 391     wxXmlParsingContext 
*ctx 
= (wxXmlParsingContext
*)userData
; 
 392     wxXmlNode 
*node 
= new wxXmlNode(wxXML_ELEMENT_NODE
, CharToString(ctx
->conv
, name
)); 
 393     const char **a 
= atts
; 
 396         node
->AddProperty(CharToString(ctx
->conv
, a
[0]), CharToString(ctx
->conv
, a
[1])); 
 399     if (ctx
->root 
== NULL
) 
 402         ctx
->node
->AddChild(node
); 
 404     ctx
->lastAsText 
= NULL
; 
 409 static void EndElementHnd(void *userData
, const char* WXUNUSED(name
)) 
 411     wxXmlParsingContext 
*ctx 
= (wxXmlParsingContext
*)userData
; 
 413     ctx
->node 
= ctx
->node
->GetParent(); 
 414     ctx
->lastAsText 
= NULL
; 
 419 static void TextHnd(void *userData
, const char *s
, int len
) 
 421     wxXmlParsingContext 
*ctx 
= (wxXmlParsingContext
*)userData
; 
 422     char *buf 
= new char[len 
+ 1]; 
 425     memcpy(buf
, s
, (size_t)len
); 
 429         ctx
->lastAsText
->SetContent(ctx
->lastAsText
->GetContent() + 
 430                                     CharToString(ctx
->conv
, buf
)); 
 434         bool whiteOnly 
= true; 
 435         for (char *c 
= buf
; *c 
!= '\0'; c
++) 
 436             if (*c 
!= ' ' && *c 
!= '\t' && *c 
!= '\n' && *c 
!= '\r') 
 443             ctx
->lastAsText 
= new wxXmlNode(wxXML_TEXT_NODE
, wxT("text"), 
 444                                             CharToString(ctx
->conv
, buf
)); 
 445             ctx
->node
->AddChild(ctx
->lastAsText
); 
 454 static void CommentHnd(void *userData
, const char *data
) 
 456     wxXmlParsingContext 
*ctx 
= (wxXmlParsingContext
*)userData
; 
 460         // VS: ctx->node == NULL happens if there is a comment before 
 461         //     the root element (e.g. wxDesigner's output). We ignore such 
 462         //     comments, no big deal... 
 463         ctx
->node
->AddChild(new wxXmlNode(wxXML_COMMENT_NODE
, 
 464                             wxT("comment"), CharToString(ctx
->conv
, data
))); 
 466     ctx
->lastAsText 
= NULL
; 
 471 static void DefaultHnd(void *userData
, const char *s
, int len
) 
 474     if (len 
> 6 && memcmp(s
, "<?xml ", 6) == 0) 
 476         wxXmlParsingContext 
*ctx 
= (wxXmlParsingContext
*)userData
; 
 478         wxString buf 
= CharToString(ctx
->conv
, s
, (size_t)len
); 
 480         pos 
= buf
.Find(wxT("encoding=")); 
 481         if (pos 
!= wxNOT_FOUND
) 
 482             ctx
->encoding 
= buf
.Mid(pos 
+ 10).BeforeFirst(buf
[(size_t)pos
+9]); 
 483         pos 
= buf
.Find(wxT("version=")); 
 484         if (pos 
!= wxNOT_FOUND
) 
 485             ctx
->version 
= buf
.Mid(pos 
+ 9).BeforeFirst(buf
[(size_t)pos
+8]); 
 491 static int UnknownEncodingHnd(void * WXUNUSED(encodingHandlerData
), 
 492                               const XML_Char 
*name
, XML_Encoding 
*info
) 
 494     // We must build conversion table for expat. The easiest way to do so 
 495     // is to let wxCSConv convert as string containing all characters to 
 496     // wide character representation: 
 497     wxString 
str(name
, wxConvLibc
); 
 505     for (i 
= 0; i 
< 255; i
++) 
 507         mbBuf
[0] = (char)(i
+1); 
 508         if (conv
.MB2WC(wcBuf
, mbBuf
, 2) == (size_t)-1) 
 510             // invalid/undefined byte in the encoding: 
 513         info
->map
[i
+1] = (int)wcBuf
[0]; 
 517     info
->convert 
= NULL
; 
 518     info
->release 
= NULL
; 
 524 bool wxXmlDocument::Load(wxInputStream
& stream
, const wxString
& encoding
) 
 529     m_encoding 
= encoding
; 
 532     const size_t BUFSIZE 
= 1024; 
 534     wxXmlParsingContext ctx
; 
 536     XML_Parser parser 
= XML_ParserCreate(NULL
); 
 538     ctx
.root 
= ctx
.node 
= NULL
; 
 539     ctx
.encoding 
= wxT("UTF-8"); // default in absence of encoding="" 
 542     if ( encoding 
!= wxT("UTF-8") && encoding 
!= wxT("utf-8") ) 
 543         ctx
.conv 
= new wxCSConv(encoding
); 
 546     XML_SetUserData(parser
, (void*)&ctx
); 
 547     XML_SetElementHandler(parser
, StartElementHnd
, EndElementHnd
); 
 548     XML_SetCharacterDataHandler(parser
, TextHnd
); 
 549     XML_SetCommentHandler(parser
, CommentHnd
); 
 550     XML_SetDefaultHandler(parser
, DefaultHnd
); 
 551     XML_SetUnknownEncodingHandler(parser
, UnknownEncodingHnd
, NULL
); 
 556         size_t len 
= stream
.Read(buf
, BUFSIZE
).LastRead(); 
 557         done 
= (len 
< BUFSIZE
); 
 558         if (!XML_Parse(parser
, buf
, len
, done
)) 
 560             wxString 
error(XML_ErrorString(XML_GetErrorCode(parser
)), 
 562             wxLogError(_("XML parsing error: '%s' at line %d"), 
 564                        XML_GetCurrentLineNumber(parser
)); 
 572         if (!ctx
.version
.empty()) 
 573             SetVersion(ctx
.version
); 
 574         if (!ctx
.encoding
.empty()) 
 575             SetFileEncoding(ctx
.encoding
); 
 583     XML_ParserFree(parser
); 
 595 //----------------------------------------------------------------------------- 
 596 //  wxXmlDocument saving routines 
 597 //----------------------------------------------------------------------------- 
 599 // write string to output: 
 600 inline static void OutputString(wxOutputStream
& stream
, const wxString
& str
, 
 602     wxMBConv 
* WXUNUSED(convMem
), 
 608     if (str
.empty()) return; 
 610     const wxWX2MBbuf 
buf(str
.mb_str(*(convFile 
? convFile 
: &wxConvUTF8
))); 
 611     stream
.Write((const char*)buf
, strlen((const char*)buf
)); 
 613     if ( convFile 
== NULL 
) 
 614         stream
.Write(str
.mb_str(), str
.Len()); 
 617         wxString 
str2(str
.wc_str(*convMem
), *convFile
); 
 618         stream
.Write(str2
.mb_str(), str2
.Len()); 
 623 // Same as above, but create entities first. 
 624 // Translates '<' to "<", '>' to ">" and '&' to "&" 
 625 static void OutputStringEnt(wxOutputStream
& stream
, const wxString
& str
, 
 626                             wxMBConv 
*convMem
, wxMBConv 
*convFile
, 
 627                             bool escapeQuotes 
= false) 
 635     for (i 
= 0; i 
< len
; i
++) 
 638         if (c 
== wxT('<') || c 
== wxT('>') || 
 639             (c 
== wxT('&') && str
.Mid(i
+1, 4) != wxT("amp;")) || 
 640             (escapeQuotes 
&& c 
== wxT('"'))) 
 642             OutputString(stream
, str
.Mid(last
, i 
- last
), convMem
, convFile
); 
 646                     OutputString(stream
, wxT("<"), NULL
, NULL
); 
 649                     OutputString(stream
, wxT(">"), NULL
, NULL
); 
 652                     OutputString(stream
, wxT("&"), NULL
, NULL
); 
 655                     OutputString(stream
, wxT("""), NULL
, NULL
); 
 662     OutputString(stream
, str
.Mid(last
, i 
- last
), convMem
, convFile
); 
 665 inline static void OutputIndentation(wxOutputStream
& stream
, int indent
) 
 667     wxString str 
= wxT("\n"); 
 668     for (int i 
= 0; i 
< indent
; i
++) 
 669         str 
<< wxT(' ') << wxT(' '); 
 670     OutputString(stream
, str
, NULL
, NULL
); 
 673 static void OutputNode(wxOutputStream
& stream
, wxXmlNode 
*node
, int indent
, 
 674                        wxMBConv 
*convMem
, wxMBConv 
*convFile
) 
 679     switch (node
->GetType()) 
 681         case wxXML_TEXT_NODE
: 
 682             OutputStringEnt(stream
, node
->GetContent(), convMem
, convFile
); 
 685         case wxXML_ELEMENT_NODE
: 
 686             OutputString(stream
, wxT("<"), NULL
, NULL
); 
 687             OutputString(stream
, node
->GetName(), NULL
, NULL
); 
 689             prop 
= node
->GetProperties(); 
 692                 OutputString(stream
, wxT(" ") + prop
->GetName() +  wxT("=\""), 
 694                 OutputStringEnt(stream
, prop
->GetValue(), NULL
, NULL
, 
 695                                 true/*escapeQuotes*/); 
 696                 OutputString(stream
, wxT("\""), NULL
, NULL
); 
 697                 prop 
= prop
->GetNext(); 
 700             if (node
->GetChildren()) 
 702                 OutputString(stream
, wxT(">"), NULL
, NULL
); 
 704                 n 
= node
->GetChildren(); 
 707                     if (n 
&& n
->GetType() != wxXML_TEXT_NODE
) 
 708                         OutputIndentation(stream
, indent 
+ 1); 
 709                     OutputNode(stream
, n
, indent 
+ 1, convMem
, convFile
); 
 713                 if (prev 
&& prev
->GetType() != wxXML_TEXT_NODE
) 
 714                     OutputIndentation(stream
, indent
); 
 715                 OutputString(stream
, wxT("</"), NULL
, NULL
); 
 716                 OutputString(stream
, node
->GetName(), NULL
, NULL
); 
 717                 OutputString(stream
, wxT(">"), NULL
, NULL
); 
 720                 OutputString(stream
, wxT("/>"), NULL
, NULL
); 
 723         case wxXML_COMMENT_NODE
: 
 724             OutputString(stream
, wxT("<!--"), NULL
, NULL
); 
 725             OutputString(stream
, node
->GetContent(), convMem
, convFile
); 
 726             OutputString(stream
, wxT("-->"), NULL
, NULL
); 
 730             wxFAIL_MSG(wxT("unsupported node type")); 
 734 bool wxXmlDocument::Save(wxOutputStream
& stream
) const 
 741     wxMBConv 
*convMem 
= NULL
; 
 744     wxMBConv 
*convFile 
= new wxCSConv(GetFileEncoding()); 
 746     wxMBConv 
*convFile 
= NULL
; 
 747     if ( GetFileEncoding() != GetEncoding() ) 
 749         convFile 
= new wxCSConv(GetFileEncoding()); 
 750         convMem 
= new wxCSConv(GetEncoding()); 
 754     s
.Printf(wxT("<?xml version=\"%s\" encoding=\"%s\"?>\n"), 
 755              GetVersion().c_str(), GetFileEncoding().c_str()); 
 756     OutputString(stream
, s
, NULL
, NULL
); 
 758     OutputNode(stream
, GetRoot(), 0, convMem
, convFile
); 
 759     OutputString(stream
, wxT("\n"), NULL
, NULL
);