37#include <wx/aui/framemanager.h>
40#include <wx/fileconf.h>
41#include <wx/filename.h>
44#include <wx/stdstream.h>
45#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,
84 m_internals = std::make_unique<JSON_SETTINGS_INTERNALS>();
92 wxLogTrace(
traceSettings, wxT(
"Error: Could not create filename field for %s" ),
149 wxLogTrace(
traceSettings, wxT(
"param '%s' load err" ), param->GetJsonPath().c_str() );
162 bool migrated =
false;
163 bool legacy_migrated =
false;
167 auto migrateFromLegacy =
168 [&] ( wxFileName& aPath )
172 bool backed_up =
false;
175 if( aPath.IsDirWritable() )
177 temp.AssignTempFileName( aPath.GetFullPath() );
179 if( !wxCopyFile( aPath.GetFullPath(), temp.GetFullPath() ) )
182 wxT(
"%s: could not create temp file for migration" ),
194 wxConfigBase::DontCreateOnDemand();
195 auto cfg = std::make_unique<wxFileConfig>( wxT(
"" ), wxT(
"" ),
196 aPath.GetFullPath() );
204 wxT(
"%s: migrated; not all settings were found in legacy file" ),
210 wxLogTrace(
traceSettings, wxT(
"%s: migrated from legacy format" ),
218 if( !wxCopyFile( temp.GetFullPath(), aPath.GetFullPath() ) )
221 wxT(
"migrate; copy temp file %s to %s failed" ),
223 aPath.GetFullPath() );
226 if( !wxRemoveFile( temp.GetFullPath() ) )
229 wxT(
"migrate; failed to remove temp file %s" ),
230 temp.GetFullPath() );
235 legacy_migrated =
true;
240 if( aDirectory.empty() )
257 migrateFromLegacy(
path );
265 migrateFromLegacy(
path );
274 if( !
path.IsFileWritable() )
279 wxFFileInputStream fp(
path.GetFullPath(), wxT(
"rt" ) );
280 wxStdInputStream fstream( fp );
284 *
static_cast<nlohmann::json*
>(
m_internals.get() ) =
285 nlohmann::json::parse( fstream,
nullptr,
301 wxLogTrace(
traceSettings, wxT(
"%s: file version could not be read!" ),
308 wxLogTrace(
traceSettings, wxT(
"%s: attempting migration from version "
327 wxT(
"%s: warning: file version %d is newer than latest (%d)" ),
336 wxLogTrace(
traceSettings, wxT(
"%s exists but can't be opened for read" ),
340 catch( nlohmann::json::parse_error& error )
343 wxLogTrace(
traceSettings, wxT(
"Json parse error reading %s: %s" ),
344 path.GetFullPath(), error.what() );
345 wxLogTrace(
traceSettings, wxT(
"Attempting migration in case file is in legacy "
347 migrateFromLegacy(
path );
356 settings->LoadFromFile();
358 wxLogTrace(
traceSettings, wxT(
"Loaded <%s> with schema %d" ),
367 if(
m_writeFile && ( legacy_migrated || migrated ) )
371 bool savedMigrated =
SaveToFile( aDirectory,
true );
373 if( savedMigrated && legacy_migrated && !wxRemoveFile(
path.GetFullPath() ) )
375 wxLogTrace(
traceSettings, wxT(
"Warning: could not remove legacy file %s" ),
376 path.GetFullPath() );
378 else if( !savedMigrated )
381 wxT(
"Migrated save of %s failed; leaving legacy file intact" ),
398 param->Store(
this );
400 catch(
const std::exception& e )
402 wxLogTrace(
traceSettings, wxT(
"param '%s' store err: %s" ),
403 param->GetJsonPath().c_str(), e.what() );
420 std::map<std::string, nlohmann::json> histories;
422 for(
const std::string& candidate : { std::string(
"system.file_history" ) } )
425 histories[candidate] =
GetJson( candidate ).value();
443 if( aDirectory.empty() )
450 wxString dir( aDirectory );
457 wxT(
"File for %s doesn't exist and m_createIfMissing == false; not saving" ),
463 if( !
path.DirExists() && !
path.Mkdir() )
465 wxLogTrace(
traceSettings, wxT(
"Warning: could not create path %s, can't save %s" ),
470 if( (
path.FileExists() && !
path.IsFileWritable() ) ||
471 ( !
path.FileExists() && !
path.IsDirWritable() ) )
473 wxLogTrace(
traceSettings, wxT(
"File for %s is read-only; not saving" ),
478 bool modified =
false;
482 wxCHECK2( settings,
continue );
484 modified |= settings->SaveToFile();
489 if( !modified && !aForce &&
path.FileExists() )
491 wxLogTrace(
traceSettings, wxT(
"%s contents not modified, skipping save" ),
498 wxT(
"%s contents still default and m_createIfDefault == false; not saving" ),
513 if( param->ClearUnknownKeys() )
517 toSave[p] = nlohmann::json( {} );
525 std::stringstream buffer;
526 buffer << std::setw( 2 ) << toSave << std::endl;
528 std::string payload = buffer.str();
539 catch( nlohmann::json::exception& error )
541 wxLogTrace(
traceSettings, wxT(
"Catch error: could not save %s. Json error %s" ),
547 wxLogTrace(
traceSettings, wxT(
"Error: could not save %s." ) );
564 std::stringstream buffer;
565 buffer << std::setw( 2 ) << *
m_internals << std::endl;
575 wxFFileInputStream fp( aPath, wxT(
"rt" ) );
576 wxStdInputStream fstream( fp );
580 *
static_cast<nlohmann::json*
>(
m_internals.get() ) =
581 nlohmann::json::parse( fstream,
nullptr,
590 catch( nlohmann::json::parse_error& error )
592 wxLogTrace(
traceSettings, wxT(
"Json parse error reading %s: %s" ), aPath, error.what() );
605 nlohmann::json::json_pointer ptr =
m_internals->PointerFromString( aPath );
611 return std::optional<nlohmann::json>{
m_internals->at( ptr ) };
618 return std::optional<nlohmann::json>{};
622template<
typename ValueType>
625 if( std::optional<nlohmann::json> ret =
GetJson( aPath ) )
629 return ret->get<ValueType>();
677template<
typename ValueType>
680 m_internals->SetFromString( aPath, std::move( aVal ) );
690 unsigned int aValue );
692 unsigned long long aValue );
694 const char* aValue );
696 std::string aValue );
698 nlohmann::json aValue );
712 wxAuiPaneInfo aValue );
718 std::function<
bool()> aMigrator )
720 wxASSERT( aNewSchemaVersion > aOldSchemaVersion );
722 m_migrators[aOldSchemaVersion] = std::make_pair( aNewSchemaVersion, aMigrator );
728 int filever =
m_internals->Get<
int>(
"meta.version" );
736 wxLogTrace(
traceSettings, wxT(
"Migrator missing for %s version %d!" ),
737 typeid( *this ).name(),
742 std::pair<int, std::function<bool()>> pair =
m_migrators.at( filever );
746 wxLogTrace(
traceSettings, wxT(
"Migrated %s from %d to %d" ),
747 typeid( *this ).name(),
750 filever = pair.first;
755 wxLogTrace(
traceSettings, wxT(
"Migration failed for %s from %d to %d" ),
756 typeid( *this ).name(),
769 wxLogTrace(
traceSettings, wxT(
"MigrateFromLegacy() not implemented for %s" ),
770 typeid( *this ).name() );
780 if( aObj.contains( ptr ) && aObj.at( ptr ).is_string() )
782 aTarget = aObj.at( ptr ).get<wxString>();
795 if( aObj.contains( ptr ) && aObj.at( ptr ).is_boolean() )
797 aTarget = aObj.at( ptr ).get<
bool>();
810 if( aObj.contains( ptr ) && aObj.at( ptr ).is_number_integer() )
812 aTarget = aObj.at( ptr ).get<
int>();
821 unsigned int& aTarget )
825 if( aObj.contains( ptr ) && aObj.at( ptr ).is_number_unsigned() )
827 aTarget = aObj.at( ptr ).get<
unsigned int>();
835template<
typename ValueType>
837 const std::string& aDest )
841 if( aConfig->Read( aKey, &val ) )
845 ( *m_internals )[aDest] = val;
849 wxASSERT_MSG(
false, wxT(
"Could not write value in fromLegacy!" ) );
864 const std::string& );
868 const std::string& );
872 const std::string& );
876 const std::string& aDest )
880 if( aConfig->Read( aKey, &str ) )
884 ( *m_internals )[aDest] = str.ToUTF8();
888 wxASSERT_MSG(
false, wxT(
"Could not write value in fromLegacyString!" ) );
900 const std::string& aDest )
904 if( aConfig->Read( aKey, &str ) )
911 nlohmann::json js = nlohmann::json::array( { color.
r, color.
g, color.
b, color.
a } );
912 ( *m_internals )[aDest] = std::move( js );
916 wxASSERT_MSG(
false, wxT(
"Could not write value in fromLegacyColor!" ) );
942 return aPtr == aSettings;
947 wxLogTrace(
traceSettings, wxT(
"Flush and release %s" ), ( *it )->GetFilename() );
960 if( std::optional<nlohmann::json> opt_json =
GetJson( aPath ) )
961 return wxString( opt_json->get<std::string>().c_str(), wxConvUTF8 );
970 ( *m_internals )[aPath] = aVal.ToUTF8();
974template<
typename ResultType>
976 ResultType aDefault )
978 ResultType ret = std::move( aDefault );
982 if( aJson.contains( aKey ) )
983 ret = aJson.at( aKey ).get<ResultType>();
995 const std::string& aKey,
996 std::string aDefault );
1001 const std::string& aKey,
bool aDefault );
static nlohmann::json::json_pointer PointerFromString(std::string aPath)
Builds a JSON pointer based on a given string.
bool fromLegacyString(wxConfigBase *aConfig, const std::string &aKey, const std::string &aDest)
Translates a legacy wxConfig string value to a given JSON pointer value.
bool fromLegacy(wxConfigBase *aConfig, const std::string &aKey, const std::string &aDest)
Translates a legacy wxConfig value to a given JSON pointer value.
virtual wxString getFileExt() const
virtual std::map< std::string, nlohmann::json > GetFileHistories()
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...
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....
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)
bool m_isFutureFormat
Set to true if this settings is loaded from a file with a newer schema version than is known.
std::vector< NESTED_SETTINGS * > m_nested_settings
Nested settings files that live inside this one, if any.
bool m_modified
True if the JSON data store has been written to since the last file write.
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::optional< ValueType > Get(const std::string &aPath) const
Fetches a value from within the JSON document.
std::vector< PARAM_BASE * > m_params
The list of parameters (owned by this object)
virtual bool MigrateFromLegacy(wxConfigBase *aLegacyConfig)
Migrates from wxConfig to JSON-based configuration.
const std::string FormatAsString()
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()
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.
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
friend class NESTED_SETTINGS
SETTINGS_LOC m_location
The location of this settings file (.
virtual bool SaveToFile(const wxString &aDirectory="", bool aForce=false)
Calls Store() and then writes the contents of the JSON document to a file.
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.
bool SetFromWxString(const wxString &aColorString)
Set color values by parsing a string using wxColour::Set().
Instantiate the current locale within a scope in which you are expecting exceptions to be thrown.
void SetParent(JSON_SETTINGS *aParent, bool aLoadFromFile=true)
std::vector< FAB_LAYER_COLOR > dummy
Common grid settings, available to every frame.