You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
198 lines
7.8 KiB
198 lines
7.8 KiB
using Common.Logging;
|
|
using System.Xml.Serialization;
|
|
|
|
namespace Common.AppSettings;
|
|
|
|
public class XmlAppSettingsProvider : IAppSettingsProvider
|
|
{
|
|
private readonly string settingsFilePath;
|
|
private readonly XmlSerializer serializer;
|
|
private List<AppSetting> settings;
|
|
|
|
public event EventHandler<LogEntry>? LogWorthyEvent;
|
|
|
|
public XmlAppSettingsProvider(string settingsFilePath)
|
|
{
|
|
// check for file path validity
|
|
if (String.IsNullOrEmpty(settingsFilePath)) { throw new ArgumentNullException(nameof(settingsFilePath)); }
|
|
|
|
this.settingsFilePath = settingsFilePath;
|
|
this.settings = new List<AppSetting>();
|
|
this.serializer = new XmlSerializer(typeof(List<AppSetting>));
|
|
|
|
// create file if it does not exist yet
|
|
if (File.Exists(settingsFilePath) == false)
|
|
{
|
|
this.Save();
|
|
}
|
|
}
|
|
|
|
public void Load()
|
|
{
|
|
// read file by creating a stream. this will throw an exception when the file does not exist.
|
|
using var fileStream = new FileStream(this.settingsFilePath, FileMode.Open);
|
|
|
|
// deserialize and cast to List if not null
|
|
var deserializedObj = this.serializer.Deserialize(fileStream);
|
|
if(deserializedObj != null)
|
|
{
|
|
// create log entry if there were already some settings loaded
|
|
if(this.settings.Count > 0)
|
|
{
|
|
this.LogWorthyEvent?.Invoke(this, new LogEntry(level: LogLevel.Warn, category: nameof(XmlAppSettingsProvider),
|
|
message: $"'{nameof(Load)}()' deserialization overwrote previously loaded settings."));
|
|
}
|
|
this.settings = (List<AppSetting>)deserializedObj;
|
|
}
|
|
else
|
|
{
|
|
this.LogWorthyEvent?.Invoke(this, new LogEntry(level: LogLevel.Warn, category: nameof(XmlAppSettingsProvider),
|
|
message: $"'{nameof(Load)}()' deserialization resulted in null object. Did not change loaded settings."));
|
|
}
|
|
}
|
|
|
|
public void Save()
|
|
{
|
|
// if saving fails, just log the exception. user is happier if the app is useable but does not remember settings
|
|
try
|
|
{
|
|
// create file and serialize data
|
|
TextWriter writer = new StreamWriter(this.settingsFilePath);
|
|
this.serializer.Serialize(writer, this.settings);
|
|
writer.Close();
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
this.LogWorthyEvent?.Invoke(this, new LogEntry(){ LogLevel = LogLevel.Error, Category = nameof(XmlAppSettingsProvider),
|
|
Message = $"'{nameof(Save)}()' failed to create file or serialize data.", Exception = ex });
|
|
}
|
|
}
|
|
|
|
|
|
|
|
public bool WriteSetting(string key, string value)
|
|
{
|
|
// guard empty key or value
|
|
if(String.IsNullOrEmpty(key)) throw new ArgumentNullException(nameof(key));
|
|
if (String.IsNullOrEmpty(value)) throw new ArgumentNullException(nameof(value));
|
|
|
|
// get all settings with this key
|
|
var foundSettings = this.settings.FindAll(x => x.Key == key);
|
|
|
|
// check if the key already exists multiple times
|
|
if(foundSettings.Count > 1)
|
|
{
|
|
this.LogWorthyEvent?.Invoke(this, new LogEntry(level: LogLevel.Error, category: nameof(XmlAppSettingsProvider),
|
|
message: $"'{nameof(WriteSetting)}()' cancelled because {foundSettings.Count} settings with the key '{key}' were found. Expected none or exactly one."));
|
|
|
|
return false; // cancel
|
|
}
|
|
// if exactly one is found => remove existing app setting
|
|
else if(foundSettings.Count == 1)
|
|
{
|
|
this.settings.Remove(this.settings.Find(x => x.Key == key)!);
|
|
}
|
|
// none existing yet => inform via event
|
|
else if(foundSettings.Count == 0)
|
|
{
|
|
this.LogWorthyEvent?.Invoke(this, new LogEntry(level: LogLevel.Info, category: nameof(XmlAppSettingsProvider),
|
|
message: $"'{nameof(WriteSetting)}()' initialized new setting with key '{key}' and value '{value}'."));
|
|
}
|
|
|
|
// add new setting
|
|
this.settings.Add(new AppSetting(key, value));
|
|
return true;
|
|
}
|
|
|
|
public bool TryReadSetting(string key, out string value)
|
|
{
|
|
// guard empty key
|
|
if (String.IsNullOrEmpty(key)) throw new ArgumentNullException(nameof(key));
|
|
|
|
// get all settings with this key
|
|
var foundSettings = this.settings.FindAll(x => x.Key == key);
|
|
|
|
// too many values found
|
|
if (foundSettings.Count > 1)
|
|
{
|
|
this.LogWorthyEvent?.Invoke(this, new LogEntry(level: LogLevel.Error, category: nameof(XmlAppSettingsProvider),
|
|
message: $"'{nameof(TryReadSetting)}()' did find {foundSettings.Count} settings with the key '{key}'. Expected only one."));
|
|
|
|
value = string.Empty;
|
|
return false;
|
|
}
|
|
|
|
// no value found
|
|
if (foundSettings.Count == 0)
|
|
{
|
|
this.LogWorthyEvent?.Invoke(this, new LogEntry(level: LogLevel.Error, category: nameof(XmlAppSettingsProvider),
|
|
message: $"'{nameof(TryReadSetting)}()' did not find a setting with the key '{key}'."));
|
|
|
|
value = string.Empty;
|
|
return false;
|
|
}
|
|
|
|
// extract correct setting when found exactly one
|
|
var setting = foundSettings[0];
|
|
if(setting.Value != null)
|
|
{
|
|
value = setting.Value;
|
|
}
|
|
// if value is null it was probably an empty string or similar => set it to empty string
|
|
else
|
|
{
|
|
this.LogWorthyEvent?.Invoke(this, new LogEntry(level: LogLevel.Warn, category: nameof(XmlAppSettingsProvider),
|
|
message: $"'{nameof(TryReadSetting)}()' value of retrieved setting with key '{key}' was null. Returned empty string instead."));
|
|
|
|
value = string.Empty;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
public bool TryReadSettingOrAddDefault(string key, out string value, string defaultValue)
|
|
{
|
|
// guard empty key or defaultValue
|
|
if (String.IsNullOrEmpty(key)) throw new ArgumentNullException(nameof(key));
|
|
if (String.IsNullOrEmpty(defaultValue)) throw new ArgumentNullException(nameof(defaultValue));
|
|
|
|
// get all settings with this key
|
|
var foundSettings = this.settings.FindAll(x => x.Key == key);
|
|
|
|
// too many values found
|
|
if (foundSettings.Count > 1)
|
|
{
|
|
this.LogWorthyEvent?.Invoke(this, new LogEntry(level: LogLevel.Error, category: nameof(XmlAppSettingsProvider),
|
|
message: $"'{nameof(TryReadSettingOrAddDefault)}()' did find {foundSettings.Count} settings with the key '{key}'. Expected only one."));
|
|
|
|
value = string.Empty;
|
|
return false;
|
|
}
|
|
|
|
// no value found => initialize with the default value
|
|
if (foundSettings.Count == 0)
|
|
{
|
|
this.LogWorthyEvent?.Invoke(this, new LogEntry(level: LogLevel.Info, category: nameof(XmlAppSettingsProvider),
|
|
message: $"'{nameof(TryReadSettingOrAddDefault)}()' did not find a setting with the key '{key}'. Adding setting with value '{defaultValue}'."));
|
|
|
|
value = defaultValue;
|
|
return this.WriteSetting(key, defaultValue);
|
|
}
|
|
|
|
// extract correct setting when found exactly one
|
|
var setting = foundSettings[0];
|
|
if (setting.Value != null)
|
|
{
|
|
value = setting.Value;
|
|
}
|
|
// if value is null it was probably an empty string or similar => set it to empty string
|
|
else
|
|
{
|
|
this.LogWorthyEvent?.Invoke(this, new LogEntry(level: LogLevel.Warn, category: nameof(XmlAppSettingsProvider),
|
|
message: $"'{nameof(TryReadSetting)}()' value of retrieved setting with key '{key}' was null. Returned empty string instead."));
|
|
|
|
value = string.Empty;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
}
|
|
|