|
|
|
|
@ -0,0 +1,186 @@ |
|
|
|
|
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 |
|
|
|
|
// TODO create file if it does not exist yet |
|
|
|
|
if (String.IsNullOrEmpty(settingsFilePath)) { throw new ArgumentNullException(nameof(settingsFilePath)); } |
|
|
|
|
|
|
|
|
|
this.settingsFilePath = settingsFilePath; |
|
|
|
|
this.settings = new List<AppSetting>(); |
|
|
|
|
this.serializer = new XmlSerializer(typeof(List<AppSetting>)); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
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() |
|
|
|
|
{ |
|
|
|
|
// TODO write log entries for when something goes wrong |
|
|
|
|
TextWriter writer = new StreamWriter(this.settingsFilePath); |
|
|
|
|
this.serializer.Serialize(writer, this.settings); |
|
|
|
|
writer.Close(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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}'.")); |
|
|
|
|
|
|
|
|
|
this.WriteSetting(key, defaultValue); |
|
|
|
|
value = defaultValue; |
|
|
|
|
return true; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// 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; |
|
|
|
|
} |
|
|
|
|
} |