X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/fa6a837300efb9d295b889a8308b74537c9d8677..9d5507f7a2701395e1d5c121bd877bb9066ee6ea:/src/xml/xml.cpp diff --git a/src/xml/xml.cpp b/src/xml/xml.cpp index 067cd02319..a68f828b40 100644 --- a/src/xml/xml.cpp +++ b/src/xml/xml.cpp @@ -39,6 +39,9 @@ WX_CHECK_BUILD_OPTIONS("wxXML") IMPLEMENT_CLASS(wxXmlDocument, wxObject) +// a private utility used by wxXML +static bool wxIsWhiteOnly(const wxString& buf); + //----------------------------------------------------------------------------- // wxXmlNode @@ -46,10 +49,11 @@ IMPLEMENT_CLASS(wxXmlDocument, wxObject) wxXmlNode::wxXmlNode(wxXmlNode *parent,wxXmlNodeType type, const wxString& name, const wxString& content, - wxXmlProperty *props, wxXmlNode *next) + wxXmlAttribute *attrs, wxXmlNode *next, int lineNo) : m_type(type), m_name(name), m_content(content), - m_properties(props), m_parent(parent), - m_children(NULL), m_next(next) + m_attrs(attrs), m_parent(parent), + m_children(NULL), m_next(next), + m_lineNo(lineNo) { if (m_parent) { @@ -64,10 +68,12 @@ wxXmlNode::wxXmlNode(wxXmlNode *parent,wxXmlNodeType type, } wxXmlNode::wxXmlNode(wxXmlNodeType type, const wxString& name, - const wxString& content) + const wxString& content, + int lineNo) : m_type(type), m_name(name), m_content(content), - m_properties(NULL), m_parent(NULL), - m_children(NULL), m_next(NULL) + m_attrs(NULL), m_parent(NULL), + m_children(NULL), m_next(NULL), + m_lineNo(lineNo) {} wxXmlNode::wxXmlNode(const wxXmlNode& node) @@ -86,8 +92,8 @@ wxXmlNode::~wxXmlNode() delete c; } - wxXmlProperty *p, *p2; - for (p = m_properties; p; p = p2) + wxXmlAttribute *p, *p2; + for (p = m_attrs; p; p = p2) { p2 = p->GetNext(); delete p; @@ -96,7 +102,7 @@ wxXmlNode::~wxXmlNode() wxXmlNode& wxXmlNode::operator=(const wxXmlNode& node) { - wxDELETE(m_properties); + wxDELETE(m_attrs); wxDELETE(m_children); DoCopy(node); return *this; @@ -107,6 +113,7 @@ void wxXmlNode::DoCopy(const wxXmlNode& node) m_type = node.m_type; m_name = node.m_name; m_content = node.m_content; + m_lineNo = node.m_lineNo; m_children = NULL; wxXmlNode *n = node.m_children; @@ -116,49 +123,51 @@ void wxXmlNode::DoCopy(const wxXmlNode& node) n = n->GetNext(); } - m_properties = NULL; - wxXmlProperty *p = node.m_properties; + m_attrs = NULL; + wxXmlAttribute *p = node.m_attrs; while (p) { - AddProperty(p->GetName(), p->GetValue()); + AddAttribute(p->GetName(), p->GetValue()); p = p->GetNext(); } } -bool wxXmlNode::HasProp(const wxString& propName) const +bool wxXmlNode::HasAttribute(const wxString& attrName) const { - wxXmlProperty *prop = GetProperties(); + wxXmlAttribute *attr = GetAttributes(); - while (prop) + while (attr) { - if (prop->GetName() == propName) return true; - prop = prop->GetNext(); + if (attr->GetName() == attrName) return true; + attr = attr->GetNext(); } return false; } -bool wxXmlNode::GetPropVal(const wxString& propName, wxString *value) const +bool wxXmlNode::GetAttribute(const wxString& attrName, wxString *value) const { - wxXmlProperty *prop = GetProperties(); + wxCHECK_MSG( value, false, "value argument must not be NULL" ); + + wxXmlAttribute *attr = GetAttributes(); - while (prop) + while (attr) { - if (prop->GetName() == propName) + if (attr->GetName() == attrName) { - *value = prop->GetValue(); + *value = attr->GetValue(); return true; } - prop = prop->GetNext(); + attr = attr->GetNext(); } return false; } -wxString wxXmlNode::GetPropVal(const wxString& propName, const wxString& defaultVal) const +wxString wxXmlNode::GetAttribute(const wxString& attrName, const wxString& defaultVal) const { wxString tmp; - if (GetPropVal(propName, &tmp)) + if (GetAttribute(attrName, &tmp)) return tmp; return defaultVal; @@ -243,50 +252,65 @@ bool wxXmlNode::RemoveChild(wxXmlNode *child) } } +void wxXmlNode::AddAttribute(const wxString& name, const wxString& value) +{ + AddProperty(name, value); +} + +void wxXmlNode::AddAttribute(wxXmlAttribute *attr) +{ + AddProperty(attr); +} + +bool wxXmlNode::DeleteAttribute(const wxString& name) +{ + return DeleteProperty(name); +} + void wxXmlNode::AddProperty(const wxString& name, const wxString& value) { - AddProperty(new wxXmlProperty(name, value, NULL)); + AddProperty(new wxXmlAttribute(name, value, NULL)); } -void wxXmlNode::AddProperty(wxXmlProperty *prop) +void wxXmlNode::AddProperty(wxXmlAttribute *attr) { - if (m_properties == NULL) - m_properties = prop; + if (m_attrs == NULL) + m_attrs = attr; else { - wxXmlProperty *p = m_properties; + wxXmlAttribute *p = m_attrs; while (p->GetNext()) p = p->GetNext(); - p->SetNext(prop); + p->SetNext(attr); } } bool wxXmlNode::DeleteProperty(const wxString& name) { - wxXmlProperty *prop; + wxXmlAttribute *attr; - if (m_properties == NULL) + if (m_attrs == NULL) return false; - else if (m_properties->GetName() == name) + else if (m_attrs->GetName() == name) { - prop = m_properties; - m_properties = prop->GetNext(); - prop->SetNext(NULL); - delete prop; + attr = m_attrs; + m_attrs = attr->GetNext(); + attr->SetNext(NULL); + delete attr; return true; } else { - wxXmlProperty *p = m_properties; + wxXmlAttribute *p = m_attrs; while (p->GetNext()) { if (p->GetNext()->GetName() == name) { - prop = p->GetNext(); - p->SetNext(prop->GetNext()); - prop->SetNext(NULL); - delete prop; + attr = p->GetNext(); + p->SetNext(attr->GetNext()); + attr->SetNext(NULL); + delete attr; return true; } p = p->GetNext(); @@ -309,6 +333,28 @@ wxString wxXmlNode::GetNodeContent() const return wxEmptyString; } +int wxXmlNode::GetDepth(wxXmlNode *grandparent) const +{ + const wxXmlNode *n = this; + int ret = -1; + + do + { + ret++; + n = n->GetParent(); + if (n == grandparent) + return ret; + + } while (n); + + return wxNOT_FOUND; +} + +bool wxXmlNode::IsWhitespaceOnly() const +{ + return wxIsWhiteOnly(m_content); +} + //----------------------------------------------------------------------------- @@ -361,23 +407,27 @@ void wxXmlDocument::DoCopy(const wxXmlDocument& doc) m_encoding = doc.m_encoding; #endif m_fileEncoding = doc.m_fileEncoding; - m_root = new wxXmlNode(*doc.m_root); + + if (doc.m_root) + m_root = new wxXmlNode(*doc.m_root); + else + m_root = NULL; } -bool wxXmlDocument::Load(const wxString& filename, const wxString& encoding) +bool wxXmlDocument::Load(const wxString& filename, const wxString& encoding, int flags) { wxFileInputStream stream(filename); if (!stream.Ok()) return false; - return Load(stream, encoding); + return Load(stream, encoding, flags); } -bool wxXmlDocument::Save(const wxString& filename) const +bool wxXmlDocument::Save(const wxString& filename, int indentstep) const { wxFileOutputStream stream(filename); if (!stream.Ok()) return false; - return Save(stream); + return Save(stream, indentstep); } @@ -386,57 +436,66 @@ bool wxXmlDocument::Save(const wxString& filename) const // wxXmlDocument loading routines //----------------------------------------------------------------------------- -/* - FIXME: - - process all elements, including CDATA - */ - // converts Expat-produced string in UTF-8 into wxString using the specified // conv or keep in UTF-8 if conv is NULL static wxString CharToString(wxMBConv *conv, - const char *s, size_t len = wxSTRING_MAXLEN) + const char *s, size_t len = wxString::npos) { -#if wxUSE_UNICODE - wxUnusedVar(conv); - - return wxString(s, wxConvUTF8, len); -#else // !wxUSE_UNICODE +#if !wxUSE_UNICODE if ( conv ) { // there can be no embedded NULs in this string so we don't need the // output length, it will be NUL-terminated const wxWCharBuffer wbuf( - wxConvUTF8.cMB2WC(s, len == wxSTRING_MAXLEN ? wxNO_LEN : len, NULL)); + wxConvUTF8.cMB2WC(s, len == wxString::npos ? wxNO_LEN : len, NULL)); return wxString(wbuf, *conv); } - else // already in UTF-8, no conversion needed + // else: the string is wanted in UTF-8 +#endif // !wxUSE_UNICODE + + wxUnusedVar(conv); + return wxString::FromUTF8(s, len); +} + +// returns true if the given string contains only whitespaces +bool wxIsWhiteOnly(const wxString& buf) +{ + for ( wxString::const_iterator i = buf.begin(); i != buf.end(); ++i ) { - return wxString(s, len != wxSTRING_MAXLEN ? len : strlen(s)); + wxChar c = *i; + if ( c != wxT(' ') && c != wxT('\t') && c != wxT('\n') && c != wxT('\r')) + return false; } -#endif // wxUSE_UNICODE/!wxUSE_UNICODE + return true; } + struct wxXmlParsingContext { + XML_Parser parser; wxMBConv *conv; wxXmlNode *root; wxXmlNode *node; wxXmlNode *lastAsText; wxString encoding; wxString version; - bool bLastCdata; + bool removeWhiteOnlyNodes; }; extern "C" { static void StartElementHnd(void *userData, const char *name, const char **atts) { wxXmlParsingContext *ctx = (wxXmlParsingContext*)userData; - wxXmlNode *node = new wxXmlNode(wxXML_ELEMENT_NODE, CharToString(ctx->conv, name)); + wxXmlNode *node = new wxXmlNode(wxXML_ELEMENT_NODE, + CharToString(ctx->conv, name), + wxEmptyString, + XML_GetCurrentLineNumber(ctx->parser)); const char **a = atts; + while (*a) { - node->AddProperty(CharToString(ctx->conv, a[0]), CharToString(ctx->conv, a[1])); + node->AddAttribute(CharToString(ctx->conv, a[0]), CharToString(ctx->conv, a[1])); a += 2; } if (ctx->root == NULL) @@ -446,9 +505,7 @@ static void StartElementHnd(void *userData, const char *name, const char **atts) ctx->node = node; ctx->lastAsText = NULL; } -} -extern "C" { static void EndElementHnd(void *userData, const char* WXUNUSED(name)) { wxXmlParsingContext *ctx = (wxXmlParsingContext*)userData; @@ -456,73 +513,42 @@ static void EndElementHnd(void *userData, const char* WXUNUSED(name)) ctx->node = ctx->node->GetParent(); ctx->lastAsText = NULL; } -} -extern "C" { static void TextHnd(void *userData, const char *s, int len) { wxXmlParsingContext *ctx = (wxXmlParsingContext*)userData; - char *buf = new char[len + 1]; - - buf[len] = '\0'; - memcpy(buf, s, (size_t)len); + wxString str = CharToString(ctx->conv, s, len); if (ctx->lastAsText) { - if ( ctx->bLastCdata ) - { - ctx->lastAsText->SetContent(ctx->lastAsText->GetContent() + - CharToString(NULL, buf)); - } - else - { - ctx->lastAsText->SetContent(ctx->lastAsText->GetContent() + - CharToString(ctx->conv, buf)); - } + ctx->lastAsText->SetContent(ctx->lastAsText->GetContent() + str); } else { - bool whiteOnly = true; - for (char *c = buf; *c != '\0'; c++) - if (*c != ' ' && *c != '\t' && *c != '\n' && *c != '\r') - { - whiteOnly = false; - break; - } + bool whiteOnly = false; + if (ctx->removeWhiteOnlyNodes) + whiteOnly = wxIsWhiteOnly(str); + if (!whiteOnly) { - ctx->lastAsText = new wxXmlNode(wxXML_TEXT_NODE, wxT("text"), - CharToString(ctx->conv, buf)); + ctx->lastAsText = + new wxXmlNode(wxXML_TEXT_NODE, wxT("text"), str, + XML_GetCurrentLineNumber(ctx->parser)); ctx->node->AddChild(ctx->lastAsText); } } - - delete[] buf; -} } -extern "C" { static void StartCdataHnd(void *userData) { wxXmlParsingContext *ctx = (wxXmlParsingContext*)userData; - ctx->bLastCdata = true; - - ctx->lastAsText = new wxXmlNode(wxXML_CDATA_SECTION_NODE, wxT("cdata"),wxT("")); - ctx->node->AddChild(ctx->lastAsText); -} -} - -extern "C" { -static void EndCdataHnd(void *userData) -{ - wxXmlParsingContext *ctx = (wxXmlParsingContext*)userData; - - ctx->bLastCdata = false; -} + ctx->lastAsText = + new wxXmlNode(wxXML_CDATA_SECTION_NODE, wxT("cdata"), wxT(""), + XML_GetCurrentLineNumber(ctx->parser)); + ctx->node->AddChild(ctx->lastAsText); } -extern "C" { static void CommentHnd(void *userData, const char *data) { wxXmlParsingContext *ctx = (wxXmlParsingContext*)userData; @@ -532,14 +558,14 @@ static void CommentHnd(void *userData, const char *data) // VS: ctx->node == NULL happens if there is a comment before // the root element (e.g. wxDesigner's output). We ignore such // comments, no big deal... - ctx->node->AddChild(new wxXmlNode(wxXML_COMMENT_NODE, - wxT("comment"), CharToString(ctx->conv, data))); + ctx->node->AddChild( + new wxXmlNode(wxXML_COMMENT_NODE, + wxT("comment"), CharToString(ctx->conv, data), + XML_GetCurrentLineNumber(ctx->parser))); } ctx->lastAsText = NULL; } -} -extern "C" { static void DefaultHnd(void *userData, const char *s, int len) { // XML header: @@ -557,17 +583,14 @@ static void DefaultHnd(void *userData, const char *s, int len) ctx->version = buf.Mid(pos + 9).BeforeFirst(buf[(size_t)pos+8]); } } -} -extern "C" { static int UnknownEncodingHnd(void * WXUNUSED(encodingHandlerData), const XML_Char *name, XML_Encoding *info) { // We must build conversion table for expat. The easiest way to do so // is to let wxCSConv convert as string containing all characters to // wide character representation: - wxString str(name, wxConvLibc); - wxCSConv conv(str); + wxCSConv conv(name); char mbBuf[2]; wchar_t wcBuf[10]; size_t i; @@ -591,9 +614,10 @@ static int UnknownEncodingHnd(void * WXUNUSED(encodingHandlerData), return 1; } -} -bool wxXmlDocument::Load(wxInputStream& stream, const wxString& encoding) +} // extern "C" + +bool wxXmlDocument::Load(wxInputStream& stream, const wxString& encoding, int flags) { #if wxUSE_UNICODE (void)encoding; @@ -611,15 +635,16 @@ bool wxXmlDocument::Load(wxInputStream& stream, const wxString& encoding) ctx.encoding = wxT("UTF-8"); // default in absence of encoding="" ctx.conv = NULL; #if !wxUSE_UNICODE - if ( encoding != wxT("UTF-8") && encoding != wxT("utf-8") ) + if ( encoding.CmpNoCase(wxT("UTF-8")) != 0 ) ctx.conv = new wxCSConv(encoding); #endif - ctx.bLastCdata = false; + ctx.removeWhiteOnlyNodes = (flags & wxXMLDOC_KEEP_WHITESPACE_NODES) == 0; + ctx.parser = parser; XML_SetUserData(parser, (void*)&ctx); XML_SetElementHandler(parser, StartElementHnd, EndElementHnd); XML_SetCharacterDataHandler(parser, TextHnd); - XML_SetCdataSectionHandler(parser, StartCdataHnd, EndCdataHnd ); + XML_SetStartCdataSectionHandler(parser, StartCdataHnd); XML_SetCommentHandler(parser, CommentHnd); XML_SetDefaultHandler(parser, DefaultHnd); XML_SetUnknownEncodingHandler(parser, UnknownEncodingHnd, NULL); @@ -755,18 +780,18 @@ inline static void OutputIndentation(wxOutputStream& stream, int indent) } static void OutputNode(wxOutputStream& stream, wxXmlNode *node, int indent, - wxMBConv *convMem, wxMBConv *convFile) + wxMBConv *convMem, wxMBConv *convFile, int indentstep) { wxXmlNode *n, *prev; - wxXmlProperty *prop; + wxXmlAttribute *attr; switch (node->GetType()) { - case wxXML_CDATA_SECTION_NODE: - OutputString( stream, wxT("GetContent() ); - OutputString( stream, wxT("]]>") ); - break; + case wxXML_CDATA_SECTION_NODE: + OutputString( stream, wxT("GetContent() ); + OutputString( stream, wxT("]]>") ); + break; case wxXML_TEXT_NODE: OutputStringEnt(stream, node->GetContent(), convMem, convFile); @@ -776,14 +801,14 @@ static void OutputNode(wxOutputStream& stream, wxXmlNode *node, int indent, OutputString(stream, wxT("<")); OutputString(stream, node->GetName()); - prop = node->GetProperties(); - while (prop) + attr = node->GetAttributes(); + while (attr) { - OutputString(stream, wxT(" ") + prop->GetName() + wxT("=\"")); - OutputStringEnt(stream, prop->GetValue(), convMem, convFile, + OutputString(stream, wxT(" ") + attr->GetName() + wxT("=\"")); + OutputStringEnt(stream, attr->GetValue(), convMem, convFile, XML_ESCAPE_QUOTES); OutputString(stream, wxT("\"")); - prop = prop->GetNext(); + attr = attr->GetNext(); } if (node->GetChildren()) @@ -793,13 +818,13 @@ static void OutputNode(wxOutputStream& stream, wxXmlNode *node, int indent, n = node->GetChildren(); while (n) { - if (n && n->GetType() != wxXML_TEXT_NODE) - OutputIndentation(stream, indent + 1); - OutputNode(stream, n, indent + 1, convMem, convFile); + if (indentstep >= 0 && n && n->GetType() != wxXML_TEXT_NODE) + OutputIndentation(stream, indent + indentstep); + OutputNode(stream, n, indent + indentstep, convMem, convFile, indentstep); prev = n; n = n->GetNext(); } - if (prev && prev->GetType() != wxXML_TEXT_NODE) + if (indentstep >= 0 && prev && prev->GetType() != wxXML_TEXT_NODE) OutputIndentation(stream, indent); OutputString(stream, wxT("GetName()); @@ -820,37 +845,41 @@ static void OutputNode(wxOutputStream& stream, wxXmlNode *node, int indent, } } -bool wxXmlDocument::Save(wxOutputStream& stream) const +bool wxXmlDocument::Save(wxOutputStream& stream, int indentstep) const { if ( !IsOk() ) return false; wxString s; - wxMBConv *convMem = NULL; + wxMBConv *convMem = NULL, + *convFile; #if wxUSE_UNICODE - wxMBConv *convFile = new wxCSConv(GetFileEncoding()); + convFile = new wxCSConv(GetFileEncoding()); + convMem = NULL; #else - wxMBConv *convFile = NULL; - if ( GetFileEncoding() != GetEncoding() ) + if ( GetFileEncoding().CmpNoCase(GetEncoding()) != 0 ) { convFile = new wxCSConv(GetFileEncoding()); convMem = new wxCSConv(GetEncoding()); } + else // file and in-memory encodings are the same, no conversion needed + { + convFile = + convMem = NULL; + } #endif s.Printf(wxT("\n"), GetVersion().c_str(), GetFileEncoding().c_str()); OutputString(stream, s); - OutputNode(stream, GetRoot(), 0, convMem, convFile); + OutputNode(stream, GetRoot(), 0, convMem, convFile, indentstep); OutputString(stream, wxT("\n")); - if ( convFile ) - delete convFile; - if ( convMem ) - delete convMem; + delete convFile; + delete convMem; return true; }