From 3e1512cdfecd2c531567e6603083d9c3968216c7 Mon Sep 17 00:00:00 2001
From: Vadim Zeitlin <vadim@wxwidgets.org>
Date: Sat, 21 Jul 2007 23:47:22 +0000
Subject: [PATCH] added support for user-defined types to wxConfig (patch
 1753875)

git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@47634 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
---
 docs/changes.txt         |   1 +
 docs/latex/wx/config.tex |  35 ++++++--
 include/wx/confbase.h    |  70 ++++++++++++++--
 src/common/config.cpp    |  34 ++++----
 tests/config/config.cpp  | 175 +++++++++++++++++++++++++++++++++++++++
 5 files changed, 283 insertions(+), 32 deletions(-)
 create mode 100644 tests/config/config.cpp

diff --git a/docs/changes.txt b/docs/changes.txt
index 1e06a74e89..75824c0085 100644
--- a/docs/changes.txt
+++ b/docs/changes.txt
@@ -106,6 +106,7 @@ Major new features in this release
 
 All:
 
+- Added support for user-defined types to wxConfig (Marcin Wojdyr)
 - Added wxJoin() and wxSplit() functions (Francesco Montorsi)
 - Added wxMutex::LockTimeout() (Aleksandr Napylov)
 - Added wxMemoryInputStream(wxInputStream&) ctor (Stas Sergeev)
diff --git a/docs/latex/wx/config.tex b/docs/latex/wx/config.tex
index 5955677d54..61c2136e6a 100644
--- a/docs/latex/wx/config.tex
+++ b/docs/latex/wx/config.tex
@@ -262,13 +262,11 @@ These function are the core of wxConfigBase class: they allow you to read and
 write config file data. All {\it Read} function take a default value which
 will be returned if the specified key is not found in the config file.
 
-Currently, only two types of data are supported: string and long (but it might
-change in the near future). To work with other types: for {\it int} or {\it
-bool} you can work with function taking/returning {\it long} and just use the
-casts. Better yet, just use {\it long} for all variables which you're going to
-save in the config file: chances are that {\tt sizeof(bool) == sizeof(int) == sizeof(long)} anyhow on your system. For {\it float}, {\it double} and, in
-general, any other type you'd have to translate them to/from string
-representation and use string functions.
+Currently, supported types of data are:
+\helpref{wxString}{wxstring}, {\it long}, {\it double}, {\it bool},
+\helpref{wxColour}{wxcolour} and any other types,
+for which functions \helpref{wxToString}{wxtostring}
+and \helpref{wxFromString}{wxfromstring} are defined.
 
 Try not to read long values into string variables and vice versa: although it
 just might work with wxFileConfig, you will get a system error with
@@ -721,6 +719,23 @@ not found, {\it defaultVal} is used instead.
 Reads a binary block, returning \true if the value was found. If the value was
 not found, {\it buf} is not changed.
 
+\constfunc{bool}{Read}{\param{const wxString\& }{ key}, \param{T*}{ value}}
+
+Reads a value of type T, for which function
+\helpref{wxFromString}{wxfromstring} is defined,
+returning \true if the value was found.
+If the value was not found, {\it value} is not changed.
+
+\constfunc{bool}{Read}{\param{const wxString\& }{ key}, \param{T*}{ value},
+                       \param{T const\& }{ defaultVal}}
+
+Reads a value of type T, for which function
+\helpref{wxFromString}{wxfromstring} is defined,
+returning \true if the value was found.
+If the value was not found, {\it defaultVal} is used instead.
+
+bool Read(const wxString& key, T* value) const;
+
 \pythonnote{In place of a single overloaded method name, wxPython
 implements the following methods:\par
 \indented{2cm}{\begin{twocollist}
@@ -813,7 +828,11 @@ value}}
 
 \func{bool}{Write}{\param{const wxString\& }{ key}, \param{const wxMemoryBuffer\&}{ buf}}
 
-These functions write the specified value to the config file and return \true on success.
+\func{bool}{Write}{\param{const wxString\& }{ key}, \param{const T\&}{ buf}}
+
+These functions write the specified value to the config file and return \true
+on success. In the last one, function \helpref{wxToString}{wxtostring} must be
+defined for type {\it T}.
 
 \pythonnote{In place of a single overloaded method name, wxPython
 implements the following methods:\par
diff --git a/include/wx/confbase.h b/include/wx/confbase.h
index 39d06305b8..bcba0a1eea 100644
--- a/include/wx/confbase.h
+++ b/include/wx/confbase.h
@@ -166,7 +166,7 @@ public:
   bool Read(const wxString& key, long *pl) const;
   bool Read(const wxString& key, long *pl, long defVal) const;
 
-    // read an int
+    // read an int (wrapper around `long' version)
   bool Read(const wxString& key, int *pi) const;
   bool Read(const wxString& key, int *pi, int defVal) const;
 
@@ -185,12 +185,45 @@ public:
    // no default version since it does not make sense for binary data
 #endif // wxUSE_BASE64
 
+
+  // read other types, for which wxFromString is defined
+  template <typename T>
+  bool Read(const wxString& key, T* value) const
+  {
+      wxString s;
+      if ( !Read(key, &s) )
+          return false;
+      return wxFromString(s, value);
+  }
+
+  template <typename T>
+  bool Read(const wxString& key, T* value, const T& defVal) const
+  {
+      const bool found = Read(key, value);
+      if ( !found )
+      {
+          if (IsRecordingDefaults())
+              ((wxConfigBase *)this)->Write(key, defVal);
+          *value = defVal;
+      }
+      return found;
+  }
+
   // convenience functions returning directly the value (we don't have them for
   // int/double/bool as there would be ambiguities with the long one then)
   wxString Read(const wxString& key,
                 const wxString& defVal = wxEmptyString) const
     { wxString s; (void)Read(key, &s, defVal); return s; }
 
+  // we have to provide a separate version for C strings as otherwise the
+  // template Read() would be used
+  wxString Read(const wxString& key, const char* defVal) const
+    { return Read(key, wxString(defVal)); }
+#if wxUSE_WCHAR_T
+  wxString Read(const wxString& key, const wchar_t* defVal) const
+    { return Read(key, wxString(defVal)); }
+#endif
+
   long Read(const wxString& key, long defVal) const
     { long l; (void)Read(key, &l, defVal); return l; }
 
@@ -201,9 +234,6 @@ public:
   bool Write(const wxString& key, long value)
     { return DoWriteLong(key, value); }
 
-  bool Write(const wxString& key, int value)
-    { return DoWriteInt(key, value); }
-
   bool Write(const wxString& key, double value)
     { return DoWriteDouble(key, value); }
 
@@ -219,11 +249,41 @@ public:
   // would be converted to bool and not to wxString as expected!
   bool Write(const wxString& key, const char *value)
     { return Write(key, wxString(value)); }
+  bool Write(const wxString& key, const unsigned char *value)
+    { return Write(key, wxString(value)); }
 #if wxUSE_WCHAR_T
   bool Write(const wxString& key, const wchar_t *value)
     { return Write(key, wxString(value)); }
 #endif
 
+
+  // we also have to provide specializations for other types which we want to
+  // handle using the specialized DoWriteXXX() instead of the generic template
+  // version below
+  bool Write(const wxString& key, short value)
+    { return DoWriteLong(key, value); }
+
+  bool Write(const wxString& key, unsigned short value)
+    { return DoWriteLong(key, value); }
+
+  bool Write(const wxString& key, unsigned int value)
+    { return DoWriteLong(key, value); }
+
+  bool Write(const wxString& key, int value)
+    { return DoWriteLong(key, value); }
+
+  bool Write(const wxString& key, unsigned long value)
+    { return DoWriteLong(key, value); }
+
+  bool Write(const wxString& key, float value)
+    { return DoWriteDouble(key, value); }
+
+
+  // for other types, use wxToString()
+  template <typename T>
+  bool Write(const wxString& key, T const& value)
+    { return Write(key, wxToString(value)); }
+
   // permanently writes all changes
   virtual bool Flush(bool bCurrentOnly = false) = 0;
 
@@ -283,7 +343,6 @@ protected:
   // do read/write the values of different types
   virtual bool DoReadString(const wxString& key, wxString *pStr) const = 0;
   virtual bool DoReadLong(const wxString& key, long *pl) const = 0;
-  virtual bool DoReadInt(const wxString& key, int *pi) const;
   virtual bool DoReadDouble(const wxString& key, double* val) const;
   virtual bool DoReadBool(const wxString& key, bool* val) const;
 #if wxUSE_BASE64
@@ -292,7 +351,6 @@ protected:
 
   virtual bool DoWriteString(const wxString& key, const wxString& value) = 0;
   virtual bool DoWriteLong(const wxString& key, long value) = 0;
-  virtual bool DoWriteInt(const wxString& key, int value);
   virtual bool DoWriteDouble(const wxString& key, double value);
   virtual bool DoWriteBool(const wxString& key, bool value);
 #if wxUSE_BASE64
diff --git a/src/common/config.cpp b/src/common/config.cpp
index c4ae2ed8d1..adaa109546 100644
--- a/src/common/config.cpp
+++ b/src/common/config.cpp
@@ -156,29 +156,32 @@ wxConfigBase *wxConfigBase::Create()
 
 IMPLEMENT_READ_FOR_TYPE(String, wxString, const wxString&, ExpandEnvVars)
 IMPLEMENT_READ_FOR_TYPE(Long, long, long, long)
-IMPLEMENT_READ_FOR_TYPE(Int, int, int, int)
 IMPLEMENT_READ_FOR_TYPE(Double, double, double, double)
 IMPLEMENT_READ_FOR_TYPE(Bool, bool, bool, bool)
 
 #undef IMPLEMENT_READ_FOR_TYPE
 
-// the DoReadXXX() for the other types have implementation in the base class
-// but can be overridden in the derived ones
-bool wxConfigBase::DoReadInt(const wxString& key, int *pi) const
+// int is stored as long
+bool wxConfigBase::Read(const wxString& key, int *pi) const
 {
-    wxCHECK_MSG( pi, false, _T("wxConfig::Read(): NULL parameter") );
-
-    long l;
-    if ( !DoReadLong(key, &l) )
-        return false;
-
-    wxASSERT_MSG( l < INT_MAX, _T("overflow in wxConfig::DoReadInt") );
-
+    long l = *pi;
+    bool r = Read(key, &l);
+    wxASSERT_MSG( l < INT_MAX, _T("int overflow in wxConfig::Read") );
     *pi = (int)l;
+    return r;
+}
 
-    return true;
+bool wxConfigBase::Read(const wxString& key, int *pi, int defVal) const
+{
+    long l = *pi;
+    bool r = Read(key, &l, defVal);
+    wxASSERT_MSG( l < INT_MAX, _T("int overflow in wxConfig::Read") );
+    *pi = (int)l;
+    return r;
 }
 
+// the DoReadXXX() for the other types have implementation in the base class
+// but can be overridden in the derived ones
 bool wxConfigBase::DoReadBool(const wxString& key, bool* val) const
 {
     wxCHECK_MSG( val, false, _T("wxConfig::Read(): NULL parameter") );
@@ -225,11 +228,6 @@ bool wxConfigBase::DoWriteDouble(const wxString& key, double val)
     return DoWriteString(key, wxString::Format(_T("%g"), val));
 }
 
-bool wxConfigBase::DoWriteInt(const wxString& key, int value)
-{
-    return DoWriteLong(key, (long)value);
-}
-
 bool wxConfigBase::DoWriteBool(const wxString& key, bool value)
 {
     return DoWriteLong(key, value ? 1l : 0l);
diff --git a/tests/config/config.cpp b/tests/config/config.cpp
new file mode 100644
index 0000000000..1fc9a47dc5
--- /dev/null
+++ b/tests/config/config.cpp
@@ -0,0 +1,175 @@
+///////////////////////////////////////////////////////////////////////////////
+// Name:        tests/config/config.cpp
+// Purpose:     wxConfig unit test
+// Author:      Marcin Wojdyr
+// Created:     2007-07-07
+// RCS-ID:      $Id$
+// Copyright:   (c) 2007 Marcin Wojdyr
+///////////////////////////////////////////////////////////////////////////////
+
+// see also tests/fileconf/fileconftest.cpp for wxFileConfig specific tests
+
+// ----------------------------------------------------------------------------
+// headers
+// ----------------------------------------------------------------------------
+
+#include "testprec.h"
+
+#ifdef __BORLANDC__
+    #pragma hdrstop
+#endif
+
+#ifndef WX_PRECOMP
+    #include "wx/wx.h"
+#endif // WX_PRECOMP
+
+#include "wx/config.h"
+#include "wx/colour.h"
+
+// --------------------------------------------------------------------------
+// test class
+// --------------------------------------------------------------------------
+
+class ConfigTestCase : public CppUnit::TestCase
+{
+public:
+    ConfigTestCase() {}
+
+private:
+    CPPUNIT_TEST_SUITE( ConfigTestCase );
+        CPPUNIT_TEST( ReadWriteLocalTest );
+        CPPUNIT_TEST( RecordingDefaultsTest );
+    CPPUNIT_TEST_SUITE_END();
+
+    void ReadWriteLocalTest();
+    void RecordingDefaultsTest();
+
+    DECLARE_NO_COPY_CLASS(ConfigTestCase)
+};
+
+// register in the unnamed registry so that these tests are run by default
+CPPUNIT_TEST_SUITE_REGISTRATION( ConfigTestCase );
+
+// also include in it's own registry so that these tests can be run alone
+CPPUNIT_TEST_SUITE_NAMED_REGISTRATION( ConfigTestCase, "ConfigTestCase" );
+
+void ConfigTestCase::ReadWriteLocalTest()
+{
+    wxString app = wxT("wxConfigTestCase");
+    wxString vendor = wxT("wxWidgets");
+    wxConfig *config = new wxConfig(app, vendor, wxT(""), wxT(""),
+                                    wxCONFIG_USE_LOCAL_FILE);
+    config->DeleteAll();
+    config->Write(wxT("string1"), wxT("abc"));
+    config->Write(wxT("string2"), wxString(wxT("def")));
+    config->Write(wxT("int1"), 123);
+    config->Write(wxString(wxT("long1")), 234L);
+    config->Write(wxT("double1"), 345.67);
+    config->Write(wxT("bool1"), true);
+    config->Write(wxT("color1"), wxColour(11,22,33,44));
+    config->Flush();
+    delete config;
+
+    config = new wxConfig(app, vendor, wxT(""), wxT(""),
+                                    wxCONFIG_USE_LOCAL_FILE);
+    wxString string1 = config->Read(wxT("string1"));
+    CPPUNIT_ASSERT( string1 == wxT("abc") );
+    string1 = config->Read(wxT("string1"), wxT("defaultvalue"));
+    CPPUNIT_ASSERT( string1 == wxT("abc") );
+    wxString string2;
+    bool r = config->Read(wxT("string2"), &string2);
+    CPPUNIT_ASSERT( r == true );
+    CPPUNIT_ASSERT( string2 == wxT("def") );
+    r = config->Read(wxT("string2"), &string2, wxT("defaultvalue"));
+    CPPUNIT_ASSERT( r == true );
+    CPPUNIT_ASSERT( string2 == wxT("def") );
+    int int1 = config->Read(wxT("int1"), 5);
+    CPPUNIT_ASSERT( int1 == 123 );
+    long long1;
+    r = config->Read(wxT("long1"), &long1);
+    CPPUNIT_ASSERT( r == true );
+    CPPUNIT_ASSERT( long1 == 234 );
+    bool bool1;
+    r = config->Read(wxT("foo"), &bool1);
+    CPPUNIT_ASSERT( r == false );
+    r = config->Read(wxT("bool1"), &bool1);
+    CPPUNIT_ASSERT( r == true );
+    CPPUNIT_ASSERT( bool1 == true );
+    wxColour color1;
+    r = config->Read(wxT("color1"), &color1);
+    CPPUNIT_ASSERT( r == true );
+    CPPUNIT_ASSERT( color1 == wxColour(11,22,33,44) );
+    config->DeleteAll();
+    delete config;
+}
+
+void ConfigTestCase::RecordingDefaultsTest()
+{
+    wxString app = wxT("wxConfigTestCaseRD");
+    wxString vendor = wxT("wxWidgets");
+    wxConfig *config = new wxConfig(app, vendor, wxT(""), wxT(""),
+                                    wxCONFIG_USE_LOCAL_FILE);
+    config->DeleteAll();
+    config->SetRecordDefaults(false); // by default it is false
+    wxString string1, string2, string3, string4;
+    string1 = config->Read(wxT("string1"), wxT("abc"));
+    string2 = config->Read(wxT("string2"), wxString(wxT("def")));
+    config->Read(wxT("string3"), &string3, wxT("abc"));
+    config->Read(wxT("string4"), &string4, wxString(wxT("def")));
+    int int1, int2;
+    config->Read(wxT("int1"), &int1, 123);
+    int2 = config->Read(wxT("int2"), 1234);
+    long long1;
+    config->Read(wxString(wxT("long1")), &long1, 234L);
+    double double1;
+    config->Read(wxT("double1"), &double1, 345.67);
+    bool bool1;
+    config->Read(wxT("bool1"), &bool1, true);
+    wxColour color1;
+    config->Read(wxT("color1"), &color1, wxColour(11,22,33,44));
+
+    CPPUNIT_ASSERT ( config->GetNumberOfEntries() == 0 );
+
+    config->SetRecordDefaults(true);
+
+    bool r;
+    string1 = config->Read(wxT("string1"), wxT("abc"));
+    string2 = config->Read(wxT("string2"), wxString(wxT("def")));
+    r = config->Read(wxT("string3"), &string3, wxT("abc"));
+    CPPUNIT_ASSERT( r == false );
+    r = config->Read(wxT("string4"), &string4, wxString(wxT("def")));
+    CPPUNIT_ASSERT( r == false );
+    r = config->Read(wxT("int1"), &int1, 123);
+    CPPUNIT_ASSERT( r == false );
+    int2 = config->Read(wxT("int2"), 1234);
+    r = config->Read(wxString(wxT("long1")), &long1, 234L);
+    CPPUNIT_ASSERT( r == false );
+    r = config->Read(wxT("double1"), &double1, 345.67);
+    CPPUNIT_ASSERT( r == false );
+    r = config->Read(wxT("bool1"), &bool1, true);
+    CPPUNIT_ASSERT( r == false );
+    r = config->Read(wxT("color1"), &color1, wxColour(11,22,33,44));
+    CPPUNIT_ASSERT( r == false );
+
+    CPPUNIT_ASSERT ( config->GetNumberOfEntries() == 10 );
+
+    r = config->Read(wxT("string3"), &string3, wxT("abc"));
+    CPPUNIT_ASSERT( r == true );
+    r = config->Read(wxT("string4"), &string4, wxString(wxT("def")));
+    CPPUNIT_ASSERT( r == true );
+    r = config->Read(wxT("int1"), &int1, 123);
+    CPPUNIT_ASSERT( r == true );
+    r = config->Read(wxString(wxT("long1")), &long1, 234L);
+    CPPUNIT_ASSERT( r == true );
+    r = config->Read(wxT("double1"), &double1, 345.67);
+    CPPUNIT_ASSERT( r == true );
+    r = config->Read(wxT("bool1"), &bool1, true);
+    CPPUNIT_ASSERT( r == true );
+    r = config->Read(wxT("color1"), &color1, wxColour(11,22,33,44));
+    CPPUNIT_ASSERT( r == true );
+
+    config->DeleteAll();
+    delete config;
+}
+
+
-- 
2.47.2

