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.
Zero/ZeroLevel/Services/Config/BaseConfiguration.cs

635 lines
21 KiB

6 years ago
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
5 years ago
using System.Net;
using System.Reflection;
using ZeroLevel.Services.Collections;
using ZeroLevel.Services.ObjectMapping;
6 years ago
using ZeroLevel.Services.Reflection;
using ZeroLevel.Services.Serialization;
namespace ZeroLevel.Services.Config
{
/// <summary>
/// Base configuration
6 years ago
/// </summary>
internal sealed class BaseConfiguration :
IConfiguration
{
#region Private members
6 years ago
/// <summary>
/// When true, any changes disallow
6 years ago
/// </summary>
private bool _freezed = false;
6 years ago
/// <summary>
/// When true, freeze permanent, can't be canceled
6 years ago
/// </summary>
private bool _permanentFreezed = false;
6 years ago
private readonly object _freezeLock = new object();
6 years ago
/// <summary>
/// Key-values dictionary
6 years ago
/// </summary>
private readonly ConcurrentDictionary<string, IList<string>> _keyValues = new ConcurrentDictionary<string, IList<string>>();
6 years ago
/// <summary>
/// Empty list
6 years ago
/// </summary>
private static readonly IEnumerable<string> EmptyValuesList = new List<string>(0);
private static string GetKey(string key)
{
if (string.IsNullOrWhiteSpace(key))
{
throw new ArgumentNullException("key");
}
return key.Trim().ToLower(CultureInfo.InvariantCulture);
}
#endregion Private members
6 years ago
#region Properties
6 years ago
/// <summary>
/// Get values by key
6 years ago
/// </summary>
/// <param name="key">key</param>
/// <returns>Values list</returns>
6 years ago
public IEnumerable<string> this[string key]
{
get
{
key = key.ToLower(CultureInfo.CurrentCulture);
IList<string> result;
if (_keyValues.TryGetValue(key, out result))
{
return result;
}
return EmptyValuesList;
}
}
6 years ago
/// <summary>
/// Keys list
6 years ago
/// </summary>
public IEnumerable<string> Keys
{
get { return _keyValues.Keys; }
}
public bool Freezed
{
get
{
return _freezed;
}
}
#endregion Properties
6 years ago
#region Public methods
#region Get
6 years ago
/// <summary>
6 years ago
/// Getting a list of the value corresponding to the specified key
6 years ago
/// </summary>
6 years ago
/// <param name="key">Key</param>
/// <returns>Values list</returns>
6 years ago
public IEnumerable<string> Items(string key)
{
return this[key];
}
6 years ago
/// <summary>
6 years ago
/// Getting the first value for the specified key
6 years ago
/// </summary>
6 years ago
/// <param name="key">Key</param>
/// <returns>The first value, or null if the key is, but there are no values, or KeyNotFoundException if there is no key</returns>
6 years ago
public string First(string key)
{
IList<string> result;
if (_keyValues.TryGetValue(GetKey(key), out result))
{
if (result.Count > 0)
return result[0];
7 months ago
return null!;
6 years ago
}
throw new KeyNotFoundException("Key not found: " + key);
}
public void DoWithFirst(string key, Action<string> action)
{
if (Contains(key))
{
action(First(key));
}
}
public void DoWithFirst<T>(string key, Action<T> action)
{
if (Contains(key))
{
action(First<T>(key));
}
}
/// <summary>
6 years ago
/// Getting the first value for the specified key, with an attempt to convert to the specified type
6 years ago
/// </summary>
6 years ago
/// <typeparam name="T">Expected type</typeparam>
/// <param name="key">Key</param>
/// <returns>The first value, or default (T) if there is a key but no values, or KeyNotFoundException if there is no key</returns>
6 years ago
public T First<T>(string key)
{
IList<string> result;
if (_keyValues.TryGetValue(GetKey(key), out result))
{
if (result.Count > 0)
{
6 years ago
return (T)StringToTypeConverter.TryConvert(result[0], typeof(T));
}
7 months ago
return default(T)!;
6 years ago
}
throw new KeyNotFoundException("Parameter not found: " + key);
}
6 years ago
/// <summary>
6 years ago
/// First value, or Default value if no value or key
6 years ago
/// </summary>
6 years ago
/// <param name="key">Key</param>
/// <param name="defaultValue">Default value</param>
/// <returns>First value, or Default value if no value or key</returns>
6 years ago
public string FirstOrDefault(string key, string defaultValue)
{
IList<string> result;
if (_keyValues.TryGetValue(GetKey(key), out result))
{
if (result.Count > 0)
return result[0];
}
return defaultValue;
}
6 years ago
/// <summary>
6 years ago
/// Getting the first value for the specified key, or defaults, with an attempt to convert to the specified type
6 years ago
/// </summary>
6 years ago
/// <typeparam name="T">Expected type</typeparam>
/// <param name="key">Key</param>
/// <returns>The first value, or default (T) if there are no values or a key</returns>
6 years ago
public T FirstOrDefault<T>(string key)
{
IList<string> result;
if (_keyValues.TryGetValue(GetKey(key), out result))
{
if (result.Count > 0)
{
6 years ago
return (T)StringToTypeConverter.TryConvert(result[0], typeof(T));
}
6 years ago
}
7 months ago
return default(T)!;
6 years ago
}
6 years ago
/// <summary>
6 years ago
/// Getting the first value for the specified key, or defaults, with an attempt to convert to the specified type
6 years ago
/// </summary>
6 years ago
/// <typeparam name="T">Expected type</typeparam>
/// <param name="key">Key</param>
/// <param name="defaultValue">Default value</param>
/// <returns>First value, or Default value if no value or key</returns>
6 years ago
public T FirstOrDefault<T>(string key, T defaultValue)
{
IList<string> result;
if (_keyValues.TryGetValue(GetKey(key), out result))
{
if (result.Count > 0)
{
6 years ago
return (T)StringToTypeConverter.TryConvert(result[0], typeof(T));
}
6 years ago
}
return defaultValue;
}
6 years ago
/// <summary>
6 years ago
/// Check for the presence of a key and a non-empty list of values associated with it
6 years ago
/// </summary>
6 years ago
/// <param name="key">Key</param>
/// <returns>true - if a key exists and there is at least one value</returns>
6 years ago
public bool Contains(string key)
{
key = GetKey(key);
return _keyValues.ContainsKey(key) && _keyValues[key].Count > 0;
}
6 years ago
/// <summary>
6 years ago
/// Check for one of the keys
6 years ago
/// </summary>
public bool Contains(params string[] keys)
{
foreach (var key in keys)
if (Contains(key)) return true;
return false;
}
6 years ago
/// <summary>
6 years ago
/// Check for the presence of a key and its associated value
6 years ago
/// </summary>
public bool ContainsValue(string key, string value)
{
IList<string> result;
if (_keyValues.TryGetValue(GetKey(key), out result))
{
return result.Contains(value);
}
return false;
}
6 years ago
/// <summary>
6 years ago
/// The number of values associated with the specified key
6 years ago
/// </summary>
6 years ago
/// <param name="key">Key</param>
/// <returns>Number of values</returns>
6 years ago
public int Count(string key)
{
key = GetKey(key);
if (_keyValues.ContainsKey(key))
{
return _keyValues[key].Count;
}
return 0;
}
#endregion Get
6 years ago
/// <summary>
/// Add key-value
6 years ago
/// </summary>
/// <param name="key">Key</param>
/// <param name="value">Value</param>
6 years ago
public IConfiguration Append(string key, string value)
{
if (false == _freezed)
{
key = GetKey(key);
if (false == _keyValues.ContainsKey(key))
{
_keyValues.TryAdd(key, new List<string>());
}
7 months ago
_keyValues[key].Add((value?.Trim() ?? null)!);
6 years ago
}
return this;
}
public IConfiguration Append(string key, IEnumerable<string> values)
{
if (false == _freezed)
{
key = GetKey(key);
if (false == _keyValues.ContainsKey(key))
{
_keyValues.TryAdd(key, new List<string>());
}
foreach (var value in values)
{
7 months ago
_keyValues[key].Add((value?.Trim() ?? null)!);
}
}
return this;
}
6 years ago
/// <summary>
/// Set unique value for key
6 years ago
/// </summary>
public IConfiguration SetUnique(string key, string value)
{
if (false == _freezed)
{
key = GetKey(key);
if (false == _keyValues.ContainsKey(key))
{
_keyValues.TryAdd(key, new List<string>());
}
else
{
_keyValues[key].Clear();
}
7 months ago
_keyValues[key].Add((value?.Trim() ?? null)!);
6 years ago
}
return this;
}
6 years ago
/// <summary>
/// Clean values binded with key
6 years ago
/// </summary>
/// <param name="key">Key</param>
6 years ago
public IConfiguration Clear(string key)
{
if (false == _freezed)
{
key = GetKey(key);
if (_keyValues.ContainsKey(key))
{
_keyValues[key].Clear();
}
}
return this;
}
6 years ago
/// <summary>
/// Configuration drop
6 years ago
/// </summary>
public IConfiguration Clear()
{
if (false == _freezed)
{
_keyValues.Clear();
}
return this;
}
6 years ago
/// <summary>
/// Remove key and binded values
6 years ago
/// </summary>
/// <param name="key">Key</param>
6 years ago
public IConfiguration Remove(string key)
{
if (false == _freezed)
{
7 months ago
_keyValues.TryRemove(GetKey(key), out _);
6 years ago
}
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
6 years ago
#region IEquatable
6 years ago
public bool Equals(IConfiguration other)
{
7 months ago
if (other == null!)
6 years ago
{
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
6 years ago
#region Binary Serializable
6 years ago
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<string>();
for (var k = 0; k < count_values; k++)
{
list_values.Add(reader.ReadString());
}
_keyValues.TryAdd(key, list_values);
}
}
#endregion Binary Serializable
public void CopyTo(IConfiguration config)
{
6 years ago
foreach (var key in this.Keys)
{
6 years ago
config.Append(key, this[key]);
}
}
public void MergeFrom(IConfiguration config, ConfigurationRecordExistBehavior existRecordBehavior)
{
foreach (var key in config.Keys)
{
if (this.Contains(key))
{
switch (existRecordBehavior)
{
case ConfigurationRecordExistBehavior.Append:
this.Append(key, config[key]);
break;
case ConfigurationRecordExistBehavior.IgnoreNew:
continue;
case ConfigurationRecordExistBehavior.Overwrite:
this.Remove(key);
this.Append(key, config[key]);
break;
}
}
else
{
this.Append(key, config[key]);
}
}
}
public T Bind<T>() => (T)Bind(typeof(T));
public object Bind(Type type)
{
var mapper = TypeMapper.Create(type, true);
var instance = TypeHelpers.CreateInitialState(type);
mapper.TraversalMembers(member =>
{
int count = Count(member.Name);
if (count > 0)
{
var values = this.Items(member.Name);
7 months ago
IConfigRecordParser parser = member.Original.GetCustomAttribute<ConfigRecordParseAttribute>()?.Parser!;
if (TypeHelpers.IsArray(member.ClrType) && member.ClrType.GetArrayRank() == 1)
{
int index = 0;
var itemType = member.ClrType.GetElementType();
7 months ago
if (parser == null!)
{
var elements = values.SelectMany(v => SplitRange(v, itemType)).ToArray();
var arrayBuilder = CollectionFactory.CreateArray(itemType, elements.Length);
foreach (var item in elements)
{
arrayBuilder.Set(item, index);
index++;
}
member.Setter(instance, arrayBuilder.Complete());
}
else
{
var elements = values.Select(v => parser.Parse(v)).ToArray();
var arrayBuilder = CollectionFactory.CreateArray(itemType, (elements[0] as Array)?.Length ?? 0);
foreach (var item in (elements[0] as Array) ?? Array.Empty<object>())
{
arrayBuilder.Set(item, index);
index++;
}
member.Setter(instance, arrayBuilder.Complete());
}
}
else if (TypeHelpers.IsEnumerable(member.ClrType) && member.ClrType != typeof(string))
{
var itemType = member.ClrType.GenericTypeArguments.First();
var collectionBuilder = CollectionFactory.Create(itemType);
7 months ago
if (parser == null!)
{
var elements = values.SelectMany(v => SplitRange(v, itemType)).ToArray();
foreach (var item in elements)
{
collectionBuilder.Append(item);
}
}
else
{
var elements = values.Select(v => parser.Parse(v)).ToArray();
foreach (var item in elements)
{
collectionBuilder.Append(item);
}
}
member.Setter(instance, collectionBuilder.Complete());
}
else
{
var single = values.First();
7 months ago
if (parser != null!)
{
member.Setter(instance, parser.Parse(single));
}
else
{
if (TypeHelpers.IsEnum(member.ClrType))
5 years ago
{
var value = Enum.Parse(member.ClrType, single);
5 years ago
member.Setter(instance, value);
}
else if (TypeHelpers.IsUri(member.ClrType))
{
var uri = new Uri(single);
5 years ago
member.Setter(instance, uri);
}
else if (TypeHelpers.IsIpEndPoint(member.ClrType))
{
var ep = ZeroLevel.Network.NetUtils.CreateIPEndPoint(single);
5 years ago
member.Setter(instance, ep);
}
else if (member.ClrType == typeof(IPAddress))
{
var ip = IPAddress.Parse(single);
5 years ago
member.Setter(instance, ip);
}
else
{
var itemType = member.ClrType;
member.Setter(instance, StringToTypeConverter.TryConvert(single, itemType));
}
}
}
}
});
return instance;
}
private static IEnumerable<object> SplitRange(string line, Type elementType)
{
if (string.IsNullOrWhiteSpace(line))
yield return StringToTypeConverter.TryConvert(line, elementType);
foreach (var part in line.Split(','))
{
if (TypeHelpers.IsNumericType(elementType) && part.IndexOf('-') >= 0)
{
var lr = part.Split('-');
if (lr.Length == 2)
{
// not use parser with range
long left = (long)Convert.ChangeType(StringToTypeConverter.TryConvert(lr[0], elementType), typeof(long));
long right = (long)Convert.ChangeType(StringToTypeConverter.TryConvert(lr[1], elementType), typeof(long));
for (; left <= right; left++)
{
yield return Convert.ChangeType(left, elementType);
}
}
else
{
// incorrect string
yield break;
}
}
else
{
yield return StringToTypeConverter.TryConvert(part, elementType);
}
}
}
6 years ago
}
}

Powered by TurnKey Linux.