354 lines
13 KiB
C++
354 lines
13 KiB
C++
/*
|
|
* This program source code file is part of KiCad, a free EDA CAD application.
|
|
*
|
|
* Copyright (C) 2020 Jon Evans <jon@craftyjon.com>
|
|
* Copyright (C) 2020 KiCad Developers, see AUTHORS.txt for contributors.
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify it
|
|
* under the terms of the GNU General Public License as published by the
|
|
* Free Software Foundation, either version 3 of the License, or (at your
|
|
* option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful, but
|
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License along
|
|
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#ifndef _JSON_SETTINGS_H
|
|
#define _JSON_SETTINGS_H
|
|
|
|
#include <core/wx_stl_compat.h>
|
|
|
|
#include <utility>
|
|
#include <wx/string.h>
|
|
|
|
#include <functional>
|
|
#include <optional>
|
|
#include <nlohmann/json_fwd.hpp>
|
|
|
|
class wxConfigBase;
|
|
class NESTED_SETTINGS;
|
|
class PARAM_BASE;
|
|
class SETTINGS_MANAGER;
|
|
|
|
/**
|
|
* Flag to enable debug output of settings operations and management.
|
|
*
|
|
* Use "KICAD_SETTINGS" to enable.
|
|
*
|
|
* @ingroup trace_env_vars
|
|
*/
|
|
extern const wxChar* const traceSettings;
|
|
|
|
enum class SETTINGS_LOC {
|
|
USER, ///< The main config directory (e.g. ~/.config/kicad/)
|
|
PROJECT, ///< The settings directory inside a project folder
|
|
COLORS, ///< The color scheme directory (e.g. ~/.config/kicad/colors/)
|
|
NESTED, ///< Not stored in a file, but inside another JSON_SETTINGS
|
|
NONE, ///< No directory prepended, full path in filename (used for PROJECT_FILE)
|
|
};
|
|
|
|
|
|
/// pimpl to allow hiding json.hpp
|
|
class JSON_SETTINGS_INTERNALS;
|
|
|
|
class JSON_SETTINGS
|
|
{
|
|
public:
|
|
friend class NESTED_SETTINGS;
|
|
|
|
JSON_SETTINGS( const wxString& aFilename, SETTINGS_LOC aLocation, int aSchemaVersion ) :
|
|
JSON_SETTINGS( aFilename, aLocation, aSchemaVersion, true, true, true )
|
|
{}
|
|
|
|
JSON_SETTINGS( const wxString& aFilename, SETTINGS_LOC aLocation, int aSchemaVersion,
|
|
bool aCreateIfMissing, bool aCreateIfDefault, bool aWriteFile );
|
|
|
|
virtual ~JSON_SETTINGS();
|
|
|
|
wxString GetFilename() const { return m_filename; }
|
|
|
|
wxString GetFullFilename() const;
|
|
|
|
void SetFilename( const wxString& aFilename ) { m_filename = aFilename; }
|
|
|
|
void SetLocation( SETTINGS_LOC aLocation ) { m_location = aLocation; }
|
|
SETTINGS_LOC GetLocation() const { return m_location; }
|
|
|
|
void SetLegacyFilename( const wxString& aFilename ) { m_legacy_filename = aFilename; }
|
|
|
|
bool IsReadOnly() const { return !m_writeFile; }
|
|
void SetReadOnly( bool aReadOnly ) { m_writeFile = !aReadOnly; }
|
|
|
|
/**
|
|
* Wrappers for the underlying JSON API so that most consumers don't need json.hpp
|
|
* All of these functions take a string that is passed to PointerFromString internally.
|
|
*/
|
|
|
|
nlohmann::json& At( const std::string& aPath );
|
|
bool Contains( const std::string& aPath ) const;
|
|
|
|
JSON_SETTINGS_INTERNALS* Internals();
|
|
|
|
/**
|
|
* Updates the parameters of this object based on the current JSON document contents
|
|
*/
|
|
virtual void Load();
|
|
|
|
/**
|
|
* Stores the current parameters into the JSON document represented by this object
|
|
* Note: this doesn't do any writing to disk; that's handled by SETTINGS_MANAGER
|
|
* @return true if any part of the JSON document was updated
|
|
*/
|
|
virtual bool Store();
|
|
|
|
/**
|
|
* Loads the backing file from disk and then calls Load()
|
|
* @param aDirectory is the path to the file
|
|
* @return true if the file was found on disk and loaded or migrated
|
|
*/
|
|
virtual bool LoadFromFile( const wxString& aDirectory = "" );
|
|
|
|
/**
|
|
* Calls Store() and then writes the contents of the JSON document to a file
|
|
* @param aDirectory is the directory to save to, including trailing separator
|
|
* @return true if the file was saved
|
|
*/
|
|
virtual bool SaveToFile( const wxString& aDirectory = "", bool aForce = false );
|
|
|
|
/**
|
|
* Resets all parameters to default values. Does NOT write to file or update underlying JSON.
|
|
*/
|
|
void ResetToDefaults();
|
|
|
|
/**
|
|
* Fetches a JSON object that is a subset of this JSON_SETTINGS object, using a path of the
|
|
* form "key1.key2.key3" to refer to nested objects.
|
|
* @param aPath is a string containing one or more keys separated by '.'
|
|
* @return a JSON object from within this one
|
|
*/
|
|
std::optional<nlohmann::json> GetJson( const std::string& aPath ) const;
|
|
|
|
/**
|
|
* Fetches a value from within the JSON document.
|
|
* Will return an empty optional if the value is not found or a mismatching type.
|
|
* @tparam ValueType is the type to cast to
|
|
* @param aPath is the path within the document to retrieve
|
|
* @return a value from within this document
|
|
*/
|
|
template<typename ValueType>
|
|
std::optional<ValueType> Get( const std::string& aPath ) const;
|
|
|
|
/**
|
|
* Stores a value into the JSON document
|
|
* Will throw an exception if ValueType isn't something that the library can handle
|
|
* @tparam ValueType is the type to store
|
|
* @param aPath is a path to store in the form "key1.key2.key3"
|
|
* @param aVal is the value to store
|
|
*/
|
|
template<typename ValueType>
|
|
void Set( const std::string& aPath, ValueType aVal );
|
|
|
|
/**
|
|
* Migrates the schema of this settings from the version in the file to the latest version
|
|
*
|
|
* Schema migration doesn't need to be used every time a setting is added! This is intended
|
|
* to be more of an "escape hatch" in the event that we decide to move settings around or make
|
|
* other changes to a settings file format that can't simply be handled by loading a new default
|
|
*
|
|
* @return true if migration was successful
|
|
*/
|
|
bool Migrate();
|
|
|
|
/**
|
|
* Migrates from wxConfig to JSON-based configuration. Should be implemented by any subclasses
|
|
* of JSON_SETTINGS that map to a legacy (wxConfig-based) config file.
|
|
* @param aLegacyConfig is a wxConfigBase holding a loaded configuration to migrate
|
|
* @return true if migration was successful
|
|
*/
|
|
virtual bool MigrateFromLegacy( wxConfigBase* aLegacyConfig );
|
|
|
|
/**
|
|
* Transfers ownership of a given NESTED_SETTINGS to this object.
|
|
* Can be used to construct a NESTED_SETTINGS without the parent object needing to know about
|
|
* the implementation of the nested object;
|
|
*
|
|
* @param aSettings is the settings object to take ownership of
|
|
* @param aTarget is a pointer to update to the passed in settings
|
|
*/
|
|
void AddNestedSettings( NESTED_SETTINGS* aSettings );
|
|
|
|
/**
|
|
* Saves and frees a nested settings object, if it exists within this one
|
|
* @param aSettings is a pointer to a NESTED_SETTINGS that has already been added to this one
|
|
*/
|
|
void ReleaseNestedSettings( NESTED_SETTINGS* aSettings );
|
|
|
|
void SetManager( SETTINGS_MANAGER* aManager )
|
|
{
|
|
m_manager = aManager;
|
|
}
|
|
|
|
/**
|
|
* Sets the given string if the given key/path is present
|
|
* @param aObj is the source object
|
|
* @param aTarget is the storage destination
|
|
* @return True if set, false if not
|
|
*/
|
|
static bool SetIfPresent( const nlohmann::json& aObj, const std::string& aPath,
|
|
wxString& aTarget );
|
|
|
|
/**
|
|
* Sets the given bool if the given key/path is present
|
|
* @param aObj is the source object
|
|
* @param aTarget is the storage destination
|
|
* @return True if set, false if not
|
|
*/
|
|
static bool SetIfPresent( const nlohmann::json& aObj, const std::string& aPath, bool& aTarget );
|
|
|
|
/**
|
|
* Sets the given int if the given key/path is present
|
|
* @param aObj is the source object
|
|
* @param aTarget is the storage destination
|
|
* @return True if set, false if not
|
|
*/
|
|
static bool SetIfPresent( const nlohmann::json& aObj, const std::string& aPath, int& aTarget );
|
|
|
|
/**
|
|
* Sets the given unsigned int if the given key/path is present
|
|
* @param aObj is the source object
|
|
* @param aTarget is the storage destination
|
|
* @return True if set, false if not
|
|
*/
|
|
static bool SetIfPresent( const nlohmann::json& aObj, const std::string& aPath ,
|
|
unsigned int& aTarget );
|
|
|
|
const std::string FormatAsString();
|
|
|
|
bool LoadFromRawFile( const wxString& aPath );
|
|
|
|
protected:
|
|
|
|
/**
|
|
* Registers a migration from one schema version to another. If the schema version in the file
|
|
* loaded from disk is less than the schema version of the JSON_SETTINGS class, migration
|
|
* functions will be called one after the other until the data is up-to-date.
|
|
* @param aOldSchemaVersion is the starting schema version for this migration
|
|
* @param aNewSchemaVersion is the ending schema version for this migration
|
|
* @param aMigrator is a function that performs the migration and returns true if successful
|
|
*/
|
|
void registerMigration( int aOldSchemaVersion, int aNewSchemaVersion,
|
|
std::function<bool(void)> aMigrator );
|
|
|
|
/**
|
|
* Translates a legacy wxConfig value to a given JSON pointer value
|
|
* @tparam ValueType is the basic type of the value
|
|
* @param aConfig is the legacy config to read from
|
|
* @param aKey is the key (within the current path) to read
|
|
* @param aDest is a string that will form a JSON pointer (key1.key2.key3) to write to
|
|
*/
|
|
template<typename ValueType>
|
|
bool fromLegacy( wxConfigBase* aConfig, const std::string& aKey, const std::string& aDest );
|
|
|
|
/**
|
|
* Translates a legacy wxConfig string value to a given JSON pointer value
|
|
* @param aConfig is the legacy config to read from
|
|
* @param aKey is the key (within the current path) to read
|
|
* @param aDest is a string that will form a JSON pointer (key1.key2.key3) to write to
|
|
*/
|
|
bool fromLegacyString( 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
|
|
* @param aConfig is the legacy config to read from
|
|
* @param aKey is the key (within the current path) to read
|
|
* @param aDest is a string that will form a JSON pointer (key1.key2.key3) to write to
|
|
*/
|
|
bool fromLegacyColor( wxConfigBase* aConfig, const std::string& aKey,
|
|
const std::string& aDest );
|
|
|
|
virtual wxString getFileExt() const
|
|
{
|
|
return wxT( "json" );
|
|
}
|
|
|
|
virtual wxString getLegacyFileExt() const
|
|
{
|
|
return wxEmptyString;
|
|
}
|
|
|
|
/**
|
|
* Helper to retrieve a value from a JSON object (dictionary) as a certain result type
|
|
* @tparam ResultType is the type of the retrieved value.
|
|
* @param aJson is the object to act on .
|
|
* @param aKey is the object key to retrieve the value for.
|
|
* @return the result, or aDefault if aKey is not found.
|
|
*/
|
|
template<typename ResultType>
|
|
static ResultType fetchOrDefault( const nlohmann::json& aJson, const std::string& aKey,
|
|
ResultType aDefault = ResultType() );
|
|
|
|
/// The filename (not including path) of this settings file (inicode)
|
|
wxString m_filename;
|
|
|
|
/// The filename of the wxConfig legacy file (if different from m_filename)
|
|
wxString m_legacy_filename;
|
|
|
|
/// The location of this settings file (@see SETTINGS_LOC)
|
|
SETTINGS_LOC m_location;
|
|
|
|
/// The list of parameters (owned by this object)
|
|
std::vector<PARAM_BASE*> m_params;
|
|
|
|
/// Nested settings files that live inside this one, if any
|
|
std::vector<NESTED_SETTINGS*> m_nested_settings;
|
|
|
|
/// Whether or not the backing store file should be created it if doesn't exist
|
|
bool m_createIfMissing;
|
|
|
|
/**
|
|
* Whether or not the backing store file should be created if all parameters are still
|
|
* at their default values. Ignored if m_createIfMissing is false or m_writeFile is false.
|
|
*/
|
|
bool m_createIfDefault;
|
|
|
|
/// Whether or not the backing store file should be written
|
|
bool m_writeFile;
|
|
|
|
/// Whether or not to delete legacy file after migration
|
|
bool m_deleteLegacyAfterMigration;
|
|
|
|
/// Whether or not to set parameters to their default value if missing from JSON on Load()
|
|
bool m_resetParamsIfMissing;
|
|
|
|
/// Version of this settings schema.
|
|
int m_schemaVersion;
|
|
|
|
/// A pointer to the settings manager managing this file (may be null)
|
|
SETTINGS_MANAGER* m_manager;
|
|
|
|
/// A map of starting schema version to a pair of <ending version, migrator function>
|
|
std::map<int, std::pair<int, std::function<bool()>>> m_migrators;
|
|
|
|
std::unique_ptr<JSON_SETTINGS_INTERNALS> m_internals;
|
|
};
|
|
|
|
// Specializations to allow conversion between wxString and std::string via JSON_SETTINGS API
|
|
|
|
template<> std::optional<wxString> JSON_SETTINGS::Get( const std::string& aPath ) const;
|
|
|
|
template<> void JSON_SETTINGS::Set<wxString>( const std::string& aPath, wxString aVal );
|
|
|
|
// Specializations to allow directly reading/writing wxStrings from JSON
|
|
|
|
void to_json( nlohmann::json& aJson, const wxString& aString );
|
|
|
|
void from_json( const nlohmann::json& aJson, wxString& aString );
|
|
|
|
#endif
|