using System; using System.Collections; using System.Collections.Generic; using System.IO; using System.Linq; using System.Reflection; using Swan.Formatters; using Swan.Reflection; namespace Swan.Configuration { /// /// Represents a provider to save and load settings using a plain JSON file. /// /// /// The following example shows how to save and load settings. /// /// using Swan.Configuration; /// /// public class Example /// { /// public static void Main() /// { /// // get user from settings /// var user = SettingsProvider<Settings>.Instance.Global.User; /// /// // modify the port /// SettingsProvider<Settings>.Instance.Global.Port = 20; /// /// // if we want these settings to persist /// SettingsProvider<Settings>.Instance.PersistGlobalSettings(); /// } /// /// public class Settings /// { /// public int Port { get; set; } = 9696; /// /// public string User { get; set; } = "User"; /// } /// } /// /// /// The type of settings model. public sealed class SettingsProvider : SingletonBase> { private readonly Object _syncRoot = new Object(); private T _global; /// /// Gets or sets the configuration file path. By default the entry assembly directory is used /// and the filename is 'appsettings.json'. /// /// /// The configuration file path. /// public String ConfigurationFilePath { get; set; } = Path.Combine(SwanRuntime.EntryAssemblyDirectory, "appsettings.json"); /// /// Gets the global settings object. /// /// /// The global settings object. /// public T Global { get { lock (this._syncRoot) { if (Equals(this._global, default(T)!)) { this.ReloadGlobalSettings(); } return this._global; } } } /// /// Reloads the global settings. /// public void ReloadGlobalSettings() { if (File.Exists(this.ConfigurationFilePath) == false || File.ReadAllText(this.ConfigurationFilePath).Length == 0) { this.ResetGlobalSettings(); return; } lock (this._syncRoot) { this._global = Json.Deserialize(File.ReadAllText(this.ConfigurationFilePath)); } } /// /// Persists the global settings. /// public void PersistGlobalSettings() => File.WriteAllText(this.ConfigurationFilePath, Json.Serialize(this.Global, true)); /// /// Updates settings from list. /// /// The list. /// /// A list of settings of type ref="ExtendedPropertyInfo". /// /// propertyList. public List RefreshFromList(List> propertyList) { if (propertyList == null) { throw new ArgumentNullException(nameof(propertyList)); } List changedSettings = new List(); IEnumerable globalProps = PropertyTypeCache.DefaultCache.Value.RetrieveAllProperties(); foreach (ExtendedPropertyInfo property in propertyList) { PropertyInfo propertyInfo = globalProps.FirstOrDefault(x => x.Name == property.Property); if (propertyInfo == null) { continue; } Object originalValue = propertyInfo.GetValue(this.Global); Boolean isChanged = propertyInfo.PropertyType.IsArray ? property.Value is IEnumerable enumerable && propertyInfo.TrySetArray(enumerable.Cast(), this.Global) : this.SetValue(property.Value, originalValue, propertyInfo); if (!isChanged) { continue; } changedSettings.Add(property.Property); this.PersistGlobalSettings(); } return changedSettings; } /// /// Gets the list. /// /// A List of ExtendedPropertyInfo of the type T. public List> GetList() { Dictionary jsonData = Json.Deserialize(Json.Serialize(this.Global)) as Dictionary; return jsonData?.Keys .Select(p => new ExtendedPropertyInfo(p) { Value = jsonData[p] }) .ToList(); } /// /// Resets the global settings. /// public void ResetGlobalSettings() { lock (this._syncRoot) { this._global = Activator.CreateInstance(); } this.PersistGlobalSettings(); } private Boolean SetValue(Object property, Object originalValue, PropertyInfo propertyInfo) { switch (property) { case null when originalValue == null: break; case null: propertyInfo.SetValue(this.Global, null); return true; default: if (propertyInfo.PropertyType.TryParseBasicType(property, out Object propertyValue) && !propertyValue.Equals(originalValue)) { propertyInfo.SetValue(this.Global, propertyValue); return true; } break; } return false; } } }