35#include <wx/fileconf.h>
36#include <wx/filename.h>
38#include <wx/stdstream.h>
39#include <wx/wfstream.h>
46 std::replace( aPath.begin(), aPath.end(),
'.',
'/' );
47 aPath.insert( 0,
"/" );
49 nlohmann::json::json_pointer p;
53 p = nlohmann::json::json_pointer( aPath );
57 wxASSERT_MSG(
false, wxT(
"Invalid pointer path in PointerFromString!" ) );
65 int aSchemaVersion,
bool aCreateIfMissing,
bool aCreateIfDefault,
67 m_filename( aFilename ),
68 m_legacy_filename(
"" ),
69 m_location( aLocation ),
70 m_createIfMissing( aCreateIfMissing ),
71 m_createIfDefault( aCreateIfDefault ),
72 m_writeFile( aWriteFile ),
73 m_deleteLegacyAfterMigration( true ),
74 m_resetParamsIfMissing( true ),
75 m_schemaVersion( aSchemaVersion ),
78 m_internals = std::make_unique<JSON_SETTINGS_INTERNALS>();
86 wxLogTrace(
traceSettings, wxT(
"Error: Could not create filename field for %s" ),
140 wxLogTrace(
traceSettings, wxT(
"param '%s' load err" ), param->GetJsonPath().c_str() );
153 bool migrated =
false;
154 bool legacy_migrated =
false;
158 auto migrateFromLegacy = [&] ( wxFileName& aPath ) {
160 bool backed_up =
false;
163 if( aPath.IsDirWritable() )
165 temp.AssignTempFileName( aPath.GetFullPath() );
167 if( !wxCopyFile( aPath.GetFullPath(), temp.GetFullPath() ) )
169 wxLogTrace(
traceSettings, wxT(
"%s: could not create temp file for migration" ),
179 wxConfigBase::DontCreateOnDemand();
180 auto cfg = std::make_unique<wxFileConfig>( wxT(
"" ), wxT(
"" ), aPath.GetFullPath() );
188 wxT(
"%s: migrated; not all settings were found in legacy file" ),
201 if( !wxCopyFile( temp.GetFullPath(), aPath.GetFullPath() ) )
204 wxT(
"migrate; copy temp file %s to %s failed" ),
205 temp.GetFullPath(), aPath.GetFullPath() );
208 if( !wxRemoveFile( temp.GetFullPath() ) )
211 wxT(
"migrate; failed to remove temp file %s" ),
212 temp.GetFullPath() );
217 legacy_migrated =
true;
222 if( aDirectory.empty() )
229 wxString dir( aDirectory );
240 migrateFromLegacy(
path );
248 migrateFromLegacy(
path );
257 if( !
path.IsFileWritable() )
262 wxFFileInputStream fp(
path.GetFullPath(), wxT(
"rt" ) );
263 wxStdInputStream fstream( fp );
281 wxLogTrace(
traceSettings, wxT(
"%s: file version could not be read!" ),
288 wxLogTrace(
traceSettings, wxT(
"%s: attempting migration from version %d to %d" ),
303 wxT(
"%s: warning: file version %d is newer than latest (%d)" ),
309 wxLogTrace(
traceSettings, wxT(
"%s exists but can't be opened for read" ),
313 catch( nlohmann::json::parse_error& error )
316 wxLogTrace(
traceSettings, wxT(
"Json parse error reading %s: %s" ),
317 path.GetFullPath(), error.what() );
318 wxLogTrace(
traceSettings, wxT(
"Attempting migration in case file is in legacy format" ) );
319 migrateFromLegacy(
path );
328 settings->LoadFromFile();
333 if(
m_writeFile && ( legacy_migrated || migrated ) )
337 wxLogTrace(
traceSettings, wxT(
"Warning: could not remove legacy file %s" ),
338 path.GetFullPath() );
352 bool modified =
false;
356 modified |= !param->MatchesFile(
this );
357 param->Store(
this );
382 if( aDirectory.empty() )
389 wxString dir( aDirectory );
396 wxT(
"File for %s doesn't exist and m_createIfMissing == false; not saving" ),
402 if( !
path.DirExists() && !
path.Mkdir() )
404 wxLogTrace(
traceSettings, wxT(
"Warning: could not create path %s, can't save %s" ),
409 if( (
path.FileExists() && !
path.IsFileWritable() ) ||
410 ( !
path.FileExists() && !
path.IsDirWritable() ) )
416 bool modified =
false;
419 modified |= settings->SaveToFile();
423 if( !modified && !aForce &&
path.FileExists() )
431 wxT(
"%s contents still default and m_createIfDefault == false; not saving" ),
443 std::stringstream buffer;
444 buffer << std::setw( 2 ) << *
m_internals << std::endl;
446 wxFFileOutputStream fileStream(
path.GetFullPath(),
"wb" );
448 if( !fileStream.IsOk()
449 || !fileStream.WriteAll( buffer.str().c_str(), buffer.str().size() ) )
455 catch( nlohmann::json::exception& error )
457 wxLogTrace(
traceSettings, wxT(
"Catch error: could not save %s. Json error %s" ),
463 wxLogTrace(
traceSettings, wxT(
"Error: could not save %s." ) );
475 std::stringstream buffer;
476 buffer << std::setw( 2 ) << *
m_internals << std::endl;
486 wxFFileInputStream fp( aPath, wxT(
"rt" ) );
487 wxStdInputStream fstream( fp );
501 catch( nlohmann::json::parse_error& error )
503 wxLogTrace(
traceSettings, wxT(
"Json parse error reading %s: %s" ), aPath, error.what() );
516 nlohmann::json::json_pointer ptr =
m_internals->PointerFromString( aPath );
522 return std::optional<nlohmann::json>{
m_internals->at( ptr ) };
529 return std::optional<nlohmann::json>{};
533template<
typename ValueType>
536 if( std::optional<nlohmann::json> ret =
GetJson( aPath ) )
540 return ret->get<ValueType>();
552template std::optional<bool> JSON_SETTINGS::Get<bool>(
const std::string& aPath )
const;
553template std::optional<double> JSON_SETTINGS::Get<double>(
const std::string& aPath )
const;
554template std::optional<float> JSON_SETTINGS::Get<float>(
const std::string& aPath )
const;
555template std::optional<int> JSON_SETTINGS::Get<int>(
const std::string& aPath )
const;
556template std::optional<unsigned int> JSON_SETTINGS::Get<unsigned int>(
const std::string& aPath )
const;
557template std::optional<unsigned long long> JSON_SETTINGS::Get<unsigned long long>(
const std::string& aPath )
const;
558template std::optional<std::string> JSON_SETTINGS::Get<std::string>(
const std::string& aPath )
const;
559template std::optional<nlohmann::json> JSON_SETTINGS::Get<nlohmann::json>(
const std::string& aPath )
const;
560template std::optional<KIGFX::COLOR4D> JSON_SETTINGS::Get<KIGFX::COLOR4D>(
const std::string& aPath )
const;
563template<
typename ValueType>
571template void JSON_SETTINGS::Set<bool>(
const std::string& aPath,
bool aValue );
572template void JSON_SETTINGS::Set<double>(
const std::string& aPath,
double aValue );
573template void JSON_SETTINGS::Set<float>(
const std::string& aPath,
float aValue );
574template void JSON_SETTINGS::Set<int>(
const std::string& aPath,
int aValue );
575template void JSON_SETTINGS::Set<unsigned int>(
const std::string& aPath,
unsigned int aValue );
576template void JSON_SETTINGS::Set<unsigned long long>(
const std::string& aPath,
unsigned long long aValue );
577template void JSON_SETTINGS::Set<const char*>(
const std::string& aPath,
const char* aValue );
578template void JSON_SETTINGS::Set<std::string>(
const std::string& aPath, std::string aValue );
579template void JSON_SETTINGS::Set<nlohmann::json>(
const std::string& aPath,
nlohmann::json aValue );
580template void JSON_SETTINGS::Set<KIGFX::COLOR4D>(
const std::string& aPath,
KIGFX::COLOR4D aValue );
584 std::function<
bool()> aMigrator )
586 wxASSERT( aNewSchemaVersion > aOldSchemaVersion );
588 m_migrators[aOldSchemaVersion] = std::make_pair( aNewSchemaVersion, aMigrator );
594 int filever =
m_internals->Get<
int>(
"meta.version" );
600 wxLogTrace(
traceSettings, wxT(
"Migrator missing for %s version %d!" ),
601 typeid( *this ).name(), filever );
605 std::pair<int, std::function<bool()>> pair =
m_migrators.at( filever );
609 wxLogTrace(
traceSettings, wxT(
"Migrated %s from %d to %d" ),
typeid( *this ).name(),
610 filever, pair.first );
611 filever = pair.first;
616 wxLogTrace(
traceSettings, wxT(
"Migration failed for %s from %d to %d" ),
617 typeid( *this ).name(), filever, pair.first );
629 wxT(
"MigrateFromLegacy() not implemented for %s" ),
typeid( *this ).name() );
639 if( aObj.contains( ptr ) && aObj.at( ptr ).is_string() )
641 aTarget = aObj.at( ptr ).get<wxString>();
654 if( aObj.contains( ptr ) && aObj.at( ptr ).is_boolean() )
656 aTarget = aObj.at( ptr ).get<
bool>();
669 if( aObj.contains( ptr ) && aObj.at( ptr ).is_number_integer() )
671 aTarget = aObj.at( ptr ).get<
int>();
680 unsigned int& aTarget )
684 if( aObj.contains( ptr ) && aObj.at( ptr ).is_number_unsigned() )
686 aTarget = aObj.at( ptr ).get<
unsigned int>();
694template<
typename ValueType>
696 const std::string& aDest )
700 if( aConfig->Read( aKey, &val ) )
704 ( *m_internals )[aDest] = val;
708 wxASSERT_MSG(
false, wxT(
"Could not write value in fromLegacy!" ) );
721template bool JSON_SETTINGS::fromLegacy<int>( wxConfigBase*,
const std::string&,
722 const std::string& );
724template bool JSON_SETTINGS::fromLegacy<double>( wxConfigBase*,
const std::string&,
725 const std::string& );
727template bool JSON_SETTINGS::fromLegacy<bool>( wxConfigBase*,
const std::string&,
728 const std::string& );
732 const std::string& aDest )
736 if( aConfig->Read( aKey, &str ) )
740 ( *m_internals )[aDest] = str.ToUTF8();
744 wxASSERT_MSG(
false, wxT(
"Could not write value in fromLegacyString!" ) );
756 const std::string& aDest )
760 if( aConfig->Read( aKey, &str ) )
763 color.SetFromWxString( str );
768 ( *m_internals )[aDest] = js;
772 wxASSERT_MSG(
false, wxT(
"Could not write value in fromLegacyColor!" ) );
797 return aPtr == aSettings;
802 wxLogTrace(
traceSettings, wxT(
"Flush and release %s" ), ( *it )->GetFilename() );
803 ( *it )->SaveToFile();
815 if( std::optional<nlohmann::json> opt_json =
GetJson( aPath ) )
816 return wxString( opt_json->get<std::string>().c_str(), wxConvUTF8 );
822template<>
void JSON_SETTINGS::Set<wxString>(
const std::string& aPath, wxString aVal )
824 ( *m_internals )[aPath] = aVal.ToUTF8();
831 aJson = aString.ToUTF8();
837 aString = wxString( aJson.get<std::string>().c_str(), wxConvUTF8 );
841template<
typename ResultType>
843 ResultType aDefault )
845 ResultType ret = aDefault;
849 if( aJson.contains( aKey ) )
850 ret = aJson.at( aKey ).get<ResultType>();
861 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< 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.
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
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...
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)
std::optional< ValueType > Get(const std::string &aPath) const
Fetches a value from within the JSON document.
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