2
0
mirror of https://github.com/esiur/esiur-dotnet.git synced 2026-04-29 14:48:44 +00:00

.Net 6 Upgrade

This commit is contained in:
2021-12-01 12:17:45 +03:00
parent 1166e93ba9
commit 530df018ec
164 changed files with 21247 additions and 21425 deletions
+245 -246
View File
@@ -30,285 +30,284 @@ using System.Collections;
using Esiur.Core;
using System.Reflection;
namespace Esiur.Data
namespace Esiur.Data;
public class AutoList<T, ST> : IEnumerable<T>, ICollection, ICollection<T>
{
public class AutoList<T, ST> : IEnumerable<T>, ICollection, ICollection<T>
private readonly object syncRoot = new object();
private List<T> list = new List<T>();
public delegate void Modified(ST sender, int index, T oldValue, T newValue);
public delegate void Added(ST sender, T value);
public delegate void Removed(ST sender, T value);
public delegate void Cleared(ST sender);
public event Modified OnModified;
public event Removed OnRemoved;
public event Cleared OnCleared;
public event Added OnAdd;
bool removableList;
public ST State { get; set; }
/*
IOrderedEnumerable<T> OrderBy<T, TK>(Func<T, TK> keySelector)
{
return list.OrderBy<T,TK>(keySelector);
}
*/
private readonly object syncRoot = new object();
private List<T> list = new List<T>();
public delegate void Modified(ST sender, int index, T oldValue, T newValue);
public delegate void Added(ST sender, T value);
public delegate void Removed(ST sender, T value);
public delegate void Cleared(ST sender);
public event Modified OnModified;
public event Removed OnRemoved;
public event Cleared OnCleared;
public event Added OnAdd;
bool removableList;
public ST State { get; set; }
/*
IOrderedEnumerable<T> OrderBy<T, TK>(Func<T, TK> keySelector)
{
return list.OrderBy<T,TK>(keySelector);
}
*/
public void Sort()
{
lock(syncRoot)
public void Sort()
{
lock (syncRoot)
list.Sort();
}
}
public void Sort(IComparer<T> comparer)
{
lock (syncRoot)
list.Sort(comparer);
}
public void Sort(IComparer<T> comparer)
{
lock (syncRoot)
list.Sort(comparer);
}
public void Sort(Comparison<T> comparison)
{
lock (syncRoot)
list.Sort(comparison);
}
public void Sort(Comparison<T> comparison)
{
lock (syncRoot)
list.Sort(comparison);
}
public IEnumerable<T> Where(Func<T, bool> predicate)
{
return list.Where(predicate);
}
public IEnumerable<T> Where(Func<T, bool> predicate)
{
return list.Where(predicate);
}
/// <summary>
/// Convert AutoList to array
/// </summary>
/// <returns>Array</returns>
public T[] ToArray()
{
// list.OrderBy()
return list.ToArray();
}
/// <summary>
/// Convert AutoList to array
/// </summary>
/// <returns>Array</returns>
public T[] ToArray()
{
// list.OrderBy()
return list.ToArray();
}
/// <summary>
/// Create a new instance of AutoList
/// </summary>
/// <param name="state">State object to be included when an event is raised.</param>
public AutoList(ST state)
{
State = state;
/// <summary>
/// Create a new instance of AutoList
/// </summary>
/// <param name="state">State object to be included when an event is raised.</param>
public AutoList(ST state)
{
State = state;
#if NETSTANDARD
removableList = (typeof(IDestructible).GetTypeInfo().IsAssignableFrom(typeof(T).GetTypeInfo()));
removableList = (typeof(IDestructible).GetTypeInfo().IsAssignableFrom(typeof(T).GetTypeInfo()));
#else
removableList = (typeof(IDestructible).IsAssignableFrom(typeof(T)));
#endif
}
}
/// <summary>
/// Create a new instance of AutoList
/// </summary>
/// <param name="values">Populate the list with items</param>
/// <returns></returns>
public AutoList(ST state, T[] values)
{
State = state;
/// <summary>
/// Create a new instance of AutoList
/// </summary>
/// <param name="values">Populate the list with items</param>
/// <returns></returns>
public AutoList(ST state, T[] values)
{
State = state;
#if NETSTANDARD
removableList = (typeof(IDestructible).GetTypeInfo().IsAssignableFrom(typeof(T).GetTypeInfo()));
removableList = (typeof(IDestructible).GetTypeInfo().IsAssignableFrom(typeof(T).GetTypeInfo()));
#else
removableList = (typeof(IDestructible).IsAssignableFrom(typeof(T)));
#endif
AddRange(values);
}
AddRange(values);
}
/// <summary>
/// Synchronization lock of the list
/// </summary>
public object SyncRoot
/// <summary>
/// Synchronization lock of the list
/// </summary>
public object SyncRoot
{
get
{
get
{
return syncRoot;
}
return syncRoot;
}
}
/// <summary>
/// First item in the list
/// </summary>
public T First()
/// <summary>
/// First item in the list
/// </summary>
public T First()
{
return list.First();
}
/// <summary>
/// Get an item at a specified index
/// </summary>
public T this[int index]
{
get
{
return list.First();
return list[index];
}
/// <summary>
/// Get an item at a specified index
/// </summary>
public T this[int index]
set
{
get
{
return list[index];
}
set
{
var oldValue = list[index];
var oldValue = list[index];
if (removableList)
{
if (oldValue != null)
((IDestructible)oldValue).OnDestroy -= ItemDestroyed;
if (value != null)
((IDestructible)value).OnDestroy += ItemDestroyed;
}
lock (syncRoot)
list[index] = value;
OnModified?.Invoke(State, index, oldValue, value);
}
}
/// <summary>
/// Add item to the list
/// </summary>
public void Add(T value)
{
if (removableList)
{
if (oldValue != null)
((IDestructible)oldValue).OnDestroy -= ItemDestroyed;
if (value != null)
((IDestructible)value).OnDestroy += ItemDestroyed;
}
lock (syncRoot)
list.Add(value);
list[index] = value;
OnAdd?.Invoke(State, value);
OnModified?.Invoke(State, index, oldValue, value);
}
/// <summary>
/// Add an array of items to the list
/// </summary>
public void AddRange(T[] values)
{
foreach (var v in values)
Add(v);
}
private void ItemDestroyed(object sender)
{
Remove((T)sender);
}
/// <summary>
/// Clear the list
/// </summary>
public void Clear()
{
if (removableList)
foreach (IDestructible v in list)
if (v != null)
v.OnDestroy -= ItemDestroyed;
lock (syncRoot)
list.Clear();
OnCleared?.Invoke(State);
}
/// <summary>
/// Remove an item from the list
/// <param name="value">Item to remove</param>
/// </summary>
public bool Remove(T value)
{
if (!list.Contains(value))
return false;
if (removableList)
if (value != null)
((IDestructible)value).OnDestroy -= ItemDestroyed;
lock (syncRoot)
list.Remove(value);
OnRemoved?.Invoke(State, value);
return true;
}
/// <summary>
/// Number of items in the list
/// </summary>
public int Count
{
get { return list.Count; }
}
public bool IsSynchronized => (list as ICollection).IsSynchronized;
public bool IsReadOnly => false;
/// <summary>
/// Check if an item exists in the list
/// </summary>
/// <param name="value">Item to check if exists</param>
public bool Contains(T value)
{
return list.Contains(value);
}
/// <summary>
/// Check if any item of the given array is in the list
/// </summary>
/// <param name="values">Array of items</param>
public bool ContainsAny(T[] values)
{
foreach (var v in values)
if (list.Contains(v))
return true;
return false;
}
/// <summary>
/// Check if any item of the given list is in the list
/// </summary>
/// <param name="values">List of items</param>
public bool ContainsAny(AutoList<T, ST> values)
{
foreach (var v in values)
if (list.Contains((T)v))
return true;
return false;
}
public IEnumerator<T> GetEnumerator()
{
return ((IEnumerable<T>)list).GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return ((IEnumerable<T>)list).GetEnumerator();
}
public void CopyTo(Array array, int index)
{
lock (syncRoot)
(list as ICollection).CopyTo(array, index);
}
public void CopyTo(T[] array, int arrayIndex)
{
lock (syncRoot)
list.CopyTo(array, arrayIndex);
}
//bool ICollection<T>.Remove(T item)
//{
// lock(syncRoot)
// return Remove(item);
//}
}
/// <summary>
/// Add item to the list
/// </summary>
public void Add(T value)
{
if (removableList)
if (value != null)
((IDestructible)value).OnDestroy += ItemDestroyed;
lock (syncRoot)
list.Add(value);
OnAdd?.Invoke(State, value);
}
/// <summary>
/// Add an array of items to the list
/// </summary>
public void AddRange(T[] values)
{
foreach (var v in values)
Add(v);
}
private void ItemDestroyed(object sender)
{
Remove((T)sender);
}
/// <summary>
/// Clear the list
/// </summary>
public void Clear()
{
if (removableList)
foreach (IDestructible v in list)
if (v != null)
v.OnDestroy -= ItemDestroyed;
lock (syncRoot)
list.Clear();
OnCleared?.Invoke(State);
}
/// <summary>
/// Remove an item from the list
/// <param name="value">Item to remove</param>
/// </summary>
public bool Remove(T value)
{
if (!list.Contains(value))
return false;
if (removableList)
if (value != null)
((IDestructible)value).OnDestroy -= ItemDestroyed;
lock (syncRoot)
list.Remove(value);
OnRemoved?.Invoke(State, value);
return true;
}
/// <summary>
/// Number of items in the list
/// </summary>
public int Count
{
get { return list.Count; }
}
public bool IsSynchronized => (list as ICollection).IsSynchronized;
public bool IsReadOnly => false;
/// <summary>
/// Check if an item exists in the list
/// </summary>
/// <param name="value">Item to check if exists</param>
public bool Contains(T value)
{
return list.Contains(value);
}
/// <summary>
/// Check if any item of the given array is in the list
/// </summary>
/// <param name="values">Array of items</param>
public bool ContainsAny(T[] values)
{
foreach (var v in values)
if (list.Contains(v))
return true;
return false;
}
/// <summary>
/// Check if any item of the given list is in the list
/// </summary>
/// <param name="values">List of items</param>
public bool ContainsAny(AutoList<T, ST> values)
{
foreach (var v in values)
if (list.Contains((T)v))
return true;
return false;
}
public IEnumerator<T> GetEnumerator()
{
return ((IEnumerable<T>)list).GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return ((IEnumerable<T>)list).GetEnumerator();
}
public void CopyTo(Array array, int index)
{
lock (syncRoot)
(list as ICollection).CopyTo(array, index);
}
public void CopyTo(T[] array, int arrayIndex)
{
lock (syncRoot)
list.CopyTo(array, arrayIndex);
}
//bool ICollection<T>.Remove(T item)
//{
// lock(syncRoot)
// return Remove(item);
//}
}
+656 -657
View File
File diff suppressed because it is too large Load Diff
+1437 -1437
View File
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+79 -81
View File
@@ -28,92 +28,90 @@ using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Esiur.Data
namespace Esiur.Data;
public enum DataType : byte
{
public enum DataType : byte
{
Void = 0x0,
//Variant,
Bool,
Int8,
UInt8,
Char,
Int16,
UInt16,
Int32,
UInt32,
Int64,
UInt64,
Float32,
Float64,
Decimal,
DateTime,
Resource,
DistributedResource,
ResourceLink,
String,
Structure,
Record,
//Stream,
//Array = 0x80,
VarArray = 0x80,
BoolArray,
Int8Array,
UInt8Array,
CharArray,
Int16Array,
UInt16Array,
Int32Array,
UInt32Array,
Int64Array,
UInt64Array,
Float32Array,
Float64Array,
DecimalArray,
DateTimeArray,
ResourceArray,
DistributedResourceArray,
ResourceLinkArray,
StringArray,
StructureArray,
RecordArray,
NotModified = 0x7f,
Unspecified = 0xff,
}
Void = 0x0,
//Variant,
Bool,
Int8,
UInt8,
Char,
Int16,
UInt16,
Int32,
UInt32,
Int64,
UInt64,
Float32,
Float64,
Decimal,
DateTime,
Resource,
DistributedResource,
ResourceLink,
String,
Structure,
Record,
//Stream,
//Array = 0x80,
VarArray = 0x80,
BoolArray,
Int8Array,
UInt8Array,
CharArray,
Int16Array,
UInt16Array,
Int32Array,
UInt32Array,
Int64Array,
UInt64Array,
Float32Array,
Float64Array,
DecimalArray,
DateTimeArray,
ResourceArray,
DistributedResourceArray,
ResourceLinkArray,
StringArray,
StructureArray,
RecordArray,
NotModified = 0x7f,
Unspecified = 0xff,
}
public static class DataTypeExpansions
public static class DataTypeExpansions
{
public static int Size(this DataType t)
{
public static int Size(this DataType t)
switch (t)
{
switch (t)
{
case DataType.Void:
case DataType.NotModified:
return 0;
case DataType.Bool:
case DataType.UInt8:
case DataType.Int8:
return 1;
case DataType.Char:
case DataType.UInt16:
case DataType.Int16:
return 2;
case DataType.Int32:
case DataType.UInt32:
case DataType.Float32:
case DataType.Resource:
return 4;
case DataType.Int64:
case DataType.UInt64:
case DataType.Float64:
case DataType.DateTime:
return 8;
case DataType.DistributedResource:
return 4;
case DataType.Void:
case DataType.NotModified:
return 0;
case DataType.Bool:
case DataType.UInt8:
case DataType.Int8:
return 1;
case DataType.Char:
case DataType.UInt16:
case DataType.Int16:
return 2;
case DataType.Int32:
case DataType.UInt32:
case DataType.Float32:
case DataType.Resource:
return 4;
case DataType.Int64:
case DataType.UInt64:
case DataType.Float64:
case DataType.DateTime:
return 8;
case DataType.DistributedResource:
return 4;
default:
return -1;
}
default:
return -1;
}
}
}
+4 -5
View File
@@ -2,10 +2,9 @@
using System.Collections.Generic;
using System.Text;
namespace Esiur.Data
{
public interface IRecord
{
namespace Esiur.Data;
public interface IRecord
{
}
}
+5 -6
View File
@@ -2,11 +2,10 @@
using System.Collections.Generic;
using System.Text;
namespace Esiur.Data
namespace Esiur.Data;
public interface IUserType
{
public interface IUserType
{
object Get();
void Set(object value);
}
object Get();
void Set(object value);
}
+174 -176
View File
@@ -33,212 +33,210 @@ using System.Linq;
using System.Linq.Expressions;
using Esiur.Core;
namespace Esiur.Data
namespace Esiur.Data;
public class KeyList<KT, T> : IEnumerable<KeyValuePair<KT, T>>
{
private readonly object syncRoot = new object();
private Dictionary<KT, T> dic;
public class KeyList<KT, T> : IEnumerable<KeyValuePair<KT, T>>
public delegate void Modified(KT key, T oldValue, T newValue, KeyList<KT, T> sender);
public delegate void Added(T value, KeyList<KT, T> sender);
public delegate void Removed(KT key, T value, KeyList<KT, T> sender);
public delegate void Cleared(KeyList<KT, T> sender);
public event Modified OnModified;
public event Removed OnRemoved;
public event Cleared OnCleared;
public event Added OnAdd;
bool removableList;
public object SyncRoot
{
private readonly object syncRoot = new object();
private Dictionary<KT, T> dic;
public delegate void Modified(KT key, T oldValue, T newValue, KeyList<KT, T> sender);
public delegate void Added(T value, KeyList<KT, T> sender);
public delegate void Removed(KT key, T value, KeyList<KT, T> sender);
public delegate void Cleared(KeyList<KT, T> sender);
public event Modified OnModified;
public event Removed OnRemoved;
public event Cleared OnCleared;
public event Added OnAdd;
bool removableList;
public object SyncRoot
get
{
get
{
return syncRoot;
}
return syncRoot;
}
}
public T Take(KT key)
public T Take(KT key)
{
if (dic.ContainsKey(key))
{
var v = dic[key];
Remove(key);
return v;
}
else
return default(T);
}
public void Sort(Func<KeyValuePair<KT, T>, object> keySelector)
{
dic = dic.OrderBy(keySelector).ToDictionary(x => x.Key, x => x.Value);
}
public T[] ToArray()
{
var a = new T[Count];
dic.Values.CopyTo(a, 0);
return a;
}
public void Add(KT key, T value)
{
lock (syncRoot)
{
if (removableList)
if (value != null)
((IDestructible)value).OnDestroy += ItemDestroyed;
if (dic.ContainsKey(key))
{
var v = dic[key];
Remove(key);
return v;
var oldValue = dic[key];
if (removableList)
if (oldValue != null)
((IDestructible)oldValue).OnDestroy -= ItemDestroyed;
dic[key] = value;
if (OnModified != null)
OnModified(key, oldValue, value, this);
}
else
{
dic.Add(key, value);
if (OnAdd != null)
OnAdd(value, this);
}
}
}
private void ItemDestroyed(object sender)
{
RemoveValue((T)sender);
}
public void RemoveValue(T value)
{
var toRemove = new List<KT>();
foreach (var kv in dic)
if (kv.Value.Equals(value))
toRemove.Add(kv.Key);
foreach (var k in toRemove)
Remove(k);
}
public T this[KT key]
{
get
{
if (dic.ContainsKey(key))
return dic[key];
else
return default(T);
}
public void Sort(Func<KeyValuePair<KT, T>, object> keySelector)
set
{
dic = dic.OrderBy(keySelector).ToDictionary(x => x.Key, x => x.Value);
Add(key, value);
}
}
public T[] ToArray()
public IEnumerator<KeyValuePair<KT, T>> GetEnumerator()
{
return dic.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return dic.GetEnumerator();
}
public void Clear()
{
if (removableList)
foreach (IDestructible v in dic.Values)
if (v != null)
v.OnDestroy -= ItemDestroyed;
lock (syncRoot)
dic.Clear();
if (OnCleared != null)
OnCleared(this);
}
public Dictionary<KT, T>.KeyCollection Keys
{
get { return dic.Keys; }
}
public Dictionary<KT, T>.ValueCollection Values
{
get
{
var a = new T[Count];
dic.Values.CopyTo(a, 0);
return a;
return dic.Values;
}
}
public void Add(KT key, T value)
{
lock (syncRoot)
{
if (removableList)
if (value != null)
((IDestructible)value).OnDestroy += ItemDestroyed;
public void Remove(KT key)
{
if (!dic.ContainsKey(key))
return;
if (dic.ContainsKey(key))
{
var oldValue = dic[key];
if (removableList)
if (oldValue != null)
((IDestructible)oldValue).OnDestroy -= ItemDestroyed;
dic[key] = value;
if (OnModified != null)
OnModified(key, oldValue, value, this);
}
else
{
dic.Add(key, value);
var value = dic[key];
if (OnAdd != null)
OnAdd(value, this);
}
}
}
if (removableList)
if (value != null)
((IDestructible)value).OnDestroy -= ItemDestroyed;
private void ItemDestroyed(object sender)
{
RemoveValue((T)sender);
}
lock (syncRoot)
dic.Remove(key);
public void RemoveValue(T value)
{
var toRemove = new List<KT>();
foreach (var kv in dic)
if (kv.Value.Equals(value))
toRemove.Add(kv.Key);
if (OnRemoved != null)
OnRemoved(key, value, this);
}
foreach (var k in toRemove)
Remove(k);
}
public object Owner
{
get;
set;
}
public T this[KT key]
{
get
{
if (dic.ContainsKey(key))
return dic[key];
else
return default(T);
}
set
{
Add(key, value);
}
}
public IEnumerator<KeyValuePair<KT, T>> GetEnumerator()
{
return dic.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return dic.GetEnumerator();
}
public int Count
{
get { return dic.Count; }
}
public bool Contains(KT Key)
{
return dic.ContainsKey(Key);
}
public bool ContainsKey(KT Key)
{
return dic.ContainsKey(Key);
}
public bool ContainsValue(T Value)
{
return dic.ContainsValue(Value);
}
public void Clear()
{
if (removableList)
foreach (IDestructible v in dic.Values)
if (v != null)
v.OnDestroy -= ItemDestroyed;
lock (syncRoot)
dic.Clear();
if (OnCleared != null)
OnCleared(this);
}
public Dictionary<KT, T>.KeyCollection Keys
{
get { return dic.Keys; }
}
public Dictionary<KT, T>.ValueCollection Values
{
get
{
return dic.Values;
}
}
public void Remove(KT key)
{
if (!dic.ContainsKey(key))
return;
var value = dic[key];
if (removableList)
if (value != null)
((IDestructible)value).OnDestroy -= ItemDestroyed;
lock (syncRoot)
dic.Remove(key);
if (OnRemoved != null)
OnRemoved(key, value, this);
}
public object Owner
{
get;
set;
}
public int Count
{
get { return dic.Count; }
}
public bool Contains(KT Key)
{
return dic.ContainsKey(Key);
}
public bool ContainsKey(KT Key)
{
return dic.ContainsKey(Key);
}
public bool ContainsValue(T Value)
{
return dic.ContainsValue(Value);
}
public KeyList(object owner = null)
{
#if NETSTANDARD
removableList = (typeof(IDestructible).GetTypeInfo().IsAssignableFrom(typeof(T).GetTypeInfo()));
public KeyList(object owner = null)
{
#if NETSTANDARD
removableList = (typeof(IDestructible).GetTypeInfo().IsAssignableFrom(typeof(T).GetTypeInfo()));
#else
removableList = (typeof(IDestructible).IsAssignableFrom(typeof(T)));
#endif
this.Owner = owner;
this.Owner = owner;
if (typeof(KT) == typeof(string))
dic = (Dictionary<KT, T>)(object)new Dictionary<string, T>(StringComparer.OrdinalIgnoreCase);
else
dic = new Dictionary<KT, T>();
}
if (typeof(KT) == typeof(string))
dic = (Dictionary<KT, T>)(object)new Dictionary<string, T>(StringComparer.OrdinalIgnoreCase);
else
dic = new Dictionary<KT, T>();
}
}
}
+2 -4
View File
@@ -28,10 +28,8 @@ using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Esiur.Data
namespace Esiur.Data;
public class NotModified
{
public class NotModified
{
}
}
+27 -28
View File
@@ -2,34 +2,33 @@
using System.Collections.Generic;
using System.Text;
namespace Esiur.Data
{
public class PropertyValue
{
/// <summary>
/// Get or set the value.
/// </summary>
public object Value { get; set; }
/// <summary>
/// Get or set date of modification or occurrence.
/// </summary>
public DateTime Date { get; set; }
/// <summary>
/// Get or set property age.
/// </summary>
public ulong Age { get; set; }
namespace Esiur.Data;
/// <summary>
/// Create an instance of PropertyValue.
/// </summary>
/// <param name="value">Value.</param>
/// <param name="age">Age.</param>
/// <param name="date">Date.</param>
public PropertyValue(object value, ulong age, DateTime date)
{
Value = value;
Age = age;
Date = date;
}
public class PropertyValue
{
/// <summary>
/// Get or set the value.
/// </summary>
public object Value { get; set; }
/// <summary>
/// Get or set date of modification or occurrence.
/// </summary>
public DateTime Date { get; set; }
/// <summary>
/// Get or set property age.
/// </summary>
public ulong Age { get; set; }
/// <summary>
/// Create an instance of PropertyValue.
/// </summary>
/// <param name="value">Value.</param>
/// <param name="age">Age.</param>
/// <param name="date">Date.</param>
public PropertyValue(object value, ulong age, DateTime date)
{
Value = value;
Age = age;
Date = date;
}
}
+3 -5
View File
@@ -2,10 +2,8 @@
using System.Collections.Generic;
using System.Text;
namespace Esiur.Data
namespace Esiur.Data;
public class Record : KeyList<string, object>, IRecord
{
public class Record: KeyList<string, object>, IRecord
{
}
}
+7 -8
View File
@@ -2,13 +2,12 @@
using System.Collections.Generic;
using System.Text;
namespace Esiur.Data
namespace Esiur.Data;
public enum RecordComparisonResult : byte
{
public enum RecordComparisonResult : byte
{
Null,
Record,
RecordSameType,
Same
}
Null,
Record,
RecordSameType,
Same
}
+6 -7
View File
@@ -2,12 +2,11 @@
using System.Collections.Generic;
using System.Text;
namespace Esiur.Data
namespace Esiur.Data;
public enum ResourceArrayType
{
public enum ResourceArrayType
{
Dynamic = 0x0,
Static = 0x10,
Wrapper = 0x20,
}
Dynamic = 0x0,
Static = 0x10,
Wrapper = 0x20,
}
+7 -8
View File
@@ -2,13 +2,12 @@
using System.Collections.Generic;
using System.Text;
namespace Esiur.Data
namespace Esiur.Data;
public enum ResourceComparisonResult
{
public enum ResourceComparisonResult
{
Null, // null
Distributed, // resource is distributed
Local, // resource is local
Same, // Same as previous
}
Null, // null
Distributed, // resource is distributed
Local, // resource is local
Same, // Same as previous
}
+55 -55
View File
@@ -1,5 +1,4 @@
using Esiur.Net.IIP;
using Esiur.Resource;
/*
Copyright (c) 2017-2021 Ahmed Kh. Zamil
@@ -24,76 +23,77 @@ SOFTWARE.
*/
using Esiur.Net.IIP;
using Esiur.Resource;
using System;
using System.Collections.Generic;
using System.Text;
using System.Text.Json;
using System.Text.Json.Serialization;
namespace Esiur.Data
namespace Esiur.Data;
class ResourceJsonConverter : JsonConverter<IResource>
{
class ResourceJsonConverter : JsonConverter<IResource>
public override IResource Read(
ref Utf8JsonReader reader,
Type typeToConvert,
JsonSerializerOptions options)
{
public override IResource Read(
ref Utf8JsonReader reader,
Type typeToConvert,
JsonSerializerOptions options)
{
return (IResource)JsonSerializer.Deserialize(ref reader,typeof(IResource), options);
}
public override void Write(
Utf8JsonWriter writer,
IResource resource,
JsonSerializerOptions options)
{
writer.WriteStartObject();
foreach (var pt in resource.Instance.Template.Properties)
{
var rt = pt.PropertyInfo.GetValue(resource, null);
if (rt is DistributedPropertyContext)
continue;
writer.WritePropertyName(options.PropertyNamingPolicy?.ConvertName(pt.Name) ?? pt.Name);
if (rt is IResource)
JsonSerializer.Serialize(writer, (IResource) rt, options);
else
JsonSerializer.Serialize(writer, rt, options);
}
writer.WriteEndObject();
}
return (IResource)JsonSerializer.Deserialize(ref reader, typeof(IResource), options);
}
public class DoubleJsonConverter : JsonConverter<double>
public override void Write(
Utf8JsonWriter writer,
IResource resource,
JsonSerializerOptions options)
{
public override double Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
if (reader.TokenType == JsonTokenType.String && reader.GetString() == "NaN")
{
return double.NaN;
}
return reader.GetDouble(); // JsonException thrown if reader.TokenType != JsonTokenType.Number
}
writer.WriteStartObject();
public override void Write(Utf8JsonWriter writer, double value, JsonSerializerOptions options)
foreach (var pt in resource.Instance.Template.Properties)
{
if (double.IsNaN(value))
{
writer.WriteStringValue("NaN");
}
var rt = pt.PropertyInfo.GetValue(resource, null);
if (rt is DistributedPropertyContext)
continue;
writer.WritePropertyName(options.PropertyNamingPolicy?.ConvertName(pt.Name) ?? pt.Name);
if (rt is IResource)
JsonSerializer.Serialize(writer, (IResource)rt, options);
else
{
writer.WriteNumberValue(value);
}
JsonSerializer.Serialize(writer, rt, options);
}
writer.WriteEndObject();
}
}
public class DoubleJsonConverter : JsonConverter<double>
{
public override double Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
if (reader.TokenType == JsonTokenType.String && reader.GetString() == "NaN")
{
return double.NaN;
}
return reader.GetDouble(); // JsonException thrown if reader.TokenType != JsonTokenType.Number
}
public override void Write(Utf8JsonWriter writer, double value, JsonSerializerOptions options)
{
if (double.IsNaN(value))
{
writer.WriteStringValue("NaN");
}
else
{
writer.WriteNumberValue(value);
}
}
}
+229 -230
View File
@@ -30,245 +30,244 @@ using System.Collections;
using Esiur.Core;
using System.Reflection;
namespace Esiur.Data
namespace Esiur.Data;
public class ResourceList<T, ST> : IEnumerable<T>, ICollection, ICollection<T>
{
public class ResourceList<T, ST> : IEnumerable<T>, ICollection, ICollection<T>
private readonly object syncRoot = new object();
private List<T> list = new List<T>();
public delegate void Modified(ST sender, int index, T oldValue, T newValue);
public delegate void Added(ST sender, T value);
public delegate void Removed(ST sender, int index, T value);
public delegate void Cleared(ST sender);
public event Modified OnModified;
public event Removed OnRemoved;
public event Cleared OnCleared;
public event Added OnAdd;
ST state;
public void Sort()
{
list.Sort();
}
private readonly object syncRoot = new object();
private List<T> list = new List<T>();
public void Sort(IComparer<T> comparer)
{
list.Sort(comparer);
}
public delegate void Modified(ST sender, int index, T oldValue, T newValue);
public delegate void Added(ST sender, T value);
public delegate void Removed(ST sender, int index, T value);
public delegate void Cleared(ST sender);
public void Sort(Comparison<T> comparison)
{
list.Sort(comparison);
}
public IEnumerable<T> Where(Func<T, bool> predicate)
{
return list.Where(predicate);
}
public event Modified OnModified;
public event Removed OnRemoved;
public event Cleared OnCleared;
public event Added OnAdd;
/// <summary>
/// Convert AutoList to array
/// </summary>
/// <returns>Array</returns>
public T[] ToArray()
{
// list.OrderBy()
return list.ToArray();
}
ST state;
/// <summary>
/// Create a new instance of AutoList
/// </summary>
/// <param name="state">State object to be included when an event is raised.</param>
public ResourceList(ST state)
{
this.state = state;
}
public void Sort()
/// <summary>
/// Create a new instance of AutoList
/// </summary>
/// <param name="values">Populate the list with items</param>
/// <returns></returns>
public ResourceList(ST state, T[] values)
{
this.state = state;
AddRange(values);
}
/// <summary>
/// Synchronization lock of the list
/// </summary>
public object SyncRoot
{
get
{
list.Sort();
}
public void Sort(IComparer<T> comparer)
{
list.Sort(comparer);
}
public void Sort(Comparison<T> comparison)
{
list.Sort(comparison);
}
public IEnumerable<T> Where(Func<T, bool> predicate)
{
return list.Where(predicate);
}
/// <summary>
/// Convert AutoList to array
/// </summary>
/// <returns>Array</returns>
public T[] ToArray()
{
// list.OrderBy()
return list.ToArray();
}
/// <summary>
/// Create a new instance of AutoList
/// </summary>
/// <param name="state">State object to be included when an event is raised.</param>
public ResourceList(ST state)
{
this.state = state;
}
/// <summary>
/// Create a new instance of AutoList
/// </summary>
/// <param name="values">Populate the list with items</param>
/// <returns></returns>
public ResourceList(ST state, T[] values)
{
this.state = state;
AddRange(values);
}
/// <summary>
/// Synchronization lock of the list
/// </summary>
public object SyncRoot
{
get
{
return syncRoot;
}
}
/// <summary>
/// First item in the list
/// </summary>
public T First()
{
return list.First();
}
/// <summary>
/// Get an item at a specified index
/// </summary>
public T this[int index]
{
get
{
return list[index];
}
set
{
var oldValue = list[index];
lock (syncRoot)
list[index] = value;
OnModified?.Invoke(state, index, oldValue, value);
}
}
/// <summary>
/// Add item to the list
/// </summary>
public void Add(T value)
{
lock (syncRoot)
list.Add(value);
OnAdd?.Invoke(state, value);
}
/// <summary>
/// Add an array of items to the list
/// </summary>
public void AddRange(T[] values)
{
foreach (var v in values)
Add(v);
}
private void ItemDestroyed(object sender)
{
Remove((T)sender);
}
/// <summary>
/// Clear the list
/// </summary>
public void Clear()
{
lock (syncRoot)
list.Clear();
OnCleared?.Invoke(state);
}
/// <summary>
/// Remove an item from the list
/// <param name="value">Item to remove</param>
/// </summary>
public void Remove(T value)
{
var index = 0;
lock (syncRoot)
{
index = list.IndexOf(value);
if (index == -1)
return;
list.RemoveAt(index);
}
OnRemoved?.Invoke(state, index, value);
}
/// <summary>
/// Number of items in the list
/// </summary>
public int Count
{
get { return list.Count; }
}
public bool IsSynchronized => (list as ICollection).IsSynchronized;
public bool IsReadOnly => throw new NotImplementedException();
/// <summary>
/// Check if an item exists in the list
/// </summary>
/// <param name="value">Item to check if exists</param>
public bool Contains(T value)
{
return list.Contains(value);
}
/// <summary>
/// Check if any item of the given array is in the list
/// </summary>
/// <param name="values">Array of items</param>
public bool ContainsAny(T[] values)
{
foreach (var v in values)
if (list.Contains(v))
return true;
return false;
}
/// <summary>
/// Check if any item of the given list is in the list
/// </summary>
/// <param name="values">List of items</param>
public bool ContainsAny(AutoList<T, ST> values)
{
foreach (var v in values)
if (list.Contains((T)v))
return true;
return false;
}
public IEnumerator<T> GetEnumerator()
{
return ((IEnumerable<T>)list).GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return ((IEnumerable<T>)list).GetEnumerator();
}
public void CopyTo(Array array, int index)
{
(list as ICollection).CopyTo(array, index);
}
public void CopyTo(T[] array, int arrayIndex)
{
list.CopyTo(array, arrayIndex);
}
bool ICollection<T>.Remove(T item)
{
return list.Remove(item);
return syncRoot;
}
}
/// <summary>
/// First item in the list
/// </summary>
public T First()
{
return list.First();
}
/// <summary>
/// Get an item at a specified index
/// </summary>
public T this[int index]
{
get
{
return list[index];
}
set
{
var oldValue = list[index];
lock (syncRoot)
list[index] = value;
OnModified?.Invoke(state, index, oldValue, value);
}
}
/// <summary>
/// Add item to the list
/// </summary>
public void Add(T value)
{
lock (syncRoot)
list.Add(value);
OnAdd?.Invoke(state, value);
}
/// <summary>
/// Add an array of items to the list
/// </summary>
public void AddRange(T[] values)
{
foreach (var v in values)
Add(v);
}
private void ItemDestroyed(object sender)
{
Remove((T)sender);
}
/// <summary>
/// Clear the list
/// </summary>
public void Clear()
{
lock (syncRoot)
list.Clear();
OnCleared?.Invoke(state);
}
/// <summary>
/// Remove an item from the list
/// <param name="value">Item to remove</param>
/// </summary>
public void Remove(T value)
{
var index = 0;
lock (syncRoot)
{
index = list.IndexOf(value);
if (index == -1)
return;
list.RemoveAt(index);
}
OnRemoved?.Invoke(state, index, value);
}
/// <summary>
/// Number of items in the list
/// </summary>
public int Count
{
get { return list.Count; }
}
public bool IsSynchronized => (list as ICollection).IsSynchronized;
public bool IsReadOnly => throw new NotImplementedException();
/// <summary>
/// Check if an item exists in the list
/// </summary>
/// <param name="value">Item to check if exists</param>
public bool Contains(T value)
{
return list.Contains(value);
}
/// <summary>
/// Check if any item of the given array is in the list
/// </summary>
/// <param name="values">Array of items</param>
public bool ContainsAny(T[] values)
{
foreach (var v in values)
if (list.Contains(v))
return true;
return false;
}
/// <summary>
/// Check if any item of the given list is in the list
/// </summary>
/// <param name="values">List of items</param>
public bool ContainsAny(AutoList<T, ST> values)
{
foreach (var v in values)
if (list.Contains((T)v))
return true;
return false;
}
public IEnumerator<T> GetEnumerator()
{
return ((IEnumerable<T>)list).GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return ((IEnumerable<T>)list).GetEnumerator();
}
public void CopyTo(Array array, int index)
{
(list as ICollection).CopyTo(array, index);
}
public void CopyTo(T[] array, int arrayIndex)
{
list.CopyTo(array, arrayIndex);
}
bool ICollection<T>.Remove(T item)
{
return list.Remove(item);
}
}
+159 -160
View File
@@ -31,190 +31,189 @@ using System.Text;
using System.Reflection;
using System.Linq;
namespace Esiur.Data
namespace Esiur.Data;
public class StringKeyList : IEnumerable<KeyValuePair<string, string>>
{
public class StringKeyList : IEnumerable<KeyValuePair<string, string>>
//private List<string> m_keys = new List<string>();
//private List<string> m_values = new List<string>();
private List<KeyValuePair<string, string>> m_Variables = new List<KeyValuePair<string, string>>();
private bool allowMultiple;
public delegate void Modified(string Key, string NewValue);
public event Modified OnModified;
public StringKeyList(bool AllowMultipleValues = false)
{
//private List<string> m_keys = new List<string>();
//private List<string> m_values = new List<string>();
allowMultiple = AllowMultipleValues;
}
private List<KeyValuePair<string, string>> m_Variables = new List<KeyValuePair<string, string>>();
public void Add(string Key, string Value)
{
if (OnModified != null)
OnModified(Key, Value);
private bool allowMultiple;
var key = Key.ToLower();
public delegate void Modified(string Key, string NewValue);
public event Modified OnModified;
public StringKeyList(bool AllowMultipleValues = false)
if (!allowMultiple)
{
allowMultiple = AllowMultipleValues;
}
public void Add(string Key, string Value)
{
if (OnModified != null)
OnModified(Key, Value);
var key = Key.ToLower();
if (!allowMultiple)
{
foreach(var kv in m_Variables)
{
if (kv.Key.ToLower() == key)
{
m_Variables.Remove(kv);
break;
}
}
}
m_Variables.Add(new KeyValuePair<string, string>(Key, Value));
}
public string this[string Key]
{
get
{
var key = Key.ToLower();
foreach (var kv in m_Variables)
if (kv.Key.ToLower() == key)
return kv.Value;
return null;
}
set
{
var key = Key.ToLower();
var toRemove = m_Variables.Where(x => x.Key.ToLower() == key).ToArray();
foreach (var item in toRemove)
m_Variables.Remove(item);
m_Variables.Add(new KeyValuePair<string, string>(Key, value));
OnModified?.Invoke(Key, value);
}
}
IEnumerator<KeyValuePair<string, string>> IEnumerable<KeyValuePair<string, string>>.GetEnumerator()
{
//return m_keys.GetEnumerator();
return m_Variables.GetEnumerator();
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return m_Variables.GetEnumerator();
}
public void Clear()
{
if (OnModified != null)
OnModified(null, null);
m_Variables.Clear();
}
/*
public string[] Keys
{
get
{
return m_keys.ToArray();
}
}
//public Dictionary<string, string>.ValueCollection Values
public string[] Values
{
get
{
//return m_Variables.Values;
return m_values.ToArray();
}
}
*/
public List<string> GetValues(string Key)
{
var key = Key.ToLower();
List<string> values = new List<string>();
foreach (var kv in m_Variables)
if (kv.Key.ToLower() == key)
values.Add(kv.Value);
return values;
}
public void RemoveAll(string Key)
{
while (Remove(Key)){}
}
public bool Remove(string Key)
{
var key = Key.ToLower();
foreach(var kv in m_Variables)
{
if (kv.Key.ToLower() == key)
{
if (OnModified != null)
OnModified(Key, null);
m_Variables.Remove(kv);
return true;
break;
}
}
return false;
}
public int Count
{
get { return m_Variables.Count; }
}
public bool ContainsKey(string Key)
m_Variables.Add(new KeyValuePair<string, string>(Key, Value));
}
public string this[string Key]
{
get
{
var key = Key.ToLower();
foreach (var kv in m_Variables)
if (kv.Key.ToLower() == key)
return true;
return false;
}
return kv.Value;
/*
public bool ContainsKey(string Key)
return null;
}
set
{
//return m_Variables.ContainsKey(Key);
return m_keys.Contains(Key.ToLower());
}
*/
var key = Key.ToLower();
public bool ContainsValue(string Value)
{
var value = Value.ToLower();
foreach (var kv in m_Variables)
if (kv.Value.ToLower() == value)
return true;
return false;
}
var toRemove = m_Variables.Where(x => x.Key.ToLower() == key).ToArray();
//internal KeyList()
//{
// m_Session = Session;
// m_Server = Server;
//}
foreach (var item in toRemove)
m_Variables.Remove(item);
m_Variables.Add(new KeyValuePair<string, string>(Key, value));
OnModified?.Invoke(Key, value);
}
}
IEnumerator<KeyValuePair<string, string>> IEnumerable<KeyValuePair<string, string>>.GetEnumerator()
{
//return m_keys.GetEnumerator();
return m_Variables.GetEnumerator();
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return m_Variables.GetEnumerator();
}
public void Clear()
{
if (OnModified != null)
OnModified(null, null);
m_Variables.Clear();
}
/*
public string[] Keys
{
get
{
return m_keys.ToArray();
}
}
//public Dictionary<string, string>.ValueCollection Values
public string[] Values
{
get
{
//return m_Variables.Values;
return m_values.ToArray();
}
}
*/
public List<string> GetValues(string Key)
{
var key = Key.ToLower();
List<string> values = new List<string>();
foreach (var kv in m_Variables)
if (kv.Key.ToLower() == key)
values.Add(kv.Value);
return values;
}
public void RemoveAll(string Key)
{
while (Remove(Key)) { }
}
public bool Remove(string Key)
{
var key = Key.ToLower();
foreach (var kv in m_Variables)
{
if (kv.Key.ToLower() == key)
{
if (OnModified != null)
OnModified(Key, null);
m_Variables.Remove(kv);
return true;
}
}
return false;
}
public int Count
{
get { return m_Variables.Count; }
}
public bool ContainsKey(string Key)
{
var key = Key.ToLower();
foreach (var kv in m_Variables)
if (kv.Key.ToLower() == key)
return true;
return false;
}
/*
public bool ContainsKey(string Key)
{
//return m_Variables.ContainsKey(Key);
return m_keys.Contains(Key.ToLower());
}
*/
public bool ContainsValue(string Value)
{
var value = Value.ToLower();
foreach (var kv in m_Variables)
if (kv.Value.ToLower() == value)
return true;
return false;
}
//internal KeyList()
//{
// m_Session = Session;
// m_Server = Server;
//}
}
+139 -140
View File
@@ -34,148 +34,147 @@ using Esiur.Core;
using System.Reflection;
using System.Dynamic;
namespace Esiur.Data
namespace Esiur.Data;
public class Structure : IEnumerable<KeyValuePair<string, object>>
{
public class Structure : IEnumerable<KeyValuePair<string, object>>
public struct StructureMetadata
{
public string[] Keys;
public DataType[] Types;
}
private Dictionary<string, object> dic = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);
private object syncRoot = new object();
public bool ContainsKey(string key)
{
return dic.ContainsKey(key);
}
public override string ToString()
{
var rt = "";
foreach (var kv in dic)
rt += kv.Key + ": " + kv.Value.ToString() + " \r\n";
return rt.TrimEnd('\r', '\n');
}
public Structure(Structure source)
{
dic = source.dic;
}
public Structure()
{
public struct StructureMetadata
{
public string[] Keys;
public DataType[] Types;
}
private Dictionary<string, object> dic = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);
private object syncRoot = new object();
public bool ContainsKey(string key)
{
return dic.ContainsKey(key);
}
public override string ToString()
{
var rt = "";
foreach (var kv in dic)
rt += kv.Key + ": " + kv.Value.ToString() + " \r\n";
return rt.TrimEnd('\r', '\n');
}
public Structure(Structure source)
{
dic = source.dic;
}
public Structure()
{
}
public static Structure FromStructure(Structure source, Type destinationType)
{
var rt = Activator.CreateInstance(destinationType) as Structure;
rt.dic = source.dic;
return rt;
}
public static T FromStructure<T>(Structure source) where T : Structure
{
var rt = Activator.CreateInstance<T>();
rt.dic = source.dic;
return rt;
}
public static explicit operator Structure(ExpandoObject obj) => FromDynamic(obj);
public static Structure FromDynamic(ExpandoObject obj)
{
var rt = new Structure();
foreach (var kv in obj)
rt[kv.Key] = kv.Value;
return rt;
}
public static Structure FromObject(object obj)
{
var type = obj.GetType();
if (obj is Structure)
return obj as Structure;
else //if (Codec.IsAnonymous(type))
{
var st = new Structure();
var pi = type.GetTypeInfo().GetProperties().Where(x=>x.CanRead);
foreach (var p in pi)
st[p.Name] = p.GetValue(obj);
var fi = type.GetTypeInfo().GetFields().Where(x => x.IsPublic);
foreach (var f in fi)
st[f.Name] = f.GetValue(obj);
return st;
}
//else
// return null;
}
public IEnumerator<KeyValuePair<string, object>> GetEnumerator()
{
return dic.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return dic.GetEnumerator();
}
public int Length
{
get { return dic.Count; }
}
public KeyValuePair<string, object> At(int index)
{
return dic.ElementAt(index);
}
public object SyncRoot
{
get { return syncRoot; }
}
public string[] GetKeys() => dic.Keys.ToArray();//GetKeys()
//{
// return dic.Keys.ToArray();
//}
public Structure Add(string key, object value)
{
if (dic.ContainsKey(key))
dic[key] = value;
else
dic.Add(key, value);
return this;
}
public object this[string index]
{
get
{
if (dic.ContainsKey(index))
return dic[index];
else
return null;
}
set
{
if (dic.ContainsKey(index))
dic[index] = value;
else
dic.Add(index, value);
}
}
}
public static Structure FromStructure(Structure source, Type destinationType)
{
var rt = Activator.CreateInstance(destinationType) as Structure;
rt.dic = source.dic;
return rt;
}
public static T FromStructure<T>(Structure source) where T : Structure
{
var rt = Activator.CreateInstance<T>();
rt.dic = source.dic;
return rt;
}
public static explicit operator Structure(ExpandoObject obj) => FromDynamic(obj);
public static Structure FromDynamic(ExpandoObject obj)
{
var rt = new Structure();
foreach (var kv in obj)
rt[kv.Key] = kv.Value;
return rt;
}
public static Structure FromObject(object obj)
{
var type = obj.GetType();
if (obj is Structure)
return obj as Structure;
else //if (Codec.IsAnonymous(type))
{
var st = new Structure();
var pi = type.GetTypeInfo().GetProperties().Where(x => x.CanRead);
foreach (var p in pi)
st[p.Name] = p.GetValue(obj);
var fi = type.GetTypeInfo().GetFields().Where(x => x.IsPublic);
foreach (var f in fi)
st[f.Name] = f.GetValue(obj);
return st;
}
//else
// return null;
}
public IEnumerator<KeyValuePair<string, object>> GetEnumerator()
{
return dic.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return dic.GetEnumerator();
}
public int Length
{
get { return dic.Count; }
}
public KeyValuePair<string, object> At(int index)
{
return dic.ElementAt(index);
}
public object SyncRoot
{
get { return syncRoot; }
}
public string[] GetKeys() => dic.Keys.ToArray();//GetKeys()
//{
// return dic.Keys.ToArray();
//}
public Structure Add(string key, object value)
{
if (dic.ContainsKey(key))
dic[key] = value;
else
dic.Add(key, value);
return this;
}
public object this[string index]
{
get
{
if (dic.ContainsKey(index))
return dic[index];
else
return null;
}
set
{
if (dic.ContainsKey(index))
dic[index] = value;
else
dic.Add(index, value);
}
}
}
+8 -9
View File
@@ -2,14 +2,13 @@
using System.Collections.Generic;
using System.Text;
namespace Esiur.Data
namespace Esiur.Data;
public enum StructureComparisonResult : byte
{
public enum StructureComparisonResult : byte
{
Null,
Structure,
StructureSameKeys,
StructureSameTypes,
Same
}
Null,
Structure,
StructureSameKeys,
StructureSameTypes,
Same
}