Don't keep entries for XRC resources that failed to load in wxXmlResource.

Attempting to load a resource that couldn't be loaded resulted in
wxXmlResource::Load() returning false for this and _all_the_subsequent_ calls
to it because each call to Load() reattempted to reload all resources,
including the one(s) that failed to load initially.

Instead, try to load just the resource(s) that we should load right now and
ignore all the other ones. Also, don't add entries for the one(s) that we fail
to load.

This fixes the unit test failures in the XRC test case which was affected by
the test checking that XRC couldn't be loaded from garbage that ran before it.
It also makes the code simpler by ensuring that wxXmlResourceDataRecords
elements always have a valid wxXmlDocument associated with them.

Also clean up the code: use wxScopedPtr instead of manually deleting pointers
and reorganize #if checks to be easier to follow.

git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@66219 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
This commit is contained in:
Vadim Zeitlin
2010-11-20 23:53:34 +00:00
parent f822acceee
commit 9fc5a47ce6
2 changed files with 157 additions and 118 deletions

View File

@@ -48,16 +48,45 @@
#include "wx/dir.h"
#include "wx/xml/xml.h"
#include "wx/hashset.h"
#include "wx/scopedptr.h"
namespace
{
// Helper function to get modification time of either a wxFileSystem URI or
// just a normal file name, depending on the build.
#if wxUSE_DATETIME
wxDateTime GetXRCFileModTime(const wxString& filename)
{
#if wxUSE_FILESYSTEM
wxFileSystem fsys;
wxScopedPtr<wxFSFile> file(fsys.OpenFile(filename));
return file ? file->GetModificationTime() : wxDateTime();
#else // wxUSE_FILESYSTEM
return wxDateTime(wxFileModificationTime(filename));
#endif // wxUSE_FILESYSTEM
}
#endif // wxUSE_DATETIME
} // anonymous namespace
class wxXmlResourceDataRecord
{
public:
wxXmlResourceDataRecord() : Doc(NULL) {
// Ctor takes ownership of the document pointer.
wxXmlResourceDataRecord(const wxString& File_,
wxXmlDocument *Doc_
)
: File(File_), Doc(Doc_)
{
#if wxUSE_DATETIME
Time = wxDateTime::Now();
Time = GetXRCFileModTime(File);
#endif
}
~wxXmlResourceDataRecord() {delete Doc;}
wxString File;
@@ -65,6 +94,8 @@ public:
#if wxUSE_DATETIME
wxDateTime Time;
#endif
wxDECLARE_NO_COPY_CLASS(wxXmlResourceDataRecord);
};
class wxXmlResourceDataRecords : public wxVector<wxXmlResourceDataRecord*>
@@ -314,6 +345,8 @@ bool wxXmlResource::Load(const wxString& filemask_)
{
wxString filemask = ConvertFileNameToURL(filemask_);
bool allOK = true;
#if wxUSE_FILESYSTEM
wxFileSystem fsys;
# define wxXmlFindFirst fsys.FindFirst(filemask, wxFILE)
@@ -335,14 +368,16 @@ bool wxXmlResource::Load(const wxString& filemask_)
if ( IsArchive(fnd) )
{
if ( !Load(fnd + wxT("#zip:*.xrc")) )
return false;
allOK = false;
}
else // a single resource URL
#endif // wxUSE_FILESYSTEM
{
wxXmlResourceDataRecord *drec = new wxXmlResourceDataRecord;
drec->File = fnd;
Data().push_back(drec);
wxXmlDocument * const doc = DoLoadFile(fnd);
if ( !doc )
allOK = false;
else
Data().push_back(new wxXmlResourceDataRecord(fnd, doc));
}
fnd = wxXmlFindNext;
@@ -350,7 +385,7 @@ bool wxXmlResource::Load(const wxString& filemask_)
# undef wxXmlFindFirst
# undef wxXmlFindNext
return UpdateResources();
return allOK;
}
bool wxXmlResource::Unload(const wxString& filename)
@@ -613,12 +648,84 @@ static void PreprocessForIdRanges(wxXmlNode *rootnode)
bool wxXmlResource::UpdateResources()
{
bool rt = true;
bool modif;
# if wxUSE_FILESYSTEM
wxFSFile *file = NULL;
wxUnusedVar(file);
for ( wxXmlResourceDataRecords::iterator i = Data().begin();
i != Data().end(); ++i )
{
wxXmlResourceDataRecord* const rec = *i;
// Check if we need to reload this one.
// We never do it if this flag is specified.
if ( m_flags & wxXRC_NO_RELOADING )
continue;
// Otherwise check its modification time if we can.
#if wxUSE_DATETIME
const wxDateTime lastModTime = GetXRCFileModTime(rec->File);
if ( lastModTime.IsValid() && lastModTime <= rec->Time )
#else // !wxUSE_DATETIME
// Never reload the file contents: we can't know whether it changed or
// not in this build configuration and it would be unexpected and
// counter-productive to get a performance hit (due to constant
// reloading of XRC files) in a minimal wx build which is presumably
// used because of resource constraints of the current platform.
#endif // wxUSE_DATETIME/!wxUSE_DATETIME
{
// No need to reload, the file wasn't modified since we did it
// last.
continue;
}
wxXmlDocument * const doc = DoLoadFile(rec->File);
if ( !doc )
{
// Notice that we keep the old XML document: it seems better to
// preserve it instead of throwing it away if we have nothing to
// replace it with.
rt = false;
continue;
}
// Replace the old resource contents with the new one.
delete rec->Doc;
rec->Doc = doc;
// And, now that we loaded it successfully, update the last load time.
#if wxUSE_DATETIME
rec->Time = lastModTime.IsValid() ? lastModTime : wxDateTime::Now();
#endif // wxUSE_DATETIME
}
return rt;
}
wxXmlDocument *wxXmlResource::DoLoadFile(const wxString& filename)
{
wxLogTrace(wxT("xrc"), wxT("opening file '%s'"), filename);
wxInputStream *stream = NULL;
#if wxUSE_FILESYSTEM
wxFileSystem fsys;
# endif
wxScopedPtr<wxFSFile> file(fsys.OpenFile(filename));
if (file)
{
// Notice that we don't have ownership of the stream in this case, it
// remains owned by wxFSFile.
stream = file->GetStream();
}
#else // !wxUSE_FILESYSTEM
wxFileInputStream fstream(filename);
stream = &fstream;
#endif // wxUSE_FILESYSTEM/!wxUSE_FILESYSTEM
if ( !stream || !stream->IsOk() )
{
wxLogError(_("Cannot open resources file '%s'."), filename);
return NULL;
}
wxString encoding(wxT("UTF-8"));
#if !wxUSE_UNICODE && wxUSE_INTL
@@ -631,115 +738,43 @@ bool wxXmlResource::UpdateResources()
}
#endif
for ( wxXmlResourceDataRecords::iterator i = Data().begin();
i != Data().end(); ++i )
wxScopedPtr<wxXmlDocument> doc(new wxXmlDocument);
if (!doc->Load(*stream, encoding))
{
wxXmlResourceDataRecord* const rec = *i;
modif = (rec->Doc == NULL);
if (!modif && !(m_flags & wxXRC_NO_RELOADING))
{
# if wxUSE_FILESYSTEM
file = fsys.OpenFile(rec->File);
# if wxUSE_DATETIME
modif = file && file->GetModificationTime() > rec->Time;
# else // wxUSE_DATETIME
modif = true;
# endif // wxUSE_DATETIME
if (!file)
{
wxLogError(_("Cannot open file '%s'."), rec->File);
rt = false;
}
wxDELETE(file);
wxUnusedVar(file);
# else // wxUSE_FILESYSTEM
# if wxUSE_DATETIME
modif = wxDateTime(wxFileModificationTime(rec->File)) > rec->Time;
# else // wxUSE_DATETIME
modif = true;
# endif // wxUSE_DATETIME
# endif // wxUSE_FILESYSTEM
}
if (modif)
{
wxLogTrace(wxT("xrc"), wxT("opening file '%s'"), rec->File);
wxInputStream *stream = NULL;
# if wxUSE_FILESYSTEM
file = fsys.OpenFile(rec->File);
if (file)
stream = file->GetStream();
# else
stream = new wxFileInputStream(rec->File);
# endif
if (stream)
{
delete rec->Doc;
rec->Doc = new wxXmlDocument;
}
if (!stream || !stream->IsOk() || !rec->Doc->Load(*stream, encoding))
{
wxLogError(_("Cannot load resources from file '%s'."),
rec->File);
wxDELETE(rec->Doc);
rt = false;
}
else if (rec->Doc->GetRoot()->GetName() != wxT("resource"))
{
ReportError
(
rec->Doc->GetRoot(),
"invalid XRC resource, doesn't have root node <resource>"
);
wxDELETE(rec->Doc);
rt = false;
}
else
{
long version;
int v1, v2, v3, v4;
wxString verstr = rec->Doc->GetRoot()->GetAttribute(
wxT("version"), wxT("0.0.0.0"));
if (wxSscanf(verstr.c_str(), wxT("%i.%i.%i.%i"),
&v1, &v2, &v3, &v4) == 4)
version = v1*256*256*256+v2*256*256+v3*256+v4;
else
version = 0;
if (m_version == -1)
m_version = version;
if (m_version != version)
{
wxLogError("Resource files must have same version number.");
rt = false;
}
ProcessPlatformProperty(rec->Doc->GetRoot());
PreprocessForIdRanges(rec->Doc->GetRoot());
wxIdRangeManager::Get()->FinaliseRanges(rec->Doc->GetRoot());
#if wxUSE_DATETIME
#if wxUSE_FILESYSTEM
rec->Time = file->GetModificationTime();
#else // wxUSE_FILESYSTEM
rec->Time = wxDateTime(wxFileModificationTime(rec->File));
#endif // wxUSE_FILESYSTEM
#endif // wxUSE_DATETIME
}
# if wxUSE_FILESYSTEM
wxDELETE(file);
wxUnusedVar(file);
# else
wxDELETE(stream);
# endif
}
wxLogError(_("Cannot load resources from file '%s'."), filename);
return NULL;
}
return rt;
wxXmlNode * const root = doc->GetRoot();
if (root->GetName() != wxT("resource"))
{
ReportError
(
root,
"invalid XRC resource, doesn't have root node <resource>"
);
return NULL;
}
long version;
int v1, v2, v3, v4;
wxString verstr = root->GetAttribute(wxT("version"), wxT("0.0.0.0"));
if (wxSscanf(verstr, wxT("%i.%i.%i.%i"), &v1, &v2, &v3, &v4) == 4)
version = v1*256*256*256+v2*256*256+v3*256+v4;
else
version = 0;
if (m_version == -1)
m_version = version;
if (m_version != version)
{
wxLogWarning("Resource files must have same version number.");
}
ProcessPlatformProperty(root);
PreprocessForIdRanges(root);
wxIdRangeManager::Get()->FinaliseRanges(root);
return doc.release();
}
wxXmlNode *wxXmlResource::DoFindResource(wxXmlNode *parent,