using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Globalization; using ZeroLevel.Services.Reflection; using ZeroLevel.Services.Serialization; namespace ZeroLevel.Services.Config { /// /// Base configuration /// internal sealed class BaseConfiguration : IConfiguration { #region Private members /// /// When true, any changes disallow /// private bool _freezed = false; /// /// When true, freeze permanent, can't be canceled /// private bool _permanentFreezed = false; private readonly object _freezeLock = new object(); /// /// Key-values dictionary /// private readonly ConcurrentDictionary> _keyValues = new ConcurrentDictionary>(); /// /// Empty list /// private static readonly IEnumerable EmptyValuesList = new List(0); private static string GetKey(string key) { if (string.IsNullOrWhiteSpace(key)) { throw new ArgumentNullException("key"); } return key.Trim().ToLower(CultureInfo.InvariantCulture); } #endregion Private members #region Properties /// /// Get values by key /// /// key /// Values list public IEnumerable this[string key] { get { key = key.ToLower(CultureInfo.CurrentCulture); IList result; if (_keyValues.TryGetValue(key, out result)) { return result; } return EmptyValuesList; } } /// /// Keys list /// public IEnumerable Keys { get { return _keyValues.Keys; } } public bool Freezed { get { return _freezed; } } #endregion Properties #region Public methods #region Get /// /// Получение списка значение соотвествующих указанному ключу /// /// Ключ /// Список значений public IEnumerable Items(string key) { return this[key]; } /// /// Получение первого значения для указанного ключа /// /// Ключ /// Первое значение, или null если ключ есть, но нет значений, или KeyNotFoundException если нет ключа public string First(string key) { IList result; if (_keyValues.TryGetValue(GetKey(key), out result)) { if (result.Count > 0) return result[0]; return null; } throw new KeyNotFoundException("Key not found: " + key); } public void DoWithFirst(string key, Action action) { if (Contains(key)) { action(First(key)); } } public void DoWithFirst(string key, Action action) { if (Contains(key)) { action(First(key)); } } /// /// Получение первого значения для указанного ключа, с попыткой преобразования в указанный тип /// /// Ожидаемый тип /// Ключ /// Первое значение, или default(T) если ключ есть, но нет значений, или KeyNotFoundException если нет ключа public T First(string key) { IList result; if (_keyValues.TryGetValue(GetKey(key), out result)) { if (result.Count > 0) return (T)StringToTypeConverter.TryConvert(result[0], typeof(T)); return default(T); } throw new KeyNotFoundException("Parameter not found: " + key); } /// /// Получение первого значения для указанного ключа, или значения по умолчанию /// /// Ключ /// Значение по умолчанию /// Первое значение, или значение по умолчанию если нет значений или ключа public string FirstOrDefault(string key, string defaultValue) { IList result; if (_keyValues.TryGetValue(GetKey(key), out result)) { if (result.Count > 0) return result[0]; } return defaultValue; } /// /// Получение первого значения для указанного ключа, или значения по умолчанию, с попыткой преобразования в указанный тип /// /// Ожидаемый тип /// Ключ /// Первое значение, или default(T) если нет значений или ключа public T FirstOrDefault(string key) { IList result; if (_keyValues.TryGetValue(GetKey(key), out result)) { if (result.Count > 0) return (T)StringToTypeConverter.TryConvert(result[0], typeof(T)); } return default(T); } /// /// Получение первого значения для указанного ключа, или значения по умолчанию, с попыткой преобразования в указанный тип /// /// Ожидаемый тип /// Ключ /// Значение по умолчанию /// Первое значение, или значение по умолчанию если нет значений или ключа public T FirstOrDefault(string key, T defaultValue) { IList result; if (_keyValues.TryGetValue(GetKey(key), out result)) { if (result.Count > 0) return (T)StringToTypeConverter.TryConvert(result[0], typeof(T)); } return defaultValue; } /// /// Проверка наличия ключа и непустого списка связанных с ним значений /// /// Ключ /// true - если существует ключ и есть хотя бы одно значение public bool Contains(string key) { key = GetKey(key); return _keyValues.ContainsKey(key) && _keyValues[key].Count > 0; } /// /// Проверка наличия одного из ключей /// public bool Contains(params string[] keys) { foreach (var key in keys) if (Contains(key)) return true; return false; } /// /// Проверка наличия ключа и связанного с ним значения /// /// /// /// public bool ContainsValue(string key, string value) { IList result; if (_keyValues.TryGetValue(GetKey(key), out result)) { return result.Contains(value); } return false; } /// /// Количество значений связанных с указанным ключом /// /// Ключ /// Количество значений public int Count(string key) { key = GetKey(key); if (_keyValues.ContainsKey(key)) { return _keyValues[key].Count; } return 0; } #endregion Get /// /// Add key-value /// /// Key /// Value public IConfiguration Append(string key, string value) { if (false == _freezed) { key = GetKey(key); if (false == _keyValues.ContainsKey(key)) { _keyValues.TryAdd(key, new List()); } _keyValues[key].Add(value?.Trim() ?? null); } return this; } /// /// Set unique value for key /// public IConfiguration SetUnique(string key, string value) { if (false == _freezed) { key = GetKey(key); if (false == _keyValues.ContainsKey(key)) { _keyValues.TryAdd(key, new List()); } else { _keyValues[key].Clear(); } _keyValues[key].Add(value?.Trim() ?? null); } return this; } /// /// Clean values binded with key /// /// Key public IConfiguration Clear(string key) { if (false == _freezed) { key = GetKey(key); if (_keyValues.ContainsKey(key)) { _keyValues[key].Clear(); } } return this; } /// /// Configuration drop /// public IConfiguration Clear() { if (false == _freezed) { _keyValues.Clear(); } return this; } /// /// Remove key and binded values /// /// Key public IConfiguration Remove(string key) { if (false == _freezed) { IList removed; _keyValues.TryRemove(GetKey(key), out removed); } return this; } public bool Freeze(bool permanent = false) { lock (_freezeLock) { if (false == _freezed) { _freezed = true; _permanentFreezed = permanent; return true; } else if (_permanentFreezed == false && permanent) { _permanentFreezed = true; return true; } return false; } } public bool Unfreeze() { lock (_freezeLock) { if (_freezed && _permanentFreezed == false) { _freezed = false; return true; } return false; } } #endregion Public methods #region IEquatable public bool Equals(IConfiguration other) { if (other == null) { return false; } if (this.Keys.NoOrderingEquals(other.Keys) == false) { return false; } foreach (var key in Keys) { if (this[key].NoOrderingEquals(other[key]) == false) { return false; } } return true; } #endregion IEquatable #region Binary Serializable public void Serialize(IBinaryWriter writer) { writer.WriteBoolean(this._freezed); writer.WriteBoolean(this._permanentFreezed); writer.WriteInt32(_keyValues.Count); foreach (var pair in _keyValues) { writer.WriteString(pair.Key); writer.WriteInt32(pair.Value.Count); foreach (var value in pair.Value) { writer.WriteString(value); } } } public void Deserialize(IBinaryReader reader) { this._freezed = reader.ReadBoolean(); this._permanentFreezed = reader.ReadBoolean(); var count = reader.ReadInt32(); _keyValues.Clear(); for (int i = 0; i < count; i++) { var key = reader.ReadString(); var count_values = reader.ReadInt32(); var list_values = new List(); for (var k = 0; k < count_values; k++) { list_values.Add(reader.ReadString()); } _keyValues.TryAdd(key, list_values); } } #endregion Binary Serializable } }