35#include <wx/aui/framemanager.h>
38#include <wx/fileconf.h>
39#include <wx/filename.h>
42#include <wx/stdstream.h>
43#include <wx/wfstream.h>
50 std::replace( aPath.begin(), aPath.end(),
'.',
'/' );
51 aPath.insert( 0,
"/" );
53 nlohmann::json::json_pointer p;
57 p = nlohmann::json::json_pointer( aPath );
61 wxASSERT_MSG(
false, wxT(
"Invalid pointer path in PointerFromString!" ) );
69 int aSchemaVersion,
bool aCreateIfMissing,
bool aCreateIfDefault,
71 m_filename( aFilename ),
72 m_legacy_filename(
"" ),
73 m_location( aLocation ),
74 m_createIfMissing( aCreateIfMissing ),
75 m_createIfDefault( aCreateIfDefault ),
76 m_writeFile( aWriteFile ),
77 m_deleteLegacyAfterMigration( true ),
78 m_resetParamsIfMissing( true ),
79 m_schemaVersion( aSchemaVersion ),
82 m_internals = std::make_unique<JSON_SETTINGS_INTERNALS>();
90 wxLogTrace(
traceSettings, wxT(
"Error: Could not create filename field for %s" ),
144 wxLogTrace(
traceSettings, wxT(
"param '%s' load err" ), param->GetJsonPath().c_str() );
157 bool migrated =
false;
158 bool legacy_migrated =
false;
162 auto migrateFromLegacy = [&] ( wxFileName& aPath ) {
164 bool backed_up =
false;
167 if( aPath.IsDirWritable() )
169 temp.AssignTempFileName( aPath.GetFullPath() );
171 if( !wxCopyFile( aPath.GetFullPath(), temp.GetFullPath() ) )
173 wxLogTrace(
traceSettings, wxT(
"%s: could not create temp file for migration" ),
183 wxConfigBase::DontCreateOnDemand();
184 auto cfg = std::make_unique<wxFileConfig>( wxT(
"" ), wxT(
"" ), aPath.GetFullPath() );
192 wxT(
"%s: migrated; not all settings were found in legacy file" ),
205 if( !wxCopyFile( temp.GetFullPath(), aPath.GetFullPath() ) )
208 wxT(
"migrate; copy temp file %s to %s failed" ),
209 temp.GetFullPath(), aPath.GetFullPath() );
212 if( !wxRemoveFile( temp.GetFullPath() ) )
215 wxT(
"migrate; failed to remove temp file %s" ),
216 temp.GetFullPath() );
221 legacy_migrated =
true;
226 if( aDirectory.empty() )
233 wxString dir( aDirectory );
244 migrateFromLegacy(
path );
252 migrateFromLegacy(
path );
261 if( !
path.IsFileWritable() )
266 wxFFileInputStream fp(
path.GetFullPath(), wxT(
"rt" ) );
267 wxStdInputStream fstream( fp );
271 *
static_cast<nlohmann::json*
>(
m_internals.get() ) =
272 nlohmann::json::parse( fstream,
nullptr,
288 wxLogTrace(
traceSettings, wxT(
"%s: file version could not be read!" ),
295 wxLogTrace(
traceSettings, wxT(
"%s: attempting migration from version %d to %d" ),
310 wxT(
"%s: warning: file version %d is newer than latest (%d)" ),
316 wxLogTrace(
traceSettings, wxT(
"%s exists but can't be opened for read" ),
320 catch( nlohmann::json::parse_error& error )
323 wxLogTrace(
traceSettings, wxT(
"Json parse error reading %s: %s" ),
324 path.GetFullPath(), error.what() );
325 wxLogTrace(
traceSettings, wxT(
"Attempting migration in case file is in legacy format" ) );
326 migrateFromLegacy(
path );
335 settings->LoadFromFile();
340 if(
m_writeFile && ( legacy_migrated || migrated ) )
344 wxLogTrace(
traceSettings, wxT(
"Warning: could not remove legacy file %s" ),
345 path.GetFullPath() );
359 bool modified =
false;
363 modified |= !param->MatchesFile(
this );
364 param->Store(
this );
389 if( aDirectory.empty() )
396 wxString dir( aDirectory );
403 wxT(
"File for %s doesn't exist and m_createIfMissing == false; not saving" ),
409 if( !
path.DirExists() && !
path.Mkdir() )
411 wxLogTrace(
traceSettings, wxT(
"Warning: could not create path %s, can't save %s" ),
416 if( (
path.FileExists() && !
path.IsFileWritable() ) ||
417 ( !
path.FileExists() && !
path.IsDirWritable() ) )
423 bool modified =
false;
426 modified |= settings->SaveToFile();
430 if( !modified && !aForce &&
path.FileExists() )
438 wxT(
"%s contents still default and m_createIfDefault == false; not saving" ),
454 std::stringstream buffer;
455 buffer << std::setw( 2 ) << toSave << std::endl;
457 wxFFileOutputStream fileStream(
path.GetFullPath(),
"wb" );
459 if( !fileStream.IsOk()
460 || !fileStream.WriteAll( buffer.str().c_str(), buffer.str().size() ) )
466 catch( nlohmann::json::exception& error )
468 wxLogTrace(
traceSettings, wxT(
"Catch error: could not save %s. Json error %s" ),
474 wxLogTrace(
traceSettings, wxT(
"Error: could not save %s." ) );
486 std::stringstream buffer;
487 buffer << std::setw( 2 ) << *
m_internals << std::endl;
497 wxFFileInputStream fp( aPath, wxT(
"rt" ) );
498 wxStdInputStream fstream( fp );
502 *
static_cast<nlohmann::json*
>(
m_internals.get() ) =
503 nlohmann::json::parse( fstream,
nullptr,
512 catch( nlohmann::json::parse_error& error )
514 wxLogTrace(
traceSettings, wxT(
"Json parse error reading %s: %s" ), aPath, error.what() );
527 nlohmann::json::json_pointer ptr =
m_internals->PointerFromString( aPath );
533 return std::optional<nlohmann::json>{
m_internals->at( ptr ) };
540 return std::optional<nlohmann::json>{};
544template<
typename ValueType>
547 if( std::optional<nlohmann::json> ret =
GetJson( aPath ) )
551 return ret->get<ValueType>();
563template std::optional<bool> JSON_SETTINGS::Get<bool>(
const std::string& aPath )
const;
564template std::optional<double> JSON_SETTINGS::Get<double>(
const std::string& aPath )
const;
565template std::optional<float> JSON_SETTINGS::Get<float>(
const std::string& aPath )
const;
566template std::optional<int> JSON_SETTINGS::Get<int>(
const std::string& aPath )
const;
567template std::optional<unsigned int> JSON_SETTINGS::Get<unsigned int>(
const std::string& aPath )
const;
568template std::optional<unsigned long long> JSON_SETTINGS::Get<unsigned long long>(
const std::string& aPath )
const;
569template std::optional<std::string> JSON_SETTINGS::Get<std::string>(
const std::string& aPath )
const;
570template std::optional<nlohmann::json> JSON_SETTINGS::Get<nlohmann::json>(
const std::string& aPath )
const;
571template std::optional<KIGFX::COLOR4D> JSON_SETTINGS::Get<KIGFX::COLOR4D>(
const std::string& aPath )
const;
572template std::optional<BOM_FIELD> JSON_SETTINGS::Get<BOM_FIELD>(
const std::string& aPath )
const;
573template std::optional<BOM_PRESET> JSON_SETTINGS::Get<BOM_PRESET>(
const std::string& aPath )
const;
574template std::optional<BOM_FMT_PRESET> JSON_SETTINGS::Get<BOM_FMT_PRESET>(
const std::string& aPath )
const;
575template std::optional<wxPoint> JSON_SETTINGS::Get<wxPoint>(
const std::string& aPath )
const;
576template std::optional<wxSize> JSON_SETTINGS::Get<wxSize>(
const std::string& aPath )
const;
577template std::optional<wxRect> JSON_SETTINGS::Get<wxRect>(
const std::string& aPath )
const;
578template std::optional<wxAuiPaneInfo> JSON_SETTINGS::Get<wxAuiPaneInfo>(
const std::string& aPath )
const;
580template<
typename ValueType>
588template void JSON_SETTINGS::Set<bool>(
const std::string& aPath,
bool aValue );
589template void JSON_SETTINGS::Set<double>(
const std::string& aPath,
double aValue );
590template void JSON_SETTINGS::Set<float>(
const std::string& aPath,
float aValue );
591template void JSON_SETTINGS::Set<int>(
const std::string& aPath,
int aValue );
592template void JSON_SETTINGS::Set<unsigned int>(
const std::string& aPath,
unsigned int aValue );
593template void JSON_SETTINGS::Set<unsigned long long>(
const std::string& aPath,
unsigned long long aValue );
594template void JSON_SETTINGS::Set<const char*>(
const std::string& aPath,
const char* aValue );
595template void JSON_SETTINGS::Set<std::string>(
const std::string& aPath, std::string aValue );
596template void JSON_SETTINGS::Set<nlohmann::json>(
const std::string& aPath, nlohmann::json aValue );
597template void JSON_SETTINGS::Set<KIGFX::COLOR4D>(
const std::string& aPath,
KIGFX::COLOR4D aValue );
598template void JSON_SETTINGS::Set<BOM_FIELD>(
const std::string& aPath,
BOM_FIELD aValue );
599template void JSON_SETTINGS::Set<BOM_PRESET>(
const std::string& aPath,
BOM_PRESET aValue );
600template void JSON_SETTINGS::Set<BOM_FMT_PRESET>(
const std::string& aPath,
BOM_FMT_PRESET aValue );
601template void JSON_SETTINGS::Set<wxPoint>(
const std::string& aPath, wxPoint aValue );
602template void JSON_SETTINGS::Set<wxSize>(
const std::string& aPath, wxSize aValue );
603template void JSON_SETTINGS::Set<wxRect>(
const std::string& aPath, wxRect aValue );
604template void JSON_SETTINGS::Set<wxAuiPaneInfo>(
const std::string& aPath, wxAuiPaneInfo aValue );
608 std::function<
bool()> aMigrator )
610 wxASSERT( aNewSchemaVersion > aOldSchemaVersion );
612 m_migrators[aOldSchemaVersion] = std::make_pair( aNewSchemaVersion, aMigrator );
618 int filever =
m_internals->Get<
int>(
"meta.version" );
624 wxLogTrace(
traceSettings, wxT(
"Migrator missing for %s version %d!" ),
625 typeid( *this ).name(), filever );
629 std::pair<int, std::function<bool()>> pair =
m_migrators.at( filever );
633 wxLogTrace(
traceSettings, wxT(
"Migrated %s from %d to %d" ),
typeid( *this ).name(),
634 filever, pair.first );
635 filever = pair.first;
640 wxLogTrace(
traceSettings, wxT(
"Migration failed for %s from %d to %d" ),
641 typeid( *this ).name(), filever, pair.first );
653 wxT(
"MigrateFromLegacy() not implemented for %s" ),
typeid( *this ).name() );
663 if( aObj.contains( ptr ) && aObj.at( ptr ).is_string() )
665 aTarget = aObj.at( ptr ).get<wxString>();
678 if( aObj.contains( ptr ) && aObj.at( ptr ).is_boolean() )
680 aTarget = aObj.at( ptr ).get<
bool>();
693 if( aObj.contains( ptr ) && aObj.at( ptr ).is_number_integer() )
695 aTarget = aObj.at( ptr ).get<
int>();
704 unsigned int& aTarget )
708 if( aObj.contains( ptr ) && aObj.at( ptr ).is_number_unsigned() )
710 aTarget = aObj.at( ptr ).get<
unsigned int>();
718template<
typename ValueType>
720 const std::string& aDest )
724 if( aConfig->Read( aKey, &val ) )
728 ( *m_internals )[aDest] = val;
732 wxASSERT_MSG(
false, wxT(
"Could not write value in fromLegacy!" ) );
745template bool JSON_SETTINGS::fromLegacy<int>( wxConfigBase*,
const std::string&,
746 const std::string& );
748template bool JSON_SETTINGS::fromLegacy<double>( wxConfigBase*,
const std::string&,
749 const std::string& );
751template bool JSON_SETTINGS::fromLegacy<bool>( wxConfigBase*,
const std::string&,
752 const std::string& );
756 const std::string& aDest )
760 if( aConfig->Read( aKey, &str ) )
764 ( *m_internals )[aDest] = str.ToUTF8();
768 wxASSERT_MSG(
false, wxT(
"Could not write value in fromLegacyString!" ) );
780 const std::string& aDest )
784 if( aConfig->Read( aKey, &str ) )
787 color.SetFromWxString( str );
792 ( *m_internals )[aDest] = js;
796 wxASSERT_MSG(
false, wxT(
"Could not write value in fromLegacyColor!" ) );
821 return aPtr == aSettings;
826 wxLogTrace(
traceSettings, wxT(
"Flush and release %s" ), ( *it )->GetFilename() );
827 ( *it )->SaveToFile();
839 if( std::optional<nlohmann::json> opt_json =
GetJson( aPath ) )
840 return wxString( opt_json->get<std::string>().c_str(), wxConvUTF8 );
846template<>
void JSON_SETTINGS::Set<wxString>(
const std::string& aPath, wxString aVal )
848 ( *m_internals )[aPath] = aVal.ToUTF8();
853void to_json( nlohmann::json& aJson,
const wxString& aString )
855 aJson = aString.ToUTF8();
859void from_json(
const nlohmann::json& aJson, wxString& aString )
861 aString = wxString( aJson.get<std::string>().c_str(), wxConvUTF8 );
865template<
typename ResultType>
867 ResultType aDefault )
869 ResultType ret = aDefault;
873 if( aJson.contains( aKey ) )
874 ret = aJson.at( aKey ).get<ResultType>();
885 const std::string& aKey, std::string aDefault );
static nlohmann::json::json_pointer PointerFromString(std::string aPath)
Builds a JSON pointer based on a given string.
const std::string FormatAsString() const
bool fromLegacyString(wxConfigBase *aConfig, const std::string &aKey, const std::string &aDest)
Translates a legacy wxConfig string value to a given JSON pointer value.
virtual wxString getFileExt() const
std::optional< ValueType > Get(const std::string &aPath) const
Fetches a value from within the JSON document.
std::optional< nlohmann::json > GetJson(const std::string &aPath) const
Fetches a JSON object that is a subset of this JSON_SETTINGS object, using a path of the form "key1....
bool fromLegacy(wxConfigBase *aConfig, const std::string &aKey, const std::string &aDest)
Translates a legacy wxConfig value to a given JSON pointer value.
wxString m_filename
The filename (not including path) of this settings file (inicode)
SETTINGS_MANAGER * m_manager
A pointer to the settings manager managing this file (may be null)
bool Contains(const std::string &aPath) const
bool LoadFromRawFile(const wxString &aPath)
std::vector< NESTED_SETTINGS * > m_nested_settings
Nested settings files that live inside this one, if any.
virtual bool LoadFromFile(const wxString &aDirectory="")
Loads the backing file from disk and then calls Load()
bool m_createIfDefault
Whether or not the backing store file should be created if all parameters are still at their default ...
bool m_writeFile
Whether or not the backing store file should be written.
static bool SetIfPresent(const nlohmann::json &aObj, const std::string &aPath, wxString &aTarget)
Sets the given string if the given key/path is present.
virtual void Load()
Updates the parameters of this object based on the current JSON document contents.
void ResetToDefaults()
Resets all parameters to default values.
bool fromLegacyColor(wxConfigBase *aConfig, const std::string &aKey, const std::string &aDest)
Translates a legacy COLOR4D stored in a wxConfig string to a given JSON pointer value.
wxString GetFullFilename() const
bool m_createIfMissing
Whether or not the backing store file should be created it if doesn't exist.
bool Migrate()
Migrates the schema of this settings from the version in the file to the latest version.
std::vector< PARAM_BASE * > m_params
The list of parameters (owned by this object)
static ResultType fetchOrDefault(const nlohmann::json &aJson, const std::string &aKey, ResultType aDefault=ResultType())
Helper to retrieve a value from a JSON object (dictionary) as a certain result type.
virtual bool MigrateFromLegacy(wxConfigBase *aLegacyConfig)
Migrates from wxConfig to JSON-based configuration.
void registerMigration(int aOldSchemaVersion, int aNewSchemaVersion, std::function< bool(void)> aMigrator)
Registers a migration from one schema version to another.
nlohmann::json & At(const std::string &aPath)
Wrappers for the underlying JSON API so that most consumers don't need json.hpp All of these function...
JSON_SETTINGS_INTERNALS * Internals()
void ReleaseNestedSettings(NESTED_SETTINGS *aSettings)
Saves and frees a nested settings object, if it exists within this one.
void Set(const std::string &aPath, ValueType aVal)
Stores a value into the JSON document Will throw an exception if ValueType isn't something that the l...
JSON_SETTINGS(const wxString &aFilename, SETTINGS_LOC aLocation, int aSchemaVersion)
bool m_deleteLegacyAfterMigration
Whether or not to delete legacy file after migration.
std::unique_ptr< JSON_SETTINGS_INTERNALS > m_internals
virtual bool SaveToFile(const wxString &aDirectory="", bool aForce=false)
virtual wxString getLegacyFileExt() const
bool m_resetParamsIfMissing
Whether or not to set parameters to their default value if missing from JSON on Load()
std::map< int, std::pair< int, std::function< bool()> > > m_migrators
A map of starting schema version to a pair of <ending version, migrator function>
int m_schemaVersion
Version of this settings schema.
wxString m_legacy_filename
The filename of the wxConfig legacy file (if different from m_filename)
void AddNestedSettings(NESTED_SETTINGS *aSettings)
Transfers ownership of a given NESTED_SETTINGS to this object.
virtual bool Store()
Stores the current parameters into the JSON document represented by this object Note: this doesn't do...
wxString GetFilename() const
A color representation with 4 components: red, green, blue, alpha.
Instantiate the current locale within a scope in which you are expecting exceptions to be thrown.
NESTED_SETTINGS is a JSON_SETTINGS that lives inside a JSON_SETTINGS.
void SetParent(JSON_SETTINGS *aParent, bool aLoadFromFile=true)
const wxChar *const traceSettings
Flag to enable debug output of settings operations and management.
void from_json(const nlohmann::json &aJson, wxString &aString)
void to_json(nlohmann::json &aJson, const wxString &aString)
std::vector< FAB_LAYER_COLOR > dummy