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,
86 m_internals = std::make_unique<JSON_SETTINGS_INTERNALS>();
156 wxLogTrace(
traceSettings, wxT(
"param '%s' load err" ), param->GetJsonPath().c_str() );
169 bool migrated =
false;
170 bool legacy_migrated =
false;
174 auto migrateFromLegacy =
175 [&] ( wxFileName& aPath )
179 bool backed_up =
false;
182 if( aPath.IsDirWritable() )
184 temp.AssignTempFileName( aPath.GetFullPath() );
186 if( !wxCopyFile( aPath.GetFullPath(), temp.GetFullPath() ) )
188 wxLogTrace(
traceSettings, wxT(
"%s: could not create temp file for migration" ),
200 wxConfigBase::DontCreateOnDemand();
201 auto cfg = std::make_unique<wxFileConfig>( wxT(
"" ), wxT(
"" ), aPath.GetFullPath() );
208 wxLogTrace(
traceSettings, wxT(
"%s: migrated; not all settings were found in legacy file" ),
221 if( !wxCopyFile( temp.GetFullPath(), aPath.GetFullPath() ) )
223 wxLogTrace(
traceSettings, wxT(
"migrate; copy temp file %s to %s failed" ),
224 temp.GetFullPath(), aPath.GetFullPath() );
227 if( !wxRemoveFile( temp.GetFullPath() ) )
229 wxLogTrace(
traceSettings, 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() ) = nlohmann::json::parse( fstream,
nullptr,
306 wxLogTrace(
traceSettings, wxT(
"%s: attempting migration from version %d to %d" ),
320 wxLogTrace(
traceSettings, wxT(
"%s: warning: file version %d is newer than latest (%d)" ),
330 catch( nlohmann::json::parse_error& error )
333 wxLogTrace(
traceSettings, wxT(
"Json parse error reading %s: %s" ),
path.GetFullPath(), error.what() );
334 wxLogTrace(
traceSettings, wxT(
"Attempting migration in case file is in legacy format" ) );
335 migrateFromLegacy(
path );
344 settings->LoadFromFile();
354 bool doWrite =
m_writeFile && !wxGetEnv( wxT(
"KICAD_INHIBIT_SETTINGS_WRITES" ),
nullptr );
359 if( doWrite && ( legacy_migrated || migrated ) )
363 bool savedMigrated =
SaveToFile( aDirectory,
true );
365 if( savedMigrated && legacy_migrated && !wxRemoveFile(
path.GetFullPath() ) )
367 wxLogTrace(
traceSettings, wxT(
"Warning: could not remove legacy file %s" ),
path.GetFullPath() );
369 else if( !savedMigrated )
371 wxLogTrace(
traceSettings, wxT(
"Migrated save of %s failed; leaving legacy file intact" ),
388 param->Store(
this );
390 catch(
const std::exception& e )
392 wxLogTrace(
traceSettings, wxT(
"param '%s' store err: %s" ), param->GetJsonPath().c_str(), e.what() );
409 std::map<std::string, nlohmann::json> histories;
411 for(
const std::string& candidate : { std::string(
"system.file_history" ) } )
414 histories[candidate] =
GetJson( candidate ).value();
434 if( aDirectory.empty() )
441 wxString dir( aDirectory );
448 wxT(
"File for %s doesn't exist and m_createIfMissing == false; not saving" ),
455 if( !modified && !aForce &&
path.FileExists() )
462 wxLogTrace(
traceSettings, wxT(
"%s contents still default and m_createIfDefault == false; not saving" ),
480 if( !aForce &&
path.FileExists() )
482 std::ifstream existing(
path.GetFullPath().fn_str(), std::ios::in | std::ios::binary );
486 std::string current( ( std::istreambuf_iterator<char>( existing ) ),
487 std::istreambuf_iterator<char>() );
491 if( !existing.bad() && current == payload )
493 wxLogTrace(
traceSettings, wxT(
"%s on-disk contents match payload, skipping write" ),
505 if( !
path.DirExists() && !
path.Mkdir() )
507 wxLogTrace(
traceSettings, wxT(
"Warning: could not create path %s, can't save %s" ),
511 else if( (
path.FileExists() && !
path.IsFileWritable() )
512 || ( !
path.FileExists() && !
path.IsDirWritable() ) )
524 catch( nlohmann::json::exception& error )
532 wxLogTrace(
traceSettings, wxT(
"Error: could not save %s." ) );
550 bool modified =
false;
554 wxCHECK2( settings,
continue );
556 modified |= settings->SaveToFile();
579 if( param->ClearUnknownKeys() )
583 toSave[p] = nlohmann::json::object();
589 return toSave.dump( 2 ) +
'\n';
599 std::stringstream buffer;
600 buffer << std::setw( 2 ) << *
m_internals << std::endl;
610 wxFFileInputStream fp( aPath, wxT(
"rt" ) );
611 wxStdInputStream fstream( fp );
615 *
static_cast<nlohmann::json*
>(
m_internals.get() ) = nlohmann::json::parse( fstream,
nullptr,
624 catch( nlohmann::json::parse_error& error )
626 wxLogTrace(
traceSettings, wxT(
"Json parse error reading %s: %s" ), aPath, error.what() );
639 nlohmann::json::json_pointer ptr =
m_internals->PointerFromString( aPath );
645 return std::optional<nlohmann::json>{
m_internals->at( ptr ) };
652 return std::optional<nlohmann::json>{};
656template<
typename ValueType>
659 if( std::optional<nlohmann::json> ret =
GetJson( aPath ) )
663 return ret->get<ValueType>();
711template<
typename ValueType>
714 m_internals->SetFromString( aPath, std::move( aVal ) );
724 unsigned int aValue );
726 unsigned long long aValue );
728 const char* aValue );
730 std::string aValue );
732 nlohmann::json aValue );
746 wxAuiPaneInfo aValue );
752 std::function<
bool()> aMigrator )
754 wxASSERT( aNewSchemaVersion > aOldSchemaVersion );
756 m_migrators[aOldSchemaVersion] = std::make_pair( aNewSchemaVersion, aMigrator );
762 int filever =
m_internals->Get<
int>(
"meta.version" );
770 wxLogTrace(
traceSettings, wxT(
"Migrator missing for %s version %d!" ),
typeid( *this ).name(),
775 std::pair<int, std::function<bool()>> pair =
m_migrators.at( filever );
779 wxLogTrace(
traceSettings, wxT(
"Migrated %s from %d to %d" ),
typeid( *this ).name(), filever,
781 filever = pair.first;
786 wxLogTrace(
traceSettings, wxT(
"Migration failed for %s from %d to %d" ),
typeid( *this ).name(),
787 filever, pair.first );
798 wxLogTrace(
traceSettings, wxT(
"MigrateFromLegacy() not implemented for %s" ),
typeid( *this ).name() );
807 if( aObj.contains( ptr ) && aObj.at( ptr ).is_string() )
809 aTarget = aObj.at( ptr ).get<wxString>();
821 if( aObj.contains( ptr ) && aObj.at( ptr ).is_boolean() )
823 aTarget = aObj.at( ptr ).get<
bool>();
835 if( aObj.contains( ptr ) && aObj.at( ptr ).is_number_integer() )
837 aTarget = aObj.at( ptr ).get<
int>();
849 if( aObj.contains( ptr ) && aObj.at( ptr ).is_number_unsigned() )
851 aTarget = aObj.at( ptr ).get<
unsigned int>();
859template<
typename ValueType>
864 if( aConfig->Read( aKey, &val ) )
868 ( *m_internals )[aDest] = val;
872 wxASSERT_MSG(
false, wxT(
"Could not write value in fromLegacy!" ) );
899 if( aConfig->Read( aKey, &str ) )
903 ( *m_internals )[aDest] = str.ToUTF8();
907 wxASSERT_MSG(
false, wxT(
"Could not write value in fromLegacyString!" ) );
922 if( aConfig->Read( aKey, &str ) )
929 nlohmann::json js = nlohmann::json::array( { color.
r, color.
g, color.
b, color.
a } );
930 ( *m_internals )[aDest] = std::move( js );
934 wxASSERT_MSG(
false, wxT(
"Could not write value in fromLegacyColor!" ) );
960 return aPtr == aSettings;
965 wxLogTrace(
traceSettings, wxT(
"Flush and release %s" ), ( *it )->GetFilename() );
983 if( std::optional<nlohmann::json> opt_json =
GetJson( aPath ) )
984 return wxString( opt_json->get<std::string>().c_str(), wxConvUTF8 );
993 ( *m_internals )[aPath] = aVal.ToUTF8();
997template<
typename ResultType>
999 ResultType aDefault )
1001 ResultType ret = std::move( aDefault );
1005 if( aJson.contains( aKey ) )
1006 ret = aJson.at( aKey ).get<ResultType>();
1018 std::string 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
const nlohmann::json & originalJson() const
virtual std::map< std::string, nlohmann::json > GetFileHistories()
bool flushToStore()
Flushes nested settings and parameters into the JSON store.
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.
bool m_lastSaveFailed
True if the most recent SaveToFile() had changes to write and could not write them.
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::string formatFileContents()
Overlays the current store on the original file contents.
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.