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,
85 m_internals = std::make_unique<JSON_SETTINGS_INTERNALS>();
93 wxLogTrace(
traceSettings, wxT(
"Error: Could not create filename field for %s" ),
157 wxLogTrace(
traceSettings, wxT(
"param '%s' load err" ), param->GetJsonPath().c_str() );
170 bool migrated =
false;
171 bool legacy_migrated =
false;
175 auto migrateFromLegacy =
176 [&] ( wxFileName& aPath )
180 bool backed_up =
false;
183 if( aPath.IsDirWritable() )
185 temp.AssignTempFileName( aPath.GetFullPath() );
187 if( !wxCopyFile( aPath.GetFullPath(), temp.GetFullPath() ) )
190 wxT(
"%s: could not create temp file for migration" ),
202 wxConfigBase::DontCreateOnDemand();
203 auto cfg = std::make_unique<wxFileConfig>( wxT(
"" ), wxT(
"" ),
204 aPath.GetFullPath() );
212 wxT(
"%s: migrated; not all settings were found in legacy file" ),
218 wxLogTrace(
traceSettings, wxT(
"%s: migrated from legacy format" ),
226 if( !wxCopyFile( temp.GetFullPath(), aPath.GetFullPath() ) )
229 wxT(
"migrate; copy temp file %s to %s failed" ),
231 aPath.GetFullPath() );
234 if( !wxRemoveFile( temp.GetFullPath() ) )
237 wxT(
"migrate; failed to remove temp file %s" ),
238 temp.GetFullPath() );
243 legacy_migrated =
true;
248 if( aDirectory.empty() )
265 migrateFromLegacy(
path );
273 migrateFromLegacy(
path );
282 if( !
path.IsFileWritable() )
287 wxFFileInputStream fp(
path.GetFullPath(), wxT(
"rt" ) );
288 wxStdInputStream fstream( fp );
292 *
static_cast<nlohmann::json*
>(
m_internals.get() ) =
293 nlohmann::json::parse( fstream,
nullptr,
309 wxLogTrace(
traceSettings, wxT(
"%s: file version could not be read!" ),
316 wxLogTrace(
traceSettings, wxT(
"%s: attempting migration from version "
335 wxT(
"%s: warning: file version %d is newer than latest (%d)" ),
344 wxLogTrace(
traceSettings, wxT(
"%s exists but can't be opened for read" ),
348 catch( nlohmann::json::parse_error& error )
351 wxLogTrace(
traceSettings, wxT(
"Json parse error reading %s: %s" ),
352 path.GetFullPath(), error.what() );
353 wxLogTrace(
traceSettings, wxT(
"Attempting migration in case file is in legacy "
355 migrateFromLegacy(
path );
364 settings->LoadFromFile();
366 wxLogTrace(
traceSettings, wxT(
"Loaded <%s> with schema %d" ),
377 bool doWrite =
m_writeFile && !wxGetEnv( wxT(
"KICAD_INHIBIT_SETTINGS_WRITES" ),
nullptr );
382 if( doWrite && ( legacy_migrated || migrated ) )
386 bool savedMigrated =
SaveToFile( aDirectory,
true );
388 if( savedMigrated && legacy_migrated && !wxRemoveFile(
path.GetFullPath() ) )
390 wxLogTrace(
traceSettings, wxT(
"Warning: could not remove legacy file %s" ),
391 path.GetFullPath() );
393 else if( !savedMigrated )
396 wxT(
"Migrated save of %s failed; leaving legacy file intact" ),
413 param->Store(
this );
415 catch(
const std::exception& e )
417 wxLogTrace(
traceSettings, wxT(
"param '%s' store err: %s" ),
418 param->GetJsonPath().c_str(), e.what() );
435 std::map<std::string, nlohmann::json> histories;
437 for(
const std::string& candidate : { std::string(
"system.file_history" ) } )
440 histories[candidate] =
GetJson( candidate ).value();
458 if( aDirectory.empty() )
465 wxString dir( aDirectory );
472 wxT(
"File for %s doesn't exist and m_createIfMissing == false; not saving" ),
478 if( !
path.DirExists() && !
path.Mkdir() )
480 wxLogTrace(
traceSettings, wxT(
"Warning: could not create path %s, can't save %s" ),
485 if( (
path.FileExists() && !
path.IsFileWritable() ) ||
486 ( !
path.FileExists() && !
path.IsDirWritable() ) )
488 wxLogTrace(
traceSettings, wxT(
"File for %s is read-only; not saving" ),
493 bool modified =
false;
497 wxCHECK2( settings,
continue );
499 modified |= settings->SaveToFile();
504 if( !modified && !aForce &&
path.FileExists() )
506 wxLogTrace(
traceSettings, wxT(
"%s contents not modified, skipping save" ),
513 wxT(
"%s contents still default and m_createIfDefault == false; not saving" ),
528 if( param->ClearUnknownKeys() )
532 toSave[p] = nlohmann::json::object();
540 std::stringstream buffer;
541 buffer << std::setw( 2 ) << toSave << std::endl;
543 std::string payload = buffer.str();
550 if( !aForce &&
path.FileExists() )
552 std::ifstream existing(
path.GetFullPath().fn_str(), std::ios::in | std::ios::binary );
556 std::string current( ( std::istreambuf_iterator<char>( existing ) ),
557 std::istreambuf_iterator<char>() );
561 if( !existing.bad() && current == payload )
564 wxT(
"%s on-disk contents match payload, skipping write" ),
582 catch( nlohmann::json::exception& error )
584 wxLogTrace(
traceSettings, wxT(
"Catch error: could not save %s. Json error %s" ),
590 wxLogTrace(
traceSettings, wxT(
"Error: could not save %s." ) );
610 std::stringstream buffer;
611 buffer << std::setw( 2 ) << *
m_internals << std::endl;
621 wxFFileInputStream fp( aPath, wxT(
"rt" ) );
622 wxStdInputStream fstream( fp );
626 *
static_cast<nlohmann::json*
>(
m_internals.get() ) =
627 nlohmann::json::parse( fstream,
nullptr,
636 catch( nlohmann::json::parse_error& error )
638 wxLogTrace(
traceSettings, wxT(
"Json parse error reading %s: %s" ), aPath, error.what() );
651 nlohmann::json::json_pointer ptr =
m_internals->PointerFromString( aPath );
657 return std::optional<nlohmann::json>{
m_internals->at( ptr ) };
664 return std::optional<nlohmann::json>{};
668template<
typename ValueType>
671 if( std::optional<nlohmann::json> ret =
GetJson( aPath ) )
675 return ret->get<ValueType>();
723template<
typename ValueType>
726 m_internals->SetFromString( aPath, std::move( aVal ) );
736 unsigned int aValue );
738 unsigned long long aValue );
740 const char* aValue );
742 std::string aValue );
744 nlohmann::json aValue );
758 wxAuiPaneInfo aValue );
764 std::function<
bool()> aMigrator )
766 wxASSERT( aNewSchemaVersion > aOldSchemaVersion );
768 m_migrators[aOldSchemaVersion] = std::make_pair( aNewSchemaVersion, aMigrator );
774 int filever =
m_internals->Get<
int>(
"meta.version" );
782 wxLogTrace(
traceSettings, wxT(
"Migrator missing for %s version %d!" ),
783 typeid( *this ).name(),
788 std::pair<int, std::function<bool()>> pair =
m_migrators.at( filever );
792 wxLogTrace(
traceSettings, wxT(
"Migrated %s from %d to %d" ),
793 typeid( *this ).name(),
796 filever = pair.first;
801 wxLogTrace(
traceSettings, wxT(
"Migration failed for %s from %d to %d" ),
802 typeid( *this ).name(),
815 wxLogTrace(
traceSettings, wxT(
"MigrateFromLegacy() not implemented for %s" ),
816 typeid( *this ).name() );
826 if( aObj.contains( ptr ) && aObj.at( ptr ).is_string() )
828 aTarget = aObj.at( ptr ).get<wxString>();
841 if( aObj.contains( ptr ) && aObj.at( ptr ).is_boolean() )
843 aTarget = aObj.at( ptr ).get<
bool>();
856 if( aObj.contains( ptr ) && aObj.at( ptr ).is_number_integer() )
858 aTarget = aObj.at( ptr ).get<
int>();
867 unsigned int& aTarget )
871 if( aObj.contains( ptr ) && aObj.at( ptr ).is_number_unsigned() )
873 aTarget = aObj.at( ptr ).get<
unsigned int>();
881template<
typename ValueType>
883 const std::string& aDest )
887 if( aConfig->Read( aKey, &val ) )
891 ( *m_internals )[aDest] = val;
895 wxASSERT_MSG(
false, wxT(
"Could not write value in fromLegacy!" ) );
910 const std::string& );
914 const std::string& );
918 const std::string& );
922 const std::string& aDest )
926 if( aConfig->Read( aKey, &str ) )
930 ( *m_internals )[aDest] = str.ToUTF8();
934 wxASSERT_MSG(
false, wxT(
"Could not write value in fromLegacyString!" ) );
946 const std::string& aDest )
950 if( aConfig->Read( aKey, &str ) )
957 nlohmann::json js = nlohmann::json::array( { color.
r, color.
g, color.
b, color.
a } );
958 ( *m_internals )[aDest] = std::move( js );
962 wxASSERT_MSG(
false, wxT(
"Could not write value in fromLegacyColor!" ) );
988 return aPtr == aSettings;
993 wxLogTrace(
traceSettings, wxT(
"Flush and release %s" ), ( *it )->GetFilename() );
1011 if( std::optional<nlohmann::json> opt_json =
GetJson( aPath ) )
1012 return wxString( opt_json->get<std::string>().c_str(), wxConvUTF8 );
1014 return std::nullopt;
1021 ( *m_internals )[aPath] = aVal.ToUTF8();
1025template<
typename ResultType>
1027 ResultType aDefault )
1029 ResultType ret = std::move( aDefault );
1033 if( aJson.contains( aKey ) )
1034 ret = aJson.at( aKey ).get<ResultType>();
1046 const std::string& aKey,
1047 std::string aDefault );
1052 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 m_fileSynced
True once the store has been synchronized with an on-disk file (loaded or saved)
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.