Coding style
This commit is contained in:
parent
c1e8637516
commit
2f74732924
@ -1,8 +1,7 @@
|
|||||||
namespace Unosquare.Swan.Abstractions
|
using System;
|
||||||
{
|
using System.Threading;
|
||||||
using System;
|
|
||||||
using System.Threading;
|
|
||||||
|
|
||||||
|
namespace Unosquare.Swan.Abstractions {
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Provides a generic implementation of an Atomic (interlocked) type
|
/// Provides a generic implementation of an Atomic (interlocked) type
|
||||||
///
|
///
|
||||||
@ -11,35 +10,29 @@
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <typeparam name="T">The structure type backed by a 64-bit value.</typeparam>
|
/// <typeparam name="T">The structure type backed by a 64-bit value.</typeparam>
|
||||||
public abstract class AtomicTypeBase<T> : IComparable, IComparable<T>, IComparable<AtomicTypeBase<T>>, IEquatable<T>, IEquatable<AtomicTypeBase<T>>
|
public abstract class AtomicTypeBase<T> : IComparable, IComparable<T>, IComparable<AtomicTypeBase<T>>, IEquatable<T>, IEquatable<AtomicTypeBase<T>>
|
||||||
where T : struct, IComparable, IComparable<T>, IEquatable<T>
|
where T : struct, IComparable, IComparable<T>, IEquatable<T> {
|
||||||
{
|
private Int64 _backingValue;
|
||||||
private long _backingValue;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="AtomicTypeBase{T}"/> class.
|
/// Initializes a new instance of the <see cref="AtomicTypeBase{T}"/> class.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="initialValue">The initial value.</param>
|
/// <param name="initialValue">The initial value.</param>
|
||||||
protected AtomicTypeBase(long initialValue)
|
protected AtomicTypeBase(Int64 initialValue) => this.BackingValue = initialValue;
|
||||||
{
|
|
||||||
BackingValue = initialValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the value.
|
/// Gets or sets the value.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public T Value
|
public T Value {
|
||||||
{
|
get => this.FromLong(this.BackingValue);
|
||||||
get => FromLong(BackingValue);
|
set => this.BackingValue = this.ToLong(value);
|
||||||
set => BackingValue = ToLong(value);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the backing value.
|
/// Gets or sets the backing value.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
protected long BackingValue
|
protected Int64 BackingValue {
|
||||||
{
|
get => Interlocked.Read(ref this._backingValue);
|
||||||
get => Interlocked.Read(ref _backingValue);
|
set => Interlocked.Exchange(ref this._backingValue, value);
|
||||||
set => Interlocked.Exchange(ref _backingValue, value);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -50,7 +43,7 @@
|
|||||||
/// <returns>
|
/// <returns>
|
||||||
/// The result of the operator.
|
/// The result of the operator.
|
||||||
/// </returns>
|
/// </returns>
|
||||||
public static bool operator ==(AtomicTypeBase<T> a, T b) => a?.Equals(b) == true;
|
public static Boolean operator ==(AtomicTypeBase<T> a, T b) => a?.Equals(b) == true;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Implements the operator !=.
|
/// Implements the operator !=.
|
||||||
@ -60,7 +53,7 @@
|
|||||||
/// <returns>
|
/// <returns>
|
||||||
/// The result of the operator.
|
/// The result of the operator.
|
||||||
/// </returns>
|
/// </returns>
|
||||||
public static bool operator !=(AtomicTypeBase<T> a, T b) => a?.Equals(b) == false;
|
public static Boolean operator !=(AtomicTypeBase<T> a, T b) => a?.Equals(b) == false;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Implements the operator >.
|
/// Implements the operator >.
|
||||||
@ -70,7 +63,7 @@
|
|||||||
/// <returns>
|
/// <returns>
|
||||||
/// The result of the operator.
|
/// The result of the operator.
|
||||||
/// </returns>
|
/// </returns>
|
||||||
public static bool operator >(AtomicTypeBase<T> a, T b) => a.CompareTo(b) > 0;
|
public static Boolean operator >(AtomicTypeBase<T> a, T b) => a.CompareTo(b) > 0;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Implements the operator <.
|
/// Implements the operator <.
|
||||||
@ -80,7 +73,7 @@
|
|||||||
/// <returns>
|
/// <returns>
|
||||||
/// The result of the operator.
|
/// The result of the operator.
|
||||||
/// </returns>
|
/// </returns>
|
||||||
public static bool operator <(AtomicTypeBase<T> a, T b) => a.CompareTo(b) < 0;
|
public static Boolean operator <(AtomicTypeBase<T> a, T b) => a.CompareTo(b) < 0;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Implements the operator >=.
|
/// Implements the operator >=.
|
||||||
@ -90,7 +83,7 @@
|
|||||||
/// <returns>
|
/// <returns>
|
||||||
/// The result of the operator.
|
/// The result of the operator.
|
||||||
/// </returns>
|
/// </returns>
|
||||||
public static bool operator >=(AtomicTypeBase<T> a, T b) => a.CompareTo(b) >= 0;
|
public static Boolean operator >=(AtomicTypeBase<T> a, T b) => a.CompareTo(b) >= 0;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Implements the operator <=.
|
/// Implements the operator <=.
|
||||||
@ -100,7 +93,7 @@
|
|||||||
/// <returns>
|
/// <returns>
|
||||||
/// The result of the operator.
|
/// The result of the operator.
|
||||||
/// </returns>
|
/// </returns>
|
||||||
public static bool operator <=(AtomicTypeBase<T> a, T b) => a.CompareTo(b) <= 0;
|
public static Boolean operator <=(AtomicTypeBase<T> a, T b) => a.CompareTo(b) <= 0;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Implements the operator ++.
|
/// Implements the operator ++.
|
||||||
@ -109,9 +102,8 @@
|
|||||||
/// <returns>
|
/// <returns>
|
||||||
/// The result of the operator.
|
/// The result of the operator.
|
||||||
/// </returns>
|
/// </returns>
|
||||||
public static AtomicTypeBase<T> operator ++(AtomicTypeBase<T> instance)
|
public static AtomicTypeBase<T> operator ++(AtomicTypeBase<T> instance) {
|
||||||
{
|
_ = Interlocked.Increment(ref instance._backingValue);
|
||||||
Interlocked.Increment(ref instance._backingValue);
|
|
||||||
return instance;
|
return instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -122,9 +114,8 @@
|
|||||||
/// <returns>
|
/// <returns>
|
||||||
/// The result of the operator.
|
/// The result of the operator.
|
||||||
/// </returns>
|
/// </returns>
|
||||||
public static AtomicTypeBase<T> operator --(AtomicTypeBase<T> instance)
|
public static AtomicTypeBase<T> operator --(AtomicTypeBase<T> instance) {
|
||||||
{
|
_ = Interlocked.Decrement(ref instance._backingValue);
|
||||||
Interlocked.Decrement(ref instance._backingValue);
|
|
||||||
return instance;
|
return instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -136,9 +127,8 @@
|
|||||||
/// <returns>
|
/// <returns>
|
||||||
/// The result of the operator.
|
/// The result of the operator.
|
||||||
/// </returns>
|
/// </returns>
|
||||||
public static AtomicTypeBase<T> operator +(AtomicTypeBase<T> instance, long operand)
|
public static AtomicTypeBase<T> operator +(AtomicTypeBase<T> instance, Int64 operand) {
|
||||||
{
|
instance.BackingValue += operand;
|
||||||
instance.BackingValue = instance.BackingValue + operand;
|
|
||||||
return instance;
|
return instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -150,9 +140,8 @@
|
|||||||
/// <returns>
|
/// <returns>
|
||||||
/// The result of the operator.
|
/// The result of the operator.
|
||||||
/// </returns>
|
/// </returns>
|
||||||
public static AtomicTypeBase<T> operator -(AtomicTypeBase<T> instance, long operand)
|
public static AtomicTypeBase<T> operator -(AtomicTypeBase<T> instance, Int64 operand) {
|
||||||
{
|
instance.BackingValue -= operand;
|
||||||
instance.BackingValue = instance.BackingValue - operand;
|
|
||||||
return instance;
|
return instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -162,16 +151,14 @@
|
|||||||
/// <param name="other">The other instance.</param>
|
/// <param name="other">The other instance.</param>
|
||||||
/// <returns>0 if equal, 1 if this instance is greater, -1 if this instance is less than.</returns>
|
/// <returns>0 if equal, 1 if this instance is greater, -1 if this instance is less than.</returns>
|
||||||
/// <exception cref="ArgumentException">When types are incompatible.</exception>
|
/// <exception cref="ArgumentException">When types are incompatible.</exception>
|
||||||
public int CompareTo(object other)
|
public Int32 CompareTo(Object other) {
|
||||||
{
|
switch(other) {
|
||||||
switch (other)
|
|
||||||
{
|
|
||||||
case null:
|
case null:
|
||||||
return 1;
|
return 1;
|
||||||
case AtomicTypeBase<T> atomic:
|
case AtomicTypeBase<T> atomic:
|
||||||
return BackingValue.CompareTo(atomic.BackingValue);
|
return this.BackingValue.CompareTo(atomic.BackingValue);
|
||||||
case T variable:
|
case T variable:
|
||||||
return Value.CompareTo(variable);
|
return this.Value.CompareTo(variable);
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new ArgumentException("Incompatible comparison types");
|
throw new ArgumentException("Incompatible comparison types");
|
||||||
@ -182,30 +169,28 @@
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="other">The other instance.</param>
|
/// <param name="other">The other instance.</param>
|
||||||
/// <returns>0 if equal, 1 if this instance is greater, -1 if this instance is less than.</returns>
|
/// <returns>0 if equal, 1 if this instance is greater, -1 if this instance is less than.</returns>
|
||||||
public int CompareTo(T other) => Value.CompareTo(other);
|
public Int32 CompareTo(T other) => this.Value.CompareTo(other);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Compares the value to the other instance.
|
/// Compares the value to the other instance.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="other">The other instance.</param>
|
/// <param name="other">The other instance.</param>
|
||||||
/// <returns>0 if equal, 1 if this instance is greater, -1 if this instance is less than.</returns>
|
/// <returns>0 if equal, 1 if this instance is greater, -1 if this instance is less than.</returns>
|
||||||
public int CompareTo(AtomicTypeBase<T> other) => BackingValue.CompareTo(other?.BackingValue ?? default);
|
public Int32 CompareTo(AtomicTypeBase<T> other) => this.BackingValue.CompareTo(other?.BackingValue ?? default);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Determines whether the specified <see cref="object" />, is equal to this instance.
|
/// Determines whether the specified <see cref="Object" />, is equal to this instance.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="other">The <see cref="object" /> to compare with this instance.</param>
|
/// <param name="other">The <see cref="Object" /> to compare with this instance.</param>
|
||||||
/// <returns>
|
/// <returns>
|
||||||
/// <c>true</c> if the specified <see cref="object" /> is equal to this instance; otherwise, <c>false</c>.
|
/// <c>true</c> if the specified <see cref="Object" /> is equal to this instance; otherwise, <c>false</c>.
|
||||||
/// </returns>
|
/// </returns>
|
||||||
public override bool Equals(object other)
|
public override Boolean Equals(Object other) {
|
||||||
{
|
switch(other) {
|
||||||
switch (other)
|
|
||||||
{
|
|
||||||
case AtomicTypeBase<T> atomic:
|
case AtomicTypeBase<T> atomic:
|
||||||
return Equals(atomic);
|
return this.Equals(atomic);
|
||||||
case T variable:
|
case T variable:
|
||||||
return Equals(variable);
|
return this.Equals(variable);
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
@ -217,27 +202,27 @@
|
|||||||
/// <returns>
|
/// <returns>
|
||||||
/// A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table.
|
/// A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table.
|
||||||
/// </returns>
|
/// </returns>
|
||||||
public override int GetHashCode() => BackingValue.GetHashCode();
|
public override Int32 GetHashCode() => this.BackingValue.GetHashCode();
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public bool Equals(AtomicTypeBase<T> other) =>
|
public Boolean Equals(AtomicTypeBase<T> other) =>
|
||||||
BackingValue == (other?.BackingValue ?? default);
|
this.BackingValue == (other?.BackingValue ?? default);
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public bool Equals(T other) => Equals(Value, other);
|
public Boolean Equals(T other) => Equals(this.Value, other);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Converts from a long value to the target type.
|
/// Converts from a long value to the target type.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="backingValue">The backing value.</param>
|
/// <param name="backingValue">The backing value.</param>
|
||||||
/// <returns>The value converted form a long value.</returns>
|
/// <returns>The value converted form a long value.</returns>
|
||||||
protected abstract T FromLong(long backingValue);
|
protected abstract T FromLong(Int64 backingValue);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Converts from the target type to a long value.
|
/// Converts from the target type to a long value.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="value">The value.</param>
|
/// <param name="value">The value.</param>
|
||||||
/// <returns>The value converted to a long value.</returns>
|
/// <returns>The value converted to a long value.</returns>
|
||||||
protected abstract long ToLong(T value);
|
protected abstract Int64 ToLong(T value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,22 +1,20 @@
|
|||||||
namespace Unosquare.Swan.Abstractions
|
using System;
|
||||||
{
|
using System.Threading;
|
||||||
using System;
|
|
||||||
using System.Threading;
|
|
||||||
|
|
||||||
|
namespace Unosquare.Swan.Abstractions {
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A threading <see cref="_backingTimer"/> implementation that executes at most one cycle at a time
|
/// A threading <see cref="_backingTimer"/> implementation that executes at most one cycle at a time
|
||||||
/// in a <see cref="ThreadPool"/> thread. Callback execution is NOT guaranteed to be carried out
|
/// in a <see cref="ThreadPool"/> thread. Callback execution is NOT guaranteed to be carried out
|
||||||
/// on the same <see cref="ThreadPool"/> thread every time the timer fires.
|
/// on the same <see cref="ThreadPool"/> thread every time the timer fires.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public sealed class ExclusiveTimer : IDisposable
|
public sealed class ExclusiveTimer : IDisposable {
|
||||||
{
|
private readonly Object _syncLock = new Object();
|
||||||
private readonly object _syncLock = new object();
|
|
||||||
private readonly ManualResetEventSlim _cycleDoneEvent = new ManualResetEventSlim(true);
|
private readonly ManualResetEventSlim _cycleDoneEvent = new ManualResetEventSlim(true);
|
||||||
private readonly Timer _backingTimer;
|
private readonly Timer _backingTimer;
|
||||||
private readonly TimerCallback _userCallback;
|
private readonly TimerCallback _userCallback;
|
||||||
private readonly AtomicBoolean _isDisposing = new AtomicBoolean();
|
private readonly AtomicBoolean _isDisposing = new AtomicBoolean();
|
||||||
private readonly AtomicBoolean _isDisposed = new AtomicBoolean();
|
private readonly AtomicBoolean _isDisposed = new AtomicBoolean();
|
||||||
private int _period;
|
private Int32 _period;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="ExclusiveTimer"/> class.
|
/// Initializes a new instance of the <see cref="ExclusiveTimer"/> class.
|
||||||
@ -25,11 +23,10 @@
|
|||||||
/// <param name="state">The state.</param>
|
/// <param name="state">The state.</param>
|
||||||
/// <param name="dueTime">The due time.</param>
|
/// <param name="dueTime">The due time.</param>
|
||||||
/// <param name="period">The period.</param>
|
/// <param name="period">The period.</param>
|
||||||
public ExclusiveTimer(TimerCallback timerCallback, object state, int dueTime, int period)
|
public ExclusiveTimer(TimerCallback timerCallback, Object state, Int32 dueTime, Int32 period) {
|
||||||
{
|
this._period = period;
|
||||||
_period = period;
|
this._userCallback = timerCallback;
|
||||||
_userCallback = timerCallback;
|
this._backingTimer = new Timer(this.InternalCallback, state ?? this, dueTime, Timeout.Infinite);
|
||||||
_backingTimer = new Timer(InternalCallback, state ?? this, dueTime, Timeout.Infinite);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -39,9 +36,8 @@
|
|||||||
/// <param name="state">The state.</param>
|
/// <param name="state">The state.</param>
|
||||||
/// <param name="dueTime">The due time.</param>
|
/// <param name="dueTime">The due time.</param>
|
||||||
/// <param name="period">The period.</param>
|
/// <param name="period">The period.</param>
|
||||||
public ExclusiveTimer(TimerCallback timerCallback, object state, TimeSpan dueTime, TimeSpan period)
|
public ExclusiveTimer(TimerCallback timerCallback, Object state, TimeSpan dueTime, TimeSpan period)
|
||||||
: this(timerCallback, state, Convert.ToInt32(dueTime.TotalMilliseconds), Convert.ToInt32(period.TotalMilliseconds))
|
: this(timerCallback, state, Convert.ToInt32(dueTime.TotalMilliseconds), Convert.ToInt32(period.TotalMilliseconds)) {
|
||||||
{
|
|
||||||
// placeholder
|
// placeholder
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -50,8 +46,7 @@
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="timerCallback">The timer callback.</param>
|
/// <param name="timerCallback">The timer callback.</param>
|
||||||
public ExclusiveTimer(TimerCallback timerCallback)
|
public ExclusiveTimer(TimerCallback timerCallback)
|
||||||
: this(timerCallback, null, Timeout.Infinite, Timeout.Infinite)
|
: this(timerCallback, null, Timeout.Infinite, Timeout.Infinite) {
|
||||||
{
|
|
||||||
// placholder
|
// placholder
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -61,9 +56,8 @@
|
|||||||
/// <param name="timerCallback">The timer callback.</param>
|
/// <param name="timerCallback">The timer callback.</param>
|
||||||
/// <param name="dueTime">The due time.</param>
|
/// <param name="dueTime">The due time.</param>
|
||||||
/// <param name="period">The period.</param>
|
/// <param name="period">The period.</param>
|
||||||
public ExclusiveTimer(Action timerCallback, int dueTime, int period)
|
public ExclusiveTimer(Action timerCallback, Int32 dueTime, Int32 period)
|
||||||
: this(s => { timerCallback?.Invoke(); }, null, dueTime, period)
|
: this(s => timerCallback?.Invoke(), null, dueTime, period) {
|
||||||
{
|
|
||||||
// placeholder
|
// placeholder
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -74,8 +68,7 @@
|
|||||||
/// <param name="dueTime">The due time.</param>
|
/// <param name="dueTime">The due time.</param>
|
||||||
/// <param name="period">The period.</param>
|
/// <param name="period">The period.</param>
|
||||||
public ExclusiveTimer(Action timerCallback, TimeSpan dueTime, TimeSpan period)
|
public ExclusiveTimer(Action timerCallback, TimeSpan dueTime, TimeSpan period)
|
||||||
: this(s => { timerCallback?.Invoke(); }, null, dueTime, period)
|
: this(s => timerCallback?.Invoke(), null, dueTime, period) {
|
||||||
{
|
|
||||||
// placeholder
|
// placeholder
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -84,8 +77,7 @@
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="timerCallback">The timer callback.</param>
|
/// <param name="timerCallback">The timer callback.</param>
|
||||||
public ExclusiveTimer(Action timerCallback)
|
public ExclusiveTimer(Action timerCallback)
|
||||||
: this(timerCallback, Timeout.Infinite, Timeout.Infinite)
|
: this(timerCallback, Timeout.Infinite, Timeout.Infinite) {
|
||||||
{
|
|
||||||
// placeholder
|
// placeholder
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -95,7 +87,7 @@
|
|||||||
/// <value>
|
/// <value>
|
||||||
/// <c>true</c> if this instance is disposing; otherwise, <c>false</c>.
|
/// <c>true</c> if this instance is disposing; otherwise, <c>false</c>.
|
||||||
/// </value>
|
/// </value>
|
||||||
public bool IsDisposing => _isDisposing.Value;
|
public Boolean IsDisposing => this._isDisposing.Value;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets a value indicating whether this instance is disposed.
|
/// Gets a value indicating whether this instance is disposed.
|
||||||
@ -103,18 +95,17 @@
|
|||||||
/// <value>
|
/// <value>
|
||||||
/// <c>true</c> if this instance is disposed; otherwise, <c>false</c>.
|
/// <c>true</c> if this instance is disposed; otherwise, <c>false</c>.
|
||||||
/// </value>
|
/// </value>
|
||||||
public bool IsDisposed => _isDisposed.Value;
|
public Boolean IsDisposed => this._isDisposed.Value;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Changes the start time and the interval between method invocations for the internal timer.
|
/// Changes the start time and the interval between method invocations for the internal timer.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="dueTime">The due time.</param>
|
/// <param name="dueTime">The due time.</param>
|
||||||
/// <param name="period">The period.</param>
|
/// <param name="period">The period.</param>
|
||||||
public void Change(int dueTime, int period)
|
public void Change(Int32 dueTime, Int32 period) {
|
||||||
{
|
this._period = period;
|
||||||
_period = period;
|
|
||||||
|
|
||||||
_backingTimer.Change(dueTime, Timeout.Infinite);
|
_ = this._backingTimer.Change(dueTime, Timeout.Infinite);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -123,46 +114,42 @@
|
|||||||
/// <param name="dueTime">The due time.</param>
|
/// <param name="dueTime">The due time.</param>
|
||||||
/// <param name="period">The period.</param>
|
/// <param name="period">The period.</param>
|
||||||
public void Change(TimeSpan dueTime, TimeSpan period)
|
public void Change(TimeSpan dueTime, TimeSpan period)
|
||||||
=> Change(Convert.ToInt32(dueTime.TotalMilliseconds), Convert.ToInt32(period.TotalMilliseconds));
|
=> this.Change(Convert.ToInt32(dueTime.TotalMilliseconds), Convert.ToInt32(period.TotalMilliseconds));
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Changes the interval between method invocations for the internal timer.
|
/// Changes the interval between method invocations for the internal timer.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="period">The period.</param>
|
/// <param name="period">The period.</param>
|
||||||
public void Resume(int period) => Change(0, period);
|
public void Resume(Int32 period) => this.Change(0, period);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Changes the interval between method invocations for the internal timer.
|
/// Changes the interval between method invocations for the internal timer.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="period">The period.</param>
|
/// <param name="period">The period.</param>
|
||||||
public void Resume(TimeSpan period) => Change(TimeSpan.Zero, period);
|
public void Resume(TimeSpan period) => this.Change(TimeSpan.Zero, period);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Pauses this instance.
|
/// Pauses this instance.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void Pause() => Change(Timeout.Infinite, Timeout.Infinite);
|
public void Pause() => this.Change(Timeout.Infinite, Timeout.Infinite);
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public void Dispose()
|
public void Dispose() {
|
||||||
{
|
lock(this._syncLock) {
|
||||||
lock (_syncLock)
|
if(this._isDisposed == true || this._isDisposing == true) {
|
||||||
{
|
|
||||||
if (_isDisposed == true || _isDisposing == true)
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
_isDisposing.Value = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
try
|
this._isDisposing.Value = true;
|
||||||
{
|
|
||||||
_backingTimer.Dispose();
|
|
||||||
_cycleDoneEvent.Wait();
|
|
||||||
_cycleDoneEvent.Dispose();
|
|
||||||
}
|
}
|
||||||
finally
|
|
||||||
{
|
try {
|
||||||
_isDisposed.Value = true;
|
this._backingTimer.Dispose();
|
||||||
_isDisposing.Value = false;
|
this._cycleDoneEvent.Wait();
|
||||||
|
this._cycleDoneEvent.Dispose();
|
||||||
|
} finally {
|
||||||
|
this._isDisposed.Value = true;
|
||||||
|
this._isDisposing.Value = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -170,27 +157,24 @@
|
|||||||
/// Logic that runs every time the timer hits the due time.
|
/// Logic that runs every time the timer hits the due time.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="state">The state.</param>
|
/// <param name="state">The state.</param>
|
||||||
private void InternalCallback(object state)
|
private void InternalCallback(Object state) {
|
||||||
{
|
lock(this._syncLock) {
|
||||||
lock (_syncLock)
|
if(this.IsDisposed || this.IsDisposing) {
|
||||||
{
|
return;
|
||||||
if (IsDisposed || IsDisposing)
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(this._cycleDoneEvent.IsSet == false) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_cycleDoneEvent.IsSet == false)
|
this._cycleDoneEvent.Reset();
|
||||||
return;
|
|
||||||
|
|
||||||
_cycleDoneEvent.Reset();
|
try {
|
||||||
|
this._userCallback(state);
|
||||||
try
|
} finally {
|
||||||
{
|
this._cycleDoneEvent?.Set();
|
||||||
_userCallback(state);
|
_ = this._backingTimer?.Change(this._period, Timeout.Infinite);
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
_cycleDoneEvent?.Set();
|
|
||||||
_backingTimer?.Change(_period, Timeout.Infinite);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,24 +1,21 @@
|
|||||||
namespace Unosquare.Swan.Abstractions
|
using System;
|
||||||
{
|
using System.Linq;
|
||||||
using System;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq.Expressions;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq.Expressions;
|
|
||||||
|
|
||||||
|
namespace Unosquare.Swan.Abstractions {
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents a generic expression parser.
|
/// Represents a generic expression parser.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public abstract class ExpressionParser
|
public abstract class ExpressionParser {
|
||||||
{
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Resolves the expression.
|
/// Resolves the expression.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <typeparam name="T">The type of expression result.</typeparam>
|
/// <typeparam name="T">The type of expression result.</typeparam>
|
||||||
/// <param name="tokens">The tokens.</param>
|
/// <param name="tokens">The tokens.</param>
|
||||||
/// <returns>The representation of the expression parsed.</returns>
|
/// <returns>The representation of the expression parsed.</returns>
|
||||||
public virtual T ResolveExpression<T>(IEnumerable<Token> tokens)
|
public virtual T ResolveExpression<T>(IEnumerable<Token> tokens) {
|
||||||
{
|
UnaryExpression conversion = Expression.Convert(this.Parse(tokens), typeof(T));
|
||||||
var conversion = Expression.Convert(Parse(tokens), typeof(T));
|
|
||||||
return Expression.Lambda<Func<T>>(conversion).Compile()();
|
return Expression.Lambda<Func<T>>(conversion).Compile()();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -27,17 +24,15 @@
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="tokens">The tokens.</param>
|
/// <param name="tokens">The tokens.</param>
|
||||||
/// <returns>The final expression.</returns>
|
/// <returns>The final expression.</returns>
|
||||||
public virtual Expression Parse(IEnumerable<Token> tokens)
|
public virtual Expression Parse(IEnumerable<Token> tokens) {
|
||||||
{
|
List<Stack<Expression>> expressionStack = new List<Stack<Expression>>();
|
||||||
var expressionStack = new List<Stack<Expression>>();
|
|
||||||
|
|
||||||
foreach (var token in tokens)
|
foreach(Token token in tokens) {
|
||||||
{
|
if(expressionStack.Any() == false) {
|
||||||
if (expressionStack.Any() == false)
|
|
||||||
expressionStack.Add(new Stack<Expression>());
|
expressionStack.Add(new Stack<Expression>());
|
||||||
|
}
|
||||||
|
|
||||||
switch (token.Type)
|
switch(token.Type) {
|
||||||
{
|
|
||||||
case TokenType.Wall:
|
case TokenType.Wall:
|
||||||
expressionStack.Add(new Stack<Expression>());
|
expressionStack.Add(new Stack<Expression>());
|
||||||
break;
|
break;
|
||||||
@ -45,21 +40,20 @@
|
|||||||
expressionStack.Last().Push(Expression.Constant(Convert.ToDecimal(token.Value)));
|
expressionStack.Last().Push(Expression.Constant(Convert.ToDecimal(token.Value)));
|
||||||
break;
|
break;
|
||||||
case TokenType.Variable:
|
case TokenType.Variable:
|
||||||
ResolveVariable(token.Value, expressionStack.Last());
|
this.ResolveVariable(token.Value, expressionStack.Last());
|
||||||
break;
|
break;
|
||||||
case TokenType.String:
|
case TokenType.String:
|
||||||
expressionStack.Last().Push(Expression.Constant(token.Value));
|
expressionStack.Last().Push(Expression.Constant(token.Value));
|
||||||
break;
|
break;
|
||||||
case TokenType.Operator:
|
case TokenType.Operator:
|
||||||
ResolveOperator(token.Value, expressionStack.Last());
|
this.ResolveOperator(token.Value, expressionStack.Last());
|
||||||
break;
|
break;
|
||||||
case TokenType.Function:
|
case TokenType.Function:
|
||||||
ResolveFunction(token.Value, expressionStack.Last());
|
this.ResolveFunction(token.Value, expressionStack.Last());
|
||||||
|
|
||||||
if (expressionStack.Count > 1 && expressionStack.Last().Count == 1)
|
if(expressionStack.Count > 1 && expressionStack.Last().Count == 1) {
|
||||||
{
|
Expression lastValue = expressionStack.Last().Pop();
|
||||||
var lastValue = expressionStack.Last().Pop();
|
_ = expressionStack.Remove(expressionStack.Last());
|
||||||
expressionStack.Remove(expressionStack.Last());
|
|
||||||
expressionStack.Last().Push(lastValue);
|
expressionStack.Last().Push(lastValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -75,20 +69,20 @@
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="value">The value.</param>
|
/// <param name="value">The value.</param>
|
||||||
/// <param name="expressionStack">The expression stack.</param>
|
/// <param name="expressionStack">The expression stack.</param>
|
||||||
public abstract void ResolveVariable(string value, Stack<Expression> expressionStack);
|
public abstract void ResolveVariable(String value, Stack<Expression> expressionStack);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Resolves the operator.
|
/// Resolves the operator.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="value">The value.</param>
|
/// <param name="value">The value.</param>
|
||||||
/// <param name="expressionStack">The expression stack.</param>
|
/// <param name="expressionStack">The expression stack.</param>
|
||||||
public abstract void ResolveOperator(string value, Stack<Expression> expressionStack);
|
public abstract void ResolveOperator(String value, Stack<Expression> expressionStack);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Resolves the function.
|
/// Resolves the function.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="value">The value.</param>
|
/// <param name="value">The value.</param>
|
||||||
/// <param name="expressionStack">The expression stack.</param>
|
/// <param name="expressionStack">The expression stack.</param>
|
||||||
public abstract void ResolveFunction(string value, Stack<Expression> expressionStack);
|
public abstract void ResolveFunction(String value, Stack<Expression> expressionStack);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,27 +1,31 @@
|
|||||||
namespace Unosquare.Swan.Abstractions
|
using System;
|
||||||
{
|
using System.Collections.Generic;
|
||||||
using System;
|
using System.Reflection;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Reflection;
|
|
||||||
|
|
||||||
|
namespace Unosquare.Swan.Abstractions {
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Interface object map.
|
/// Interface object map.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public interface IObjectMap
|
public interface IObjectMap {
|
||||||
{
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the map.
|
/// Gets or sets the map.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
Dictionary<PropertyInfo, List<PropertyInfo>> Map { get; }
|
Dictionary<PropertyInfo, List<PropertyInfo>> Map {
|
||||||
|
get;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the type of the source.
|
/// Gets or sets the type of the source.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
Type SourceType { get; }
|
Type SourceType {
|
||||||
|
get;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the type of the destination.
|
/// Gets or sets the type of the destination.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
Type DestinationType { get; }
|
Type DestinationType {
|
||||||
|
get;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,12 +1,10 @@
|
|||||||
namespace Unosquare.Swan.Abstractions
|
using System;
|
||||||
{
|
|
||||||
using System;
|
|
||||||
|
|
||||||
|
namespace Unosquare.Swan.Abstractions {
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Defines a generic interface for synchronized locking mechanisms.
|
/// Defines a generic interface for synchronized locking mechanisms.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public interface ISyncLocker : IDisposable
|
public interface ISyncLocker : IDisposable {
|
||||||
{
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Acquires a writer lock.
|
/// Acquires a writer lock.
|
||||||
/// The lock is released when the returned locking object is disposed.
|
/// The lock is released when the returned locking object is disposed.
|
||||||
|
@ -1,14 +1,16 @@
|
|||||||
namespace Unosquare.Swan.Abstractions
|
using System;
|
||||||
{
|
|
||||||
|
namespace Unosquare.Swan.Abstractions {
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A simple Validator interface.
|
/// A simple Validator interface.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public interface IValidator
|
public interface IValidator {
|
||||||
{
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The error message.
|
/// The error message.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
string ErrorMessage { get; }
|
String ErrorMessage {
|
||||||
|
get;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Checks if a value is valid.
|
/// Checks if a value is valid.
|
||||||
@ -16,6 +18,6 @@
|
|||||||
/// <typeparam name="T">The type.</typeparam>
|
/// <typeparam name="T">The type.</typeparam>
|
||||||
/// <param name="value"> The value.</param>
|
/// <param name="value"> The value.</param>
|
||||||
/// <returns>True if it is valid.False if it is not.</returns>
|
/// <returns>True if it is valid.False if it is not.</returns>
|
||||||
bool IsValid<T>(T value);
|
Boolean IsValid<T>(T value);
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,33 +1,39 @@
|
|||||||
namespace Unosquare.Swan.Abstractions
|
using System;
|
||||||
{
|
|
||||||
using System;
|
|
||||||
|
|
||||||
|
namespace Unosquare.Swan.Abstractions {
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Provides a generalized API for ManualResetEvent and ManualResetEventSlim.
|
/// Provides a generalized API for ManualResetEvent and ManualResetEventSlim.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <seealso cref="IDisposable" />
|
/// <seealso cref="IDisposable" />
|
||||||
public interface IWaitEvent : IDisposable
|
public interface IWaitEvent : IDisposable {
|
||||||
{
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets a value indicating whether the event is in the completed state.
|
/// Gets a value indicating whether the event is in the completed state.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
bool IsCompleted { get; }
|
Boolean IsCompleted {
|
||||||
|
get;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets a value indicating whether the Begin method has been called.
|
/// Gets a value indicating whether the Begin method has been called.
|
||||||
/// It returns false after the Complete method is called.
|
/// It returns false after the Complete method is called.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
bool IsInProgress { get; }
|
Boolean IsInProgress {
|
||||||
|
get;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Returns true if the underlying handle is not closed and it is still valid.
|
/// Returns true if the underlying handle is not closed and it is still valid.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
bool IsValid { get; }
|
Boolean IsValid {
|
||||||
|
get;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets a value indicating whether this instance is disposed.
|
/// Gets a value indicating whether this instance is disposed.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
bool IsDisposed { get; }
|
Boolean IsDisposed {
|
||||||
|
get;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Enters the state in which waiters need to wait.
|
/// Enters the state in which waiters need to wait.
|
||||||
@ -52,6 +58,6 @@
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="timeout">The maximum amount of time to wait for.</param>
|
/// <param name="timeout">The maximum amount of time to wait for.</param>
|
||||||
/// <returns><c>true</c> when there was no timeout. <c>false</c> if the timeout was reached.</returns>
|
/// <returns><c>true</c> when there was no timeout. <c>false</c> if the timeout was reached.</returns>
|
||||||
bool Wait(TimeSpan timeout);
|
Boolean Wait(TimeSpan timeout);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,8 @@
|
|||||||
namespace Unosquare.Swan.Abstractions
|
namespace Unosquare.Swan.Abstractions {
|
||||||
{
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A simple interface for application workers.
|
/// A simple interface for application workers.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public interface IWorker
|
public interface IWorker {
|
||||||
{
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Should start the task immediately and asynchronously.
|
/// Should start the task immediately and asynchronously.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -1,16 +1,12 @@
|
|||||||
#if !NETSTANDARD1_3
|
#if !NETSTANDARD1_3
|
||||||
namespace Unosquare.Swan.Abstractions
|
using System;
|
||||||
{
|
using System.Collections.Generic;
|
||||||
using System;
|
using System.Threading;
|
||||||
using System.Collections.Generic;
|
namespace Unosquare.Swan.Abstractions {
|
||||||
using System.Threading;
|
|
||||||
using Swan;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents an background worker abstraction with a life cycle and running at a independent thread.
|
/// Represents an background worker abstraction with a life cycle and running at a independent thread.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public abstract class RunnerBase
|
public abstract class RunnerBase {
|
||||||
{
|
|
||||||
private Thread _worker;
|
private Thread _worker;
|
||||||
private CancellationTokenSource _cancelTokenSource;
|
private CancellationTokenSource _cancelTokenSource;
|
||||||
private ManualResetEvent _workFinished;
|
private ManualResetEvent _workFinished;
|
||||||
@ -19,10 +15,9 @@ namespace Unosquare.Swan.Abstractions
|
|||||||
/// Initializes a new instance of the <see cref="RunnerBase"/> class.
|
/// Initializes a new instance of the <see cref="RunnerBase"/> class.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="isEnabled">if set to <c>true</c> [is enabled].</param>
|
/// <param name="isEnabled">if set to <c>true</c> [is enabled].</param>
|
||||||
protected RunnerBase(bool isEnabled)
|
protected RunnerBase(Boolean isEnabled) {
|
||||||
{
|
this.Name = this.GetType().Name;
|
||||||
Name = GetType().Name;
|
this.IsEnabled = isEnabled;
|
||||||
IsEnabled = isEnabled;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -31,7 +26,7 @@ namespace Unosquare.Swan.Abstractions
|
|||||||
/// <value>
|
/// <value>
|
||||||
/// The error messages.
|
/// The error messages.
|
||||||
/// </value>
|
/// </value>
|
||||||
public List<string> ErrorMessages { get; } = new List<string>();
|
public List<String> ErrorMessages { get; } = new List<String>();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the name.
|
/// Gets the name.
|
||||||
@ -39,7 +34,9 @@ namespace Unosquare.Swan.Abstractions
|
|||||||
/// <value>
|
/// <value>
|
||||||
/// The name.
|
/// The name.
|
||||||
/// </value>
|
/// </value>
|
||||||
public string Name { get; }
|
public String Name {
|
||||||
|
get;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets a value indicating whether this instance is running.
|
/// Gets a value indicating whether this instance is running.
|
||||||
@ -47,7 +44,9 @@ namespace Unosquare.Swan.Abstractions
|
|||||||
/// <value>
|
/// <value>
|
||||||
/// <c>true</c> if this instance is running; otherwise, <c>false</c>.
|
/// <c>true</c> if this instance is running; otherwise, <c>false</c>.
|
||||||
/// </value>
|
/// </value>
|
||||||
public bool IsRunning { get; private set; }
|
public Boolean IsRunning {
|
||||||
|
get; private set;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets a value indicating whether this instance is enabled.
|
/// Gets a value indicating whether this instance is enabled.
|
||||||
@ -55,68 +54,59 @@ namespace Unosquare.Swan.Abstractions
|
|||||||
/// <value>
|
/// <value>
|
||||||
/// <c>true</c> if this instance is enabled; otherwise, <c>false</c>.
|
/// <c>true</c> if this instance is enabled; otherwise, <c>false</c>.
|
||||||
/// </value>
|
/// </value>
|
||||||
public bool IsEnabled { get; }
|
public Boolean IsEnabled {
|
||||||
|
get;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Starts this instance.
|
/// Starts this instance.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public virtual void Start()
|
public virtual void Start() {
|
||||||
{
|
if(this.IsEnabled == false) {
|
||||||
if (IsEnabled == false)
|
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
$"Start Requested".Debug(Name);
|
$"Start Requested".Debug(this.Name);
|
||||||
_cancelTokenSource = new CancellationTokenSource();
|
this._cancelTokenSource = new CancellationTokenSource();
|
||||||
_workFinished = new ManualResetEvent(false);
|
this._workFinished = new ManualResetEvent(false);
|
||||||
|
|
||||||
_worker = new Thread(() =>
|
this._worker = new Thread(() => {
|
||||||
{
|
_ = this._workFinished.Reset();
|
||||||
_workFinished.Reset();
|
this.IsRunning = true;
|
||||||
IsRunning = true;
|
try {
|
||||||
try
|
this.Setup();
|
||||||
{
|
this.DoBackgroundWork(this._cancelTokenSource.Token);
|
||||||
Setup();
|
} catch(ThreadAbortException) {
|
||||||
DoBackgroundWork(_cancelTokenSource.Token);
|
$"{nameof(ThreadAbortException)} caught.".Warn(this.Name);
|
||||||
|
} catch(Exception ex) {
|
||||||
|
$"{ex.GetType()}: {ex.Message}\r\n{ex.StackTrace}".Error(this.Name);
|
||||||
|
} finally {
|
||||||
|
this.Cleanup();
|
||||||
|
_ = this._workFinished?.Set();
|
||||||
|
this.IsRunning = false;
|
||||||
|
"Stopped Completely".Debug(this.Name);
|
||||||
}
|
}
|
||||||
catch (ThreadAbortException)
|
}) {
|
||||||
{
|
|
||||||
$"{nameof(ThreadAbortException)} caught.".Warn(Name);
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
$"{ex.GetType()}: {ex.Message}\r\n{ex.StackTrace}".Error(Name);
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
Cleanup();
|
|
||||||
_workFinished?.Set();
|
|
||||||
IsRunning = false;
|
|
||||||
"Stopped Completely".Debug(Name);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
{
|
|
||||||
IsBackground = true,
|
IsBackground = true,
|
||||||
Name = $"{Name}Thread",
|
Name = $"{this.Name}Thread",
|
||||||
};
|
};
|
||||||
|
|
||||||
_worker.Start();
|
this._worker.Start();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Stops this instance.
|
/// Stops this instance.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public virtual void Stop()
|
public virtual void Stop() {
|
||||||
{
|
if(this.IsEnabled == false || this.IsRunning == false) {
|
||||||
if (IsEnabled == false || IsRunning == false)
|
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
$"Stop Requested".Debug(Name);
|
$"Stop Requested".Debug(this.Name);
|
||||||
_cancelTokenSource.Cancel();
|
this._cancelTokenSource.Cancel();
|
||||||
var waitRetries = 5;
|
Int32 waitRetries = 5;
|
||||||
while (waitRetries >= 1)
|
while(waitRetries >= 1) {
|
||||||
{
|
if(this._workFinished.WaitOne(250)) {
|
||||||
if (_workFinished.WaitOne(250))
|
|
||||||
{
|
|
||||||
waitRetries = -1;
|
waitRetries = -1;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -124,38 +114,34 @@ namespace Unosquare.Swan.Abstractions
|
|||||||
waitRetries--;
|
waitRetries--;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (waitRetries < 0)
|
if(waitRetries < 0) {
|
||||||
{
|
"Workbench stopped gracefully".Debug(this.Name);
|
||||||
"Workbench stopped gracefully".Debug(Name);
|
} else {
|
||||||
}
|
"Did not respond to stop request. Aborting thread and waiting . . .".Warn(this.Name);
|
||||||
else
|
this._worker.Abort();
|
||||||
{
|
|
||||||
"Did not respond to stop request. Aborting thread and waiting . . .".Warn(Name);
|
|
||||||
_worker.Abort();
|
|
||||||
|
|
||||||
if (_workFinished.WaitOne(5000) == false)
|
if(this._workFinished.WaitOne(5000) == false) {
|
||||||
"Waited and no response. Worker might have been left in an inconsistent state.".Error(Name);
|
"Waited and no response. Worker might have been left in an inconsistent state.".Error(this.Name);
|
||||||
else
|
} else {
|
||||||
"Waited for worker and it finally responded (OK).".Debug(Name);
|
"Waited for worker and it finally responded (OK).".Debug(this.Name);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_workFinished.Dispose();
|
this._workFinished.Dispose();
|
||||||
_workFinished = null;
|
this._workFinished = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Setups this instance.
|
/// Setups this instance.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
protected virtual void Setup()
|
protected virtual void Setup() {
|
||||||
{
|
|
||||||
// empty
|
// empty
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Cleanups this instance.
|
/// Cleanups this instance.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
protected virtual void Cleanup()
|
protected virtual void Cleanup() {
|
||||||
{
|
|
||||||
// empty
|
// empty
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,14 +1,13 @@
|
|||||||
namespace Unosquare.Swan.Abstractions
|
using Unosquare.Swan.Formatters;
|
||||||
{
|
using Unosquare.Swan.Reflection;
|
||||||
using Formatters;
|
using System;
|
||||||
using Reflection;
|
using System.Collections;
|
||||||
using System;
|
using System.Collections.Generic;
|
||||||
using System.Collections;
|
using System.IO;
|
||||||
using System.Collections.Generic;
|
using System.Linq;
|
||||||
using System.IO;
|
using System.Reflection;
|
||||||
using System.Linq;
|
|
||||||
using System.Reflection;
|
|
||||||
|
|
||||||
|
namespace Unosquare.Swan.Abstractions {
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents a provider to save and load settings using a plain JSON file.
|
/// Represents a provider to save and load settings using a plain JSON file.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -42,9 +41,8 @@
|
|||||||
/// </example>
|
/// </example>
|
||||||
/// <typeparam name="T">The type of settings model.</typeparam>
|
/// <typeparam name="T">The type of settings model.</typeparam>
|
||||||
public sealed class SettingsProvider<T>
|
public sealed class SettingsProvider<T>
|
||||||
: SingletonBase<SettingsProvider<T>>
|
: SingletonBase<SettingsProvider<T>> {
|
||||||
{
|
private readonly Object _syncRoot = new Object();
|
||||||
private readonly object _syncRoot = new object();
|
|
||||||
|
|
||||||
private T _global;
|
private T _global;
|
||||||
|
|
||||||
@ -55,7 +53,9 @@
|
|||||||
/// <value>
|
/// <value>
|
||||||
/// The configuration file path.
|
/// The configuration file path.
|
||||||
/// </value>
|
/// </value>
|
||||||
public string ConfigurationFilePath { get; set; } =
|
public String ConfigurationFilePath {
|
||||||
|
get; set;
|
||||||
|
} =
|
||||||
#if NETSTANDARD1_3
|
#if NETSTANDARD1_3
|
||||||
Path.Combine(Runtime.LocalStoragePath, "appsettings.json");
|
Path.Combine(Runtime.LocalStoragePath, "appsettings.json");
|
||||||
#else
|
#else
|
||||||
@ -68,16 +68,14 @@
|
|||||||
/// <value>
|
/// <value>
|
||||||
/// The global settings object.
|
/// The global settings object.
|
||||||
/// </value>
|
/// </value>
|
||||||
public T Global
|
public T Global {
|
||||||
{
|
get {
|
||||||
get
|
lock(this._syncRoot) {
|
||||||
{
|
if(Equals(this._global, default(T))) {
|
||||||
lock (_syncRoot)
|
this.ReloadGlobalSettings();
|
||||||
{
|
}
|
||||||
if (Equals(_global, default(T)))
|
|
||||||
ReloadGlobalSettings();
|
|
||||||
|
|
||||||
return _global;
|
return this._global;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -85,22 +83,21 @@
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Reloads the global settings.
|
/// Reloads the global settings.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void ReloadGlobalSettings()
|
public void ReloadGlobalSettings() {
|
||||||
{
|
if(File.Exists(this.ConfigurationFilePath) == false || File.ReadAllText(this.ConfigurationFilePath).Length == 0) {
|
||||||
if (File.Exists(ConfigurationFilePath) == false || File.ReadAllText(ConfigurationFilePath).Length == 0)
|
this.ResetGlobalSettings();
|
||||||
{
|
|
||||||
ResetGlobalSettings();
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
lock (_syncRoot)
|
lock(this._syncRoot) {
|
||||||
_global = Json.Deserialize<T>(File.ReadAllText(ConfigurationFilePath));
|
this._global = Json.Deserialize<T>(File.ReadAllText(this.ConfigurationFilePath));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Persists the global settings.
|
/// Persists the global settings.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void PersistGlobalSettings() => File.WriteAllText(ConfigurationFilePath, Json.Serialize(Global, true));
|
public void PersistGlobalSettings() => File.WriteAllText(this.ConfigurationFilePath, Json.Serialize(this.Global, true));
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Updates settings from list.
|
/// Updates settings from list.
|
||||||
@ -110,29 +107,32 @@
|
|||||||
/// A list of settings of type ref="ExtendedPropertyInfo".
|
/// A list of settings of type ref="ExtendedPropertyInfo".
|
||||||
/// </returns>
|
/// </returns>
|
||||||
/// <exception cref="ArgumentNullException">propertyList.</exception>
|
/// <exception cref="ArgumentNullException">propertyList.</exception>
|
||||||
public List<string> RefreshFromList(List<ExtendedPropertyInfo<T>> propertyList)
|
public List<String> RefreshFromList(List<ExtendedPropertyInfo<T>> propertyList) {
|
||||||
{
|
if(propertyList == null) {
|
||||||
if (propertyList == null)
|
|
||||||
throw new ArgumentNullException(nameof(propertyList));
|
throw new ArgumentNullException(nameof(propertyList));
|
||||||
|
}
|
||||||
|
|
||||||
var changedSettings = new List<string>();
|
List<String> changedSettings = new List<String>();
|
||||||
var globalProps = Runtime.PropertyTypeCache.RetrieveAllProperties<T>();
|
IEnumerable<PropertyInfo> globalProps = Runtime.PropertyTypeCache.RetrieveAllProperties<T>();
|
||||||
|
|
||||||
foreach (var property in propertyList)
|
foreach(ExtendedPropertyInfo<T> property in propertyList) {
|
||||||
{
|
PropertyInfo propertyInfo = globalProps.FirstOrDefault(x => x.Name == property.Property);
|
||||||
var propertyInfo = globalProps.FirstOrDefault(x => x.Name == property.Property);
|
|
||||||
|
|
||||||
if (propertyInfo == null) continue;
|
if(propertyInfo == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
var originalValue = propertyInfo.GetValue(Global);
|
Object originalValue = propertyInfo.GetValue(this.Global);
|
||||||
var isChanged = propertyInfo.PropertyType.IsArray
|
Boolean isChanged = propertyInfo.PropertyType.IsArray
|
||||||
? property.Value is IEnumerable enumerable && propertyInfo.TrySetArray(enumerable.Cast<object>(), Global)
|
? property.Value is IEnumerable enumerable && propertyInfo.TrySetArray(enumerable.Cast<Object>(), this.Global)
|
||||||
: SetValue(property.Value, originalValue, propertyInfo);
|
: this.SetValue(property.Value, originalValue, propertyInfo);
|
||||||
|
|
||||||
if (!isChanged) continue;
|
if(!isChanged) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
changedSettings.Add(property.Property);
|
changedSettings.Add(property.Property);
|
||||||
PersistGlobalSettings();
|
this.PersistGlobalSettings();
|
||||||
}
|
}
|
||||||
|
|
||||||
return changedSettings;
|
return changedSettings;
|
||||||
@ -142,9 +142,8 @@
|
|||||||
/// Gets the list.
|
/// Gets the list.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>A List of ExtendedPropertyInfo of the type T.</returns>
|
/// <returns>A List of ExtendedPropertyInfo of the type T.</returns>
|
||||||
public List<ExtendedPropertyInfo<T>> GetList()
|
public List<ExtendedPropertyInfo<T>> GetList() {
|
||||||
{
|
Dictionary<String, Object> jsonData = Json.Deserialize(Json.Serialize(this.Global)) as Dictionary<String, Object>;
|
||||||
var jsonData = Json.Deserialize(Json.Serialize(Global)) as Dictionary<string, object>;
|
|
||||||
|
|
||||||
return jsonData?.Keys
|
return jsonData?.Keys
|
||||||
.Select(p => new ExtendedPropertyInfo<T>(p) { Value = jsonData[p] })
|
.Select(p => new ExtendedPropertyInfo<T>(p) { Value = jsonData[p] })
|
||||||
@ -154,28 +153,25 @@
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Resets the global settings.
|
/// Resets the global settings.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void ResetGlobalSettings()
|
public void ResetGlobalSettings() {
|
||||||
{
|
lock(this._syncRoot) {
|
||||||
lock (_syncRoot)
|
this._global = Activator.CreateInstance<T>();
|
||||||
_global = Activator.CreateInstance<T>();
|
|
||||||
|
|
||||||
PersistGlobalSettings();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool SetValue(object property, object originalValue, PropertyInfo propertyInfo)
|
this.PersistGlobalSettings();
|
||||||
{
|
}
|
||||||
switch (property)
|
|
||||||
{
|
private Boolean SetValue(Object property, Object originalValue, PropertyInfo propertyInfo) {
|
||||||
|
switch(property) {
|
||||||
case null when originalValue == null:
|
case null when originalValue == null:
|
||||||
break;
|
break;
|
||||||
case null:
|
case null:
|
||||||
propertyInfo.SetValue(Global, null);
|
propertyInfo.SetValue(this.Global, null);
|
||||||
return true;
|
return true;
|
||||||
default:
|
default:
|
||||||
if (propertyInfo.PropertyType.TryParseBasicType(property, out var propertyValue) &&
|
if(propertyInfo.PropertyType.TryParseBasicType(property, out Object propertyValue) &&
|
||||||
!propertyValue.Equals(originalValue))
|
!propertyValue.Equals(originalValue)) {
|
||||||
{
|
propertyInfo.SetValue(this.Global, propertyValue);
|
||||||
propertyInfo.SetValue(Global, propertyValue);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,14 +1,12 @@
|
|||||||
namespace Unosquare.Swan.Abstractions
|
using System;
|
||||||
{
|
|
||||||
using System;
|
|
||||||
|
|
||||||
|
namespace Unosquare.Swan.Abstractions {
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents a singleton pattern abstract class.
|
/// Represents a singleton pattern abstract class.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <typeparam name="T">The type of class.</typeparam>
|
/// <typeparam name="T">The type of class.</typeparam>
|
||||||
public abstract class SingletonBase<T> : IDisposable
|
public abstract class SingletonBase<T> : IDisposable
|
||||||
where T : class
|
where T : class {
|
||||||
{
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The static, singleton instance reference.
|
/// The static, singleton instance reference.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -16,7 +14,7 @@
|
|||||||
valueFactory: () => Activator.CreateInstance(typeof(T), true) as T,
|
valueFactory: () => Activator.CreateInstance(typeof(T), true) as T,
|
||||||
isThreadSafe: true);
|
isThreadSafe: true);
|
||||||
|
|
||||||
private bool _isDisposing; // To detect redundant calls
|
private Boolean _isDisposing; // To detect redundant calls
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the instance that this singleton represents.
|
/// Gets the instance that this singleton represents.
|
||||||
@ -28,7 +26,7 @@
|
|||||||
public static T Instance => LazyInstance.Value;
|
public static T Instance => LazyInstance.Value;
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public void Dispose() => Dispose(true);
|
public void Dispose() => this.Dispose(true);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Releases unmanaged and - optionally - managed resources.
|
/// Releases unmanaged and - optionally - managed resources.
|
||||||
@ -36,22 +34,22 @@
|
|||||||
/// a non-default class finalizer (destructor).
|
/// a non-default class finalizer (destructor).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="disposeManaged"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
|
/// <param name="disposeManaged"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
|
||||||
protected virtual void Dispose(bool disposeManaged)
|
protected virtual void Dispose(Boolean disposeManaged) {
|
||||||
{
|
if(this._isDisposing) {
|
||||||
if (_isDisposing) return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
_isDisposing = true;
|
this._isDisposing = true;
|
||||||
|
|
||||||
// free managed resources
|
// free managed resources
|
||||||
if (LazyInstance == null) return;
|
if(LazyInstance == null) {
|
||||||
|
return;
|
||||||
try
|
|
||||||
{
|
|
||||||
var disposableInstance = LazyInstance.Value as IDisposable;
|
|
||||||
disposableInstance?.Dispose();
|
|
||||||
}
|
}
|
||||||
catch
|
|
||||||
{
|
try {
|
||||||
|
IDisposable disposableInstance = LazyInstance.Value as IDisposable;
|
||||||
|
disposableInstance?.Dispose();
|
||||||
|
} catch {
|
||||||
// swallow
|
// swallow
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,22 +1,20 @@
|
|||||||
namespace Unosquare.Swan.Abstractions
|
using System;
|
||||||
{
|
using System.Collections.Generic;
|
||||||
using System;
|
using System.Linq;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
|
|
||||||
|
namespace Unosquare.Swan.Abstractions {
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents a generic tokenizer.
|
/// Represents a generic tokenizer.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public abstract class Tokenizer
|
public abstract class Tokenizer {
|
||||||
{
|
private const Char PeriodChar = '.';
|
||||||
private const char PeriodChar = '.';
|
private const Char CommaChar = ',';
|
||||||
private const char CommaChar = ',';
|
private const Char StringQuotedChar = '"';
|
||||||
private const char StringQuotedChar = '"';
|
private const Char OpenFuncChar = '(';
|
||||||
private const char OpenFuncChar = '(';
|
private const Char CloseFuncChar = ')';
|
||||||
private const char CloseFuncChar = ')';
|
private const Char NegativeChar = '-';
|
||||||
private const char NegativeChar = '-';
|
|
||||||
|
|
||||||
private const string OpenFuncStr = "(";
|
private const String OpenFuncStr = "(";
|
||||||
|
|
||||||
private readonly List<Operator> _operators = new List<Operator>();
|
private readonly List<Operator> _operators = new List<Operator>();
|
||||||
|
|
||||||
@ -84,10 +82,9 @@
|
|||||||
/// </list>
|
/// </list>
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="input">The input.</param>
|
/// <param name="input">The input.</param>
|
||||||
protected Tokenizer(string input)
|
protected Tokenizer(String input) {
|
||||||
{
|
this._operators.AddRange(this.GetDefaultOperators());
|
||||||
_operators.AddRange(GetDefaultOperators());
|
this.Tokenize(input);
|
||||||
Tokenize(input);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -95,10 +92,9 @@
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="input">The input.</param>
|
/// <param name="input">The input.</param>
|
||||||
/// <param name="operators">The operators to use.</param>
|
/// <param name="operators">The operators to use.</param>
|
||||||
protected Tokenizer(string input, IEnumerable<Operator> operators)
|
protected Tokenizer(String input, IEnumerable<Operator> operators) {
|
||||||
{
|
this._operators.AddRange(operators);
|
||||||
_operators.AddRange(operators);
|
this.Tokenize(input);
|
||||||
Tokenize(input);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -115,14 +111,14 @@
|
|||||||
/// <param name="input">The input.</param>
|
/// <param name="input">The input.</param>
|
||||||
/// <param name="startIndex">The start index.</param>
|
/// <param name="startIndex">The start index.</param>
|
||||||
/// <returns><c>true</c> if the input is valid, otherwise <c>false</c>.</returns>
|
/// <returns><c>true</c> if the input is valid, otherwise <c>false</c>.</returns>
|
||||||
public abstract bool ValidateInput(string input, out int startIndex);
|
public abstract Boolean ValidateInput(String input, out Int32 startIndex);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Resolves the type of the function or member.
|
/// Resolves the type of the function or member.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="input">The input.</param>
|
/// <param name="input">The input.</param>
|
||||||
/// <returns>The token type.</returns>
|
/// <returns>The token type.</returns>
|
||||||
public abstract TokenType ResolveFunctionOrMemberType(string input);
|
public abstract TokenType ResolveFunctionOrMemberType(String input);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Evaluates the function or member.
|
/// Evaluates the function or member.
|
||||||
@ -130,7 +126,7 @@
|
|||||||
/// <param name="input">The input.</param>
|
/// <param name="input">The input.</param>
|
||||||
/// <param name="position">The position.</param>
|
/// <param name="position">The position.</param>
|
||||||
/// <returns><c>true</c> if the input is a valid function or variable, otherwise <c>false</c>.</returns>
|
/// <returns><c>true</c> if the input is a valid function or variable, otherwise <c>false</c>.</returns>
|
||||||
public virtual bool EvaluateFunctionOrMember(string input, int position) => false;
|
public virtual Boolean EvaluateFunctionOrMember(String input, Int32 position) => false;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the default operators.
|
/// Gets the default operators.
|
||||||
@ -165,14 +161,11 @@
|
|||||||
/// or
|
/// or
|
||||||
/// Mismatched parenthesis.
|
/// Mismatched parenthesis.
|
||||||
/// </exception>
|
/// </exception>
|
||||||
public virtual IEnumerable<Token> ShuntingYard(bool includeFunctionStopper = true)
|
public virtual IEnumerable<Token> ShuntingYard(Boolean includeFunctionStopper = true) {
|
||||||
{
|
Stack<Token> stack = new Stack<Token>();
|
||||||
var stack = new Stack<Token>();
|
|
||||||
|
|
||||||
foreach (var tok in Tokens)
|
foreach(Token tok in this.Tokens) {
|
||||||
{
|
switch(tok.Type) {
|
||||||
switch (tok.Type)
|
|
||||||
{
|
|
||||||
case TokenType.Number:
|
case TokenType.Number:
|
||||||
case TokenType.Variable:
|
case TokenType.Variable:
|
||||||
case TokenType.String:
|
case TokenType.String:
|
||||||
@ -182,38 +175,37 @@
|
|||||||
stack.Push(tok);
|
stack.Push(tok);
|
||||||
break;
|
break;
|
||||||
case TokenType.Operator:
|
case TokenType.Operator:
|
||||||
while (stack.Any() && stack.Peek().Type == TokenType.Operator &&
|
while(stack.Any() && stack.Peek().Type == TokenType.Operator &&
|
||||||
CompareOperators(tok.Value, stack.Peek().Value))
|
this.CompareOperators(tok.Value, stack.Peek().Value)) {
|
||||||
yield return stack.Pop();
|
yield return stack.Pop();
|
||||||
|
}
|
||||||
|
|
||||||
stack.Push(tok);
|
stack.Push(tok);
|
||||||
break;
|
break;
|
||||||
case TokenType.Comma:
|
case TokenType.Comma:
|
||||||
while (stack.Any() && (stack.Peek().Type != TokenType.Comma &&
|
while(stack.Any() && stack.Peek().Type != TokenType.Comma &&
|
||||||
stack.Peek().Type != TokenType.Parenthesis))
|
stack.Peek().Type != TokenType.Parenthesis) {
|
||||||
yield return stack.Pop();
|
yield return stack.Pop();
|
||||||
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
case TokenType.Parenthesis:
|
case TokenType.Parenthesis:
|
||||||
if (tok.Value == OpenFuncStr)
|
if(tok.Value == OpenFuncStr) {
|
||||||
{
|
if(stack.Any() && stack.Peek().Type == TokenType.Function) {
|
||||||
if (stack.Any() && stack.Peek().Type == TokenType.Function)
|
if(includeFunctionStopper) {
|
||||||
{
|
|
||||||
if (includeFunctionStopper)
|
|
||||||
yield return new Token(TokenType.Wall, tok.Value);
|
yield return new Token(TokenType.Wall, tok.Value);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
stack.Push(tok);
|
stack.Push(tok);
|
||||||
}
|
} else {
|
||||||
else
|
while(stack.Peek().Value != OpenFuncStr) {
|
||||||
{
|
|
||||||
while (stack.Peek().Value != OpenFuncStr)
|
|
||||||
yield return stack.Pop();
|
yield return stack.Pop();
|
||||||
|
}
|
||||||
|
|
||||||
stack.Pop();
|
_ = stack.Pop();
|
||||||
|
|
||||||
if (stack.Any() && stack.Peek().Type == TokenType.Function)
|
if(stack.Any() && stack.Peek().Type == TokenType.Function) {
|
||||||
{
|
|
||||||
yield return stack.Pop();
|
yield return stack.Pop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -224,142 +216,136 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
while (stack.Any())
|
while(stack.Any()) {
|
||||||
{
|
Token tok = stack.Pop();
|
||||||
var tok = stack.Pop();
|
if(tok.Type == TokenType.Parenthesis) {
|
||||||
if (tok.Type == TokenType.Parenthesis)
|
|
||||||
throw new InvalidOperationException("Mismatched parenthesis");
|
throw new InvalidOperationException("Mismatched parenthesis");
|
||||||
|
}
|
||||||
|
|
||||||
yield return tok;
|
yield return tok;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool CompareOperators(Operator op1, Operator op2) => op1.RightAssociative
|
private static Boolean CompareOperators(Operator op1, Operator op2) => op1.RightAssociative
|
||||||
? op1.Precedence < op2.Precedence
|
? op1.Precedence < op2.Precedence
|
||||||
: op1.Precedence <= op2.Precedence;
|
: op1.Precedence <= op2.Precedence;
|
||||||
|
|
||||||
private void Tokenize(string input)
|
private void Tokenize(String input) {
|
||||||
{
|
if(!this.ValidateInput(input, out Int32 startIndex)) {
|
||||||
if (!ValidateInput(input, out var startIndex))
|
|
||||||
{
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (var i = startIndex; i < input.Length; i++)
|
for(Int32 i = startIndex; i < input.Length; i++) {
|
||||||
{
|
if(Char.IsWhiteSpace(input, i)) {
|
||||||
if (char.IsWhiteSpace(input, i)) continue;
|
|
||||||
|
|
||||||
if (input[i] == CommaChar)
|
|
||||||
{
|
|
||||||
Tokens.Add(new Token(TokenType.Comma, new string(new[] { input[i] })));
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (input[i] == StringQuotedChar)
|
if(input[i] == CommaChar) {
|
||||||
{
|
this.Tokens.Add(new Token(TokenType.Comma, new String(new[] { input[i] })));
|
||||||
i = ExtractString(input, i);
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (char.IsLetter(input, i) || EvaluateFunctionOrMember(input, i))
|
if(input[i] == StringQuotedChar) {
|
||||||
{
|
i = this.ExtractString(input, i);
|
||||||
i = ExtractFunctionOrMember(input, i);
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(Char.IsLetter(input, i) || this.EvaluateFunctionOrMember(input, i)) {
|
||||||
|
i = this.ExtractFunctionOrMember(input, i);
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (char.IsNumber(input, i) || (
|
if(Char.IsNumber(input, i) ||
|
||||||
input[i] == NegativeChar &&
|
input[i] == NegativeChar &&
|
||||||
((Tokens.Any() && Tokens.Last().Type != TokenType.Number) || !Tokens.Any())))
|
(this.Tokens.Any() && this.Tokens.Last().Type != TokenType.Number || !this.Tokens.Any())) {
|
||||||
{
|
i = this.ExtractNumber(input, i);
|
||||||
i = ExtractNumber(input, i);
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (input[i] == OpenFuncChar ||
|
if(input[i] == OpenFuncChar ||
|
||||||
input[i] == CloseFuncChar)
|
input[i] == CloseFuncChar) {
|
||||||
{
|
this.Tokens.Add(new Token(TokenType.Parenthesis, new String(new[] { input[i] })));
|
||||||
Tokens.Add(new Token(TokenType.Parenthesis, new string(new[] { input[i] })));
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
i = ExtractOperator(input, i);
|
i = this.ExtractOperator(input, i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private int ExtractData(
|
private Int32 ExtractData(
|
||||||
string input,
|
String input,
|
||||||
int i,
|
Int32 i,
|
||||||
Func<string, TokenType> tokenTypeEvaluation,
|
Func<String, TokenType> tokenTypeEvaluation,
|
||||||
Func<char, bool> evaluation,
|
Func<Char, Boolean> evaluation,
|
||||||
int right = 0,
|
Int32 right = 0,
|
||||||
int left = -1)
|
Int32 left = -1) {
|
||||||
{
|
Int32 charCount = 0;
|
||||||
var charCount = 0;
|
for(Int32 j = i + right; j < input.Length; j++) {
|
||||||
for (var j = i + right; j < input.Length; j++)
|
if(evaluation(input[j])) {
|
||||||
{
|
|
||||||
if (evaluation(input[j]))
|
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
charCount++;
|
charCount++;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Extract and set the value
|
// Extract and set the value
|
||||||
var value = input.SliceLength(i + right, charCount);
|
String value = input.SliceLength(i + right, charCount);
|
||||||
Tokens.Add(new Token(tokenTypeEvaluation(value), value));
|
this.Tokens.Add(new Token(tokenTypeEvaluation(value), value));
|
||||||
|
|
||||||
i += charCount + left;
|
i += charCount + left;
|
||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
|
|
||||||
private int ExtractOperator(string input, int i) =>
|
private Int32 ExtractOperator(String input, Int32 i) =>
|
||||||
ExtractData(input, i, x => TokenType.Operator, x => x == OpenFuncChar ||
|
this.ExtractData(input, i, x => TokenType.Operator, x => x == OpenFuncChar ||
|
||||||
x == CommaChar ||
|
x == CommaChar ||
|
||||||
x == PeriodChar ||
|
x == PeriodChar ||
|
||||||
x == StringQuotedChar ||
|
x == StringQuotedChar ||
|
||||||
char.IsWhiteSpace(x) ||
|
Char.IsWhiteSpace(x) ||
|
||||||
char.IsNumber(x));
|
Char.IsNumber(x));
|
||||||
|
|
||||||
private int ExtractFunctionOrMember(string input, int i) =>
|
private Int32 ExtractFunctionOrMember(String input, Int32 i) =>
|
||||||
ExtractData(input, i, ResolveFunctionOrMemberType, x => x == OpenFuncChar ||
|
this.ExtractData(input, i, this.ResolveFunctionOrMemberType, x => x == OpenFuncChar ||
|
||||||
x == CloseFuncChar ||
|
x == CloseFuncChar ||
|
||||||
x == CommaChar ||
|
x == CommaChar ||
|
||||||
char.IsWhiteSpace(x));
|
Char.IsWhiteSpace(x));
|
||||||
|
|
||||||
private int ExtractNumber(string input, int i) =>
|
private Int32 ExtractNumber(String input, Int32 i) =>
|
||||||
ExtractData(input, i, x => TokenType.Number,
|
this.ExtractData(input, i, x => TokenType.Number,
|
||||||
x => !char.IsNumber(x) && x != PeriodChar && x != NegativeChar);
|
x => !Char.IsNumber(x) && x != PeriodChar && x != NegativeChar);
|
||||||
|
|
||||||
private int ExtractString(string input, int i)
|
private Int32 ExtractString(String input, Int32 i) {
|
||||||
{
|
Int32 length = this.ExtractData(input, i, x => TokenType.String, x => x == StringQuotedChar, 1, 1);
|
||||||
var length = ExtractData(input, i, x => TokenType.String, x => x == StringQuotedChar, 1, 1);
|
|
||||||
|
|
||||||
// open string, report issue
|
// open string, report issue
|
||||||
if (length == input.Length && input[length - 1] != StringQuotedChar)
|
if(length == input.Length && input[length - 1] != StringQuotedChar) {
|
||||||
throw new FormatException($"Parser error (Position {i}): Expected '\"' but got '{input[length - 1]}'.");
|
throw new FormatException($"Parser error (Position {i}): Expected '\"' but got '{input[length - 1]}'.");
|
||||||
|
}
|
||||||
|
|
||||||
return length;
|
return length;
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool CompareOperators(string op1, string op2)
|
private Boolean CompareOperators(String op1, String op2)
|
||||||
=> CompareOperators(GetOperatorOrDefault(op1), GetOperatorOrDefault(op2));
|
=> CompareOperators(this.GetOperatorOrDefault(op1), this.GetOperatorOrDefault(op2));
|
||||||
|
|
||||||
private Operator GetOperatorOrDefault(string op)
|
private Operator GetOperatorOrDefault(String op)
|
||||||
=> _operators.FirstOrDefault(x => x.Name == op) ?? new Operator { Name = op, Precedence = 0 };
|
=> this._operators.FirstOrDefault(x => x.Name == op) ?? new Operator { Name = op, Precedence = 0 };
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents an operator with precedence.
|
/// Represents an operator with precedence.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class Operator
|
public class Operator {
|
||||||
{
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the name.
|
/// Gets or sets the name.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <value>
|
/// <value>
|
||||||
/// The name.
|
/// The name.
|
||||||
/// </value>
|
/// </value>
|
||||||
public string Name { get; set; }
|
public String Name {
|
||||||
|
get; set;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the precedence.
|
/// Gets or sets the precedence.
|
||||||
@ -367,7 +353,9 @@
|
|||||||
/// <value>
|
/// <value>
|
||||||
/// The precedence.
|
/// The precedence.
|
||||||
/// </value>
|
/// </value>
|
||||||
public int Precedence { get; set; }
|
public Int32 Precedence {
|
||||||
|
get; set;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets a value indicating whether [right associative].
|
/// Gets or sets a value indicating whether [right associative].
|
||||||
@ -375,23 +363,23 @@
|
|||||||
/// <value>
|
/// <value>
|
||||||
/// <c>true</c> if [right associative]; otherwise, <c>false</c>.
|
/// <c>true</c> if [right associative]; otherwise, <c>false</c>.
|
||||||
/// </value>
|
/// </value>
|
||||||
public bool RightAssociative { get; set; }
|
public Boolean RightAssociative {
|
||||||
|
get; set;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents a Token structure.
|
/// Represents a Token structure.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public struct Token
|
public struct Token {
|
||||||
{
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="Token"/> struct.
|
/// Initializes a new instance of the <see cref="Token"/> struct.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="type">The type.</param>
|
/// <param name="type">The type.</param>
|
||||||
/// <param name="value">The value.</param>
|
/// <param name="value">The value.</param>
|
||||||
public Token(TokenType type, string value)
|
public Token(TokenType type, String value) {
|
||||||
{
|
this.Type = type;
|
||||||
Type = type;
|
this.Value = type == TokenType.Function || type == TokenType.Operator ? value.ToLowerInvariant() : value;
|
||||||
Value = type == TokenType.Function || type == TokenType.Operator ? value.ToLowerInvariant() : value;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -400,7 +388,9 @@
|
|||||||
/// <value>
|
/// <value>
|
||||||
/// The type.
|
/// The type.
|
||||||
/// </value>
|
/// </value>
|
||||||
public TokenType Type { get; set; }
|
public TokenType Type {
|
||||||
|
get; set;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the value.
|
/// Gets the value.
|
||||||
@ -408,14 +398,15 @@
|
|||||||
/// <value>
|
/// <value>
|
||||||
/// The value.
|
/// The value.
|
||||||
/// </value>
|
/// </value>
|
||||||
public string Value { get; }
|
public String Value {
|
||||||
|
get;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Enums the token types.
|
/// Enums the token types.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public enum TokenType
|
public enum TokenType {
|
||||||
{
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The number
|
/// The number
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -1,37 +1,32 @@
|
|||||||
namespace Unosquare.Swan.Lite.Abstractions
|
using System;
|
||||||
{
|
using System.Collections.Concurrent;
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Generic;
|
||||||
using System.Collections.Generic;
|
using System.ComponentModel;
|
||||||
using System.ComponentModel;
|
using System.Linq;
|
||||||
using System.Linq;
|
using System.Runtime.CompilerServices;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Threading.Tasks;
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
|
namespace Unosquare.Swan.Lite.Abstractions {
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A base class for implementing models that fire notifications when their properties change.
|
/// A base class for implementing models that fire notifications when their properties change.
|
||||||
/// This class is ideal for implementing MVVM driven UIs.
|
/// This class is ideal for implementing MVVM driven UIs.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <seealso cref="INotifyPropertyChanged" />
|
/// <seealso cref="INotifyPropertyChanged" />
|
||||||
public abstract class ViewModelBase : INotifyPropertyChanged
|
public abstract class ViewModelBase : INotifyPropertyChanged {
|
||||||
{
|
private readonly ConcurrentDictionary<String, Boolean> QueuedNotifications = new ConcurrentDictionary<String, Boolean>();
|
||||||
private readonly ConcurrentDictionary<string, bool> QueuedNotifications = new ConcurrentDictionary<string, bool>();
|
private readonly Boolean UseDeferredNotifications;
|
||||||
private readonly bool UseDeferredNotifications;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="ViewModelBase"/> class.
|
/// Initializes a new instance of the <see cref="ViewModelBase"/> class.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="useDeferredNotifications">Set to <c>true</c> to use deferred notifications in the background.</param>
|
/// <param name="useDeferredNotifications">Set to <c>true</c> to use deferred notifications in the background.</param>
|
||||||
protected ViewModelBase(bool useDeferredNotifications)
|
protected ViewModelBase(Boolean useDeferredNotifications) => this.UseDeferredNotifications = useDeferredNotifications;
|
||||||
{
|
|
||||||
UseDeferredNotifications = useDeferredNotifications;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="ViewModelBase"/> class.
|
/// Initializes a new instance of the <see cref="ViewModelBase"/> class.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
protected ViewModelBase()
|
protected ViewModelBase()
|
||||||
: this(false)
|
: this(false) {
|
||||||
{
|
|
||||||
// placeholder
|
// placeholder
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -52,13 +47,13 @@
|
|||||||
/// <param name="notifyAlso">An rray of property names to notify in addition to notifying the changes on the current property name.</param>
|
/// <param name="notifyAlso">An rray of property names to notify in addition to notifying the changes on the current property name.</param>
|
||||||
/// <returns>True if the value was changed, false if the existing value matched the
|
/// <returns>True if the value was changed, false if the existing value matched the
|
||||||
/// desired value.</returns>
|
/// desired value.</returns>
|
||||||
protected bool SetProperty<T>(ref T storage, T value, [CallerMemberName] string propertyName = "", string[] notifyAlso = null)
|
protected Boolean SetProperty<T>(ref T storage, T value, [CallerMemberName] String propertyName = "", String[] notifyAlso = null) {
|
||||||
{
|
if(EqualityComparer<T>.Default.Equals(storage, value)) {
|
||||||
if (EqualityComparer<T>.Default.Equals(storage, value))
|
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
storage = value;
|
storage = value;
|
||||||
NotifyPropertyChanged(propertyName, notifyAlso);
|
this.NotifyPropertyChanged(propertyName, notifyAlso);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -66,54 +61,55 @@
|
|||||||
/// Notifies one or more properties changed.
|
/// Notifies one or more properties changed.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="propertyNames">The property names.</param>
|
/// <param name="propertyNames">The property names.</param>
|
||||||
protected void NotifyPropertyChanged(params string[] propertyNames) => NotifyPropertyChanged(null, propertyNames);
|
protected void NotifyPropertyChanged(params String[] propertyNames) => this.NotifyPropertyChanged(null, propertyNames);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Notifies one or more properties changed.
|
/// Notifies one or more properties changed.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="mainProperty">The main property.</param>
|
/// <param name="mainProperty">The main property.</param>
|
||||||
/// <param name="auxiliaryProperties">The auxiliary properties.</param>
|
/// <param name="auxiliaryProperties">The auxiliary properties.</param>
|
||||||
private void NotifyPropertyChanged(string mainProperty, string[] auxiliaryProperties)
|
private void NotifyPropertyChanged(String mainProperty, String[] auxiliaryProperties) {
|
||||||
{
|
|
||||||
// Queue property notification
|
// Queue property notification
|
||||||
if (string.IsNullOrWhiteSpace(mainProperty) == false)
|
if(String.IsNullOrWhiteSpace(mainProperty) == false) {
|
||||||
QueuedNotifications[mainProperty] = true;
|
this.QueuedNotifications[mainProperty] = true;
|
||||||
|
}
|
||||||
|
|
||||||
// Set the state for notification properties
|
// Set the state for notification properties
|
||||||
if (auxiliaryProperties != null)
|
if(auxiliaryProperties != null) {
|
||||||
{
|
foreach(String property in auxiliaryProperties) {
|
||||||
foreach (var property in auxiliaryProperties)
|
if(String.IsNullOrWhiteSpace(property) == false) {
|
||||||
{
|
this.QueuedNotifications[property] = true;
|
||||||
if (string.IsNullOrWhiteSpace(property) == false)
|
}
|
||||||
QueuedNotifications[property] = true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Depending on operation mode, either fire the notifications in the background
|
// Depending on operation mode, either fire the notifications in the background
|
||||||
// or fire them immediately
|
// or fire them immediately
|
||||||
if (UseDeferredNotifications)
|
if(this.UseDeferredNotifications) {
|
||||||
Task.Run(() => NotifyQueuedProperties());
|
_ = Task.Run(() => this.NotifyQueuedProperties());
|
||||||
else
|
} else {
|
||||||
NotifyQueuedProperties();
|
this.NotifyQueuedProperties();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Notifies the queued properties and resets the property name to a non-queued stated.
|
/// Notifies the queued properties and resets the property name to a non-queued stated.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private void NotifyQueuedProperties()
|
private void NotifyQueuedProperties() {
|
||||||
{
|
|
||||||
// get a snapshot of property names.
|
// get a snapshot of property names.
|
||||||
var propertyNames = QueuedNotifications.Keys.ToArray();
|
String[] propertyNames = this.QueuedNotifications.Keys.ToArray();
|
||||||
|
|
||||||
// Iterate through the properties
|
// Iterate through the properties
|
||||||
foreach (var property in propertyNames)
|
foreach(String property in propertyNames) {
|
||||||
{
|
|
||||||
// don't notify if we don't have a change
|
// don't notify if we don't have a change
|
||||||
if (!QueuedNotifications[property]) continue;
|
if(!this.QueuedNotifications[property]) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
// notify and reset queued state to false
|
// notify and reset queued state to false
|
||||||
try { OnPropertyChanged(property); }
|
try {
|
||||||
finally { QueuedNotifications[property] = false; }
|
this.OnPropertyChanged(property);
|
||||||
|
} finally { this.QueuedNotifications[property] = false; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -121,7 +117,7 @@
|
|||||||
/// Called when a property changes its backing value.
|
/// Called when a property changes its backing value.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="propertyName">Name of the property.</param>
|
/// <param name="propertyName">Name of the property.</param>
|
||||||
private void OnPropertyChanged(string propertyName) =>
|
private void OnPropertyChanged(String propertyName) =>
|
||||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName ?? string.Empty));
|
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName ?? String.Empty));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,26 +1,24 @@
|
|||||||
namespace Unosquare.Swan
|
using System;
|
||||||
{
|
using Unosquare.Swan.Abstractions;
|
||||||
using Abstractions;
|
|
||||||
|
|
||||||
|
namespace Unosquare.Swan {
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Fast, atomic boolean combining interlocked to write value and volatile to read values.
|
/// Fast, atomic boolean combining interlocked to write value and volatile to read values.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public sealed class AtomicBoolean : AtomicTypeBase<bool>
|
public sealed class AtomicBoolean : AtomicTypeBase<Boolean> {
|
||||||
{
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="AtomicBoolean"/> class.
|
/// Initializes a new instance of the <see cref="AtomicBoolean"/> class.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="initialValue">if set to <c>true</c> [initial value].</param>
|
/// <param name="initialValue">if set to <c>true</c> [initial value].</param>
|
||||||
public AtomicBoolean(bool initialValue = default)
|
public AtomicBoolean(Boolean initialValue = default)
|
||||||
: base(initialValue ? 1 : 0)
|
: base(initialValue ? 1 : 0) {
|
||||||
{
|
|
||||||
// placeholder
|
// placeholder
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
protected override bool FromLong(long backingValue) => backingValue != 0;
|
protected override Boolean FromLong(Int64 backingValue) => backingValue != 0;
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
protected override long ToLong(bool value) => value ? 1 : 0;
|
protected override Int64 ToLong(Boolean value) => value ? 1 : 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,29 +1,26 @@
|
|||||||
namespace Unosquare.Swan
|
using System;
|
||||||
{
|
using Unosquare.Swan.Abstractions;
|
||||||
using System;
|
|
||||||
using Abstractions;
|
|
||||||
|
|
||||||
|
namespace Unosquare.Swan {
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Fast, atomic double combining interlocked to write value and volatile to read values.
|
/// Fast, atomic double combining interlocked to write value and volatile to read values.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public sealed class AtomicDouble : AtomicTypeBase<double>
|
public sealed class AtomicDouble : AtomicTypeBase<Double> {
|
||||||
{
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="AtomicDouble"/> class.
|
/// Initializes a new instance of the <see cref="AtomicDouble"/> class.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="initialValue">if set to <c>true</c> [initial value].</param>
|
/// <param name="initialValue">if set to <c>true</c> [initial value].</param>
|
||||||
public AtomicDouble(double initialValue = default)
|
public AtomicDouble(Double initialValue = default)
|
||||||
: base(BitConverter.DoubleToInt64Bits(initialValue))
|
: base(BitConverter.DoubleToInt64Bits(initialValue)) {
|
||||||
{
|
|
||||||
// placeholder
|
// placeholder
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
protected override double FromLong(long backingValue) =>
|
protected override Double FromLong(Int64 backingValue) =>
|
||||||
BitConverter.Int64BitsToDouble(backingValue);
|
BitConverter.Int64BitsToDouble(backingValue);
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
protected override long ToLong(double value) =>
|
protected override Int64 ToLong(Double value) =>
|
||||||
BitConverter.DoubleToInt64Bits(value);
|
BitConverter.DoubleToInt64Bits(value);
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,29 +1,26 @@
|
|||||||
namespace Unosquare.Swan
|
using System;
|
||||||
{
|
using Unosquare.Swan.Abstractions;
|
||||||
using System;
|
|
||||||
using Abstractions;
|
|
||||||
|
|
||||||
|
namespace Unosquare.Swan {
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents an atomically readable or writable integer.
|
/// Represents an atomically readable or writable integer.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class AtomicInteger : AtomicTypeBase<int>
|
public class AtomicInteger : AtomicTypeBase<Int32> {
|
||||||
{
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="AtomicInteger"/> class.
|
/// Initializes a new instance of the <see cref="AtomicInteger"/> class.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="initialValue">if set to <c>true</c> [initial value].</param>
|
/// <param name="initialValue">if set to <c>true</c> [initial value].</param>
|
||||||
public AtomicInteger(int initialValue = default)
|
public AtomicInteger(Int32 initialValue = default)
|
||||||
: base(Convert.ToInt64(initialValue))
|
: base(Convert.ToInt64(initialValue)) {
|
||||||
{
|
|
||||||
// placeholder
|
// placeholder
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
protected override int FromLong(long backingValue) =>
|
protected override Int32 FromLong(Int64 backingValue) =>
|
||||||
Convert.ToInt32(backingValue);
|
Convert.ToInt32(backingValue);
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
protected override long ToLong(int value) =>
|
protected override Int64 ToLong(Int32 value) =>
|
||||||
Convert.ToInt64(value);
|
Convert.ToInt64(value);
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,26 +1,24 @@
|
|||||||
namespace Unosquare.Swan
|
using System;
|
||||||
{
|
using Unosquare.Swan.Abstractions;
|
||||||
using Abstractions;
|
|
||||||
|
|
||||||
|
namespace Unosquare.Swan {
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Fast, atomioc long combining interlocked to write value and volatile to read values.
|
/// Fast, atomioc long combining interlocked to write value and volatile to read values.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public sealed class AtomicLong : AtomicTypeBase<long>
|
public sealed class AtomicLong : AtomicTypeBase<Int64> {
|
||||||
{
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="AtomicLong"/> class.
|
/// Initializes a new instance of the <see cref="AtomicLong"/> class.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="initialValue">if set to <c>true</c> [initial value].</param>
|
/// <param name="initialValue">if set to <c>true</c> [initial value].</param>
|
||||||
public AtomicLong(long initialValue = default)
|
public AtomicLong(Int64 initialValue = default)
|
||||||
: base(initialValue)
|
: base(initialValue) {
|
||||||
{
|
|
||||||
// placeholder
|
// placeholder
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
protected override long FromLong(long backingValue) => backingValue;
|
protected override Int64 FromLong(Int64 backingValue) => backingValue;
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
protected override long ToLong(long value) => value;
|
protected override Int64 ToLong(Int64 value) => value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,31 +1,27 @@
|
|||||||
namespace Unosquare.Swan.Attributes
|
using System;
|
||||||
{
|
|
||||||
using System;
|
|
||||||
|
|
||||||
|
namespace Unosquare.Swan.Attributes {
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Models an option specification.
|
/// Models an option specification.
|
||||||
/// Based on CommandLine (Copyright 2005-2015 Giacomo Stelluti Scala and Contributors.).
|
/// Based on CommandLine (Copyright 2005-2015 Giacomo Stelluti Scala and Contributors.).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[AttributeUsage(AttributeTargets.Property)]
|
[AttributeUsage(AttributeTargets.Property)]
|
||||||
public sealed class ArgumentOptionAttribute
|
public sealed class ArgumentOptionAttribute
|
||||||
: Attribute
|
: Attribute {
|
||||||
{
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="ArgumentOptionAttribute"/> class.
|
/// Initializes a new instance of the <see cref="ArgumentOptionAttribute"/> class.
|
||||||
/// The default long name will be inferred from target property.
|
/// The default long name will be inferred from target property.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public ArgumentOptionAttribute()
|
public ArgumentOptionAttribute()
|
||||||
: this(string.Empty, string.Empty)
|
: this(String.Empty, String.Empty) {
|
||||||
{
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="ArgumentOptionAttribute"/> class.
|
/// Initializes a new instance of the <see cref="ArgumentOptionAttribute"/> class.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="longName">The long name of the option.</param>
|
/// <param name="longName">The long name of the option.</param>
|
||||||
public ArgumentOptionAttribute(string longName)
|
public ArgumentOptionAttribute(String longName)
|
||||||
: this(string.Empty, longName)
|
: this(String.Empty, longName) {
|
||||||
{
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -33,24 +29,21 @@
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="shortName">The short name of the option.</param>
|
/// <param name="shortName">The short name of the option.</param>
|
||||||
/// <param name="longName">The long name of the option or null if not used.</param>
|
/// <param name="longName">The long name of the option or null if not used.</param>
|
||||||
public ArgumentOptionAttribute(char shortName, string longName)
|
public ArgumentOptionAttribute(Char shortName, String longName)
|
||||||
: this(new string(shortName, 1), longName)
|
: this(new String(shortName, 1), longName) {
|
||||||
{
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="ArgumentOptionAttribute"/> class.
|
/// Initializes a new instance of the <see cref="ArgumentOptionAttribute"/> class.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="shortName">The short name of the option..</param>
|
/// <param name="shortName">The short name of the option..</param>
|
||||||
public ArgumentOptionAttribute(char shortName)
|
public ArgumentOptionAttribute(Char shortName)
|
||||||
: this(new string(shortName, 1), string.Empty)
|
: this(new String(shortName, 1), String.Empty) {
|
||||||
{
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private ArgumentOptionAttribute(string shortName, string longName)
|
private ArgumentOptionAttribute(String shortName, String longName) {
|
||||||
{
|
this.ShortName = shortName ?? throw new ArgumentNullException(nameof(shortName));
|
||||||
ShortName = shortName ?? throw new ArgumentNullException(nameof(shortName));
|
this.LongName = longName ?? throw new ArgumentNullException(nameof(longName));
|
||||||
LongName = longName ?? throw new ArgumentNullException(nameof(longName));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -59,7 +52,9 @@
|
|||||||
/// <value>
|
/// <value>
|
||||||
/// The long name.
|
/// The long name.
|
||||||
/// </value>
|
/// </value>
|
||||||
public string LongName { get; }
|
public String LongName {
|
||||||
|
get;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets a short name of this command line option, made of one character.
|
/// Gets a short name of this command line option, made of one character.
|
||||||
@ -67,13 +62,15 @@
|
|||||||
/// <value>
|
/// <value>
|
||||||
/// The short name.
|
/// The short name.
|
||||||
/// </value>
|
/// </value>
|
||||||
public string ShortName { get; }
|
public String ShortName {
|
||||||
|
get;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// When applying attribute to <see cref="System.Collections.Generic.IEnumerable{T}"/> target properties,
|
/// When applying attribute to <see cref="System.Collections.Generic.IEnumerable{T}"/> target properties,
|
||||||
/// it allows you to split an argument and consume its content as a sequence.
|
/// it allows you to split an argument and consume its content as a sequence.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public char Separator { get; set; } = '\0';
|
public Char Separator { get; set; } = '\0';
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets mapped property default value.
|
/// Gets or sets mapped property default value.
|
||||||
@ -81,7 +78,9 @@
|
|||||||
/// <value>
|
/// <value>
|
||||||
/// The default value.
|
/// The default value.
|
||||||
/// </value>
|
/// </value>
|
||||||
public object DefaultValue { get; set; }
|
public Object DefaultValue {
|
||||||
|
get; set;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets a value indicating whether a command line option is required.
|
/// Gets or sets a value indicating whether a command line option is required.
|
||||||
@ -89,7 +88,9 @@
|
|||||||
/// <value>
|
/// <value>
|
||||||
/// <c>true</c> if required; otherwise, <c>false</c>.
|
/// <c>true</c> if required; otherwise, <c>false</c>.
|
||||||
/// </value>
|
/// </value>
|
||||||
public bool Required { get; set; }
|
public Boolean Required {
|
||||||
|
get; set;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets a short description of this command line option. Usually a sentence summary.
|
/// Gets or sets a short description of this command line option. Usually a sentence summary.
|
||||||
@ -97,6 +98,8 @@
|
|||||||
/// <value>
|
/// <value>
|
||||||
/// The help text.
|
/// The help text.
|
||||||
/// </value>
|
/// </value>
|
||||||
public string HelpText { get; set; }
|
public String HelpText {
|
||||||
|
get; set;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,13 +1,11 @@
|
|||||||
namespace Unosquare.Swan.Attributes
|
using System;
|
||||||
{
|
|
||||||
using System;
|
|
||||||
|
|
||||||
|
namespace Unosquare.Swan.Attributes {
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents an attribute to select which properties are copyable between objects.
|
/// Represents an attribute to select which properties are copyable between objects.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <seealso cref="Attribute" />
|
/// <seealso cref="Attribute" />
|
||||||
[AttributeUsage(AttributeTargets.Property)]
|
[AttributeUsage(AttributeTargets.Property)]
|
||||||
public class CopyableAttribute : Attribute
|
public class CopyableAttribute : Attribute {
|
||||||
{
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,23 +1,20 @@
|
|||||||
namespace Unosquare.Swan.Attributes
|
using System;
|
||||||
{
|
|
||||||
using System;
|
|
||||||
|
|
||||||
|
namespace Unosquare.Swan.Attributes {
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// An attribute used to help setup a property behavior when serialize/deserialize JSON.
|
/// An attribute used to help setup a property behavior when serialize/deserialize JSON.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <seealso cref="Attribute" />
|
/// <seealso cref="Attribute" />
|
||||||
[AttributeUsage(AttributeTargets.Property)]
|
[AttributeUsage(AttributeTargets.Property)]
|
||||||
public sealed class JsonPropertyAttribute : Attribute
|
public sealed class JsonPropertyAttribute : Attribute {
|
||||||
{
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="JsonPropertyAttribute" /> class.
|
/// Initializes a new instance of the <see cref="JsonPropertyAttribute" /> class.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="propertyName">Name of the property.</param>
|
/// <param name="propertyName">Name of the property.</param>
|
||||||
/// <param name="ignored">if set to <c>true</c> [ignored].</param>
|
/// <param name="ignored">if set to <c>true</c> [ignored].</param>
|
||||||
public JsonPropertyAttribute(string propertyName, bool ignored = false)
|
public JsonPropertyAttribute(String propertyName, Boolean ignored = false) {
|
||||||
{
|
this.PropertyName = propertyName ?? throw new ArgumentNullException(nameof(propertyName));
|
||||||
PropertyName = propertyName ?? throw new ArgumentNullException(nameof(propertyName));
|
this.Ignored = ignored;
|
||||||
Ignored = ignored;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -26,7 +23,9 @@
|
|||||||
/// <value>
|
/// <value>
|
||||||
/// The name of the property.
|
/// The name of the property.
|
||||||
/// </value>
|
/// </value>
|
||||||
public string PropertyName { get; }
|
public String PropertyName {
|
||||||
|
get;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets a value indicating whether this <see cref="JsonPropertyAttribute" /> is ignored.
|
/// Gets or sets a value indicating whether this <see cref="JsonPropertyAttribute" /> is ignored.
|
||||||
@ -34,6 +33,8 @@
|
|||||||
/// <value>
|
/// <value>
|
||||||
/// <c>true</c> if ignored; otherwise, <c>false</c>.
|
/// <c>true</c> if ignored; otherwise, <c>false</c>.
|
||||||
/// </value>
|
/// </value>
|
||||||
public bool Ignored { get; }
|
public Boolean Ignored {
|
||||||
|
get;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,7 +1,6 @@
|
|||||||
namespace Unosquare.Swan.Attributes
|
using System;
|
||||||
{
|
|
||||||
using System;
|
|
||||||
|
|
||||||
|
namespace Unosquare.Swan.Attributes {
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// An attribute used to include additional information to a Property for serialization.
|
/// An attribute used to include additional information to a Property for serialization.
|
||||||
///
|
///
|
||||||
@ -9,15 +8,16 @@
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <seealso cref="System.Attribute" />
|
/// <seealso cref="System.Attribute" />
|
||||||
[AttributeUsage(AttributeTargets.Property)]
|
[AttributeUsage(AttributeTargets.Property)]
|
||||||
public sealed class PropertyDisplayAttribute : Attribute
|
public sealed class PropertyDisplayAttribute : Attribute {
|
||||||
{
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the name.
|
/// Gets or sets the name.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <value>
|
/// <value>
|
||||||
/// The name.
|
/// The name.
|
||||||
/// </value>
|
/// </value>
|
||||||
public string Name { get; set; }
|
public String Name {
|
||||||
|
get; set;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the description.
|
/// Gets or sets the description.
|
||||||
@ -25,7 +25,9 @@
|
|||||||
/// <value>
|
/// <value>
|
||||||
/// The description.
|
/// The description.
|
||||||
/// </value>
|
/// </value>
|
||||||
public string Description { get; set; }
|
public String Description {
|
||||||
|
get; set;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the name of the group.
|
/// Gets or sets the name of the group.
|
||||||
@ -33,7 +35,9 @@
|
|||||||
/// <value>
|
/// <value>
|
||||||
/// The name of the group.
|
/// The name of the group.
|
||||||
/// </value>
|
/// </value>
|
||||||
public string GroupName { get; set; }
|
public String GroupName {
|
||||||
|
get; set;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the default value.
|
/// Gets or sets the default value.
|
||||||
@ -41,7 +45,9 @@
|
|||||||
/// <value>
|
/// <value>
|
||||||
/// The default value.
|
/// The default value.
|
||||||
/// </value>
|
/// </value>
|
||||||
public object DefaultValue { get; set; }
|
public Object DefaultValue {
|
||||||
|
get; set;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the format string to call with method <c>ToString</c>.
|
/// Gets or sets the format string to call with method <c>ToString</c>.
|
||||||
@ -49,6 +55,8 @@
|
|||||||
/// <value>
|
/// <value>
|
||||||
/// The format.
|
/// The format.
|
||||||
/// </value>
|
/// </value>
|
||||||
public string Format { get; set; }
|
public String Format {
|
||||||
|
get; set;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,23 +1,18 @@
|
|||||||
namespace Unosquare.Swan.Attributes
|
using System;
|
||||||
{
|
|
||||||
using System;
|
|
||||||
|
|
||||||
|
namespace Unosquare.Swan.Attributes {
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// An attribute used to help conversion structs back and forth into arrays of bytes via
|
/// An attribute used to help conversion structs back and forth into arrays of bytes via
|
||||||
/// extension methods included in this library ToStruct and ToBytes.
|
/// extension methods included in this library ToStruct and ToBytes.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <seealso cref="Attribute" />
|
/// <seealso cref="Attribute" />
|
||||||
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Struct)]
|
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Struct)]
|
||||||
public class StructEndiannessAttribute : Attribute
|
public class StructEndiannessAttribute : Attribute {
|
||||||
{
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="StructEndiannessAttribute"/> class.
|
/// Initializes a new instance of the <see cref="StructEndiannessAttribute"/> class.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="endianness">The endianness.</param>
|
/// <param name="endianness">The endianness.</param>
|
||||||
public StructEndiannessAttribute(Endianness endianness)
|
public StructEndiannessAttribute(Endianness endianness) => this.Endianness = endianness;
|
||||||
{
|
|
||||||
Endianness = endianness;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the endianness.
|
/// Gets the endianness.
|
||||||
@ -25,6 +20,8 @@
|
|||||||
/// <value>
|
/// <value>
|
||||||
/// The endianness.
|
/// The endianness.
|
||||||
/// </value>
|
/// </value>
|
||||||
public Endianness Endianness { get; }
|
public Endianness Endianness {
|
||||||
|
get;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,54 +1,50 @@
|
|||||||
namespace Unosquare.Swan.Attributes
|
using System;
|
||||||
{
|
using System.Text.RegularExpressions;
|
||||||
using System;
|
using Unosquare.Swan.Abstractions;
|
||||||
using System.Text.RegularExpressions;
|
|
||||||
using Abstractions;
|
|
||||||
|
|
||||||
|
namespace Unosquare.Swan.Attributes {
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Regex validator.
|
/// Regex validator.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[AttributeUsage(AttributeTargets.Property)]
|
[AttributeUsage(AttributeTargets.Property)]
|
||||||
public class MatchAttribute : Attribute, IValidator
|
public class MatchAttribute : Attribute, IValidator {
|
||||||
{
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="MatchAttribute" /> class.
|
/// Initializes a new instance of the <see cref="MatchAttribute" /> class.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="regex">A regex string.</param>
|
/// <param name="regex">A regex string.</param>
|
||||||
/// <param name="errorMessage">The error message.</param>
|
/// <param name="errorMessage">The error message.</param>
|
||||||
/// <exception cref="ArgumentNullException">Expression.</exception>
|
/// <exception cref="ArgumentNullException">Expression.</exception>
|
||||||
public MatchAttribute(string regex, string errorMessage = null)
|
public MatchAttribute(String regex, String errorMessage = null) {
|
||||||
{
|
this.Expression = regex ?? throw new ArgumentNullException(nameof(this.Expression));
|
||||||
Expression = regex ?? throw new ArgumentNullException(nameof(Expression));
|
this.ErrorMessage = errorMessage ?? "String does not match the specified regular expression";
|
||||||
ErrorMessage = errorMessage ?? "String does not match the specified regular expression";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The string regex used to find a match.
|
/// The string regex used to find a match.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string Expression { get; }
|
public String Expression {
|
||||||
|
get;
|
||||||
/// <inheritdoc/>
|
|
||||||
public string ErrorMessage { get; internal set; }
|
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public bool IsValid<T>(T value)
|
|
||||||
{
|
|
||||||
if (Equals(value, default(T)))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
return !(value is string)
|
|
||||||
? throw new ArgumentException("Property is not a string")
|
|
||||||
: Regex.IsMatch(value.ToString(), Expression);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public String ErrorMessage {
|
||||||
|
get; internal set;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public Boolean IsValid<T>(T value) => Equals(value, default(T))
|
||||||
|
? false
|
||||||
|
: !(value is String)
|
||||||
|
? throw new ArgumentException("Property is not a string")
|
||||||
|
: Regex.IsMatch(value.ToString(), this.Expression);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Email validator.
|
/// Email validator.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[AttributeUsage(AttributeTargets.Property)]
|
[AttributeUsage(AttributeTargets.Property)]
|
||||||
public class EmailAttribute : MatchAttribute
|
public class EmailAttribute : MatchAttribute {
|
||||||
{
|
private const String EmailRegExp =
|
||||||
private const string EmailRegExp =
|
|
||||||
@"^(?("")("".+?(?<!\\)""@)|(([0-9a-z]((\.(?!\.))|[-!#\$%&'\*\+/=\?\^`\{\}\|~\w])*)(?<=[0-9a-z])@))" +
|
@"^(?("")("".+?(?<!\\)""@)|(([0-9a-z]((\.(?!\.))|[-!#\$%&'\*\+/=\?\^`\{\}\|~\w])*)(?<=[0-9a-z])@))" +
|
||||||
@"(?(\[)(\[(\d{1,3}\.){3}\d{1,3}\])|(([0-9a-z][-0-9a-z]*[0-9a-z]*\.)+[a-z0-9][\-a-z0-9]{0,22}[a-z0-9]))$";
|
@"(?(\[)(\[(\d{1,3}\.){3}\d{1,3}\])|(([0-9a-z][-0-9a-z]*[0-9a-z]*\.)+[a-z0-9][\-a-z0-9]{0,22}[a-z0-9]))$";
|
||||||
|
|
||||||
@ -56,9 +52,8 @@
|
|||||||
/// Initializes a new instance of the <see cref="EmailAttribute"/> class.
|
/// Initializes a new instance of the <see cref="EmailAttribute"/> class.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="errorMessage">The error message.</param>
|
/// <param name="errorMessage">The error message.</param>
|
||||||
public EmailAttribute(string errorMessage = null)
|
public EmailAttribute(String errorMessage = null)
|
||||||
: base(EmailRegExp, errorMessage ?? "String is not an email")
|
: base(EmailRegExp, errorMessage ?? "String is not an email") {
|
||||||
{
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -66,34 +61,32 @@
|
|||||||
/// A not null validator.
|
/// A not null validator.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[AttributeUsage(AttributeTargets.Property)]
|
[AttributeUsage(AttributeTargets.Property)]
|
||||||
public class NotNullAttribute : Attribute, IValidator
|
public class NotNullAttribute : Attribute, IValidator {
|
||||||
{
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public string ErrorMessage => "Value is null";
|
public String ErrorMessage => "Value is null";
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public bool IsValid<T>(T value) => !Equals(default(T), value);
|
public Boolean IsValid<T>(T value) => !Equals(default(T), value);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A range constraint validator.
|
/// A range constraint validator.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[AttributeUsage(AttributeTargets.Property)]
|
[AttributeUsage(AttributeTargets.Property)]
|
||||||
public class RangeAttribute : Attribute, IValidator
|
public class RangeAttribute : Attribute, IValidator {
|
||||||
{
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="RangeAttribute"/> class.
|
/// Initializes a new instance of the <see cref="RangeAttribute"/> class.
|
||||||
/// Constructor that takes integer minimum and maximum values.
|
/// Constructor that takes integer minimum and maximum values.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="min">The minimum value.</param>
|
/// <param name="min">The minimum value.</param>
|
||||||
/// <param name="max">The maximum value.</param>
|
/// <param name="max">The maximum value.</param>
|
||||||
public RangeAttribute(int min, int max)
|
public RangeAttribute(Int32 min, Int32 max) {
|
||||||
{
|
if(min >= max) {
|
||||||
if (min >= max)
|
|
||||||
throw new InvalidOperationException("Maximum value must be greater than minimum");
|
throw new InvalidOperationException("Maximum value must be greater than minimum");
|
||||||
|
}
|
||||||
|
|
||||||
Maximum = max;
|
this.Maximum = max;
|
||||||
Minimum = min;
|
this.Minimum = min;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -102,32 +95,36 @@
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="min">The minimum value.</param>
|
/// <param name="min">The minimum value.</param>
|
||||||
/// <param name="max">The maximum value.</param>
|
/// <param name="max">The maximum value.</param>
|
||||||
public RangeAttribute(double min, double max)
|
public RangeAttribute(Double min, Double max) {
|
||||||
{
|
if(min >= max) {
|
||||||
if (min >= max)
|
|
||||||
throw new InvalidOperationException("Maximum value must be greater than minimum");
|
throw new InvalidOperationException("Maximum value must be greater than minimum");
|
||||||
|
}
|
||||||
|
|
||||||
Maximum = max;
|
this.Maximum = max;
|
||||||
Minimum = min;
|
this.Minimum = min;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public string ErrorMessage => "Value is not within the specified range";
|
public String ErrorMessage => "Value is not within the specified range";
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Maximum value for the range.
|
/// Maximum value for the range.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public IComparable Maximum { get; }
|
public IComparable Maximum {
|
||||||
|
get;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Minimum value for the range.
|
/// Minimum value for the range.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public IComparable Minimum { get; }
|
public IComparable Minimum {
|
||||||
|
get;
|
||||||
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public bool IsValid<T>(T value)
|
public Boolean IsValid<T>(T value)
|
||||||
=> value is IComparable comparable
|
=> value is IComparable comparable
|
||||||
? comparable.CompareTo(Minimum) >= 0 && comparable.CompareTo(Maximum) <= 0
|
? comparable.CompareTo(this.Minimum) >= 0 && comparable.CompareTo(this.Maximum) <= 0
|
||||||
: throw new ArgumentException(nameof(value));
|
: throw new ArgumentException(nameof(value));
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,22 +1,17 @@
|
|||||||
namespace Unosquare.Swan.Attributes
|
using System;
|
||||||
{
|
|
||||||
using System;
|
|
||||||
|
|
||||||
|
namespace Unosquare.Swan.Attributes {
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Models a verb option.
|
/// Models a verb option.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[AttributeUsage(AttributeTargets.Property)]
|
[AttributeUsage(AttributeTargets.Property)]
|
||||||
public sealed class VerbOptionAttribute : Attribute
|
public sealed class VerbOptionAttribute : Attribute {
|
||||||
{
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="VerbOptionAttribute" /> class.
|
/// Initializes a new instance of the <see cref="VerbOptionAttribute" /> class.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="name">The name.</param>
|
/// <param name="name">The name.</param>
|
||||||
/// <exception cref="ArgumentNullException">name.</exception>
|
/// <exception cref="ArgumentNullException">name.</exception>
|
||||||
public VerbOptionAttribute(string name)
|
public VerbOptionAttribute(String name) => this.Name = name ?? throw new ArgumentNullException(nameof(name));
|
||||||
{
|
|
||||||
Name = name ?? throw new ArgumentNullException(nameof(name));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the name of the verb option.
|
/// Gets the name of the verb option.
|
||||||
@ -24,7 +19,9 @@
|
|||||||
/// <value>
|
/// <value>
|
||||||
/// Name.
|
/// Name.
|
||||||
/// </value>
|
/// </value>
|
||||||
public string Name { get; }
|
public String Name {
|
||||||
|
get;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets a short description of this command line verb. Usually a sentence summary.
|
/// Gets or sets a short description of this command line verb. Usually a sentence summary.
|
||||||
@ -32,9 +29,11 @@
|
|||||||
/// <value>
|
/// <value>
|
||||||
/// The help text.
|
/// The help text.
|
||||||
/// </value>
|
/// </value>
|
||||||
public string HelpText { get; set; }
|
public String HelpText {
|
||||||
|
get; set;
|
||||||
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override string ToString() => $" {Name}\t\t{HelpText}";
|
public override String ToString() => $" {this.Name}\t\t{this.HelpText}";
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,21 +1,18 @@
|
|||||||
namespace Unosquare.Swan.Components
|
using System.Linq;
|
||||||
{
|
using System.Reflection;
|
||||||
using System.Linq;
|
using Unosquare.Swan.Attributes;
|
||||||
using System.Reflection;
|
using System;
|
||||||
using Attributes;
|
using System.Collections.Generic;
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
|
|
||||||
|
namespace Unosquare.Swan.Components {
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Provides methods to parse command line arguments.
|
/// Provides methods to parse command line arguments.
|
||||||
/// Based on CommandLine (Copyright 2005-2015 Giacomo Stelluti Scala and Contributors.).
|
/// Based on CommandLine (Copyright 2005-2015 Giacomo Stelluti Scala and Contributors.).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public partial class ArgumentParser
|
public partial class ArgumentParser {
|
||||||
{
|
private sealed class Validator {
|
||||||
private sealed class Validator
|
private readonly Object _instance;
|
||||||
{
|
private readonly IEnumerable<String> _args;
|
||||||
private readonly object _instance;
|
|
||||||
private readonly IEnumerable<string> _args;
|
|
||||||
private readonly List<PropertyInfo> _updatedList = new List<PropertyInfo>();
|
private readonly List<PropertyInfo> _updatedList = new List<PropertyInfo>();
|
||||||
private readonly ArgumentParserSettings _settings;
|
private readonly ArgumentParserSettings _settings;
|
||||||
|
|
||||||
@ -23,122 +20,112 @@
|
|||||||
|
|
||||||
public Validator(
|
public Validator(
|
||||||
PropertyInfo[] properties,
|
PropertyInfo[] properties,
|
||||||
IEnumerable<string> args,
|
IEnumerable<String> args,
|
||||||
object instance,
|
Object instance,
|
||||||
ArgumentParserSettings settings)
|
ArgumentParserSettings settings) {
|
||||||
{
|
this._args = args;
|
||||||
_args = args;
|
this._instance = instance;
|
||||||
_instance = instance;
|
this._settings = settings;
|
||||||
_settings = settings;
|
this._properties = properties;
|
||||||
_properties = properties;
|
|
||||||
|
|
||||||
PopulateInstance();
|
this.PopulateInstance();
|
||||||
SetDefaultValues();
|
this.SetDefaultValues();
|
||||||
GetRequiredList();
|
this.GetRequiredList();
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<string> UnknownList { get; } = new List<string>();
|
public List<String> UnknownList { get; } = new List<String>();
|
||||||
public List<string> RequiredList { get; } = new List<string>();
|
public List<String> RequiredList { get; } = new List<String>();
|
||||||
|
|
||||||
public bool IsValid() => (_settings.IgnoreUnknownArguments || !UnknownList.Any()) && !RequiredList.Any();
|
public Boolean IsValid() => (this._settings.IgnoreUnknownArguments || !this.UnknownList.Any()) && !this.RequiredList.Any();
|
||||||
|
|
||||||
public IEnumerable<ArgumentOptionAttribute> GetPropertiesOptions()
|
public IEnumerable<ArgumentOptionAttribute> GetPropertiesOptions()
|
||||||
=> _properties.Select(p => Runtime.AttributeCache.RetrieveOne<ArgumentOptionAttribute>(p))
|
=> this._properties.Select(p => Runtime.AttributeCache.RetrieveOne<ArgumentOptionAttribute>(p))
|
||||||
.Where(x => x != null);
|
.Where(x => x != null);
|
||||||
|
|
||||||
private void GetRequiredList()
|
private void GetRequiredList() {
|
||||||
{
|
foreach(PropertyInfo targetProperty in this._properties) {
|
||||||
foreach (var targetProperty in _properties)
|
ArgumentOptionAttribute optionAttr = Runtime.AttributeCache.RetrieveOne<ArgumentOptionAttribute>(targetProperty);
|
||||||
{
|
|
||||||
var optionAttr = Runtime.AttributeCache.RetrieveOne<ArgumentOptionAttribute>(targetProperty);
|
|
||||||
|
|
||||||
if (optionAttr == null || optionAttr.Required == false)
|
if(optionAttr == null || optionAttr.Required == false) {
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (targetProperty.GetValue(_instance) == null)
|
if(targetProperty.GetValue(this._instance) == null) {
|
||||||
{
|
this.RequiredList.Add(optionAttr.LongName ?? optionAttr.ShortName);
|
||||||
RequiredList.Add(optionAttr.LongName ?? optionAttr.ShortName);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SetDefaultValues()
|
private void SetDefaultValues() {
|
||||||
{
|
foreach(PropertyInfo targetProperty in this._properties.Except(this._updatedList)) {
|
||||||
foreach (var targetProperty in _properties.Except(_updatedList))
|
ArgumentOptionAttribute optionAttr = Runtime.AttributeCache.RetrieveOne<ArgumentOptionAttribute>(targetProperty);
|
||||||
{
|
|
||||||
var optionAttr = Runtime.AttributeCache.RetrieveOne<ArgumentOptionAttribute>(targetProperty);
|
|
||||||
|
|
||||||
var defaultValue = optionAttr?.DefaultValue;
|
Object defaultValue = optionAttr?.DefaultValue;
|
||||||
|
|
||||||
if (defaultValue == null)
|
if(defaultValue == null) {
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (SetPropertyValue(targetProperty, defaultValue.ToString(), _instance, optionAttr))
|
if(this.SetPropertyValue(targetProperty, defaultValue.ToString(), this._instance, optionAttr)) {
|
||||||
_updatedList.Add(targetProperty);
|
this._updatedList.Add(targetProperty);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void PopulateInstance()
|
private void PopulateInstance() {
|
||||||
{
|
const Char dash = '-';
|
||||||
const char dash = '-';
|
String propertyName = String.Empty;
|
||||||
var propertyName = string.Empty;
|
|
||||||
|
|
||||||
foreach (var arg in _args)
|
foreach(String arg in this._args) {
|
||||||
{
|
Boolean ignoreSetValue = String.IsNullOrWhiteSpace(propertyName);
|
||||||
var ignoreSetValue = string.IsNullOrWhiteSpace(propertyName);
|
|
||||||
|
|
||||||
if (ignoreSetValue)
|
if(ignoreSetValue) {
|
||||||
{
|
if(String.IsNullOrWhiteSpace(arg) || arg[0] != dash) {
|
||||||
if (string.IsNullOrWhiteSpace(arg) || arg[0] != dash) continue;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
propertyName = arg.Substring(1);
|
propertyName = arg.Substring(1);
|
||||||
|
|
||||||
if (!string.IsNullOrWhiteSpace(propertyName) && propertyName[0] == dash)
|
if(!String.IsNullOrWhiteSpace(propertyName) && propertyName[0] == dash) {
|
||||||
propertyName = propertyName.Substring(1);
|
propertyName = propertyName.Substring(1);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var targetProperty = TryGetProperty(propertyName);
|
PropertyInfo targetProperty = this.TryGetProperty(propertyName);
|
||||||
|
|
||||||
if (targetProperty == null)
|
if(targetProperty == null) {
|
||||||
{
|
|
||||||
// Skip if the property is not found
|
// Skip if the property is not found
|
||||||
UnknownList.Add(propertyName);
|
this.UnknownList.Add(propertyName);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!ignoreSetValue && SetPropertyValue(targetProperty, arg, _instance))
|
if(!ignoreSetValue && this.SetPropertyValue(targetProperty, arg, this._instance)) {
|
||||||
{
|
this._updatedList.Add(targetProperty);
|
||||||
_updatedList.Add(targetProperty);
|
propertyName = String.Empty;
|
||||||
propertyName = string.Empty;
|
} else if(targetProperty.PropertyType == typeof(Boolean)) {
|
||||||
}
|
|
||||||
else if (targetProperty.PropertyType == typeof(bool))
|
|
||||||
{
|
|
||||||
// If the arg is a boolean property set it to true.
|
// If the arg is a boolean property set it to true.
|
||||||
targetProperty.SetValue(_instance, true);
|
targetProperty.SetValue(this._instance, true);
|
||||||
|
|
||||||
_updatedList.Add(targetProperty);
|
this._updatedList.Add(targetProperty);
|
||||||
propertyName = string.Empty;
|
propertyName = String.Empty;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(propertyName))
|
if(!String.IsNullOrEmpty(propertyName)) {
|
||||||
{
|
this.UnknownList.Add(propertyName);
|
||||||
UnknownList.Add(propertyName);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool SetPropertyValue(
|
private Boolean SetPropertyValue(
|
||||||
PropertyInfo targetProperty,
|
PropertyInfo targetProperty,
|
||||||
string propertyValueString,
|
String propertyValueString,
|
||||||
object result,
|
Object result,
|
||||||
ArgumentOptionAttribute optionAttr = null)
|
ArgumentOptionAttribute optionAttr = null) {
|
||||||
{
|
if(targetProperty.PropertyType.GetTypeInfo().IsEnum) {
|
||||||
if (targetProperty.PropertyType.GetTypeInfo().IsEnum)
|
Object parsedValue = Enum.Parse(
|
||||||
{
|
|
||||||
var parsedValue = Enum.Parse(
|
|
||||||
targetProperty.PropertyType,
|
targetProperty.PropertyType,
|
||||||
propertyValueString,
|
propertyValueString,
|
||||||
_settings.CaseInsensitiveEnumValues);
|
this._settings.CaseInsensitiveEnumValues);
|
||||||
|
|
||||||
targetProperty.SetValue(result, Enum.ToObject(targetProperty.PropertyType, parsedValue));
|
targetProperty.SetValue(result, Enum.ToObject(targetProperty.PropertyType, parsedValue));
|
||||||
|
|
||||||
@ -150,10 +137,10 @@
|
|||||||
: targetProperty.TrySetBasicType(propertyValueString, result);
|
: targetProperty.TrySetBasicType(propertyValueString, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
private PropertyInfo TryGetProperty(string propertyName)
|
private PropertyInfo TryGetProperty(String propertyName)
|
||||||
=> _properties.FirstOrDefault(p =>
|
=> this._properties.FirstOrDefault(p =>
|
||||||
string.Equals(Runtime.AttributeCache.RetrieveOne<ArgumentOptionAttribute>(p)?.LongName, propertyName, _settings.NameComparer) ||
|
String.Equals(Runtime.AttributeCache.RetrieveOne<ArgumentOptionAttribute>(p)?.LongName, propertyName, this._settings.NameComparer) ||
|
||||||
string.Equals(Runtime.AttributeCache.RetrieveOne<ArgumentOptionAttribute>(p)?.ShortName, propertyName, _settings.NameComparer));
|
String.Equals(Runtime.AttributeCache.RetrieveOne<ArgumentOptionAttribute>(p)?.ShortName, propertyName, this._settings.NameComparer));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,53 +1,48 @@
|
|||||||
namespace Unosquare.Swan.Components
|
using System.Linq;
|
||||||
{
|
using System.Reflection;
|
||||||
using System.Linq;
|
using Unosquare.Swan.Attributes;
|
||||||
using System.Reflection;
|
using System;
|
||||||
using Attributes;
|
|
||||||
using System;
|
|
||||||
|
|
||||||
|
namespace Unosquare.Swan.Components {
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Provides methods to parse command line arguments.
|
/// Provides methods to parse command line arguments.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public partial class ArgumentParser
|
public partial class ArgumentParser {
|
||||||
{
|
private sealed class TypeResolver<T> {
|
||||||
private sealed class TypeResolver<T>
|
private readonly String _selectedVerb;
|
||||||
{
|
|
||||||
private readonly string _selectedVerb;
|
|
||||||
|
|
||||||
private PropertyInfo[] _properties;
|
private PropertyInfo[] _properties;
|
||||||
|
|
||||||
public TypeResolver(string selectedVerb)
|
public TypeResolver(String selectedVerb) => this._selectedVerb = selectedVerb;
|
||||||
{
|
|
||||||
_selectedVerb = selectedVerb;
|
public PropertyInfo[] GetProperties() => this._properties?.Any() == true ? this._properties : null;
|
||||||
|
|
||||||
|
public Object GetOptionsObject(T instance) {
|
||||||
|
this._properties = Runtime.PropertyTypeCache.RetrieveAllProperties<T>(true).ToArray();
|
||||||
|
|
||||||
|
if(!this._properties.Any(x => x.GetCustomAttributes(typeof(VerbOptionAttribute), false).Any())) {
|
||||||
|
return instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
public PropertyInfo[] GetProperties() => _properties?.Any() == true ? _properties : null;
|
PropertyInfo selectedVerb = String.IsNullOrWhiteSpace(this._selectedVerb)
|
||||||
|
|
||||||
public object GetOptionsObject(T instance)
|
|
||||||
{
|
|
||||||
_properties = Runtime.PropertyTypeCache.RetrieveAllProperties<T>(true).ToArray();
|
|
||||||
|
|
||||||
if (!_properties.Any(x => x.GetCustomAttributes(typeof(VerbOptionAttribute), false).Any()))
|
|
||||||
return instance;
|
|
||||||
|
|
||||||
var selectedVerb = string.IsNullOrWhiteSpace(_selectedVerb)
|
|
||||||
? null
|
? null
|
||||||
: _properties.FirstOrDefault(x =>
|
: this._properties.FirstOrDefault(x =>
|
||||||
Runtime.AttributeCache.RetrieveOne<VerbOptionAttribute>(x).Name.Equals(_selectedVerb));
|
Runtime.AttributeCache.RetrieveOne<VerbOptionAttribute>(x).Name.Equals(this._selectedVerb));
|
||||||
|
|
||||||
if (selectedVerb == null) return null;
|
if(selectedVerb == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
var type = instance.GetType();
|
Type type = instance.GetType();
|
||||||
|
|
||||||
var verbProperty = type.GetProperty(selectedVerb.Name);
|
PropertyInfo verbProperty = type.GetProperty(selectedVerb.Name);
|
||||||
|
|
||||||
if (verbProperty?.GetValue(instance) == null)
|
if(verbProperty?.GetValue(instance) == null) {
|
||||||
{
|
Object propertyInstance = Activator.CreateInstance(selectedVerb.PropertyType);
|
||||||
var propertyInstance = Activator.CreateInstance(selectedVerb.PropertyType);
|
|
||||||
verbProperty?.SetValue(instance, propertyInstance);
|
verbProperty?.SetValue(instance, propertyInstance);
|
||||||
}
|
}
|
||||||
|
|
||||||
_properties = Runtime.PropertyTypeCache.RetrieveAllProperties(selectedVerb.PropertyType, true)
|
this._properties = Runtime.PropertyTypeCache.RetrieveAllProperties(selectedVerb.PropertyType, true)
|
||||||
.ToArray();
|
.ToArray();
|
||||||
|
|
||||||
return verbProperty?.GetValue(instance);
|
return verbProperty?.GetValue(instance);
|
||||||
|
@ -1,10 +1,9 @@
|
|||||||
namespace Unosquare.Swan.Components
|
using System;
|
||||||
{
|
using System.Collections.Generic;
|
||||||
using System;
|
using Unosquare.Swan.Attributes;
|
||||||
using System.Collections.Generic;
|
using System.Linq;
|
||||||
using Attributes;
|
|
||||||
using System.Linq;
|
|
||||||
|
|
||||||
|
namespace Unosquare.Swan.Components {
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Provides methods to parse command line arguments.
|
/// Provides methods to parse command line arguments.
|
||||||
/// Based on CommandLine (Copyright 2005-2015 Giacomo Stelluti Scala and Contributors.).
|
/// Based on CommandLine (Copyright 2005-2015 Giacomo Stelluti Scala and Contributors.).
|
||||||
@ -106,14 +105,12 @@ namespace Unosquare.Swan.Components
|
|||||||
/// }
|
/// }
|
||||||
/// </code>
|
/// </code>
|
||||||
/// </example>
|
/// </example>
|
||||||
public partial class ArgumentParser
|
public partial class ArgumentParser {
|
||||||
{
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="ArgumentParser"/> class.
|
/// Initializes a new instance of the <see cref="ArgumentParser"/> class.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public ArgumentParser()
|
public ArgumentParser()
|
||||||
: this(new ArgumentParserSettings())
|
: this(new ArgumentParserSettings()) {
|
||||||
{
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -121,10 +118,7 @@ namespace Unosquare.Swan.Components
|
|||||||
/// configurable with <see cref="ArgumentParserSettings" /> using a delegate.
|
/// configurable with <see cref="ArgumentParserSettings" /> using a delegate.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="parseSettings">The parse settings.</param>
|
/// <param name="parseSettings">The parse settings.</param>
|
||||||
public ArgumentParser(ArgumentParserSettings parseSettings)
|
public ArgumentParser(ArgumentParserSettings parseSettings) => this.Settings = parseSettings ?? throw new ArgumentNullException(nameof(parseSettings));
|
||||||
{
|
|
||||||
Settings = parseSettings ?? throw new ArgumentNullException(nameof(parseSettings));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the instance that implements <see cref="ArgumentParserSettings" /> in use.
|
/// Gets the instance that implements <see cref="ArgumentParserSettings" /> in use.
|
||||||
@ -132,7 +126,9 @@ namespace Unosquare.Swan.Components
|
|||||||
/// <value>
|
/// <value>
|
||||||
/// The settings.
|
/// The settings.
|
||||||
/// </value>
|
/// </value>
|
||||||
public ArgumentParserSettings Settings { get; }
|
public ArgumentParserSettings Settings {
|
||||||
|
get;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Parses a string array of command line arguments constructing values in an instance of type <typeparamref name="T" />.
|
/// Parses a string array of command line arguments constructing values in an instance of type <typeparamref name="T" />.
|
||||||
@ -150,39 +146,40 @@ namespace Unosquare.Swan.Components
|
|||||||
/// <exception cref="InvalidOperationException">
|
/// <exception cref="InvalidOperationException">
|
||||||
/// The exception that is thrown when a method call is invalid for the object's current state.
|
/// The exception that is thrown when a method call is invalid for the object's current state.
|
||||||
/// </exception>
|
/// </exception>
|
||||||
public bool ParseArguments<T>(IEnumerable<string> args, T instance)
|
public Boolean ParseArguments<T>(IEnumerable<String> args, T instance) {
|
||||||
{
|
if(args == null) {
|
||||||
if (args == null)
|
|
||||||
throw new ArgumentNullException(nameof(args));
|
throw new ArgumentNullException(nameof(args));
|
||||||
|
}
|
||||||
|
|
||||||
if (Equals(instance, default(T)))
|
if(Equals(instance, default(T))) {
|
||||||
throw new ArgumentNullException(nameof(instance));
|
throw new ArgumentNullException(nameof(instance));
|
||||||
|
}
|
||||||
|
|
||||||
var typeResolver = new TypeResolver<T>(args.FirstOrDefault());
|
TypeResolver<T> typeResolver = new TypeResolver<T>(args.FirstOrDefault());
|
||||||
var options = typeResolver.GetOptionsObject(instance);
|
Object options = typeResolver.GetOptionsObject(instance);
|
||||||
|
|
||||||
if (options == null)
|
if(options == null) {
|
||||||
{
|
|
||||||
ReportUnknownVerb<T>();
|
ReportUnknownVerb<T>();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
var properties = typeResolver.GetProperties();
|
System.Reflection.PropertyInfo[] properties = typeResolver.GetProperties();
|
||||||
|
|
||||||
if (properties == null)
|
if(properties == null) {
|
||||||
throw new InvalidOperationException($"Type {typeof(T).Name} is not valid");
|
throw new InvalidOperationException($"Type {typeof(T).Name} is not valid");
|
||||||
|
}
|
||||||
|
|
||||||
var validator = new Validator(properties, args, options, Settings);
|
Validator validator = new Validator(properties, args, options, this.Settings);
|
||||||
|
|
||||||
if (validator.IsValid())
|
if(validator.IsValid()) {
|
||||||
return true;
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
ReportIssues(validator);
|
this.ReportIssues(validator);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void ReportUnknownVerb<T>()
|
private static void ReportUnknownVerb<T>() {
|
||||||
{
|
|
||||||
"No verb was specified".WriteLine(ConsoleColor.Red);
|
"No verb was specified".WriteLine(ConsoleColor.Red);
|
||||||
"Valid verbs:".WriteLine(ConsoleColor.Cyan);
|
"Valid verbs:".WriteLine(ConsoleColor.Cyan);
|
||||||
|
|
||||||
@ -193,38 +190,39 @@ namespace Unosquare.Swan.Components
|
|||||||
.ForEach(x => x.ToString().WriteLine(ConsoleColor.Cyan));
|
.ForEach(x => x.ToString().WriteLine(ConsoleColor.Cyan));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ReportIssues(Validator validator)
|
private void ReportIssues(Validator validator) {
|
||||||
{
|
|
||||||
#if !NETSTANDARD1_3
|
#if !NETSTANDARD1_3
|
||||||
if (Settings.WriteBanner)
|
if(this.Settings.WriteBanner) {
|
||||||
Runtime.WriteWelcomeBanner();
|
Runtime.WriteWelcomeBanner();
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
var options = validator.GetPropertiesOptions();
|
IEnumerable<ArgumentOptionAttribute> options = validator.GetPropertiesOptions();
|
||||||
|
|
||||||
foreach (var option in options)
|
foreach(ArgumentOptionAttribute option in options) {
|
||||||
{
|
String.Empty.WriteLine();
|
||||||
string.Empty.WriteLine();
|
|
||||||
|
|
||||||
// TODO: If Enum list values
|
// TODO: If Enum list values
|
||||||
var shortName = string.IsNullOrWhiteSpace(option.ShortName) ? string.Empty : $"-{option.ShortName}";
|
String shortName = String.IsNullOrWhiteSpace(option.ShortName) ? String.Empty : $"-{option.ShortName}";
|
||||||
var longName = string.IsNullOrWhiteSpace(option.LongName) ? string.Empty : $"--{option.LongName}";
|
String longName = String.IsNullOrWhiteSpace(option.LongName) ? String.Empty : $"--{option.LongName}";
|
||||||
var comma = string.IsNullOrWhiteSpace(shortName) || string.IsNullOrWhiteSpace(longName)
|
String comma = String.IsNullOrWhiteSpace(shortName) || String.IsNullOrWhiteSpace(longName)
|
||||||
? string.Empty
|
? String.Empty
|
||||||
: ", ";
|
: ", ";
|
||||||
var defaultValue = option.DefaultValue == null ? string.Empty : $"(Default: {option.DefaultValue}) ";
|
String defaultValue = option.DefaultValue == null ? String.Empty : $"(Default: {option.DefaultValue}) ";
|
||||||
|
|
||||||
$" {shortName}{comma}{longName}\t\t{defaultValue}{option.HelpText}".WriteLine(ConsoleColor.Cyan);
|
$" {shortName}{comma}{longName}\t\t{defaultValue}{option.HelpText}".WriteLine(ConsoleColor.Cyan);
|
||||||
}
|
}
|
||||||
|
|
||||||
string.Empty.WriteLine();
|
String.Empty.WriteLine();
|
||||||
" --help\t\tDisplay this help screen.".WriteLine(ConsoleColor.Cyan);
|
" --help\t\tDisplay this help screen.".WriteLine(ConsoleColor.Cyan);
|
||||||
|
|
||||||
if (validator.UnknownList.Any())
|
if(validator.UnknownList.Any()) {
|
||||||
$"Unknown arguments: {string.Join(", ", validator.UnknownList)}".WriteLine(ConsoleColor.Red);
|
$"Unknown arguments: {String.Join(", ", validator.UnknownList)}".WriteLine(ConsoleColor.Red);
|
||||||
|
}
|
||||||
|
|
||||||
if (validator.RequiredList.Any())
|
if(validator.RequiredList.Any()) {
|
||||||
$"Required arguments: {string.Join(", ", validator.RequiredList)}".WriteLine(ConsoleColor.Red);
|
$"Required arguments: {String.Join(", ", validator.RequiredList)}".WriteLine(ConsoleColor.Red);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,20 +1,18 @@
|
|||||||
namespace Unosquare.Swan.Components
|
using System;
|
||||||
{
|
|
||||||
using System;
|
|
||||||
|
|
||||||
|
namespace Unosquare.Swan.Components {
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Provides settings for <see cref="ArgumentParser"/>.
|
/// Provides settings for <see cref="ArgumentParser"/>.
|
||||||
/// Based on CommandLine (Copyright 2005-2015 Giacomo Stelluti Scala and Contributors.).
|
/// Based on CommandLine (Copyright 2005-2015 Giacomo Stelluti Scala and Contributors.).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class ArgumentParserSettings
|
public class ArgumentParserSettings {
|
||||||
{
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets a value indicating whether [write banner].
|
/// Gets or sets a value indicating whether [write banner].
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <value>
|
/// <value>
|
||||||
/// <c>true</c> if [write banner]; otherwise, <c>false</c>.
|
/// <c>true</c> if [write banner]; otherwise, <c>false</c>.
|
||||||
/// </value>
|
/// </value>
|
||||||
public bool WriteBanner { get; set; } = true;
|
public Boolean WriteBanner { get; set; } = true;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets a value indicating whether perform case sensitive comparisons.
|
/// Gets or sets a value indicating whether perform case sensitive comparisons.
|
||||||
@ -24,7 +22,7 @@
|
|||||||
/// <value>
|
/// <value>
|
||||||
/// <c>true</c> if [case sensitive]; otherwise, <c>false</c>.
|
/// <c>true</c> if [case sensitive]; otherwise, <c>false</c>.
|
||||||
/// </value>
|
/// </value>
|
||||||
public bool CaseSensitive { get; set; } = false;
|
public Boolean CaseSensitive { get; set; } = false;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets a value indicating whether perform case sensitive comparisons of <i>values</i>.
|
/// Gets or sets a value indicating whether perform case sensitive comparisons of <i>values</i>.
|
||||||
@ -33,7 +31,7 @@
|
|||||||
/// <value>
|
/// <value>
|
||||||
/// <c>true</c> if [case insensitive enum values]; otherwise, <c>false</c>.
|
/// <c>true</c> if [case insensitive enum values]; otherwise, <c>false</c>.
|
||||||
/// </value>
|
/// </value>
|
||||||
public bool CaseInsensitiveEnumValues { get; set; } = true;
|
public Boolean CaseInsensitiveEnumValues { get; set; } = true;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets a value indicating whether the parser shall move on to the next argument and ignore the given argument if it
|
/// Gets or sets a value indicating whether the parser shall move on to the next argument and ignore the given argument if it
|
||||||
@ -46,8 +44,8 @@
|
|||||||
/// This allows fragmented version class parsing, useful for project with add-on where add-ons also requires command line arguments but
|
/// This allows fragmented version class parsing, useful for project with add-on where add-ons also requires command line arguments but
|
||||||
/// when these are unknown by the main program at build time.
|
/// when these are unknown by the main program at build time.
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
public bool IgnoreUnknownArguments { get; set; } = true;
|
public Boolean IgnoreUnknownArguments { get; set; } = true;
|
||||||
|
|
||||||
internal StringComparison NameComparer => CaseSensitive ? StringComparison.Ordinal : StringComparison.OrdinalIgnoreCase;
|
internal StringComparison NameComparer => this.CaseSensitive ? StringComparison.Ordinal : StringComparison.OrdinalIgnoreCase;
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,11 +1,10 @@
|
|||||||
namespace Unosquare.Swan.Components
|
using System;
|
||||||
{
|
using System.Collections.Generic;
|
||||||
using System;
|
using System.Diagnostics;
|
||||||
using System.Collections.Generic;
|
using System.Linq;
|
||||||
using System.Diagnostics;
|
using System.Text;
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
|
|
||||||
|
namespace Unosquare.Swan.Components {
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A simple benchmarking class.
|
/// A simple benchmarking class.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -33,31 +32,27 @@
|
|||||||
/// }
|
/// }
|
||||||
/// </code>
|
/// </code>
|
||||||
/// </example>
|
/// </example>
|
||||||
public static class Benchmark
|
public static class Benchmark {
|
||||||
{
|
private static readonly Object SyncLock = new Object();
|
||||||
private static readonly object SyncLock = new object();
|
private static readonly Dictionary<String, List<TimeSpan>> Measures = new Dictionary<String, List<TimeSpan>>();
|
||||||
private static readonly Dictionary<string, List<TimeSpan>> Measures = new Dictionary<string, List<TimeSpan>>();
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Starts measuring with the given identifier.
|
/// Starts measuring with the given identifier.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="identifier">The identifier.</param>
|
/// <param name="identifier">The identifier.</param>
|
||||||
/// <returns>A disposable object that when disposed, adds a benchmark result.</returns>
|
/// <returns>A disposable object that when disposed, adds a benchmark result.</returns>
|
||||||
public static IDisposable Start(string identifier) => new BenchmarkUnit(identifier);
|
public static IDisposable Start(String identifier) => new BenchmarkUnit(identifier);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Outputs the benchmark statistics.
|
/// Outputs the benchmark statistics.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>A string containing human-readable statistics.</returns>
|
/// <returns>A string containing human-readable statistics.</returns>
|
||||||
public static string Dump()
|
public static String Dump() {
|
||||||
{
|
StringBuilder builder = new StringBuilder();
|
||||||
var builder = new StringBuilder();
|
|
||||||
|
|
||||||
lock (SyncLock)
|
lock(SyncLock) {
|
||||||
{
|
foreach(KeyValuePair<String, List<TimeSpan>> kvp in Measures) {
|
||||||
foreach (var kvp in Measures)
|
_ = builder.Append($"BID: {kvp.Key,-30} | ")
|
||||||
{
|
|
||||||
builder.Append($"BID: {kvp.Key,-30} | ")
|
|
||||||
.Append($"CNT: {kvp.Value.Count,6} | ")
|
.Append($"CNT: {kvp.Value.Count,6} | ")
|
||||||
.Append($"AVG: {kvp.Value.Average(t => t.TotalMilliseconds),8:0.000} ms. | ")
|
.Append($"AVG: {kvp.Value.Average(t => t.TotalMilliseconds),8:0.000} ms. | ")
|
||||||
.Append($"MAX: {kvp.Value.Max(t => t.TotalMilliseconds),8:0.000} ms. | ")
|
.Append($"MAX: {kvp.Value.Max(t => t.TotalMilliseconds),8:0.000} ms. | ")
|
||||||
@ -74,12 +69,11 @@
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="identifier">The identifier.</param>
|
/// <param name="identifier">The identifier.</param>
|
||||||
/// <param name="elapsed">The elapsed.</param>
|
/// <param name="elapsed">The elapsed.</param>
|
||||||
private static void Add(string identifier, TimeSpan elapsed)
|
private static void Add(String identifier, TimeSpan elapsed) {
|
||||||
{
|
lock(SyncLock) {
|
||||||
lock (SyncLock)
|
if(Measures.ContainsKey(identifier) == false) {
|
||||||
{
|
|
||||||
if (Measures.ContainsKey(identifier) == false)
|
|
||||||
Measures[identifier] = new List<TimeSpan>(1024 * 1024);
|
Measures[identifier] = new List<TimeSpan>(1024 * 1024);
|
||||||
|
}
|
||||||
|
|
||||||
Measures[identifier].Add(elapsed);
|
Measures[identifier].Add(elapsed);
|
||||||
}
|
}
|
||||||
@ -89,41 +83,39 @@
|
|||||||
/// Represents a disposable benchmark unit.
|
/// Represents a disposable benchmark unit.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <seealso cref="IDisposable" />
|
/// <seealso cref="IDisposable" />
|
||||||
private sealed class BenchmarkUnit : IDisposable
|
private sealed class BenchmarkUnit : IDisposable {
|
||||||
{
|
private readonly String _identifier;
|
||||||
private readonly string _identifier;
|
private Boolean _isDisposed; // To detect redundant calls
|
||||||
private bool _isDisposed; // To detect redundant calls
|
|
||||||
private Stopwatch _stopwatch = new Stopwatch();
|
private Stopwatch _stopwatch = new Stopwatch();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="BenchmarkUnit" /> class.
|
/// Initializes a new instance of the <see cref="BenchmarkUnit" /> class.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="identifier">The identifier.</param>
|
/// <param name="identifier">The identifier.</param>
|
||||||
public BenchmarkUnit(string identifier)
|
public BenchmarkUnit(String identifier) {
|
||||||
{
|
this._identifier = identifier;
|
||||||
_identifier = identifier;
|
this._stopwatch.Start();
|
||||||
_stopwatch.Start();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public void Dispose() => Dispose(true);
|
public void Dispose() => this.Dispose(true);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Releases unmanaged and - optionally - managed resources.
|
/// Releases unmanaged and - optionally - managed resources.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="alsoManaged"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
|
/// <param name="alsoManaged"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
|
||||||
private void Dispose(bool alsoManaged)
|
private void Dispose(Boolean alsoManaged) {
|
||||||
{
|
if(this._isDisposed) {
|
||||||
if (_isDisposed) return;
|
return;
|
||||||
|
|
||||||
if (alsoManaged)
|
|
||||||
{
|
|
||||||
Add(_identifier, _stopwatch.Elapsed);
|
|
||||||
_stopwatch?.Stop();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_stopwatch = null;
|
if(alsoManaged) {
|
||||||
_isDisposed = true;
|
Add(this._identifier, this._stopwatch.Elapsed);
|
||||||
|
this._stopwatch?.Stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
this._stopwatch = null;
|
||||||
|
this._isDisposed = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,16 +1,14 @@
|
|||||||
namespace Unosquare.Swan.Components
|
using System;
|
||||||
{
|
using System.Collections.Concurrent;
|
||||||
using System;
|
using System.Collections.Generic;
|
||||||
using System.Collections.Concurrent;
|
using System.Linq;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
|
|
||||||
|
namespace Unosquare.Swan.Components {
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A thread-safe collection cache repository for types.
|
/// A thread-safe collection cache repository for types.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <typeparam name="TValue">The type of member to cache.</typeparam>
|
/// <typeparam name="TValue">The type of member to cache.</typeparam>
|
||||||
public class CollectionCacheRepository<TValue>
|
public class CollectionCacheRepository<TValue> {
|
||||||
{
|
|
||||||
private readonly Lazy<ConcurrentDictionary<Type, IEnumerable<TValue>>> _data =
|
private readonly Lazy<ConcurrentDictionary<Type, IEnumerable<TValue>>> _data =
|
||||||
new Lazy<ConcurrentDictionary<Type, IEnumerable<TValue>>>(() =>
|
new Lazy<ConcurrentDictionary<Type, IEnumerable<TValue>>>(() =>
|
||||||
new ConcurrentDictionary<Type, IEnumerable<TValue>>(), true);
|
new ConcurrentDictionary<Type, IEnumerable<TValue>>(), true);
|
||||||
@ -20,7 +18,7 @@
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="key">The key.</param>
|
/// <param name="key">The key.</param>
|
||||||
/// <returns><c>true</c> if the cache contains the key, otherwise <c>false</c>.</returns>
|
/// <returns><c>true</c> if the cache contains the key, otherwise <c>false</c>.</returns>
|
||||||
public bool ContainsKey(Type key) => _data.Value.ContainsKey(key);
|
public Boolean ContainsKey(Type key) => this._data.Value.ContainsKey(key);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Retrieves the properties stored for the specified type.
|
/// Retrieves the properties stored for the specified type.
|
||||||
@ -33,12 +31,12 @@
|
|||||||
/// An array of the properties stored for the specified type.
|
/// An array of the properties stored for the specified type.
|
||||||
/// </returns>
|
/// </returns>
|
||||||
/// <exception cref="System.ArgumentNullException">type.</exception>
|
/// <exception cref="System.ArgumentNullException">type.</exception>
|
||||||
public IEnumerable<TValue> Retrieve(Type key, Func<Type, IEnumerable<TValue>> factory)
|
public IEnumerable<TValue> Retrieve(Type key, Func<Type, IEnumerable<TValue>> factory) {
|
||||||
{
|
if(factory == null) {
|
||||||
if (factory == null)
|
|
||||||
throw new ArgumentNullException(nameof(factory));
|
throw new ArgumentNullException(nameof(factory));
|
||||||
|
}
|
||||||
|
|
||||||
return _data.Value.GetOrAdd(key, k => factory.Invoke(k).Where(item => item != null));
|
return this._data.Value.GetOrAdd(key, k => factory.Invoke(k).Where(item => item != null));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,28 +1,23 @@
|
|||||||
namespace Unosquare.Swan.Components
|
using System;
|
||||||
{
|
using System.Collections.Generic;
|
||||||
using System;
|
using System.Linq;
|
||||||
using System.Collections.Generic;
|
using Unosquare.Swan.Abstractions;
|
||||||
using System.Linq;
|
|
||||||
using Abstractions;
|
|
||||||
|
|
||||||
|
namespace Unosquare.Swan.Components {
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Provide Enumerations helpers with internal cache.
|
/// Provide Enumerations helpers with internal cache.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class EnumHelper
|
public class EnumHelper
|
||||||
: SingletonBase<CollectionCacheRepository<Tuple<string, object>>>
|
: SingletonBase<CollectionCacheRepository<Tuple<String, Object>>> {
|
||||||
{
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets all the names and enumerators from a specific Enum type.
|
/// Gets all the names and enumerators from a specific Enum type.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <typeparam name="T">The type of the attribute to be retrieved.</typeparam>
|
/// <typeparam name="T">The type of the attribute to be retrieved.</typeparam>
|
||||||
/// <returns>A tuple of enumerator names and their value stored for the specified type.</returns>
|
/// <returns>A tuple of enumerator names and their value stored for the specified type.</returns>
|
||||||
public static IEnumerable<Tuple<string, object>> Retrieve<T>()
|
public static IEnumerable<Tuple<String, Object>> Retrieve<T>()
|
||||||
where T : struct, IConvertible
|
where T : struct, IConvertible => Instance.Retrieve(typeof(T), t => Enum.GetValues(t)
|
||||||
{
|
.Cast<Object>()
|
||||||
return Instance.Retrieve(typeof(T), t => Enum.GetValues(t)
|
|
||||||
.Cast<object>()
|
|
||||||
.Select(item => Tuple.Create(Enum.GetName(t, item), item)));
|
.Select(item => Tuple.Create(Enum.GetName(t, item), item)));
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the cached items with the enum item value.
|
/// Gets the cached items with the enum item value.
|
||||||
@ -33,12 +28,9 @@
|
|||||||
/// A collection of Type/Tuple pairs
|
/// A collection of Type/Tuple pairs
|
||||||
/// that represents items with the enum item value.
|
/// that represents items with the enum item value.
|
||||||
/// </returns>
|
/// </returns>
|
||||||
public static IEnumerable<Tuple<int, string>> GetItemsWithValue<T>(bool humanize = true)
|
public static IEnumerable<Tuple<Int32, String>> GetItemsWithValue<T>(Boolean humanize = true)
|
||||||
where T : struct, IConvertible
|
where T : struct, IConvertible => Retrieve<T>()
|
||||||
{
|
.Select(x => Tuple.Create((Int32)x.Item2, humanize ? x.Item1.Humanize() : x.Item1));
|
||||||
return Retrieve<T>()
|
|
||||||
.Select(x => Tuple.Create((int) x.Item2, humanize ? x.Item1.Humanize() : x.Item1));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the flag values.
|
/// Gets the flag values.
|
||||||
@ -49,14 +41,11 @@
|
|||||||
/// <returns>
|
/// <returns>
|
||||||
/// A list of values in the flag.
|
/// A list of values in the flag.
|
||||||
/// </returns>
|
/// </returns>
|
||||||
public static IEnumerable<int> GetFlagValues<TEnum>(int value, bool ignoreZero = false)
|
public static IEnumerable<Int32> GetFlagValues<TEnum>(Int32 value, Boolean ignoreZero = false)
|
||||||
where TEnum : struct, IConvertible
|
where TEnum : struct, IConvertible => Retrieve<TEnum>()
|
||||||
{
|
.Select(x => (Int32)x.Item2)
|
||||||
return Retrieve<TEnum>()
|
|
||||||
.Select(x => (int) x.Item2)
|
|
||||||
.When(() => ignoreZero, q => q.Where(f => f != 0))
|
.When(() => ignoreZero, q => q.Where(f => f != 0))
|
||||||
.Where(x => (x & value) == x);
|
.Where(x => (x & value) == x);
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the flag values.
|
/// Gets the flag values.
|
||||||
@ -67,14 +56,11 @@
|
|||||||
/// <returns>
|
/// <returns>
|
||||||
/// A list of values in the flag.
|
/// A list of values in the flag.
|
||||||
/// </returns>
|
/// </returns>
|
||||||
public static IEnumerable<long> GetFlagValues<TEnum>(long value, bool ignoreZero = false)
|
public static IEnumerable<Int64> GetFlagValues<TEnum>(Int64 value, Boolean ignoreZero = false)
|
||||||
where TEnum : struct, IConvertible
|
where TEnum : struct, IConvertible => Retrieve<TEnum>()
|
||||||
{
|
.Select(x => (Int64)x.Item2)
|
||||||
return Retrieve<TEnum>()
|
|
||||||
.Select(x => (long) x.Item2)
|
|
||||||
.When(() => ignoreZero, q => q.Where(f => f != 0))
|
.When(() => ignoreZero, q => q.Where(f => f != 0))
|
||||||
.Where(x => (x & value) == x);
|
.Where(x => (x & value) == x);
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the flag values.
|
/// Gets the flag values.
|
||||||
@ -85,14 +71,11 @@
|
|||||||
/// <returns>
|
/// <returns>
|
||||||
/// A list of values in the flag.
|
/// A list of values in the flag.
|
||||||
/// </returns>
|
/// </returns>
|
||||||
public static IEnumerable<byte> GetFlagValues<TEnum>(byte value, bool ignoreZero = false)
|
public static IEnumerable<Byte> GetFlagValues<TEnum>(Byte value, Boolean ignoreZero = false)
|
||||||
where TEnum : struct, IConvertible
|
where TEnum : struct, IConvertible => Retrieve<TEnum>()
|
||||||
{
|
.Select(x => (Byte)x.Item2)
|
||||||
return Retrieve<TEnum>()
|
|
||||||
.Select(x => (byte) x.Item2)
|
|
||||||
.When(() => ignoreZero, q => q.Where(f => f != 0))
|
.When(() => ignoreZero, q => q.Where(f => f != 0))
|
||||||
.Where(x => (x & value) == x);
|
.Where(x => (x & value) == x);
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the flag names.
|
/// Gets the flag names.
|
||||||
@ -104,14 +87,11 @@
|
|||||||
/// <returns>
|
/// <returns>
|
||||||
/// A list of flag names.
|
/// A list of flag names.
|
||||||
/// </returns>
|
/// </returns>
|
||||||
public static IEnumerable<string> GetFlagNames<TEnum>(int value, bool ignoreZero = false, bool humanize = true)
|
public static IEnumerable<String> GetFlagNames<TEnum>(Int32 value, Boolean ignoreZero = false, Boolean humanize = true)
|
||||||
where TEnum : struct, IConvertible
|
where TEnum : struct, IConvertible => Retrieve<TEnum>()
|
||||||
{
|
.When(() => ignoreZero, q => q.Where(f => (Int32)f.Item2 != 0))
|
||||||
return Retrieve<TEnum>()
|
.Where(x => ((Int32)x.Item2 & value) == (Int32)x.Item2)
|
||||||
.When(() => ignoreZero, q => q.Where(f => (int) f.Item2 != 0))
|
|
||||||
.Where(x => ((int) x.Item2 & value) == (int) x.Item2)
|
|
||||||
.Select(x => humanize ? x.Item1.Humanize() : x.Item1);
|
.Select(x => humanize ? x.Item1.Humanize() : x.Item1);
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the flag names.
|
/// Gets the flag names.
|
||||||
@ -123,14 +103,11 @@
|
|||||||
/// <returns>
|
/// <returns>
|
||||||
/// A list of flag names.
|
/// A list of flag names.
|
||||||
/// </returns>
|
/// </returns>
|
||||||
public static IEnumerable<string> GetFlagNames<TEnum>(long value, bool ignoreZero = false, bool humanize = true)
|
public static IEnumerable<String> GetFlagNames<TEnum>(Int64 value, Boolean ignoreZero = false, Boolean humanize = true)
|
||||||
where TEnum : struct, IConvertible
|
where TEnum : struct, IConvertible => Retrieve<TEnum>()
|
||||||
{
|
.When(() => ignoreZero, q => q.Where(f => (Int64)f.Item2 != 0))
|
||||||
return Retrieve<TEnum>()
|
.Where(x => ((Int64)x.Item2 & value) == (Int64)x.Item2)
|
||||||
.When(() => ignoreZero, q => q.Where(f => (long) f.Item2 != 0))
|
|
||||||
.Where(x => ((long) x.Item2 & value) == (long) x.Item2)
|
|
||||||
.Select(x => humanize ? x.Item1.Humanize() : x.Item1);
|
.Select(x => humanize ? x.Item1.Humanize() : x.Item1);
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the flag names.
|
/// Gets the flag names.
|
||||||
@ -142,14 +119,11 @@
|
|||||||
/// <returns>
|
/// <returns>
|
||||||
/// A list of flag names.
|
/// A list of flag names.
|
||||||
/// </returns>
|
/// </returns>
|
||||||
public static IEnumerable<string> GetFlagNames<TEnum>(byte value, bool ignoreZero = false, bool humanize = true)
|
public static IEnumerable<String> GetFlagNames<TEnum>(Byte value, Boolean ignoreZero = false, Boolean humanize = true)
|
||||||
where TEnum : struct, IConvertible
|
where TEnum : struct, IConvertible => Retrieve<TEnum>()
|
||||||
{
|
.When(() => ignoreZero, q => q.Where(f => (Byte)f.Item2 != 0))
|
||||||
return Retrieve<TEnum>()
|
.Where(x => ((Byte)x.Item2 & value) == (Byte)x.Item2)
|
||||||
.When(() => ignoreZero, q => q.Where(f => (byte) f.Item2 != 0))
|
|
||||||
.Where(x => ((byte) x.Item2 & value) == (byte) x.Item2)
|
|
||||||
.Select(x => humanize ? x.Item1.Humanize() : x.Item1);
|
.Select(x => humanize ? x.Item1.Humanize() : x.Item1);
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the cached items with the enum item index.
|
/// Gets the cached items with the enum item index.
|
||||||
@ -159,10 +133,9 @@
|
|||||||
/// <returns>
|
/// <returns>
|
||||||
/// A collection of Type/Tuple pairs that represents items with the enum item value.
|
/// A collection of Type/Tuple pairs that represents items with the enum item value.
|
||||||
/// </returns>
|
/// </returns>
|
||||||
public static IEnumerable<Tuple<int, string>> GetItemsWithIndex<T>(bool humanize = true)
|
public static IEnumerable<Tuple<Int32, String>> GetItemsWithIndex<T>(Boolean humanize = true)
|
||||||
where T : struct, IConvertible
|
where T : struct, IConvertible {
|
||||||
{
|
Int32 i = 0;
|
||||||
var i = 0;
|
|
||||||
|
|
||||||
return Retrieve<T>()
|
return Retrieve<T>()
|
||||||
.Select(x => Tuple.Create(i++, humanize ? x.Item1.Humanize() : x.Item1));
|
.Select(x => Tuple.Create(i++, humanize ? x.Item1.Humanize() : x.Item1));
|
||||||
|
@ -1,17 +1,15 @@
|
|||||||
namespace Unosquare.Swan.Components
|
using System;
|
||||||
{
|
using System.Collections;
|
||||||
using System;
|
using System.Collections.Generic;
|
||||||
using System.Collections;
|
using System.Linq;
|
||||||
using System.Collections.Generic;
|
using System.Reflection;
|
||||||
using System.Linq;
|
|
||||||
using System.Reflection;
|
|
||||||
|
|
||||||
|
namespace Unosquare.Swan.Components {
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents a quick object comparer using the public properties of an object
|
/// Represents a quick object comparer using the public properties of an object
|
||||||
/// or the public members in a structure.
|
/// or the public members in a structure.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static class ObjectComparer
|
public static class ObjectComparer {
|
||||||
{
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Compare if two variables of the same type are equal.
|
/// Compare if two variables of the same type are equal.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -19,7 +17,7 @@
|
|||||||
/// <param name="left">The left.</param>
|
/// <param name="left">The left.</param>
|
||||||
/// <param name="right">The right.</param>
|
/// <param name="right">The right.</param>
|
||||||
/// <returns><c>true</c> if the variables are equal; otherwise, <c>false</c>.</returns>
|
/// <returns><c>true</c> if the variables are equal; otherwise, <c>false</c>.</returns>
|
||||||
public static bool AreEqual<T>(T left, T right) => AreEqual(left, right, typeof(T));
|
public static Boolean AreEqual<T>(T left, T right) => AreEqual(left, right, typeof(T));
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Compare if two variables of the same type are equal.
|
/// Compare if two variables of the same type are equal.
|
||||||
@ -31,18 +29,16 @@
|
|||||||
/// <c>true</c> if the variables are equal; otherwise, <c>false</c>.
|
/// <c>true</c> if the variables are equal; otherwise, <c>false</c>.
|
||||||
/// </returns>
|
/// </returns>
|
||||||
/// <exception cref="ArgumentNullException">targetType.</exception>
|
/// <exception cref="ArgumentNullException">targetType.</exception>
|
||||||
public static bool AreEqual(object left, object right, Type targetType)
|
public static Boolean AreEqual(Object left, Object right, Type targetType) {
|
||||||
{
|
if(targetType == null) {
|
||||||
if (targetType == null)
|
|
||||||
throw new ArgumentNullException(nameof(targetType));
|
throw new ArgumentNullException(nameof(targetType));
|
||||||
|
}
|
||||||
|
|
||||||
if (Definitions.BasicTypesInfo.ContainsKey(targetType))
|
return Definitions.BasicTypesInfo.ContainsKey(targetType)
|
||||||
return Equals(left, right);
|
? Equals(left, right)
|
||||||
|
: targetType.IsValueType() || targetType.IsArray
|
||||||
if (targetType.IsValueType() || targetType.IsArray)
|
? AreStructsEqual(left, right, targetType)
|
||||||
return AreStructsEqual(left, right, targetType);
|
: AreObjectsEqual(left, right, targetType);
|
||||||
|
|
||||||
return AreObjectsEqual(left, right, targetType);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -52,11 +48,8 @@
|
|||||||
/// <param name="left">The left.</param>
|
/// <param name="left">The left.</param>
|
||||||
/// <param name="right">The right.</param>
|
/// <param name="right">The right.</param>
|
||||||
/// <returns><c>true</c> if the objects are equal; otherwise, <c>false</c>.</returns>
|
/// <returns><c>true</c> if the objects are equal; otherwise, <c>false</c>.</returns>
|
||||||
public static bool AreObjectsEqual<T>(T left, T right)
|
public static Boolean AreObjectsEqual<T>(T left, T right)
|
||||||
where T : class
|
where T : class => AreObjectsEqual(left, right, typeof(T));
|
||||||
{
|
|
||||||
return AreObjectsEqual(left, right, typeof(T));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Compare if two objects of the same type are equal.
|
/// Compare if two objects of the same type are equal.
|
||||||
@ -66,31 +59,29 @@
|
|||||||
/// <param name="targetType">Type of the target.</param>
|
/// <param name="targetType">Type of the target.</param>
|
||||||
/// <returns><c>true</c> if the objects are equal; otherwise, <c>false</c>.</returns>
|
/// <returns><c>true</c> if the objects are equal; otherwise, <c>false</c>.</returns>
|
||||||
/// <exception cref="ArgumentNullException">targetType.</exception>
|
/// <exception cref="ArgumentNullException">targetType.</exception>
|
||||||
public static bool AreObjectsEqual(object left, object right, Type targetType)
|
public static Boolean AreObjectsEqual(Object left, Object right, Type targetType) {
|
||||||
{
|
if(targetType == null) {
|
||||||
if (targetType == null)
|
|
||||||
throw new ArgumentNullException(nameof(targetType));
|
throw new ArgumentNullException(nameof(targetType));
|
||||||
|
}
|
||||||
|
|
||||||
var properties = Runtime.PropertyTypeCache.RetrieveAllProperties(targetType).ToArray();
|
PropertyInfo[] properties = Runtime.PropertyTypeCache.RetrieveAllProperties(targetType).ToArray();
|
||||||
|
|
||||||
foreach (var propertyTarget in properties)
|
foreach(PropertyInfo propertyTarget in properties) {
|
||||||
{
|
Func<Object, Object> targetPropertyGetMethod = propertyTarget.GetCacheGetMethod();
|
||||||
var targetPropertyGetMethod = propertyTarget.GetCacheGetMethod();
|
|
||||||
|
|
||||||
if (propertyTarget.PropertyType.IsArray)
|
if(propertyTarget.PropertyType.IsArray) {
|
||||||
{
|
IEnumerable leftObj = targetPropertyGetMethod(left) as IEnumerable;
|
||||||
var leftObj = targetPropertyGetMethod(left) as IEnumerable;
|
IEnumerable rightObj = targetPropertyGetMethod(right) as IEnumerable;
|
||||||
var rightObj = targetPropertyGetMethod(right) as IEnumerable;
|
|
||||||
|
|
||||||
if (!AreEnumerationsEquals(leftObj, rightObj))
|
if(!AreEnumerationsEquals(leftObj, rightObj)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
else
|
} else {
|
||||||
{
|
if(!Equals(targetPropertyGetMethod(left), targetPropertyGetMethod(right))) {
|
||||||
if (!Equals(targetPropertyGetMethod(left), targetPropertyGetMethod(right)))
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -102,11 +93,8 @@
|
|||||||
/// <param name="left">The left.</param>
|
/// <param name="left">The left.</param>
|
||||||
/// <param name="right">The right.</param>
|
/// <param name="right">The right.</param>
|
||||||
/// <returns><c>true</c> if the structs are equal; otherwise, <c>false</c>.</returns>
|
/// <returns><c>true</c> if the structs are equal; otherwise, <c>false</c>.</returns>
|
||||||
public static bool AreStructsEqual<T>(T left, T right)
|
public static Boolean AreStructsEqual<T>(T left, T right)
|
||||||
where T : struct
|
where T : struct => AreStructsEqual(left, right, typeof(T));
|
||||||
{
|
|
||||||
return AreStructsEqual(left, right, typeof(T));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Compare if two structures of the same type are equal.
|
/// Compare if two structures of the same type are equal.
|
||||||
@ -118,28 +106,30 @@
|
|||||||
/// <c>true</c> if the structs are equal; otherwise, <c>false</c>.
|
/// <c>true</c> if the structs are equal; otherwise, <c>false</c>.
|
||||||
/// </returns>
|
/// </returns>
|
||||||
/// <exception cref="ArgumentNullException">targetType.</exception>
|
/// <exception cref="ArgumentNullException">targetType.</exception>
|
||||||
public static bool AreStructsEqual(object left, object right, Type targetType)
|
public static Boolean AreStructsEqual(Object left, Object right, Type targetType) {
|
||||||
{
|
if(targetType == null) {
|
||||||
if (targetType == null)
|
|
||||||
throw new ArgumentNullException(nameof(targetType));
|
throw new ArgumentNullException(nameof(targetType));
|
||||||
|
}
|
||||||
|
|
||||||
var fields = new List<MemberInfo>(Runtime.FieldTypeCache.RetrieveAllFields(targetType))
|
IEnumerable<MemberInfo> fields = new List<MemberInfo>(Runtime.FieldTypeCache.RetrieveAllFields(targetType))
|
||||||
.Union(Runtime.PropertyTypeCache.RetrieveAllProperties(targetType));
|
.Union(Runtime.PropertyTypeCache.RetrieveAllProperties(targetType));
|
||||||
|
|
||||||
foreach (var targetMember in fields)
|
foreach(MemberInfo targetMember in fields) {
|
||||||
{
|
switch(targetMember) {
|
||||||
switch (targetMember)
|
|
||||||
{
|
|
||||||
case FieldInfo field:
|
case FieldInfo field:
|
||||||
if (Equals(field.GetValue(left), field.GetValue(right)) == false)
|
if(Equals(field.GetValue(left), field.GetValue(right)) == false) {
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
case PropertyInfo property:
|
case PropertyInfo property:
|
||||||
var targetPropertyGetMethod = property.GetCacheGetMethod();
|
Func<Object, Object> targetPropertyGetMethod = property.GetCacheGetMethod();
|
||||||
|
|
||||||
if (targetPropertyGetMethod != null &&
|
if(targetPropertyGetMethod != null &&
|
||||||
!Equals(targetPropertyGetMethod(left), targetPropertyGetMethod(right)))
|
!Equals(targetPropertyGetMethod(left), targetPropertyGetMethod(right))) {
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -161,28 +151,28 @@
|
|||||||
/// or
|
/// or
|
||||||
/// right.
|
/// right.
|
||||||
/// </exception>
|
/// </exception>
|
||||||
public static bool AreEnumerationsEquals<T>(T left, T right)
|
public static Boolean AreEnumerationsEquals<T>(T left, T right)
|
||||||
where T : IEnumerable
|
where T : IEnumerable {
|
||||||
{
|
if(Equals(left, default(T))) {
|
||||||
if (Equals(left, default(T)))
|
|
||||||
throw new ArgumentNullException(nameof(left));
|
throw new ArgumentNullException(nameof(left));
|
||||||
|
}
|
||||||
|
|
||||||
if (Equals(right, default(T)))
|
if(Equals(right, default(T))) {
|
||||||
throw new ArgumentNullException(nameof(right));
|
throw new ArgumentNullException(nameof(right));
|
||||||
|
}
|
||||||
|
|
||||||
var leftEnumerable = left.Cast<object>().ToArray();
|
Object[] leftEnumerable = left.Cast<Object>().ToArray();
|
||||||
var rightEnumerable = right.Cast<object>().ToArray();
|
Object[] rightEnumerable = right.Cast<Object>().ToArray();
|
||||||
|
|
||||||
if (leftEnumerable.Length != rightEnumerable.Length)
|
if(leftEnumerable.Length != rightEnumerable.Length) {
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
for (var i = 0; i < leftEnumerable.Length; i++)
|
for(Int32 i = 0; i < leftEnumerable.Length; i++) {
|
||||||
{
|
Object leftEl = leftEnumerable[i];
|
||||||
var leftEl = leftEnumerable[i];
|
Object rightEl = rightEnumerable[i];
|
||||||
var rightEl = rightEnumerable[i];
|
|
||||||
|
|
||||||
if (!AreEqual(leftEl, rightEl, leftEl.GetType()))
|
if(!AreEqual(leftEl, rightEl, leftEl.GetType())) {
|
||||||
{
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,37 +1,40 @@
|
|||||||
namespace Unosquare.Swan.Components
|
using System;
|
||||||
{
|
using System.Collections.Generic;
|
||||||
using System;
|
using System.Linq;
|
||||||
using System.Collections.Generic;
|
using System.Linq.Expressions;
|
||||||
using System.Linq;
|
using System.Reflection;
|
||||||
using System.Linq.Expressions;
|
using Unosquare.Swan.Abstractions;
|
||||||
using System.Reflection;
|
|
||||||
using Abstractions;
|
|
||||||
|
|
||||||
|
namespace Unosquare.Swan.Components {
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents an object map.
|
/// Represents an object map.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <typeparam name="TSource">The type of the source.</typeparam>
|
/// <typeparam name="TSource">The type of the source.</typeparam>
|
||||||
/// <typeparam name="TDestination">The type of the destination.</typeparam>
|
/// <typeparam name="TDestination">The type of the destination.</typeparam>
|
||||||
/// <seealso cref="Unosquare.Swan.Abstractions.IObjectMap" />
|
/// <seealso cref="Unosquare.Swan.Abstractions.IObjectMap" />
|
||||||
public class ObjectMap<TSource, TDestination> : IObjectMap
|
public class ObjectMap<TSource, TDestination> : IObjectMap {
|
||||||
{
|
internal ObjectMap(IEnumerable<PropertyInfo> intersect) {
|
||||||
internal ObjectMap(IEnumerable<PropertyInfo> intersect)
|
this.SourceType = typeof(TSource);
|
||||||
{
|
this.DestinationType = typeof(TDestination);
|
||||||
SourceType = typeof(TSource);
|
this.Map = intersect.ToDictionary(
|
||||||
DestinationType = typeof(TDestination);
|
property => this.DestinationType.GetProperty(property.Name),
|
||||||
Map = intersect.ToDictionary(
|
property => new List<PropertyInfo> { this.SourceType.GetProperty(property.Name) });
|
||||||
property => DestinationType.GetProperty(property.Name),
|
|
||||||
property => new List<PropertyInfo> {SourceType.GetProperty(property.Name)});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public Dictionary<PropertyInfo, List<PropertyInfo>> Map { get; }
|
public Dictionary<PropertyInfo, List<PropertyInfo>> Map {
|
||||||
|
get;
|
||||||
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public Type SourceType { get; }
|
public Type SourceType {
|
||||||
|
get;
|
||||||
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public Type DestinationType { get; }
|
public Type DestinationType {
|
||||||
|
get;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Maps the property.
|
/// Maps the property.
|
||||||
@ -47,25 +50,22 @@
|
|||||||
public ObjectMap<TSource, TDestination> MapProperty
|
public ObjectMap<TSource, TDestination> MapProperty
|
||||||
<TDestinationProperty, TSourceProperty>(
|
<TDestinationProperty, TSourceProperty>(
|
||||||
Expression<Func<TDestination, TDestinationProperty>> destinationProperty,
|
Expression<Func<TDestination, TDestinationProperty>> destinationProperty,
|
||||||
Expression<Func<TSource, TSourceProperty>> sourceProperty)
|
Expression<Func<TSource, TSourceProperty>> sourceProperty) {
|
||||||
{
|
PropertyInfo propertyDestinationInfo = (destinationProperty.Body as MemberExpression)?.Member as PropertyInfo;
|
||||||
var propertyDestinationInfo = (destinationProperty.Body as MemberExpression)?.Member as PropertyInfo;
|
|
||||||
|
|
||||||
if (propertyDestinationInfo == null)
|
if(propertyDestinationInfo == null) {
|
||||||
{
|
|
||||||
throw new ArgumentException("Invalid destination expression", nameof(destinationProperty));
|
throw new ArgumentException("Invalid destination expression", nameof(destinationProperty));
|
||||||
}
|
}
|
||||||
|
|
||||||
var sourceMembers = GetSourceMembers(sourceProperty);
|
List<PropertyInfo> sourceMembers = GetSourceMembers(sourceProperty);
|
||||||
|
|
||||||
if (sourceMembers.Any() == false)
|
if(sourceMembers.Any() == false) {
|
||||||
{
|
|
||||||
throw new ArgumentException("Invalid source expression", nameof(sourceProperty));
|
throw new ArgumentException("Invalid source expression", nameof(sourceProperty));
|
||||||
}
|
}
|
||||||
|
|
||||||
// reverse order
|
// reverse order
|
||||||
sourceMembers.Reverse();
|
sourceMembers.Reverse();
|
||||||
Map[propertyDestinationInfo] = sourceMembers;
|
this.Map[propertyDestinationInfo] = sourceMembers;
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
@ -81,31 +81,31 @@
|
|||||||
/// </returns>
|
/// </returns>
|
||||||
/// <exception cref="System.Exception">Invalid destination expression.</exception>
|
/// <exception cref="System.Exception">Invalid destination expression.</exception>
|
||||||
public ObjectMap<TSource, TDestination> RemoveMapProperty<TDestinationProperty>(
|
public ObjectMap<TSource, TDestination> RemoveMapProperty<TDestinationProperty>(
|
||||||
Expression<Func<TDestination, TDestinationProperty>> destinationProperty)
|
Expression<Func<TDestination, TDestinationProperty>> destinationProperty) {
|
||||||
{
|
PropertyInfo propertyDestinationInfo = (destinationProperty.Body as MemberExpression)?.Member as PropertyInfo;
|
||||||
var propertyDestinationInfo = (destinationProperty.Body as MemberExpression)?.Member as PropertyInfo;
|
|
||||||
|
|
||||||
if (propertyDestinationInfo == null)
|
if(propertyDestinationInfo == null) {
|
||||||
throw new ArgumentException("Invalid destination expression", nameof(destinationProperty));
|
throw new ArgumentException("Invalid destination expression", nameof(destinationProperty));
|
||||||
|
}
|
||||||
|
|
||||||
if (Map.ContainsKey(propertyDestinationInfo))
|
if(this.Map.ContainsKey(propertyDestinationInfo)) {
|
||||||
{
|
_ = this.Map.Remove(propertyDestinationInfo);
|
||||||
Map.Remove(propertyDestinationInfo);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static List<PropertyInfo> GetSourceMembers<TSourceProperty>(Expression<Func<TSource, TSourceProperty>> sourceProperty)
|
private static List<PropertyInfo> GetSourceMembers<TSourceProperty>(Expression<Func<TSource, TSourceProperty>> sourceProperty) {
|
||||||
{
|
List<PropertyInfo> sourceMembers = new List<PropertyInfo>();
|
||||||
var sourceMembers = new List<PropertyInfo>();
|
MemberExpression initialExpression = sourceProperty.Body as MemberExpression;
|
||||||
var initialExpression = sourceProperty.Body as MemberExpression;
|
|
||||||
|
|
||||||
while (true)
|
while(true) {
|
||||||
{
|
PropertyInfo propertySourceInfo = initialExpression?.Member as PropertyInfo;
|
||||||
var propertySourceInfo = initialExpression?.Member as PropertyInfo;
|
|
||||||
|
if(propertySourceInfo == null) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
if (propertySourceInfo == null) break;
|
|
||||||
sourceMembers.Add(propertySourceInfo);
|
sourceMembers.Add(propertySourceInfo);
|
||||||
initialExpression = initialExpression.Expression as MemberExpression;
|
initialExpression = initialExpression.Expression as MemberExpression;
|
||||||
}
|
}
|
||||||
|
@ -1,12 +1,11 @@
|
|||||||
namespace Unosquare.Swan.Components
|
using System;
|
||||||
{
|
using System.Collections;
|
||||||
using System;
|
using System.Collections.Generic;
|
||||||
using System.Collections;
|
using System.Linq;
|
||||||
using System.Collections.Generic;
|
using System.Reflection;
|
||||||
using System.Linq;
|
using Unosquare.Swan.Abstractions;
|
||||||
using System.Reflection;
|
|
||||||
using Abstractions;
|
|
||||||
|
|
||||||
|
namespace Unosquare.Swan.Components {
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents an AutoMapper-like object to map from one object type
|
/// Represents an AutoMapper-like object to map from one object type
|
||||||
/// to another using defined properties map or using the default behaviour
|
/// to another using defined properties map or using the default behaviour
|
||||||
@ -77,8 +76,7 @@
|
|||||||
/// }
|
/// }
|
||||||
/// </code>
|
/// </code>
|
||||||
/// </example>
|
/// </example>
|
||||||
public class ObjectMapper
|
public class ObjectMapper {
|
||||||
{
|
|
||||||
private readonly List<IObjectMap> _maps = new List<IObjectMap>();
|
private readonly List<IObjectMap> _maps = new List<IObjectMap>();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -96,17 +94,18 @@
|
|||||||
/// or
|
/// or
|
||||||
/// target.
|
/// target.
|
||||||
/// </exception>
|
/// </exception>
|
||||||
public static int Copy(
|
public static Int32 Copy(
|
||||||
object source,
|
Object source,
|
||||||
object target,
|
Object target,
|
||||||
string[] propertiesToCopy = null,
|
String[] propertiesToCopy = null,
|
||||||
string[] ignoreProperties = null)
|
String[] ignoreProperties = null) {
|
||||||
{
|
if(source == null) {
|
||||||
if (source == null)
|
|
||||||
throw new ArgumentNullException(nameof(source));
|
throw new ArgumentNullException(nameof(source));
|
||||||
|
}
|
||||||
|
|
||||||
if (target == null)
|
if(target == null) {
|
||||||
throw new ArgumentNullException(nameof(target));
|
throw new ArgumentNullException(nameof(target));
|
||||||
|
}
|
||||||
|
|
||||||
return Copy(
|
return Copy(
|
||||||
target,
|
target,
|
||||||
@ -130,17 +129,18 @@
|
|||||||
/// or
|
/// or
|
||||||
/// target.
|
/// target.
|
||||||
/// </exception>
|
/// </exception>
|
||||||
public static int Copy(
|
public static Int32 Copy(
|
||||||
IDictionary<string, object> source,
|
IDictionary<String, Object> source,
|
||||||
object target,
|
Object target,
|
||||||
string[] propertiesToCopy = null,
|
String[] propertiesToCopy = null,
|
||||||
string[] ignoreProperties = null)
|
String[] ignoreProperties = null) {
|
||||||
{
|
if(source == null) {
|
||||||
if (source == null)
|
|
||||||
throw new ArgumentNullException(nameof(source));
|
throw new ArgumentNullException(nameof(source));
|
||||||
|
}
|
||||||
|
|
||||||
if (target == null)
|
if(target == null) {
|
||||||
throw new ArgumentNullException(nameof(target));
|
throw new ArgumentNullException(nameof(target));
|
||||||
|
}
|
||||||
|
|
||||||
return Copy(
|
return Copy(
|
||||||
target,
|
target,
|
||||||
@ -148,7 +148,7 @@
|
|||||||
ignoreProperties,
|
ignoreProperties,
|
||||||
source.ToDictionary(
|
source.ToDictionary(
|
||||||
x => x.Key.ToLowerInvariant(),
|
x => x.Key.ToLowerInvariant(),
|
||||||
x => new TypeValuePair(typeof(object), x.Value)));
|
x => new TypeValuePair(typeof(Object), x.Value)));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -165,26 +165,23 @@
|
|||||||
/// or
|
/// or
|
||||||
/// Types doesn't match.
|
/// Types doesn't match.
|
||||||
/// </exception>
|
/// </exception>
|
||||||
public ObjectMap<TSource, TDestination> CreateMap<TSource, TDestination>()
|
public ObjectMap<TSource, TDestination> CreateMap<TSource, TDestination>() {
|
||||||
{
|
if(this._maps.Any(x => x.SourceType == typeof(TSource) && x.DestinationType == typeof(TDestination))) {
|
||||||
if (_maps.Any(x => x.SourceType == typeof(TSource) && x.DestinationType == typeof(TDestination)))
|
|
||||||
{
|
|
||||||
throw new InvalidOperationException("You can't create an existing map");
|
throw new InvalidOperationException("You can't create an existing map");
|
||||||
}
|
}
|
||||||
|
|
||||||
var sourceType = Runtime.PropertyTypeCache.RetrieveAllProperties<TSource>(true);
|
IEnumerable<PropertyInfo> sourceType = Runtime.PropertyTypeCache.RetrieveAllProperties<TSource>(true);
|
||||||
var destinationType = Runtime.PropertyTypeCache.RetrieveAllProperties<TDestination>(true);
|
IEnumerable<PropertyInfo> destinationType = Runtime.PropertyTypeCache.RetrieveAllProperties<TDestination>(true);
|
||||||
|
|
||||||
var intersect = sourceType.Intersect(destinationType, new PropertyInfoComparer()).ToArray();
|
PropertyInfo[] intersect = sourceType.Intersect(destinationType, new PropertyInfoComparer()).ToArray();
|
||||||
|
|
||||||
if (intersect.Any() == false)
|
if(intersect.Any() == false) {
|
||||||
{
|
|
||||||
throw new InvalidOperationException("Types doesn't match");
|
throw new InvalidOperationException("Types doesn't match");
|
||||||
}
|
}
|
||||||
|
|
||||||
var map = new ObjectMap<TSource, TDestination>(intersect);
|
ObjectMap<TSource, TDestination> map = new ObjectMap<TSource, TDestination>(intersect);
|
||||||
|
|
||||||
_maps.Add(map);
|
this._maps.Add(map);
|
||||||
|
|
||||||
return map;
|
return map;
|
||||||
}
|
}
|
||||||
@ -200,58 +197,50 @@
|
|||||||
/// </returns>
|
/// </returns>
|
||||||
/// <exception cref="ArgumentNullException">source.</exception>
|
/// <exception cref="ArgumentNullException">source.</exception>
|
||||||
/// <exception cref="InvalidOperationException">You can't map from type {source.GetType().Name} to {typeof(TDestination).Name}.</exception>
|
/// <exception cref="InvalidOperationException">You can't map from type {source.GetType().Name} to {typeof(TDestination).Name}.</exception>
|
||||||
public TDestination Map<TDestination>(object source, bool autoResolve = true)
|
public TDestination Map<TDestination>(Object source, Boolean autoResolve = true) {
|
||||||
{
|
if(source == null) {
|
||||||
if (source == null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(source));
|
throw new ArgumentNullException(nameof(source));
|
||||||
}
|
}
|
||||||
|
|
||||||
var destination = Activator.CreateInstance<TDestination>();
|
TDestination destination = Activator.CreateInstance<TDestination>();
|
||||||
var map = _maps
|
IObjectMap map = this._maps
|
||||||
.FirstOrDefault(x => x.SourceType == source.GetType() && x.DestinationType == typeof(TDestination));
|
.FirstOrDefault(x => x.SourceType == source.GetType() && x.DestinationType == typeof(TDestination));
|
||||||
|
|
||||||
if (map != null)
|
if(map != null) {
|
||||||
{
|
foreach(KeyValuePair<PropertyInfo, List<PropertyInfo>> property in map.Map) {
|
||||||
foreach (var property in map.Map)
|
Object finalSource = property.Value.Aggregate(source,
|
||||||
{
|
|
||||||
var finalSource = property.Value.Aggregate(source,
|
|
||||||
(current, sourceProperty) => sourceProperty.GetValue(current));
|
(current, sourceProperty) => sourceProperty.GetValue(current));
|
||||||
|
|
||||||
property.Key.SetValue(destination, finalSource);
|
property.Key.SetValue(destination, finalSource);
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
else
|
if(!autoResolve) {
|
||||||
{
|
|
||||||
if (!autoResolve)
|
|
||||||
{
|
|
||||||
throw new InvalidOperationException(
|
throw new InvalidOperationException(
|
||||||
$"You can't map from type {source.GetType().Name} to {typeof(TDestination).Name}");
|
$"You can't map from type {source.GetType().Name} to {typeof(TDestination).Name}");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Missing mapping, try to use default behavior
|
// Missing mapping, try to use default behavior
|
||||||
Copy(source, destination);
|
_ = Copy(source, destination);
|
||||||
}
|
}
|
||||||
|
|
||||||
return destination;
|
return destination;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int Copy(
|
private static Int32 Copy(
|
||||||
object target,
|
Object target,
|
||||||
IEnumerable<string> propertiesToCopy,
|
IEnumerable<String> propertiesToCopy,
|
||||||
IEnumerable<string> ignoreProperties,
|
IEnumerable<String> ignoreProperties,
|
||||||
Dictionary<string, TypeValuePair> sourceProperties)
|
Dictionary<String, TypeValuePair> sourceProperties) {
|
||||||
{
|
|
||||||
// Filter properties
|
// Filter properties
|
||||||
var requiredProperties = propertiesToCopy?
|
IEnumerable<String> requiredProperties = propertiesToCopy?
|
||||||
.Where(p => !string.IsNullOrWhiteSpace(p))
|
.Where(p => !String.IsNullOrWhiteSpace(p))
|
||||||
.Select(p => p.ToLowerInvariant());
|
.Select(p => p.ToLowerInvariant());
|
||||||
|
|
||||||
var ignoredProperties = ignoreProperties?
|
IEnumerable<String> ignoredProperties = ignoreProperties?
|
||||||
.Where(p => !string.IsNullOrWhiteSpace(p))
|
.Where(p => !String.IsNullOrWhiteSpace(p))
|
||||||
.Select(p => p.ToLowerInvariant());
|
.Select(p => p.ToLowerInvariant());
|
||||||
|
|
||||||
var properties = Runtime.PropertyTypeCache
|
IEnumerable<PropertyInfo> properties = Runtime.PropertyTypeCache
|
||||||
.RetrieveFilteredProperties(target.GetType(), true, x => x.CanWrite);
|
.RetrieveFilteredProperties(target.GetType(), true, x => x.CanWrite);
|
||||||
|
|
||||||
return properties
|
return properties
|
||||||
@ -265,115 +254,99 @@
|
|||||||
.Sum(x => TrySetValue(x, target) ? 1 : 0);
|
.Sum(x => TrySetValue(x, target) ? 1 : 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool TrySetValue(KeyValuePair<PropertyInfo, TypeValuePair> property, object target)
|
private static Boolean TrySetValue(KeyValuePair<PropertyInfo, TypeValuePair> property, Object target) {
|
||||||
{
|
try {
|
||||||
try
|
|
||||||
{
|
|
||||||
SetValue(property, target);
|
SetValue(property, target);
|
||||||
return true;
|
return true;
|
||||||
}
|
} catch {
|
||||||
catch
|
|
||||||
{
|
|
||||||
// swallow
|
// swallow
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void SetValue(KeyValuePair<PropertyInfo, TypeValuePair> property, object target)
|
private static void SetValue(KeyValuePair<PropertyInfo, TypeValuePair> property, Object target) {
|
||||||
{
|
if(property.Value.Type.GetTypeInfo().IsEnum) {
|
||||||
if (property.Value.Type.GetTypeInfo().IsEnum)
|
|
||||||
{
|
|
||||||
property.Key.SetValue(target,
|
property.Key.SetValue(target,
|
||||||
Enum.ToObject(property.Key.PropertyType, property.Value.Value));
|
Enum.ToObject(property.Key.PropertyType, property.Value.Value));
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!property.Value.Type.IsValueType() && property.Key.PropertyType == property.Value.Type)
|
if(!property.Value.Type.IsValueType() && property.Key.PropertyType == property.Value.Type) {
|
||||||
{
|
|
||||||
property.Key.SetValue(target, GetValue(property.Value.Value, property.Key.PropertyType));
|
property.Key.SetValue(target, GetValue(property.Value.Value, property.Key.PropertyType));
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (property.Key.PropertyType == typeof(bool))
|
if(property.Key.PropertyType == typeof(Boolean)) {
|
||||||
{
|
|
||||||
property.Key.SetValue(target,
|
property.Key.SetValue(target,
|
||||||
Convert.ToBoolean(property.Value.Value));
|
Convert.ToBoolean(property.Value.Value));
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
property.Key.TrySetBasicType(property.Value.Value, target);
|
_ = property.Key.TrySetBasicType(property.Value.Value, target);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static object GetValue(object source, Type targetType)
|
private static Object GetValue(Object source, Type targetType) {
|
||||||
{
|
if(source == null) {
|
||||||
if (source == null)
|
|
||||||
return null;
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
object target = null;
|
Object target = null;
|
||||||
|
|
||||||
source.CreateTarget(targetType, false, ref target);
|
source.CreateTarget(targetType, false, ref target);
|
||||||
|
|
||||||
switch (source)
|
switch(source) {
|
||||||
{
|
case String _:
|
||||||
case string _:
|
|
||||||
target = source;
|
target = source;
|
||||||
break;
|
break;
|
||||||
case IList sourceList when target is Array targetArray:
|
case IList sourceList when target is Array targetArray:
|
||||||
for (var i = 0; i < sourceList.Count; i++)
|
for(Int32 i = 0; i < sourceList.Count; i++) {
|
||||||
{
|
try {
|
||||||
try
|
|
||||||
{
|
|
||||||
targetArray.SetValue(
|
targetArray.SetValue(
|
||||||
sourceList[i].GetType().IsValueType()
|
sourceList[i].GetType().IsValueType()
|
||||||
? sourceList[i]
|
? sourceList[i]
|
||||||
: sourceList[i].CopyPropertiesToNew<object>(), i);
|
: sourceList[i].CopyPropertiesToNew<Object>(), i);
|
||||||
}
|
} catch {
|
||||||
catch
|
|
||||||
{
|
|
||||||
// ignored
|
// ignored
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
case IList sourceList when target is IList targetList:
|
case IList sourceList when target is IList targetList:
|
||||||
var addMethod = targetType.GetMethods()
|
MethodInfo addMethod = targetType.GetMethods()
|
||||||
.FirstOrDefault(
|
.FirstOrDefault(
|
||||||
m => m.Name.Equals(Formatters.Json.AddMethodName) && m.IsPublic &&
|
m => m.Name.Equals(Formatters.Json.AddMethodName) && m.IsPublic &&
|
||||||
m.GetParameters().Length == 1);
|
m.GetParameters().Length == 1);
|
||||||
|
|
||||||
if (addMethod == null) return target;
|
if(addMethod == null) {
|
||||||
|
return target;
|
||||||
foreach (var item in sourceList)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
targetList.Add(item.GetType().IsValueType()
|
|
||||||
? item
|
|
||||||
: item.CopyPropertiesToNew<object>());
|
|
||||||
}
|
}
|
||||||
catch
|
|
||||||
{
|
foreach(Object item in sourceList) {
|
||||||
|
try {
|
||||||
|
_ = targetList.Add(item.GetType().IsValueType()
|
||||||
|
? item
|
||||||
|
: item.CopyPropertiesToNew<Object>());
|
||||||
|
} catch {
|
||||||
// ignored
|
// ignored
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
source.CopyPropertiesTo(target);
|
_ = source.CopyPropertiesTo(target);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return target;
|
return target;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Dictionary<string, TypeValuePair> GetSourceMap(object source)
|
private static Dictionary<String, TypeValuePair> GetSourceMap(Object source) {
|
||||||
{
|
|
||||||
// select distinct properties because they can be duplicated by inheritance
|
// select distinct properties because they can be duplicated by inheritance
|
||||||
var sourceProperties = Runtime.PropertyTypeCache
|
PropertyInfo[] sourceProperties = Runtime.PropertyTypeCache
|
||||||
.RetrieveFilteredProperties(source.GetType(), true, x => x.CanRead)
|
.RetrieveFilteredProperties(source.GetType(), true, x => x.CanRead)
|
||||||
.ToArray();
|
.ToArray();
|
||||||
|
|
||||||
@ -386,25 +359,26 @@
|
|||||||
sourceProperties.First(y => y.Name == x).GetValue(source)));
|
sourceProperties.First(y => y.Name == x).GetValue(source)));
|
||||||
}
|
}
|
||||||
|
|
||||||
internal class TypeValuePair
|
internal class TypeValuePair {
|
||||||
{
|
public TypeValuePair(Type type, Object value) {
|
||||||
public TypeValuePair(Type type, object value)
|
this.Type = type;
|
||||||
{
|
this.Value = value;
|
||||||
Type = type;
|
|
||||||
Value = value;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Type Type { get; }
|
public Type Type {
|
||||||
|
get;
|
||||||
public object Value { get; }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
internal class PropertyInfoComparer : IEqualityComparer<PropertyInfo>
|
public Object Value {
|
||||||
{
|
get;
|
||||||
public bool Equals(PropertyInfo x, PropertyInfo y)
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class PropertyInfoComparer : IEqualityComparer<PropertyInfo> {
|
||||||
|
public Boolean Equals(PropertyInfo x, PropertyInfo y)
|
||||||
=> x != null && y != null && x.Name == y.Name && x.PropertyType == y.PropertyType;
|
=> x != null && y != null && x.Name == y.Name && x.PropertyType == y.PropertyType;
|
||||||
|
|
||||||
public int GetHashCode(PropertyInfo obj)
|
public Int32 GetHashCode(PropertyInfo obj)
|
||||||
=> obj.Name.GetHashCode() + obj.PropertyType.Name.GetHashCode();
|
=> obj.Name.GetHashCode() + obj.PropertyType.Name.GetHashCode();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,10 @@
|
|||||||
namespace Unosquare.Swan.Components
|
using System;
|
||||||
{
|
using System.Linq;
|
||||||
using System;
|
using System.Collections.Concurrent;
|
||||||
using System.Linq;
|
using System.Collections.Generic;
|
||||||
using System.Collections.Concurrent;
|
using Unosquare.Swan.Abstractions;
|
||||||
using System.Collections.Generic;
|
|
||||||
using Abstractions;
|
|
||||||
|
|
||||||
|
namespace Unosquare.Swan.Components {
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents an object validator.
|
/// Represents an object validator.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -68,10 +67,9 @@
|
|||||||
/// }
|
/// }
|
||||||
/// </code>
|
/// </code>
|
||||||
/// </example>
|
/// </example>
|
||||||
public class ObjectValidator
|
public class ObjectValidator {
|
||||||
{
|
private readonly ConcurrentDictionary<Type, List<Tuple<Delegate, String>>> _predicates =
|
||||||
private readonly ConcurrentDictionary<Type, List<Tuple<Delegate, string>>> _predicates =
|
new ConcurrentDictionary<Type, List<Tuple<Delegate, String>>>();
|
||||||
new ConcurrentDictionary<Type, List<Tuple<Delegate, string>>>();
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Validates an object given the specified validators and attributes.
|
/// Validates an object given the specified validators and attributes.
|
||||||
@ -79,10 +77,9 @@
|
|||||||
/// <typeparam name="T">The type of the object.</typeparam>
|
/// <typeparam name="T">The type of the object.</typeparam>
|
||||||
/// <param name="obj">The object.</param>
|
/// <param name="obj">The object.</param>
|
||||||
/// <returns cref="ObjectValidationResult">A validation result. </returns>
|
/// <returns cref="ObjectValidationResult">A validation result. </returns>
|
||||||
public ObjectValidationResult Validate<T>(T obj)
|
public ObjectValidationResult Validate<T>(T obj) {
|
||||||
{
|
ObjectValidationResult errorList = new ObjectValidationResult();
|
||||||
var errorList = new ObjectValidationResult();
|
_ = this.ValidateObject(obj, false, errorList.Add);
|
||||||
ValidateObject(obj, false, errorList.Add);
|
|
||||||
|
|
||||||
return errorList;
|
return errorList;
|
||||||
}
|
}
|
||||||
@ -96,7 +93,7 @@
|
|||||||
/// <c>true</c> if the specified object is valid; otherwise, <c>false</c>.
|
/// <c>true</c> if the specified object is valid; otherwise, <c>false</c>.
|
||||||
/// </returns>
|
/// </returns>
|
||||||
/// <exception cref="ArgumentNullException">obj.</exception>
|
/// <exception cref="ArgumentNullException">obj.</exception>
|
||||||
public bool IsValid<T>(T obj) => ValidateObject(obj);
|
public Boolean IsValid<T>(T obj) => this.ValidateObject(obj);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Adds a validator to a specific class.
|
/// Adds a validator to a specific class.
|
||||||
@ -109,52 +106,56 @@
|
|||||||
/// or
|
/// or
|
||||||
/// message.
|
/// message.
|
||||||
/// </exception>
|
/// </exception>
|
||||||
public void AddValidator<T>(Predicate<T> predicate, string message)
|
public void AddValidator<T>(Predicate<T> predicate, String message)
|
||||||
where T : class
|
where T : class {
|
||||||
{
|
if(predicate == null) {
|
||||||
if (predicate == null)
|
|
||||||
throw new ArgumentNullException(nameof(predicate));
|
throw new ArgumentNullException(nameof(predicate));
|
||||||
|
}
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(message))
|
if(String.IsNullOrEmpty(message)) {
|
||||||
throw new ArgumentNullException(message);
|
throw new ArgumentNullException(message);
|
||||||
|
|
||||||
if (!_predicates.TryGetValue(typeof(T), out var existing))
|
|
||||||
{
|
|
||||||
existing = new List<Tuple<Delegate, string>>();
|
|
||||||
_predicates[typeof(T)] = existing;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
existing.Add(Tuple.Create((Delegate) predicate, message));
|
if(!this._predicates.TryGetValue(typeof(T), out List<Tuple<Delegate, String>> existing)) {
|
||||||
|
existing = new List<Tuple<Delegate, String>>();
|
||||||
|
this._predicates[typeof(T)] = existing;
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool ValidateObject<T>(T obj, bool returnOnError = true, Action<string, string> action = null)
|
existing.Add(Tuple.Create((Delegate)predicate, message));
|
||||||
{
|
}
|
||||||
if (Equals(obj, null))
|
|
||||||
|
private Boolean ValidateObject<T>(T obj, Boolean returnOnError = true, Action<String, String> action = null) {
|
||||||
|
if(Equals(obj, null)) {
|
||||||
throw new ArgumentNullException(nameof(obj));
|
throw new ArgumentNullException(nameof(obj));
|
||||||
|
}
|
||||||
|
|
||||||
if (_predicates.ContainsKey(typeof(T)))
|
if(this._predicates.ContainsKey(typeof(T))) {
|
||||||
{
|
foreach(Tuple<Delegate, String> validation in this._predicates[typeof(T)]) {
|
||||||
foreach (var validation in _predicates[typeof(T)])
|
if((Boolean)validation.Item1.DynamicInvoke(obj)) {
|
||||||
{
|
continue;
|
||||||
if ((bool) validation.Item1.DynamicInvoke(obj)) continue;
|
}
|
||||||
|
|
||||||
action?.Invoke(string.Empty, validation.Item2);
|
action?.Invoke(String.Empty, validation.Item2);
|
||||||
if (returnOnError) return false;
|
if(returnOnError) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var properties = Runtime.AttributeCache.RetrieveFromType<T>(typeof(IValidator));
|
Dictionary<System.Reflection.PropertyInfo, IEnumerable<Object>> properties = Runtime.AttributeCache.RetrieveFromType<T>(typeof(IValidator));
|
||||||
|
|
||||||
foreach (var prop in properties)
|
foreach(KeyValuePair<System.Reflection.PropertyInfo, IEnumerable<Object>> prop in properties) {
|
||||||
{
|
foreach(Object attribute in prop.Value) {
|
||||||
foreach (var attribute in prop.Value)
|
IValidator val = (IValidator)attribute;
|
||||||
{
|
|
||||||
var val = (IValidator) attribute;
|
|
||||||
|
|
||||||
if (val.IsValid(prop.Key.GetValue(obj, null))) continue;
|
if(val.IsValid(prop.Key.GetValue(obj, null))) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
action?.Invoke(prop.Key.Name, val.ErrorMessage);
|
action?.Invoke(prop.Key.Name, val.ErrorMessage);
|
||||||
if (returnOnError) return false;
|
if(returnOnError) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -165,8 +166,7 @@
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Defines a validation result containing all validation errors and their properties.
|
/// Defines a validation result containing all validation errors and their properties.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class ObjectValidationResult
|
public class ObjectValidationResult {
|
||||||
{
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A list of errors.
|
/// A list of errors.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -175,30 +175,33 @@
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// <c>true</c> if there are no errors; otherwise, <c>false</c>.
|
/// <c>true</c> if there are no errors; otherwise, <c>false</c>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool IsValid => !Errors.Any();
|
public Boolean IsValid => !this.Errors.Any();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Adds an error with a specified property name.
|
/// Adds an error with a specified property name.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="propertyName">The property name.</param>
|
/// <param name="propertyName">The property name.</param>
|
||||||
/// <param name="errorMessage">The error message.</param>
|
/// <param name="errorMessage">The error message.</param>
|
||||||
public void Add(string propertyName, string errorMessage) =>
|
public void Add(String propertyName, String errorMessage) =>
|
||||||
Errors.Add(new ValidationError {ErrorMessage = errorMessage, PropertyName = propertyName});
|
this.Errors.Add(new ValidationError { ErrorMessage = errorMessage, PropertyName = propertyName });
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Defines a validation error.
|
/// Defines a validation error.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class ValidationError
|
public class ValidationError {
|
||||||
{
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The property name.
|
/// The property name.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string PropertyName { get; set; }
|
public String PropertyName {
|
||||||
|
get; set;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The message error.
|
/// The message error.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string ErrorMessage { get; set; }
|
public String ErrorMessage {
|
||||||
|
get; set;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,22 +1,19 @@
|
|||||||
namespace Unosquare.Swan.Components
|
using System;
|
||||||
{
|
using System.Threading;
|
||||||
using System;
|
using Unosquare.Swan.Abstractions;
|
||||||
using System.Threading;
|
|
||||||
using Abstractions;
|
|
||||||
|
|
||||||
|
namespace Unosquare.Swan.Components {
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Provides factory methods to create synchronized reader-writer locks
|
/// Provides factory methods to create synchronized reader-writer locks
|
||||||
/// that support a generalized locking and releasing api and syntax.
|
/// that support a generalized locking and releasing api and syntax.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static class SyncLockerFactory
|
public static class SyncLockerFactory {
|
||||||
{
|
|
||||||
#region Enums and Interfaces
|
#region Enums and Interfaces
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Enumerates the locking operations.
|
/// Enumerates the locking operations.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private enum LockHolderType
|
private enum LockHolderType {
|
||||||
{
|
|
||||||
Read,
|
Read,
|
||||||
Write,
|
Write,
|
||||||
}
|
}
|
||||||
@ -24,8 +21,7 @@
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Defines methods for releasing locks.
|
/// Defines methods for releasing locks.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private interface ISyncReleasable
|
private interface ISyncReleasable {
|
||||||
{
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Releases the writer lock.
|
/// Releases the writer lock.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -67,7 +63,7 @@
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="useSlim">if set to <c>true</c> it uses the Slim version of a reader-writer lock.</param>
|
/// <param name="useSlim">if set to <c>true</c> it uses the Slim version of a reader-writer lock.</param>
|
||||||
/// <returns>The Sync Locker.</returns>
|
/// <returns>The Sync Locker.</returns>
|
||||||
public static ISyncLocker Create(bool useSlim) => useSlim ? CreateSlim() : Create();
|
public static ISyncLocker Create(Boolean useSlim) => useSlim ? CreateSlim() : Create();
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
@ -77,34 +73,35 @@
|
|||||||
/// The lock releaser. Calling the dispose method releases the lock entered by the parent SyncLocker.
|
/// The lock releaser. Calling the dispose method releases the lock entered by the parent SyncLocker.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <seealso cref="System.IDisposable" />
|
/// <seealso cref="System.IDisposable" />
|
||||||
private sealed class SyncLockReleaser : IDisposable
|
private sealed class SyncLockReleaser : IDisposable {
|
||||||
{
|
|
||||||
private readonly ISyncReleasable _parent;
|
private readonly ISyncReleasable _parent;
|
||||||
private readonly LockHolderType _operation;
|
private readonly LockHolderType _operation;
|
||||||
|
|
||||||
private bool _isDisposed;
|
private Boolean _isDisposed;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="SyncLockReleaser"/> class.
|
/// Initializes a new instance of the <see cref="SyncLockReleaser"/> class.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="parent">The parent.</param>
|
/// <param name="parent">The parent.</param>
|
||||||
/// <param name="operation">The operation.</param>
|
/// <param name="operation">The operation.</param>
|
||||||
public SyncLockReleaser(ISyncReleasable parent, LockHolderType operation)
|
public SyncLockReleaser(ISyncReleasable parent, LockHolderType operation) {
|
||||||
{
|
this._parent = parent;
|
||||||
_parent = parent;
|
this._operation = operation;
|
||||||
_operation = operation;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public void Dispose()
|
public void Dispose() {
|
||||||
{
|
if(this._isDisposed) {
|
||||||
if (_isDisposed) return;
|
return;
|
||||||
_isDisposed = true;
|
}
|
||||||
|
|
||||||
if (_operation == LockHolderType.Read)
|
this._isDisposed = true;
|
||||||
_parent.ReleaseReaderLock();
|
|
||||||
else
|
if(this._operation == LockHolderType.Read) {
|
||||||
_parent.ReleaseWriterLock();
|
this._parent.ReleaseReaderLock();
|
||||||
|
} else {
|
||||||
|
this._parent.ReleaseWriterLock();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -114,38 +111,37 @@
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <seealso cref="ISyncLocker" />
|
/// <seealso cref="ISyncLocker" />
|
||||||
/// <seealso cref="ISyncReleasable" />
|
/// <seealso cref="ISyncReleasable" />
|
||||||
private sealed class SyncLocker : ISyncLocker, ISyncReleasable
|
private sealed class SyncLocker : ISyncLocker, ISyncReleasable {
|
||||||
{
|
private Boolean _isDisposed;
|
||||||
private bool _isDisposed;
|
|
||||||
private ReaderWriterLock _locker = new ReaderWriterLock();
|
private ReaderWriterLock _locker = new ReaderWriterLock();
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public IDisposable AcquireReaderLock()
|
public IDisposable AcquireReaderLock() {
|
||||||
{
|
this._locker?.AcquireReaderLock(Timeout.Infinite);
|
||||||
_locker?.AcquireReaderLock(Timeout.Infinite);
|
|
||||||
return new SyncLockReleaser(this, LockHolderType.Read);
|
return new SyncLockReleaser(this, LockHolderType.Read);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public IDisposable AcquireWriterLock()
|
public IDisposable AcquireWriterLock() {
|
||||||
{
|
this._locker?.AcquireWriterLock(Timeout.Infinite);
|
||||||
_locker?.AcquireWriterLock(Timeout.Infinite);
|
|
||||||
return new SyncLockReleaser(this, LockHolderType.Write);
|
return new SyncLockReleaser(this, LockHolderType.Write);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public void ReleaseWriterLock() => _locker?.ReleaseWriterLock();
|
public void ReleaseWriterLock() => this._locker?.ReleaseWriterLock();
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public void ReleaseReaderLock() => _locker?.ReleaseReaderLock();
|
public void ReleaseReaderLock() => this._locker?.ReleaseReaderLock();
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public void Dispose()
|
public void Dispose() {
|
||||||
{
|
if(this._isDisposed) {
|
||||||
if (_isDisposed) return;
|
return;
|
||||||
_isDisposed = true;
|
}
|
||||||
_locker?.ReleaseLock();
|
|
||||||
_locker = null;
|
this._isDisposed = true;
|
||||||
|
_ = this._locker?.ReleaseLock();
|
||||||
|
this._locker = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@ -155,40 +151,39 @@
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <seealso cref="ISyncLocker" />
|
/// <seealso cref="ISyncLocker" />
|
||||||
/// <seealso cref="ISyncReleasable" />
|
/// <seealso cref="ISyncReleasable" />
|
||||||
private sealed class SyncLockerSlim : ISyncLocker, ISyncReleasable
|
private sealed class SyncLockerSlim : ISyncLocker, ISyncReleasable {
|
||||||
{
|
private Boolean _isDisposed;
|
||||||
private bool _isDisposed;
|
|
||||||
|
|
||||||
private ReaderWriterLockSlim _locker
|
private ReaderWriterLockSlim _locker
|
||||||
= new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion);
|
= new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion);
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public IDisposable AcquireReaderLock()
|
public IDisposable AcquireReaderLock() {
|
||||||
{
|
this._locker?.EnterReadLock();
|
||||||
_locker?.EnterReadLock();
|
|
||||||
return new SyncLockReleaser(this, LockHolderType.Read);
|
return new SyncLockReleaser(this, LockHolderType.Read);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public IDisposable AcquireWriterLock()
|
public IDisposable AcquireWriterLock() {
|
||||||
{
|
this._locker?.EnterWriteLock();
|
||||||
_locker?.EnterWriteLock();
|
|
||||||
return new SyncLockReleaser(this, LockHolderType.Write);
|
return new SyncLockReleaser(this, LockHolderType.Write);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public void ReleaseWriterLock() => _locker?.ExitWriteLock();
|
public void ReleaseWriterLock() => this._locker?.ExitWriteLock();
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public void ReleaseReaderLock() => _locker?.ExitReadLock();
|
public void ReleaseReaderLock() => this._locker?.ExitReadLock();
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public void Dispose()
|
public void Dispose() {
|
||||||
{
|
if(this._isDisposed) {
|
||||||
if (_isDisposed) return;
|
return;
|
||||||
_isDisposed = true;
|
}
|
||||||
_locker?.Dispose();
|
|
||||||
_locker = null;
|
this._isDisposed = true;
|
||||||
|
this._locker?.Dispose();
|
||||||
|
this._locker = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,10 +1,9 @@
|
|||||||
#if !NETSTANDARD1_3
|
#if !NETSTANDARD1_3
|
||||||
namespace Unosquare.Swan.Components
|
|
||||||
{
|
|
||||||
using System;
|
|
||||||
using System.Threading;
|
|
||||||
using Abstractions;
|
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Threading;
|
||||||
|
using Unosquare.Swan.Abstractions;
|
||||||
|
namespace Unosquare.Swan.Components {
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Use this singleton to wait for a specific <c>TimeSpan</c> or time.
|
/// Use this singleton to wait for a specific <c>TimeSpan</c> or time.
|
||||||
///
|
///
|
||||||
@ -12,43 +11,36 @@ namespace Unosquare.Swan.Components
|
|||||||
/// the time condition is satisfied.
|
/// the time condition is satisfied.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <seealso cref="SingletonBase{TimerControl}" />
|
/// <seealso cref="SingletonBase{TimerControl}" />
|
||||||
public class TimerControl : SingletonBase<TimerControl>
|
public class TimerControl : SingletonBase<TimerControl> {
|
||||||
{
|
[System.Diagnostics.CodeAnalysis.SuppressMessage("Codequalität", "IDE0052:Ungelesene private Member entfernen", Justification = "<Ausstehend>")]
|
||||||
private readonly Timer _innerTimer;
|
private readonly Timer _innerTimer;
|
||||||
private readonly IWaitEvent _delayLock = WaitEventFactory.Create(true);
|
private readonly IWaitEvent _delayLock = WaitEventFactory.Create(true);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="TimerControl"/> class.
|
/// Initializes a new instance of the <see cref="TimerControl"/> class.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
protected TimerControl()
|
protected TimerControl() => this._innerTimer = new Timer(
|
||||||
{
|
x => {
|
||||||
_innerTimer = new Timer(
|
try {
|
||||||
x =>
|
this._delayLock.Complete();
|
||||||
{
|
this._delayLock.Begin();
|
||||||
try
|
} catch {
|
||||||
{
|
|
||||||
_delayLock.Complete();
|
|
||||||
_delayLock.Begin();
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
// ignore
|
// ignore
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
null,
|
null,
|
||||||
0,
|
0,
|
||||||
15);
|
15);
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Waits until the time is elapsed.
|
/// Waits until the time is elapsed.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="untilDate">The until date.</param>
|
/// <param name="untilDate">The until date.</param>
|
||||||
/// <param name="ct">The cancellation token.</param>
|
/// <param name="ct">The cancellation token.</param>
|
||||||
public void WaitUntil(DateTime untilDate, CancellationToken ct = default)
|
public void WaitUntil(DateTime untilDate, CancellationToken ct = default) {
|
||||||
{
|
while(!ct.IsCancellationRequested && DateTime.UtcNow < untilDate) {
|
||||||
while (!ct.IsCancellationRequested && DateTime.UtcNow < untilDate)
|
this._delayLock.Wait();
|
||||||
_delayLock.Wait();
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -57,7 +49,7 @@ namespace Unosquare.Swan.Components
|
|||||||
/// <param name="waitTime">The wait time.</param>
|
/// <param name="waitTime">The wait time.</param>
|
||||||
/// <param name="ct">The cancellation token.</param>
|
/// <param name="ct">The cancellation token.</param>
|
||||||
public void Wait(TimeSpan waitTime, CancellationToken ct = default) =>
|
public void Wait(TimeSpan waitTime, CancellationToken ct = default) =>
|
||||||
WaitUntil(DateTime.UtcNow.Add(waitTime), ct);
|
this.WaitUntil(DateTime.UtcNow.Add(waitTime), ct);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
@ -1,10 +1,10 @@
|
|||||||
#if !NETSTANDARD1_3
|
|
||||||
namespace Unosquare.Swan.Components
|
|
||||||
{
|
|
||||||
using System;
|
|
||||||
using System.Threading;
|
|
||||||
using Abstractions;
|
|
||||||
|
|
||||||
|
#if !NETSTANDARD1_3
|
||||||
|
using System;
|
||||||
|
using System.Threading;
|
||||||
|
using Unosquare.Swan.Abstractions;
|
||||||
|
namespace Unosquare.Swan.Components {
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Provides a Manual Reset Event factory with a unified API.
|
/// Provides a Manual Reset Event factory with a unified API.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -53,8 +53,7 @@ namespace Unosquare.Swan.Components
|
|||||||
/// }
|
/// }
|
||||||
/// </code>
|
/// </code>
|
||||||
/// </example>
|
/// </example>
|
||||||
public static class WaitEventFactory
|
public static class WaitEventFactory {
|
||||||
{
|
|
||||||
#region Factory Methods
|
#region Factory Methods
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -62,14 +61,14 @@ namespace Unosquare.Swan.Components
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="isCompleted">if initially set to completed. Generally true.</param>
|
/// <param name="isCompleted">if initially set to completed. Generally true.</param>
|
||||||
/// <returns>The Wait Event.</returns>
|
/// <returns>The Wait Event.</returns>
|
||||||
public static IWaitEvent Create(bool isCompleted) => new WaitEvent(isCompleted);
|
public static IWaitEvent Create(Boolean isCompleted) => new WaitEvent(isCompleted);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates a Wait Event backed by a ManualResetEventSlim.
|
/// Creates a Wait Event backed by a ManualResetEventSlim.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="isCompleted">if initially set to completed. Generally true.</param>
|
/// <param name="isCompleted">if initially set to completed. Generally true.</param>
|
||||||
/// <returns>The Wait Event.</returns>
|
/// <returns>The Wait Event.</returns>
|
||||||
public static IWaitEvent CreateSlim(bool isCompleted) => new WaitEventSlim(isCompleted);
|
public static IWaitEvent CreateSlim(Boolean isCompleted) => new WaitEventSlim(isCompleted);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates a Wait Event backed by a ManualResetEventSlim.
|
/// Creates a Wait Event backed by a ManualResetEventSlim.
|
||||||
@ -77,7 +76,7 @@ namespace Unosquare.Swan.Components
|
|||||||
/// <param name="isCompleted">if initially set to completed. Generally true.</param>
|
/// <param name="isCompleted">if initially set to completed. Generally true.</param>
|
||||||
/// <param name="useSlim">if set to <c>true</c> creates a slim version of the wait event.</param>
|
/// <param name="useSlim">if set to <c>true</c> creates a slim version of the wait event.</param>
|
||||||
/// <returns>The Wait Event.</returns>
|
/// <returns>The Wait Event.</returns>
|
||||||
public static IWaitEvent Create(bool isCompleted, bool useSlim) => useSlim ? CreateSlim(isCompleted) : Create(isCompleted);
|
public static IWaitEvent Create(Boolean isCompleted, Boolean useSlim) => useSlim ? CreateSlim(isCompleted) : Create(isCompleted);
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
@ -86,134 +85,109 @@ namespace Unosquare.Swan.Components
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Defines a WaitEvent backed by a ManualResetEvent.
|
/// Defines a WaitEvent backed by a ManualResetEvent.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private class WaitEvent : IWaitEvent
|
private class WaitEvent : IWaitEvent {
|
||||||
{
|
|
||||||
private ManualResetEvent _event;
|
private ManualResetEvent _event;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="WaitEvent"/> class.
|
/// Initializes a new instance of the <see cref="WaitEvent"/> class.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="isCompleted">if set to <c>true</c> [is completed].</param>
|
/// <param name="isCompleted">if set to <c>true</c> [is completed].</param>
|
||||||
public WaitEvent(bool isCompleted)
|
public WaitEvent(Boolean isCompleted) => this._event = new ManualResetEvent(isCompleted);
|
||||||
{
|
|
||||||
_event = new ManualResetEvent(isCompleted);
|
/// <inheritdoc />
|
||||||
|
public Boolean IsDisposed {
|
||||||
|
get; private set;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public bool IsDisposed { get; private set; }
|
public Boolean IsValid => this.IsDisposed || this._event == null
|
||||||
|
? false
|
||||||
|
: this._event?.SafeWaitHandle?.IsClosed ?? true ? false : !(this._event?.SafeWaitHandle?.IsInvalid ?? true);
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public bool IsValid
|
public Boolean IsCompleted => this.IsValid == false ? true : this._event?.WaitOne(0) ?? true;
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
if (IsDisposed || _event == null)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (_event?.SafeWaitHandle?.IsClosed ?? true)
|
/// <inheritdoc />
|
||||||
return false;
|
public Boolean IsInProgress => !this.IsCompleted;
|
||||||
|
|
||||||
return !(_event?.SafeWaitHandle?.IsInvalid ?? true);
|
/// <inheritdoc />
|
||||||
|
public void Begin() => this._event?.Reset();
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public void Complete() => this._event?.Set();
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
void IDisposable.Dispose() {
|
||||||
|
if(this.IsDisposed) {
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.IsDisposed = true;
|
||||||
|
|
||||||
|
_ = this._event?.Set();
|
||||||
|
this._event?.Dispose();
|
||||||
|
this._event = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public bool IsCompleted
|
public void Wait() => this._event?.WaitOne();
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
if (IsValid == false)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
return _event?.WaitOne(0) ?? true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public bool IsInProgress => !IsCompleted;
|
public Boolean Wait(TimeSpan timeout) => this._event?.WaitOne(timeout) ?? true;
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public void Begin() => _event?.Reset();
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public void Complete() => _event?.Set();
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
void IDisposable.Dispose()
|
|
||||||
{
|
|
||||||
if (IsDisposed) return;
|
|
||||||
IsDisposed = true;
|
|
||||||
|
|
||||||
_event?.Set();
|
|
||||||
_event?.Dispose();
|
|
||||||
_event = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public void Wait() => _event?.WaitOne();
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public bool Wait(TimeSpan timeout) => _event?.WaitOne(timeout) ?? true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Defines a WaitEvent backed by a ManualResetEventSlim.
|
/// Defines a WaitEvent backed by a ManualResetEventSlim.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private class WaitEventSlim : IWaitEvent
|
private class WaitEventSlim : IWaitEvent {
|
||||||
{
|
|
||||||
private ManualResetEventSlim _event;
|
private ManualResetEventSlim _event;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="WaitEventSlim"/> class.
|
/// Initializes a new instance of the <see cref="WaitEventSlim"/> class.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="isCompleted">if set to <c>true</c> [is completed].</param>
|
/// <param name="isCompleted">if set to <c>true</c> [is completed].</param>
|
||||||
public WaitEventSlim(bool isCompleted)
|
public WaitEventSlim(Boolean isCompleted) => this._event = new ManualResetEventSlim(isCompleted);
|
||||||
{
|
|
||||||
_event = new ManualResetEventSlim(isCompleted);
|
/// <inheritdoc />
|
||||||
|
public Boolean IsDisposed {
|
||||||
|
get; private set;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public bool IsDisposed { get; private set; }
|
public Boolean IsValid => this.IsDisposed || this._event?.WaitHandle?.SafeWaitHandle == null
|
||||||
|
? false
|
||||||
|
: !this._event.WaitHandle.SafeWaitHandle.IsClosed && !this._event.WaitHandle.SafeWaitHandle.IsInvalid;
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public bool IsValid
|
public Boolean IsCompleted => this.IsValid == false || this._event.IsSet;
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
if (IsDisposed || _event?.WaitHandle?.SafeWaitHandle == null) return false;
|
|
||||||
|
|
||||||
return !_event.WaitHandle.SafeWaitHandle.IsClosed && !_event.WaitHandle.SafeWaitHandle.IsInvalid;
|
/// <inheritdoc />
|
||||||
|
public Boolean IsInProgress => !this.IsCompleted;
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public void Begin() => this._event?.Reset();
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public void Complete() => this._event?.Set();
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
void IDisposable.Dispose() {
|
||||||
|
if(this.IsDisposed) {
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.IsDisposed = true;
|
||||||
|
|
||||||
|
this._event?.Set();
|
||||||
|
this._event?.Dispose();
|
||||||
|
this._event = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public bool IsCompleted => IsValid == false || _event.IsSet;
|
public void Wait() => this._event?.Wait();
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public bool IsInProgress => !IsCompleted;
|
public Boolean Wait(TimeSpan timeout) => this._event?.Wait(timeout) ?? true;
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public void Begin() => _event?.Reset();
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public void Complete() => _event?.Set();
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
void IDisposable.Dispose()
|
|
||||||
{
|
|
||||||
if (IsDisposed) return;
|
|
||||||
IsDisposed = true;
|
|
||||||
|
|
||||||
_event?.Set();
|
|
||||||
_event?.Dispose();
|
|
||||||
_event = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public void Wait() => _event?.Wait();
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public bool Wait(TimeSpan timeout) => _event?.Wait(timeout) ?? true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
@ -1,15 +1,13 @@
|
|||||||
namespace Unosquare.Swan
|
using System;
|
||||||
{
|
|
||||||
using System;
|
|
||||||
|
|
||||||
|
namespace Unosquare.Swan {
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents a struct of DateTimeSpan to compare dates and get in
|
/// Represents a struct of DateTimeSpan to compare dates and get in
|
||||||
/// separate fields the amount of time between those dates.
|
/// separate fields the amount of time between those dates.
|
||||||
///
|
///
|
||||||
/// Based on https://stackoverflow.com/a/9216404/1096693.
|
/// Based on https://stackoverflow.com/a/9216404/1096693.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public struct DateTimeSpan
|
public struct DateTimeSpan {
|
||||||
{
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="DateTimeSpan"/> struct.
|
/// Initializes a new instance of the <see cref="DateTimeSpan"/> struct.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -20,15 +18,14 @@
|
|||||||
/// <param name="minutes">The minutes.</param>
|
/// <param name="minutes">The minutes.</param>
|
||||||
/// <param name="seconds">The seconds.</param>
|
/// <param name="seconds">The seconds.</param>
|
||||||
/// <param name="milliseconds">The milliseconds.</param>
|
/// <param name="milliseconds">The milliseconds.</param>
|
||||||
public DateTimeSpan(int years, int months, int days, int hours, int minutes, int seconds, int milliseconds)
|
public DateTimeSpan(Int32 years, Int32 months, Int32 days, Int32 hours, Int32 minutes, Int32 seconds, Int32 milliseconds) {
|
||||||
{
|
this.Years = years;
|
||||||
Years = years;
|
this.Months = months;
|
||||||
Months = months;
|
this.Days = days;
|
||||||
Days = days;
|
this.Hours = hours;
|
||||||
Hours = hours;
|
this.Minutes = minutes;
|
||||||
Minutes = minutes;
|
this.Seconds = seconds;
|
||||||
Seconds = seconds;
|
this.Milliseconds = milliseconds;
|
||||||
Milliseconds = milliseconds;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -37,7 +34,9 @@
|
|||||||
/// <value>
|
/// <value>
|
||||||
/// The years.
|
/// The years.
|
||||||
/// </value>
|
/// </value>
|
||||||
public int Years { get; }
|
public Int32 Years {
|
||||||
|
get;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the months.
|
/// Gets the months.
|
||||||
@ -45,7 +44,9 @@
|
|||||||
/// <value>
|
/// <value>
|
||||||
/// The months.
|
/// The months.
|
||||||
/// </value>
|
/// </value>
|
||||||
public int Months { get; }
|
public Int32 Months {
|
||||||
|
get;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the days.
|
/// Gets the days.
|
||||||
@ -53,7 +54,9 @@
|
|||||||
/// <value>
|
/// <value>
|
||||||
/// The days.
|
/// The days.
|
||||||
/// </value>
|
/// </value>
|
||||||
public int Days { get; }
|
public Int32 Days {
|
||||||
|
get;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the hours.
|
/// Gets the hours.
|
||||||
@ -61,7 +64,9 @@
|
|||||||
/// <value>
|
/// <value>
|
||||||
/// The hours.
|
/// The hours.
|
||||||
/// </value>
|
/// </value>
|
||||||
public int Hours { get; }
|
public Int32 Hours {
|
||||||
|
get;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the minutes.
|
/// Gets the minutes.
|
||||||
@ -69,7 +74,9 @@
|
|||||||
/// <value>
|
/// <value>
|
||||||
/// The minutes.
|
/// The minutes.
|
||||||
/// </value>
|
/// </value>
|
||||||
public int Minutes { get; }
|
public Int32 Minutes {
|
||||||
|
get;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the seconds.
|
/// Gets the seconds.
|
||||||
@ -77,7 +84,9 @@
|
|||||||
/// <value>
|
/// <value>
|
||||||
/// The seconds.
|
/// The seconds.
|
||||||
/// </value>
|
/// </value>
|
||||||
public int Seconds { get; }
|
public Int32 Seconds {
|
||||||
|
get;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the milliseconds.
|
/// Gets the milliseconds.
|
||||||
@ -85,62 +94,54 @@
|
|||||||
/// <value>
|
/// <value>
|
||||||
/// The milliseconds.
|
/// The milliseconds.
|
||||||
/// </value>
|
/// </value>
|
||||||
public int Milliseconds { get; }
|
public Int32 Milliseconds {
|
||||||
|
get;
|
||||||
|
}
|
||||||
|
|
||||||
internal static DateTimeSpan CompareDates(DateTime date1, DateTime date2)
|
internal static DateTimeSpan CompareDates(DateTime date1, DateTime date2) {
|
||||||
{
|
if(date2 < date1) {
|
||||||
if (date2 < date1)
|
DateTime sub = date1;
|
||||||
{
|
|
||||||
var sub = date1;
|
|
||||||
date1 = date2;
|
date1 = date2;
|
||||||
date2 = sub;
|
date2 = sub;
|
||||||
}
|
}
|
||||||
|
|
||||||
var current = date1;
|
DateTime current = date1;
|
||||||
var years = 0;
|
Int32 years = 0;
|
||||||
var months = 0;
|
Int32 months = 0;
|
||||||
var days = 0;
|
Int32 days = 0;
|
||||||
|
|
||||||
var phase = Phase.Years;
|
Phase phase = Phase.Years;
|
||||||
var span = new DateTimeSpan();
|
DateTimeSpan span = new DateTimeSpan();
|
||||||
var officialDay = current.Day;
|
Int32 officialDay = current.Day;
|
||||||
|
|
||||||
while (phase != Phase.Done)
|
while(phase != Phase.Done) {
|
||||||
{
|
switch(phase) {
|
||||||
switch (phase)
|
|
||||||
{
|
|
||||||
case Phase.Years:
|
case Phase.Years:
|
||||||
if (current.AddYears(years + 1) > date2)
|
if(current.AddYears(years + 1) > date2) {
|
||||||
{
|
|
||||||
phase = Phase.Months;
|
phase = Phase.Months;
|
||||||
current = current.AddYears(years);
|
current = current.AddYears(years);
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
years++;
|
years++;
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
case Phase.Months:
|
case Phase.Months:
|
||||||
if (current.AddMonths(months + 1) > date2)
|
if(current.AddMonths(months + 1) > date2) {
|
||||||
{
|
|
||||||
phase = Phase.Days;
|
phase = Phase.Days;
|
||||||
current = current.AddMonths(months);
|
current = current.AddMonths(months);
|
||||||
if (current.Day < officialDay &&
|
if(current.Day < officialDay &&
|
||||||
officialDay <= DateTime.DaysInMonth(current.Year, current.Month))
|
officialDay <= DateTime.DaysInMonth(current.Year, current.Month)) {
|
||||||
current = current.AddDays(officialDay - current.Day);
|
current = current.AddDays(officialDay - current.Day);
|
||||||
}
|
}
|
||||||
else
|
} else {
|
||||||
{
|
|
||||||
months++;
|
months++;
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
case Phase.Days:
|
case Phase.Days:
|
||||||
if (current.AddDays(days + 1) > date2)
|
if(current.AddDays(days + 1) > date2) {
|
||||||
{
|
|
||||||
current = current.AddDays(days);
|
current = current.AddDays(days);
|
||||||
var timespan = date2 - current;
|
TimeSpan timespan = date2 - current;
|
||||||
span = new DateTimeSpan(
|
span = new DateTimeSpan(
|
||||||
years,
|
years,
|
||||||
months,
|
months,
|
||||||
@ -150,9 +151,7 @@
|
|||||||
timespan.Seconds,
|
timespan.Seconds,
|
||||||
timespan.Milliseconds);
|
timespan.Milliseconds);
|
||||||
phase = Phase.Done;
|
phase = Phase.Done;
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
days++;
|
days++;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -163,8 +162,7 @@
|
|||||||
return span;
|
return span;
|
||||||
}
|
}
|
||||||
|
|
||||||
private enum Phase
|
private enum Phase {
|
||||||
{
|
|
||||||
Years,
|
Years,
|
||||||
Months,
|
Months,
|
||||||
Days,
|
Days,
|
||||||
|
@ -1,16 +1,14 @@
|
|||||||
namespace Unosquare.Swan
|
using Unosquare.Swan.Reflection;
|
||||||
{
|
using System;
|
||||||
using Reflection;
|
using System.Collections.Generic;
|
||||||
using System;
|
using System.Linq;
|
||||||
using System.Collections.Generic;
|
using System.Net;
|
||||||
using System.Linq;
|
|
||||||
using System.Net;
|
|
||||||
|
|
||||||
|
namespace Unosquare.Swan {
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Contains useful constants and definitions.
|
/// Contains useful constants and definitions.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static partial class Definitions
|
public static partial class Definitions {
|
||||||
{
|
|
||||||
#region Main Dictionary Definition
|
#region Main Dictionary Definition
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -21,39 +19,39 @@
|
|||||||
{
|
{
|
||||||
// Non-Nullables
|
// Non-Nullables
|
||||||
{typeof(DateTime), new ExtendedTypeInfo<DateTime>()},
|
{typeof(DateTime), new ExtendedTypeInfo<DateTime>()},
|
||||||
{typeof(byte), new ExtendedTypeInfo<byte>()},
|
{typeof(Byte), new ExtendedTypeInfo<Byte>()},
|
||||||
{typeof(sbyte), new ExtendedTypeInfo<sbyte>()},
|
{typeof(SByte), new ExtendedTypeInfo<SByte>()},
|
||||||
{typeof(int), new ExtendedTypeInfo<int>()},
|
{typeof(Int32), new ExtendedTypeInfo<Int32>()},
|
||||||
{typeof(uint), new ExtendedTypeInfo<uint>()},
|
{typeof(UInt32), new ExtendedTypeInfo<UInt32>()},
|
||||||
{typeof(short), new ExtendedTypeInfo<short>()},
|
{typeof(Int16), new ExtendedTypeInfo<Int16>()},
|
||||||
{typeof(ushort), new ExtendedTypeInfo<ushort>()},
|
{typeof(UInt16), new ExtendedTypeInfo<UInt16>()},
|
||||||
{typeof(long), new ExtendedTypeInfo<long>()},
|
{typeof(Int64), new ExtendedTypeInfo<Int64>()},
|
||||||
{typeof(ulong), new ExtendedTypeInfo<ulong>()},
|
{typeof(UInt64), new ExtendedTypeInfo<UInt64>()},
|
||||||
{typeof(float), new ExtendedTypeInfo<float>()},
|
{typeof(Single), new ExtendedTypeInfo<Single>()},
|
||||||
{typeof(double), new ExtendedTypeInfo<double>()},
|
{typeof(Double), new ExtendedTypeInfo<Double>()},
|
||||||
{typeof(char), new ExtendedTypeInfo<char>()},
|
{typeof(Char), new ExtendedTypeInfo<Char>()},
|
||||||
{typeof(bool), new ExtendedTypeInfo<bool>()},
|
{typeof(Boolean), new ExtendedTypeInfo<Boolean>()},
|
||||||
{typeof(decimal), new ExtendedTypeInfo<decimal>()},
|
{typeof(Decimal), new ExtendedTypeInfo<Decimal>()},
|
||||||
{typeof(Guid), new ExtendedTypeInfo<Guid>()},
|
{typeof(Guid), new ExtendedTypeInfo<Guid>()},
|
||||||
|
|
||||||
// Strings is also considered a basic type (it's the only basic reference type)
|
// Strings is also considered a basic type (it's the only basic reference type)
|
||||||
{typeof(string), new ExtendedTypeInfo<string>()},
|
{typeof(String), new ExtendedTypeInfo<String>()},
|
||||||
|
|
||||||
// Nullables
|
// Nullables
|
||||||
{typeof(DateTime?), new ExtendedTypeInfo<DateTime?>()},
|
{typeof(DateTime?), new ExtendedTypeInfo<DateTime?>()},
|
||||||
{typeof(byte?), new ExtendedTypeInfo<byte?>()},
|
{typeof(Byte?), new ExtendedTypeInfo<Byte?>()},
|
||||||
{typeof(sbyte?), new ExtendedTypeInfo<sbyte?>()},
|
{typeof(SByte?), new ExtendedTypeInfo<SByte?>()},
|
||||||
{typeof(int?), new ExtendedTypeInfo<int?>()},
|
{typeof(Int32?), new ExtendedTypeInfo<Int32?>()},
|
||||||
{typeof(uint?), new ExtendedTypeInfo<uint?>()},
|
{typeof(UInt32?), new ExtendedTypeInfo<UInt32?>()},
|
||||||
{typeof(short?), new ExtendedTypeInfo<short?>()},
|
{typeof(Int16?), new ExtendedTypeInfo<Int16?>()},
|
||||||
{typeof(ushort?), new ExtendedTypeInfo<ushort?>()},
|
{typeof(UInt16?), new ExtendedTypeInfo<UInt16?>()},
|
||||||
{typeof(long?), new ExtendedTypeInfo<long?>()},
|
{typeof(Int64?), new ExtendedTypeInfo<Int64?>()},
|
||||||
{typeof(ulong?), new ExtendedTypeInfo<ulong?>()},
|
{typeof(UInt64?), new ExtendedTypeInfo<UInt64?>()},
|
||||||
{typeof(float?), new ExtendedTypeInfo<float?>()},
|
{typeof(Single?), new ExtendedTypeInfo<Single?>()},
|
||||||
{typeof(double?), new ExtendedTypeInfo<double?>()},
|
{typeof(Double?), new ExtendedTypeInfo<Double?>()},
|
||||||
{typeof(char?), new ExtendedTypeInfo<char?>()},
|
{typeof(Char?), new ExtendedTypeInfo<Char?>()},
|
||||||
{typeof(bool?), new ExtendedTypeInfo<bool?>()},
|
{typeof(Boolean?), new ExtendedTypeInfo<Boolean?>()},
|
||||||
{typeof(decimal?), new ExtendedTypeInfo<decimal?>()},
|
{typeof(Decimal?), new ExtendedTypeInfo<Decimal?>()},
|
||||||
{typeof(Guid?), new ExtendedTypeInfo<Guid?>()},
|
{typeof(Guid?), new ExtendedTypeInfo<Guid?>()},
|
||||||
|
|
||||||
// Additional Types
|
// Additional Types
|
||||||
@ -79,7 +77,9 @@
|
|||||||
/// <value>
|
/// <value>
|
||||||
/// All numeric types.
|
/// All numeric types.
|
||||||
/// </value>
|
/// </value>
|
||||||
public static List<Type> AllNumericTypes { get; } = new List<Type>(
|
public static List<Type> AllNumericTypes {
|
||||||
|
get;
|
||||||
|
} = new List<Type>(
|
||||||
BasicTypesInfo
|
BasicTypesInfo
|
||||||
.Where(kvp => kvp.Value.IsNumeric)
|
.Where(kvp => kvp.Value.IsNumeric)
|
||||||
.Select(kvp => kvp.Key).ToArray());
|
.Select(kvp => kvp.Key).ToArray());
|
||||||
@ -91,7 +91,9 @@
|
|||||||
/// <value>
|
/// <value>
|
||||||
/// All numeric value types.
|
/// All numeric value types.
|
||||||
/// </value>
|
/// </value>
|
||||||
public static List<Type> AllNumericValueTypes { get; } = new List<Type>(
|
public static List<Type> AllNumericValueTypes {
|
||||||
|
get;
|
||||||
|
} = new List<Type>(
|
||||||
BasicTypesInfo
|
BasicTypesInfo
|
||||||
.Where(kvp => kvp.Value.IsNumeric && kvp.Value.IsNullableValueType == false)
|
.Where(kvp => kvp.Value.IsNumeric && kvp.Value.IsNullableValueType == false)
|
||||||
.Select(kvp => kvp.Key).ToArray());
|
.Select(kvp => kvp.Key).ToArray());
|
||||||
@ -102,7 +104,9 @@
|
|||||||
/// <value>
|
/// <value>
|
||||||
/// All basic value types.
|
/// All basic value types.
|
||||||
/// </value>
|
/// </value>
|
||||||
public static List<Type> AllBasicValueTypes { get; } = new List<Type>(
|
public static List<Type> AllBasicValueTypes {
|
||||||
|
get;
|
||||||
|
} = new List<Type>(
|
||||||
BasicTypesInfo
|
BasicTypesInfo
|
||||||
.Where(kvp => kvp.Value.IsValueType)
|
.Where(kvp => kvp.Value.IsValueType)
|
||||||
.Select(kvp => kvp.Key).ToArray());
|
.Select(kvp => kvp.Key).ToArray());
|
||||||
@ -113,9 +117,11 @@
|
|||||||
/// <value>
|
/// <value>
|
||||||
/// All basic value and string types.
|
/// All basic value and string types.
|
||||||
/// </value>
|
/// </value>
|
||||||
public static List<Type> AllBasicValueAndStringTypes { get; } = new List<Type>(
|
public static List<Type> AllBasicValueAndStringTypes {
|
||||||
|
get;
|
||||||
|
} = new List<Type>(
|
||||||
BasicTypesInfo
|
BasicTypesInfo
|
||||||
.Where(kvp => kvp.Value.IsValueType || kvp.Key == typeof(string))
|
.Where(kvp => kvp.Value.IsValueType || kvp.Key == typeof(String))
|
||||||
.Select(kvp => kvp.Key).ToArray());
|
.Select(kvp => kvp.Key).ToArray());
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -124,7 +130,9 @@
|
|||||||
/// <value>
|
/// <value>
|
||||||
/// All basic nullable value types.
|
/// All basic nullable value types.
|
||||||
/// </value>
|
/// </value>
|
||||||
public static List<Type> AllBasicNullableValueTypes { get; } = new List<Type>(
|
public static List<Type> AllBasicNullableValueTypes {
|
||||||
|
get;
|
||||||
|
} = new List<Type>(
|
||||||
BasicTypesInfo
|
BasicTypesInfo
|
||||||
.Where(kvp => kvp.Value.IsNullableValueType)
|
.Where(kvp => kvp.Value.IsNullableValueType)
|
||||||
.Select(kvp => kvp.Key).ToArray());
|
.Select(kvp => kvp.Key).ToArray());
|
||||||
|
@ -1,12 +1,11 @@
|
|||||||
namespace Unosquare.Swan
|
using System;
|
||||||
{
|
using System.Text;
|
||||||
using System.Text;
|
|
||||||
|
|
||||||
|
namespace Unosquare.Swan {
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Contains useful constants and definitions.
|
/// Contains useful constants and definitions.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static partial class Definitions
|
public static partial class Definitions {
|
||||||
{
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The MS Windows codepage 1252 encoding used in some legacy scenarios
|
/// The MS Windows codepage 1252 encoding used in some legacy scenarios
|
||||||
/// such as default CSV text encoding from Excel.
|
/// such as default CSV text encoding from Excel.
|
||||||
@ -22,15 +21,11 @@
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes the <see cref="Definitions"/> class.
|
/// Initializes the <see cref="Definitions"/> class.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
static Definitions()
|
static Definitions() {
|
||||||
{
|
CurrentAnsiEncoding = Encoding.GetEncoding(default(Int32));
|
||||||
CurrentAnsiEncoding = Encoding.GetEncoding(default(int));
|
try {
|
||||||
try
|
|
||||||
{
|
|
||||||
Windows1252Encoding = Encoding.GetEncoding(1252);
|
Windows1252Encoding = Encoding.GetEncoding(1252);
|
||||||
}
|
} catch {
|
||||||
catch
|
|
||||||
{
|
|
||||||
// ignore, the codepage is not available use default
|
// ignore, the codepage is not available use default
|
||||||
Windows1252Encoding = CurrentAnsiEncoding;
|
Windows1252Encoding = CurrentAnsiEncoding;
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,8 @@
|
|||||||
namespace Unosquare.Swan
|
namespace Unosquare.Swan {
|
||||||
{
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Enumeration of Operating Systems.
|
/// Enumeration of Operating Systems.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public enum OperatingSystem
|
public enum OperatingSystem {
|
||||||
{
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Unknown OS
|
/// Unknown OS
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -29,8 +27,7 @@
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Enumerates the different Application Worker States.
|
/// Enumerates the different Application Worker States.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public enum AppWorkerState
|
public enum AppWorkerState {
|
||||||
{
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The stopped
|
/// The stopped
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -45,8 +42,7 @@
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Defines Endianness, big or little.
|
/// Defines Endianness, big or little.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public enum Endianness
|
public enum Endianness {
|
||||||
{
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// In big endian, you store the most significant byte in the smallest address.
|
/// In big endian, you store the most significant byte in the smallest address.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -1,15 +1,13 @@
|
|||||||
namespace Unosquare.Swan
|
using System;
|
||||||
{
|
|
||||||
using System;
|
|
||||||
|
|
||||||
|
namespace Unosquare.Swan {
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Event arguments representing the message that is logged
|
/// Event arguments representing the message that is logged
|
||||||
/// on to the terminal. Use the properties to forward the data to
|
/// on to the terminal. Use the properties to forward the data to
|
||||||
/// your logger of choice.
|
/// your logger of choice.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <seealso cref="System.EventArgs" />
|
/// <seealso cref="System.EventArgs" />
|
||||||
public class LogMessageReceivedEventArgs : EventArgs
|
public class LogMessageReceivedEventArgs : EventArgs {
|
||||||
{
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="LogMessageReceivedEventArgs" /> class.
|
/// Initializes a new instance of the <see cref="LogMessageReceivedEventArgs" /> class.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -23,25 +21,24 @@
|
|||||||
/// <param name="callerFilePath">The caller file path.</param>
|
/// <param name="callerFilePath">The caller file path.</param>
|
||||||
/// <param name="callerLineNumber">The caller line number.</param>
|
/// <param name="callerLineNumber">The caller line number.</param>
|
||||||
public LogMessageReceivedEventArgs(
|
public LogMessageReceivedEventArgs(
|
||||||
ulong sequence,
|
UInt64 sequence,
|
||||||
LogMessageType messageType,
|
LogMessageType messageType,
|
||||||
DateTime utcDate,
|
DateTime utcDate,
|
||||||
string source,
|
String source,
|
||||||
string message,
|
String message,
|
||||||
object extendedData,
|
Object extendedData,
|
||||||
string callerMemberName,
|
String callerMemberName,
|
||||||
string callerFilePath,
|
String callerFilePath,
|
||||||
int callerLineNumber)
|
Int32 callerLineNumber) {
|
||||||
{
|
this.Sequence = sequence;
|
||||||
Sequence = sequence;
|
this.MessageType = messageType;
|
||||||
MessageType = messageType;
|
this.UtcDate = utcDate;
|
||||||
UtcDate = utcDate;
|
this.Source = source;
|
||||||
Source = source;
|
this.Message = message;
|
||||||
Message = message;
|
this.CallerMemberName = callerMemberName;
|
||||||
CallerMemberName = callerMemberName;
|
this.CallerFilePath = callerFilePath;
|
||||||
CallerFilePath = callerFilePath;
|
this.CallerLineNumber = callerLineNumber;
|
||||||
CallerLineNumber = callerLineNumber;
|
this.ExtendedData = extendedData;
|
||||||
ExtendedData = extendedData;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -50,7 +47,9 @@
|
|||||||
/// <value>
|
/// <value>
|
||||||
/// The sequence.
|
/// The sequence.
|
||||||
/// </value>
|
/// </value>
|
||||||
public ulong Sequence { get; }
|
public UInt64 Sequence {
|
||||||
|
get;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the type of the message.
|
/// Gets the type of the message.
|
||||||
@ -59,7 +58,9 @@
|
|||||||
/// <value>
|
/// <value>
|
||||||
/// The type of the message.
|
/// The type of the message.
|
||||||
/// </value>
|
/// </value>
|
||||||
public LogMessageType MessageType { get; }
|
public LogMessageType MessageType {
|
||||||
|
get;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the UTC date at which the event at which the message was logged.
|
/// Gets the UTC date at which the event at which the message was logged.
|
||||||
@ -67,7 +68,9 @@
|
|||||||
/// <value>
|
/// <value>
|
||||||
/// The UTC date.
|
/// The UTC date.
|
||||||
/// </value>
|
/// </value>
|
||||||
public DateTime UtcDate { get; }
|
public DateTime UtcDate {
|
||||||
|
get;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the name of the source where the logging message
|
/// Gets the name of the source where the logging message
|
||||||
@ -76,7 +79,9 @@
|
|||||||
/// <value>
|
/// <value>
|
||||||
/// The source.
|
/// The source.
|
||||||
/// </value>
|
/// </value>
|
||||||
public string Source { get; }
|
public String Source {
|
||||||
|
get;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the body of the message.
|
/// Gets the body of the message.
|
||||||
@ -84,7 +89,9 @@
|
|||||||
/// <value>
|
/// <value>
|
||||||
/// The message.
|
/// The message.
|
||||||
/// </value>
|
/// </value>
|
||||||
public string Message { get; }
|
public String Message {
|
||||||
|
get;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the name of the caller member.
|
/// Gets the name of the caller member.
|
||||||
@ -92,7 +99,9 @@
|
|||||||
/// <value>
|
/// <value>
|
||||||
/// The name of the caller member.
|
/// The name of the caller member.
|
||||||
/// </value>
|
/// </value>
|
||||||
public string CallerMemberName { get; }
|
public String CallerMemberName {
|
||||||
|
get;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the caller file path.
|
/// Gets the caller file path.
|
||||||
@ -100,7 +109,9 @@
|
|||||||
/// <value>
|
/// <value>
|
||||||
/// The caller file path.
|
/// The caller file path.
|
||||||
/// </value>
|
/// </value>
|
||||||
public string CallerFilePath { get; }
|
public String CallerFilePath {
|
||||||
|
get;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the caller line number.
|
/// Gets the caller line number.
|
||||||
@ -108,7 +119,9 @@
|
|||||||
/// <value>
|
/// <value>
|
||||||
/// The caller line number.
|
/// The caller line number.
|
||||||
/// </value>
|
/// </value>
|
||||||
public int CallerLineNumber { get; }
|
public Int32 CallerLineNumber {
|
||||||
|
get;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets an object representing extended data.
|
/// Gets an object representing extended data.
|
||||||
@ -117,7 +130,9 @@
|
|||||||
/// <value>
|
/// <value>
|
||||||
/// The extended data.
|
/// The extended data.
|
||||||
/// </value>
|
/// </value>
|
||||||
public object ExtendedData { get; }
|
public Object ExtendedData {
|
||||||
|
get;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the Extended Data properties cast as an Exception (if possible)
|
/// Gets the Extended Data properties cast as an Exception (if possible)
|
||||||
@ -126,7 +141,7 @@
|
|||||||
/// <value>
|
/// <value>
|
||||||
/// The exception.
|
/// The exception.
|
||||||
/// </value>
|
/// </value>
|
||||||
public Exception Exception => ExtendedData as Exception;
|
public Exception Exception => this.ExtendedData as Exception;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -135,8 +150,7 @@
|
|||||||
/// event handler to prevent the terminal from displaying the message.
|
/// event handler to prevent the terminal from displaying the message.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <seealso cref="LogMessageReceivedEventArgs" />
|
/// <seealso cref="LogMessageReceivedEventArgs" />
|
||||||
public class LogMessageDisplayingEventArgs : LogMessageReceivedEventArgs
|
public class LogMessageDisplayingEventArgs : LogMessageReceivedEventArgs {
|
||||||
{
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="LogMessageDisplayingEventArgs"/> class.
|
/// Initializes a new instance of the <see cref="LogMessageDisplayingEventArgs"/> class.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -151,10 +165,7 @@
|
|||||||
data.ExtendedData,
|
data.ExtendedData,
|
||||||
data.CallerMemberName,
|
data.CallerMemberName,
|
||||||
data.CallerFilePath,
|
data.CallerFilePath,
|
||||||
data.CallerLineNumber)
|
data.CallerLineNumber) => this.CancelOutput = false;
|
||||||
{
|
|
||||||
CancelOutput = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets a value indicating whether the displaying of the
|
/// Gets or sets a value indicating whether the displaying of the
|
||||||
@ -163,6 +174,8 @@
|
|||||||
/// <value>
|
/// <value>
|
||||||
/// <c>true</c> if [cancel output]; otherwise, <c>false</c>.
|
/// <c>true</c> if [cancel output]; otherwise, <c>false</c>.
|
||||||
/// </value>
|
/// </value>
|
||||||
public bool CancelOutput { get; set; }
|
public Boolean CancelOutput {
|
||||||
|
get; set;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,19 +1,17 @@
|
|||||||
namespace Unosquare.Swan
|
using System;
|
||||||
{
|
using System.Collections.Generic;
|
||||||
using System;
|
using System.Globalization;
|
||||||
using System.Collections.Generic;
|
using System.IO;
|
||||||
using System.Globalization;
|
using System.Linq;
|
||||||
using System.IO;
|
using System.Text;
|
||||||
using System.Linq;
|
using System.Threading;
|
||||||
using System.Text;
|
using System.Threading.Tasks;
|
||||||
using System.Threading;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
|
namespace Unosquare.Swan {
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Provides various extension methods for byte arrays and streams.
|
/// Provides various extension methods for byte arrays and streams.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static class ByteArrayExtensions
|
public static class ByteArrayExtensions {
|
||||||
{
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Converts an array of bytes to its lower-case, hexadecimal representation.
|
/// Converts an array of bytes to its lower-case, hexadecimal representation.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -23,7 +21,7 @@
|
|||||||
/// The specified string instance; no actual conversion is performed.
|
/// The specified string instance; no actual conversion is performed.
|
||||||
/// </returns>
|
/// </returns>
|
||||||
/// <exception cref="ArgumentNullException">bytes.</exception>
|
/// <exception cref="ArgumentNullException">bytes.</exception>
|
||||||
public static string ToLowerHex(this byte[] bytes, bool addPrefix = false)
|
public static String ToLowerHex(this Byte[] bytes, Boolean addPrefix = false)
|
||||||
=> ToHex(bytes, addPrefix, "x2");
|
=> ToHex(bytes, addPrefix, "x2");
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -35,7 +33,7 @@
|
|||||||
/// The specified string instance; no actual conversion is performed.
|
/// The specified string instance; no actual conversion is performed.
|
||||||
/// </returns>
|
/// </returns>
|
||||||
/// <exception cref="ArgumentNullException">bytes.</exception>
|
/// <exception cref="ArgumentNullException">bytes.</exception>
|
||||||
public static string ToUpperHex(this byte[] bytes, bool addPrefix = false)
|
public static String ToUpperHex(this Byte[] bytes, Boolean addPrefix = false)
|
||||||
=> ToHex(bytes, addPrefix, "X2");
|
=> ToHex(bytes, addPrefix, "X2");
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -47,14 +45,14 @@
|
|||||||
/// A string of hexadecimal pairs separated by hyphens, where each pair represents
|
/// A string of hexadecimal pairs separated by hyphens, where each pair represents
|
||||||
/// the corresponding element in value; for example, "7F-2C-4A-00".
|
/// the corresponding element in value; for example, "7F-2C-4A-00".
|
||||||
/// </returns>
|
/// </returns>
|
||||||
public static string ToDashedHex(this byte[] bytes) => BitConverter.ToString(bytes);
|
public static String ToDashedHex(this Byte[] bytes) => BitConverter.ToString(bytes);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Converts an array of bytes to a base-64 encoded string.
|
/// Converts an array of bytes to a base-64 encoded string.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="bytes">The bytes.</param>
|
/// <param name="bytes">The bytes.</param>
|
||||||
/// <returns>A <see cref="System.String" /> converted from an array of bytes.</returns>
|
/// <returns>A <see cref="System.String" /> converted from an array of bytes.</returns>
|
||||||
public static string ToBase64(this byte[] bytes) => Convert.ToBase64String(bytes);
|
public static String ToBase64(this Byte[] bytes) => Convert.ToBase64String(bytes);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Converts a set of hexadecimal characters (uppercase or lowercase)
|
/// Converts a set of hexadecimal characters (uppercase or lowercase)
|
||||||
@ -66,10 +64,10 @@
|
|||||||
/// A byte array containing the results of encoding the specified set of characters.
|
/// A byte array containing the results of encoding the specified set of characters.
|
||||||
/// </returns>
|
/// </returns>
|
||||||
/// <exception cref="ArgumentNullException">hex.</exception>
|
/// <exception cref="ArgumentNullException">hex.</exception>
|
||||||
public static byte[] ConvertHexadecimalToBytes(this string hex)
|
public static Byte[] ConvertHexadecimalToBytes(this String hex) {
|
||||||
{
|
if(String.IsNullOrWhiteSpace(hex)) {
|
||||||
if (string.IsNullOrWhiteSpace(hex))
|
|
||||||
throw new ArgumentNullException(nameof(hex));
|
throw new ArgumentNullException(nameof(hex));
|
||||||
|
}
|
||||||
|
|
||||||
return Enumerable
|
return Enumerable
|
||||||
.Range(0, hex.Length / 2)
|
.Range(0, hex.Length / 2)
|
||||||
@ -86,7 +84,7 @@
|
|||||||
/// <returns>
|
/// <returns>
|
||||||
/// Bit value at the given offset.
|
/// Bit value at the given offset.
|
||||||
/// </returns>
|
/// </returns>
|
||||||
public static byte GetBitValueAt(this byte b, byte offset, byte length = 1) => (byte)((b >> offset) & ~(0xff << length));
|
public static Byte GetBitValueAt(this Byte b, Byte offset, Byte length = 1) => (Byte)((b >> offset) & ~(0xff << length));
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Sets the bit value at the given offset.
|
/// Sets the bit value at the given offset.
|
||||||
@ -96,12 +94,11 @@
|
|||||||
/// <param name="length">The length.</param>
|
/// <param name="length">The length.</param>
|
||||||
/// <param name="value">The value.</param>
|
/// <param name="value">The value.</param>
|
||||||
/// <returns>Bit value at the given offset.</returns>
|
/// <returns>Bit value at the given offset.</returns>
|
||||||
public static byte SetBitValueAt(this byte b, byte offset, byte length, byte value)
|
public static Byte SetBitValueAt(this Byte b, Byte offset, Byte length, Byte value) {
|
||||||
{
|
Int32 mask = ~(0xff << length);
|
||||||
var mask = ~(0xff << length);
|
Byte valueAt = (Byte)(value & mask);
|
||||||
var valueAt = (byte)(value & mask);
|
|
||||||
|
|
||||||
return (byte)((valueAt << offset) | (b & ~(mask << offset)));
|
return (Byte)((valueAt << offset) | (b & ~(mask << offset)));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -111,7 +108,7 @@
|
|||||||
/// <param name="offset">The offset.</param>
|
/// <param name="offset">The offset.</param>
|
||||||
/// <param name="value">The value.</param>
|
/// <param name="value">The value.</param>
|
||||||
/// <returns>Bit value at the given offset.</returns>
|
/// <returns>Bit value at the given offset.</returns>
|
||||||
public static byte SetBitValueAt(this byte b, byte offset, byte value) => b.SetBitValueAt(offset, 1, value);
|
public static Byte SetBitValueAt(this Byte b, Byte offset, Byte value) => b.SetBitValueAt(offset, 1, value);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Splits a byte array delimited by the specified sequence of bytes.
|
/// Splits a byte array delimited by the specified sequence of bytes.
|
||||||
@ -130,32 +127,29 @@
|
|||||||
/// or
|
/// or
|
||||||
/// sequence.
|
/// sequence.
|
||||||
/// </exception>
|
/// </exception>
|
||||||
public static List<byte[]> Split(this byte[] buffer, int offset, params byte[] sequence)
|
public static List<Byte[]> Split(this Byte[] buffer, Int32 offset, params Byte[] sequence) {
|
||||||
{
|
if(buffer == null) {
|
||||||
if (buffer == null)
|
|
||||||
throw new ArgumentNullException(nameof(buffer));
|
throw new ArgumentNullException(nameof(buffer));
|
||||||
|
}
|
||||||
|
|
||||||
if (sequence == null)
|
if(sequence == null) {
|
||||||
throw new ArgumentNullException(nameof(sequence));
|
throw new ArgumentNullException(nameof(sequence));
|
||||||
|
}
|
||||||
|
|
||||||
var seqOffset = offset.Clamp(0, buffer.Length - 1);
|
Int32 seqOffset = offset.Clamp(0, buffer.Length - 1);
|
||||||
|
|
||||||
var result = new List<byte[]>();
|
List<Byte[]> result = new List<Byte[]>();
|
||||||
|
|
||||||
while (seqOffset < buffer.Length)
|
while(seqOffset < buffer.Length) {
|
||||||
{
|
Int32 separatorStartIndex = buffer.GetIndexOf(sequence, seqOffset);
|
||||||
var separatorStartIndex = buffer.GetIndexOf(sequence, seqOffset);
|
|
||||||
|
|
||||||
if (separatorStartIndex >= 0)
|
if(separatorStartIndex >= 0) {
|
||||||
{
|
Byte[] item = new Byte[separatorStartIndex - seqOffset + sequence.Length];
|
||||||
var item = new byte[separatorStartIndex - seqOffset + sequence.Length];
|
|
||||||
Array.Copy(buffer, seqOffset, item, 0, item.Length);
|
Array.Copy(buffer, seqOffset, item, 0, item.Length);
|
||||||
result.Add(item);
|
result.Add(item);
|
||||||
seqOffset += item.Length;
|
seqOffset += item.Length;
|
||||||
}
|
} else {
|
||||||
else
|
Byte[] item = new Byte[buffer.Length - seqOffset];
|
||||||
{
|
|
||||||
var item = new byte[buffer.Length - seqOffset];
|
|
||||||
Array.Copy(buffer, seqOffset, item, 0, item.Length);
|
Array.Copy(buffer, seqOffset, item, 0, item.Length);
|
||||||
result.Add(item);
|
result.Add(item);
|
||||||
break;
|
break;
|
||||||
@ -170,12 +164,12 @@
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="buffer">The buffer.</param>
|
/// <param name="buffer">The buffer.</param>
|
||||||
/// <returns>A byte array containing the results of encoding the specified set of characters.</returns>
|
/// <returns>A byte array containing the results of encoding the specified set of characters.</returns>
|
||||||
public static byte[] DeepClone(this byte[] buffer)
|
public static Byte[] DeepClone(this Byte[] buffer) {
|
||||||
{
|
if(buffer == null) {
|
||||||
if (buffer == null)
|
|
||||||
return null;
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
var result = new byte[buffer.Length];
|
Byte[] result = new Byte[buffer.Length];
|
||||||
Array.Copy(buffer, result, buffer.Length);
|
Array.Copy(buffer, result, buffer.Length);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@ -189,15 +183,16 @@
|
|||||||
/// A new trimmed byte array.
|
/// A new trimmed byte array.
|
||||||
/// </returns>
|
/// </returns>
|
||||||
/// <exception cref="ArgumentNullException">buffer.</exception>
|
/// <exception cref="ArgumentNullException">buffer.</exception>
|
||||||
public static byte[] TrimStart(this byte[] buffer, params byte[] sequence)
|
public static Byte[] TrimStart(this Byte[] buffer, params Byte[] sequence) {
|
||||||
{
|
if(buffer == null) {
|
||||||
if (buffer == null)
|
|
||||||
throw new ArgumentNullException(nameof(buffer));
|
throw new ArgumentNullException(nameof(buffer));
|
||||||
|
}
|
||||||
|
|
||||||
if (buffer.StartsWith(sequence) == false)
|
if(buffer.StartsWith(sequence) == false) {
|
||||||
return buffer.DeepClone();
|
return buffer.DeepClone();
|
||||||
|
}
|
||||||
|
|
||||||
var result = new byte[buffer.Length - sequence.Length];
|
Byte[] result = new Byte[buffer.Length - sequence.Length];
|
||||||
Array.Copy(buffer, sequence.Length, result, 0, result.Length);
|
Array.Copy(buffer, sequence.Length, result, 0, result.Length);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@ -211,15 +206,16 @@
|
|||||||
/// A byte array containing the results of encoding the specified set of characters.
|
/// A byte array containing the results of encoding the specified set of characters.
|
||||||
/// </returns>
|
/// </returns>
|
||||||
/// <exception cref="ArgumentNullException">buffer.</exception>
|
/// <exception cref="ArgumentNullException">buffer.</exception>
|
||||||
public static byte[] TrimEnd(this byte[] buffer, params byte[] sequence)
|
public static Byte[] TrimEnd(this Byte[] buffer, params Byte[] sequence) {
|
||||||
{
|
if(buffer == null) {
|
||||||
if (buffer == null)
|
|
||||||
throw new ArgumentNullException(nameof(buffer));
|
throw new ArgumentNullException(nameof(buffer));
|
||||||
|
}
|
||||||
|
|
||||||
if (buffer.EndsWith(sequence) == false)
|
if(buffer.EndsWith(sequence) == false) {
|
||||||
return buffer.DeepClone();
|
return buffer.DeepClone();
|
||||||
|
}
|
||||||
|
|
||||||
var result = new byte[buffer.Length - sequence.Length];
|
Byte[] result = new Byte[buffer.Length - sequence.Length];
|
||||||
Array.Copy(buffer, 0, result, 0, result.Length);
|
Array.Copy(buffer, 0, result, 0, result.Length);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@ -231,9 +227,8 @@
|
|||||||
/// <param name="buffer">The buffer.</param>
|
/// <param name="buffer">The buffer.</param>
|
||||||
/// <param name="sequence">The sequence.</param>
|
/// <param name="sequence">The sequence.</param>
|
||||||
/// <returns>A byte array containing the results of encoding the specified set of characters.</returns>
|
/// <returns>A byte array containing the results of encoding the specified set of characters.</returns>
|
||||||
public static byte[] Trim(this byte[] buffer, params byte[] sequence)
|
public static Byte[] Trim(this Byte[] buffer, params Byte[] sequence) {
|
||||||
{
|
Byte[] trimStart = buffer.TrimStart(sequence);
|
||||||
var trimStart = buffer.TrimStart(sequence);
|
|
||||||
return trimStart.TrimEnd(sequence);
|
return trimStart.TrimEnd(sequence);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -246,12 +241,12 @@
|
|||||||
/// True if the specified buffer is ends; otherwise, false.
|
/// True if the specified buffer is ends; otherwise, false.
|
||||||
/// </returns>
|
/// </returns>
|
||||||
/// <exception cref="ArgumentNullException">buffer.</exception>
|
/// <exception cref="ArgumentNullException">buffer.</exception>
|
||||||
public static bool EndsWith(this byte[] buffer, params byte[] sequence)
|
public static Boolean EndsWith(this Byte[] buffer, params Byte[] sequence) {
|
||||||
{
|
if(buffer == null) {
|
||||||
if (buffer == null)
|
|
||||||
throw new ArgumentNullException(nameof(buffer));
|
throw new ArgumentNullException(nameof(buffer));
|
||||||
|
}
|
||||||
|
|
||||||
var startIndex = buffer.Length - sequence.Length;
|
Int32 startIndex = buffer.Length - sequence.Length;
|
||||||
return buffer.GetIndexOf(sequence, startIndex) == startIndex;
|
return buffer.GetIndexOf(sequence, startIndex) == startIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -261,7 +256,7 @@
|
|||||||
/// <param name="buffer">The buffer.</param>
|
/// <param name="buffer">The buffer.</param>
|
||||||
/// <param name="sequence">The sequence.</param>
|
/// <param name="sequence">The sequence.</param>
|
||||||
/// <returns><c>true</c> if the specified buffer starts; otherwise, <c>false</c>.</returns>
|
/// <returns><c>true</c> if the specified buffer starts; otherwise, <c>false</c>.</returns>
|
||||||
public static bool StartsWith(this byte[] buffer, params byte[] sequence) => buffer.GetIndexOf(sequence) == 0;
|
public static Boolean StartsWith(this Byte[] buffer, params Byte[] sequence) => buffer.GetIndexOf(sequence) == 0;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Determines whether the buffer contains the specified sequence.
|
/// Determines whether the buffer contains the specified sequence.
|
||||||
@ -271,7 +266,7 @@
|
|||||||
/// <returns>
|
/// <returns>
|
||||||
/// <c>true</c> if [contains] [the specified sequence]; otherwise, <c>false</c>.
|
/// <c>true</c> if [contains] [the specified sequence]; otherwise, <c>false</c>.
|
||||||
/// </returns>
|
/// </returns>
|
||||||
public static bool Contains(this byte[] buffer, params byte[] sequence) => buffer.GetIndexOf(sequence) >= 0;
|
public static Boolean Contains(this Byte[] buffer, params Byte[] sequence) => buffer.GetIndexOf(sequence) >= 0;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Determines whether the buffer exactly matches, byte by byte the specified sequence.
|
/// Determines whether the buffer exactly matches, byte by byte the specified sequence.
|
||||||
@ -282,13 +277,14 @@
|
|||||||
/// <c>true</c> if [is equal to] [the specified sequence]; otherwise, <c>false</c>.
|
/// <c>true</c> if [is equal to] [the specified sequence]; otherwise, <c>false</c>.
|
||||||
/// </returns>
|
/// </returns>
|
||||||
/// <exception cref="ArgumentNullException">buffer.</exception>
|
/// <exception cref="ArgumentNullException">buffer.</exception>
|
||||||
public static bool IsEqualTo(this byte[] buffer, params byte[] sequence)
|
public static Boolean IsEqualTo(this Byte[] buffer, params Byte[] sequence) {
|
||||||
{
|
if(ReferenceEquals(buffer, sequence)) {
|
||||||
if (ReferenceEquals(buffer, sequence))
|
|
||||||
return true;
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
if (buffer == null)
|
if(buffer == null) {
|
||||||
throw new ArgumentNullException(nameof(buffer));
|
throw new ArgumentNullException(nameof(buffer));
|
||||||
|
}
|
||||||
|
|
||||||
return buffer.Length == sequence.Length && buffer.GetIndexOf(sequence) == 0;
|
return buffer.Length == sequence.Length && buffer.GetIndexOf(sequence) == 0;
|
||||||
}
|
}
|
||||||
@ -306,30 +302,37 @@
|
|||||||
/// or
|
/// or
|
||||||
/// sequence.
|
/// sequence.
|
||||||
/// </exception>
|
/// </exception>
|
||||||
public static int GetIndexOf(this byte[] buffer, byte[] sequence, int offset = 0)
|
public static Int32 GetIndexOf(this Byte[] buffer, Byte[] sequence, Int32 offset = 0) {
|
||||||
{
|
if(buffer == null) {
|
||||||
if (buffer == null)
|
|
||||||
throw new ArgumentNullException(nameof(buffer));
|
throw new ArgumentNullException(nameof(buffer));
|
||||||
if (sequence == null)
|
}
|
||||||
|
|
||||||
|
if(sequence == null) {
|
||||||
throw new ArgumentNullException(nameof(sequence));
|
throw new ArgumentNullException(nameof(sequence));
|
||||||
if (sequence.Length == 0)
|
}
|
||||||
return -1;
|
|
||||||
if (sequence.Length > buffer.Length)
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
var seqOffset = offset < 0 ? 0 : offset;
|
if(sequence.Length == 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
var matchedCount = 0;
|
if(sequence.Length > buffer.Length) {
|
||||||
for (var i = seqOffset; i < buffer.Length; i++)
|
return -1;
|
||||||
{
|
}
|
||||||
if (buffer[i] == sequence[matchedCount])
|
|
||||||
|
Int32 seqOffset = offset < 0 ? 0 : offset;
|
||||||
|
|
||||||
|
Int32 matchedCount = 0;
|
||||||
|
for(Int32 i = seqOffset; i < buffer.Length; i++) {
|
||||||
|
if(buffer[i] == sequence[matchedCount]) {
|
||||||
matchedCount++;
|
matchedCount++;
|
||||||
else
|
} else {
|
||||||
matchedCount = 0;
|
matchedCount = 0;
|
||||||
|
}
|
||||||
|
|
||||||
if (matchedCount == sequence.Length)
|
if(matchedCount == sequence.Length) {
|
||||||
return i - (matchedCount - 1);
|
return i - (matchedCount - 1);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
@ -347,13 +350,14 @@
|
|||||||
/// or
|
/// or
|
||||||
/// buffer.
|
/// buffer.
|
||||||
/// </exception>
|
/// </exception>
|
||||||
public static MemoryStream Append(this MemoryStream stream, byte[] buffer)
|
public static MemoryStream Append(this MemoryStream stream, Byte[] buffer) {
|
||||||
{
|
if(stream == null) {
|
||||||
if (stream == null)
|
|
||||||
throw new ArgumentNullException(nameof(stream));
|
throw new ArgumentNullException(nameof(stream));
|
||||||
|
}
|
||||||
|
|
||||||
if (buffer == null)
|
if(buffer == null) {
|
||||||
throw new ArgumentNullException(nameof(buffer));
|
throw new ArgumentNullException(nameof(buffer));
|
||||||
|
}
|
||||||
|
|
||||||
stream.Write(buffer, 0, buffer.Length);
|
stream.Write(buffer, 0, buffer.Length);
|
||||||
return stream;
|
return stream;
|
||||||
@ -368,7 +372,7 @@
|
|||||||
/// Block of bytes to the current stream using data read from a buffer.
|
/// Block of bytes to the current stream using data read from a buffer.
|
||||||
/// </returns>
|
/// </returns>
|
||||||
/// <exception cref="ArgumentNullException">buffer.</exception>
|
/// <exception cref="ArgumentNullException">buffer.</exception>
|
||||||
public static MemoryStream Append(this MemoryStream stream, IEnumerable<byte> buffer) => Append(stream, buffer?.ToArray());
|
public static MemoryStream Append(this MemoryStream stream, IEnumerable<Byte> buffer) => Append(stream, buffer?.ToArray());
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Appends the Memory Stream with the specified set of buffers.
|
/// Appends the Memory Stream with the specified set of buffers.
|
||||||
@ -379,13 +383,14 @@
|
|||||||
/// Block of bytes to the current stream using data read from a buffer.
|
/// Block of bytes to the current stream using data read from a buffer.
|
||||||
/// </returns>
|
/// </returns>
|
||||||
/// <exception cref="ArgumentNullException">buffers.</exception>
|
/// <exception cref="ArgumentNullException">buffers.</exception>
|
||||||
public static MemoryStream Append(this MemoryStream stream, IEnumerable<byte[]> buffers)
|
public static MemoryStream Append(this MemoryStream stream, IEnumerable<Byte[]> buffers) {
|
||||||
{
|
if(buffers == null) {
|
||||||
if (buffers == null)
|
|
||||||
throw new ArgumentNullException(nameof(buffers));
|
throw new ArgumentNullException(nameof(buffers));
|
||||||
|
}
|
||||||
|
|
||||||
foreach (var buffer in buffers)
|
foreach(Byte[] buffer in buffers) {
|
||||||
Append(stream, buffer);
|
_ = Append(stream, buffer);
|
||||||
|
}
|
||||||
|
|
||||||
return stream;
|
return stream;
|
||||||
}
|
}
|
||||||
@ -396,14 +401,14 @@
|
|||||||
/// <param name="buffer">The buffer.</param>
|
/// <param name="buffer">The buffer.</param>
|
||||||
/// <param name="encoding">The encoding.</param>
|
/// <param name="encoding">The encoding.</param>
|
||||||
/// <returns>A <see cref="System.String" /> that contains the results of decoding the specified sequence of bytes.</returns>
|
/// <returns>A <see cref="System.String" /> that contains the results of decoding the specified sequence of bytes.</returns>
|
||||||
public static string ToText(this IEnumerable<byte> buffer, Encoding encoding) => encoding.GetString(buffer.ToArray());
|
public static String ToText(this IEnumerable<Byte> buffer, Encoding encoding) => encoding.GetString(buffer.ToArray());
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Converts an array of bytes into text with UTF8 encoding.
|
/// Converts an array of bytes into text with UTF8 encoding.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="buffer">The buffer.</param>
|
/// <param name="buffer">The buffer.</param>
|
||||||
/// <returns>A <see cref="System.String" /> that contains the results of decoding the specified sequence of bytes.</returns>
|
/// <returns>A <see cref="System.String" /> that contains the results of decoding the specified sequence of bytes.</returns>
|
||||||
public static string ToText(this IEnumerable<byte> buffer) => buffer.ToText(Encoding.UTF8);
|
public static String ToText(this IEnumerable<Byte> buffer) => buffer.ToText(Encoding.UTF8);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Retrieves a sub-array from the specified <paramref name="array"/>. A sub-array starts at
|
/// Retrieves a sub-array from the specified <paramref name="array"/>. A sub-array starts at
|
||||||
@ -417,28 +422,30 @@
|
|||||||
/// An array of T from which to retrieve a sub-array.
|
/// An array of T from which to retrieve a sub-array.
|
||||||
/// </param>
|
/// </param>
|
||||||
/// <param name="startIndex">
|
/// <param name="startIndex">
|
||||||
/// An <see cref="int"/> that represents the zero-based starting position of
|
/// An <see cref="Int32"/> that represents the zero-based starting position of
|
||||||
/// a sub-array in <paramref name="array"/>.
|
/// a sub-array in <paramref name="array"/>.
|
||||||
/// </param>
|
/// </param>
|
||||||
/// <param name="length">
|
/// <param name="length">
|
||||||
/// An <see cref="int"/> that represents the number of elements to retrieve.
|
/// An <see cref="Int32"/> that represents the number of elements to retrieve.
|
||||||
/// </param>
|
/// </param>
|
||||||
/// <typeparam name="T">
|
/// <typeparam name="T">
|
||||||
/// The type of elements in <paramref name="array"/>.
|
/// The type of elements in <paramref name="array"/>.
|
||||||
/// </typeparam>
|
/// </typeparam>
|
||||||
public static T[] SubArray<T>(this T[] array, int startIndex, int length)
|
public static T[] SubArray<T>(this T[] array, Int32 startIndex, Int32 length) {
|
||||||
{
|
Int32 len;
|
||||||
int len;
|
if(array == null || (len = array.Length) == 0) {
|
||||||
if (array == null || (len = array.Length) == 0)
|
|
||||||
return new T[0];
|
return new T[0];
|
||||||
|
}
|
||||||
|
|
||||||
if (startIndex < 0 || length <= 0 || startIndex + length > len)
|
if(startIndex < 0 || length <= 0 || startIndex + length > len) {
|
||||||
return new T[0];
|
return new T[0];
|
||||||
|
}
|
||||||
|
|
||||||
if (startIndex == 0 && length == len)
|
if(startIndex == 0 && length == len) {
|
||||||
return array;
|
return array;
|
||||||
|
}
|
||||||
|
|
||||||
var subArray = new T[length];
|
T[] subArray = new T[length];
|
||||||
Array.Copy(array, startIndex, subArray, 0, length);
|
Array.Copy(array, startIndex, subArray, 0, length);
|
||||||
|
|
||||||
return subArray;
|
return subArray;
|
||||||
@ -456,16 +463,16 @@
|
|||||||
/// An array of T from which to retrieve a sub-array.
|
/// An array of T from which to retrieve a sub-array.
|
||||||
/// </param>
|
/// </param>
|
||||||
/// <param name="startIndex">
|
/// <param name="startIndex">
|
||||||
/// A <see cref="long"/> that represents the zero-based starting position of
|
/// A <see cref="Int64"/> that represents the zero-based starting position of
|
||||||
/// a sub-array in <paramref name="array"/>.
|
/// a sub-array in <paramref name="array"/>.
|
||||||
/// </param>
|
/// </param>
|
||||||
/// <param name="length">
|
/// <param name="length">
|
||||||
/// A <see cref="long"/> that represents the number of elements to retrieve.
|
/// A <see cref="Int64"/> that represents the number of elements to retrieve.
|
||||||
/// </param>
|
/// </param>
|
||||||
/// <typeparam name="T">
|
/// <typeparam name="T">
|
||||||
/// The type of elements in <paramref name="array"/>.
|
/// The type of elements in <paramref name="array"/>.
|
||||||
/// </typeparam>
|
/// </typeparam>
|
||||||
public static T[] SubArray<T>(this T[] array, long startIndex, long length) => array.SubArray((int)startIndex, (int)length);
|
public static T[] SubArray<T>(this T[] array, Int64 startIndex, Int64 length) => array.SubArray((Int32)startIndex, (Int32)length);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Reads the bytes asynchronous.
|
/// Reads the bytes asynchronous.
|
||||||
@ -478,31 +485,28 @@
|
|||||||
/// A byte array containing the results of encoding the specified set of characters.
|
/// A byte array containing the results of encoding the specified set of characters.
|
||||||
/// </returns>
|
/// </returns>
|
||||||
/// <exception cref="ArgumentNullException">stream.</exception>
|
/// <exception cref="ArgumentNullException">stream.</exception>
|
||||||
public static async Task<byte[]> ReadBytesAsync(this Stream stream, long length, int bufferLength, CancellationToken ct = default)
|
public static async Task<Byte[]> ReadBytesAsync(this Stream stream, Int64 length, Int32 bufferLength, CancellationToken ct = default) {
|
||||||
{
|
if(stream == null) {
|
||||||
if (stream == null)
|
|
||||||
throw new ArgumentNullException(nameof(stream));
|
throw new ArgumentNullException(nameof(stream));
|
||||||
|
}
|
||||||
|
|
||||||
using (var dest = new MemoryStream())
|
using(MemoryStream dest = new MemoryStream()) {
|
||||||
{
|
try {
|
||||||
try
|
Byte[] buff = new Byte[bufferLength];
|
||||||
{
|
while(length > 0) {
|
||||||
var buff = new byte[bufferLength];
|
if(length < bufferLength) {
|
||||||
while (length > 0)
|
bufferLength = (Int32)length;
|
||||||
{
|
}
|
||||||
if (length < bufferLength)
|
|
||||||
bufferLength = (int)length;
|
|
||||||
|
|
||||||
var nread = await stream.ReadAsync(buff, 0, bufferLength, ct).ConfigureAwait(false);
|
Int32 nread = await stream.ReadAsync(buff, 0, bufferLength, ct).ConfigureAwait(false);
|
||||||
if (nread == 0)
|
if(nread == 0) {
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
dest.Write(buff, 0, nread);
|
dest.Write(buff, 0, nread);
|
||||||
length -= nread;
|
length -= nread;
|
||||||
}
|
}
|
||||||
}
|
} catch {
|
||||||
catch
|
|
||||||
{
|
|
||||||
// ignored
|
// ignored
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -520,27 +524,24 @@
|
|||||||
/// A byte array containing the results of encoding the specified set of characters.
|
/// A byte array containing the results of encoding the specified set of characters.
|
||||||
/// </returns>
|
/// </returns>
|
||||||
/// <exception cref="ArgumentNullException">stream.</exception>
|
/// <exception cref="ArgumentNullException">stream.</exception>
|
||||||
public static async Task<byte[]> ReadBytesAsync(this Stream stream, int length, CancellationToken ct = default)
|
public static async Task<Byte[]> ReadBytesAsync(this Stream stream, Int32 length, CancellationToken ct = default) {
|
||||||
{
|
if(stream == null) {
|
||||||
if (stream == null)
|
|
||||||
throw new ArgumentNullException(nameof(stream));
|
throw new ArgumentNullException(nameof(stream));
|
||||||
|
}
|
||||||
|
|
||||||
var buff = new byte[length];
|
Byte[] buff = new Byte[length];
|
||||||
var offset = 0;
|
Int32 offset = 0;
|
||||||
try
|
try {
|
||||||
{
|
while(length > 0) {
|
||||||
while (length > 0)
|
Int32 nread = await stream.ReadAsync(buff, offset, length, ct).ConfigureAwait(false);
|
||||||
{
|
if(nread == 0) {
|
||||||
var nread = await stream.ReadAsync(buff, offset, length, ct).ConfigureAwait(false);
|
|
||||||
if (nread == 0)
|
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
offset += nread;
|
offset += nread;
|
||||||
length -= nread;
|
length -= nread;
|
||||||
}
|
}
|
||||||
}
|
} catch {
|
||||||
catch
|
|
||||||
{
|
|
||||||
// ignored
|
// ignored
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -555,14 +556,15 @@
|
|||||||
/// The byte array from conversion.
|
/// The byte array from conversion.
|
||||||
/// </returns>
|
/// </returns>
|
||||||
/// <exception cref="ArgumentNullException">sbyteArray.</exception>
|
/// <exception cref="ArgumentNullException">sbyteArray.</exception>
|
||||||
public static byte[] ToByteArray(this sbyte[] sbyteArray)
|
public static Byte[] ToByteArray(this SByte[] sbyteArray) {
|
||||||
{
|
if(sbyteArray == null) {
|
||||||
if (sbyteArray == null)
|
|
||||||
throw new ArgumentNullException(nameof(sbyteArray));
|
throw new ArgumentNullException(nameof(sbyteArray));
|
||||||
|
}
|
||||||
|
|
||||||
var byteArray = new byte[sbyteArray.Length];
|
Byte[] byteArray = new Byte[sbyteArray.Length];
|
||||||
for (var index = 0; index < sbyteArray.Length; index++)
|
for(Int32 index = 0; index < sbyteArray.Length; index++) {
|
||||||
byteArray[index] = (byte)sbyteArray[index];
|
byteArray[index] = (Byte)sbyteArray[index];
|
||||||
|
}
|
||||||
|
|
||||||
return byteArray;
|
return byteArray;
|
||||||
}
|
}
|
||||||
@ -575,14 +577,16 @@
|
|||||||
/// The sbyte array from conversion.
|
/// The sbyte array from conversion.
|
||||||
/// </returns>
|
/// </returns>
|
||||||
/// <exception cref="ArgumentNullException">byteArray.</exception>
|
/// <exception cref="ArgumentNullException">byteArray.</exception>
|
||||||
public static sbyte[] ToSByteArray(this byte[] byteArray)
|
public static SByte[] ToSByteArray(this Byte[] byteArray) {
|
||||||
{
|
if(byteArray == null) {
|
||||||
if (byteArray == null)
|
|
||||||
throw new ArgumentNullException(nameof(byteArray));
|
throw new ArgumentNullException(nameof(byteArray));
|
||||||
|
}
|
||||||
|
|
||||||
|
SByte[] sbyteArray = new SByte[byteArray.Length];
|
||||||
|
for(Int32 index = 0; index < byteArray.Length; index++) {
|
||||||
|
sbyteArray[index] = (SByte)byteArray[index];
|
||||||
|
}
|
||||||
|
|
||||||
var sbyteArray = new sbyte[byteArray.Length];
|
|
||||||
for (var index = 0; index < byteArray.Length; index++)
|
|
||||||
sbyteArray[index] = (sbyte)byteArray[index];
|
|
||||||
return sbyteArray;
|
return sbyteArray;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -592,7 +596,7 @@
|
|||||||
/// <param name="encoding">The encoding.</param>
|
/// <param name="encoding">The encoding.</param>
|
||||||
/// <param name="s">The s.</param>
|
/// <param name="s">The s.</param>
|
||||||
/// <returns>The sbyte array from string.</returns>
|
/// <returns>The sbyte array from string.</returns>
|
||||||
public static sbyte[] GetSBytes(this Encoding encoding, string s)
|
public static SByte[] GetSBytes(this Encoding encoding, String s)
|
||||||
=> encoding.GetBytes(s).ToSByteArray();
|
=> encoding.GetBytes(s).ToSByteArray();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -601,7 +605,7 @@
|
|||||||
/// <param name="encoding">The encoding.</param>
|
/// <param name="encoding">The encoding.</param>
|
||||||
/// <param name="data">The data.</param>
|
/// <param name="data">The data.</param>
|
||||||
/// <returns>The string.</returns>
|
/// <returns>The string.</returns>
|
||||||
public static string GetString(this Encoding encoding, sbyte[] data)
|
public static String GetString(this Encoding encoding, SByte[] data)
|
||||||
=> encoding.GetString(data.ToByteArray());
|
=> encoding.GetString(data.ToByteArray());
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -620,53 +624,59 @@
|
|||||||
/// or
|
/// or
|
||||||
/// target.
|
/// target.
|
||||||
/// </exception>
|
/// </exception>
|
||||||
public static int ReadInput(this Stream sourceStream, ref sbyte[] target, int start, int count)
|
public static Int32 ReadInput(this Stream sourceStream, ref SByte[] target, Int32 start, Int32 count) {
|
||||||
{
|
if(sourceStream == null) {
|
||||||
if (sourceStream == null)
|
|
||||||
throw new ArgumentNullException(nameof(sourceStream));
|
throw new ArgumentNullException(nameof(sourceStream));
|
||||||
|
}
|
||||||
|
|
||||||
if (target == null)
|
if(target == null) {
|
||||||
throw new ArgumentNullException(nameof(target));
|
throw new ArgumentNullException(nameof(target));
|
||||||
|
}
|
||||||
|
|
||||||
// Returns 0 bytes if not enough space in target
|
// Returns 0 bytes if not enough space in target
|
||||||
if (target.Length == 0)
|
if(target.Length == 0) {
|
||||||
return 0;
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
var receiver = new byte[target.Length];
|
Byte[] receiver = new Byte[target.Length];
|
||||||
var bytesRead = 0;
|
Int32 bytesRead = 0;
|
||||||
var startIndex = start;
|
Int32 startIndex = start;
|
||||||
var bytesToRead = count;
|
Int32 bytesToRead = count;
|
||||||
while (bytesToRead > 0)
|
while(bytesToRead > 0) {
|
||||||
{
|
Int32 n = sourceStream.Read(receiver, startIndex, bytesToRead);
|
||||||
var n = sourceStream.Read(receiver, startIndex, bytesToRead);
|
if(n == 0) {
|
||||||
if (n == 0)
|
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
bytesRead += n;
|
bytesRead += n;
|
||||||
startIndex += n;
|
startIndex += n;
|
||||||
bytesToRead -= n;
|
bytesToRead -= n;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns -1 if EOF
|
// Returns -1 if EOF
|
||||||
if (bytesRead == 0)
|
if(bytesRead == 0) {
|
||||||
return -1;
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
for (var i = start; i < start + bytesRead; i++)
|
for(Int32 i = start; i < start + bytesRead; i++) {
|
||||||
target[i] = (sbyte)receiver[i];
|
target[i] = (SByte)receiver[i];
|
||||||
|
}
|
||||||
|
|
||||||
return bytesRead;
|
return bytesRead;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static string ToHex(byte[] bytes, bool addPrefix, string format)
|
private static String ToHex(Byte[] bytes, Boolean addPrefix, String format) {
|
||||||
{
|
if(bytes == null) {
|
||||||
if (bytes == null)
|
|
||||||
throw new ArgumentNullException(nameof(bytes));
|
throw new ArgumentNullException(nameof(bytes));
|
||||||
|
}
|
||||||
|
|
||||||
var sb = new StringBuilder(bytes.Length * 2);
|
StringBuilder sb = new StringBuilder(bytes.Length * 2);
|
||||||
|
|
||||||
foreach (var item in bytes)
|
foreach(Byte item in bytes) {
|
||||||
sb.Append(item.ToString(format, CultureInfo.InvariantCulture));
|
_ = sb.Append(item.ToString(format, CultureInfo.InvariantCulture));
|
||||||
|
}
|
||||||
|
|
||||||
return $"{(addPrefix ? "0x" : string.Empty)}{sb}";
|
return $"{(addPrefix ? "0x" : String.Empty)}{sb}";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,15 +1,13 @@
|
|||||||
namespace Unosquare.Swan
|
using System;
|
||||||
{
|
using System.Collections.Generic;
|
||||||
using System;
|
using System.Linq;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
|
|
||||||
|
namespace Unosquare.Swan {
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Provides various extension methods for dates.
|
/// Provides various extension methods for dates.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static class DateExtensions
|
public static class DateExtensions {
|
||||||
{
|
private static readonly Dictionary<String, Int32> DateRanges = new Dictionary<String, Int32>()
|
||||||
private static readonly Dictionary<string, int> DateRanges = new Dictionary<string, int>()
|
|
||||||
{
|
{
|
||||||
{ "minute", 59},
|
{ "minute", 59},
|
||||||
{ "hour", 23},
|
{ "hour", 23},
|
||||||
@ -23,7 +21,7 @@
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="date">The date.</param>
|
/// <param name="date">The date.</param>
|
||||||
/// <returns>The concatenation of date.Year, date.Month and date.Day.</returns>
|
/// <returns>The concatenation of date.Year, date.Month and date.Day.</returns>
|
||||||
public static string ToSortableDate(this DateTime date)
|
public static String ToSortableDate(this DateTime date)
|
||||||
=> $"{date.Year:0000}-{date.Month:00}-{date.Day:00}";
|
=> $"{date.Year:0000}-{date.Month:00}-{date.Day:00}";
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -31,7 +29,7 @@
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="date">The date.</param>
|
/// <param name="date">The date.</param>
|
||||||
/// <returns>The concatenation of date.Year, date.Month, date.Day, date.Hour, date.Minute and date.Second.</returns>
|
/// <returns>The concatenation of date.Year, date.Month, date.Day, date.Hour, date.Minute and date.Second.</returns>
|
||||||
public static string ToSortableDateTime(this DateTime date)
|
public static String ToSortableDateTime(this DateTime date)
|
||||||
=> $"{date.Year:0000}-{date.Month:00}-{date.Day:00} {date.Hour:00}:{date.Minute:00}:{date.Second:00}";
|
=> $"{date.Year:0000}-{date.Month:00}-{date.Day:00} {date.Hour:00}:{date.Minute:00}:{date.Second:00}";
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -49,43 +47,44 @@
|
|||||||
/// <exception cref="ArgumentException">
|
/// <exception cref="ArgumentException">
|
||||||
/// Unable to parse sortable date and time. - sortableDate.
|
/// Unable to parse sortable date and time. - sortableDate.
|
||||||
/// </exception>
|
/// </exception>
|
||||||
public static DateTime ToDateTime(this string sortableDate)
|
public static DateTime ToDateTime(this String sortableDate) {
|
||||||
{
|
if(String.IsNullOrWhiteSpace(sortableDate)) {
|
||||||
if (string.IsNullOrWhiteSpace(sortableDate))
|
|
||||||
throw new ArgumentNullException(nameof(sortableDate));
|
throw new ArgumentNullException(nameof(sortableDate));
|
||||||
|
}
|
||||||
|
|
||||||
var hour = 0;
|
Int32 hour = 0;
|
||||||
var minute = 0;
|
Int32 minute = 0;
|
||||||
var second = 0;
|
Int32 second = 0;
|
||||||
|
|
||||||
var dateTimeParts = sortableDate.Split(' ');
|
String[] dateTimeParts = sortableDate.Split(' ');
|
||||||
|
|
||||||
try
|
try {
|
||||||
{
|
if(dateTimeParts.Length != 1 && dateTimeParts.Length != 2) {
|
||||||
if (dateTimeParts.Length != 1 && dateTimeParts.Length != 2)
|
|
||||||
throw new Exception();
|
throw new Exception();
|
||||||
|
}
|
||||||
|
|
||||||
var dateParts = dateTimeParts[0].Split('-');
|
String[] dateParts = dateTimeParts[0].Split('-');
|
||||||
if (dateParts.Length != 3) throw new Exception();
|
if(dateParts.Length != 3) {
|
||||||
|
throw new Exception();
|
||||||
|
}
|
||||||
|
|
||||||
var year = int.Parse(dateParts[0]);
|
Int32 year = Int32.Parse(dateParts[0]);
|
||||||
var month = int.Parse(dateParts[1]);
|
Int32 month = Int32.Parse(dateParts[1]);
|
||||||
var day = int.Parse(dateParts[2]);
|
Int32 day = Int32.Parse(dateParts[2]);
|
||||||
|
|
||||||
if (dateTimeParts.Length > 1)
|
if(dateTimeParts.Length > 1) {
|
||||||
{
|
String[] timeParts = dateTimeParts[1].Split(':');
|
||||||
var timeParts = dateTimeParts[1].Split(':');
|
if(timeParts.Length != 3) {
|
||||||
if (timeParts.Length != 3) throw new Exception();
|
throw new Exception();
|
||||||
|
}
|
||||||
|
|
||||||
hour = int.Parse(timeParts[0]);
|
hour = Int32.Parse(timeParts[0]);
|
||||||
minute = int.Parse(timeParts[1]);
|
minute = Int32.Parse(timeParts[1]);
|
||||||
second = int.Parse(timeParts[2]);
|
second = Int32.Parse(timeParts[2]);
|
||||||
}
|
}
|
||||||
|
|
||||||
return new DateTime(year, month, day, hour, minute, second);
|
return new DateTime(year, month, day, hour, minute, second);
|
||||||
}
|
} catch(Exception) {
|
||||||
catch (Exception)
|
|
||||||
{
|
|
||||||
throw new ArgumentException("Unable to parse sortable date and time.", nameof(sortableDate));
|
throw new ArgumentException("Unable to parse sortable date and time.", nameof(sortableDate));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -110,19 +109,18 @@
|
|||||||
/// A new instance of the DateTime structure to the specified datetime and timespan ticks.
|
/// A new instance of the DateTime structure to the specified datetime and timespan ticks.
|
||||||
/// </returns>
|
/// </returns>
|
||||||
public static DateTime RoundUp(this DateTime date, TimeSpan timeSpan)
|
public static DateTime RoundUp(this DateTime date, TimeSpan timeSpan)
|
||||||
=> new DateTime(((date.Ticks + timeSpan.Ticks - 1) / timeSpan.Ticks) * timeSpan.Ticks);
|
=> new DateTime((date.Ticks + timeSpan.Ticks - 1) / timeSpan.Ticks * timeSpan.Ticks);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Get this datetime as a Unix epoch timestamp (seconds since Jan 1, 1970, midnight UTC).
|
/// Get this datetime as a Unix epoch timestamp (seconds since Jan 1, 1970, midnight UTC).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="date">The date to convert.</param>
|
/// <param name="date">The date to convert.</param>
|
||||||
/// <returns>Seconds since Unix epoch.</returns>
|
/// <returns>Seconds since Unix epoch.</returns>
|
||||||
public static long ToUnixEpochDate(this DateTime date)
|
public static Int64 ToUnixEpochDate(this DateTime date) {
|
||||||
{
|
|
||||||
#if NETSTANDARD2_0
|
#if NETSTANDARD2_0
|
||||||
return new DateTimeOffset(date).ToUniversalTime().ToUnixTimeSeconds();
|
return new DateTimeOffset(date).ToUniversalTime().ToUnixTimeSeconds();
|
||||||
#else
|
#else
|
||||||
var epochTicks = new DateTime(1970, 1, 1).Ticks;
|
Int64 epochTicks = new DateTime(1970, 1, 1).Ticks;
|
||||||
|
|
||||||
return (date.Ticks - epochTicks) / TimeSpan.TicksPerSecond;
|
return (date.Ticks - epochTicks) / TimeSpan.TicksPerSecond;
|
||||||
#endif
|
#endif
|
||||||
@ -147,15 +145,14 @@
|
|||||||
/// <param name="month">The month. (1-12).</param>
|
/// <param name="month">The month. (1-12).</param>
|
||||||
/// <param name="dayOfWeek">The day of week. (0-6)(Sunday = 0).</param>
|
/// <param name="dayOfWeek">The day of week. (0-6)(Sunday = 0).</param>
|
||||||
/// <returns>Returns <c>true</c> if Months, Days, Hours and Minutes match, otherwise <c>false</c>.</returns>
|
/// <returns>Returns <c>true</c> if Months, Days, Hours and Minutes match, otherwise <c>false</c>.</returns>
|
||||||
public static bool AsCronCanRun(this DateTime date, int? minute = null, int? hour = null, int? dayOfMonth = null, int? month = null, int? dayOfWeek = null)
|
public static Boolean AsCronCanRun(this DateTime date, Int32? minute = null, Int32? hour = null, Int32? dayOfMonth = null, Int32? month = null, Int32? dayOfWeek = null) {
|
||||||
{
|
List<Boolean?> results = new List<Boolean?>
|
||||||
var results = new List<bool?>
|
|
||||||
{
|
{
|
||||||
GetElementParts(minute, date.Minute),
|
GetElementParts(minute, date.Minute),
|
||||||
GetElementParts(hour, date.Hour),
|
GetElementParts(hour, date.Hour),
|
||||||
GetElementParts(dayOfMonth, date.Day),
|
GetElementParts(dayOfMonth, date.Day),
|
||||||
GetElementParts(month, date.Month),
|
GetElementParts(month, date.Month),
|
||||||
GetElementParts(dayOfWeek, (int) date.DayOfWeek),
|
GetElementParts(dayOfWeek, (Int32) date.DayOfWeek),
|
||||||
};
|
};
|
||||||
|
|
||||||
return results.Any(x => x != false);
|
return results.Any(x => x != false);
|
||||||
@ -171,60 +168,64 @@
|
|||||||
/// <param name="month">The month. (1-12).</param>
|
/// <param name="month">The month. (1-12).</param>
|
||||||
/// <param name="dayOfWeek">The day of week. (0-6)(Sunday = 0).</param>
|
/// <param name="dayOfWeek">The day of week. (0-6)(Sunday = 0).</param>
|
||||||
/// <returns>Returns <c>true</c> if Months, Days, Hours and Minutes match, otherwise <c>false</c>.</returns>
|
/// <returns>Returns <c>true</c> if Months, Days, Hours and Minutes match, otherwise <c>false</c>.</returns>
|
||||||
public static bool AsCronCanRun(this DateTime date, string minute = "*", string hour = "*", string dayOfMonth = "*", string month = "*", string dayOfWeek = "*")
|
public static Boolean AsCronCanRun(this DateTime date, String minute = "*", String hour = "*", String dayOfMonth = "*", String month = "*", String dayOfWeek = "*") {
|
||||||
{
|
List<Boolean?> results = new List<Boolean?>
|
||||||
var results = new List<bool?>
|
|
||||||
{
|
{
|
||||||
GetElementParts(minute, nameof(minute), date.Minute),
|
GetElementParts(minute, nameof(minute), date.Minute),
|
||||||
GetElementParts(hour, nameof(hour), date.Hour),
|
GetElementParts(hour, nameof(hour), date.Hour),
|
||||||
GetElementParts(dayOfMonth, nameof(dayOfMonth), date.Day),
|
GetElementParts(dayOfMonth, nameof(dayOfMonth), date.Day),
|
||||||
GetElementParts(month, nameof(month), date.Month),
|
GetElementParts(month, nameof(month), date.Month),
|
||||||
GetElementParts(dayOfWeek, nameof(dayOfWeek), (int) date.DayOfWeek),
|
GetElementParts(dayOfWeek, nameof(dayOfWeek), (Int32) date.DayOfWeek),
|
||||||
};
|
};
|
||||||
|
|
||||||
return results.Any(x => x != false);
|
return results.Any(x => x != false);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool? GetElementParts(int? status, int value) => status.HasValue ? status.Value == value : (bool?) null;
|
private static Boolean? GetElementParts(Int32? status, Int32 value) => status.HasValue ? status.Value == value : (Boolean?)null;
|
||||||
|
|
||||||
private static bool? GetElementParts(string parts, string type, int value)
|
private static Boolean? GetElementParts(String parts, String type, Int32 value) {
|
||||||
{
|
if(String.IsNullOrWhiteSpace(parts) || parts == "*") {
|
||||||
if (string.IsNullOrWhiteSpace(parts) || parts == "*") return null;
|
return null;
|
||||||
|
|
||||||
if (parts.Contains(","))
|
|
||||||
{
|
|
||||||
return parts.Split(',').Select(int.Parse).Contains(value);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var stop = DateRanges[type];
|
if(parts.Contains(",")) {
|
||||||
|
return parts.Split(',').Select(Int32.Parse).Contains(value);
|
||||||
|
}
|
||||||
|
|
||||||
if (parts.Contains("/"))
|
Int32 stop = DateRanges[type];
|
||||||
{
|
|
||||||
var multiple = int.Parse(parts.Split('/').Last());
|
|
||||||
var start = type == "dayOfMonth" || type == "month" ? 1 : 0;
|
|
||||||
|
|
||||||
for (var i = start; i <= stop; i += multiple)
|
if(parts.Contains("/")) {
|
||||||
if (i == value) return true;
|
Int32 multiple = Int32.Parse(parts.Split('/').Last());
|
||||||
|
Int32 start = type == "dayOfMonth" || type == "month" ? 1 : 0;
|
||||||
|
|
||||||
|
for(Int32 i = start; i <= stop; i += multiple) {
|
||||||
|
if(i == value) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (parts.Contains("-"))
|
if(parts.Contains("-")) {
|
||||||
{
|
String[] range = parts.Split('-');
|
||||||
var range = parts.Split('-');
|
Int32 start = Int32.Parse(range.First());
|
||||||
var start = int.Parse(range.First());
|
stop = Math.Max(stop, Int32.Parse(range.Last()));
|
||||||
stop = Math.Max(stop, int.Parse(range.Last()));
|
|
||||||
|
|
||||||
if ((type == "dayOfMonth" || type == "month") && start == 0)
|
if((type == "dayOfMonth" || type == "month") && start == 0) {
|
||||||
start = 1;
|
start = 1;
|
||||||
|
}
|
||||||
|
|
||||||
for (var i = start; i <= stop; i++)
|
for(Int32 i = start; i <= stop; i++) {
|
||||||
if (i == value) return true;
|
if(i == value) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return int.Parse(parts) == value;
|
return Int32.Parse(parts) == value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,13 +1,11 @@
|
|||||||
namespace Unosquare.Swan
|
using System;
|
||||||
{
|
using System.Collections.Generic;
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
|
|
||||||
|
namespace Unosquare.Swan {
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Extension methods.
|
/// Extension methods.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static partial class Extensions
|
public static partial class Extensions {
|
||||||
{
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the value if exists or default.
|
/// Gets the value if exists or default.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -20,10 +18,10 @@
|
|||||||
/// The value of the provided key or default.
|
/// The value of the provided key or default.
|
||||||
/// </returns>
|
/// </returns>
|
||||||
/// <exception cref="ArgumentNullException">dict.</exception>
|
/// <exception cref="ArgumentNullException">dict.</exception>
|
||||||
public static TValue GetValueOrDefault<TKey, TValue>(this IDictionary<TKey, TValue> dict, TKey key, TValue defaultValue = default)
|
public static TValue GetValueOrDefault<TKey, TValue>(this IDictionary<TKey, TValue> dict, TKey key, TValue defaultValue = default) {
|
||||||
{
|
if(dict == null) {
|
||||||
if (dict == null)
|
|
||||||
throw new ArgumentNullException(nameof(dict));
|
throw new ArgumentNullException(nameof(dict));
|
||||||
|
}
|
||||||
|
|
||||||
return dict.ContainsKey(key) ? dict[key] : defaultValue;
|
return dict.ContainsKey(key) ? dict[key] : defaultValue;
|
||||||
}
|
}
|
||||||
@ -40,15 +38,17 @@
|
|||||||
/// <param name="key">The key.</param>
|
/// <param name="key">The key.</param>
|
||||||
/// <param name="valueFactory">The value factory.</param>
|
/// <param name="valueFactory">The value factory.</param>
|
||||||
/// <returns>The value for the key.</returns>
|
/// <returns>The value for the key.</returns>
|
||||||
public static TValue GetOrAdd<TKey, TValue>(this IDictionary<TKey, TValue> dict, TKey key, Func<TKey, TValue> valueFactory)
|
public static TValue GetOrAdd<TKey, TValue>(this IDictionary<TKey, TValue> dict, TKey key, Func<TKey, TValue> valueFactory) {
|
||||||
{
|
if(dict == null) {
|
||||||
if (dict == null)
|
|
||||||
throw new ArgumentNullException(nameof(dict));
|
throw new ArgumentNullException(nameof(dict));
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!dict.ContainsKey(key)) {
|
||||||
|
TValue value = valueFactory(key);
|
||||||
|
if(Equals(value, default)) {
|
||||||
|
return default;
|
||||||
|
}
|
||||||
|
|
||||||
if (!dict.ContainsKey(key))
|
|
||||||
{
|
|
||||||
var value = valueFactory(key);
|
|
||||||
if (Equals(value, default)) return default;
|
|
||||||
dict[key] = value;
|
dict[key] = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -63,13 +63,12 @@
|
|||||||
/// <param name="dict">The dictionary.</param>
|
/// <param name="dict">The dictionary.</param>
|
||||||
/// <param name="itemAction">The item action.</param>
|
/// <param name="itemAction">The item action.</param>
|
||||||
/// <exception cref="ArgumentNullException">dict.</exception>
|
/// <exception cref="ArgumentNullException">dict.</exception>
|
||||||
public static void ForEach<TKey, TValue>(this IDictionary<TKey, TValue> dict, Action<TKey, TValue> itemAction)
|
public static void ForEach<TKey, TValue>(this IDictionary<TKey, TValue> dict, Action<TKey, TValue> itemAction) {
|
||||||
{
|
if(dict == null) {
|
||||||
if (dict == null)
|
|
||||||
throw new ArgumentNullException(nameof(dict));
|
throw new ArgumentNullException(nameof(dict));
|
||||||
|
}
|
||||||
|
|
||||||
foreach (var kvp in dict)
|
foreach(KeyValuePair<TKey, TValue> kvp in dict) {
|
||||||
{
|
|
||||||
itemAction(kvp.Key, kvp.Value);
|
itemAction(kvp.Key, kvp.Value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,14 +1,12 @@
|
|||||||
namespace Unosquare.Swan
|
using System;
|
||||||
{
|
using System.Collections.Generic;
|
||||||
using System;
|
using System.Linq;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
|
|
||||||
|
namespace Unosquare.Swan {
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Functional programming extension methods.
|
/// Functional programming extension methods.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static class FunctionalExtensions
|
public static class FunctionalExtensions {
|
||||||
{
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Whens the specified condition.
|
/// Whens the specified condition.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -28,17 +26,19 @@
|
|||||||
/// </exception>
|
/// </exception>
|
||||||
public static IQueryable<T> When<T>(
|
public static IQueryable<T> When<T>(
|
||||||
this IQueryable<T> list,
|
this IQueryable<T> list,
|
||||||
Func<bool> condition,
|
Func<Boolean> condition,
|
||||||
Func<IQueryable<T>, IQueryable<T>> fn)
|
Func<IQueryable<T>, IQueryable<T>> fn) {
|
||||||
{
|
if(list == null) {
|
||||||
if (list == null)
|
|
||||||
throw new ArgumentNullException(nameof(list));
|
throw new ArgumentNullException(nameof(list));
|
||||||
|
}
|
||||||
|
|
||||||
if (condition == null)
|
if(condition == null) {
|
||||||
throw new ArgumentNullException(nameof(condition));
|
throw new ArgumentNullException(nameof(condition));
|
||||||
|
}
|
||||||
|
|
||||||
if (fn == null)
|
if(fn == null) {
|
||||||
throw new ArgumentNullException(nameof(fn));
|
throw new ArgumentNullException(nameof(fn));
|
||||||
|
}
|
||||||
|
|
||||||
return condition() ? fn(list) : list;
|
return condition() ? fn(list) : list;
|
||||||
}
|
}
|
||||||
@ -62,17 +62,19 @@
|
|||||||
/// </exception>
|
/// </exception>
|
||||||
public static IEnumerable<T> When<T>(
|
public static IEnumerable<T> When<T>(
|
||||||
this IEnumerable<T> list,
|
this IEnumerable<T> list,
|
||||||
Func<bool> condition,
|
Func<Boolean> condition,
|
||||||
Func<IEnumerable<T>, IEnumerable<T>> fn)
|
Func<IEnumerable<T>, IEnumerable<T>> fn) {
|
||||||
{
|
if(list == null) {
|
||||||
if (list == null)
|
|
||||||
throw new ArgumentNullException(nameof(list));
|
throw new ArgumentNullException(nameof(list));
|
||||||
|
}
|
||||||
|
|
||||||
if (condition == null)
|
if(condition == null) {
|
||||||
throw new ArgumentNullException(nameof(condition));
|
throw new ArgumentNullException(nameof(condition));
|
||||||
|
}
|
||||||
|
|
||||||
if (fn == null)
|
if(fn == null) {
|
||||||
throw new ArgumentNullException(nameof(fn));
|
throw new ArgumentNullException(nameof(fn));
|
||||||
|
}
|
||||||
|
|
||||||
return condition() ? fn(list) : list;
|
return condition() ? fn(list) : list;
|
||||||
}
|
}
|
||||||
@ -96,20 +98,23 @@
|
|||||||
/// </exception>
|
/// </exception>
|
||||||
public static IList<T> AddWhen<T>(
|
public static IList<T> AddWhen<T>(
|
||||||
this IList<T> list,
|
this IList<T> list,
|
||||||
Func<bool> condition,
|
Func<Boolean> condition,
|
||||||
Func<T> value)
|
Func<T> value) {
|
||||||
{
|
if(list == null) {
|
||||||
if (list == null)
|
|
||||||
throw new ArgumentNullException(nameof(list));
|
throw new ArgumentNullException(nameof(list));
|
||||||
|
}
|
||||||
|
|
||||||
if (condition == null)
|
if(condition == null) {
|
||||||
throw new ArgumentNullException(nameof(condition));
|
throw new ArgumentNullException(nameof(condition));
|
||||||
|
}
|
||||||
|
|
||||||
if (value == null)
|
if(value == null) {
|
||||||
throw new ArgumentNullException(nameof(value));
|
throw new ArgumentNullException(nameof(value));
|
||||||
|
}
|
||||||
|
|
||||||
if (condition())
|
if(condition()) {
|
||||||
list.Add(value());
|
list.Add(value());
|
||||||
|
}
|
||||||
|
|
||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
@ -127,14 +132,15 @@
|
|||||||
/// <exception cref="ArgumentNullException">list.</exception>
|
/// <exception cref="ArgumentNullException">list.</exception>
|
||||||
public static IList<T> AddWhen<T>(
|
public static IList<T> AddWhen<T>(
|
||||||
this IList<T> list,
|
this IList<T> list,
|
||||||
bool condition,
|
Boolean condition,
|
||||||
T value)
|
T value) {
|
||||||
{
|
if(list == null) {
|
||||||
if (list == null)
|
|
||||||
throw new ArgumentNullException(nameof(list));
|
throw new ArgumentNullException(nameof(list));
|
||||||
|
}
|
||||||
|
|
||||||
if (condition)
|
if(condition) {
|
||||||
list.Add(value);
|
list.Add(value);
|
||||||
|
}
|
||||||
|
|
||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
@ -158,20 +164,23 @@
|
|||||||
/// </exception>
|
/// </exception>
|
||||||
public static List<T> AddRangeWhen<T>(
|
public static List<T> AddRangeWhen<T>(
|
||||||
this List<T> list,
|
this List<T> list,
|
||||||
Func<bool> condition,
|
Func<Boolean> condition,
|
||||||
Func<IEnumerable<T>> value)
|
Func<IEnumerable<T>> value) {
|
||||||
{
|
if(list == null) {
|
||||||
if (list == null)
|
|
||||||
throw new ArgumentNullException(nameof(list));
|
throw new ArgumentNullException(nameof(list));
|
||||||
|
}
|
||||||
|
|
||||||
if (condition == null)
|
if(condition == null) {
|
||||||
throw new ArgumentNullException(nameof(condition));
|
throw new ArgumentNullException(nameof(condition));
|
||||||
|
}
|
||||||
|
|
||||||
if (value == null)
|
if(value == null) {
|
||||||
throw new ArgumentNullException(nameof(value));
|
throw new ArgumentNullException(nameof(value));
|
||||||
|
}
|
||||||
|
|
||||||
if (condition())
|
if(condition()) {
|
||||||
list.AddRange(value());
|
list.AddRange(value());
|
||||||
|
}
|
||||||
|
|
||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
|
@ -1,23 +1,21 @@
|
|||||||
namespace Unosquare.Swan
|
using System;
|
||||||
{
|
using System.Collections.Concurrent;
|
||||||
using System;
|
using System.Collections;
|
||||||
using System.Collections.Concurrent;
|
using System.Linq;
|
||||||
using System.Collections;
|
using System.Reflection;
|
||||||
using System.Linq;
|
using System.Collections.Generic;
|
||||||
using System.Reflection;
|
using Unosquare.Swan.Attributes;
|
||||||
using System.Collections.Generic;
|
|
||||||
using Attributes;
|
|
||||||
|
|
||||||
|
namespace Unosquare.Swan {
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Provides various extension methods for Reflection and Types.
|
/// Provides various extension methods for Reflection and Types.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static class ReflectionExtensions
|
public static class ReflectionExtensions {
|
||||||
{
|
private static readonly Lazy<ConcurrentDictionary<Tuple<Boolean, PropertyInfo>, Func<Object, Object>>> CacheGetMethods =
|
||||||
private static readonly Lazy<ConcurrentDictionary<Tuple<bool, PropertyInfo>, Func<object, object>>> CacheGetMethods =
|
new Lazy<ConcurrentDictionary<Tuple<Boolean, PropertyInfo>, Func<Object, Object>>>(() => new ConcurrentDictionary<Tuple<Boolean, PropertyInfo>, Func<Object, Object>>(), true);
|
||||||
new Lazy<ConcurrentDictionary<Tuple<bool, PropertyInfo>, Func<object, object>>>(() => new ConcurrentDictionary<Tuple<bool, PropertyInfo>, Func<object, object>>(), true);
|
|
||||||
|
|
||||||
private static readonly Lazy<ConcurrentDictionary<Tuple<bool, PropertyInfo>, Action<object, object[]>>> CacheSetMethods =
|
private static readonly Lazy<ConcurrentDictionary<Tuple<Boolean, PropertyInfo>, Action<Object, Object[]>>> CacheSetMethods =
|
||||||
new Lazy<ConcurrentDictionary<Tuple<bool, PropertyInfo>, Action<object, object[]>>>(() => new ConcurrentDictionary<Tuple<bool, PropertyInfo>, Action<object, object[]>>(), true);
|
new Lazy<ConcurrentDictionary<Tuple<Boolean, PropertyInfo>, Action<Object, Object[]>>>(() => new ConcurrentDictionary<Tuple<Boolean, PropertyInfo>, Action<Object, Object[]>>(), true);
|
||||||
|
|
||||||
#region Assembly Extensions
|
#region Assembly Extensions
|
||||||
|
|
||||||
@ -29,17 +27,14 @@
|
|||||||
/// Array of Type objects representing the types specified by an assembly.
|
/// Array of Type objects representing the types specified by an assembly.
|
||||||
/// </returns>
|
/// </returns>
|
||||||
/// <exception cref="ArgumentNullException">assembly.</exception>
|
/// <exception cref="ArgumentNullException">assembly.</exception>
|
||||||
public static IEnumerable<Type> GetAllTypes(this Assembly assembly)
|
public static IEnumerable<Type> GetAllTypes(this Assembly assembly) {
|
||||||
{
|
if(assembly == null) {
|
||||||
if (assembly == null)
|
|
||||||
throw new ArgumentNullException(nameof(assembly));
|
throw new ArgumentNullException(nameof(assembly));
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
return assembly.GetTypes();
|
|
||||||
}
|
}
|
||||||
catch (ReflectionTypeLoadException e)
|
|
||||||
{
|
try {
|
||||||
|
return assembly.GetTypes();
|
||||||
|
} catch(ReflectionTypeLoadException e) {
|
||||||
return e.Types.Where(t => t != null);
|
return e.Types.Where(t => t != null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -56,10 +51,10 @@
|
|||||||
/// Default value of this type.
|
/// Default value of this type.
|
||||||
/// </returns>
|
/// </returns>
|
||||||
/// <exception cref="ArgumentNullException">type.</exception>
|
/// <exception cref="ArgumentNullException">type.</exception>
|
||||||
public static object GetDefault(this Type type)
|
public static Object GetDefault(this Type type) {
|
||||||
{
|
if(type == null) {
|
||||||
if (type == null)
|
|
||||||
throw new ArgumentNullException(nameof(type));
|
throw new ArgumentNullException(nameof(type));
|
||||||
|
}
|
||||||
|
|
||||||
return type.IsValueType() ? Activator.CreateInstance(type) : null;
|
return type.IsValueType() ? Activator.CreateInstance(type) : null;
|
||||||
}
|
}
|
||||||
@ -72,12 +67,12 @@
|
|||||||
/// <c>true</c> if the specified source type is collection; otherwise, <c>false</c>.
|
/// <c>true</c> if the specified source type is collection; otherwise, <c>false</c>.
|
||||||
/// </returns>
|
/// </returns>
|
||||||
/// <exception cref="ArgumentNullException">sourceType.</exception>
|
/// <exception cref="ArgumentNullException">sourceType.</exception>
|
||||||
public static bool IsCollection(this Type sourceType)
|
public static Boolean IsCollection(this Type sourceType) {
|
||||||
{
|
if(sourceType == null) {
|
||||||
if (sourceType == null)
|
|
||||||
throw new ArgumentNullException(nameof(sourceType));
|
throw new ArgumentNullException(nameof(sourceType));
|
||||||
|
}
|
||||||
|
|
||||||
return sourceType != typeof(string) &&
|
return sourceType != typeof(String) &&
|
||||||
typeof(IEnumerable).IsAssignableFrom(sourceType);
|
typeof(IEnumerable).IsAssignableFrom(sourceType);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -99,25 +94,28 @@
|
|||||||
public static MethodInfo GetMethod(
|
public static MethodInfo GetMethod(
|
||||||
this Type type,
|
this Type type,
|
||||||
BindingFlags bindingFlags,
|
BindingFlags bindingFlags,
|
||||||
string methodName,
|
String methodName,
|
||||||
Type[] genericTypes,
|
Type[] genericTypes,
|
||||||
Type[] parameterTypes)
|
Type[] parameterTypes) {
|
||||||
{
|
if(type == null) {
|
||||||
if (type == null)
|
|
||||||
throw new ArgumentNullException(nameof(type));
|
throw new ArgumentNullException(nameof(type));
|
||||||
|
}
|
||||||
|
|
||||||
if (methodName == null)
|
if(methodName == null) {
|
||||||
throw new ArgumentNullException(nameof(methodName));
|
throw new ArgumentNullException(nameof(methodName));
|
||||||
|
}
|
||||||
|
|
||||||
if (genericTypes == null)
|
if(genericTypes == null) {
|
||||||
throw new ArgumentNullException(nameof(genericTypes));
|
throw new ArgumentNullException(nameof(genericTypes));
|
||||||
|
}
|
||||||
|
|
||||||
if (parameterTypes == null)
|
if(parameterTypes == null) {
|
||||||
throw new ArgumentNullException(nameof(parameterTypes));
|
throw new ArgumentNullException(nameof(parameterTypes));
|
||||||
|
}
|
||||||
|
|
||||||
var methods = type
|
List<MethodInfo> methods = type
|
||||||
.GetMethods(bindingFlags)
|
.GetMethods(bindingFlags)
|
||||||
.Where(mi => string.Equals(methodName, mi.Name, StringComparison.Ordinal))
|
.Where(mi => String.Equals(methodName, mi.Name, StringComparison.Ordinal))
|
||||||
.Where(mi => mi.ContainsGenericParameters)
|
.Where(mi => mi.ContainsGenericParameters)
|
||||||
.Where(mi => mi.GetGenericArguments().Length == genericTypes.Length)
|
.Where(mi => mi.GetGenericArguments().Length == genericTypes.Length)
|
||||||
.Where(mi => mi.GetParameters().Length == parameterTypes.Length)
|
.Where(mi => mi.GetParameters().Length == parameterTypes.Length)
|
||||||
@ -135,7 +133,7 @@
|
|||||||
/// <returns>
|
/// <returns>
|
||||||
/// <c>true</c> if the specified type is class; otherwise, <c>false</c>.
|
/// <c>true</c> if the specified type is class; otherwise, <c>false</c>.
|
||||||
/// </returns>
|
/// </returns>
|
||||||
public static bool IsClass(this Type type) => type.GetTypeInfo().IsClass;
|
public static Boolean IsClass(this Type type) => type.GetTypeInfo().IsClass;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Determines whether this instance is abstract.
|
/// Determines whether this instance is abstract.
|
||||||
@ -144,7 +142,7 @@
|
|||||||
/// <returns>
|
/// <returns>
|
||||||
/// <c>true</c> if the specified type is abstract; otherwise, <c>false</c>.
|
/// <c>true</c> if the specified type is abstract; otherwise, <c>false</c>.
|
||||||
/// </returns>
|
/// </returns>
|
||||||
public static bool IsAbstract(this Type type) => type.GetTypeInfo().IsAbstract;
|
public static Boolean IsAbstract(this Type type) => type.GetTypeInfo().IsAbstract;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Determines whether this instance is interface.
|
/// Determines whether this instance is interface.
|
||||||
@ -153,7 +151,7 @@
|
|||||||
/// <returns>
|
/// <returns>
|
||||||
/// <c>true</c> if the specified type is interface; otherwise, <c>false</c>.
|
/// <c>true</c> if the specified type is interface; otherwise, <c>false</c>.
|
||||||
/// </returns>
|
/// </returns>
|
||||||
public static bool IsInterface(this Type type) => type.GetTypeInfo().IsInterface;
|
public static Boolean IsInterface(this Type type) => type.GetTypeInfo().IsInterface;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Determines whether this instance is primitive.
|
/// Determines whether this instance is primitive.
|
||||||
@ -162,7 +160,7 @@
|
|||||||
/// <returns>
|
/// <returns>
|
||||||
/// <c>true</c> if the specified type is primitive; otherwise, <c>false</c>.
|
/// <c>true</c> if the specified type is primitive; otherwise, <c>false</c>.
|
||||||
/// </returns>
|
/// </returns>
|
||||||
public static bool IsPrimitive(this Type type) => type.GetTypeInfo().IsPrimitive;
|
public static Boolean IsPrimitive(this Type type) => type.GetTypeInfo().IsPrimitive;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Determines whether [is value type].
|
/// Determines whether [is value type].
|
||||||
@ -171,7 +169,7 @@
|
|||||||
/// <returns>
|
/// <returns>
|
||||||
/// <c>true</c> if [is value type] [the specified type]; otherwise, <c>false</c>.
|
/// <c>true</c> if [is value type] [the specified type]; otherwise, <c>false</c>.
|
||||||
/// </returns>
|
/// </returns>
|
||||||
public static bool IsValueType(this Type type) => type.GetTypeInfo().IsValueType;
|
public static Boolean IsValueType(this Type type) => type.GetTypeInfo().IsValueType;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Determines whether [is generic type].
|
/// Determines whether [is generic type].
|
||||||
@ -180,7 +178,7 @@
|
|||||||
/// <returns>
|
/// <returns>
|
||||||
/// <c>true</c> if [is generic type] [the specified type]; otherwise, <c>false</c>.
|
/// <c>true</c> if [is generic type] [the specified type]; otherwise, <c>false</c>.
|
||||||
/// </returns>
|
/// </returns>
|
||||||
public static bool IsGenericType(this Type type) => type.GetTypeInfo().IsGenericType;
|
public static Boolean IsGenericType(this Type type) => type.GetTypeInfo().IsGenericType;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Determines whether [is generic parameter].
|
/// Determines whether [is generic parameter].
|
||||||
@ -189,7 +187,7 @@
|
|||||||
/// <returns>
|
/// <returns>
|
||||||
/// <c>true</c> if [is generic parameter] [the specified type]; otherwise, <c>false</c>.
|
/// <c>true</c> if [is generic parameter] [the specified type]; otherwise, <c>false</c>.
|
||||||
/// </returns>
|
/// </returns>
|
||||||
public static bool IsGenericParameter(this Type type) => type.IsGenericParameter;
|
public static Boolean IsGenericParameter(this Type type) => type.IsGenericParameter;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Determines whether the specified attribute type is defined.
|
/// Determines whether the specified attribute type is defined.
|
||||||
@ -200,7 +198,7 @@
|
|||||||
/// <returns>
|
/// <returns>
|
||||||
/// <c>true</c> if the specified attribute type is defined; otherwise, <c>false</c>.
|
/// <c>true</c> if the specified attribute type is defined; otherwise, <c>false</c>.
|
||||||
/// </returns>
|
/// </returns>
|
||||||
public static bool IsDefined(this Type type, Type attributeType, bool inherit) =>
|
public static Boolean IsDefined(this Type type, Type attributeType, Boolean inherit) =>
|
||||||
type.GetTypeInfo().IsDefined(attributeType, inherit);
|
type.GetTypeInfo().IsDefined(attributeType, inherit);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -212,7 +210,7 @@
|
|||||||
/// <returns>
|
/// <returns>
|
||||||
/// Attributes associated with the property represented by this PropertyInfo object.
|
/// Attributes associated with the property represented by this PropertyInfo object.
|
||||||
/// </returns>
|
/// </returns>
|
||||||
public static Attribute[] GetCustomAttributes(this Type type, Type attributeType, bool inherit) =>
|
public static Attribute[] GetCustomAttributes(this Type type, Type attributeType, Boolean inherit) =>
|
||||||
type.GetTypeInfo().GetCustomAttributes(attributeType, inherit).Cast<Attribute>().ToArray();
|
type.GetTypeInfo().GetCustomAttributes(attributeType, inherit).Cast<Attribute>().ToArray();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -222,7 +220,7 @@
|
|||||||
/// <returns>
|
/// <returns>
|
||||||
/// <c>true</c> if [is generic type definition] [the specified type]; otherwise, <c>false</c>.
|
/// <c>true</c> if [is generic type definition] [the specified type]; otherwise, <c>false</c>.
|
||||||
/// </returns>
|
/// </returns>
|
||||||
public static bool IsGenericTypeDefinition(this Type type) => type.GetTypeInfo().IsGenericTypeDefinition;
|
public static Boolean IsGenericTypeDefinition(this Type type) => type.GetTypeInfo().IsGenericTypeDefinition;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Bases the type.
|
/// Bases the type.
|
||||||
@ -246,7 +244,7 @@
|
|||||||
/// <c>true</c> if [is i enumerable request] [the specified type]; otherwise, <c>false</c>.
|
/// <c>true</c> if [is i enumerable request] [the specified type]; otherwise, <c>false</c>.
|
||||||
/// </returns>
|
/// </returns>
|
||||||
/// <exception cref="ArgumentNullException">type.</exception>
|
/// <exception cref="ArgumentNullException">type.</exception>
|
||||||
public static bool IsIEnumerable(this Type type)
|
public static Boolean IsIEnumerable(this Type type)
|
||||||
=> type == null
|
=> type == null
|
||||||
? throw new ArgumentNullException(nameof(type))
|
? throw new ArgumentNullException(nameof(type))
|
||||||
: type.IsGenericType() && type.GetGenericTypeDefinition() == typeof(IEnumerable<>);
|
: type.IsGenericType() && type.GetGenericTypeDefinition() == typeof(IEnumerable<>);
|
||||||
@ -262,7 +260,7 @@
|
|||||||
/// <returns>
|
/// <returns>
|
||||||
/// <c>true</c> if parsing was successful; otherwise, <c>false</c>.
|
/// <c>true</c> if parsing was successful; otherwise, <c>false</c>.
|
||||||
/// </returns>
|
/// </returns>
|
||||||
public static bool TryParseBasicType(this Type type, object value, out object result)
|
public static Boolean TryParseBasicType(this Type type, Object value, out Object result)
|
||||||
=> TryParseBasicType(type, value.ToStringInvariant(), out result);
|
=> TryParseBasicType(type, value.ToStringInvariant(), out result);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -274,8 +272,7 @@
|
|||||||
/// <returns>
|
/// <returns>
|
||||||
/// <c>true</c> if parsing was successful; otherwise, <c>false</c>.
|
/// <c>true</c> if parsing was successful; otherwise, <c>false</c>.
|
||||||
/// </returns>
|
/// </returns>
|
||||||
public static bool TryParseBasicType(this Type type, string value, out object result)
|
public static Boolean TryParseBasicType(this Type type, String value, out Object result) {
|
||||||
{
|
|
||||||
result = null;
|
result = null;
|
||||||
|
|
||||||
return Definitions.BasicTypesInfo.ContainsKey(type) && Definitions.BasicTypesInfo[type].TryParse(value, out result);
|
return Definitions.BasicTypesInfo.ContainsKey(type) && Definitions.BasicTypesInfo[type].TryParse(value, out result);
|
||||||
@ -290,18 +287,13 @@
|
|||||||
/// <returns>
|
/// <returns>
|
||||||
/// <c>true</c> if parsing was successful; otherwise, <c>false</c>.
|
/// <c>true</c> if parsing was successful; otherwise, <c>false</c>.
|
||||||
/// </returns>
|
/// </returns>
|
||||||
public static bool TrySetBasicType(this PropertyInfo property, object value, object obj)
|
public static Boolean TrySetBasicType(this PropertyInfo property, Object value, Object obj) {
|
||||||
{
|
try {
|
||||||
try
|
if(property.PropertyType.TryParseBasicType(value, out Object propertyValue)) {
|
||||||
{
|
|
||||||
if (property.PropertyType.TryParseBasicType(value, out var propertyValue))
|
|
||||||
{
|
|
||||||
property.SetValue(obj, propertyValue);
|
property.SetValue(obj, propertyValue);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
} catch {
|
||||||
catch
|
|
||||||
{
|
|
||||||
// swallow
|
// swallow
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -318,30 +310,23 @@
|
|||||||
/// <returns>
|
/// <returns>
|
||||||
/// <c>true</c> if parsing was successful; otherwise, <c>false</c>.
|
/// <c>true</c> if parsing was successful; otherwise, <c>false</c>.
|
||||||
/// </returns>
|
/// </returns>
|
||||||
public static bool TrySetArrayBasicType(this Type type, object value, Array array, int index)
|
public static Boolean TrySetArrayBasicType(this Type type, Object value, Array array, Int32 index) {
|
||||||
{
|
try {
|
||||||
try
|
if(value == null) {
|
||||||
{
|
|
||||||
if (value == null)
|
|
||||||
{
|
|
||||||
array.SetValue(null, index);
|
array.SetValue(null, index);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (type.TryParseBasicType(value, out var propertyValue))
|
if(type.TryParseBasicType(value, out Object propertyValue)) {
|
||||||
{
|
|
||||||
array.SetValue(propertyValue, index);
|
array.SetValue(propertyValue, index);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (type.GetTypeInfo().IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>))
|
if(type.GetTypeInfo().IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>)) {
|
||||||
{
|
|
||||||
array.SetValue(null, index);
|
array.SetValue(null, index);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
} catch {
|
||||||
catch
|
|
||||||
{
|
|
||||||
// swallow
|
// swallow
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -357,22 +342,23 @@
|
|||||||
/// <returns>
|
/// <returns>
|
||||||
/// <c>true</c> if parsing was successful; otherwise, <c>false</c>.
|
/// <c>true</c> if parsing was successful; otherwise, <c>false</c>.
|
||||||
/// </returns>
|
/// </returns>
|
||||||
public static bool TrySetArray(this PropertyInfo propertyInfo, IEnumerable<object> value, object obj)
|
public static Boolean TrySetArray(this PropertyInfo propertyInfo, IEnumerable<Object> value, Object obj) {
|
||||||
{
|
Type elementType = propertyInfo.PropertyType.GetElementType();
|
||||||
var elementType = propertyInfo.PropertyType.GetElementType();
|
|
||||||
|
|
||||||
if (elementType == null)
|
if(elementType == null) {
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
var targetArray = Array.CreateInstance(elementType, value.Count());
|
Array targetArray = Array.CreateInstance(elementType, value.Count());
|
||||||
|
|
||||||
var i = 0;
|
Int32 i = 0;
|
||||||
|
|
||||||
foreach (var sourceElement in value)
|
foreach(Object sourceElement in value) {
|
||||||
{
|
Boolean result = elementType.TrySetArrayBasicType(sourceElement, targetArray, i++);
|
||||||
var result = elementType.TrySetArrayBasicType(sourceElement, targetArray, i++);
|
|
||||||
|
|
||||||
if (!result) return false;
|
if(!result) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
propertyInfo.SetValue(obj, targetArray);
|
propertyInfo.SetValue(obj, targetArray);
|
||||||
@ -391,23 +377,21 @@
|
|||||||
/// <param name="propertyInfo">The property information.</param>
|
/// <param name="propertyInfo">The property information.</param>
|
||||||
/// <param name="obj">The object.</param>
|
/// <param name="obj">The object.</param>
|
||||||
/// <returns>The property value or null.</returns>
|
/// <returns>The property value or null.</returns>
|
||||||
public static string ToFormattedString(this PropertyInfo propertyInfo, object obj)
|
public static String ToFormattedString(this PropertyInfo propertyInfo, Object obj) {
|
||||||
{
|
try {
|
||||||
try
|
Object value = propertyInfo.GetValue(obj);
|
||||||
{
|
PropertyDisplayAttribute attr = Runtime.AttributeCache.RetrieveOne<PropertyDisplayAttribute>(propertyInfo);
|
||||||
var value = propertyInfo.GetValue(obj);
|
|
||||||
var attr = Runtime.AttributeCache.RetrieveOne<PropertyDisplayAttribute>(propertyInfo);
|
|
||||||
|
|
||||||
if (attr == null) return value?.ToString() ?? string.Empty;
|
if(attr == null) {
|
||||||
|
return value?.ToString() ?? String.Empty;
|
||||||
var valueToFormat = value ?? attr.DefaultValue;
|
|
||||||
|
|
||||||
return string.IsNullOrEmpty(attr.Format)
|
|
||||||
? (valueToFormat?.ToString() ?? string.Empty)
|
|
||||||
: ConvertObjectAndFormat(propertyInfo.PropertyType, valueToFormat, attr.Format);
|
|
||||||
}
|
}
|
||||||
catch
|
|
||||||
{
|
Object valueToFormat = value ?? attr.DefaultValue;
|
||||||
|
|
||||||
|
return String.IsNullOrEmpty(attr.Format)
|
||||||
|
? (valueToFormat?.ToString() ?? String.Empty)
|
||||||
|
: ConvertObjectAndFormat(propertyInfo.PropertyType, valueToFormat, attr.Format);
|
||||||
|
} catch {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -420,9 +404,8 @@
|
|||||||
/// <returns>
|
/// <returns>
|
||||||
/// The cached MethodInfo.
|
/// The cached MethodInfo.
|
||||||
/// </returns>
|
/// </returns>
|
||||||
public static Func<object, object> GetCacheGetMethod(this PropertyInfo propertyInfo, bool nonPublic = false)
|
public static Func<Object, Object> GetCacheGetMethod(this PropertyInfo propertyInfo, Boolean nonPublic = false) {
|
||||||
{
|
Tuple<Boolean, PropertyInfo> key = Tuple.Create(!nonPublic, propertyInfo);
|
||||||
var key = Tuple.Create(!nonPublic, propertyInfo);
|
|
||||||
|
|
||||||
return !nonPublic && !CacheGetMethods.Value.ContainsKey(key) && !propertyInfo.GetGetMethod(true).IsPublic
|
return !nonPublic && !CacheGetMethods.Value.ContainsKey(key) && !propertyInfo.GetGetMethod(true).IsPublic
|
||||||
? null
|
? null
|
||||||
@ -439,9 +422,8 @@
|
|||||||
/// <returns>
|
/// <returns>
|
||||||
/// The cached MethodInfo.
|
/// The cached MethodInfo.
|
||||||
/// </returns>
|
/// </returns>
|
||||||
public static Action<object, object[]> GetCacheSetMethod(this PropertyInfo propertyInfo, bool nonPublic = false)
|
public static Action<Object, Object[]> GetCacheSetMethod(this PropertyInfo propertyInfo, Boolean nonPublic = false) {
|
||||||
{
|
Tuple<Boolean, PropertyInfo> key = Tuple.Create(!nonPublic, propertyInfo);
|
||||||
var key = Tuple.Create(!nonPublic, propertyInfo);
|
|
||||||
|
|
||||||
return !nonPublic && !CacheSetMethods.Value.ContainsKey(key) && !propertyInfo.GetSetMethod(true).IsPublic
|
return !nonPublic && !CacheSetMethods.Value.ContainsKey(key) && !propertyInfo.GetSetMethod(true).IsPublic
|
||||||
? null
|
? null
|
||||||
@ -450,20 +432,16 @@
|
|||||||
x => (obj, args) => x.Item2.GetSetMethod(nonPublic).Invoke(obj, args));
|
x => (obj, args) => x.Item2.GetSetMethod(nonPublic).Invoke(obj, args));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static string ConvertObjectAndFormat(Type propertyType, object value, string format)
|
private static String ConvertObjectAndFormat(Type propertyType, Object value, String format) => propertyType == typeof(DateTime) || propertyType == typeof(DateTime?)
|
||||||
{
|
? Convert.ToDateTime(value).ToString(format)
|
||||||
if (propertyType == typeof(DateTime) || propertyType == typeof(DateTime?))
|
: propertyType == typeof(Int32) || propertyType == typeof(Int32?)
|
||||||
return Convert.ToDateTime(value).ToString(format);
|
? Convert.ToInt32(value).ToString(format)
|
||||||
if (propertyType == typeof(int) || propertyType == typeof(int?))
|
: propertyType == typeof(Decimal) || propertyType == typeof(Decimal?)
|
||||||
return Convert.ToInt32(value).ToString(format);
|
? Convert.ToDecimal(value).ToString(format)
|
||||||
if (propertyType == typeof(decimal) || propertyType == typeof(decimal?))
|
: propertyType == typeof(Double) || propertyType == typeof(Double?)
|
||||||
return Convert.ToDecimal(value).ToString(format);
|
? Convert.ToDouble(value).ToString(format)
|
||||||
if (propertyType == typeof(double) || propertyType == typeof(double?))
|
: propertyType == typeof(Byte) || propertyType == typeof(Byte?)
|
||||||
return Convert.ToDouble(value).ToString(format);
|
? Convert.ToByte(value).ToString(format)
|
||||||
if (propertyType == typeof(byte) || propertyType == typeof(byte?))
|
: value?.ToString() ?? String.Empty;
|
||||||
return Convert.ToByte(value).ToString(format);
|
|
||||||
|
|
||||||
return value?.ToString() ?? string.Empty;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,24 +1,22 @@
|
|||||||
namespace Unosquare.Swan
|
using Unosquare.Swan.Formatters;
|
||||||
{
|
using System;
|
||||||
using Formatters;
|
using System.IO;
|
||||||
using System;
|
using System.Linq;
|
||||||
using System.IO;
|
using System.Security.Cryptography;
|
||||||
using System.Linq;
|
using System.Text;
|
||||||
using System.Security.Cryptography;
|
using System.Text.RegularExpressions;
|
||||||
using System.Text;
|
|
||||||
using System.Text.RegularExpressions;
|
|
||||||
|
|
||||||
|
namespace Unosquare.Swan {
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// String related extension methods.
|
/// String related extension methods.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static class StringExtensions
|
public static class StringExtensions {
|
||||||
{
|
|
||||||
#region Private Declarations
|
#region Private Declarations
|
||||||
|
|
||||||
private const RegexOptions StandardRegexOptions =
|
private const RegexOptions StandardRegexOptions =
|
||||||
RegexOptions.Multiline | RegexOptions.Compiled | RegexOptions.CultureInvariant;
|
RegexOptions.Multiline | RegexOptions.Compiled | RegexOptions.CultureInvariant;
|
||||||
|
|
||||||
private static readonly string[] ByteSuffixes = {"B", "KB", "MB", "GB", "TB"};
|
private static readonly String[] ByteSuffixes = { "B", "KB", "MB", "GB", "TB" };
|
||||||
|
|
||||||
private static readonly Lazy<MD5> Md5Hasher = new Lazy<MD5>(MD5.Create, true);
|
private static readonly Lazy<MD5> Md5Hasher = new Lazy<MD5>(MD5.Create, true);
|
||||||
private static readonly Lazy<SHA1> SHA1Hasher = new Lazy<SHA1>(SHA1.Create, true);
|
private static readonly Lazy<SHA1> SHA1Hasher = new Lazy<SHA1>(SHA1.Create, true);
|
||||||
@ -39,17 +37,13 @@
|
|||||||
new Regex(@"[a-z][A-Z]",
|
new Regex(@"[a-z][A-Z]",
|
||||||
StandardRegexOptions));
|
StandardRegexOptions));
|
||||||
|
|
||||||
private static readonly Lazy<MatchEvaluator> SplitCamelCaseString = new Lazy<MatchEvaluator>(() =>
|
private static readonly Lazy<MatchEvaluator> SplitCamelCaseString = new Lazy<MatchEvaluator>(() => m => {
|
||||||
{
|
String x = m.ToString();
|
||||||
return m =>
|
|
||||||
{
|
|
||||||
var x = m.ToString();
|
|
||||||
return x[0] + " " + x.Substring(1, x.Length - 1);
|
return x[0] + " " + x.Substring(1, x.Length - 1);
|
||||||
};
|
|
||||||
});
|
});
|
||||||
|
|
||||||
private static readonly Lazy<string[]> InvalidFilenameChars =
|
private static readonly Lazy<String[]> InvalidFilenameChars =
|
||||||
new Lazy<string[]>(() => Path.GetInvalidFileNameChars().Select(c => c.ToString()).ToArray());
|
new Lazy<String[]>(() => Path.GetInvalidFileNameChars().Select(c => c.ToString()).ToArray());
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
@ -63,32 +57,33 @@
|
|||||||
/// The computed hash code.
|
/// The computed hash code.
|
||||||
/// </returns>
|
/// </returns>
|
||||||
/// <exception cref="ArgumentNullException">stream.</exception>
|
/// <exception cref="ArgumentNullException">stream.</exception>
|
||||||
public static byte[] ComputeMD5(this Stream stream, bool createHasher = false)
|
[System.Diagnostics.CodeAnalysis.SuppressMessage("Codequalität", "IDE0067:Objekte verwerfen, bevor Bereich verloren geht", Justification = "<Ausstehend>")]
|
||||||
{
|
public static Byte[] ComputeMD5(this Stream stream, Boolean createHasher = false) {
|
||||||
if (stream == null)
|
if(stream == null) {
|
||||||
throw new ArgumentNullException(nameof(stream));
|
throw new ArgumentNullException(nameof(stream));
|
||||||
|
}
|
||||||
|
|
||||||
#if NET452
|
#if NET452
|
||||||
var md5 = MD5.Create();
|
MD5 md5 = MD5.Create();
|
||||||
const int bufferSize = 4096;
|
const Int32 bufferSize = 4096;
|
||||||
|
|
||||||
var readAheadBuffer = new byte[bufferSize];
|
Byte[] readAheadBuffer = new Byte[bufferSize];
|
||||||
var readAheadBytesRead = stream.Read(readAheadBuffer, 0, readAheadBuffer.Length);
|
Int32 readAheadBytesRead = stream.Read(readAheadBuffer, 0, readAheadBuffer.Length);
|
||||||
|
|
||||||
do
|
do {
|
||||||
{
|
Int32 bytesRead = readAheadBytesRead;
|
||||||
var bytesRead = readAheadBytesRead;
|
Byte[] buffer = readAheadBuffer;
|
||||||
var buffer = readAheadBuffer;
|
|
||||||
|
|
||||||
readAheadBuffer = new byte[bufferSize];
|
readAheadBuffer = new Byte[bufferSize];
|
||||||
readAheadBytesRead = stream.Read(readAheadBuffer, 0, readAheadBuffer.Length);
|
readAheadBytesRead = stream.Read(readAheadBuffer, 0, readAheadBuffer.Length);
|
||||||
|
|
||||||
if (readAheadBytesRead == 0)
|
if(readAheadBytesRead == 0) {
|
||||||
md5.TransformFinalBlock(buffer, 0, bytesRead);
|
md5.TransformFinalBlock(buffer, 0, bytesRead);
|
||||||
else
|
} else {
|
||||||
md5.TransformBlock(buffer, 0, bytesRead, buffer, 0);
|
md5.TransformBlock(buffer, 0, bytesRead, buffer, 0);
|
||||||
}
|
}
|
||||||
while (readAheadBytesRead != 0);
|
}
|
||||||
|
while(readAheadBytesRead != 0);
|
||||||
|
|
||||||
return md5.Hash;
|
return md5.Hash;
|
||||||
#else
|
#else
|
||||||
@ -108,18 +103,21 @@
|
|||||||
/// <param name="value">The input string.</param>
|
/// <param name="value">The input string.</param>
|
||||||
/// <param name="createHasher">if set to <c>true</c> [create hasher].</param>
|
/// <param name="createHasher">if set to <c>true</c> [create hasher].</param>
|
||||||
/// <returns>The computed hash code.</returns>
|
/// <returns>The computed hash code.</returns>
|
||||||
public static byte[] ComputeMD5(this string value, bool createHasher = false) =>
|
public static Byte[] ComputeMD5(this String value, Boolean createHasher = false) =>
|
||||||
Encoding.UTF8.GetBytes(value).ComputeMD5(createHasher);
|
Encoding.UTF8.GetBytes(value).ComputeMD5(createHasher);
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Computes the MD5 hash of the given byte array.
|
/// Computes the MD5 hash of the given byte array.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="data">The data.</param>
|
/// <param name="data">The data.</param>
|
||||||
/// <param name="createHasher">if set to <c>true</c> [create hasher].</param>
|
/// <param name="createHasher">if set to <c>true</c> [create hasher].</param>
|
||||||
/// <returns>The computed hash code.</returns>
|
/// <returns>The computed hash code.</returns>
|
||||||
public static byte[] ComputeMD5(this byte[] data, bool createHasher = false) =>
|
[System.Diagnostics.CodeAnalysis.SuppressMessage("Codequalität", "IDE0067:Objekte verwerfen, bevor Bereich verloren geht", Justification = "<Ausstehend>")]
|
||||||
|
public static Byte[] ComputeMD5(this Byte[] data, Boolean createHasher = false) =>
|
||||||
(createHasher ? MD5.Create() : Md5Hasher.Value).ComputeHash(data);
|
(createHasher ? MD5.Create() : Md5Hasher.Value).ComputeHash(data);
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Computes the SHA-1 hash of the given string using UTF8 byte encoding.
|
/// Computes the SHA-1 hash of the given string using UTF8 byte encoding.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -129,12 +127,13 @@
|
|||||||
/// The computes a Hash-based Message Authentication Code (HMAC)
|
/// The computes a Hash-based Message Authentication Code (HMAC)
|
||||||
/// using the SHA1 hash function.
|
/// using the SHA1 hash function.
|
||||||
/// </returns>
|
/// </returns>
|
||||||
public static byte[] ComputeSha1(this string value, bool createHasher = false)
|
[System.Diagnostics.CodeAnalysis.SuppressMessage("Codequalität", "IDE0067:Objekte verwerfen, bevor Bereich verloren geht", Justification = "<Ausstehend>")]
|
||||||
{
|
public static Byte[] ComputeSha1(this String value, Boolean createHasher = false) {
|
||||||
var inputBytes = Encoding.UTF8.GetBytes(value);
|
Byte[] inputBytes = Encoding.UTF8.GetBytes(value);
|
||||||
return (createHasher ? SHA1.Create() : SHA1Hasher.Value).ComputeHash(inputBytes);
|
return (createHasher ? SHA1.Create() : SHA1Hasher.Value).ComputeHash(inputBytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Computes the SHA-256 hash of the given string using UTF8 byte encoding.
|
/// Computes the SHA-256 hash of the given string using UTF8 byte encoding.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -144,12 +143,13 @@
|
|||||||
/// The computes a Hash-based Message Authentication Code (HMAC)
|
/// The computes a Hash-based Message Authentication Code (HMAC)
|
||||||
/// by using the SHA256 hash function.
|
/// by using the SHA256 hash function.
|
||||||
/// </returns>
|
/// </returns>
|
||||||
public static byte[] ComputeSha256(this string value, bool createHasher = false)
|
[System.Diagnostics.CodeAnalysis.SuppressMessage("Codequalität", "IDE0067:Objekte verwerfen, bevor Bereich verloren geht", Justification = "<Ausstehend>")]
|
||||||
{
|
public static Byte[] ComputeSha256(this String value, Boolean createHasher = false) {
|
||||||
var inputBytes = Encoding.UTF8.GetBytes(value);
|
Byte[] inputBytes = Encoding.UTF8.GetBytes(value);
|
||||||
return (createHasher ? SHA256.Create() : SHA256Hasher.Value).ComputeHash(inputBytes);
|
return (createHasher ? SHA256.Create() : SHA256Hasher.Value).ComputeHash(inputBytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Computes the SHA-512 hash of the given string using UTF8 byte encoding.
|
/// Computes the SHA-512 hash of the given string using UTF8 byte encoding.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -159,9 +159,9 @@
|
|||||||
/// The computes a Hash-based Message Authentication Code (HMAC)
|
/// The computes a Hash-based Message Authentication Code (HMAC)
|
||||||
/// using the SHA512 hash function.
|
/// using the SHA512 hash function.
|
||||||
/// </returns>
|
/// </returns>
|
||||||
public static byte[] ComputeSha512(this string value, bool createHasher = false)
|
[System.Diagnostics.CodeAnalysis.SuppressMessage("Codequalität", "IDE0067:Objekte verwerfen, bevor Bereich verloren geht", Justification = "<Ausstehend>")]
|
||||||
{
|
public static Byte[] ComputeSha512(this String value, Boolean createHasher = false) {
|
||||||
var inputBytes = Encoding.UTF8.GetBytes(value);
|
Byte[] inputBytes = Encoding.UTF8.GetBytes(value);
|
||||||
return (createHasher ? SHA512.Create() : SHA512Hasher.Value).ComputeHash(inputBytes);
|
return (createHasher ? SHA512.Create() : SHA512Hasher.Value).ComputeHash(inputBytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -172,17 +172,16 @@
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="obj">The item.</param>
|
/// <param name="obj">The item.</param>
|
||||||
/// <returns>A <see cref="System.String" /> that represents the current object.</returns>
|
/// <returns>A <see cref="System.String" /> that represents the current object.</returns>
|
||||||
public static string ToStringInvariant(this object obj)
|
public static String ToStringInvariant(this Object obj) {
|
||||||
{
|
if(obj == null) {
|
||||||
if (obj == null)
|
return String.Empty;
|
||||||
return string.Empty;
|
}
|
||||||
|
|
||||||
var itemType = obj.GetType();
|
Type itemType = obj.GetType();
|
||||||
|
|
||||||
if (itemType == typeof(string))
|
return itemType == typeof(String)
|
||||||
return obj as string;
|
? obj as String
|
||||||
|
: Definitions.BasicTypesInfo.ContainsKey(itemType)
|
||||||
return Definitions.BasicTypesInfo.ContainsKey(itemType)
|
|
||||||
? Definitions.BasicTypesInfo[itemType].ToStringInvariant(obj)
|
? Definitions.BasicTypesInfo[itemType].ToStringInvariant(obj)
|
||||||
: obj.ToString();
|
: obj.ToString();
|
||||||
}
|
}
|
||||||
@ -195,13 +194,7 @@
|
|||||||
/// <typeparam name="T">The type to get the string.</typeparam>
|
/// <typeparam name="T">The type to get the string.</typeparam>
|
||||||
/// <param name="item">The item.</param>
|
/// <param name="item">The item.</param>
|
||||||
/// <returns>A <see cref="System.String" /> that represents the current object.</returns>
|
/// <returns>A <see cref="System.String" /> that represents the current object.</returns>
|
||||||
public static string ToStringInvariant<T>(this T item)
|
public static String ToStringInvariant<T>(this T item) => typeof(String) == typeof(T) ? Equals(item, default(T)) ? String.Empty : item as String : ToStringInvariant(item as Object);
|
||||||
{
|
|
||||||
if (typeof(string) == typeof(T))
|
|
||||||
return Equals(item, default(T)) ? string.Empty : item as string;
|
|
||||||
|
|
||||||
return ToStringInvariant(item as object);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Removes the control characters from a string except for those specified.
|
/// Removes the control characters from a string except for those specified.
|
||||||
@ -212,16 +205,17 @@
|
|||||||
/// A string that represents the current object.
|
/// A string that represents the current object.
|
||||||
/// </returns>
|
/// </returns>
|
||||||
/// <exception cref="ArgumentNullException">input.</exception>
|
/// <exception cref="ArgumentNullException">input.</exception>
|
||||||
public static string RemoveControlCharsExcept(this string value, params char[] excludeChars)
|
public static String RemoveControlCharsExcept(this String value, params Char[] excludeChars) {
|
||||||
{
|
if(value == null) {
|
||||||
if (value == null)
|
|
||||||
throw new ArgumentNullException(nameof(value));
|
throw new ArgumentNullException(nameof(value));
|
||||||
|
}
|
||||||
|
|
||||||
if (excludeChars == null)
|
if(excludeChars == null) {
|
||||||
excludeChars = new char[] { };
|
excludeChars = new Char[] { };
|
||||||
|
}
|
||||||
|
|
||||||
return new string(value
|
return new String(value
|
||||||
.Where(c => char.IsControl(c) == false || excludeChars.Contains(c))
|
.Where(c => Char.IsControl(c) == false || excludeChars.Contains(c))
|
||||||
.ToArray());
|
.ToArray());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -231,7 +225,7 @@
|
|||||||
/// <param name="value">The input.</param>
|
/// <param name="value">The input.</param>
|
||||||
/// <returns>A <see cref="System.String" /> that represents the current object.</returns>
|
/// <returns>A <see cref="System.String" /> that represents the current object.</returns>
|
||||||
/// <exception cref="ArgumentNullException">input.</exception>
|
/// <exception cref="ArgumentNullException">input.</exception>
|
||||||
public static string RemoveControlChars(this string value) => value.RemoveControlCharsExcept(null);
|
public static String RemoveControlChars(this String value) => value.RemoveControlCharsExcept(null);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Outputs JSON string representing this object.
|
/// Outputs JSON string representing this object.
|
||||||
@ -239,8 +233,8 @@
|
|||||||
/// <param name="obj">The object.</param>
|
/// <param name="obj">The object.</param>
|
||||||
/// <param name="format">if set to <c>true</c> format the output.</param>
|
/// <param name="format">if set to <c>true</c> format the output.</param>
|
||||||
/// <returns>A <see cref="System.String" /> that represents the current object.</returns>
|
/// <returns>A <see cref="System.String" /> that represents the current object.</returns>
|
||||||
public static string ToJson(this object obj, bool format = true) =>
|
public static String ToJson(this Object obj, Boolean format = true) =>
|
||||||
obj == null ? string.Empty : Json.Serialize(obj, format);
|
obj == null ? String.Empty : Json.Serialize(obj, format);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Returns text representing the properties of the specified object in a human-readable format.
|
/// Returns text representing the properties of the specified object in a human-readable format.
|
||||||
@ -249,20 +243,17 @@
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="obj">The object.</param>
|
/// <param name="obj">The object.</param>
|
||||||
/// <returns>A <see cref="System.String" /> that represents the current object.</returns>
|
/// <returns>A <see cref="System.String" /> that represents the current object.</returns>
|
||||||
public static string Stringify(this object obj)
|
public static String Stringify(this Object obj) {
|
||||||
{
|
if(obj == null) {
|
||||||
if (obj == null)
|
|
||||||
return "(null)";
|
return "(null)";
|
||||||
|
}
|
||||||
|
|
||||||
try
|
try {
|
||||||
{
|
String jsonText = Json.Serialize(obj, false, "$type");
|
||||||
var jsonText = Json.Serialize(obj, false, "$type");
|
Object jsonData = Json.Deserialize(jsonText);
|
||||||
var jsonData = Json.Deserialize(jsonText);
|
|
||||||
|
|
||||||
return new HumanizeJson(jsonData, 0).GetResult();
|
return new HumanizeJson(jsonData, 0).GetResult();
|
||||||
}
|
} catch {
|
||||||
catch
|
|
||||||
{
|
|
||||||
return obj.ToStringInvariant();
|
return obj.ToStringInvariant();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -276,14 +267,14 @@
|
|||||||
/// <param name="startIndex">The start index.</param>
|
/// <param name="startIndex">The start index.</param>
|
||||||
/// <param name="endIndex">The end index.</param>
|
/// <param name="endIndex">The end index.</param>
|
||||||
/// <returns>Retrieves a substring from this instance.</returns>
|
/// <returns>Retrieves a substring from this instance.</returns>
|
||||||
public static string Slice(this string value, int startIndex, int endIndex)
|
public static String Slice(this String value, Int32 startIndex, Int32 endIndex) {
|
||||||
{
|
if(value == null) {
|
||||||
if (value == null)
|
return String.Empty;
|
||||||
return string.Empty;
|
}
|
||||||
|
|
||||||
var end = endIndex.Clamp(startIndex, value.Length - 1);
|
Int32 end = endIndex.Clamp(startIndex, value.Length - 1);
|
||||||
|
|
||||||
return startIndex >= end ? string.Empty : value.Substring(startIndex, (end - startIndex) + 1);
|
return startIndex >= end ? String.Empty : value.Substring(startIndex, end - startIndex + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -295,15 +286,15 @@
|
|||||||
/// <param name="startIndex">The start index.</param>
|
/// <param name="startIndex">The start index.</param>
|
||||||
/// <param name="length">The length.</param>
|
/// <param name="length">The length.</param>
|
||||||
/// <returns>Retrieves a substring from this instance.</returns>
|
/// <returns>Retrieves a substring from this instance.</returns>
|
||||||
public static string SliceLength(this string str, int startIndex, int length)
|
public static String SliceLength(this String str, Int32 startIndex, Int32 length) {
|
||||||
{
|
if(str == null) {
|
||||||
if (str == null)
|
return String.Empty;
|
||||||
return string.Empty;
|
}
|
||||||
|
|
||||||
var start = startIndex.Clamp(0, str.Length - 1);
|
Int32 start = startIndex.Clamp(0, str.Length - 1);
|
||||||
var len = length.Clamp(0, str.Length - start);
|
Int32 len = length.Clamp(0, str.Length - start);
|
||||||
|
|
||||||
return len == 0 ? string.Empty : str.Substring(start, len);
|
return len == 0 ? String.Empty : str.Substring(start, len);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -314,8 +305,8 @@
|
|||||||
/// An array whose elements contain the substrings from this instance
|
/// An array whose elements contain the substrings from this instance
|
||||||
/// that are delimited by one or more characters in separator.
|
/// that are delimited by one or more characters in separator.
|
||||||
/// </returns>
|
/// </returns>
|
||||||
public static string[] ToLines(this string value) =>
|
public static String[] ToLines(this String value) =>
|
||||||
value == null ? new string[] { } : SplitLinesRegex.Value.Split(value);
|
value == null ? new String[] { } : SplitLinesRegex.Value.Split(value);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Humanizes (make more human-readable) an identifier-style string
|
/// Humanizes (make more human-readable) an identifier-style string
|
||||||
@ -324,12 +315,12 @@
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="value">The identifier-style string.</param>
|
/// <param name="value">The identifier-style string.</param>
|
||||||
/// <returns>A <see cref="System.String" /> that represents the current object.</returns>
|
/// <returns>A <see cref="System.String" /> that represents the current object.</returns>
|
||||||
public static string Humanize(this string value)
|
public static String Humanize(this String value) {
|
||||||
{
|
if(value == null) {
|
||||||
if (value == null)
|
return String.Empty;
|
||||||
return string.Empty;
|
}
|
||||||
|
|
||||||
var returnValue = UnderscoreRegex.Value.Replace(value, " ");
|
String returnValue = UnderscoreRegex.Value.Replace(value, " ");
|
||||||
returnValue = CamelCaseRegEx.Value.Replace(returnValue, SplitCamelCaseString.Value);
|
returnValue = CamelCaseRegEx.Value.Replace(returnValue, SplitCamelCaseString.Value);
|
||||||
return returnValue;
|
return returnValue;
|
||||||
}
|
}
|
||||||
@ -341,18 +332,21 @@
|
|||||||
/// <param name="value">The text.</param>
|
/// <param name="value">The text.</param>
|
||||||
/// <param name="spaces">The spaces.</param>
|
/// <param name="spaces">The spaces.</param>
|
||||||
/// <returns>A <see cref="System.String" /> that represents the current object.</returns>
|
/// <returns>A <see cref="System.String" /> that represents the current object.</returns>
|
||||||
public static string Indent(this string value, int spaces = 4)
|
public static String Indent(this String value, Int32 spaces = 4) {
|
||||||
{
|
if(value == null) {
|
||||||
if (value == null) value = string.Empty;
|
value = String.Empty;
|
||||||
if (spaces <= 0) return value;
|
}
|
||||||
|
|
||||||
var lines = value.ToLines();
|
if(spaces <= 0) {
|
||||||
var builder = new StringBuilder();
|
return value;
|
||||||
var indentStr = new string(' ', spaces);
|
}
|
||||||
|
|
||||||
foreach (var line in lines)
|
String[] lines = value.ToLines();
|
||||||
{
|
StringBuilder builder = new StringBuilder();
|
||||||
builder.AppendLine($"{indentStr}{line}");
|
String indentStr = new String(' ', spaces);
|
||||||
|
|
||||||
|
foreach(String line in lines) {
|
||||||
|
_ = builder.AppendLine($"{indentStr}{line}");
|
||||||
}
|
}
|
||||||
|
|
||||||
return builder.ToString().TrimEnd();
|
return builder.ToString().TrimEnd();
|
||||||
@ -368,28 +362,27 @@
|
|||||||
/// <param name="value">The string.</param>
|
/// <param name="value">The string.</param>
|
||||||
/// <param name="charIndex">Index of the character.</param>
|
/// <param name="charIndex">Index of the character.</param>
|
||||||
/// <returns>A 2-tuple whose value is (item1, item2).</returns>
|
/// <returns>A 2-tuple whose value is (item1, item2).</returns>
|
||||||
public static Tuple<int, int> TextPositionAt(this string value, int charIndex)
|
public static Tuple<Int32, Int32> TextPositionAt(this String value, Int32 charIndex) {
|
||||||
{
|
if(value == null) {
|
||||||
if (value == null)
|
|
||||||
return Tuple.Create(0, 0);
|
return Tuple.Create(0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
var index = charIndex.Clamp(0, value.Length - 1);
|
Int32 index = charIndex.Clamp(0, value.Length - 1);
|
||||||
|
|
||||||
var lineIndex = 0;
|
Int32 lineIndex = 0;
|
||||||
var colNumber = 0;
|
Int32 colNumber = 0;
|
||||||
|
|
||||||
for (var i = 0; i <= index; i++)
|
for(Int32 i = 0; i <= index; i++) {
|
||||||
{
|
if(value[i] == '\n') {
|
||||||
if (value[i] == '\n')
|
|
||||||
{
|
|
||||||
lineIndex++;
|
lineIndex++;
|
||||||
colNumber = 0;
|
colNumber = 0;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (value[i] != '\r')
|
if(value[i] != '\r') {
|
||||||
colNumber++;
|
colNumber++;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return Tuple.Create(lineIndex + 1, colNumber);
|
return Tuple.Create(lineIndex + 1, colNumber);
|
||||||
}
|
}
|
||||||
@ -402,14 +395,11 @@
|
|||||||
/// A string with a safe file name.
|
/// A string with a safe file name.
|
||||||
/// </returns>
|
/// </returns>
|
||||||
/// <exception cref="ArgumentNullException">s.</exception>
|
/// <exception cref="ArgumentNullException">s.</exception>
|
||||||
public static string ToSafeFilename(this string value)
|
public static String ToSafeFilename(this String value) => value == null
|
||||||
{
|
|
||||||
return value == null
|
|
||||||
? throw new ArgumentNullException(nameof(value))
|
? throw new ArgumentNullException(nameof(value))
|
||||||
: InvalidFilenameChars.Value
|
: InvalidFilenameChars.Value
|
||||||
.Aggregate(value, (current, c) => current.Replace(c, string.Empty))
|
.Aggregate(value, (current, c) => current.Replace(c, String.Empty))
|
||||||
.Slice(0, 220);
|
.Slice(0, 220);
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Formats a long into the closest bytes string.
|
/// Formats a long into the closest bytes string.
|
||||||
@ -418,7 +408,7 @@
|
|||||||
/// <returns>
|
/// <returns>
|
||||||
/// The string representation of the current Byte object, formatted as specified by the format parameter.
|
/// The string representation of the current Byte object, formatted as specified by the format parameter.
|
||||||
/// </returns>
|
/// </returns>
|
||||||
public static string FormatBytes(this long bytes) => ((ulong) bytes).FormatBytes();
|
public static String FormatBytes(this Int64 bytes) => ((UInt64)bytes).FormatBytes();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Formats a long into the closest bytes string.
|
/// Formats a long into the closest bytes string.
|
||||||
@ -428,13 +418,11 @@
|
|||||||
/// A copy of format in which the format items have been replaced by the string
|
/// A copy of format in which the format items have been replaced by the string
|
||||||
/// representations of the corresponding arguments.
|
/// representations of the corresponding arguments.
|
||||||
/// </returns>
|
/// </returns>
|
||||||
public static string FormatBytes(this ulong bytes)
|
public static String FormatBytes(this UInt64 bytes) {
|
||||||
{
|
Int32 i;
|
||||||
int i;
|
Double dblSByte = bytes;
|
||||||
double dblSByte = bytes;
|
|
||||||
|
|
||||||
for (i = 0; i < ByteSuffixes.Length && bytes >= 1024; i++, bytes /= 1024)
|
for(i = 0; i < ByteSuffixes.Length && bytes >= 1024; i++, bytes /= 1024) {
|
||||||
{
|
|
||||||
dblSByte = bytes / 1024.0;
|
dblSByte = bytes / 1024.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -450,8 +438,8 @@
|
|||||||
/// Retrieves a substring from this instance.
|
/// Retrieves a substring from this instance.
|
||||||
/// The substring starts at a specified character position and has a specified length.
|
/// The substring starts at a specified character position and has a specified length.
|
||||||
/// </returns>
|
/// </returns>
|
||||||
public static string Truncate(this string value, int maximumLength) =>
|
public static String Truncate(this String value, Int32 maximumLength) =>
|
||||||
Truncate(value, maximumLength, string.Empty);
|
Truncate(value, maximumLength, String.Empty);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Truncates the specified value and append the omission last.
|
/// Truncates the specified value and append the omission last.
|
||||||
@ -463,32 +451,28 @@
|
|||||||
/// Retrieves a substring from this instance.
|
/// Retrieves a substring from this instance.
|
||||||
/// The substring starts at a specified character position and has a specified length.
|
/// The substring starts at a specified character position and has a specified length.
|
||||||
/// </returns>
|
/// </returns>
|
||||||
public static string Truncate(this string value, int maximumLength, string omission)
|
public static String Truncate(this String value, Int32 maximumLength, String omission) => value == null
|
||||||
{
|
? null
|
||||||
if (value == null)
|
: value.Length > maximumLength
|
||||||
return null;
|
? value.Substring(0, maximumLength) + (omission ?? String.Empty)
|
||||||
|
|
||||||
return value.Length > maximumLength
|
|
||||||
? value.Substring(0, maximumLength) + (omission ?? string.Empty)
|
|
||||||
: value;
|
: value;
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Determines whether the specified <see cref="string"/> contains any of characters in
|
/// Determines whether the specified <see cref="String"/> contains any of characters in
|
||||||
/// the specified array of <see cref="char"/>.
|
/// the specified array of <see cref="Char"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>
|
/// <returns>
|
||||||
/// <c>true</c> if <paramref name="value"/> contains any of <paramref name="chars"/>;
|
/// <c>true</c> if <paramref name="value"/> contains any of <paramref name="chars"/>;
|
||||||
/// otherwise, <c>false</c>.
|
/// otherwise, <c>false</c>.
|
||||||
/// </returns>
|
/// </returns>
|
||||||
/// <param name="value">
|
/// <param name="value">
|
||||||
/// A <see cref="string"/> to test.
|
/// A <see cref="String"/> to test.
|
||||||
/// </param>
|
/// </param>
|
||||||
/// <param name="chars">
|
/// <param name="chars">
|
||||||
/// An array of <see cref="char"/> that contains characters to find.
|
/// An array of <see cref="Char"/> that contains characters to find.
|
||||||
/// </param>
|
/// </param>
|
||||||
public static bool Contains(this string value, params char[] chars) =>
|
public static Boolean Contains(this String value, params Char[] chars) =>
|
||||||
chars?.Length == 0 || (!string.IsNullOrEmpty(value) && value.IndexOfAny(chars) > -1);
|
chars?.Length == 0 || !String.IsNullOrEmpty(value) && value.IndexOfAny(chars) > -1;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Replaces all chars in a string.
|
/// Replaces all chars in a string.
|
||||||
@ -497,8 +481,8 @@
|
|||||||
/// <param name="replaceValue">The replace value.</param>
|
/// <param name="replaceValue">The replace value.</param>
|
||||||
/// <param name="chars">The chars.</param>
|
/// <param name="chars">The chars.</param>
|
||||||
/// <returns>The string with the characters replaced.</returns>
|
/// <returns>The string with the characters replaced.</returns>
|
||||||
public static string ReplaceAll(this string value, string replaceValue, params char[] chars) =>
|
public static String ReplaceAll(this String value, String replaceValue, params Char[] chars) =>
|
||||||
chars.Aggregate(value, (current, c) => current.Replace(new string(new[] {c}), replaceValue));
|
chars.Aggregate(value, (current, c) => current.Replace(new String(new[] { c }), replaceValue));
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Convert hex character to an integer. Return -1 if char is something
|
/// Convert hex character to an integer. Return -1 if char is something
|
||||||
@ -506,9 +490,7 @@
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="value">The c.</param>
|
/// <param name="value">The c.</param>
|
||||||
/// <returns>Converted integer.</returns>
|
/// <returns>Converted integer.</returns>
|
||||||
public static int Hex2Int(this char value)
|
public static Int32 Hex2Int(this Char value) => value >= '0' && value <= '9'
|
||||||
{
|
|
||||||
return value >= '0' && value <= '9'
|
|
||||||
? value - '0'
|
? value - '0'
|
||||||
: value >= 'A' && value <= 'F'
|
: value >= 'A' && value <= 'F'
|
||||||
? value - 'A' + 10
|
? value - 'A' + 10
|
||||||
@ -516,5 +498,4 @@
|
|||||||
? value - 'a' + 10
|
? value - 'a' + 10
|
||||||
: -1;
|
: -1;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
@ -1,15 +1,13 @@
|
|||||||
namespace Unosquare.Swan
|
using System;
|
||||||
{
|
using System.Reflection;
|
||||||
using System;
|
using System.Runtime.InteropServices;
|
||||||
using System.Reflection;
|
using Unosquare.Swan.Attributes;
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
using Attributes;
|
|
||||||
|
|
||||||
|
namespace Unosquare.Swan {
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Provides various extension methods for value types and structs.
|
/// Provides various extension methods for value types and structs.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static class ValueTypeExtensions
|
public static class ValueTypeExtensions {
|
||||||
{
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Clamps the specified value between the minimum and the maximum.
|
/// Clamps the specified value between the minimum and the maximum.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -19,12 +17,7 @@
|
|||||||
/// <param name="max">The maximum.</param>
|
/// <param name="max">The maximum.</param>
|
||||||
/// <returns>A value that indicates the relative order of the objects being compared.</returns>
|
/// <returns>A value that indicates the relative order of the objects being compared.</returns>
|
||||||
public static T Clamp<T>(this T value, T min, T max)
|
public static T Clamp<T>(this T value, T min, T max)
|
||||||
where T : struct, IComparable
|
where T : struct, IComparable => value.CompareTo(min) < 0 ? min : value.CompareTo(max) > 0 ? max : value;
|
||||||
{
|
|
||||||
if (value.CompareTo(min) < 0) return min;
|
|
||||||
|
|
||||||
return value.CompareTo(max) > 0 ? max : value;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Clamps the specified value between the minimum and the maximum.
|
/// Clamps the specified value between the minimum and the maximum.
|
||||||
@ -33,7 +26,7 @@
|
|||||||
/// <param name="min">The minimum.</param>
|
/// <param name="min">The minimum.</param>
|
||||||
/// <param name="max">The maximum.</param>
|
/// <param name="max">The maximum.</param>
|
||||||
/// <returns>A value that indicates the relative order of the objects being compared.</returns>
|
/// <returns>A value that indicates the relative order of the objects being compared.</returns>
|
||||||
public static int Clamp(this int value, int min, int max)
|
public static Int32 Clamp(this Int32 value, Int32 min, Int32 max)
|
||||||
=> value < min ? min : (value > max ? max : value);
|
=> value < min ? min : (value > max ? max : value);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -46,11 +39,8 @@
|
|||||||
/// <returns>
|
/// <returns>
|
||||||
/// <c>true</c> if the specified minimum is between; otherwise, <c>false</c>.
|
/// <c>true</c> if the specified minimum is between; otherwise, <c>false</c>.
|
||||||
/// </returns>
|
/// </returns>
|
||||||
public static bool IsBetween<T>(this T value, T min, T max)
|
public static Boolean IsBetween<T>(this T value, T min, T max)
|
||||||
where T : struct, IComparable
|
where T : struct, IComparable => value.CompareTo(min) >= 0 && value.CompareTo(max) <= 0;
|
||||||
{
|
|
||||||
return value.CompareTo(min) >= 0 && value.CompareTo(max) <= 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Converts an array of bytes into the given struct type.
|
/// Converts an array of bytes into the given struct type.
|
||||||
@ -58,11 +48,8 @@
|
|||||||
/// <typeparam name="T">The type of structure to convert.</typeparam>
|
/// <typeparam name="T">The type of structure to convert.</typeparam>
|
||||||
/// <param name="data">The data.</param>
|
/// <param name="data">The data.</param>
|
||||||
/// <returns>a struct type derived from convert an array of bytes ref=ToStruct".</returns>
|
/// <returns>a struct type derived from convert an array of bytes ref=ToStruct".</returns>
|
||||||
public static T ToStruct<T>(this byte[] data)
|
public static T ToStruct<T>(this Byte[] data)
|
||||||
where T : struct
|
where T : struct => ToStruct<T>(data, 0, data.Length);
|
||||||
{
|
|
||||||
return ToStruct<T>(data, 0, data.Length);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Converts an array of bytes into the given struct type.
|
/// Converts an array of bytes into the given struct type.
|
||||||
@ -75,22 +62,19 @@
|
|||||||
/// A managed object containing the data pointed to by the ptr parameter.
|
/// A managed object containing the data pointed to by the ptr parameter.
|
||||||
/// </returns>
|
/// </returns>
|
||||||
/// <exception cref="ArgumentNullException">data.</exception>
|
/// <exception cref="ArgumentNullException">data.</exception>
|
||||||
public static T ToStruct<T>(this byte[] data, int offset, int length)
|
public static T ToStruct<T>(this Byte[] data, Int32 offset, Int32 length)
|
||||||
where T : struct
|
where T : struct {
|
||||||
{
|
if(data == null) {
|
||||||
if (data == null)
|
|
||||||
throw new ArgumentNullException(nameof(data));
|
throw new ArgumentNullException(nameof(data));
|
||||||
|
|
||||||
var buffer = new byte[length];
|
|
||||||
Array.Copy(data, offset, buffer, 0, buffer.Length);
|
|
||||||
var handle = GCHandle.Alloc(GetStructBytes<T>(buffer), GCHandleType.Pinned);
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
return Marshal.PtrToStructure<T>(handle.AddrOfPinnedObject());
|
|
||||||
}
|
}
|
||||||
finally
|
|
||||||
{
|
Byte[] buffer = new Byte[length];
|
||||||
|
Array.Copy(data, offset, buffer, 0, buffer.Length);
|
||||||
|
GCHandle handle = GCHandle.Alloc(GetStructBytes<T>(buffer), GCHandleType.Pinned);
|
||||||
|
|
||||||
|
try {
|
||||||
|
return Marshal.PtrToStructure<T>(handle.AddrOfPinnedObject());
|
||||||
|
} finally {
|
||||||
handle.Free();
|
handle.Free();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -101,19 +85,15 @@
|
|||||||
/// <typeparam name="T">The type of structure to convert.</typeparam>
|
/// <typeparam name="T">The type of structure to convert.</typeparam>
|
||||||
/// <param name="obj">The object.</param>
|
/// <param name="obj">The object.</param>
|
||||||
/// <returns>A byte array containing the results of encoding the specified set of characters.</returns>
|
/// <returns>A byte array containing the results of encoding the specified set of characters.</returns>
|
||||||
public static byte[] ToBytes<T>(this T obj)
|
public static Byte[] ToBytes<T>(this T obj)
|
||||||
where T : struct
|
where T : struct {
|
||||||
{
|
Byte[] data = new Byte[Marshal.SizeOf(obj)];
|
||||||
var data = new byte[Marshal.SizeOf(obj)];
|
GCHandle handle = GCHandle.Alloc(data, GCHandleType.Pinned);
|
||||||
var handle = GCHandle.Alloc(data, GCHandleType.Pinned);
|
|
||||||
|
|
||||||
try
|
try {
|
||||||
{
|
|
||||||
Marshal.StructureToPtr(obj, handle.AddrOfPinnedObject(), false);
|
Marshal.StructureToPtr(obj, handle.AddrOfPinnedObject(), false);
|
||||||
return GetStructBytes<T>(data);
|
return GetStructBytes<T>(data);
|
||||||
}
|
} finally {
|
||||||
finally
|
|
||||||
{
|
|
||||||
handle.Free();
|
handle.Free();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -126,38 +106,37 @@
|
|||||||
/// A 32-bit unsigned integer equivalent to the ulong
|
/// A 32-bit unsigned integer equivalent to the ulong
|
||||||
/// contained in longBytes.
|
/// contained in longBytes.
|
||||||
/// </returns>
|
/// </returns>
|
||||||
public static uint SwapEndianness(this ulong longBytes)
|
public static UInt32 SwapEndianness(this UInt64 longBytes)
|
||||||
=> (uint) (((longBytes & 0x000000ff) << 24) +
|
=> (UInt32)(((longBytes & 0x000000ff) << 24) +
|
||||||
((longBytes & 0x0000ff00) << 8) +
|
((longBytes & 0x0000ff00) << 8) +
|
||||||
((longBytes & 0x00ff0000) >> 8) +
|
((longBytes & 0x00ff0000) >> 8) +
|
||||||
((longBytes & 0xff000000) >> 24));
|
((longBytes & 0xff000000) >> 24));
|
||||||
|
|
||||||
private static byte[] GetStructBytes<T>(byte[] data)
|
private static Byte[] GetStructBytes<T>(Byte[] data) {
|
||||||
{
|
if(data == null) {
|
||||||
if (data == null)
|
|
||||||
throw new ArgumentNullException(nameof(data));
|
throw new ArgumentNullException(nameof(data));
|
||||||
|
}
|
||||||
|
|
||||||
#if !NETSTANDARD1_3
|
#if !NETSTANDARD1_3
|
||||||
var fields = typeof(T).GetTypeInfo()
|
FieldInfo[] fields = typeof(T).GetTypeInfo()
|
||||||
.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
|
.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
|
||||||
#else
|
#else
|
||||||
var fields = typeof(T).GetTypeInfo().DeclaredFields;
|
var fields = typeof(T).GetTypeInfo().DeclaredFields;
|
||||||
#endif
|
#endif
|
||||||
var endian = Runtime.AttributeCache.RetrieveOne<StructEndiannessAttribute, T>();
|
StructEndiannessAttribute endian = Runtime.AttributeCache.RetrieveOne<StructEndiannessAttribute, T>();
|
||||||
|
|
||||||
foreach (var field in fields)
|
foreach(FieldInfo field in fields) {
|
||||||
{
|
if(endian == null && !field.IsDefined(typeof(StructEndiannessAttribute), false)) {
|
||||||
if (endian == null && !field.IsDefined(typeof(StructEndiannessAttribute), false))
|
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
var offset = Marshal.OffsetOf<T>(field.Name).ToInt32();
|
Int32 offset = Marshal.OffsetOf<T>(field.Name).ToInt32();
|
||||||
var length = Marshal.SizeOf(field.FieldType);
|
Int32 length = Marshal.SizeOf(field.FieldType);
|
||||||
|
|
||||||
endian = endian ?? Runtime.AttributeCache.RetrieveOne<StructEndiannessAttribute>(field);
|
endian = endian ?? Runtime.AttributeCache.RetrieveOne<StructEndiannessAttribute>(field);
|
||||||
|
|
||||||
if (endian != null && (endian.Endianness == Endianness.Big && BitConverter.IsLittleEndian ||
|
if(endian != null && (endian.Endianness == Endianness.Big && BitConverter.IsLittleEndian ||
|
||||||
endian.Endianness == Endianness.Little && !BitConverter.IsLittleEndian))
|
endian.Endianness == Endianness.Little && !BitConverter.IsLittleEndian)) {
|
||||||
{
|
|
||||||
Array.Reverse(data, offset, length);
|
Array.Reverse(data, offset, length);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,19 +1,17 @@
|
|||||||
namespace Unosquare.Swan
|
using Unosquare.Swan.Attributes;
|
||||||
{
|
using System;
|
||||||
using Attributes;
|
using System.Collections;
|
||||||
using System;
|
using System.Collections.Generic;
|
||||||
using System.Collections;
|
using System.Diagnostics;
|
||||||
using System.Collections.Generic;
|
using System.Linq;
|
||||||
using System.Diagnostics;
|
using System.Reflection;
|
||||||
using System.Linq;
|
using System.Threading.Tasks;
|
||||||
using System.Reflection;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
|
namespace Unosquare.Swan {
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Extension methods.
|
/// Extension methods.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static partial class Extensions
|
public static partial class Extensions {
|
||||||
{
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Iterates over the public, instance, readable properties of the source and
|
/// Iterates over the public, instance, readable properties of the source and
|
||||||
/// tries to write a compatible value to a public, instance, writable property in the destination.
|
/// tries to write a compatible value to a public, instance, writable property in the destination.
|
||||||
@ -22,10 +20,9 @@
|
|||||||
/// <param name="source">The source.</param>
|
/// <param name="source">The source.</param>
|
||||||
/// <param name="target">The target.</param>
|
/// <param name="target">The target.</param>
|
||||||
/// <returns>Number of properties that was copied successful.</returns>
|
/// <returns>Number of properties that was copied successful.</returns>
|
||||||
public static int CopyPropertiesTo<T>(this T source, object target)
|
public static Int32 CopyPropertiesTo<T>(this T source, Object target)
|
||||||
where T : class
|
where T : class {
|
||||||
{
|
IEnumerable<String> copyable = GetCopyableProperties(target);
|
||||||
var copyable = GetCopyableProperties(target);
|
|
||||||
return copyable.Any()
|
return copyable.Any()
|
||||||
? CopyOnlyPropertiesTo(source, target, copyable.ToArray())
|
? CopyOnlyPropertiesTo(source, target, copyable.ToArray())
|
||||||
: CopyPropertiesTo(source, target, null);
|
: CopyPropertiesTo(source, target, null);
|
||||||
@ -41,7 +38,7 @@
|
|||||||
/// <returns>
|
/// <returns>
|
||||||
/// Number of properties that were successfully copied.
|
/// Number of properties that were successfully copied.
|
||||||
/// </returns>
|
/// </returns>
|
||||||
public static int CopyPropertiesTo(this object source, object target, string[] ignoreProperties = null)
|
public static Int32 CopyPropertiesTo(this Object source, Object target, String[] ignoreProperties = null)
|
||||||
=> Components.ObjectMapper.Copy(source, target, null, ignoreProperties);
|
=> Components.ObjectMapper.Copy(source, target, null, ignoreProperties);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -52,11 +49,8 @@
|
|||||||
/// <param name="source">The source.</param>
|
/// <param name="source">The source.</param>
|
||||||
/// <param name="target">The target.</param>
|
/// <param name="target">The target.</param>
|
||||||
/// <returns>Number of properties that was copied successful.</returns>
|
/// <returns>Number of properties that was copied successful.</returns>
|
||||||
public static int CopyOnlyPropertiesTo<T>(this T source, object target)
|
public static Int32 CopyOnlyPropertiesTo<T>(this T source, Object target)
|
||||||
where T : class
|
where T : class => CopyOnlyPropertiesTo(source, target, null);
|
||||||
{
|
|
||||||
return CopyOnlyPropertiesTo(source, target, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Iterates over the public, instance, readable properties of the source and
|
/// Iterates over the public, instance, readable properties of the source and
|
||||||
@ -68,10 +62,7 @@
|
|||||||
/// <returns>
|
/// <returns>
|
||||||
/// Number of properties that were successfully copied.
|
/// Number of properties that were successfully copied.
|
||||||
/// </returns>
|
/// </returns>
|
||||||
public static int CopyOnlyPropertiesTo(this object source, object target, string[] propertiesToCopy)
|
public static Int32 CopyOnlyPropertiesTo(this Object source, Object target, String[] propertiesToCopy) => Components.ObjectMapper.Copy(source, target, propertiesToCopy);
|
||||||
{
|
|
||||||
return Components.ObjectMapper.Copy(source, target, propertiesToCopy);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Copies the properties to new instance of T.
|
/// Copies the properties to new instance of T.
|
||||||
@ -83,11 +74,8 @@
|
|||||||
/// The specified type with properties copied.
|
/// The specified type with properties copied.
|
||||||
/// </returns>
|
/// </returns>
|
||||||
/// <exception cref="ArgumentNullException">source.</exception>
|
/// <exception cref="ArgumentNullException">source.</exception>
|
||||||
public static T DeepClone<T>(this T source, string[] ignoreProperties = null)
|
public static T DeepClone<T>(this T source, String[] ignoreProperties = null)
|
||||||
where T : class
|
where T : class => source.CopyPropertiesToNew<T>(ignoreProperties);
|
||||||
{
|
|
||||||
return source.CopyPropertiesToNew<T>(ignoreProperties);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Copies the properties to new instance of T.
|
/// Copies the properties to new instance of T.
|
||||||
@ -99,19 +87,16 @@
|
|||||||
/// The specified type with properties copied.
|
/// The specified type with properties copied.
|
||||||
/// </returns>
|
/// </returns>
|
||||||
/// <exception cref="ArgumentNullException">source.</exception>
|
/// <exception cref="ArgumentNullException">source.</exception>
|
||||||
public static T CopyPropertiesToNew<T>(this object source, string[] ignoreProperties = null)
|
public static T CopyPropertiesToNew<T>(this Object source, String[] ignoreProperties = null)
|
||||||
where T : class
|
where T : class {
|
||||||
{
|
if(source == null) {
|
||||||
if (source == null)
|
|
||||||
throw new ArgumentNullException(nameof(source));
|
throw new ArgumentNullException(nameof(source));
|
||||||
|
}
|
||||||
|
|
||||||
var target = Activator.CreateInstance<T>();
|
T target = Activator.CreateInstance<T>();
|
||||||
var copyable = target.GetCopyableProperties();
|
IEnumerable<String> copyable = target.GetCopyableProperties();
|
||||||
|
|
||||||
if (copyable.Any())
|
_ = copyable.Any() ? source.CopyOnlyPropertiesTo(target, copyable.ToArray()) : source.CopyPropertiesTo(target, ignoreProperties);
|
||||||
source.CopyOnlyPropertiesTo(target, copyable.ToArray());
|
|
||||||
else
|
|
||||||
source.CopyPropertiesTo(target, ignoreProperties);
|
|
||||||
|
|
||||||
return target;
|
return target;
|
||||||
}
|
}
|
||||||
@ -126,14 +111,14 @@
|
|||||||
/// The specified type with properties copied.
|
/// The specified type with properties copied.
|
||||||
/// </returns>
|
/// </returns>
|
||||||
/// <exception cref="ArgumentNullException">source.</exception>
|
/// <exception cref="ArgumentNullException">source.</exception>
|
||||||
public static T CopyOnlyPropertiesToNew<T>(this object source, string[] propertiesToCopy)
|
public static T CopyOnlyPropertiesToNew<T>(this Object source, String[] propertiesToCopy)
|
||||||
where T : class
|
where T : class {
|
||||||
{
|
if(source == null) {
|
||||||
if (source == null)
|
|
||||||
throw new ArgumentNullException(nameof(source));
|
throw new ArgumentNullException(nameof(source));
|
||||||
|
}
|
||||||
|
|
||||||
var target = Activator.CreateInstance<T>();
|
T target = Activator.CreateInstance<T>();
|
||||||
source.CopyOnlyPropertiesTo(target, propertiesToCopy);
|
_ = source.CopyOnlyPropertiesTo(target, propertiesToCopy);
|
||||||
return target;
|
return target;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -145,13 +130,10 @@
|
|||||||
/// <param name="target">The target.</param>
|
/// <param name="target">The target.</param>
|
||||||
/// <param name="ignoreKeys">The ignore keys.</param>
|
/// <param name="ignoreKeys">The ignore keys.</param>
|
||||||
/// <returns>Number of properties that was copied successful.</returns>
|
/// <returns>Number of properties that was copied successful.</returns>
|
||||||
public static int CopyKeyValuePairTo(
|
public static Int32 CopyKeyValuePairTo(
|
||||||
this IDictionary<string, object> source,
|
this IDictionary<String, Object> source,
|
||||||
object target,
|
Object target,
|
||||||
string[] ignoreKeys = null)
|
String[] ignoreKeys = null) => Components.ObjectMapper.Copy(source, target, null, ignoreKeys);
|
||||||
{
|
|
||||||
return Components.ObjectMapper.Copy(source, target, null, ignoreKeys);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Measures the elapsed time of the given action as a TimeSpan
|
/// Measures the elapsed time of the given action as a TimeSpan
|
||||||
@ -162,24 +144,19 @@
|
|||||||
/// A time interval that represents a specified time, where the specification is in units of ticks.
|
/// A time interval that represents a specified time, where the specification is in units of ticks.
|
||||||
/// </returns>
|
/// </returns>
|
||||||
/// <exception cref="ArgumentNullException">target.</exception>
|
/// <exception cref="ArgumentNullException">target.</exception>
|
||||||
public static TimeSpan Benchmark(this Action target)
|
public static TimeSpan Benchmark(this Action target) {
|
||||||
{
|
if(target == null) {
|
||||||
if (target == null)
|
|
||||||
throw new ArgumentNullException(nameof(target));
|
throw new ArgumentNullException(nameof(target));
|
||||||
|
}
|
||||||
|
|
||||||
var sw = new Stopwatch();
|
Stopwatch sw = new Stopwatch();
|
||||||
|
|
||||||
try
|
try {
|
||||||
{
|
|
||||||
sw.Start();
|
sw.Start();
|
||||||
target.Invoke();
|
target.Invoke();
|
||||||
}
|
} catch {
|
||||||
catch
|
|
||||||
{
|
|
||||||
// swallow
|
// swallow
|
||||||
}
|
} finally {
|
||||||
finally
|
|
||||||
{
|
|
||||||
sw.Stop();
|
sw.Stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -195,13 +172,12 @@
|
|||||||
public static void Retry(
|
public static void Retry(
|
||||||
this Action action,
|
this Action action,
|
||||||
TimeSpan retryInterval = default,
|
TimeSpan retryInterval = default,
|
||||||
int retryCount = 3)
|
Int32 retryCount = 3) {
|
||||||
{
|
if(action == null) {
|
||||||
if (action == null)
|
|
||||||
throw new ArgumentNullException(nameof(action));
|
throw new ArgumentNullException(nameof(action));
|
||||||
|
}
|
||||||
|
|
||||||
Retry<object>(() =>
|
_ = Retry<Object>(() => {
|
||||||
{
|
|
||||||
action();
|
action();
|
||||||
return null;
|
return null;
|
||||||
},
|
},
|
||||||
@ -224,27 +200,25 @@
|
|||||||
public static T Retry<T>(
|
public static T Retry<T>(
|
||||||
this Func<T> action,
|
this Func<T> action,
|
||||||
TimeSpan retryInterval = default,
|
TimeSpan retryInterval = default,
|
||||||
int retryCount = 3)
|
Int32 retryCount = 3) {
|
||||||
{
|
if(action == null) {
|
||||||
if (action == null)
|
|
||||||
throw new ArgumentNullException(nameof(action));
|
throw new ArgumentNullException(nameof(action));
|
||||||
|
}
|
||||||
|
|
||||||
if (retryInterval == default)
|
if(retryInterval == default) {
|
||||||
retryInterval = TimeSpan.FromSeconds(1);
|
retryInterval = TimeSpan.FromSeconds(1);
|
||||||
|
}
|
||||||
|
|
||||||
var exceptions = new List<Exception>();
|
List<Exception> exceptions = new List<Exception>();
|
||||||
|
|
||||||
for (var retry = 0; retry < retryCount; retry++)
|
for(Int32 retry = 0; retry < retryCount; retry++) {
|
||||||
{
|
try {
|
||||||
try
|
if(retry > 0) {
|
||||||
{
|
|
||||||
if (retry > 0)
|
|
||||||
Task.Delay(retryInterval).Wait();
|
Task.Delay(retryInterval).Wait();
|
||||||
|
}
|
||||||
|
|
||||||
return action();
|
return action();
|
||||||
}
|
} catch(Exception ex) {
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
exceptions.Add(ex);
|
exceptions.Add(ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -258,19 +232,19 @@
|
|||||||
/// <param name="ex">The ex.</param>
|
/// <param name="ex">The ex.</param>
|
||||||
/// <param name="priorMessage">The prior message.</param>
|
/// <param name="priorMessage">The prior message.</param>
|
||||||
/// <returns>A <see cref="System.String" /> that represents this instance.</returns>
|
/// <returns>A <see cref="System.String" /> that represents this instance.</returns>
|
||||||
public static string ExceptionMessage(this Exception ex, string priorMessage = "")
|
public static String ExceptionMessage(this Exception ex, String priorMessage = "") {
|
||||||
{
|
while(true) {
|
||||||
while (true)
|
if(ex == null) {
|
||||||
{
|
|
||||||
if (ex == null)
|
|
||||||
throw new ArgumentNullException(nameof(ex));
|
throw new ArgumentNullException(nameof(ex));
|
||||||
|
}
|
||||||
|
|
||||||
var fullMessage = string.IsNullOrWhiteSpace(priorMessage)
|
String fullMessage = String.IsNullOrWhiteSpace(priorMessage)
|
||||||
? ex.Message
|
? ex.Message
|
||||||
: priorMessage + "\r\n" + ex.Message;
|
: priorMessage + "\r\n" + ex.Message;
|
||||||
|
|
||||||
if (string.IsNullOrWhiteSpace(ex.InnerException?.Message))
|
if(String.IsNullOrWhiteSpace(ex.InnerException?.Message)) {
|
||||||
return fullMessage;
|
return fullMessage;
|
||||||
|
}
|
||||||
|
|
||||||
ex = ex.InnerException;
|
ex = ex.InnerException;
|
||||||
priorMessage = fullMessage;
|
priorMessage = fullMessage;
|
||||||
@ -285,14 +259,14 @@
|
|||||||
/// Array of properties.
|
/// Array of properties.
|
||||||
/// </returns>
|
/// </returns>
|
||||||
/// <exception cref="ArgumentNullException">model.</exception>
|
/// <exception cref="ArgumentNullException">model.</exception>
|
||||||
public static IEnumerable<string> GetCopyableProperties(this object obj)
|
public static IEnumerable<String> GetCopyableProperties(this Object obj) {
|
||||||
{
|
if(obj == null) {
|
||||||
if (obj == null)
|
|
||||||
throw new ArgumentNullException(nameof(obj));
|
throw new ArgumentNullException(nameof(obj));
|
||||||
|
}
|
||||||
|
|
||||||
return Runtime.PropertyTypeCache
|
return Runtime.PropertyTypeCache
|
||||||
.RetrieveAllProperties(obj.GetType(), true)
|
.RetrieveAllProperties(obj.GetType(), true)
|
||||||
.Select(x => new { x.Name, HasAttribute = Runtime.AttributeCache.RetrieveOne<CopyableAttribute>(x) != null})
|
.Select(x => new { x.Name, HasAttribute = Runtime.AttributeCache.RetrieveOne<CopyableAttribute>(x) != null })
|
||||||
.Where(x => x.HasAttribute)
|
.Where(x => x.HasAttribute)
|
||||||
.Select(x => x.Name);
|
.Select(x => x.Name);
|
||||||
}
|
}
|
||||||
@ -304,23 +278,23 @@
|
|||||||
/// <returns>
|
/// <returns>
|
||||||
/// <c>true</c> if the specified model is valid; otherwise, <c>false</c>.
|
/// <c>true</c> if the specified model is valid; otherwise, <c>false</c>.
|
||||||
/// </returns>
|
/// </returns>
|
||||||
public static bool IsValid(this object obj) => Runtime.ObjectValidator.IsValid(obj);
|
public static Boolean IsValid(this Object obj) => Runtime.ObjectValidator.IsValid(obj);
|
||||||
|
|
||||||
internal static void CreateTarget(
|
internal static void CreateTarget(
|
||||||
this object source,
|
this Object source,
|
||||||
Type targetType,
|
Type targetType,
|
||||||
bool includeNonPublic,
|
Boolean includeNonPublic,
|
||||||
ref object target)
|
ref Object target) {
|
||||||
{
|
switch(source) {
|
||||||
switch (source)
|
case String _:
|
||||||
{
|
|
||||||
case string _:
|
|
||||||
break; // do nothing. Simply skip creation
|
break; // do nothing. Simply skip creation
|
||||||
case IList sourceObjectList when targetType.IsArray: // When using arrays, there is no default constructor, attempt to build a compatible array
|
case IList sourceObjectList when targetType.IsArray: // When using arrays, there is no default constructor, attempt to build a compatible array
|
||||||
var elementType = targetType.GetElementType();
|
Type elementType = targetType.GetElementType();
|
||||||
|
|
||||||
if (elementType != null)
|
if(elementType != null) {
|
||||||
target = Array.CreateInstance(elementType, sourceObjectList.Count);
|
target = Array.CreateInstance(elementType, sourceObjectList.Count);
|
||||||
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
target = Activator.CreateInstance(targetType, includeNonPublic);
|
target = Activator.CreateInstance(targetType, includeNonPublic);
|
||||||
|
@ -1,13 +1,12 @@
|
|||||||
namespace Unosquare.Swan.Formatters
|
using Unosquare.Swan.Reflection;
|
||||||
{
|
using System;
|
||||||
using Reflection;
|
using System.Collections.Generic;
|
||||||
using System;
|
using System.IO;
|
||||||
using System.Collections.Generic;
|
using System.Linq;
|
||||||
using System.IO;
|
using System.Reflection;
|
||||||
using System.Linq;
|
using System.Text;
|
||||||
using System.Reflection;
|
|
||||||
using System.Text;
|
|
||||||
|
|
||||||
|
namespace Unosquare.Swan.Formatters {
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents a reader designed for CSV text.
|
/// Represents a reader designed for CSV text.
|
||||||
/// It is capable of deserializing objects from individual lines of CSV text,
|
/// It is capable of deserializing objects from individual lines of CSV text,
|
||||||
@ -66,19 +65,18 @@
|
|||||||
/// }
|
/// }
|
||||||
/// </code>
|
/// </code>
|
||||||
/// </example>
|
/// </example>
|
||||||
public class CsvReader : IDisposable
|
public class CsvReader : IDisposable {
|
||||||
{
|
|
||||||
private static readonly PropertyTypeCache TypeCache = new PropertyTypeCache();
|
private static readonly PropertyTypeCache TypeCache = new PropertyTypeCache();
|
||||||
|
|
||||||
private readonly object _syncLock = new object();
|
private readonly Object _syncLock = new Object();
|
||||||
|
|
||||||
private ulong _count;
|
private UInt64 _count;
|
||||||
private char _escapeCharacter = '"';
|
private Char _escapeCharacter = '"';
|
||||||
private char _separatorCharacter = ',';
|
private Char _separatorCharacter = ',';
|
||||||
|
|
||||||
private bool _hasDisposed; // To detect redundant calls
|
private Boolean _hasDisposed; // To detect redundant calls
|
||||||
private string[] _headings;
|
private String[] _headings;
|
||||||
private Dictionary<string, string> _defaultMap;
|
private Dictionary<String, String> _defaultMap;
|
||||||
private StreamReader _reader;
|
private StreamReader _reader;
|
||||||
|
|
||||||
#region Constructors
|
#region Constructors
|
||||||
@ -89,15 +87,16 @@
|
|||||||
/// <param name="inputStream">The stream.</param>
|
/// <param name="inputStream">The stream.</param>
|
||||||
/// <param name="leaveOpen">if set to <c>true</c> leaves the input stream open.</param>
|
/// <param name="leaveOpen">if set to <c>true</c> leaves the input stream open.</param>
|
||||||
/// <param name="textEncoding">The text encoding.</param>
|
/// <param name="textEncoding">The text encoding.</param>
|
||||||
public CsvReader(Stream inputStream, bool leaveOpen, Encoding textEncoding)
|
public CsvReader(Stream inputStream, Boolean leaveOpen, Encoding textEncoding) {
|
||||||
{
|
if(inputStream == null) {
|
||||||
if (inputStream == null)
|
|
||||||
throw new ArgumentNullException(nameof(inputStream));
|
throw new ArgumentNullException(nameof(inputStream));
|
||||||
|
}
|
||||||
|
|
||||||
if (textEncoding == null)
|
if(textEncoding == null) {
|
||||||
throw new ArgumentNullException(nameof(textEncoding));
|
throw new ArgumentNullException(nameof(textEncoding));
|
||||||
|
}
|
||||||
|
|
||||||
_reader = new StreamReader(inputStream, textEncoding, true, 2048, leaveOpen);
|
this._reader = new StreamReader(inputStream, textEncoding, true, 2048, leaveOpen);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -107,8 +106,7 @@
|
|||||||
/// <param name="stream">The stream.</param>
|
/// <param name="stream">The stream.</param>
|
||||||
/// <param name="textEncoding">The text encoding.</param>
|
/// <param name="textEncoding">The text encoding.</param>
|
||||||
public CsvReader(Stream stream, Encoding textEncoding)
|
public CsvReader(Stream stream, Encoding textEncoding)
|
||||||
: this(stream, false, textEncoding)
|
: this(stream, false, textEncoding) {
|
||||||
{
|
|
||||||
// placeholder
|
// placeholder
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -119,8 +117,7 @@
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="stream">The stream.</param>
|
/// <param name="stream">The stream.</param>
|
||||||
public CsvReader(Stream stream)
|
public CsvReader(Stream stream)
|
||||||
: this(stream, false, Definitions.Windows1252Encoding)
|
: this(stream, false, Definitions.Windows1252Encoding) {
|
||||||
{
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -129,9 +126,8 @@
|
|||||||
/// when this reader is disposed of.
|
/// when this reader is disposed of.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="filename">The filename.</param>
|
/// <param name="filename">The filename.</param>
|
||||||
public CsvReader(string filename)
|
public CsvReader(String filename)
|
||||||
: this(File.OpenRead(filename), false, Definitions.Windows1252Encoding)
|
: this(File.OpenRead(filename), false, Definitions.Windows1252Encoding) {
|
||||||
{
|
|
||||||
// placeholder
|
// placeholder
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -141,9 +137,8 @@
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="filename">The filename.</param>
|
/// <param name="filename">The filename.</param>
|
||||||
/// <param name="encoding">The encoding.</param>
|
/// <param name="encoding">The encoding.</param>
|
||||||
public CsvReader(string filename, Encoding encoding)
|
public CsvReader(String filename, Encoding encoding)
|
||||||
: this(File.OpenRead(filename), false, encoding)
|
: this(File.OpenRead(filename), false, encoding) {
|
||||||
{
|
|
||||||
// placeholder
|
// placeholder
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -157,13 +152,10 @@
|
|||||||
/// <value>
|
/// <value>
|
||||||
/// The count.
|
/// The count.
|
||||||
/// </value>
|
/// </value>
|
||||||
public ulong Count
|
public UInt64 Count {
|
||||||
{
|
get {
|
||||||
get
|
lock(this._syncLock) {
|
||||||
{
|
return this._count;
|
||||||
lock (_syncLock)
|
|
||||||
{
|
|
||||||
return _count;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -175,14 +167,11 @@
|
|||||||
/// <value>
|
/// <value>
|
||||||
/// The escape character.
|
/// The escape character.
|
||||||
/// </value>
|
/// </value>
|
||||||
public char EscapeCharacter
|
public Char EscapeCharacter {
|
||||||
{
|
get => this._escapeCharacter;
|
||||||
get => _escapeCharacter;
|
set {
|
||||||
set
|
lock(this._syncLock) {
|
||||||
{
|
this._escapeCharacter = value;
|
||||||
lock (_syncLock)
|
|
||||||
{
|
|
||||||
_escapeCharacter = value;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -194,14 +183,11 @@
|
|||||||
/// <value>
|
/// <value>
|
||||||
/// The separator character.
|
/// The separator character.
|
||||||
/// </value>
|
/// </value>
|
||||||
public char SeparatorCharacter
|
public Char SeparatorCharacter {
|
||||||
{
|
get => this._separatorCharacter;
|
||||||
get => _separatorCharacter;
|
set {
|
||||||
set
|
lock(this._syncLock) {
|
||||||
{
|
this._separatorCharacter = value;
|
||||||
lock (_syncLock)
|
|
||||||
{
|
|
||||||
_separatorCharacter = value;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -213,13 +199,10 @@
|
|||||||
/// <value>
|
/// <value>
|
||||||
/// <c>true</c> if [end of stream]; otherwise, <c>false</c>.
|
/// <c>true</c> if [end of stream]; otherwise, <c>false</c>.
|
||||||
/// </value>
|
/// </value>
|
||||||
public bool EndOfStream
|
public Boolean EndOfStream {
|
||||||
{
|
get {
|
||||||
get
|
lock(this._syncLock) {
|
||||||
{
|
return this._reader.EndOfStream;
|
||||||
lock (_syncLock)
|
|
||||||
{
|
|
||||||
return _reader.EndOfStream;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -233,15 +216,14 @@
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>An array of the specified element type containing copies of the elements of the ArrayList.</returns>
|
/// <returns>An array of the specified element type containing copies of the elements of the ArrayList.</returns>
|
||||||
/// <exception cref="System.IO.EndOfStreamException">Cannot read past the end of the stream.</exception>
|
/// <exception cref="System.IO.EndOfStreamException">Cannot read past the end of the stream.</exception>
|
||||||
public string[] ReadLine()
|
public String[] ReadLine() {
|
||||||
{
|
lock(this._syncLock) {
|
||||||
lock (_syncLock)
|
if(this._reader.EndOfStream) {
|
||||||
{
|
|
||||||
if (_reader.EndOfStream)
|
|
||||||
throw new EndOfStreamException("Cannot read past the end of the stream");
|
throw new EndOfStreamException("Cannot read past the end of the stream");
|
||||||
|
}
|
||||||
|
|
||||||
var values = ParseRecord(_reader, _escapeCharacter, _separatorCharacter);
|
String[] values = ParseRecord(this._reader, this._escapeCharacter, this._separatorCharacter);
|
||||||
_count++;
|
this._count++;
|
||||||
return values;
|
return values;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -257,14 +239,13 @@
|
|||||||
/// as the first read operation (i.e. while count is still 0).
|
/// as the first read operation (i.e. while count is still 0).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <exception cref="System.IO.EndOfStreamException">Cannot read past the end of the stream.</exception>
|
/// <exception cref="System.IO.EndOfStreamException">Cannot read past the end of the stream.</exception>
|
||||||
public void SkipRecord()
|
public void SkipRecord() {
|
||||||
{
|
lock(this._syncLock) {
|
||||||
lock (_syncLock)
|
if(this._reader.EndOfStream) {
|
||||||
{
|
|
||||||
if (_reader.EndOfStream)
|
|
||||||
throw new EndOfStreamException("Cannot read past the end of the stream");
|
throw new EndOfStreamException("Cannot read past the end of the stream");
|
||||||
|
}
|
||||||
|
|
||||||
ParseRecord(_reader, _escapeCharacter, _separatorCharacter);
|
_ = ParseRecord(this._reader, this._escapeCharacter, this._separatorCharacter);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -279,20 +260,20 @@
|
|||||||
/// ReadHeadings.
|
/// ReadHeadings.
|
||||||
/// </exception>
|
/// </exception>
|
||||||
/// <exception cref="System.IO.EndOfStreamException">Cannot read past the end of the stream.</exception>
|
/// <exception cref="System.IO.EndOfStreamException">Cannot read past the end of the stream.</exception>
|
||||||
public string[] ReadHeadings()
|
public String[] ReadHeadings() {
|
||||||
{
|
lock(this._syncLock) {
|
||||||
lock (_syncLock)
|
if(this._headings != null) {
|
||||||
{
|
|
||||||
if (_headings != null)
|
|
||||||
throw new InvalidOperationException($"The {nameof(ReadHeadings)} method had already been called.");
|
throw new InvalidOperationException($"The {nameof(ReadHeadings)} method had already been called.");
|
||||||
|
}
|
||||||
|
|
||||||
if (_count != 0)
|
if(this._count != 0) {
|
||||||
throw new InvalidOperationException("Reading headings is only supported as the first read operation.");
|
throw new InvalidOperationException("Reading headings is only supported as the first read operation.");
|
||||||
|
}
|
||||||
|
|
||||||
_headings = ReadLine();
|
this._headings = this.ReadLine();
|
||||||
_defaultMap = _headings.ToDictionary(x => x, x => x);
|
this._defaultMap = this._headings.ToDictionary(x => x, x => x);
|
||||||
|
|
||||||
return _headings.ToArray();
|
return this._headings.ToArray();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -304,25 +285,25 @@
|
|||||||
/// <exception cref="System.InvalidOperationException">ReadHeadings.</exception>
|
/// <exception cref="System.InvalidOperationException">ReadHeadings.</exception>
|
||||||
/// <exception cref="System.IO.EndOfStreamException">Cannot read past the end of the stream.</exception>
|
/// <exception cref="System.IO.EndOfStreamException">Cannot read past the end of the stream.</exception>
|
||||||
/// <exception cref="System.ArgumentNullException">map.</exception>
|
/// <exception cref="System.ArgumentNullException">map.</exception>
|
||||||
public IDictionary<string, object> ReadObject(IDictionary<string, string> map)
|
public IDictionary<String, Object> ReadObject(IDictionary<String, String> map) {
|
||||||
{
|
lock(this._syncLock) {
|
||||||
lock (_syncLock)
|
if(this._headings == null) {
|
||||||
{
|
|
||||||
if (_headings == null)
|
|
||||||
throw new InvalidOperationException($"Call the {nameof(ReadHeadings)} method before reading as an object.");
|
throw new InvalidOperationException($"Call the {nameof(ReadHeadings)} method before reading as an object.");
|
||||||
|
}
|
||||||
|
|
||||||
if (map == null)
|
if(map == null) {
|
||||||
throw new ArgumentNullException(nameof(map));
|
throw new ArgumentNullException(nameof(map));
|
||||||
|
}
|
||||||
|
|
||||||
var result = new Dictionary<string, object>();
|
Dictionary<String, Object> result = new Dictionary<String, Object>();
|
||||||
var values = ReadLine();
|
String[] values = this.ReadLine();
|
||||||
|
|
||||||
for (var i = 0; i < _headings.Length; i++)
|
for(Int32 i = 0; i < this._headings.Length; i++) {
|
||||||
{
|
if(i > values.Length - 1) {
|
||||||
if (i > values.Length - 1)
|
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
result[_headings[i]] = values[i];
|
result[this._headings[i]] = values[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
@ -334,7 +315,7 @@
|
|||||||
/// The property names correspond to the names of the CSV headings.
|
/// The property names correspond to the names of the CSV headings.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>Object of the type of the elements in the collection of key/value pairs.</returns>
|
/// <returns>Object of the type of the elements in the collection of key/value pairs.</returns>
|
||||||
public IDictionary<string, object> ReadObject() => ReadObject(_defaultMap);
|
public IDictionary<String, Object> ReadObject() => this.ReadObject(this._defaultMap);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Reads a line of CSV text converting it into an object of the given type, using a map (or Dictionary)
|
/// Reads a line of CSV text converting it into an object of the given type, using a map (or Dictionary)
|
||||||
@ -349,49 +330,52 @@
|
|||||||
/// result.</exception>
|
/// result.</exception>
|
||||||
/// <exception cref="System.InvalidOperationException">ReadHeadings.</exception>
|
/// <exception cref="System.InvalidOperationException">ReadHeadings.</exception>
|
||||||
/// <exception cref="System.IO.EndOfStreamException">Cannot read past the end of the stream.</exception>
|
/// <exception cref="System.IO.EndOfStreamException">Cannot read past the end of the stream.</exception>
|
||||||
public void ReadObject<T>(IDictionary<string, string> map, ref T result)
|
public void ReadObject<T>(IDictionary<String, String> map, ref T result) {
|
||||||
{
|
lock(this._syncLock) {
|
||||||
lock (_syncLock)
|
|
||||||
{
|
|
||||||
// Check arguments
|
// Check arguments
|
||||||
{
|
{
|
||||||
if (map == null)
|
if(map == null) {
|
||||||
throw new ArgumentNullException(nameof(map));
|
throw new ArgumentNullException(nameof(map));
|
||||||
|
}
|
||||||
|
|
||||||
if (_reader.EndOfStream)
|
if(this._reader.EndOfStream) {
|
||||||
throw new EndOfStreamException("Cannot read past the end of the stream");
|
throw new EndOfStreamException("Cannot read past the end of the stream");
|
||||||
|
}
|
||||||
|
|
||||||
if (_headings == null)
|
if(this._headings == null) {
|
||||||
throw new InvalidOperationException($"Call the {nameof(ReadHeadings)} method before reading as an object.");
|
throw new InvalidOperationException($"Call the {nameof(ReadHeadings)} method before reading as an object.");
|
||||||
|
}
|
||||||
|
|
||||||
if (Equals(result, default(T)))
|
if(Equals(result, default(T))) {
|
||||||
throw new ArgumentNullException(nameof(result));
|
throw new ArgumentNullException(nameof(result));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Read line and extract values
|
// Read line and extract values
|
||||||
var values = ReadLine();
|
String[] values = this.ReadLine();
|
||||||
|
|
||||||
// Extract properties from cache
|
// Extract properties from cache
|
||||||
var properties = TypeCache
|
IEnumerable<PropertyInfo> properties = TypeCache
|
||||||
.RetrieveFilteredProperties(typeof(T), true, x => x.CanWrite && Definitions.BasicTypesInfo.ContainsKey(x.PropertyType));
|
.RetrieveFilteredProperties(typeof(T), true, x => x.CanWrite && Definitions.BasicTypesInfo.ContainsKey(x.PropertyType));
|
||||||
|
|
||||||
// Assign property values for each heading
|
// Assign property values for each heading
|
||||||
for (var i = 0; i < _headings.Length; i++)
|
for(Int32 i = 0; i < this._headings.Length; i++) {
|
||||||
{
|
|
||||||
// break if no more headings are matched
|
// break if no more headings are matched
|
||||||
if (i > values.Length - 1)
|
if(i > values.Length - 1) {
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
// skip if no heading is available or the heading is empty
|
// skip if no heading is available or the heading is empty
|
||||||
if (map.ContainsKey(_headings[i]) == false &&
|
if(map.ContainsKey(this._headings[i]) == false &&
|
||||||
string.IsNullOrWhiteSpace(map[_headings[i]]) == false)
|
String.IsNullOrWhiteSpace(map[this._headings[i]]) == false) {
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
// Prepare the target property
|
// Prepare the target property
|
||||||
var propertyName = map[_headings[i]];
|
String propertyName = map[this._headings[i]];
|
||||||
|
|
||||||
// Parse and assign the basic type value to the property if exists
|
// Parse and assign the basic type value to the property if exists
|
||||||
properties
|
_ = properties
|
||||||
.FirstOrDefault(p => p.Name.Equals(propertyName))?
|
.FirstOrDefault(p => p.Name.Equals(propertyName))?
|
||||||
.TrySetBasicType(values[i], result);
|
.TrySetBasicType(values[i], result);
|
||||||
}
|
}
|
||||||
@ -409,11 +393,10 @@
|
|||||||
/// <exception cref="System.ArgumentNullException">map.</exception>
|
/// <exception cref="System.ArgumentNullException">map.</exception>
|
||||||
/// <exception cref="System.InvalidOperationException">ReadHeadings.</exception>
|
/// <exception cref="System.InvalidOperationException">ReadHeadings.</exception>
|
||||||
/// <exception cref="System.IO.EndOfStreamException">Cannot read past the end of the stream.</exception>
|
/// <exception cref="System.IO.EndOfStreamException">Cannot read past the end of the stream.</exception>
|
||||||
public T ReadObject<T>(IDictionary<string, string> map)
|
public T ReadObject<T>(IDictionary<String, String> map)
|
||||||
where T : new()
|
where T : new() {
|
||||||
{
|
T result = Activator.CreateInstance<T>();
|
||||||
var result = Activator.CreateInstance<T>();
|
this.ReadObject(map, ref result);
|
||||||
ReadObject(map, ref result);
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -424,10 +407,7 @@
|
|||||||
/// <typeparam name="T">The type of object.</typeparam>
|
/// <typeparam name="T">The type of object.</typeparam>
|
||||||
/// <returns>The conversion of specific type of object.</returns>
|
/// <returns>The conversion of specific type of object.</returns>
|
||||||
public T ReadObject<T>()
|
public T ReadObject<T>()
|
||||||
where T : new()
|
where T : new() => this.ReadObject<T>(this._defaultMap);
|
||||||
{
|
|
||||||
return ReadObject<T>(_defaultMap);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
@ -441,89 +421,76 @@
|
|||||||
/// <param name="escapeCharacter">The escape character.</param>
|
/// <param name="escapeCharacter">The escape character.</param>
|
||||||
/// <param name="separatorCharacter">The separator character.</param>
|
/// <param name="separatorCharacter">The separator character.</param>
|
||||||
/// <returns>An array of the specified element type containing copies of the elements of the ArrayList.</returns>
|
/// <returns>An array of the specified element type containing copies of the elements of the ArrayList.</returns>
|
||||||
private static string[] ParseRecord(StreamReader reader, char escapeCharacter = '"', char separatorCharacter = ',')
|
private static String[] ParseRecord(StreamReader reader, Char escapeCharacter = '"', Char separatorCharacter = ',') {
|
||||||
{
|
List<String> values = new List<String>();
|
||||||
var values = new List<string>();
|
StringBuilder currentValue = new StringBuilder(1024);
|
||||||
var currentValue = new StringBuilder(1024);
|
ReadState currentState = ReadState.WaitingForNewField;
|
||||||
var currentState = ReadState.WaitingForNewField;
|
String line;
|
||||||
string line;
|
|
||||||
|
|
||||||
while ((line = reader.ReadLine()) != null)
|
while((line = reader.ReadLine()) != null) {
|
||||||
{
|
for(Int32 charIndex = 0; charIndex < line.Length; charIndex++) {
|
||||||
for (var charIndex = 0; charIndex < line.Length; charIndex++)
|
|
||||||
{
|
|
||||||
// Get the current and next character
|
// Get the current and next character
|
||||||
var currentChar = line[charIndex];
|
Char currentChar = line[charIndex];
|
||||||
var nextChar = charIndex < line.Length - 1 ? line[charIndex + 1] : new char?();
|
Char? nextChar = charIndex < line.Length - 1 ? line[charIndex + 1] : new global::System.Char?();
|
||||||
|
|
||||||
// Perform logic based on state and decide on next state
|
// Perform logic based on state and decide on next state
|
||||||
switch (currentState)
|
switch(currentState) {
|
||||||
{
|
case ReadState.WaitingForNewField: {
|
||||||
case ReadState.WaitingForNewField:
|
_ = currentValue.Clear();
|
||||||
{
|
|
||||||
currentValue.Clear();
|
|
||||||
|
|
||||||
if (currentChar == escapeCharacter)
|
if(currentChar == escapeCharacter) {
|
||||||
{
|
|
||||||
currentState = ReadState.PushingQuoted;
|
currentState = ReadState.PushingQuoted;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (currentChar == separatorCharacter)
|
if(currentChar == separatorCharacter) {
|
||||||
{
|
|
||||||
values.Add(currentValue.ToString());
|
values.Add(currentValue.ToString());
|
||||||
currentState = ReadState.WaitingForNewField;
|
currentState = ReadState.WaitingForNewField;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
currentValue.Append(currentChar);
|
_ = currentValue.Append(currentChar);
|
||||||
currentState = ReadState.PushingNormal;
|
currentState = ReadState.PushingNormal;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
case ReadState.PushingNormal:
|
case ReadState.PushingNormal: {
|
||||||
{
|
|
||||||
// Handle field content delimiter by comma
|
// Handle field content delimiter by comma
|
||||||
if (currentChar == separatorCharacter)
|
if(currentChar == separatorCharacter) {
|
||||||
{
|
|
||||||
currentState = ReadState.WaitingForNewField;
|
currentState = ReadState.WaitingForNewField;
|
||||||
values.Add(currentValue.ToString());
|
values.Add(currentValue.ToString());
|
||||||
currentValue.Clear();
|
_ = currentValue.Clear();
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle double quote escaping
|
// Handle double quote escaping
|
||||||
if (currentChar == escapeCharacter && nextChar.HasValue && nextChar == escapeCharacter)
|
if(currentChar == escapeCharacter && nextChar.HasValue && nextChar == escapeCharacter) {
|
||||||
{
|
|
||||||
// advance 1 character now. The loop will advance one more.
|
// advance 1 character now. The loop will advance one more.
|
||||||
currentValue.Append(currentChar);
|
_ = currentValue.Append(currentChar);
|
||||||
charIndex++;
|
charIndex++;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
currentValue.Append(currentChar);
|
_ = currentValue.Append(currentChar);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case ReadState.PushingQuoted:
|
case ReadState.PushingQuoted: {
|
||||||
{
|
|
||||||
// Handle field content delimiter by ending double quotes
|
// Handle field content delimiter by ending double quotes
|
||||||
if (currentChar == escapeCharacter && (nextChar.HasValue == false || nextChar != escapeCharacter))
|
if(currentChar == escapeCharacter && (nextChar.HasValue == false || nextChar != escapeCharacter)) {
|
||||||
{
|
|
||||||
currentState = ReadState.PushingNormal;
|
currentState = ReadState.PushingNormal;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle double quote escaping
|
// Handle double quote escaping
|
||||||
if (currentChar == escapeCharacter && nextChar.HasValue && nextChar == escapeCharacter)
|
if(currentChar == escapeCharacter && nextChar.HasValue && nextChar == escapeCharacter) {
|
||||||
{
|
|
||||||
// advance 1 character now. The loop will advance one more.
|
// advance 1 character now. The loop will advance one more.
|
||||||
currentValue.Append(currentChar);
|
_ = currentValue.Append(currentChar);
|
||||||
charIndex++;
|
charIndex++;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
currentValue.Append(currentChar);
|
_ = currentValue.Append(currentChar);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -531,17 +498,14 @@
|
|||||||
|
|
||||||
// determine if we need to continue reading a new line if it is part of the quoted
|
// determine if we need to continue reading a new line if it is part of the quoted
|
||||||
// field value
|
// field value
|
||||||
if (currentState == ReadState.PushingQuoted)
|
if(currentState == ReadState.PushingQuoted) {
|
||||||
{
|
|
||||||
// we need to add the new line sequence to the output of the field
|
// we need to add the new line sequence to the output of the field
|
||||||
// because we were pushing a quoted value
|
// because we were pushing a quoted value
|
||||||
currentValue.Append(Environment.NewLine);
|
_ = currentValue.Append(Environment.NewLine);
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
// push anything that has not been pushed (flush) into a last value
|
// push anything that has not been pushed (flush) into a last value
|
||||||
values.Add(currentValue.ToString());
|
values.Add(currentValue.ToString());
|
||||||
currentValue.Clear();
|
_ = currentValue.Clear();
|
||||||
|
|
||||||
// stop reading more lines we have reached the end of the CSV record
|
// stop reading more lines we have reached the end of the CSV record
|
||||||
break;
|
break;
|
||||||
@ -550,8 +514,7 @@
|
|||||||
|
|
||||||
// If we ended up pushing quoted and no closing quotes we might
|
// If we ended up pushing quoted and no closing quotes we might
|
||||||
// have additional text in yt
|
// have additional text in yt
|
||||||
if (currentValue.Length > 0)
|
if(currentValue.Length > 0) {
|
||||||
{
|
|
||||||
values.Add(currentValue.ToString());
|
values.Add(currentValue.ToString());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -570,15 +533,12 @@
|
|||||||
/// <param name="stream">The stream.</param>
|
/// <param name="stream">The stream.</param>
|
||||||
/// <returns>A generic collection of objects that can be individually accessed by index.</returns>
|
/// <returns>A generic collection of objects that can be individually accessed by index.</returns>
|
||||||
public static IList<T> LoadRecords<T>(Stream stream)
|
public static IList<T> LoadRecords<T>(Stream stream)
|
||||||
where T : new()
|
where T : new() {
|
||||||
{
|
List<T> result = new List<T>();
|
||||||
var result = new List<T>();
|
|
||||||
|
|
||||||
using (var reader = new CsvReader(stream))
|
using(CsvReader reader = new CsvReader(stream)) {
|
||||||
{
|
_ = reader.ReadHeadings();
|
||||||
reader.ReadHeadings();
|
while(!reader.EndOfStream) {
|
||||||
while (!reader.EndOfStream)
|
|
||||||
{
|
|
||||||
result.Add(reader.ReadObject<T>());
|
result.Add(reader.ReadObject<T>());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -593,11 +553,8 @@
|
|||||||
/// <typeparam name="T">The type of IList items to load.</typeparam>
|
/// <typeparam name="T">The type of IList items to load.</typeparam>
|
||||||
/// <param name="filePath">The file path.</param>
|
/// <param name="filePath">The file path.</param>
|
||||||
/// <returns>A generic collection of objects that can be individually accessed by index.</returns>
|
/// <returns>A generic collection of objects that can be individually accessed by index.</returns>
|
||||||
public static IList<T> LoadRecords<T>(string filePath)
|
public static IList<T> LoadRecords<T>(String filePath)
|
||||||
where T : new()
|
where T : new() => LoadRecords<T>(File.OpenRead(filePath));
|
||||||
{
|
|
||||||
return LoadRecords<T>(File.OpenRead(filePath));
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
@ -607,32 +564,26 @@
|
|||||||
/// Releases unmanaged and - optionally - managed resources.
|
/// Releases unmanaged and - optionally - managed resources.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
|
/// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
|
||||||
protected virtual void Dispose(bool disposing)
|
protected virtual void Dispose(Boolean disposing) {
|
||||||
{
|
if(this._hasDisposed) {
|
||||||
if (_hasDisposed) return;
|
return;
|
||||||
|
|
||||||
if (disposing)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
_reader.Dispose();
|
|
||||||
}
|
}
|
||||||
finally
|
|
||||||
{
|
if(disposing) {
|
||||||
_reader = null;
|
try {
|
||||||
|
this._reader.Dispose();
|
||||||
|
} finally {
|
||||||
|
this._reader = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_hasDisposed = true;
|
this._hasDisposed = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
|
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void Dispose()
|
public void Dispose() => this.Dispose(true);
|
||||||
{
|
|
||||||
Dispose(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
@ -640,8 +591,7 @@
|
|||||||
/// Defines the 3 different read states
|
/// Defines the 3 different read states
|
||||||
/// for the parsing state machine.
|
/// for the parsing state machine.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private enum ReadState
|
private enum ReadState {
|
||||||
{
|
|
||||||
WaitingForNewField,
|
WaitingForNewField,
|
||||||
PushingNormal,
|
PushingNormal,
|
||||||
PushingQuoted,
|
PushingQuoted,
|
||||||
|
@ -1,14 +1,13 @@
|
|||||||
namespace Unosquare.Swan.Formatters
|
using Unosquare.Swan.Reflection;
|
||||||
{
|
using System;
|
||||||
using Reflection;
|
using System.Collections;
|
||||||
using System;
|
using System.Collections.Generic;
|
||||||
using System.Collections;
|
using System.IO;
|
||||||
using System.Collections.Generic;
|
using System.Linq;
|
||||||
using System.IO;
|
using System.Reflection;
|
||||||
using System.Linq;
|
using System.Text;
|
||||||
using System.Reflection;
|
|
||||||
using System.Text;
|
|
||||||
|
|
||||||
|
namespace Unosquare.Swan.Formatters {
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A CSV writer useful for exporting a set of objects.
|
/// A CSV writer useful for exporting a set of objects.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -46,16 +45,15 @@
|
|||||||
/// }
|
/// }
|
||||||
/// </code>
|
/// </code>
|
||||||
/// </example>
|
/// </example>
|
||||||
public class CsvWriter : IDisposable
|
public class CsvWriter : IDisposable {
|
||||||
{
|
|
||||||
private static readonly PropertyTypeCache TypeCache = new PropertyTypeCache();
|
private static readonly PropertyTypeCache TypeCache = new PropertyTypeCache();
|
||||||
|
|
||||||
private readonly object _syncLock = new object();
|
private readonly Object _syncLock = new Object();
|
||||||
private readonly Stream _outputStream;
|
private readonly Stream _outputStream;
|
||||||
private readonly Encoding _encoding;
|
private readonly Encoding _encoding;
|
||||||
private readonly bool _leaveStreamOpen;
|
private readonly Boolean _leaveStreamOpen;
|
||||||
private bool _isDisposing;
|
private Boolean _isDisposing;
|
||||||
private ulong _mCount;
|
private UInt64 _mCount;
|
||||||
|
|
||||||
#region Constructors
|
#region Constructors
|
||||||
|
|
||||||
@ -65,11 +63,10 @@
|
|||||||
/// <param name="outputStream">The output stream.</param>
|
/// <param name="outputStream">The output stream.</param>
|
||||||
/// <param name="leaveOpen">if set to <c>true</c> [leave open].</param>
|
/// <param name="leaveOpen">if set to <c>true</c> [leave open].</param>
|
||||||
/// <param name="encoding">The encoding.</param>
|
/// <param name="encoding">The encoding.</param>
|
||||||
public CsvWriter(Stream outputStream, bool leaveOpen, Encoding encoding)
|
public CsvWriter(Stream outputStream, Boolean leaveOpen, Encoding encoding) {
|
||||||
{
|
this._outputStream = outputStream;
|
||||||
_outputStream = outputStream;
|
this._encoding = encoding;
|
||||||
_encoding = encoding;
|
this._leaveStreamOpen = leaveOpen;
|
||||||
_leaveStreamOpen = leaveOpen;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -79,8 +76,7 @@
|
|||||||
/// <param name="outputStream">The output stream.</param>
|
/// <param name="outputStream">The output stream.</param>
|
||||||
/// <param name="encoding">The encoding.</param>
|
/// <param name="encoding">The encoding.</param>
|
||||||
public CsvWriter(Stream outputStream, Encoding encoding)
|
public CsvWriter(Stream outputStream, Encoding encoding)
|
||||||
: this(outputStream, false, encoding)
|
: this(outputStream, false, encoding) {
|
||||||
{
|
|
||||||
// placeholder
|
// placeholder
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -91,8 +87,7 @@
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="outputStream">The output stream.</param>
|
/// <param name="outputStream">The output stream.</param>
|
||||||
public CsvWriter(Stream outputStream)
|
public CsvWriter(Stream outputStream)
|
||||||
: this(outputStream, false, Definitions.Windows1252Encoding)
|
: this(outputStream, false, Definitions.Windows1252Encoding) {
|
||||||
{
|
|
||||||
// placeholder
|
// placeholder
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -102,9 +97,8 @@
|
|||||||
/// disposing of this writer, and uses the Windows 1252 encoding.
|
/// disposing of this writer, and uses the Windows 1252 encoding.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="filename">The filename.</param>
|
/// <param name="filename">The filename.</param>
|
||||||
public CsvWriter(string filename)
|
public CsvWriter(String filename)
|
||||||
: this(File.OpenWrite(filename), false, Definitions.Windows1252Encoding)
|
: this(File.OpenWrite(filename), false, Definitions.Windows1252Encoding) {
|
||||||
{
|
|
||||||
// placeholder
|
// placeholder
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -115,9 +109,8 @@
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="filename">The filename.</param>
|
/// <param name="filename">The filename.</param>
|
||||||
/// <param name="encoding">The encoding.</param>
|
/// <param name="encoding">The encoding.</param>
|
||||||
public CsvWriter(string filename, Encoding encoding)
|
public CsvWriter(String filename, Encoding encoding)
|
||||||
: this(File.OpenWrite(filename), false, encoding)
|
: this(File.OpenWrite(filename), false, encoding) {
|
||||||
{
|
|
||||||
// placeholder
|
// placeholder
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -131,7 +124,7 @@
|
|||||||
/// <value>
|
/// <value>
|
||||||
/// The separator character.
|
/// The separator character.
|
||||||
/// </value>
|
/// </value>
|
||||||
public char SeparatorCharacter { get; set; } = ',';
|
public Char SeparatorCharacter { get; set; } = ',';
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the escape character to use to escape field values.
|
/// Gets or sets the escape character to use to escape field values.
|
||||||
@ -139,7 +132,7 @@
|
|||||||
/// <value>
|
/// <value>
|
||||||
/// The escape character.
|
/// The escape character.
|
||||||
/// </value>
|
/// </value>
|
||||||
public char EscapeCharacter { get; set; } = '"';
|
public Char EscapeCharacter { get; set; } = '"';
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the new line character sequence to use when writing a line.
|
/// Gets or sets the new line character sequence to use when writing a line.
|
||||||
@ -147,7 +140,7 @@
|
|||||||
/// <value>
|
/// <value>
|
||||||
/// The new line sequence.
|
/// The new line sequence.
|
||||||
/// </value>
|
/// </value>
|
||||||
public string NewLineSequence { get; set; } = Environment.NewLine;
|
public String NewLineSequence { get; set; } = Environment.NewLine;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Defines a list of properties to ignore when outputting CSV lines.
|
/// Defines a list of properties to ignore when outputting CSV lines.
|
||||||
@ -155,7 +148,7 @@
|
|||||||
/// <value>
|
/// <value>
|
||||||
/// The ignore property names.
|
/// The ignore property names.
|
||||||
/// </value>
|
/// </value>
|
||||||
public List<string> IgnorePropertyNames { get; } = new List<string>();
|
public List<String> IgnorePropertyNames { get; } = new List<String>();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets number of lines that have been written, including the headings line.
|
/// Gets number of lines that have been written, including the headings line.
|
||||||
@ -163,13 +156,10 @@
|
|||||||
/// <value>
|
/// <value>
|
||||||
/// The count.
|
/// The count.
|
||||||
/// </value>
|
/// </value>
|
||||||
public ulong Count
|
public UInt64 Count {
|
||||||
{
|
get {
|
||||||
get
|
lock(this._syncLock) {
|
||||||
{
|
return this._mCount;
|
||||||
lock (_syncLock)
|
|
||||||
{
|
|
||||||
return _mCount;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -187,17 +177,16 @@
|
|||||||
/// <param name="stream">The stream.</param>
|
/// <param name="stream">The stream.</param>
|
||||||
/// <param name="truncateData"><c>true</c> if stream is truncated, default <c>false</c>.</param>
|
/// <param name="truncateData"><c>true</c> if stream is truncated, default <c>false</c>.</param>
|
||||||
/// <returns>Number of item saved.</returns>
|
/// <returns>Number of item saved.</returns>
|
||||||
public static int SaveRecords<T>(IEnumerable<T> items, Stream stream, bool truncateData = false)
|
public static Int32 SaveRecords<T>(IEnumerable<T> items, Stream stream, Boolean truncateData = false) {
|
||||||
{
|
|
||||||
// truncate the file if it had data
|
// truncate the file if it had data
|
||||||
if (truncateData && stream.Length > 0)
|
if(truncateData && stream.Length > 0) {
|
||||||
stream.SetLength(0);
|
stream.SetLength(0);
|
||||||
|
}
|
||||||
|
|
||||||
using (var writer = new CsvWriter(stream))
|
using(CsvWriter writer = new CsvWriter(stream)) {
|
||||||
{
|
|
||||||
writer.WriteHeadings<T>();
|
writer.WriteHeadings<T>();
|
||||||
writer.WriteObjects(items);
|
writer.WriteObjects(items);
|
||||||
return (int)writer.Count;
|
return (Int32)writer.Count;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -210,7 +199,7 @@
|
|||||||
/// <param name="items">The items.</param>
|
/// <param name="items">The items.</param>
|
||||||
/// <param name="filePath">The file path.</param>
|
/// <param name="filePath">The file path.</param>
|
||||||
/// <returns>Number of item saved.</returns>
|
/// <returns>Number of item saved.</returns>
|
||||||
public static int SaveRecords<T>(IEnumerable<T> items, string filePath) => SaveRecords(items, File.OpenWrite(filePath), true);
|
public static Int32 SaveRecords<T>(IEnumerable<T> items, String filePath) => SaveRecords(items, File.OpenWrite(filePath), true);
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
@ -222,8 +211,8 @@
|
|||||||
/// If items are not string, the ToStringInvariant() method is called on them.
|
/// If items are not string, the ToStringInvariant() method is called on them.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="items">The items.</param>
|
/// <param name="items">The items.</param>
|
||||||
public void WriteLine(params object[] items)
|
public void WriteLine(params Object[] items)
|
||||||
=> WriteLine(items.Select(x => x == null ? string.Empty : x.ToStringInvariant()));
|
=> this.WriteLine(items.Select(x => x == null ? String.Empty : x.ToStringInvariant()));
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Writes a line of CSV text. Items are converted to strings.
|
/// Writes a line of CSV text. Items are converted to strings.
|
||||||
@ -231,67 +220,66 @@
|
|||||||
/// If items are not string, the ToStringInvariant() method is called on them.
|
/// If items are not string, the ToStringInvariant() method is called on them.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="items">The items.</param>
|
/// <param name="items">The items.</param>
|
||||||
public void WriteLine(IEnumerable<object> items)
|
public void WriteLine(IEnumerable<Object> items)
|
||||||
=> WriteLine(items.Select(x => x == null ? string.Empty : x.ToStringInvariant()));
|
=> this.WriteLine(items.Select(x => x == null ? String.Empty : x.ToStringInvariant()));
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Writes a line of CSV text.
|
/// Writes a line of CSV text.
|
||||||
/// If items are found to be null, empty strings are written out.
|
/// If items are found to be null, empty strings are written out.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="items">The items.</param>
|
/// <param name="items">The items.</param>
|
||||||
public void WriteLine(params string[] items) => WriteLine((IEnumerable<string>) items);
|
public void WriteLine(params String[] items) => this.WriteLine((IEnumerable<String>)items);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Writes a line of CSV text.
|
/// Writes a line of CSV text.
|
||||||
/// If items are found to be null, empty strings are written out.
|
/// If items are found to be null, empty strings are written out.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="items">The items.</param>
|
/// <param name="items">The items.</param>
|
||||||
public void WriteLine(IEnumerable<string> items)
|
public void WriteLine(IEnumerable<String> items) {
|
||||||
{
|
lock(this._syncLock) {
|
||||||
lock (_syncLock)
|
Int32 length = items.Count();
|
||||||
{
|
Byte[] separatorBytes = this._encoding.GetBytes(new[] { this.SeparatorCharacter });
|
||||||
var length = items.Count();
|
Byte[] endOfLineBytes = this._encoding.GetBytes(this.NewLineSequence);
|
||||||
var separatorBytes = _encoding.GetBytes(new[] { SeparatorCharacter });
|
|
||||||
var endOfLineBytes = _encoding.GetBytes(NewLineSequence);
|
|
||||||
|
|
||||||
// Declare state variables here to avoid recreation, allocation and
|
// Declare state variables here to avoid recreation, allocation and
|
||||||
// reassignment in every loop
|
// reassignment in every loop
|
||||||
bool needsEnclosing;
|
Boolean needsEnclosing;
|
||||||
string textValue;
|
String textValue;
|
||||||
byte[] output;
|
Byte[] output;
|
||||||
|
|
||||||
for (var i = 0; i < length; i++)
|
for(Int32 i = 0; i < length; i++) {
|
||||||
{
|
|
||||||
textValue = items.ElementAt(i);
|
textValue = items.ElementAt(i);
|
||||||
|
|
||||||
// Determine if we need the string to be enclosed
|
// Determine if we need the string to be enclosed
|
||||||
// (it either contains an escape, new line, or separator char)
|
// (it either contains an escape, new line, or separator char)
|
||||||
needsEnclosing = textValue.IndexOf(SeparatorCharacter) >= 0
|
needsEnclosing = textValue.IndexOf(this.SeparatorCharacter) >= 0
|
||||||
|| textValue.IndexOf(EscapeCharacter) >= 0
|
|| textValue.IndexOf(this.EscapeCharacter) >= 0
|
||||||
|| textValue.IndexOf('\r') >= 0
|
|| textValue.IndexOf('\r') >= 0
|
||||||
|| textValue.IndexOf('\n') >= 0;
|
|| textValue.IndexOf('\n') >= 0;
|
||||||
|
|
||||||
// Escape the escape characters by repeating them twice for every instance
|
// Escape the escape characters by repeating them twice for every instance
|
||||||
textValue = textValue.Replace($"{EscapeCharacter}",
|
textValue = textValue.Replace($"{this.EscapeCharacter}",
|
||||||
$"{EscapeCharacter}{EscapeCharacter}");
|
$"{this.EscapeCharacter}{this.EscapeCharacter}");
|
||||||
|
|
||||||
// Enclose the text value if we need to
|
// Enclose the text value if we need to
|
||||||
if (needsEnclosing)
|
if(needsEnclosing) {
|
||||||
textValue = string.Format($"{EscapeCharacter}{textValue}{EscapeCharacter}", textValue);
|
textValue = String.Format($"{this.EscapeCharacter}{textValue}{this.EscapeCharacter}", textValue);
|
||||||
|
}
|
||||||
|
|
||||||
// Get the bytes to write to the stream and write them
|
// Get the bytes to write to the stream and write them
|
||||||
output = _encoding.GetBytes(textValue);
|
output = this._encoding.GetBytes(textValue);
|
||||||
_outputStream.Write(output, 0, output.Length);
|
this._outputStream.Write(output, 0, output.Length);
|
||||||
|
|
||||||
// only write a separator if we are moving in between values.
|
// only write a separator if we are moving in between values.
|
||||||
// the last value should not be written.
|
// the last value should not be written.
|
||||||
if (i < length - 1)
|
if(i < length - 1) {
|
||||||
_outputStream.Write(separatorBytes, 0, separatorBytes.Length);
|
this._outputStream.Write(separatorBytes, 0, separatorBytes.Length);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// output the newline sequence
|
// output the newline sequence
|
||||||
_outputStream.Write(endOfLineBytes, 0, endOfLineBytes.Length);
|
this._outputStream.Write(endOfLineBytes, 0, endOfLineBytes.Length);
|
||||||
_mCount += 1;
|
this._mCount += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -307,23 +295,21 @@
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="item">The item.</param>
|
/// <param name="item">The item.</param>
|
||||||
/// <exception cref="System.ArgumentNullException">item.</exception>
|
/// <exception cref="System.ArgumentNullException">item.</exception>
|
||||||
public void WriteObject(object item)
|
public void WriteObject(Object item) {
|
||||||
{
|
if(item == null) {
|
||||||
if (item == null)
|
|
||||||
throw new ArgumentNullException(nameof(item));
|
throw new ArgumentNullException(nameof(item));
|
||||||
|
}
|
||||||
|
|
||||||
lock (_syncLock)
|
lock(this._syncLock) {
|
||||||
{
|
switch(item) {
|
||||||
switch (item)
|
|
||||||
{
|
|
||||||
case IDictionary typedItem:
|
case IDictionary typedItem:
|
||||||
WriteLine(GetFilteredDictionary(typedItem));
|
this.WriteLine(this.GetFilteredDictionary(typedItem));
|
||||||
return;
|
return;
|
||||||
case ICollection typedItem:
|
case ICollection typedItem:
|
||||||
WriteLine(typedItem.Cast<object>());
|
this.WriteLine(typedItem.Cast<Object>());
|
||||||
return;
|
return;
|
||||||
default:
|
default:
|
||||||
WriteLine(GetFilteredTypeProperties(item.GetType())
|
this.WriteLine(this.GetFilteredTypeProperties(item.GetType())
|
||||||
.Select(x => x.ToFormattedString(item)));
|
.Select(x => x.ToFormattedString(item)));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -338,7 +324,7 @@
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <typeparam name="T">The type of object to write.</typeparam>
|
/// <typeparam name="T">The type of object to write.</typeparam>
|
||||||
/// <param name="item">The item.</param>
|
/// <param name="item">The item.</param>
|
||||||
public void WriteObject<T>(T item) => WriteObject(item as object);
|
public void WriteObject<T>(T item) => this.WriteObject(item as Object);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Writes a set of items, one per line and atomically by repeatedly calling the
|
/// Writes a set of items, one per line and atomically by repeatedly calling the
|
||||||
@ -347,12 +333,11 @@
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <typeparam name="T">The type of object to write.</typeparam>
|
/// <typeparam name="T">The type of object to write.</typeparam>
|
||||||
/// <param name="items">The items.</param>
|
/// <param name="items">The items.</param>
|
||||||
public void WriteObjects<T>(IEnumerable<T> items)
|
public void WriteObjects<T>(IEnumerable<T> items) {
|
||||||
{
|
lock(this._syncLock) {
|
||||||
lock (_syncLock)
|
foreach(T item in items) {
|
||||||
{
|
this.WriteObject(item);
|
||||||
foreach (var item in items)
|
}
|
||||||
WriteObject(item);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -365,32 +350,32 @@
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="type">The type of object to extract headings.</param>
|
/// <param name="type">The type of object to extract headings.</param>
|
||||||
/// <exception cref="System.ArgumentNullException">type.</exception>
|
/// <exception cref="System.ArgumentNullException">type.</exception>
|
||||||
public void WriteHeadings(Type type)
|
public void WriteHeadings(Type type) {
|
||||||
{
|
if(type == null) {
|
||||||
if (type == null)
|
|
||||||
throw new ArgumentNullException(nameof(type));
|
throw new ArgumentNullException(nameof(type));
|
||||||
|
}
|
||||||
|
|
||||||
var properties = GetFilteredTypeProperties(type).Select(p => p.Name).Cast<object>();
|
IEnumerable<Object> properties = this.GetFilteredTypeProperties(type).Select(p => p.Name).Cast<Object>();
|
||||||
WriteLine(properties);
|
this.WriteLine(properties);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Writes the headings.
|
/// Writes the headings.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <typeparam name="T">The type of object to extract headings.</typeparam>
|
/// <typeparam name="T">The type of object to extract headings.</typeparam>
|
||||||
public void WriteHeadings<T>() => WriteHeadings(typeof(T));
|
public void WriteHeadings<T>() => this.WriteHeadings(typeof(T));
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Writes the headings.
|
/// Writes the headings.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="dictionary">The dictionary to extract headings.</param>
|
/// <param name="dictionary">The dictionary to extract headings.</param>
|
||||||
/// <exception cref="System.ArgumentNullException">dictionary.</exception>
|
/// <exception cref="System.ArgumentNullException">dictionary.</exception>
|
||||||
public void WriteHeadings(IDictionary dictionary)
|
public void WriteHeadings(IDictionary dictionary) {
|
||||||
{
|
if(dictionary == null) {
|
||||||
if (dictionary == null)
|
|
||||||
throw new ArgumentNullException(nameof(dictionary));
|
throw new ArgumentNullException(nameof(dictionary));
|
||||||
|
}
|
||||||
|
|
||||||
WriteLine(GetFilteredDictionary(dictionary, true));
|
this.WriteLine(this.GetFilteredDictionary(dictionary, true));
|
||||||
}
|
}
|
||||||
|
|
||||||
#if NET452
|
#if NET452
|
||||||
@ -400,15 +385,16 @@
|
|||||||
/// <param name="item">The object to extract headings.</param>
|
/// <param name="item">The object to extract headings.</param>
|
||||||
/// <exception cref="ArgumentNullException">item</exception>
|
/// <exception cref="ArgumentNullException">item</exception>
|
||||||
/// <exception cref="ArgumentException">Unable to cast dynamic object to a suitable dictionary - item</exception>
|
/// <exception cref="ArgumentException">Unable to cast dynamic object to a suitable dictionary - item</exception>
|
||||||
public void WriteHeadings(dynamic item)
|
public void WriteHeadings(dynamic item) {
|
||||||
{
|
if(item == null) {
|
||||||
if (item == null)
|
|
||||||
throw new ArgumentNullException(nameof(item));
|
throw new ArgumentNullException(nameof(item));
|
||||||
|
}
|
||||||
|
|
||||||
if (!(item is IDictionary<string, object> dictionary))
|
if(!(item is IDictionary<global::System.String, global::System.Object> dictionary)) {
|
||||||
throw new ArgumentException("Unable to cast dynamic object to a suitable dictionary", nameof(item));
|
throw new ArgumentException("Unable to cast dynamic object to a suitable dictionary", nameof(item));
|
||||||
|
}
|
||||||
|
|
||||||
WriteHeadings(dictionary);
|
this.WriteHeadings(dictionary);
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -430,47 +416,46 @@
|
|||||||
#region IDisposable Support
|
#region IDisposable Support
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public void Dispose() => Dispose(true);
|
public void Dispose() => this.Dispose(true);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Releases unmanaged and - optionally - managed resources.
|
/// Releases unmanaged and - optionally - managed resources.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="disposeAlsoManaged"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
|
/// <param name="disposeAlsoManaged"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
|
||||||
protected virtual void Dispose(bool disposeAlsoManaged)
|
protected virtual void Dispose(Boolean disposeAlsoManaged) {
|
||||||
{
|
if(this._isDisposing) {
|
||||||
if (_isDisposing) return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (disposeAlsoManaged)
|
if(disposeAlsoManaged) {
|
||||||
{
|
if(this._leaveStreamOpen == false) {
|
||||||
if (_leaveStreamOpen == false)
|
this._outputStream.Dispose();
|
||||||
{
|
|
||||||
_outputStream.Dispose();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_isDisposing = true;
|
this._isDisposing = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region Support Methods
|
#region Support Methods
|
||||||
|
|
||||||
private IEnumerable<string> GetFilteredDictionary(IDictionary dictionary, bool filterKeys = false)
|
private IEnumerable<String> GetFilteredDictionary(IDictionary dictionary, Boolean filterKeys = false)
|
||||||
=> dictionary
|
=> dictionary
|
||||||
.Keys
|
.Keys
|
||||||
.Cast<object>()
|
.Cast<Object>()
|
||||||
.Select(key => key == null ? string.Empty : key.ToStringInvariant())
|
.Select(key => key == null ? String.Empty : key.ToStringInvariant())
|
||||||
.Where(stringKey => !IgnorePropertyNames.Contains(stringKey))
|
.Where(stringKey => !this.IgnorePropertyNames.Contains(stringKey))
|
||||||
.Select(stringKey =>
|
.Select(stringKey =>
|
||||||
filterKeys
|
filterKeys
|
||||||
? stringKey
|
? stringKey
|
||||||
: dictionary[stringKey] == null ? string.Empty : dictionary[stringKey].ToStringInvariant());
|
: dictionary[stringKey] == null ? String.Empty : dictionary[stringKey].ToStringInvariant());
|
||||||
|
|
||||||
private IEnumerable<PropertyInfo> GetFilteredTypeProperties(Type type)
|
private IEnumerable<PropertyInfo> GetFilteredTypeProperties(Type type)
|
||||||
=> TypeCache.Retrieve(type, t =>
|
=> TypeCache.Retrieve(type, t =>
|
||||||
t.GetProperties(BindingFlags.Public | BindingFlags.Instance)
|
t.GetProperties(BindingFlags.Public | BindingFlags.Instance)
|
||||||
.Where(p => p.CanRead))
|
.Where(p => p.CanRead))
|
||||||
.Where(p => !IgnorePropertyNames.Contains(p.Name));
|
.Where(p => !this.IgnorePropertyNames.Contains(p.Name));
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
@ -1,149 +1,133 @@
|
|||||||
namespace Unosquare.Swan.Formatters
|
using System;
|
||||||
{
|
using System.Collections.Generic;
|
||||||
using System.Collections.Generic;
|
using System.Linq;
|
||||||
using System.Linq;
|
using System.Text;
|
||||||
using System.Text;
|
|
||||||
|
|
||||||
internal class HumanizeJson
|
namespace Unosquare.Swan.Formatters {
|
||||||
{
|
internal class HumanizeJson {
|
||||||
private readonly StringBuilder _builder = new StringBuilder();
|
private readonly StringBuilder _builder = new StringBuilder();
|
||||||
private readonly int _indent;
|
private readonly Int32 _indent;
|
||||||
private readonly string _indentStr;
|
private readonly String _indentStr;
|
||||||
private readonly object _obj;
|
private readonly Object _obj;
|
||||||
|
|
||||||
public HumanizeJson(object obj, int indent)
|
public HumanizeJson(Object obj, Int32 indent) {
|
||||||
{
|
if(obj == null) {
|
||||||
if (obj == null)
|
|
||||||
{
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
_indent = indent;
|
this._indent = indent;
|
||||||
_indentStr = new string(' ', indent * 4);
|
this._indentStr = new String(' ', indent * 4);
|
||||||
_obj = obj;
|
this._obj = obj;
|
||||||
|
|
||||||
ParseObject();
|
this.ParseObject();
|
||||||
}
|
}
|
||||||
|
|
||||||
public string GetResult() => _builder == null ? string.Empty : _builder.ToString().TrimEnd();
|
public String GetResult() => this._builder == null ? String.Empty : this._builder.ToString().TrimEnd();
|
||||||
|
|
||||||
private void ParseObject()
|
private void ParseObject() {
|
||||||
{
|
switch(this._obj) {
|
||||||
switch (_obj)
|
case Dictionary<String, Object> dictionary:
|
||||||
{
|
this.AppendDictionary(dictionary);
|
||||||
case Dictionary<string, object> dictionary:
|
|
||||||
AppendDictionary(dictionary);
|
|
||||||
break;
|
break;
|
||||||
case List<object> list:
|
case List<Object> list:
|
||||||
AppendList(list);
|
this.AppendList(list);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
AppendString();
|
this.AppendString();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void AppendDictionary(Dictionary<string, object> objects)
|
private void AppendDictionary(Dictionary<String, Object> objects) {
|
||||||
{
|
foreach(KeyValuePair<String, Object> kvp in objects) {
|
||||||
foreach (var kvp in objects)
|
if(kvp.Value == null) {
|
||||||
{
|
continue;
|
||||||
if (kvp.Value == null) continue;
|
}
|
||||||
|
|
||||||
var writeOutput = false;
|
Boolean writeOutput = false;
|
||||||
|
|
||||||
switch (kvp.Value)
|
switch(kvp.Value) {
|
||||||
{
|
case Dictionary<String, Object> valueDictionary:
|
||||||
case Dictionary<string, object> valueDictionary:
|
if(valueDictionary.Count > 0) {
|
||||||
if (valueDictionary.Count > 0)
|
|
||||||
{
|
|
||||||
writeOutput = true;
|
writeOutput = true;
|
||||||
_builder
|
_ = this._builder
|
||||||
.Append($"{_indentStr}{kvp.Key,-16}: object")
|
.Append($"{this._indentStr}{kvp.Key,-16}: object")
|
||||||
.AppendLine();
|
.AppendLine();
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
case List<object> valueList:
|
case List<Object> valueList:
|
||||||
if (valueList.Count > 0)
|
if(valueList.Count > 0) {
|
||||||
{
|
|
||||||
writeOutput = true;
|
writeOutput = true;
|
||||||
_builder
|
_ = this._builder
|
||||||
.Append($"{_indentStr}{kvp.Key,-16}: array[{valueList.Count}]")
|
.Append($"{this._indentStr}{kvp.Key,-16}: array[{valueList.Count}]")
|
||||||
.AppendLine();
|
.AppendLine();
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
writeOutput = true;
|
writeOutput = true;
|
||||||
_builder.Append($"{_indentStr}{kvp.Key,-16}: ");
|
_ = this._builder.Append($"{this._indentStr}{kvp.Key,-16}: ");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (writeOutput)
|
if(writeOutput) {
|
||||||
_builder.AppendLine(new HumanizeJson(kvp.Value, _indent + 1).GetResult());
|
_ = this._builder.AppendLine(new HumanizeJson(kvp.Value, this._indent + 1).GetResult());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void AppendList(List<object> objects)
|
private void AppendList(List<Object> objects) {
|
||||||
{
|
Int32 index = 0;
|
||||||
var index = 0;
|
foreach(Object value in objects) {
|
||||||
foreach (var value in objects)
|
Boolean writeOutput = false;
|
||||||
{
|
|
||||||
var writeOutput = false;
|
|
||||||
|
|
||||||
switch (value)
|
switch(value) {
|
||||||
{
|
case Dictionary<String, Object> valueDictionary:
|
||||||
case Dictionary<string, object> valueDictionary:
|
if(valueDictionary.Count > 0) {
|
||||||
if (valueDictionary.Count > 0)
|
|
||||||
{
|
|
||||||
writeOutput = true;
|
writeOutput = true;
|
||||||
_builder
|
_ = this._builder
|
||||||
.Append($"{_indentStr}[{index}]: object")
|
.Append($"{this._indentStr}[{index}]: object")
|
||||||
.AppendLine();
|
.AppendLine();
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
case List<object> valueList:
|
case List<Object> valueList:
|
||||||
if (valueList.Count > 0)
|
if(valueList.Count > 0) {
|
||||||
{
|
|
||||||
writeOutput = true;
|
writeOutput = true;
|
||||||
_builder
|
_ = this._builder
|
||||||
.Append($"{_indentStr}[{index}]: array[{valueList.Count}]")
|
.Append($"{this._indentStr}[{index}]: array[{valueList.Count}]")
|
||||||
.AppendLine();
|
.AppendLine();
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
writeOutput = true;
|
writeOutput = true;
|
||||||
_builder.Append($"{_indentStr}[{index}]: ");
|
_ = this._builder.Append($"{this._indentStr}[{index}]: ");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
index++;
|
index++;
|
||||||
|
|
||||||
if (writeOutput)
|
if(writeOutput) {
|
||||||
_builder.AppendLine(new HumanizeJson(value, _indent + 1).GetResult());
|
_ = this._builder.AppendLine(new HumanizeJson(value, this._indent + 1).GetResult());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void AppendString()
|
private void AppendString() {
|
||||||
{
|
String stringValue = this._obj.ToString();
|
||||||
var stringValue = _obj.ToString();
|
|
||||||
|
|
||||||
if (stringValue.Length + _indentStr.Length > 96 || stringValue.IndexOf('\r') >= 0 ||
|
if(stringValue.Length + this._indentStr.Length > 96 || stringValue.IndexOf('\r') >= 0 ||
|
||||||
stringValue.IndexOf('\n') >= 0)
|
stringValue.IndexOf('\n') >= 0) {
|
||||||
{
|
_ = this._builder.AppendLine();
|
||||||
_builder.AppendLine();
|
IEnumerable<String> stringLines = stringValue.ToLines().Select(l => l.Trim());
|
||||||
var stringLines = stringValue.ToLines().Select(l => l.Trim());
|
|
||||||
|
|
||||||
foreach (var line in stringLines)
|
foreach(String line in stringLines) {
|
||||||
{
|
_ = this._builder.AppendLine($"{this._indentStr}{line}");
|
||||||
_builder.AppendLine($"{_indentStr}{line}");
|
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
else
|
_ = this._builder.Append($"{stringValue}");
|
||||||
{
|
|
||||||
_builder.Append($"{stringValue}");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,14 +1,13 @@
|
|||||||
namespace Unosquare.Swan.Formatters
|
using System;
|
||||||
{
|
using System.Collections;
|
||||||
using System;
|
using System.Collections.Concurrent;
|
||||||
using System.Collections;
|
using System.Collections.Generic;
|
||||||
using System.Collections.Concurrent;
|
using System.Linq;
|
||||||
using System.Collections.Generic;
|
using System.Reflection;
|
||||||
using System.Linq;
|
using System.Text;
|
||||||
using System.Reflection;
|
using Unosquare.Swan.Attributes;
|
||||||
using System.Text;
|
|
||||||
using Attributes;
|
|
||||||
|
|
||||||
|
namespace Unosquare.Swan.Formatters {
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A very simple, light-weight JSON library written by Mario
|
/// A very simple, light-weight JSON library written by Mario
|
||||||
/// to teach Geo how things are done
|
/// to teach Geo how things are done
|
||||||
@ -16,46 +15,45 @@
|
|||||||
/// This is an useful helper for small tasks but it doesn't represent a full-featured
|
/// This is an useful helper for small tasks but it doesn't represent a full-featured
|
||||||
/// serializer such as the beloved Json.NET.
|
/// serializer such as the beloved Json.NET.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static partial class Json
|
public static partial class Json {
|
||||||
{
|
private class Converter {
|
||||||
private class Converter
|
private static readonly ConcurrentDictionary<MemberInfo, String> MemberInfoNameCache =
|
||||||
{
|
new ConcurrentDictionary<MemberInfo, String>();
|
||||||
private static readonly ConcurrentDictionary<MemberInfo, string> MemberInfoNameCache =
|
|
||||||
new ConcurrentDictionary<MemberInfo, string>();
|
|
||||||
|
|
||||||
private static readonly ConcurrentDictionary<Type, Type> ListAddMethodCache = new ConcurrentDictionary<Type, Type>();
|
private static readonly ConcurrentDictionary<Type, Type> ListAddMethodCache = new ConcurrentDictionary<Type, Type>();
|
||||||
|
|
||||||
private readonly object _target;
|
private readonly Object _target;
|
||||||
private readonly Type _targetType;
|
private readonly Type _targetType;
|
||||||
private readonly bool _includeNonPublic;
|
private readonly Boolean _includeNonPublic;
|
||||||
|
|
||||||
private Converter(
|
private Converter(
|
||||||
object source,
|
Object source,
|
||||||
Type targetType,
|
Type targetType,
|
||||||
ref object targetInstance,
|
ref Object targetInstance,
|
||||||
bool includeNonPublic)
|
Boolean includeNonPublic) {
|
||||||
{
|
this._targetType = targetInstance != null ? targetInstance.GetType() : targetType;
|
||||||
_targetType = targetInstance != null ? targetInstance.GetType() : targetType;
|
this._includeNonPublic = includeNonPublic;
|
||||||
_includeNonPublic = includeNonPublic;
|
|
||||||
|
|
||||||
if (source == null)
|
if(source == null) {
|
||||||
{
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var sourceType = source.GetType();
|
Type sourceType = source.GetType();
|
||||||
|
|
||||||
if (_targetType == null || _targetType == typeof(object)) _targetType = sourceType;
|
if(this._targetType == null || this._targetType == typeof(Object)) {
|
||||||
if (sourceType == _targetType)
|
this._targetType = sourceType;
|
||||||
{
|
}
|
||||||
_target = source;
|
|
||||||
|
if(sourceType == this._targetType) {
|
||||||
|
this._target = source;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!TrySetInstance(targetInstance, source, ref _target))
|
if(!this.TrySetInstance(targetInstance, source, ref this._target)) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
ResolveObject(source, ref _target);
|
this.ResolveObject(source, ref this._target);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -65,21 +63,17 @@
|
|||||||
/// <param name="targetType">Type of the target.</param>
|
/// <param name="targetType">Type of the target.</param>
|
||||||
/// <param name="includeNonPublic">if set to <c>true</c> [include non public].</param>
|
/// <param name="includeNonPublic">if set to <c>true</c> [include non public].</param>
|
||||||
/// <returns>The target object.</returns>
|
/// <returns>The target object.</returns>
|
||||||
internal static object FromJsonResult(object source,
|
internal static Object FromJsonResult(Object source,
|
||||||
Type targetType,
|
Type targetType,
|
||||||
bool includeNonPublic)
|
Boolean includeNonPublic) {
|
||||||
{
|
Object nullRef = null;
|
||||||
object nullRef = null;
|
|
||||||
return new Converter(source, targetType, ref nullRef, includeNonPublic).GetResult();
|
return new Converter(source, targetType, ref nullRef, includeNonPublic).GetResult();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static object FromJsonResult(object source,
|
private static Object FromJsonResult(Object source,
|
||||||
Type targetType,
|
Type targetType,
|
||||||
ref object targetInstance,
|
ref Object targetInstance,
|
||||||
bool includeNonPublic)
|
Boolean includeNonPublic) => new Converter(source, targetType, ref targetInstance, includeNonPublic).GetResult();
|
||||||
{
|
|
||||||
return new Converter(source, targetType, ref targetInstance, includeNonPublic).GetResult();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Type GetAddMethodParameterType(Type targetType)
|
private static Type GetAddMethodParameterType(Type targetType)
|
||||||
=> ListAddMethodCache.GetOrAdd(targetType,
|
=> ListAddMethodCache.GetOrAdd(targetType,
|
||||||
@ -89,243 +83,216 @@
|
|||||||
.GetParameters()[0]
|
.GetParameters()[0]
|
||||||
.ParameterType);
|
.ParameterType);
|
||||||
|
|
||||||
private static void GetByteArray(string sourceString, ref object target)
|
private static void GetByteArray(String sourceString, ref Object target) {
|
||||||
{
|
try {
|
||||||
try
|
|
||||||
{
|
|
||||||
target = Convert.FromBase64String(sourceString);
|
target = Convert.FromBase64String(sourceString);
|
||||||
} // Try conversion from Base 64
|
} // Try conversion from Base 64
|
||||||
catch
|
catch {
|
||||||
{
|
|
||||||
target = Encoding.UTF8.GetBytes(sourceString);
|
target = Encoding.UTF8.GetBytes(sourceString);
|
||||||
} // Get the string bytes in UTF8
|
} // Get the string bytes in UTF8
|
||||||
}
|
}
|
||||||
|
|
||||||
private static object GetSourcePropertyValue(IDictionary<string, object> sourceProperties,
|
private static Object GetSourcePropertyValue(IDictionary<String, Object> sourceProperties,
|
||||||
MemberInfo targetProperty)
|
MemberInfo targetProperty) {
|
||||||
{
|
String targetPropertyName = MemberInfoNameCache.GetOrAdd(
|
||||||
var targetPropertyName = MemberInfoNameCache.GetOrAdd(
|
|
||||||
targetProperty,
|
targetProperty,
|
||||||
x => Runtime.AttributeCache.RetrieveOne<JsonPropertyAttribute>(x)?.PropertyName ?? x.Name);
|
x => Runtime.AttributeCache.RetrieveOne<JsonPropertyAttribute>(x)?.PropertyName ?? x.Name);
|
||||||
|
|
||||||
return sourceProperties.GetValueOrDefault(targetPropertyName);
|
return sourceProperties.GetValueOrDefault(targetPropertyName);
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool TrySetInstance(object targetInstance, object source, ref object target)
|
private Boolean TrySetInstance(Object targetInstance, Object source, ref Object target) {
|
||||||
{
|
if(targetInstance == null) {
|
||||||
if (targetInstance == null)
|
|
||||||
{
|
|
||||||
// Try to create a default instance
|
// Try to create a default instance
|
||||||
try
|
try {
|
||||||
{
|
source.CreateTarget(this._targetType, this._includeNonPublic, ref target);
|
||||||
source.CreateTarget(_targetType, _includeNonPublic, ref target);
|
} catch {
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
target = targetInstance;
|
target = targetInstance;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private object GetResult() => _target ?? _targetType.GetDefault();
|
private Object GetResult() => this._target ?? this._targetType.GetDefault();
|
||||||
|
|
||||||
private void ResolveObject(object source, ref object target)
|
private void ResolveObject(Object source, ref Object target) {
|
||||||
{
|
switch(source) {
|
||||||
switch (source)
|
|
||||||
{
|
|
||||||
// Case 0: Special Cases Handling (Source and Target are of specific convertible types)
|
// Case 0: Special Cases Handling (Source and Target are of specific convertible types)
|
||||||
// Case 0.1: Source is string, Target is byte[]
|
// Case 0.1: Source is string, Target is byte[]
|
||||||
case string sourceString when _targetType == typeof(byte[]):
|
case String sourceString when this._targetType == typeof(Byte[]):
|
||||||
GetByteArray(sourceString, ref target);
|
GetByteArray(sourceString, ref target);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// Case 1.1: Source is Dictionary, Target is IDictionary
|
// Case 1.1: Source is Dictionary, Target is IDictionary
|
||||||
case Dictionary<string, object> sourceProperties when target is IDictionary targetDictionary:
|
case Dictionary<String, Object> sourceProperties when target is IDictionary targetDictionary:
|
||||||
PopulateDictionary(sourceProperties, targetDictionary);
|
this.PopulateDictionary(sourceProperties, targetDictionary);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// Case 1.2: Source is Dictionary, Target is not IDictionary (i.e. it is a complex type)
|
// Case 1.2: Source is Dictionary, Target is not IDictionary (i.e. it is a complex type)
|
||||||
case Dictionary<string, object> sourceProperties:
|
case Dictionary<String, Object> sourceProperties:
|
||||||
PopulateObject(sourceProperties);
|
this.PopulateObject(sourceProperties);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// Case 2.1: Source is List, Target is Array
|
// Case 2.1: Source is List, Target is Array
|
||||||
case List<object> sourceList when target is Array targetArray:
|
case List<Object> sourceList when target is Array targetArray:
|
||||||
PopulateArray(sourceList, targetArray);
|
this.PopulateArray(sourceList, targetArray);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// Case 2.2: Source is List, Target is IList
|
// Case 2.2: Source is List, Target is IList
|
||||||
case List<object> sourceList when target is IList targetList:
|
case List<Object> sourceList when target is IList targetList:
|
||||||
PopulateIList(sourceList, targetList);
|
this.PopulateIList(sourceList, targetList);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// Case 3: Source is a simple type; Attempt conversion
|
// Case 3: Source is a simple type; Attempt conversion
|
||||||
default:
|
default:
|
||||||
var sourceStringValue = source.ToStringInvariant();
|
String sourceStringValue = source.ToStringInvariant();
|
||||||
|
|
||||||
// Handle basic types or enumerations if not
|
// Handle basic types or enumerations if not
|
||||||
if (!_targetType.TryParseBasicType(sourceStringValue, out target))
|
if(!this._targetType.TryParseBasicType(sourceStringValue, out target)) {
|
||||||
GetEnumValue(sourceStringValue, ref target);
|
this.GetEnumValue(sourceStringValue, ref target);
|
||||||
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void PopulateIList(IList<object> objects, IList list)
|
private void PopulateIList(IList<Object> objects, IList list) {
|
||||||
{
|
Type parameterType = GetAddMethodParameterType(this._targetType);
|
||||||
var parameterType = GetAddMethodParameterType(_targetType);
|
if(parameterType == null) {
|
||||||
if (parameterType == null) return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
foreach (var item in objects)
|
foreach(Object item in objects) {
|
||||||
{
|
try {
|
||||||
try
|
_ = list.Add(FromJsonResult(
|
||||||
{
|
|
||||||
list.Add(FromJsonResult(
|
|
||||||
item,
|
item,
|
||||||
parameterType,
|
parameterType,
|
||||||
_includeNonPublic));
|
this._includeNonPublic));
|
||||||
}
|
} catch {
|
||||||
catch
|
|
||||||
{
|
|
||||||
// ignored
|
// ignored
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void PopulateArray(IList<object> objects, Array array)
|
private void PopulateArray(IList<Object> objects, Array array) {
|
||||||
{
|
Type elementType = this._targetType.GetElementType();
|
||||||
var elementType = _targetType.GetElementType();
|
|
||||||
|
|
||||||
for (var i = 0; i < objects.Count; i++)
|
for(Int32 i = 0; i < objects.Count; i++) {
|
||||||
{
|
try {
|
||||||
try
|
Object targetItem = FromJsonResult(
|
||||||
{
|
|
||||||
var targetItem = FromJsonResult(
|
|
||||||
objects[i],
|
objects[i],
|
||||||
elementType,
|
elementType,
|
||||||
_includeNonPublic);
|
this._includeNonPublic);
|
||||||
array.SetValue(targetItem, i);
|
array.SetValue(targetItem, i);
|
||||||
}
|
} catch {
|
||||||
catch
|
|
||||||
{
|
|
||||||
// ignored
|
// ignored
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void GetEnumValue(string sourceStringValue, ref object target)
|
private void GetEnumValue(String sourceStringValue, ref Object target) {
|
||||||
{
|
Type enumType = Nullable.GetUnderlyingType(this._targetType);
|
||||||
var enumType = Nullable.GetUnderlyingType(_targetType);
|
if(enumType == null && this._targetType.GetTypeInfo().IsEnum) {
|
||||||
if (enumType == null && _targetType.GetTypeInfo().IsEnum) enumType = _targetType;
|
enumType = this._targetType;
|
||||||
if (enumType == null) return;
|
}
|
||||||
|
|
||||||
try
|
if(enumType == null) {
|
||||||
{
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
target = Enum.Parse(enumType, sourceStringValue);
|
target = Enum.Parse(enumType, sourceStringValue);
|
||||||
}
|
} catch {
|
||||||
catch
|
|
||||||
{
|
|
||||||
// ignored
|
// ignored
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void PopulateDictionary(IDictionary<string, object> sourceProperties, IDictionary targetDictionary)
|
private void PopulateDictionary(IDictionary<String, Object> sourceProperties, IDictionary targetDictionary) {
|
||||||
{
|
|
||||||
// find the add method of the target dictionary
|
// find the add method of the target dictionary
|
||||||
var addMethod = _targetType.GetMethods()
|
MethodInfo addMethod = this._targetType.GetMethods()
|
||||||
.FirstOrDefault(
|
.FirstOrDefault(
|
||||||
m => m.Name.Equals(AddMethodName) && m.IsPublic && m.GetParameters().Length == 2);
|
m => m.Name.Equals(AddMethodName) && m.IsPublic && m.GetParameters().Length == 2);
|
||||||
|
|
||||||
// skip if we don't have a compatible add method
|
// skip if we don't have a compatible add method
|
||||||
if (addMethod == null) return;
|
if(addMethod == null) {
|
||||||
var addMethodParameters = addMethod.GetParameters();
|
return;
|
||||||
if (addMethodParameters[0].ParameterType != typeof(string)) return;
|
}
|
||||||
|
|
||||||
|
ParameterInfo[] addMethodParameters = addMethod.GetParameters();
|
||||||
|
if(addMethodParameters[0].ParameterType != typeof(String)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Retrieve the target entry type
|
// Retrieve the target entry type
|
||||||
var targetEntryType = addMethodParameters[1].ParameterType;
|
Type targetEntryType = addMethodParameters[1].ParameterType;
|
||||||
|
|
||||||
// Add the items to the target dictionary
|
// Add the items to the target dictionary
|
||||||
foreach (var sourceProperty in sourceProperties)
|
foreach(KeyValuePair<String, Object> sourceProperty in sourceProperties) {
|
||||||
{
|
try {
|
||||||
try
|
Object targetEntryValue = FromJsonResult(
|
||||||
{
|
|
||||||
var targetEntryValue = FromJsonResult(
|
|
||||||
sourceProperty.Value,
|
sourceProperty.Value,
|
||||||
targetEntryType,
|
targetEntryType,
|
||||||
_includeNonPublic);
|
this._includeNonPublic);
|
||||||
targetDictionary.Add(sourceProperty.Key, targetEntryValue);
|
targetDictionary.Add(sourceProperty.Key, targetEntryValue);
|
||||||
}
|
} catch {
|
||||||
catch
|
|
||||||
{
|
|
||||||
// ignored
|
// ignored
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void PopulateObject(IDictionary<string, object> sourceProperties)
|
private void PopulateObject(IDictionary<String, Object> sourceProperties) {
|
||||||
{
|
if(this._targetType.IsValueType()) {
|
||||||
if (_targetType.IsValueType())
|
this.PopulateFields(sourceProperties);
|
||||||
{
|
|
||||||
PopulateFields(sourceProperties);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
PopulateProperties(sourceProperties);
|
this.PopulateProperties(sourceProperties);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void PopulateProperties(IDictionary<string, object> sourceProperties)
|
private void PopulateProperties(IDictionary<String, Object> sourceProperties) {
|
||||||
{
|
IEnumerable<PropertyInfo> properties = PropertyTypeCache.RetrieveFilteredProperties(this._targetType, false, p => p.CanWrite);
|
||||||
var properties = PropertyTypeCache.RetrieveFilteredProperties(_targetType, false, p => p.CanWrite);
|
|
||||||
|
|
||||||
foreach (var property in properties)
|
foreach(PropertyInfo property in properties) {
|
||||||
{
|
Object sourcePropertyValue = GetSourcePropertyValue(sourceProperties, property);
|
||||||
var sourcePropertyValue = GetSourcePropertyValue(sourceProperties, property);
|
if(sourcePropertyValue == null) {
|
||||||
if (sourcePropertyValue == null) continue;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
try
|
try {
|
||||||
{
|
Object currentPropertyValue = !property.PropertyType.IsArray
|
||||||
var currentPropertyValue = !property.PropertyType.IsArray
|
? property.GetCacheGetMethod(this._includeNonPublic)(this._target)
|
||||||
? property.GetCacheGetMethod(_includeNonPublic)(_target)
|
|
||||||
: null;
|
: null;
|
||||||
|
|
||||||
var targetPropertyValue = FromJsonResult(
|
Object targetPropertyValue = FromJsonResult(
|
||||||
sourcePropertyValue,
|
sourcePropertyValue,
|
||||||
property.PropertyType,
|
property.PropertyType,
|
||||||
ref currentPropertyValue,
|
ref currentPropertyValue,
|
||||||
_includeNonPublic);
|
this._includeNonPublic);
|
||||||
|
|
||||||
property.GetCacheSetMethod(_includeNonPublic)(_target, new[] { targetPropertyValue });
|
property.GetCacheSetMethod(this._includeNonPublic)(this._target, new[] { targetPropertyValue });
|
||||||
}
|
} catch {
|
||||||
catch
|
|
||||||
{
|
|
||||||
// ignored
|
// ignored
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void PopulateFields(IDictionary<string, object> sourceProperties)
|
private void PopulateFields(IDictionary<String, Object> sourceProperties) {
|
||||||
{
|
foreach(FieldInfo field in FieldTypeCache.RetrieveAllFields(this._targetType)) {
|
||||||
foreach (var field in FieldTypeCache.RetrieveAllFields(_targetType))
|
Object sourcePropertyValue = GetSourcePropertyValue(sourceProperties, field);
|
||||||
{
|
if(sourcePropertyValue == null) {
|
||||||
var sourcePropertyValue = GetSourcePropertyValue(sourceProperties, field);
|
continue;
|
||||||
if (sourcePropertyValue == null) continue;
|
}
|
||||||
|
|
||||||
var targetPropertyValue = FromJsonResult(
|
Object targetPropertyValue = FromJsonResult(
|
||||||
sourcePropertyValue,
|
sourcePropertyValue,
|
||||||
field.FieldType,
|
field.FieldType,
|
||||||
_includeNonPublic);
|
this._includeNonPublic);
|
||||||
|
|
||||||
try
|
try {
|
||||||
{
|
field.SetValue(this._target, targetPropertyValue);
|
||||||
field.SetValue(_target, targetPropertyValue);
|
} catch {
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
// ignored
|
// ignored
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,8 @@
|
|||||||
namespace Unosquare.Swan.Formatters
|
using System;
|
||||||
{
|
using System.Collections.Generic;
|
||||||
using System;
|
using System.Text;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Text;
|
|
||||||
|
|
||||||
|
namespace Unosquare.Swan.Formatters {
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A very simple, light-weight JSON library written by Mario
|
/// A very simple, light-weight JSON library written by Mario
|
||||||
/// to teach Geo how things are done
|
/// to teach Geo how things are done
|
||||||
@ -11,80 +10,76 @@
|
|||||||
/// This is an useful helper for small tasks but it doesn't represent a full-featured
|
/// This is an useful helper for small tasks but it doesn't represent a full-featured
|
||||||
/// serializer such as the beloved Json.NET.
|
/// serializer such as the beloved Json.NET.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public partial class Json
|
public partial class Json {
|
||||||
{
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A simple JSON Deserializer.
|
/// A simple JSON Deserializer.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private class Deserializer
|
private class Deserializer {
|
||||||
{
|
|
||||||
#region State Variables
|
#region State Variables
|
||||||
|
|
||||||
private readonly object _result;
|
private readonly Object _result;
|
||||||
private readonly Dictionary<string, object> _resultObject;
|
private readonly Dictionary<String, Object> _resultObject;
|
||||||
private readonly List<object> _resultArray;
|
private readonly List<Object> _resultArray;
|
||||||
|
|
||||||
private readonly ReadState _state = ReadState.WaitingForRootOpen;
|
private readonly ReadState _state = ReadState.WaitingForRootOpen;
|
||||||
private readonly string _currentFieldName;
|
private readonly String _currentFieldName;
|
||||||
private readonly string _json;
|
private readonly String _json;
|
||||||
|
|
||||||
private int _index;
|
private Int32 _index;
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
private Deserializer(string json, int startIndex)
|
private Deserializer(String json, Int32 startIndex) {
|
||||||
{
|
this._json = json;
|
||||||
_json = json;
|
|
||||||
|
|
||||||
for (_index = startIndex; _index < _json.Length; _index++)
|
for(this._index = startIndex; this._index < this._json.Length; this._index++) {
|
||||||
{
|
|
||||||
#region Wait for { or [
|
#region Wait for { or [
|
||||||
|
|
||||||
if (_state == ReadState.WaitingForRootOpen)
|
if(this._state == ReadState.WaitingForRootOpen) {
|
||||||
{
|
if(Char.IsWhiteSpace(this._json, this._index)) {
|
||||||
if (char.IsWhiteSpace(_json, _index)) continue;
|
|
||||||
|
|
||||||
if (_json[_index] == OpenObjectChar)
|
|
||||||
{
|
|
||||||
_resultObject = new Dictionary<string, object>();
|
|
||||||
_state = ReadState.WaitingForField;
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_json[_index] == OpenArrayChar)
|
if(this._json[this._index] == OpenObjectChar) {
|
||||||
{
|
this._resultObject = new Dictionary<String, Object>();
|
||||||
_resultArray = new List<object>();
|
this._state = ReadState.WaitingForField;
|
||||||
_state = ReadState.WaitingForValue;
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
throw CreateParserException($"'{OpenObjectChar}' or '{OpenArrayChar}'");
|
if(this._json[this._index] == OpenArrayChar) {
|
||||||
|
this._resultArray = new List<Object>();
|
||||||
|
this._state = ReadState.WaitingForValue;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw this.CreateParserException($"'{OpenObjectChar}' or '{OpenArrayChar}'");
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region Wait for opening field " (only applies for object results)
|
#region Wait for opening field " (only applies for object results)
|
||||||
|
|
||||||
if (_state == ReadState.WaitingForField)
|
if(this._state == ReadState.WaitingForField) {
|
||||||
{
|
if(Char.IsWhiteSpace(this._json, this._index)) {
|
||||||
if (char.IsWhiteSpace(_json, _index)) continue;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
// Handle empty arrays and empty objects
|
// Handle empty arrays and empty objects
|
||||||
if ((_resultObject != null && _json[_index] == CloseObjectChar)
|
if(this._resultObject != null && this._json[this._index] == CloseObjectChar
|
||||||
|| (_resultArray != null && _json[_index] == CloseArrayChar))
|
|| this._resultArray != null && this._json[this._index] == CloseArrayChar) {
|
||||||
{
|
this._result = this._resultObject ?? this._resultArray as Object;
|
||||||
_result = _resultObject ?? _resultArray as object;
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_json[_index] != StringQuotedChar)
|
if(this._json[this._index] != StringQuotedChar) {
|
||||||
throw CreateParserException($"'{StringQuotedChar}'");
|
throw this.CreateParserException($"'{StringQuotedChar}'");
|
||||||
|
}
|
||||||
|
|
||||||
var charCount = GetFieldNameCount();
|
Int32 charCount = this.GetFieldNameCount();
|
||||||
|
|
||||||
_currentFieldName = Unescape(_json.SliceLength(_index + 1, charCount));
|
this._currentFieldName = Unescape(this._json.SliceLength(this._index + 1, charCount));
|
||||||
_index += charCount + 1;
|
this._index += charCount + 1;
|
||||||
_state = ReadState.WaitingForColon;
|
this._state = ReadState.WaitingForColon;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -92,14 +87,16 @@
|
|||||||
|
|
||||||
#region Wait for field-value separator : (only applies for object results
|
#region Wait for field-value separator : (only applies for object results
|
||||||
|
|
||||||
if (_state == ReadState.WaitingForColon)
|
if(this._state == ReadState.WaitingForColon) {
|
||||||
{
|
if(Char.IsWhiteSpace(this._json, this._index)) {
|
||||||
if (char.IsWhiteSpace(_json, _index)) continue;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (_json[_index] != ValueSeparatorChar)
|
if(this._json[this._index] != ValueSeparatorChar) {
|
||||||
throw CreateParserException($"'{ValueSeparatorChar}'");
|
throw this.CreateParserException($"'{ValueSeparatorChar}'");
|
||||||
|
}
|
||||||
|
|
||||||
_state = ReadState.WaitingForValue;
|
this._state = ReadState.WaitingForValue;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -107,49 +104,48 @@
|
|||||||
|
|
||||||
#region Wait for and Parse the value
|
#region Wait for and Parse the value
|
||||||
|
|
||||||
if (_state == ReadState.WaitingForValue)
|
if(this._state == ReadState.WaitingForValue) {
|
||||||
{
|
if(Char.IsWhiteSpace(this._json, this._index)) {
|
||||||
if (char.IsWhiteSpace(_json, _index)) continue;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
// Handle empty arrays and empty objects
|
// Handle empty arrays and empty objects
|
||||||
if ((_resultObject != null && _json[_index] == CloseObjectChar)
|
if(this._resultObject != null && this._json[this._index] == CloseObjectChar
|
||||||
|| (_resultArray != null && _json[_index] == CloseArrayChar))
|
|| this._resultArray != null && this._json[this._index] == CloseArrayChar) {
|
||||||
{
|
this._result = this._resultObject ?? this._resultArray as Object;
|
||||||
_result = _resultObject ?? _resultArray as object;
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// determine the value based on what it starts with
|
// determine the value based on what it starts with
|
||||||
switch (_json[_index])
|
switch(this._json[this._index]) {
|
||||||
{
|
|
||||||
case StringQuotedChar: // expect a string
|
case StringQuotedChar: // expect a string
|
||||||
ExtractStringQuoted();
|
this.ExtractStringQuoted();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case OpenObjectChar: // expect object
|
case OpenObjectChar: // expect object
|
||||||
case OpenArrayChar: // expect array
|
case OpenArrayChar: // expect array
|
||||||
ExtractObject();
|
this.ExtractObject();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 't': // expect true
|
case 't': // expect true
|
||||||
ExtractConstant(TrueLiteral, true);
|
this.ExtractConstant(TrueLiteral, true);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'f': // expect false
|
case 'f': // expect false
|
||||||
ExtractConstant(FalseLiteral, false);
|
this.ExtractConstant(FalseLiteral, false);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'n': // expect null
|
case 'n': // expect null
|
||||||
ExtractConstant(NullLiteral, null);
|
this.ExtractConstant(NullLiteral, null);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default: // expect number
|
default: // expect number
|
||||||
ExtractNumber();
|
this.ExtractNumber();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
_currentFieldName = null;
|
this._currentFieldName = null;
|
||||||
_state = ReadState.WaitingForNextOrRootClose;
|
this._state = ReadState.WaitingForNextOrRootClose;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -157,84 +153,83 @@
|
|||||||
|
|
||||||
#region Wait for closing ], } or an additional field or value ,
|
#region Wait for closing ], } or an additional field or value ,
|
||||||
|
|
||||||
if (_state != ReadState.WaitingForNextOrRootClose) continue;
|
if(this._state != ReadState.WaitingForNextOrRootClose) {
|
||||||
|
|
||||||
if (char.IsWhiteSpace(_json, _index)) continue;
|
|
||||||
|
|
||||||
if (_json[_index] == FieldSeparatorChar)
|
|
||||||
{
|
|
||||||
if (_resultObject != null)
|
|
||||||
{
|
|
||||||
_state = ReadState.WaitingForField;
|
|
||||||
_currentFieldName = null;
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
_state = ReadState.WaitingForValue;
|
if(Char.IsWhiteSpace(this._json, this._index)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((_resultObject != null && _json[_index] == CloseObjectChar) ||
|
if(this._json[this._index] == FieldSeparatorChar) {
|
||||||
(_resultArray != null && _json[_index] == CloseArrayChar))
|
if(this._resultObject != null) {
|
||||||
{
|
this._state = ReadState.WaitingForField;
|
||||||
_result = _resultObject ?? _resultArray as object;
|
this._currentFieldName = null;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._state = ReadState.WaitingForValue;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(this._resultObject != null && this._json[this._index] == CloseObjectChar ||
|
||||||
|
this._resultArray != null && this._json[this._index] == CloseArrayChar) {
|
||||||
|
this._result = this._resultObject ?? this._resultArray as Object;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
throw CreateParserException($"'{FieldSeparatorChar}' '{CloseObjectChar}' or '{CloseArrayChar}'");
|
throw this.CreateParserException($"'{FieldSeparatorChar}' '{CloseObjectChar}' or '{CloseArrayChar}'");
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static object DeserializeInternal(string json) => new Deserializer(json, 0)._result;
|
internal static Object DeserializeInternal(String json) => new Deserializer(json, 0)._result;
|
||||||
|
|
||||||
private static string Unescape(string str)
|
private static String Unescape(String str) {
|
||||||
{
|
|
||||||
// check if we need to unescape at all
|
// check if we need to unescape at all
|
||||||
if (str.IndexOf(StringEscapeChar) < 0)
|
if(str.IndexOf(StringEscapeChar) < 0) {
|
||||||
return str;
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
var builder = new StringBuilder(str.Length);
|
StringBuilder builder = new StringBuilder(str.Length);
|
||||||
for (var i = 0; i < str.Length; i++)
|
for(Int32 i = 0; i < str.Length; i++) {
|
||||||
{
|
if(str[i] != StringEscapeChar) {
|
||||||
if (str[i] != StringEscapeChar)
|
_ = builder.Append(str[i]);
|
||||||
{
|
|
||||||
builder.Append(str[i]);
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (i + 1 > str.Length - 1)
|
if(i + 1 > str.Length - 1) {
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
// escape sequence begins here
|
// escape sequence begins here
|
||||||
switch (str[i + 1])
|
switch(str[i + 1]) {
|
||||||
{
|
|
||||||
case 'u':
|
case 'u':
|
||||||
i = ExtractEscapeSequence(str, i, builder);
|
i = ExtractEscapeSequence(str, i, builder);
|
||||||
break;
|
break;
|
||||||
case 'b':
|
case 'b':
|
||||||
builder.Append('\b');
|
_ = builder.Append('\b');
|
||||||
i += 1;
|
i += 1;
|
||||||
break;
|
break;
|
||||||
case 't':
|
case 't':
|
||||||
builder.Append('\t');
|
_ = builder.Append('\t');
|
||||||
i += 1;
|
i += 1;
|
||||||
break;
|
break;
|
||||||
case 'n':
|
case 'n':
|
||||||
builder.Append('\n');
|
_ = builder.Append('\n');
|
||||||
i += 1;
|
i += 1;
|
||||||
break;
|
break;
|
||||||
case 'f':
|
case 'f':
|
||||||
builder.Append('\f');
|
_ = builder.Append('\f');
|
||||||
i += 1;
|
i += 1;
|
||||||
break;
|
break;
|
||||||
case 'r':
|
case 'r':
|
||||||
builder.Append('\r');
|
_ = builder.Append('\r');
|
||||||
i += 1;
|
i += 1;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
builder.Append(str[i + 1]);
|
_ = builder.Append(str[i + 1]);
|
||||||
i += 1;
|
i += 1;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -243,30 +238,27 @@
|
|||||||
return builder.ToString();
|
return builder.ToString();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int ExtractEscapeSequence(string str, int i, StringBuilder builder)
|
private static Int32 ExtractEscapeSequence(String str, Int32 i, StringBuilder builder) {
|
||||||
{
|
Int32 startIndex = i + 2;
|
||||||
var startIndex = i + 2;
|
Int32 endIndex = i + 5;
|
||||||
var endIndex = i + 5;
|
if(endIndex > str.Length - 1) {
|
||||||
if (endIndex > str.Length - 1)
|
_ = builder.Append(str[i + 1]);
|
||||||
{
|
|
||||||
builder.Append(str[i + 1]);
|
|
||||||
i += 1;
|
i += 1;
|
||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
|
|
||||||
var hexCode = str.Slice(startIndex, endIndex).ConvertHexadecimalToBytes();
|
Byte[] hexCode = str.Slice(startIndex, endIndex).ConvertHexadecimalToBytes();
|
||||||
builder.Append(Encoding.BigEndianUnicode.GetChars(hexCode));
|
_ = builder.Append(Encoding.BigEndianUnicode.GetChars(hexCode));
|
||||||
i += 5;
|
i += 5;
|
||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
|
|
||||||
private int GetFieldNameCount()
|
private Int32 GetFieldNameCount() {
|
||||||
{
|
Int32 charCount = 0;
|
||||||
var charCount = 0;
|
for(Int32 j = this._index + 1; j < this._json.Length; j++) {
|
||||||
for (var j = _index + 1; j < _json.Length; j++)
|
if(this._json[j] == StringQuotedChar && this._json[j - 1] != StringEscapeChar) {
|
||||||
{
|
|
||||||
if (_json[j] == StringQuotedChar && _json[j - 1] != StringEscapeChar)
|
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
charCount++;
|
charCount++;
|
||||||
}
|
}
|
||||||
@ -274,95 +266,95 @@
|
|||||||
return charCount;
|
return charCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ExtractObject()
|
private void ExtractObject() {
|
||||||
{
|
|
||||||
// Extract and set the value
|
// Extract and set the value
|
||||||
var deserializer = new Deserializer(_json, _index);
|
Deserializer deserializer = new Deserializer(this._json, this._index);
|
||||||
|
|
||||||
if (_currentFieldName != null)
|
if(this._currentFieldName != null) {
|
||||||
_resultObject[_currentFieldName] = deserializer._result;
|
this._resultObject[this._currentFieldName] = deserializer._result;
|
||||||
else
|
} else {
|
||||||
_resultArray.Add(deserializer._result);
|
this._resultArray.Add(deserializer._result);
|
||||||
|
|
||||||
_index = deserializer._index;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ExtractNumber()
|
this._index = deserializer._index;
|
||||||
{
|
}
|
||||||
var charCount = 0;
|
|
||||||
for (var j = _index; j < _json.Length; j++)
|
private void ExtractNumber() {
|
||||||
{
|
Int32 charCount = 0;
|
||||||
if (char.IsWhiteSpace(_json[j]) || _json[j] == FieldSeparatorChar
|
for(Int32 j = this._index; j < this._json.Length; j++) {
|
||||||
|| (_resultObject != null && _json[j] == CloseObjectChar)
|
if(Char.IsWhiteSpace(this._json[j]) || this._json[j] == FieldSeparatorChar
|
||||||
|| (_resultArray != null && _json[j] == CloseArrayChar))
|
|| this._resultObject != null && this._json[j] == CloseObjectChar
|
||||||
|
|| this._resultArray != null && this._json[j] == CloseArrayChar) {
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
charCount++;
|
charCount++;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Extract and set the value
|
// Extract and set the value
|
||||||
var stringValue = _json.SliceLength(_index, charCount);
|
String stringValue = this._json.SliceLength(this._index, charCount);
|
||||||
|
|
||||||
if (decimal.TryParse(stringValue, out var value) == false)
|
if(Decimal.TryParse(stringValue, out Decimal value) == false) {
|
||||||
throw CreateParserException("[number]");
|
throw this.CreateParserException("[number]");
|
||||||
|
|
||||||
if (_currentFieldName != null)
|
|
||||||
_resultObject[_currentFieldName] = value;
|
|
||||||
else
|
|
||||||
_resultArray.Add(value);
|
|
||||||
|
|
||||||
_index += charCount - 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ExtractConstant(string boolValue, bool? value)
|
if(this._currentFieldName != null) {
|
||||||
{
|
this._resultObject[this._currentFieldName] = value;
|
||||||
if (!_json.SliceLength(_index, boolValue.Length).Equals(boolValue))
|
} else {
|
||||||
throw CreateParserException($"'{ValueSeparatorChar}'");
|
this._resultArray.Add(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
this._index += charCount - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ExtractConstant(String boolValue, Boolean? value) {
|
||||||
|
if(!this._json.SliceLength(this._index, boolValue.Length).Equals(boolValue)) {
|
||||||
|
throw this.CreateParserException($"'{ValueSeparatorChar}'");
|
||||||
|
}
|
||||||
|
|
||||||
// Extract and set the value
|
// Extract and set the value
|
||||||
if (_currentFieldName != null)
|
if(this._currentFieldName != null) {
|
||||||
_resultObject[_currentFieldName] = value;
|
this._resultObject[this._currentFieldName] = value;
|
||||||
else
|
} else {
|
||||||
_resultArray.Add(value);
|
this._resultArray.Add(value);
|
||||||
|
|
||||||
_index += boolValue.Length - 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ExtractStringQuoted()
|
this._index += boolValue.Length - 1;
|
||||||
{
|
}
|
||||||
var charCount = 0;
|
|
||||||
var escapeCharFound = false;
|
|
||||||
for (var j = _index + 1; j < _json.Length; j++)
|
|
||||||
{
|
|
||||||
if (_json[j] == StringQuotedChar && !escapeCharFound)
|
|
||||||
break;
|
|
||||||
|
|
||||||
escapeCharFound = _json[j] == StringEscapeChar && !escapeCharFound;
|
private void ExtractStringQuoted() {
|
||||||
|
Int32 charCount = 0;
|
||||||
|
Boolean escapeCharFound = false;
|
||||||
|
for(Int32 j = this._index + 1; j < this._json.Length; j++) {
|
||||||
|
if(this._json[j] == StringQuotedChar && !escapeCharFound) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
escapeCharFound = this._json[j] == StringEscapeChar && !escapeCharFound;
|
||||||
charCount++;
|
charCount++;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Extract and set the value
|
// Extract and set the value
|
||||||
var value = Unescape(_json.SliceLength(_index + 1, charCount));
|
String value = Unescape(this._json.SliceLength(this._index + 1, charCount));
|
||||||
if (_currentFieldName != null)
|
if(this._currentFieldName != null) {
|
||||||
_resultObject[_currentFieldName] = value;
|
this._resultObject[this._currentFieldName] = value;
|
||||||
else
|
} else {
|
||||||
_resultArray.Add(value);
|
this._resultArray.Add(value);
|
||||||
|
|
||||||
_index += charCount + 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private FormatException CreateParserException(string expected)
|
this._index += charCount + 1;
|
||||||
{
|
}
|
||||||
var textPosition = _json.TextPositionAt(_index);
|
|
||||||
|
private FormatException CreateParserException(String expected) {
|
||||||
|
Tuple<Int32, Int32> textPosition = this._json.TextPositionAt(this._index);
|
||||||
return new FormatException(
|
return new FormatException(
|
||||||
$"Parser error (Line {textPosition.Item1}, Col {textPosition.Item2}, State {_state}): Expected {expected} but got '{_json[_index]}'.");
|
$"Parser error (Line {textPosition.Item1}, Col {textPosition.Item2}, State {this._state}): Expected {expected} but got '{this._json[this._index]}'.");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Defines the different JSON read states.
|
/// Defines the different JSON read states.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private enum ReadState
|
private enum ReadState {
|
||||||
{
|
|
||||||
WaitingForRootOpen,
|
WaitingForRootOpen,
|
||||||
WaitingForField,
|
WaitingForField,
|
||||||
WaitingForColon,
|
WaitingForColon,
|
||||||
|
@ -1,12 +1,11 @@
|
|||||||
namespace Unosquare.Swan.Formatters
|
using System;
|
||||||
{
|
using System.Collections;
|
||||||
using System;
|
using System.Collections.Generic;
|
||||||
using System.Collections;
|
using System.Linq;
|
||||||
using System.Collections.Generic;
|
using System.Reflection;
|
||||||
using System.Linq;
|
using System.Text;
|
||||||
using System.Reflection;
|
|
||||||
using System.Text;
|
|
||||||
|
|
||||||
|
namespace Unosquare.Swan.Formatters {
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A very simple, light-weight JSON library written by Mario
|
/// A very simple, light-weight JSON library written by Mario
|
||||||
/// to teach Geo how things are done
|
/// to teach Geo how things are done
|
||||||
@ -14,21 +13,19 @@
|
|||||||
/// This is an useful helper for small tasks but it doesn't represent a full-featured
|
/// This is an useful helper for small tasks but it doesn't represent a full-featured
|
||||||
/// serializer such as the beloved Json.NET.
|
/// serializer such as the beloved Json.NET.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public partial class Json
|
public partial class Json {
|
||||||
{
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A simple JSON serializer.
|
/// A simple JSON serializer.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private class Serializer
|
private class Serializer {
|
||||||
{
|
|
||||||
#region Private Declarations
|
#region Private Declarations
|
||||||
|
|
||||||
private static readonly Dictionary<int, string> IndentStrings = new Dictionary<int, string>();
|
private static readonly Dictionary<Int32, String> IndentStrings = new Dictionary<Int32, String>();
|
||||||
|
|
||||||
private readonly SerializerOptions _options;
|
private readonly SerializerOptions _options;
|
||||||
private readonly string _result;
|
private readonly String _result;
|
||||||
private readonly StringBuilder _builder;
|
private readonly StringBuilder _builder;
|
||||||
private readonly string _lastCommaSearch;
|
private readonly String _lastCommaSearch;
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
@ -40,74 +37,66 @@
|
|||||||
/// <param name="obj">The object.</param>
|
/// <param name="obj">The object.</param>
|
||||||
/// <param name="depth">The depth.</param>
|
/// <param name="depth">The depth.</param>
|
||||||
/// <param name="options">The options.</param>
|
/// <param name="options">The options.</param>
|
||||||
private Serializer(object obj, int depth, SerializerOptions options)
|
private Serializer(Object obj, Int32 depth, SerializerOptions options) {
|
||||||
{
|
if(depth > 20) {
|
||||||
if (depth > 20)
|
|
||||||
{
|
|
||||||
throw new InvalidOperationException(
|
throw new InvalidOperationException(
|
||||||
"The max depth (20) has been reached. Serializer can not continue.");
|
"The max depth (20) has been reached. Serializer can not continue.");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Basic Type Handling (nulls, strings, number, date and bool)
|
// Basic Type Handling (nulls, strings, number, date and bool)
|
||||||
_result = ResolveBasicType(obj);
|
this._result = ResolveBasicType(obj);
|
||||||
|
|
||||||
if (string.IsNullOrWhiteSpace(_result) == false)
|
if(String.IsNullOrWhiteSpace(this._result) == false) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
_options = options;
|
this._options = options;
|
||||||
_lastCommaSearch = FieldSeparatorChar + (_options.Format ? Environment.NewLine : string.Empty);
|
this._lastCommaSearch = FieldSeparatorChar + (this._options.Format ? Environment.NewLine : String.Empty);
|
||||||
|
|
||||||
// Handle circular references correctly and avoid them
|
// Handle circular references correctly and avoid them
|
||||||
if (options.IsObjectPresent(obj))
|
if(options.IsObjectPresent(obj)) {
|
||||||
{
|
this._result = $"{{ \"$circref\": \"{Escape(obj.GetHashCode().ToStringInvariant(), false)}\" }}";
|
||||||
_result = $"{{ \"$circref\": \"{Escape(obj.GetHashCode().ToStringInvariant(), false)}\" }}";
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// At this point, we will need to construct the object with a StringBuilder.
|
// At this point, we will need to construct the object with a StringBuilder.
|
||||||
_builder = new StringBuilder();
|
this._builder = new StringBuilder();
|
||||||
|
|
||||||
switch (obj)
|
switch(obj) {
|
||||||
{
|
|
||||||
case IDictionary itemsZero when itemsZero.Count == 0:
|
case IDictionary itemsZero when itemsZero.Count == 0:
|
||||||
_result = EmptyObjectLiteral;
|
this._result = EmptyObjectLiteral;
|
||||||
break;
|
break;
|
||||||
case IDictionary items:
|
case IDictionary items:
|
||||||
_result = ResolveDictionary(items, depth);
|
this._result = this.ResolveDictionary(items, depth);
|
||||||
break;
|
break;
|
||||||
case IEnumerable enumerableZero when !enumerableZero.Cast<object>().Any():
|
case IEnumerable enumerableZero when !enumerableZero.Cast<Object>().Any():
|
||||||
_result = EmptyArrayLiteral;
|
this._result = EmptyArrayLiteral;
|
||||||
break;
|
break;
|
||||||
case IEnumerable enumerableBytes when enumerableBytes is byte[] bytes:
|
case IEnumerable enumerableBytes when enumerableBytes is Byte[] bytes:
|
||||||
_result = Serialize(bytes.ToBase64(), depth, _options);
|
this._result = Serialize(bytes.ToBase64(), depth, this._options);
|
||||||
break;
|
break;
|
||||||
case IEnumerable enumerable:
|
case IEnumerable enumerable:
|
||||||
_result = ResolveEnumerable(enumerable, depth);
|
this._result = this.ResolveEnumerable(enumerable, depth);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
_result = ResolveObject(obj, depth);
|
this._result = this.ResolveObject(obj, depth);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static string Serialize(object obj, int depth, SerializerOptions options)
|
internal static String Serialize(Object obj, Int32 depth, SerializerOptions options) => new Serializer(obj, depth, options)._result;
|
||||||
{
|
|
||||||
return new Serializer(obj, depth, options)._result;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region Helper Methods
|
#region Helper Methods
|
||||||
|
|
||||||
private static string ResolveBasicType(object obj)
|
private static String ResolveBasicType(Object obj) {
|
||||||
{
|
switch(obj) {
|
||||||
switch (obj)
|
|
||||||
{
|
|
||||||
case null:
|
case null:
|
||||||
return NullLiteral;
|
return NullLiteral;
|
||||||
case string s:
|
case String s:
|
||||||
return Escape(s, true);
|
return Escape(s, true);
|
||||||
case bool b:
|
case Boolean b:
|
||||||
return b ? TrueLiteral : FalseLiteral;
|
return b ? TrueLiteral : FalseLiteral;
|
||||||
case Type _:
|
case Type _:
|
||||||
case Assembly _:
|
case Assembly _:
|
||||||
@ -118,81 +107,84 @@
|
|||||||
case DateTime d:
|
case DateTime d:
|
||||||
return $"{StringQuotedChar}{d:s}{StringQuotedChar}";
|
return $"{StringQuotedChar}{d:s}{StringQuotedChar}";
|
||||||
default:
|
default:
|
||||||
var targetType = obj.GetType();
|
Type targetType = obj.GetType();
|
||||||
|
|
||||||
if (!Definitions.BasicTypesInfo.ContainsKey(targetType))
|
if(!Definitions.BasicTypesInfo.ContainsKey(targetType)) {
|
||||||
return string.Empty;
|
return String.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
var escapedValue = Escape(Definitions.BasicTypesInfo[targetType].ToStringInvariant(obj), false);
|
String escapedValue = Escape(Definitions.BasicTypesInfo[targetType].ToStringInvariant(obj), false);
|
||||||
|
|
||||||
return decimal.TryParse(escapedValue, out _)
|
return Decimal.TryParse(escapedValue, out _)
|
||||||
? $"{escapedValue}"
|
? $"{escapedValue}"
|
||||||
: $"{StringQuotedChar}{escapedValue}{StringQuotedChar}";
|
: $"{StringQuotedChar}{escapedValue}{StringQuotedChar}";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool IsNonEmptyJsonArrayOrObject(string serialized)
|
private static Boolean IsNonEmptyJsonArrayOrObject(String serialized) {
|
||||||
{
|
if(serialized.Equals(EmptyObjectLiteral) || serialized.Equals(EmptyArrayLiteral)) {
|
||||||
if (serialized.Equals(EmptyObjectLiteral) || serialized.Equals(EmptyArrayLiteral)) return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// find the first position the character is not a space
|
// find the first position the character is not a space
|
||||||
return serialized.Where(c => c != ' ').Select(c => c == OpenObjectChar || c == OpenArrayChar).FirstOrDefault();
|
return serialized.Where(c => c != ' ').Select(c => c == OpenObjectChar || c == OpenArrayChar).FirstOrDefault();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static string Escape(string str, bool quoted)
|
private static String Escape(String str, Boolean quoted) {
|
||||||
{
|
if(str == null) {
|
||||||
if (str == null)
|
return String.Empty;
|
||||||
return string.Empty;
|
}
|
||||||
|
|
||||||
|
StringBuilder builder = new StringBuilder(str.Length * 2);
|
||||||
|
if(quoted) {
|
||||||
|
_ = builder.Append(StringQuotedChar);
|
||||||
|
}
|
||||||
|
|
||||||
var builder = new StringBuilder(str.Length * 2);
|
|
||||||
if (quoted) builder.Append(StringQuotedChar);
|
|
||||||
Escape(str, builder);
|
Escape(str, builder);
|
||||||
if (quoted) builder.Append(StringQuotedChar);
|
if(quoted) {
|
||||||
|
_ = builder.Append(StringQuotedChar);
|
||||||
|
}
|
||||||
|
|
||||||
return builder.ToString();
|
return builder.ToString();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void Escape(string str, StringBuilder builder)
|
private static void Escape(String str, StringBuilder builder) {
|
||||||
{
|
foreach(Char currentChar in str) {
|
||||||
foreach (var currentChar in str)
|
switch(currentChar) {
|
||||||
{
|
|
||||||
switch (currentChar)
|
|
||||||
{
|
|
||||||
case '\\':
|
case '\\':
|
||||||
case '"':
|
case '"':
|
||||||
case '/':
|
case '/':
|
||||||
builder
|
_ = builder
|
||||||
.Append('\\')
|
.Append('\\')
|
||||||
.Append(currentChar);
|
.Append(currentChar);
|
||||||
break;
|
break;
|
||||||
case '\b':
|
case '\b':
|
||||||
builder.Append("\\b");
|
_ = builder.Append("\\b");
|
||||||
break;
|
break;
|
||||||
case '\t':
|
case '\t':
|
||||||
builder.Append("\\t");
|
_ = builder.Append("\\t");
|
||||||
break;
|
break;
|
||||||
case '\n':
|
case '\n':
|
||||||
builder.Append("\\n");
|
_ = builder.Append("\\n");
|
||||||
break;
|
break;
|
||||||
case '\f':
|
case '\f':
|
||||||
builder.Append("\\f");
|
_ = builder.Append("\\f");
|
||||||
break;
|
break;
|
||||||
case '\r':
|
case '\r':
|
||||||
builder.Append("\\r");
|
_ = builder.Append("\\r");
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
if (currentChar < ' ')
|
if(currentChar < ' ') {
|
||||||
{
|
Byte[] escapeBytes = BitConverter.GetBytes((UInt16)currentChar);
|
||||||
var escapeBytes = BitConverter.GetBytes((ushort)currentChar);
|
if(BitConverter.IsLittleEndian == false) {
|
||||||
if (BitConverter.IsLittleEndian == false)
|
|
||||||
Array.Reverse(escapeBytes);
|
Array.Reverse(escapeBytes);
|
||||||
|
}
|
||||||
|
|
||||||
builder.Append("\\u")
|
_ = builder.Append("\\u")
|
||||||
.Append(escapeBytes[1].ToString("X").PadLeft(2, '0'))
|
.Append(escapeBytes[1].ToString("X").PadLeft(2, '0'))
|
||||||
.Append(escapeBytes[0].ToString("X").PadLeft(2, '0'));
|
.Append(escapeBytes[0].ToString("X").PadLeft(2, '0'));
|
||||||
}
|
} else {
|
||||||
else
|
_ = builder.Append(currentChar);
|
||||||
{
|
|
||||||
builder.Append(currentChar);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
@ -200,29 +192,25 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private Dictionary<string, object> CreateDictionary(
|
private Dictionary<String, Object> CreateDictionary(
|
||||||
Dictionary<string, MemberInfo> fields,
|
Dictionary<String, MemberInfo> fields,
|
||||||
string targetType,
|
String targetType,
|
||||||
object target)
|
Object target) {
|
||||||
{
|
|
||||||
// Create the dictionary and extract the properties
|
// Create the dictionary and extract the properties
|
||||||
var objectDictionary = new Dictionary<string, object>();
|
Dictionary<String, Object> objectDictionary = new Dictionary<String, Object>();
|
||||||
|
|
||||||
if (string.IsNullOrWhiteSpace(_options.TypeSpecifier) == false)
|
if(String.IsNullOrWhiteSpace(this._options.TypeSpecifier) == false) {
|
||||||
objectDictionary[_options.TypeSpecifier] = targetType;
|
objectDictionary[this._options.TypeSpecifier] = targetType;
|
||||||
|
}
|
||||||
|
|
||||||
foreach (var field in fields)
|
foreach(KeyValuePair<String, MemberInfo> field in fields) {
|
||||||
{
|
|
||||||
// Build the dictionary using property names and values
|
// Build the dictionary using property names and values
|
||||||
// Note: used to be: property.GetValue(target); but we would be reading private properties
|
// Note: used to be: property.GetValue(target); but we would be reading private properties
|
||||||
try
|
try {
|
||||||
{
|
|
||||||
objectDictionary[field.Key] = field.Value is PropertyInfo property
|
objectDictionary[field.Key] = field.Value is PropertyInfo property
|
||||||
? property.GetCacheGetMethod(_options.IncludeNonPublic)(target)
|
? property.GetCacheGetMethod(this._options.IncludeNonPublic)(target)
|
||||||
: (field.Value as FieldInfo)?.GetValue(target);
|
: (field.Value as FieldInfo)?.GetValue(target);
|
||||||
}
|
} catch {
|
||||||
catch
|
|
||||||
{
|
|
||||||
/* ignored */
|
/* ignored */
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -230,127 +218,127 @@
|
|||||||
return objectDictionary;
|
return objectDictionary;
|
||||||
}
|
}
|
||||||
|
|
||||||
private string ResolveDictionary(IDictionary items, int depth)
|
private String ResolveDictionary(IDictionary items, Int32 depth) {
|
||||||
{
|
this.Append(OpenObjectChar, depth);
|
||||||
Append(OpenObjectChar, depth);
|
this.AppendLine();
|
||||||
AppendLine();
|
|
||||||
|
|
||||||
// Iterate through the elements and output recursively
|
// Iterate through the elements and output recursively
|
||||||
var writeCount = 0;
|
Int32 writeCount = 0;
|
||||||
foreach (var key in items.Keys)
|
foreach(Object key in items.Keys) {
|
||||||
{
|
|
||||||
// Serialize and append the key (first char indented)
|
// Serialize and append the key (first char indented)
|
||||||
Append(StringQuotedChar, depth + 1);
|
this.Append(StringQuotedChar, depth + 1);
|
||||||
Escape(key.ToString(), _builder);
|
Escape(key.ToString(), this._builder);
|
||||||
_builder
|
_ = this._builder
|
||||||
.Append(StringQuotedChar)
|
.Append(StringQuotedChar)
|
||||||
.Append(ValueSeparatorChar)
|
.Append(ValueSeparatorChar)
|
||||||
.Append(" ");
|
.Append(" ");
|
||||||
|
|
||||||
// Serialize and append the value
|
// Serialize and append the value
|
||||||
var serializedValue = Serialize(items[key], depth + 1, _options);
|
String serializedValue = Serialize(items[key], depth + 1, this._options);
|
||||||
|
|
||||||
if (IsNonEmptyJsonArrayOrObject(serializedValue)) AppendLine();
|
if(IsNonEmptyJsonArrayOrObject(serializedValue)) {
|
||||||
Append(serializedValue, 0);
|
this.AppendLine();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.Append(serializedValue, 0);
|
||||||
|
|
||||||
// Add a comma and start a new line -- We will remove the last one when we are done writing the elements
|
// Add a comma and start a new line -- We will remove the last one when we are done writing the elements
|
||||||
Append(FieldSeparatorChar, 0);
|
this.Append(FieldSeparatorChar, 0);
|
||||||
AppendLine();
|
this.AppendLine();
|
||||||
writeCount++;
|
writeCount++;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Output the end of the object and set the result
|
// Output the end of the object and set the result
|
||||||
RemoveLastComma();
|
this.RemoveLastComma();
|
||||||
Append(CloseObjectChar, writeCount > 0 ? depth : 0);
|
this.Append(CloseObjectChar, writeCount > 0 ? depth : 0);
|
||||||
return _builder.ToString();
|
return this._builder.ToString();
|
||||||
}
|
}
|
||||||
|
|
||||||
private string ResolveObject(object target, int depth)
|
private String ResolveObject(Object target, Int32 depth) {
|
||||||
{
|
Type targetType = target.GetType();
|
||||||
var targetType = target.GetType();
|
Dictionary<String, MemberInfo> fields = this._options.GetProperties(targetType);
|
||||||
var fields = _options.GetProperties(targetType);
|
|
||||||
|
|
||||||
if (fields.Count == 0 && string.IsNullOrWhiteSpace(_options.TypeSpecifier))
|
if(fields.Count == 0 && String.IsNullOrWhiteSpace(this._options.TypeSpecifier)) {
|
||||||
return EmptyObjectLiteral;
|
return EmptyObjectLiteral;
|
||||||
|
}
|
||||||
|
|
||||||
// If we arrive here, then we convert the object into a
|
// If we arrive here, then we convert the object into a
|
||||||
// dictionary of property names and values and call the serialization
|
// dictionary of property names and values and call the serialization
|
||||||
// function again
|
// function again
|
||||||
var objectDictionary = CreateDictionary(fields, targetType.ToString(), target);
|
Dictionary<String, Object> objectDictionary = this.CreateDictionary(fields, targetType.ToString(), target);
|
||||||
|
|
||||||
return Serialize(objectDictionary, depth, _options);
|
return Serialize(objectDictionary, depth, this._options);
|
||||||
}
|
}
|
||||||
|
|
||||||
private string ResolveEnumerable(IEnumerable target, int depth)
|
private String ResolveEnumerable(IEnumerable target, Int32 depth) {
|
||||||
{
|
|
||||||
// Cast the items as a generic object array
|
// Cast the items as a generic object array
|
||||||
var items = target.Cast<object>();
|
IEnumerable<Object> items = target.Cast<Object>();
|
||||||
|
|
||||||
Append(OpenArrayChar, depth);
|
this.Append(OpenArrayChar, depth);
|
||||||
AppendLine();
|
this.AppendLine();
|
||||||
|
|
||||||
// Iterate through the elements and output recursively
|
// Iterate through the elements and output recursively
|
||||||
var writeCount = 0;
|
Int32 writeCount = 0;
|
||||||
foreach (var entry in items)
|
foreach(Object entry in items) {
|
||||||
{
|
String serializedValue = Serialize(entry, depth + 1, this._options);
|
||||||
var serializedValue = Serialize(entry, depth + 1, _options);
|
|
||||||
|
|
||||||
if (IsNonEmptyJsonArrayOrObject(serializedValue))
|
if(IsNonEmptyJsonArrayOrObject(serializedValue)) {
|
||||||
Append(serializedValue, 0);
|
this.Append(serializedValue, 0);
|
||||||
else
|
} else {
|
||||||
Append(serializedValue, depth + 1);
|
this.Append(serializedValue, depth + 1);
|
||||||
|
}
|
||||||
|
|
||||||
Append(FieldSeparatorChar, 0);
|
this.Append(FieldSeparatorChar, 0);
|
||||||
AppendLine();
|
this.AppendLine();
|
||||||
writeCount++;
|
writeCount++;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Output the end of the array and set the result
|
// Output the end of the array and set the result
|
||||||
RemoveLastComma();
|
this.RemoveLastComma();
|
||||||
Append(CloseArrayChar, writeCount > 0 ? depth : 0);
|
this.Append(CloseArrayChar, writeCount > 0 ? depth : 0);
|
||||||
return _builder.ToString();
|
return this._builder.ToString();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SetIndent(int depth)
|
private void SetIndent(Int32 depth) {
|
||||||
{
|
if(this._options.Format == false || depth <= 0) {
|
||||||
if (_options.Format == false || depth <= 0) return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
_builder.Append(IndentStrings.GetOrAdd(depth, x => new string(' ', x * 4)));
|
_ = this._builder.Append(IndentStrings.GetOrAdd(depth, x => new String(' ', x * 4)));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Removes the last comma in the current string builder.
|
/// Removes the last comma in the current string builder.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private void RemoveLastComma()
|
private void RemoveLastComma() {
|
||||||
{
|
if(this._builder.Length < this._lastCommaSearch.Length) {
|
||||||
if (_builder.Length < _lastCommaSearch.Length)
|
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (_lastCommaSearch.Where((t, i) => _builder[_builder.Length - _lastCommaSearch.Length + i] != t).Any())
|
if(this._lastCommaSearch.Where((t, i) => this._builder[this._builder.Length - this._lastCommaSearch.Length + i] != t).Any()) {
|
||||||
{
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we got this far, we simply remove the comma character
|
// If we got this far, we simply remove the comma character
|
||||||
_builder.Remove(_builder.Length - _lastCommaSearch.Length, 1);
|
_ = this._builder.Remove(this._builder.Length - this._lastCommaSearch.Length, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void Append(string text, int depth)
|
private void Append(String text, Int32 depth) {
|
||||||
{
|
this.SetIndent(depth);
|
||||||
SetIndent(depth);
|
_ = this._builder.Append(text);
|
||||||
_builder.Append(text);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void Append(char text, int depth)
|
private void Append(Char text, Int32 depth) {
|
||||||
{
|
this.SetIndent(depth);
|
||||||
SetIndent(depth);
|
_ = this._builder.Append(text);
|
||||||
_builder.Append(text);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void AppendLine()
|
private void AppendLine() {
|
||||||
{
|
if(this._options.Format == false) {
|
||||||
if (_options.Format == false) return;
|
return;
|
||||||
_builder.Append(Environment.NewLine);
|
}
|
||||||
|
|
||||||
|
_ = this._builder.Append(Environment.NewLine);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
@ -1,12 +1,11 @@
|
|||||||
namespace Unosquare.Swan.Formatters
|
using System;
|
||||||
{
|
using System.Collections.Generic;
|
||||||
using System;
|
using System.Collections.Concurrent;
|
||||||
using System.Collections.Generic;
|
using System.Linq;
|
||||||
using System.Collections.Concurrent;
|
using System.Reflection;
|
||||||
using System.Linq;
|
using Unosquare.Swan.Attributes;
|
||||||
using System.Reflection;
|
|
||||||
using Attributes;
|
|
||||||
|
|
||||||
|
namespace Unosquare.Swan.Formatters {
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A very simple, light-weight JSON library written by Mario
|
/// A very simple, light-weight JSON library written by Mario
|
||||||
/// to teach Geo how things are done
|
/// to teach Geo how things are done
|
||||||
@ -14,91 +13,92 @@
|
|||||||
/// This is an useful helper for small tasks but it doesn't represent a full-featured
|
/// This is an useful helper for small tasks but it doesn't represent a full-featured
|
||||||
/// serializer such as the beloved Json.NET.
|
/// serializer such as the beloved Json.NET.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public partial class Json
|
public partial class Json {
|
||||||
{
|
private class SerializerOptions {
|
||||||
private class SerializerOptions
|
private static readonly ConcurrentDictionary<Type, Dictionary<Tuple<String, String>, MemberInfo>>
|
||||||
{
|
TypeCache = new ConcurrentDictionary<Type, Dictionary<Tuple<String, String>, MemberInfo>>();
|
||||||
private static readonly ConcurrentDictionary<Type, Dictionary<Tuple<string, string>, MemberInfo>>
|
|
||||||
TypeCache = new ConcurrentDictionary<Type, Dictionary<Tuple<string, string>, MemberInfo>>();
|
|
||||||
|
|
||||||
private readonly string[] _includeProperties;
|
private readonly String[] _includeProperties;
|
||||||
private readonly string[] _excludeProperties;
|
private readonly String[] _excludeProperties;
|
||||||
private readonly Dictionary<int, List<WeakReference>> _parentReferences = new Dictionary<int, List<WeakReference>>();
|
private readonly Dictionary<Int32, List<WeakReference>> _parentReferences = new Dictionary<Int32, List<WeakReference>>();
|
||||||
|
|
||||||
public SerializerOptions(
|
public SerializerOptions(
|
||||||
bool format,
|
Boolean format,
|
||||||
string typeSpecifier,
|
String typeSpecifier,
|
||||||
string[] includeProperties,
|
String[] includeProperties,
|
||||||
string[] excludeProperties = null,
|
String[] excludeProperties = null,
|
||||||
bool includeNonPublic = true,
|
Boolean includeNonPublic = true,
|
||||||
IReadOnlyCollection<WeakReference> parentReferences = null)
|
IReadOnlyCollection<WeakReference> parentReferences = null) {
|
||||||
{
|
this._includeProperties = includeProperties;
|
||||||
_includeProperties = includeProperties;
|
this._excludeProperties = excludeProperties;
|
||||||
_excludeProperties = excludeProperties;
|
|
||||||
|
|
||||||
IncludeNonPublic = includeNonPublic;
|
this.IncludeNonPublic = includeNonPublic;
|
||||||
Format = format;
|
this.Format = format;
|
||||||
TypeSpecifier = typeSpecifier;
|
this.TypeSpecifier = typeSpecifier;
|
||||||
|
|
||||||
if (parentReferences == null)
|
if(parentReferences == null) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
foreach (var parentReference in parentReferences.Where(x => x.IsAlive))
|
foreach(WeakReference parentReference in parentReferences.Where(x => x.IsAlive)) {
|
||||||
{
|
_ = this.IsObjectPresent(parentReference.Target);
|
||||||
IsObjectPresent(parentReference.Target);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool Format { get; }
|
public Boolean Format {
|
||||||
public string TypeSpecifier { get; }
|
get;
|
||||||
public bool IncludeNonPublic { get; }
|
}
|
||||||
|
public String TypeSpecifier {
|
||||||
|
get;
|
||||||
|
}
|
||||||
|
public Boolean IncludeNonPublic {
|
||||||
|
get;
|
||||||
|
}
|
||||||
|
|
||||||
internal bool IsObjectPresent(object target)
|
internal Boolean IsObjectPresent(Object target) {
|
||||||
{
|
Int32 hashCode = target.GetHashCode();
|
||||||
var hashCode = target.GetHashCode();
|
|
||||||
|
|
||||||
if (_parentReferences.ContainsKey(hashCode))
|
if(this._parentReferences.ContainsKey(hashCode)) {
|
||||||
{
|
if(this._parentReferences[hashCode].Any(p => ReferenceEquals(p.Target, target))) {
|
||||||
if (_parentReferences[hashCode].Any(p => ReferenceEquals(p.Target, target)))
|
|
||||||
return true;
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
_parentReferences[hashCode].Add(new WeakReference(target));
|
this._parentReferences[hashCode].Add(new WeakReference(target));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
_parentReferences.Add(hashCode, new List<WeakReference> { new WeakReference(target) });
|
this._parentReferences.Add(hashCode, new List<WeakReference> { new WeakReference(target) });
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal Dictionary<string, MemberInfo> GetProperties(Type targetType)
|
internal Dictionary<String, MemberInfo> GetProperties(Type targetType)
|
||||||
=> GetPropertiesCache(targetType)
|
=> GetPropertiesCache(targetType)
|
||||||
.When(() => _includeProperties?.Length > 0,
|
.When(() => this._includeProperties?.Length > 0,
|
||||||
query => query.Where(p => _includeProperties.Contains(p.Key.Item1)))
|
query => query.Where(p => this._includeProperties.Contains(p.Key.Item1)))
|
||||||
.When(() => _excludeProperties?.Length > 0,
|
.When(() => this._excludeProperties?.Length > 0,
|
||||||
query => query.Where(p => !_excludeProperties.Contains(p.Key.Item1)))
|
query => query.Where(p => !this._excludeProperties.Contains(p.Key.Item1)))
|
||||||
.ToDictionary(x => x.Key.Item2, x => x.Value);
|
.ToDictionary(x => x.Key.Item2, x => x.Value);
|
||||||
|
|
||||||
private static Dictionary<Tuple<string, string>, MemberInfo> GetPropertiesCache(Type targetType)
|
private static Dictionary<Tuple<String, String>, MemberInfo> GetPropertiesCache(Type targetType) {
|
||||||
{
|
if(TypeCache.TryGetValue(targetType, out Dictionary<Tuple<String, String>, MemberInfo> current)) {
|
||||||
if (TypeCache.TryGetValue(targetType, out var current))
|
|
||||||
return current;
|
return current;
|
||||||
|
}
|
||||||
|
|
||||||
var fields =
|
List<MemberInfo> fields =
|
||||||
new List<MemberInfo>(PropertyTypeCache.RetrieveAllProperties(targetType).Where(p => p.CanRead));
|
new List<MemberInfo>(PropertyTypeCache.RetrieveAllProperties(targetType).Where(p => p.CanRead));
|
||||||
|
|
||||||
// If the target is a struct (value type) navigate the fields.
|
// If the target is a struct (value type) navigate the fields.
|
||||||
if (targetType.IsValueType())
|
if(targetType.IsValueType()) {
|
||||||
{
|
|
||||||
fields.AddRange(FieldTypeCache.RetrieveAllFields(targetType));
|
fields.AddRange(FieldTypeCache.RetrieveAllFields(targetType));
|
||||||
}
|
}
|
||||||
|
|
||||||
var value = fields
|
Dictionary<Tuple<String, String>, MemberInfo> value = fields
|
||||||
.ToDictionary(
|
.ToDictionary(
|
||||||
x => Tuple.Create(x.Name,
|
x => Tuple.Create(x.Name,
|
||||||
x.GetCustomAttribute<JsonPropertyAttribute>()?.PropertyName ?? x.Name),
|
x.GetCustomAttribute<JsonPropertyAttribute>()?.PropertyName ?? x.Name),
|
||||||
x => x);
|
x => x);
|
||||||
|
|
||||||
TypeCache.TryAdd(targetType, value);
|
_ = TypeCache.TryAdd(targetType, value);
|
||||||
|
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
@ -1,13 +1,12 @@
|
|||||||
namespace Unosquare.Swan.Formatters
|
using Unosquare.Swan.Reflection;
|
||||||
{
|
using System;
|
||||||
using Reflection;
|
using Unosquare.Swan.Components;
|
||||||
using System;
|
using System.Collections.Generic;
|
||||||
using Components;
|
using System.Linq;
|
||||||
using System.Collections.Generic;
|
using System.Reflection;
|
||||||
using System.Linq;
|
using Unosquare.Swan.Attributes;
|
||||||
using System.Reflection;
|
|
||||||
using Attributes;
|
|
||||||
|
|
||||||
|
namespace Unosquare.Swan.Formatters {
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A very simple, light-weight JSON library written by Mario
|
/// A very simple, light-weight JSON library written by Mario
|
||||||
/// to teach Geo how things are done
|
/// to teach Geo how things are done
|
||||||
@ -15,35 +14,34 @@
|
|||||||
/// This is an useful helper for small tasks but it doesn't represent a full-featured
|
/// This is an useful helper for small tasks but it doesn't represent a full-featured
|
||||||
/// serializer such as the beloved Json.NET.
|
/// serializer such as the beloved Json.NET.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static partial class Json
|
public static partial class Json {
|
||||||
{
|
|
||||||
#region Constants
|
#region Constants
|
||||||
|
|
||||||
internal const string AddMethodName = "Add";
|
internal const String AddMethodName = "Add";
|
||||||
|
|
||||||
private const char OpenObjectChar = '{';
|
private const Char OpenObjectChar = '{';
|
||||||
private const char CloseObjectChar = '}';
|
private const Char CloseObjectChar = '}';
|
||||||
|
|
||||||
private const char OpenArrayChar = '[';
|
private const Char OpenArrayChar = '[';
|
||||||
private const char CloseArrayChar = ']';
|
private const Char CloseArrayChar = ']';
|
||||||
|
|
||||||
private const char FieldSeparatorChar = ',';
|
private const Char FieldSeparatorChar = ',';
|
||||||
private const char ValueSeparatorChar = ':';
|
private const Char ValueSeparatorChar = ':';
|
||||||
|
|
||||||
private const char StringEscapeChar = '\\';
|
private const Char StringEscapeChar = '\\';
|
||||||
private const char StringQuotedChar = '"';
|
private const Char StringQuotedChar = '"';
|
||||||
|
|
||||||
private const string EmptyObjectLiteral = "{ }";
|
private const String EmptyObjectLiteral = "{ }";
|
||||||
private const string EmptyArrayLiteral = "[ ]";
|
private const String EmptyArrayLiteral = "[ ]";
|
||||||
private const string TrueLiteral = "true";
|
private const String TrueLiteral = "true";
|
||||||
private const string FalseLiteral = "false";
|
private const String FalseLiteral = "false";
|
||||||
private const string NullLiteral = "null";
|
private const String NullLiteral = "null";
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
private static readonly PropertyTypeCache PropertyTypeCache = new PropertyTypeCache();
|
private static readonly PropertyTypeCache PropertyTypeCache = new PropertyTypeCache();
|
||||||
private static readonly FieldTypeCache FieldTypeCache = new FieldTypeCache();
|
private static readonly FieldTypeCache FieldTypeCache = new FieldTypeCache();
|
||||||
private static readonly CollectionCacheRepository<string> IgnoredPropertiesCache = new CollectionCacheRepository<string>();
|
private static readonly CollectionCacheRepository<String> IgnoredPropertiesCache = new CollectionCacheRepository<String>();
|
||||||
|
|
||||||
#region Public API
|
#region Public API
|
||||||
|
|
||||||
@ -100,16 +98,13 @@
|
|||||||
/// }
|
/// }
|
||||||
/// </code>
|
/// </code>
|
||||||
/// </example>
|
/// </example>
|
||||||
public static string Serialize(
|
public static String Serialize(
|
||||||
object obj,
|
Object obj,
|
||||||
bool format = false,
|
Boolean format = false,
|
||||||
string typeSpecifier = null,
|
String typeSpecifier = null,
|
||||||
bool includeNonPublic = false,
|
Boolean includeNonPublic = false,
|
||||||
string[] includedNames = null,
|
String[] includedNames = null,
|
||||||
string[] excludedNames = null)
|
String[] excludedNames = null) => Serialize(obj, format, typeSpecifier, includeNonPublic, includedNames, excludedNames, null);
|
||||||
{
|
|
||||||
return Serialize(obj, format, typeSpecifier, includeNonPublic, includedNames, excludedNames, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Serializes the specified object into a JSON string.
|
/// Serializes the specified object into a JSON string.
|
||||||
@ -124,21 +119,19 @@
|
|||||||
/// <returns>
|
/// <returns>
|
||||||
/// A <see cref="System.String" /> that represents the current object.
|
/// A <see cref="System.String" /> that represents the current object.
|
||||||
/// </returns>
|
/// </returns>
|
||||||
public static string Serialize(
|
public static String Serialize(
|
||||||
object obj,
|
Object obj,
|
||||||
bool format,
|
Boolean format,
|
||||||
string typeSpecifier,
|
String typeSpecifier,
|
||||||
bool includeNonPublic,
|
Boolean includeNonPublic,
|
||||||
string[] includedNames,
|
String[] includedNames,
|
||||||
string[] excludedNames,
|
String[] excludedNames,
|
||||||
List<WeakReference> parentReferences)
|
List<WeakReference> parentReferences) {
|
||||||
{
|
if(obj != null && (obj is String || Definitions.AllBasicValueTypes.Contains(obj.GetType()))) {
|
||||||
if (obj != null && (obj is string || Definitions.AllBasicValueTypes.Contains(obj.GetType())))
|
|
||||||
{
|
|
||||||
return SerializePrimitiveValue(obj);
|
return SerializePrimitiveValue(obj);
|
||||||
}
|
}
|
||||||
|
|
||||||
var options = new SerializerOptions(
|
SerializerOptions options = new SerializerOptions(
|
||||||
format,
|
format,
|
||||||
typeSpecifier,
|
typeSpecifier,
|
||||||
includedNames,
|
includedNames,
|
||||||
@ -178,9 +171,8 @@
|
|||||||
/// }
|
/// }
|
||||||
/// </code>
|
/// </code>
|
||||||
/// </example>
|
/// </example>
|
||||||
public static string SerializeOnly(object obj, bool format, params string[] includeNames)
|
public static String SerializeOnly(Object obj, Boolean format, params String[] includeNames) {
|
||||||
{
|
SerializerOptions options = new SerializerOptions(format, null, includeNames);
|
||||||
var options = new SerializerOptions(format, null, includeNames);
|
|
||||||
|
|
||||||
return Serializer.Serialize(obj, 0, options);
|
return Serializer.Serialize(obj, 0, options);
|
||||||
}
|
}
|
||||||
@ -214,9 +206,8 @@
|
|||||||
/// }
|
/// }
|
||||||
/// </code>
|
/// </code>
|
||||||
/// </example>
|
/// </example>
|
||||||
public static string SerializeExcluding(object obj, bool format, params string[] excludeNames)
|
public static String SerializeExcluding(Object obj, Boolean format, params String[] excludeNames) {
|
||||||
{
|
SerializerOptions options = new SerializerOptions(format, null, null, excludeNames);
|
||||||
var options = new SerializerOptions(format, null, null, excludeNames);
|
|
||||||
|
|
||||||
return Serializer.Serialize(obj, 0, options);
|
return Serializer.Serialize(obj, 0, options);
|
||||||
}
|
}
|
||||||
@ -245,7 +236,7 @@
|
|||||||
/// }
|
/// }
|
||||||
/// </code>
|
/// </code>
|
||||||
/// </example>
|
/// </example>
|
||||||
public static object Deserialize(string json) => Deserializer.DeserializeInternal(json);
|
public static Object Deserialize(String json) => Deserializer.DeserializeInternal(json);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Deserializes the specified json string and converts it to the specified object type.
|
/// Deserializes the specified json string and converts it to the specified object type.
|
||||||
@ -272,7 +263,7 @@
|
|||||||
/// }
|
/// }
|
||||||
/// </code>
|
/// </code>
|
||||||
/// </example>
|
/// </example>
|
||||||
public static T Deserialize<T>(string json) => (T)Deserialize(json, typeof(T));
|
public static T Deserialize<T>(String json) => (T)Deserialize(json, typeof(T));
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Deserializes the specified json string and converts it to the specified object type.
|
/// Deserializes the specified json string and converts it to the specified object type.
|
||||||
@ -281,7 +272,7 @@
|
|||||||
/// <param name="json">The json.</param>
|
/// <param name="json">The json.</param>
|
||||||
/// <param name="includeNonPublic">if set to true, it also uses the non-public constructors and property setters.</param>
|
/// <param name="includeNonPublic">if set to true, it also uses the non-public constructors and property setters.</param>
|
||||||
/// <returns>The deserialized specified type object.</returns>
|
/// <returns>The deserialized specified type object.</returns>
|
||||||
public static T Deserialize<T>(string json, bool includeNonPublic) => (T)Deserialize(json, typeof(T), includeNonPublic);
|
public static T Deserialize<T>(String json, Boolean includeNonPublic) => (T)Deserialize(json, typeof(T), includeNonPublic);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Deserializes the specified json string and converts it to the specified object type.
|
/// Deserializes the specified json string and converts it to the specified object type.
|
||||||
@ -290,37 +281,34 @@
|
|||||||
/// <param name="resultType">Type of the result.</param>
|
/// <param name="resultType">Type of the result.</param>
|
||||||
/// <param name="includeNonPublic">if set to true, it also uses the non-public constructors and property setters.</param>
|
/// <param name="includeNonPublic">if set to true, it also uses the non-public constructors and property setters.</param>
|
||||||
/// <returns>Type of the current conversion from json result.</returns>
|
/// <returns>Type of the current conversion from json result.</returns>
|
||||||
public static object Deserialize(string json, Type resultType, bool includeNonPublic = false)
|
public static Object Deserialize(String json, Type resultType, Boolean includeNonPublic = false)
|
||||||
=> Converter.FromJsonResult(Deserializer.DeserializeInternal(json), resultType, includeNonPublic);
|
=> Converter.FromJsonResult(Deserializer.DeserializeInternal(json), resultType, includeNonPublic);
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region Private API
|
#region Private API
|
||||||
|
|
||||||
private static string[] GetExcludedNames(Type type, string[] excludedNames)
|
private static String[] GetExcludedNames(Type type, String[] excludedNames) {
|
||||||
{
|
if(type == null) {
|
||||||
if (type == null)
|
|
||||||
return excludedNames;
|
return excludedNames;
|
||||||
|
}
|
||||||
|
|
||||||
var excludedByAttr = IgnoredPropertiesCache.Retrieve(type, t => t.GetProperties()
|
IEnumerable<String> excludedByAttr = IgnoredPropertiesCache.Retrieve(type, t => t.GetProperties()
|
||||||
.Where(x => Runtime.AttributeCache.RetrieveOne<JsonPropertyAttribute>(x)?.Ignored == true)
|
.Where(x => Runtime.AttributeCache.RetrieveOne<JsonPropertyAttribute>(x)?.Ignored == true)
|
||||||
.Select(x => x.Name));
|
.Select(x => x.Name));
|
||||||
|
|
||||||
if (excludedByAttr?.Any() != true)
|
return excludedByAttr?.Any() != true
|
||||||
return excludedNames;
|
? excludedNames
|
||||||
|
: excludedNames == null
|
||||||
return excludedNames == null
|
|
||||||
? excludedByAttr.ToArray()
|
? excludedByAttr.ToArray()
|
||||||
: excludedByAttr.Intersect(excludedNames).ToArray();
|
: excludedByAttr.Intersect(excludedNames).ToArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static string SerializePrimitiveValue(object obj)
|
private static String SerializePrimitiveValue(Object obj) {
|
||||||
{
|
switch(obj) {
|
||||||
switch (obj)
|
case String stringValue:
|
||||||
{
|
|
||||||
case string stringValue:
|
|
||||||
return stringValue;
|
return stringValue;
|
||||||
case bool boolValue:
|
case Boolean boolValue:
|
||||||
return boolValue ? TrueLiteral : FalseLiteral;
|
return boolValue ? TrueLiteral : FalseLiteral;
|
||||||
default:
|
default:
|
||||||
return obj.ToString();
|
return obj.ToString();
|
||||||
|
@ -1,11 +1,10 @@
|
|||||||
namespace Unosquare.Swan.Reflection
|
using System;
|
||||||
{
|
using System.Collections.Generic;
|
||||||
using System;
|
using System.Reflection;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Concurrent;
|
||||||
using System.Reflection;
|
using System.Linq;
|
||||||
using System.Collections.Concurrent;
|
|
||||||
using System.Linq;
|
|
||||||
|
|
||||||
|
namespace Unosquare.Swan.Reflection {
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A thread-safe cache of attributes belonging to a given key (MemberInfo or Type).
|
/// A thread-safe cache of attributes belonging to a given key (MemberInfo or Type).
|
||||||
///
|
///
|
||||||
@ -13,25 +12,23 @@
|
|||||||
/// calls the retrieval process if the type is not contained
|
/// calls the retrieval process if the type is not contained
|
||||||
/// in the cache.
|
/// in the cache.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class AttributeCache
|
public class AttributeCache {
|
||||||
{
|
private readonly Lazy<ConcurrentDictionary<Tuple<Object, Type>, IEnumerable<Object>>> _data =
|
||||||
private readonly Lazy<ConcurrentDictionary<Tuple<object, Type>, IEnumerable<object>>> _data =
|
new Lazy<ConcurrentDictionary<Tuple<Object, Type>, IEnumerable<Object>>>(() =>
|
||||||
new Lazy<ConcurrentDictionary<Tuple<object, Type>, IEnumerable<object>>>(() =>
|
new ConcurrentDictionary<Tuple<Object, Type>, IEnumerable<Object>>(), true);
|
||||||
new ConcurrentDictionary<Tuple<object, Type>, IEnumerable<object>>(), true);
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="AttributeCache"/> class.
|
/// Initializes a new instance of the <see cref="AttributeCache"/> class.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="propertyCache">The property cache object.</param>
|
/// <param name="propertyCache">The property cache object.</param>
|
||||||
public AttributeCache(PropertyTypeCache propertyCache = null)
|
public AttributeCache(PropertyTypeCache propertyCache = null) => this.PropertyTypeCache = propertyCache ?? Runtime.PropertyTypeCache;
|
||||||
{
|
|
||||||
PropertyTypeCache = propertyCache ?? Runtime.PropertyTypeCache;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A PropertyTypeCache object for caching properties and their attributes.
|
/// A PropertyTypeCache object for caching properties and their attributes.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public PropertyTypeCache PropertyTypeCache { get; }
|
public PropertyTypeCache PropertyTypeCache {
|
||||||
|
get;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Determines whether [contains] [the specified member].
|
/// Determines whether [contains] [the specified member].
|
||||||
@ -41,7 +38,7 @@
|
|||||||
/// <returns>
|
/// <returns>
|
||||||
/// <c>true</c> if [contains] [the specified member]; otherwise, <c>false</c>.
|
/// <c>true</c> if [contains] [the specified member]; otherwise, <c>false</c>.
|
||||||
/// </returns>
|
/// </returns>
|
||||||
public bool Contains<T>(MemberInfo member) => _data.Value.ContainsKey(new Tuple<object, Type>(member, typeof(T)));
|
public Boolean Contains<T>(MemberInfo member) => this._data.Value.ContainsKey(new Tuple<Object, Type>(member, typeof(T)));
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets specific attributes from a member constrained to an attribute.
|
/// Gets specific attributes from a member constrained to an attribute.
|
||||||
@ -50,13 +47,13 @@
|
|||||||
/// <param name="member">The member.</param>
|
/// <param name="member">The member.</param>
|
||||||
/// <param name="inherit"><c>true</c> to inspect the ancestors of element; otherwise, <c>false</c>.</param>
|
/// <param name="inherit"><c>true</c> to inspect the ancestors of element; otherwise, <c>false</c>.</param>
|
||||||
/// <returns>An array of the attributes stored for the specified type.</returns>
|
/// <returns>An array of the attributes stored for the specified type.</returns>
|
||||||
public IEnumerable<object> Retrieve<T>(MemberInfo member, bool inherit = false)
|
public IEnumerable<Object> Retrieve<T>(MemberInfo member, Boolean inherit = false)
|
||||||
where T : Attribute
|
where T : Attribute {
|
||||||
{
|
if(member == null) {
|
||||||
if (member == null)
|
|
||||||
throw new ArgumentNullException(nameof(member));
|
throw new ArgumentNullException(nameof(member));
|
||||||
|
}
|
||||||
|
|
||||||
return Retrieve(new Tuple<object, Type>(member, typeof(T)), t => member.GetCustomAttributes<T>(inherit));
|
return this.Retrieve(new Tuple<Object, Type>(member, typeof(T)), t => member.GetCustomAttributes<T>(inherit));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -66,16 +63,17 @@
|
|||||||
/// <param name="type">The attribute type.</param>
|
/// <param name="type">The attribute type.</param>
|
||||||
/// <param name="inherit"><c>true</c> to inspect the ancestors of element; otherwise, <c>false</c>.</param>
|
/// <param name="inherit"><c>true</c> to inspect the ancestors of element; otherwise, <c>false</c>.</param>
|
||||||
/// <returns>An array of the attributes stored for the specified type.</returns>
|
/// <returns>An array of the attributes stored for the specified type.</returns>
|
||||||
public IEnumerable<object> Retrieve(MemberInfo member, Type type, bool inherit = false)
|
public IEnumerable<Object> Retrieve(MemberInfo member, Type type, Boolean inherit = false) {
|
||||||
{
|
if(member == null) {
|
||||||
if (member == null)
|
|
||||||
throw new ArgumentNullException(nameof(member));
|
throw new ArgumentNullException(nameof(member));
|
||||||
|
}
|
||||||
|
|
||||||
if (type == null)
|
if(type == null) {
|
||||||
throw new ArgumentNullException(nameof(type));
|
throw new ArgumentNullException(nameof(type));
|
||||||
|
}
|
||||||
|
|
||||||
return Retrieve(
|
return this.Retrieve(
|
||||||
new Tuple<object, Type>(member, type),
|
new Tuple<Object, Type>(member, type),
|
||||||
t => member.GetCustomAttributes(type, inherit));
|
t => member.GetCustomAttributes(type, inherit));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -86,14 +84,14 @@
|
|||||||
/// <param name="member">The member.</param>
|
/// <param name="member">The member.</param>
|
||||||
/// <param name="inherit"><c>true</c> to inspect the ancestors of element; otherwise, <c>false</c>.</param>
|
/// <param name="inherit"><c>true</c> to inspect the ancestors of element; otherwise, <c>false</c>.</param>
|
||||||
/// <returns>An attribute stored for the specified type.</returns>
|
/// <returns>An attribute stored for the specified type.</returns>
|
||||||
public T RetrieveOne<T>(MemberInfo member, bool inherit = false)
|
public T RetrieveOne<T>(MemberInfo member, Boolean inherit = false)
|
||||||
where T : Attribute
|
where T : Attribute {
|
||||||
{
|
if(member == null) {
|
||||||
if (member == null)
|
|
||||||
return default;
|
return default;
|
||||||
|
}
|
||||||
|
|
||||||
var attr = Retrieve(
|
IEnumerable<Object> attr = this.Retrieve(
|
||||||
new Tuple<object, Type>(member, typeof(T)),
|
new Tuple<Object, Type>(member, typeof(T)),
|
||||||
t => member.GetCustomAttributes(typeof(T), inherit));
|
t => member.GetCustomAttributes(typeof(T), inherit));
|
||||||
|
|
||||||
return ConvertToAttribute<T>(attr);
|
return ConvertToAttribute<T>(attr);
|
||||||
@ -106,11 +104,10 @@
|
|||||||
/// <typeparam name="T">The type to retrieve the attribute.</typeparam>
|
/// <typeparam name="T">The type to retrieve the attribute.</typeparam>
|
||||||
/// <param name="inherit">if set to <c>true</c> [inherit].</param>
|
/// <param name="inherit">if set to <c>true</c> [inherit].</param>
|
||||||
/// <returns>An attribute stored for the specified type.</returns>
|
/// <returns>An attribute stored for the specified type.</returns>
|
||||||
public TAttribute RetrieveOne<TAttribute, T>(bool inherit = false)
|
public TAttribute RetrieveOne<TAttribute, T>(Boolean inherit = false)
|
||||||
where TAttribute : Attribute
|
where TAttribute : Attribute {
|
||||||
{
|
IEnumerable<Object> attr = this.Retrieve(
|
||||||
var attr = Retrieve(
|
new Tuple<Object, Type>(typeof(T), typeof(TAttribute)),
|
||||||
new Tuple<object, Type>(typeof(T), typeof(TAttribute)),
|
|
||||||
t => typeof(T).GetCustomAttributes(typeof(TAttribute), inherit));
|
t => typeof(T).GetCustomAttributes(typeof(TAttribute), inherit));
|
||||||
|
|
||||||
return ConvertToAttribute<TAttribute>(attr);
|
return ConvertToAttribute<TAttribute>(attr);
|
||||||
@ -123,14 +120,14 @@
|
|||||||
/// <param name="type">The type of the object.</param>
|
/// <param name="type">The type of the object.</param>
|
||||||
/// <param name="inherit"><c>true</c> to inspect the ancestors of element; otherwise, <c>false</c>.</param>
|
/// <param name="inherit"><c>true</c> to inspect the ancestors of element; otherwise, <c>false</c>.</param>
|
||||||
/// <returns>A dictionary of the properties and their attributes stored for the specified type.</returns>
|
/// <returns>A dictionary of the properties and their attributes stored for the specified type.</returns>
|
||||||
public Dictionary<PropertyInfo, IEnumerable<object>> Retrieve<T>(Type type, bool inherit = false)
|
public Dictionary<PropertyInfo, IEnumerable<Object>> Retrieve<T>(Type type, Boolean inherit = false)
|
||||||
where T : Attribute
|
where T : Attribute {
|
||||||
{
|
if(type == null) {
|
||||||
if (type == null)
|
|
||||||
throw new ArgumentNullException(nameof(type));
|
throw new ArgumentNullException(nameof(type));
|
||||||
|
}
|
||||||
|
|
||||||
return PropertyTypeCache.RetrieveAllProperties(type, true)
|
return this.PropertyTypeCache.RetrieveAllProperties(type, true)
|
||||||
.ToDictionary(x => x, x => Retrieve<T>(x, inherit));
|
.ToDictionary(x => x, x => this.Retrieve<T>(x, inherit));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -142,33 +139,34 @@
|
|||||||
/// <returns>
|
/// <returns>
|
||||||
/// A dictionary of the properties and their attributes stored for the specified type.
|
/// A dictionary of the properties and their attributes stored for the specified type.
|
||||||
/// </returns>
|
/// </returns>
|
||||||
public Dictionary<PropertyInfo, IEnumerable<object>> RetrieveFromType<T>(Type attributeType, bool inherit = false)
|
public Dictionary<PropertyInfo, IEnumerable<Object>> RetrieveFromType<T>(Type attributeType, Boolean inherit = false) {
|
||||||
{
|
if(attributeType == null) {
|
||||||
if (attributeType == null)
|
|
||||||
throw new ArgumentNullException(nameof(attributeType));
|
throw new ArgumentNullException(nameof(attributeType));
|
||||||
|
|
||||||
return PropertyTypeCache.RetrieveAllProperties<T>(true)
|
|
||||||
.ToDictionary(x => x, x => Retrieve(x, attributeType, inherit));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static T ConvertToAttribute<T>(IEnumerable<object> attr)
|
return this.PropertyTypeCache.RetrieveAllProperties<T>(true)
|
||||||
where T : Attribute
|
.ToDictionary(x => x, x => this.Retrieve(x, attributeType, inherit));
|
||||||
{
|
}
|
||||||
if (attr?.Any() != true)
|
|
||||||
return default;
|
|
||||||
|
|
||||||
if (attr.Count() == 1)
|
private static T ConvertToAttribute<T>(IEnumerable<Object> attr)
|
||||||
return (T) Convert.ChangeType(attr.First(), typeof(T));
|
where T : Attribute {
|
||||||
|
if(attr?.Any() != true) {
|
||||||
|
return default;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(attr.Count() == 1) {
|
||||||
|
return (T)Convert.ChangeType(attr.First(), typeof(T));
|
||||||
|
}
|
||||||
|
|
||||||
throw new AmbiguousMatchException("Multiple custom attributes of the same type found.");
|
throw new AmbiguousMatchException("Multiple custom attributes of the same type found.");
|
||||||
}
|
}
|
||||||
|
|
||||||
private IEnumerable<object> Retrieve(Tuple<object, Type> key, Func<Tuple<object, Type>, IEnumerable<object>> factory)
|
private IEnumerable<Object> Retrieve(Tuple<Object, Type> key, Func<Tuple<Object, Type>, IEnumerable<Object>> factory) {
|
||||||
{
|
if(factory == null) {
|
||||||
if (factory == null)
|
|
||||||
throw new ArgumentNullException(nameof(factory));
|
throw new ArgumentNullException(nameof(factory));
|
||||||
|
}
|
||||||
|
|
||||||
return _data.Value.GetOrAdd(key, k => factory.Invoke(k).Where(item => item != null));
|
return this._data.Value.GetOrAdd(key, k => factory.Invoke(k).Where(item => item != null));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,34 +1,29 @@
|
|||||||
namespace Unosquare.Swan.Reflection
|
using System;
|
||||||
{
|
using System.Reflection;
|
||||||
using System;
|
using Unosquare.Swan.Attributes;
|
||||||
using System.Reflection;
|
|
||||||
using Attributes;
|
|
||||||
|
|
||||||
|
namespace Unosquare.Swan.Reflection {
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents a Property object from a Object Reflection Property with extended values.
|
/// Represents a Property object from a Object Reflection Property with extended values.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class ExtendedPropertyInfo
|
public class ExtendedPropertyInfo {
|
||||||
{
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="ExtendedPropertyInfo"/> class.
|
/// Initializes a new instance of the <see cref="ExtendedPropertyInfo"/> class.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="propertyInfo">The property information.</param>
|
/// <param name="propertyInfo">The property information.</param>
|
||||||
public ExtendedPropertyInfo(PropertyInfo propertyInfo)
|
public ExtendedPropertyInfo(PropertyInfo propertyInfo) {
|
||||||
{
|
if(propertyInfo == null) {
|
||||||
if (propertyInfo == null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(propertyInfo));
|
throw new ArgumentNullException(nameof(propertyInfo));
|
||||||
}
|
}
|
||||||
|
|
||||||
Property = propertyInfo.Name;
|
this.Property = propertyInfo.Name;
|
||||||
DataType = propertyInfo.PropertyType.Name;
|
this.DataType = propertyInfo.PropertyType.Name;
|
||||||
|
|
||||||
foreach (PropertyDisplayAttribute display in Runtime.AttributeCache.Retrieve<PropertyDisplayAttribute>(propertyInfo, true))
|
foreach(PropertyDisplayAttribute display in Runtime.AttributeCache.Retrieve<PropertyDisplayAttribute>(propertyInfo, true)) {
|
||||||
{
|
this.Name = display.Name;
|
||||||
Name = display.Name;
|
this.Description = display.Description;
|
||||||
Description = display.Description;
|
this.GroupName = display.GroupName;
|
||||||
GroupName = display.GroupName;
|
this.DefaultValue = display.DefaultValue;
|
||||||
DefaultValue = display.DefaultValue;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -38,7 +33,9 @@
|
|||||||
/// <value>
|
/// <value>
|
||||||
/// The property.
|
/// The property.
|
||||||
/// </value>
|
/// </value>
|
||||||
public string Property { get; }
|
public String Property {
|
||||||
|
get;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the type of the data.
|
/// Gets or sets the type of the data.
|
||||||
@ -46,7 +43,9 @@
|
|||||||
/// <value>
|
/// <value>
|
||||||
/// The type of the data.
|
/// The type of the data.
|
||||||
/// </value>
|
/// </value>
|
||||||
public string DataType { get; }
|
public String DataType {
|
||||||
|
get;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the value.
|
/// Gets or sets the value.
|
||||||
@ -54,7 +53,9 @@
|
|||||||
/// <value>
|
/// <value>
|
||||||
/// The value.
|
/// The value.
|
||||||
/// </value>
|
/// </value>
|
||||||
public object Value { get; set; }
|
public Object Value {
|
||||||
|
get; set;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the default value.
|
/// Gets or sets the default value.
|
||||||
@ -62,7 +63,9 @@
|
|||||||
/// <value>
|
/// <value>
|
||||||
/// The default value.
|
/// The default value.
|
||||||
/// </value>
|
/// </value>
|
||||||
public object DefaultValue { get; }
|
public Object DefaultValue {
|
||||||
|
get;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the name.
|
/// Gets or sets the name.
|
||||||
@ -70,7 +73,9 @@
|
|||||||
/// <value>
|
/// <value>
|
||||||
/// The name.
|
/// The name.
|
||||||
/// </value>
|
/// </value>
|
||||||
public string Name { get; }
|
public String Name {
|
||||||
|
get;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the description.
|
/// Gets or sets the description.
|
||||||
@ -78,7 +83,9 @@
|
|||||||
/// <value>
|
/// <value>
|
||||||
/// The description.
|
/// The description.
|
||||||
/// </value>
|
/// </value>
|
||||||
public string Description { get; }
|
public String Description {
|
||||||
|
get;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the name of the group.
|
/// Gets or sets the name of the group.
|
||||||
@ -86,22 +93,22 @@
|
|||||||
/// <value>
|
/// <value>
|
||||||
/// The name of the group.
|
/// The name of the group.
|
||||||
/// </value>
|
/// </value>
|
||||||
public string GroupName { get; }
|
public String GroupName {
|
||||||
|
get;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents a Property object from a Object Reflection Property with extended values.
|
/// Represents a Property object from a Object Reflection Property with extended values.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <typeparam name="T">The type of the object.</typeparam>
|
/// <typeparam name="T">The type of the object.</typeparam>
|
||||||
public class ExtendedPropertyInfo<T> : ExtendedPropertyInfo
|
public class ExtendedPropertyInfo<T> : ExtendedPropertyInfo {
|
||||||
{
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="ExtendedPropertyInfo{T}"/> class.
|
/// Initializes a new instance of the <see cref="ExtendedPropertyInfo{T}"/> class.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="property">The property.</param>
|
/// <param name="property">The property.</param>
|
||||||
public ExtendedPropertyInfo(string property)
|
public ExtendedPropertyInfo(String property)
|
||||||
: base(typeof(T).GetProperty(property))
|
: base(typeof(T).GetProperty(property)) {
|
||||||
{
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,38 +1,36 @@
|
|||||||
namespace Unosquare.Swan.Reflection
|
using System;
|
||||||
{
|
using System.Collections.Generic;
|
||||||
using System;
|
using System.ComponentModel;
|
||||||
using System.Collections.Generic;
|
using System.Globalization;
|
||||||
using System.ComponentModel;
|
using System.Linq;
|
||||||
using System.Globalization;
|
using System.Reflection;
|
||||||
using System.Linq;
|
|
||||||
using System.Reflection;
|
|
||||||
|
|
||||||
|
namespace Unosquare.Swan.Reflection {
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Provides extended information about a type.
|
/// Provides extended information about a type.
|
||||||
///
|
///
|
||||||
/// This class is mainly used to define sets of types within the Definition class
|
/// This class is mainly used to define sets of types within the Definition class
|
||||||
/// and it is not meant for other than querying the BasicTypesInfo dictionary.
|
/// and it is not meant for other than querying the BasicTypesInfo dictionary.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class ExtendedTypeInfo
|
public class ExtendedTypeInfo {
|
||||||
{
|
|
||||||
#region Static Declarations
|
#region Static Declarations
|
||||||
|
|
||||||
private const string TryParseMethodName = nameof(byte.TryParse);
|
private const String TryParseMethodName = nameof(Byte.TryParse);
|
||||||
private const string ToStringMethodName = nameof(ToString);
|
private const String ToStringMethodName = nameof(ToString);
|
||||||
|
|
||||||
private static readonly Type[] NumericTypes =
|
private static readonly Type[] NumericTypes =
|
||||||
{
|
{
|
||||||
typeof(byte),
|
typeof(Byte),
|
||||||
typeof(sbyte),
|
typeof(SByte),
|
||||||
typeof(decimal),
|
typeof(Decimal),
|
||||||
typeof(double),
|
typeof(Double),
|
||||||
typeof(float),
|
typeof(Single),
|
||||||
typeof(int),
|
typeof(Int32),
|
||||||
typeof(uint),
|
typeof(UInt32),
|
||||||
typeof(long),
|
typeof(Int64),
|
||||||
typeof(ulong),
|
typeof(UInt64),
|
||||||
typeof(short),
|
typeof(Int16),
|
||||||
typeof(ushort),
|
typeof(UInt16),
|
||||||
};
|
};
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
@ -40,7 +38,7 @@
|
|||||||
#region State Management
|
#region State Management
|
||||||
|
|
||||||
private readonly ParameterInfo[] _tryParseParameters;
|
private readonly ParameterInfo[] _tryParseParameters;
|
||||||
private readonly int _toStringArgumentLength;
|
private readonly Int32 _toStringArgumentLength;
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
@ -50,47 +48,40 @@
|
|||||||
/// Initializes a new instance of the <see cref="ExtendedTypeInfo"/> class.
|
/// Initializes a new instance of the <see cref="ExtendedTypeInfo"/> class.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="t">The t.</param>
|
/// <param name="t">The t.</param>
|
||||||
public ExtendedTypeInfo(Type t)
|
public ExtendedTypeInfo(Type t) {
|
||||||
{
|
this.Type = t ?? throw new ArgumentNullException(nameof(t));
|
||||||
Type = t ?? throw new ArgumentNullException(nameof(t));
|
this.IsNullableValueType = this.Type.GetTypeInfo().IsGenericType
|
||||||
IsNullableValueType = Type.GetTypeInfo().IsGenericType
|
&& this.Type.GetGenericTypeDefinition() == typeof(Nullable<>);
|
||||||
&& Type.GetGenericTypeDefinition() == typeof(Nullable<>);
|
|
||||||
|
|
||||||
IsValueType = t.GetTypeInfo().IsValueType;
|
this.IsValueType = t.GetTypeInfo().IsValueType;
|
||||||
|
|
||||||
UnderlyingType = IsNullableValueType ?
|
this.UnderlyingType = this.IsNullableValueType ?
|
||||||
new NullableConverter(Type).UnderlyingType :
|
new NullableConverter(this.Type).UnderlyingType :
|
||||||
Type;
|
this.Type;
|
||||||
|
|
||||||
IsNumeric = NumericTypes.Contains(UnderlyingType);
|
this.IsNumeric = NumericTypes.Contains(this.UnderlyingType);
|
||||||
|
|
||||||
// Extract the TryParse method info
|
// Extract the TryParse method info
|
||||||
try
|
try {
|
||||||
{
|
this.TryParseMethodInfo = this.UnderlyingType.GetMethod(TryParseMethodName,
|
||||||
TryParseMethodInfo = UnderlyingType.GetMethod(TryParseMethodName,
|
new[] { typeof(String), typeof(NumberStyles), typeof(IFormatProvider), this.UnderlyingType.MakeByRefType() }) ??
|
||||||
new[] { typeof(string), typeof(NumberStyles), typeof(IFormatProvider), UnderlyingType.MakeByRefType() }) ??
|
this.UnderlyingType.GetMethod(TryParseMethodName,
|
||||||
UnderlyingType.GetMethod(TryParseMethodName,
|
new[] { typeof(String), this.UnderlyingType.MakeByRefType() });
|
||||||
new[] { typeof(string), UnderlyingType.MakeByRefType() });
|
|
||||||
|
|
||||||
_tryParseParameters = TryParseMethodInfo?.GetParameters();
|
this._tryParseParameters = this.TryParseMethodInfo?.GetParameters();
|
||||||
}
|
} catch {
|
||||||
catch
|
|
||||||
{
|
|
||||||
// ignored
|
// ignored
|
||||||
}
|
}
|
||||||
|
|
||||||
// Extract the ToString method Info
|
// Extract the ToString method Info
|
||||||
try
|
try {
|
||||||
{
|
this.ToStringMethodInfo = this.UnderlyingType.GetMethod(ToStringMethodName,
|
||||||
ToStringMethodInfo = UnderlyingType.GetMethod(ToStringMethodName,
|
|
||||||
new[] { typeof(IFormatProvider) }) ??
|
new[] { typeof(IFormatProvider) }) ??
|
||||||
UnderlyingType.GetMethod(ToStringMethodName,
|
this.UnderlyingType.GetMethod(ToStringMethodName,
|
||||||
new Type[] { });
|
new Type[] { });
|
||||||
|
|
||||||
_toStringArgumentLength = ToStringMethodInfo?.GetParameters().Length ?? 0;
|
this._toStringArgumentLength = this.ToStringMethodInfo?.GetParameters().Length ?? 0;
|
||||||
}
|
} catch {
|
||||||
catch
|
|
||||||
{
|
|
||||||
// ignored
|
// ignored
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -105,7 +96,9 @@
|
|||||||
/// <value>
|
/// <value>
|
||||||
/// The type.
|
/// The type.
|
||||||
/// </value>
|
/// </value>
|
||||||
public Type Type { get; }
|
public Type Type {
|
||||||
|
get;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets a value indicating whether the type is a nullable value type.
|
/// Gets a value indicating whether the type is a nullable value type.
|
||||||
@ -113,7 +106,9 @@
|
|||||||
/// <value>
|
/// <value>
|
||||||
/// <c>true</c> if this instance is nullable value type; otherwise, <c>false</c>.
|
/// <c>true</c> if this instance is nullable value type; otherwise, <c>false</c>.
|
||||||
/// </value>
|
/// </value>
|
||||||
public bool IsNullableValueType { get; }
|
public Boolean IsNullableValueType {
|
||||||
|
get;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets a value indicating whether the type or underlying type is numeric.
|
/// Gets a value indicating whether the type or underlying type is numeric.
|
||||||
@ -121,13 +116,17 @@
|
|||||||
/// <value>
|
/// <value>
|
||||||
/// <c>true</c> if this instance is numeric; otherwise, <c>false</c>.
|
/// <c>true</c> if this instance is numeric; otherwise, <c>false</c>.
|
||||||
/// </value>
|
/// </value>
|
||||||
public bool IsNumeric { get; }
|
public Boolean IsNumeric {
|
||||||
|
get;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets a value indicating whether the type is value type.
|
/// Gets a value indicating whether the type is value type.
|
||||||
/// Nullable value types have this property set to False.
|
/// Nullable value types have this property set to False.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool IsValueType { get; }
|
public Boolean IsValueType {
|
||||||
|
get;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// When dealing with nullable value types, this property will
|
/// When dealing with nullable value types, this property will
|
||||||
@ -137,7 +136,9 @@
|
|||||||
/// <value>
|
/// <value>
|
||||||
/// The type of the underlying.
|
/// The type of the underlying.
|
||||||
/// </value>
|
/// </value>
|
||||||
public Type UnderlyingType { get; }
|
public Type UnderlyingType {
|
||||||
|
get;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the try parse method information. If the type does not contain
|
/// Gets the try parse method information. If the type does not contain
|
||||||
@ -146,7 +147,9 @@
|
|||||||
/// <value>
|
/// <value>
|
||||||
/// The try parse method information.
|
/// The try parse method information.
|
||||||
/// </value>
|
/// </value>
|
||||||
public MethodInfo TryParseMethodInfo { get; }
|
public MethodInfo TryParseMethodInfo {
|
||||||
|
get;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the ToString method info
|
/// Gets the ToString method info
|
||||||
@ -155,7 +158,9 @@
|
|||||||
/// <value>
|
/// <value>
|
||||||
/// To string method information.
|
/// To string method information.
|
||||||
/// </value>
|
/// </value>
|
||||||
public MethodInfo ToStringMethodInfo { get; }
|
public MethodInfo ToStringMethodInfo {
|
||||||
|
get;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets a value indicating whether the type contains a suitable TryParse method.
|
/// Gets a value indicating whether the type contains a suitable TryParse method.
|
||||||
@ -163,7 +168,7 @@
|
|||||||
/// <value>
|
/// <value>
|
||||||
/// <c>true</c> if this instance can parse natively; otherwise, <c>false</c>.
|
/// <c>true</c> if this instance can parse natively; otherwise, <c>false</c>.
|
||||||
/// </value>
|
/// </value>
|
||||||
public bool CanParseNatively => TryParseMethodInfo != null;
|
public Boolean CanParseNatively => this.TryParseMethodInfo != null;
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
@ -174,7 +179,7 @@
|
|||||||
/// For value types it returns the default value.
|
/// For value types it returns the default value.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>Default value of this type.</returns>
|
/// <returns>Default value of this type.</returns>
|
||||||
public object GetDefault() => Type.GetTypeInfo().IsValueType ? Activator.CreateInstance(Type) : null;
|
public Object GetDefault() => this.Type.GetTypeInfo().IsValueType ? Activator.CreateInstance(this.Type) : null;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Tries to parse the string into an object of the type this instance represents.
|
/// Tries to parse the string into an object of the type this instance represents.
|
||||||
@ -184,58 +189,50 @@
|
|||||||
/// <param name="s">The s.</param>
|
/// <param name="s">The s.</param>
|
||||||
/// <param name="result">The result.</param>
|
/// <param name="result">The result.</param>
|
||||||
/// <returns><c>true</c> if parse was converted successfully; otherwise, <c>false</c>.</returns>
|
/// <returns><c>true</c> if parse was converted successfully; otherwise, <c>false</c>.</returns>
|
||||||
public bool TryParse(string s, out object result)
|
public Boolean TryParse(String s, out Object result) {
|
||||||
{
|
result = this.GetDefault();
|
||||||
result = GetDefault();
|
|
||||||
|
|
||||||
try
|
try {
|
||||||
{
|
if(this.Type == typeof(String)) {
|
||||||
if (Type == typeof(string))
|
result = Convert.ChangeType(s, this.Type);
|
||||||
{
|
|
||||||
result = Convert.ChangeType(s, Type);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (IsNullableValueType && string.IsNullOrEmpty(s))
|
if(this.IsNullableValueType && String.IsNullOrEmpty(s)) {
|
||||||
{
|
result = this.GetDefault();
|
||||||
result = GetDefault();
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (CanParseNatively == false)
|
if(this.CanParseNatively == false) {
|
||||||
{
|
result = this.GetDefault();
|
||||||
result = GetDefault();
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build the arguments of the TryParse method
|
// Build the arguments of the TryParse method
|
||||||
var dynamicArguments = new List<object> { s };
|
List<Object> dynamicArguments = new List<Object> { s };
|
||||||
|
|
||||||
for (var pi = 1; pi < _tryParseParameters.Length - 1; pi++)
|
for(Int32 pi = 1; pi < this._tryParseParameters.Length - 1; pi++) {
|
||||||
{
|
ParameterInfo argInfo = this._tryParseParameters[pi];
|
||||||
var argInfo = _tryParseParameters[pi];
|
if(argInfo.ParameterType == typeof(IFormatProvider)) {
|
||||||
if (argInfo.ParameterType == typeof(IFormatProvider))
|
|
||||||
dynamicArguments.Add(CultureInfo.InvariantCulture);
|
dynamicArguments.Add(CultureInfo.InvariantCulture);
|
||||||
else if (argInfo.ParameterType == typeof(NumberStyles))
|
} else if(argInfo.ParameterType == typeof(NumberStyles)) {
|
||||||
dynamicArguments.Add(NumberStyles.Any);
|
dynamicArguments.Add(NumberStyles.Any);
|
||||||
else
|
} else {
|
||||||
dynamicArguments.Add(null);
|
dynamicArguments.Add(null);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
dynamicArguments.Add(null);
|
dynamicArguments.Add(null);
|
||||||
var parseArguments = dynamicArguments.ToArray();
|
Object[] parseArguments = dynamicArguments.ToArray();
|
||||||
|
|
||||||
if ((bool)TryParseMethodInfo.Invoke(null, parseArguments) == false)
|
if((Boolean)this.TryParseMethodInfo.Invoke(null, parseArguments) == false) {
|
||||||
{
|
result = this.GetDefault();
|
||||||
result = GetDefault();
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
result = parseArguments[parseArguments.Length - 1];
|
result = parseArguments[parseArguments.Length - 1];
|
||||||
return true;
|
return true;
|
||||||
}
|
} catch {
|
||||||
catch
|
|
||||||
{
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -247,15 +244,11 @@
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="instance">The instance.</param>
|
/// <param name="instance">The instance.</param>
|
||||||
/// <returns>A <see cref="System.String" /> that represents the current object.</returns>
|
/// <returns>A <see cref="System.String" /> that represents the current object.</returns>
|
||||||
public string ToStringInvariant(object instance)
|
public String ToStringInvariant(Object instance) => instance == null
|
||||||
{
|
? String.Empty
|
||||||
if (instance == null)
|
: this._toStringArgumentLength != 1
|
||||||
return string.Empty;
|
|
||||||
|
|
||||||
return _toStringArgumentLength != 1
|
|
||||||
? instance.ToString()
|
? instance.ToString()
|
||||||
: ToStringMethodInfo.Invoke(instance, new object[] {CultureInfo.InvariantCulture}) as string;
|
: this.ToStringMethodInfo.Invoke(instance, new Object[] { CultureInfo.InvariantCulture }) as String;
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
}
|
}
|
||||||
@ -267,14 +260,12 @@
|
|||||||
/// and it is not meant for other than querying the BasicTypesInfo dictionary.
|
/// and it is not meant for other than querying the BasicTypesInfo dictionary.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <typeparam name="T">The type of extended type information.</typeparam>
|
/// <typeparam name="T">The type of extended type information.</typeparam>
|
||||||
public class ExtendedTypeInfo<T> : ExtendedTypeInfo
|
public class ExtendedTypeInfo<T> : ExtendedTypeInfo {
|
||||||
{
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="ExtendedTypeInfo{T}"/> class.
|
/// Initializes a new instance of the <see cref="ExtendedTypeInfo{T}"/> class.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public ExtendedTypeInfo()
|
public ExtendedTypeInfo()
|
||||||
: base(typeof(T))
|
: base(typeof(T)) {
|
||||||
{
|
|
||||||
// placeholder
|
// placeholder
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -285,6 +276,6 @@
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="instance">The instance.</param>
|
/// <param name="instance">The instance.</param>
|
||||||
/// <returns>A <see cref="System.String" /> that represents the current object.</returns>
|
/// <returns>A <see cref="System.String" /> that represents the current object.</returns>
|
||||||
public string ToStringInvariant(T instance) => base.ToStringInvariant(instance);
|
public String ToStringInvariant(T instance) => base.ToStringInvariant(instance);
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,14 +1,12 @@
|
|||||||
namespace Unosquare.Swan.Reflection
|
using System;
|
||||||
{
|
using System.Reflection;
|
||||||
using System;
|
using System.Collections.Concurrent;
|
||||||
using System.Reflection;
|
|
||||||
using System.Collections.Concurrent;
|
|
||||||
|
|
||||||
|
namespace Unosquare.Swan.Reflection {
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents a Method Info Cache.
|
/// Represents a Method Info Cache.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class MethodInfoCache : ConcurrentDictionary<string, MethodInfo>
|
public class MethodInfoCache : ConcurrentDictionary<String, MethodInfo> {
|
||||||
{
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Retrieves the properties stored for the specified type.
|
/// Retrieves the properties stored for the specified type.
|
||||||
/// If the properties are not available, it calls the factory method to retrieve them
|
/// If the properties are not available, it calls the factory method to retrieve them
|
||||||
@ -25,8 +23,8 @@
|
|||||||
/// or
|
/// or
|
||||||
/// factory.</exception>
|
/// factory.</exception>
|
||||||
/// <exception cref="System.ArgumentNullException">type.</exception>
|
/// <exception cref="System.ArgumentNullException">type.</exception>
|
||||||
public MethodInfo Retrieve<T>(string name, string alias, params Type[] types)
|
public MethodInfo Retrieve<T>(String name, String alias, params Type[] types)
|
||||||
=> Retrieve(typeof(T), name, alias, types);
|
=> this.Retrieve(typeof(T), name, alias, types);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Retrieves the specified name.
|
/// Retrieves the specified name.
|
||||||
@ -37,8 +35,8 @@
|
|||||||
/// <returns>
|
/// <returns>
|
||||||
/// The cached MethodInfo.
|
/// The cached MethodInfo.
|
||||||
/// </returns>
|
/// </returns>
|
||||||
public MethodInfo Retrieve<T>(string name, params Type[] types)
|
public MethodInfo Retrieve<T>(String name, params Type[] types)
|
||||||
=> Retrieve(typeof(T), name, name, types);
|
=> this.Retrieve(typeof(T), name, name, types);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Retrieves the specified type.
|
/// Retrieves the specified type.
|
||||||
@ -49,8 +47,8 @@
|
|||||||
/// <returns>
|
/// <returns>
|
||||||
/// An array of the properties stored for the specified type.
|
/// An array of the properties stored for the specified type.
|
||||||
/// </returns>
|
/// </returns>
|
||||||
public MethodInfo Retrieve(Type type, string name, params Type[] types)
|
public MethodInfo Retrieve(Type type, String name, params Type[] types)
|
||||||
=> Retrieve(type, name, name, types);
|
=> this.Retrieve(type, name, name, types);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Retrieves the specified type.
|
/// Retrieves the specified type.
|
||||||
@ -62,18 +60,20 @@
|
|||||||
/// <returns>
|
/// <returns>
|
||||||
/// The cached MethodInfo.
|
/// The cached MethodInfo.
|
||||||
/// </returns>
|
/// </returns>
|
||||||
public MethodInfo Retrieve(Type type, string name, string alias, params Type[] types)
|
public MethodInfo Retrieve(Type type, String name, String alias, params Type[] types) {
|
||||||
{
|
if(type == null) {
|
||||||
if (type == null)
|
|
||||||
throw new ArgumentNullException(nameof(type));
|
throw new ArgumentNullException(nameof(type));
|
||||||
|
}
|
||||||
|
|
||||||
if (alias == null)
|
if(alias == null) {
|
||||||
throw new ArgumentNullException(nameof(alias));
|
throw new ArgumentNullException(nameof(alias));
|
||||||
|
}
|
||||||
|
|
||||||
if (name == null)
|
if(name == null) {
|
||||||
throw new ArgumentNullException(nameof(name));
|
throw new ArgumentNullException(nameof(name));
|
||||||
|
}
|
||||||
|
|
||||||
return GetOrAdd(
|
return this.GetOrAdd(
|
||||||
alias,
|
alias,
|
||||||
x => type.GetMethod(name, types ?? new Type[0]));
|
x => type.GetMethod(name, types ?? new Type[0]));
|
||||||
}
|
}
|
||||||
@ -86,8 +86,8 @@
|
|||||||
/// <returns>
|
/// <returns>
|
||||||
/// The cached MethodInfo.
|
/// The cached MethodInfo.
|
||||||
/// </returns>
|
/// </returns>
|
||||||
public MethodInfo Retrieve<T>(string name)
|
public MethodInfo Retrieve<T>(String name)
|
||||||
=> Retrieve(typeof(T), name);
|
=> this.Retrieve(typeof(T), name);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Retrieves the specified type.
|
/// Retrieves the specified type.
|
||||||
@ -102,15 +102,16 @@
|
|||||||
/// or
|
/// or
|
||||||
/// name.
|
/// name.
|
||||||
/// </exception>
|
/// </exception>
|
||||||
public MethodInfo Retrieve(Type type, string name)
|
public MethodInfo Retrieve(Type type, String name) {
|
||||||
{
|
if(type == null) {
|
||||||
if (type == null)
|
|
||||||
throw new ArgumentNullException(nameof(type));
|
throw new ArgumentNullException(nameof(type));
|
||||||
|
}
|
||||||
|
|
||||||
if (name == null)
|
if(name == null) {
|
||||||
throw new ArgumentNullException(nameof(name));
|
throw new ArgumentNullException(nameof(name));
|
||||||
|
}
|
||||||
|
|
||||||
return GetOrAdd(
|
return this.GetOrAdd(
|
||||||
name,
|
name,
|
||||||
type.GetMethod);
|
type.GetMethod);
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,10 @@
|
|||||||
namespace Unosquare.Swan.Reflection
|
using System.Linq;
|
||||||
{
|
using System;
|
||||||
using System.Linq;
|
using System.Collections.Generic;
|
||||||
using System;
|
using System.Reflection;
|
||||||
using System.Collections.Generic;
|
using Unosquare.Swan.Components;
|
||||||
using System.Reflection;
|
|
||||||
using Components;
|
|
||||||
|
|
||||||
|
namespace Unosquare.Swan.Reflection {
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A thread-safe cache of members belonging to a given type.
|
/// A thread-safe cache of members belonging to a given type.
|
||||||
///
|
///
|
||||||
@ -15,8 +14,7 @@
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <typeparam name="T">The type of Member to be cached.</typeparam>
|
/// <typeparam name="T">The type of Member to be cached.</typeparam>
|
||||||
public abstract class TypeCache<T> : CollectionCacheRepository<T>
|
public abstract class TypeCache<T> : CollectionCacheRepository<T>
|
||||||
where T : MemberInfo
|
where T : MemberInfo {
|
||||||
{
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Determines whether the cache contains the specified type.
|
/// Determines whether the cache contains the specified type.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -24,7 +22,7 @@
|
|||||||
/// <returns>
|
/// <returns>
|
||||||
/// <c>true</c> if [contains]; otherwise, <c>false</c>.
|
/// <c>true</c> if [contains]; otherwise, <c>false</c>.
|
||||||
/// </returns>
|
/// </returns>
|
||||||
public bool Contains<TOut>() => ContainsKey(typeof(TOut));
|
public Boolean Contains<TOut>() => this.ContainsKey(typeof(TOut));
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Retrieves the properties stored for the specified type.
|
/// Retrieves the properties stored for the specified type.
|
||||||
@ -35,7 +33,7 @@
|
|||||||
/// <param name="factory">The factory.</param>
|
/// <param name="factory">The factory.</param>
|
||||||
/// <returns>An array of the properties stored for the specified type.</returns>
|
/// <returns>An array of the properties stored for the specified type.</returns>
|
||||||
public IEnumerable<T> Retrieve<TOut>(Func<Type, IEnumerable<T>> factory)
|
public IEnumerable<T> Retrieve<TOut>(Func<Type, IEnumerable<T>> factory)
|
||||||
=> Retrieve(typeof(TOut), factory);
|
=> this.Retrieve(typeof(TOut), factory);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -45,8 +43,7 @@
|
|||||||
/// calls the retrieval process if the type is not contained
|
/// calls the retrieval process if the type is not contained
|
||||||
/// in the cache.
|
/// in the cache.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class PropertyTypeCache : TypeCache<PropertyInfo>
|
public class PropertyTypeCache : TypeCache<PropertyInfo> {
|
||||||
{
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Retrieves all properties.
|
/// Retrieves all properties.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -55,8 +52,8 @@
|
|||||||
/// <returns>
|
/// <returns>
|
||||||
/// A collection with all the properties in the given type.
|
/// A collection with all the properties in the given type.
|
||||||
/// </returns>
|
/// </returns>
|
||||||
public IEnumerable<PropertyInfo> RetrieveAllProperties<T>(bool onlyPublic = false)
|
public IEnumerable<PropertyInfo> RetrieveAllProperties<T>(Boolean onlyPublic = false)
|
||||||
=> Retrieve<T>(onlyPublic ? GetAllPublicPropertiesFunc() : GetAllPropertiesFunc());
|
=> this.Retrieve<T>(onlyPublic ? GetAllPublicPropertiesFunc() : GetAllPropertiesFunc());
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Retrieves all properties.
|
/// Retrieves all properties.
|
||||||
@ -66,8 +63,8 @@
|
|||||||
/// <returns>
|
/// <returns>
|
||||||
/// A collection with all the properties in the given type.
|
/// A collection with all the properties in the given type.
|
||||||
/// </returns>
|
/// </returns>
|
||||||
public IEnumerable<PropertyInfo> RetrieveAllProperties(Type type, bool onlyPublic = false)
|
public IEnumerable<PropertyInfo> RetrieveAllProperties(Type type, Boolean onlyPublic = false)
|
||||||
=> Retrieve(type, onlyPublic ? GetAllPublicPropertiesFunc() : GetAllPropertiesFunc());
|
=> this.Retrieve(type, onlyPublic ? GetAllPublicPropertiesFunc() : GetAllPropertiesFunc());
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Retrieves the filtered properties.
|
/// Retrieves the filtered properties.
|
||||||
@ -80,23 +77,23 @@
|
|||||||
/// </returns>
|
/// </returns>
|
||||||
public IEnumerable<PropertyInfo> RetrieveFilteredProperties(
|
public IEnumerable<PropertyInfo> RetrieveFilteredProperties(
|
||||||
Type type,
|
Type type,
|
||||||
bool onlyPublic,
|
Boolean onlyPublic,
|
||||||
Func<PropertyInfo, bool> filter)
|
Func<PropertyInfo, Boolean> filter)
|
||||||
=> Retrieve(type,
|
=> this.Retrieve(type,
|
||||||
onlyPublic ? GetAllPublicPropertiesFunc(filter) : GetAllPropertiesFunc(filter));
|
onlyPublic ? GetAllPublicPropertiesFunc(filter) : GetAllPropertiesFunc(filter));
|
||||||
|
|
||||||
private static Func<Type, IEnumerable<PropertyInfo>> GetAllPropertiesFunc(
|
private static Func<Type, IEnumerable<PropertyInfo>> GetAllPropertiesFunc(
|
||||||
Func<PropertyInfo, bool> filter = null)
|
Func<PropertyInfo, Boolean> filter = null)
|
||||||
=> GetPropertiesFunc(
|
=> GetPropertiesFunc(
|
||||||
BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance,
|
BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance,
|
||||||
filter);
|
filter);
|
||||||
|
|
||||||
private static Func<Type, IEnumerable<PropertyInfo>> GetAllPublicPropertiesFunc(
|
private static Func<Type, IEnumerable<PropertyInfo>> GetAllPublicPropertiesFunc(
|
||||||
Func<PropertyInfo, bool> filter = null)
|
Func<PropertyInfo, Boolean> filter = null)
|
||||||
=> GetPropertiesFunc(BindingFlags.Public | BindingFlags.Instance, filter);
|
=> GetPropertiesFunc(BindingFlags.Public | BindingFlags.Instance, filter);
|
||||||
|
|
||||||
private static Func<Type, IEnumerable<PropertyInfo>> GetPropertiesFunc(BindingFlags flags,
|
private static Func<Type, IEnumerable<PropertyInfo>> GetPropertiesFunc(BindingFlags flags,
|
||||||
Func<PropertyInfo, bool> filter = null)
|
Func<PropertyInfo, Boolean> filter = null)
|
||||||
=> t => t.GetProperties(flags)
|
=> t => t.GetProperties(flags)
|
||||||
.Where(filter ?? (p => p.CanRead || p.CanWrite));
|
.Where(filter ?? (p => p.CanRead || p.CanWrite));
|
||||||
}
|
}
|
||||||
@ -107,8 +104,7 @@
|
|||||||
/// calls the retrieval process if the type is not contained
|
/// calls the retrieval process if the type is not contained
|
||||||
/// in the cache.
|
/// in the cache.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class FieldTypeCache : TypeCache<FieldInfo>
|
public class FieldTypeCache : TypeCache<FieldInfo> {
|
||||||
{
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Retrieves all fields.
|
/// Retrieves all fields.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -117,7 +113,7 @@
|
|||||||
/// A collection with all the fields in the given type.
|
/// A collection with all the fields in the given type.
|
||||||
/// </returns>
|
/// </returns>
|
||||||
public IEnumerable<FieldInfo> RetrieveAllFields<T>()
|
public IEnumerable<FieldInfo> RetrieveAllFields<T>()
|
||||||
=> Retrieve<T>(GetAllFieldsFunc());
|
=> this.Retrieve<T>(GetAllFieldsFunc());
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Retrieves all fields.
|
/// Retrieves all fields.
|
||||||
@ -127,7 +123,7 @@
|
|||||||
/// A collection with all the fields in the given type.
|
/// A collection with all the fields in the given type.
|
||||||
/// </returns>
|
/// </returns>
|
||||||
public IEnumerable<FieldInfo> RetrieveAllFields(Type type)
|
public IEnumerable<FieldInfo> RetrieveAllFields(Type type)
|
||||||
=> Retrieve(type, GetAllFieldsFunc());
|
=> this.Retrieve(type, GetAllFieldsFunc());
|
||||||
|
|
||||||
private static Func<Type, IEnumerable<FieldInfo>> GetAllFieldsFunc()
|
private static Func<Type, IEnumerable<FieldInfo>> GetAllFieldsFunc()
|
||||||
=> t => t.GetFields(BindingFlags.Public | BindingFlags.Instance);
|
=> t => t.GetFields(BindingFlags.Public | BindingFlags.Instance);
|
||||||
|
@ -1,14 +1,15 @@
|
|||||||
namespace Unosquare.Swan
|
using Unosquare.Swan.Components;
|
||||||
{
|
using System;
|
||||||
using Components;
|
using System.IO;
|
||||||
using System;
|
using System.Threading;
|
||||||
using System.IO;
|
using Unosquare.Swan.Reflection;
|
||||||
using System.Threading;
|
|
||||||
using Reflection;
|
|
||||||
#if !NETSTANDARD1_3
|
#if !NETSTANDARD1_3
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
namespace Unosquare.Swan {
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Provides utility methods to retrieve information about the current application.
|
/// Provides utility methods to retrieve information about the current application.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -41,25 +42,22 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if !NETSTANDARD1_3
|
#if !NETSTANDARD1_3
|
||||||
private static readonly Lazy<string> CompanyNameLazy = new Lazy<string>(() =>
|
private static readonly Lazy<String> CompanyNameLazy = new Lazy<String>(() => {
|
||||||
{
|
AssemblyCompanyAttribute attribute =
|
||||||
var attribute =
|
|
||||||
EntryAssembly.GetCustomAttribute(typeof(AssemblyCompanyAttribute)) as AssemblyCompanyAttribute;
|
EntryAssembly.GetCustomAttribute(typeof(AssemblyCompanyAttribute)) as AssemblyCompanyAttribute;
|
||||||
return attribute?.Company ?? string.Empty;
|
return attribute?.Company ?? String.Empty;
|
||||||
});
|
});
|
||||||
|
|
||||||
private static readonly Lazy<string> ProductNameLazy = new Lazy<string>(() =>
|
private static readonly Lazy<String> ProductNameLazy = new Lazy<String>(() => {
|
||||||
{
|
AssemblyProductAttribute attribute =
|
||||||
var attribute =
|
|
||||||
EntryAssembly.GetCustomAttribute(typeof(AssemblyProductAttribute)) as AssemblyProductAttribute;
|
EntryAssembly.GetCustomAttribute(typeof(AssemblyProductAttribute)) as AssemblyProductAttribute;
|
||||||
return attribute?.Product ?? string.Empty;
|
return attribute?.Product ?? String.Empty;
|
||||||
});
|
});
|
||||||
|
|
||||||
private static readonly Lazy<string> ProductTrademarkLazy = new Lazy<string>(() =>
|
private static readonly Lazy<String> ProductTrademarkLazy = new Lazy<String>(() => {
|
||||||
{
|
AssemblyTrademarkAttribute attribute =
|
||||||
var attribute =
|
|
||||||
EntryAssembly.GetCustomAttribute(typeof(AssemblyTrademarkAttribute)) as AssemblyTrademarkAttribute;
|
EntryAssembly.GetCustomAttribute(typeof(AssemblyTrademarkAttribute)) as AssemblyTrademarkAttribute;
|
||||||
return attribute?.Trademark ?? string.Empty;
|
return attribute?.Trademark ?? String.Empty;
|
||||||
});
|
});
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -69,12 +67,12 @@
|
|||||||
private static readonly Lazy<ObjectMapper> _objectMapper = new Lazy<ObjectMapper>(() => new ObjectMapper());
|
private static readonly Lazy<ObjectMapper> _objectMapper = new Lazy<ObjectMapper>(() => new ObjectMapper());
|
||||||
|
|
||||||
#if !NETSTANDARD1_3
|
#if !NETSTANDARD1_3
|
||||||
private static readonly string ApplicationMutexName = "Global\\{{" + EntryAssembly.FullName + "}}";
|
private static readonly String ApplicationMutexName = "Global\\{{" + EntryAssembly.FullName + "}}";
|
||||||
#else
|
#else
|
||||||
private const string ApplicationMutexName = "Global\\{{SWANINSTANCE}}";
|
private const string ApplicationMutexName = "Global\\{{SWANINSTANCE}}";
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
private static readonly object SyncLock = new object();
|
private static readonly Object SyncLock = new Object();
|
||||||
|
|
||||||
private static OperatingSystem? _oS;
|
private static OperatingSystem? _oS;
|
||||||
|
|
||||||
@ -86,23 +84,15 @@
|
|||||||
/// <value>
|
/// <value>
|
||||||
/// The os.
|
/// The os.
|
||||||
/// </value>
|
/// </value>
|
||||||
public static OperatingSystem OS
|
public static OperatingSystem OS {
|
||||||
{
|
get {
|
||||||
get
|
if(_oS.HasValue == false) {
|
||||||
{
|
String windowsDirectory = Environment.GetEnvironmentVariable("windir");
|
||||||
if (_oS.HasValue == false)
|
_oS = String.IsNullOrEmpty(windowsDirectory) == false
|
||||||
{
|
|
||||||
var windowsDirectory = Environment.GetEnvironmentVariable("windir");
|
|
||||||
if (string.IsNullOrEmpty(windowsDirectory) == false
|
|
||||||
&& windowsDirectory.Contains(@"\")
|
&& windowsDirectory.Contains(@"\")
|
||||||
&& Directory.Exists(windowsDirectory))
|
&& Directory.Exists(windowsDirectory)
|
||||||
{
|
? (OperatingSystem?)OperatingSystem.Windows
|
||||||
_oS = OperatingSystem.Windows;
|
: (OperatingSystem?)(File.Exists(@"/proc/sys/kernel/ostype") ? OperatingSystem.Unix : OperatingSystem.Osx);
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_oS = File.Exists(@"/proc/sys/kernel/ostype") ? OperatingSystem.Unix : OperatingSystem.Osx;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return _oS ?? OperatingSystem.Unknown;
|
return _oS ?? OperatingSystem.Unknown;
|
||||||
@ -125,31 +115,23 @@
|
|||||||
/// <value>
|
/// <value>
|
||||||
/// <c>true</c> if this instance is the only instance; otherwise, <c>false</c>.
|
/// <c>true</c> if this instance is the only instance; otherwise, <c>false</c>.
|
||||||
/// </value>
|
/// </value>
|
||||||
public static bool IsTheOnlyInstance
|
[System.Diagnostics.CodeAnalysis.SuppressMessage("Codequalität", "IDE0067:Objekte verwerfen, bevor Bereich verloren geht", Justification = "<Ausstehend>")]
|
||||||
{
|
public static Boolean IsTheOnlyInstance {
|
||||||
get
|
get {
|
||||||
{
|
lock(SyncLock) {
|
||||||
lock (SyncLock)
|
try {
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
// Try to open existing mutex.
|
// Try to open existing mutex.
|
||||||
Mutex.OpenExisting(ApplicationMutexName);
|
_ = Mutex.OpenExisting(ApplicationMutexName);
|
||||||
}
|
} catch {
|
||||||
catch
|
try {
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
// If exception occurred, there is no such mutex.
|
// If exception occurred, there is no such mutex.
|
||||||
var appMutex = new Mutex(true, ApplicationMutexName);
|
Mutex appMutex = new Mutex(true, ApplicationMutexName);
|
||||||
$"Application Mutex created {appMutex} named '{ApplicationMutexName}'".Debug(
|
$"Application Mutex created {appMutex} named '{ApplicationMutexName}'".Debug(
|
||||||
typeof(Runtime));
|
typeof(Runtime));
|
||||||
|
|
||||||
// Only one instance.
|
// Only one instance.
|
||||||
return true;
|
return true;
|
||||||
}
|
} catch {
|
||||||
catch
|
|
||||||
{
|
|
||||||
// Sometimes the user can't create the Global Mutex
|
// Sometimes the user can't create the Global Mutex
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -166,7 +148,7 @@
|
|||||||
/// <value>
|
/// <value>
|
||||||
/// <c>true</c> if this instance is using MONO runtime; otherwise, <c>false</c>.
|
/// <c>true</c> if this instance is using MONO runtime; otherwise, <c>false</c>.
|
||||||
/// </value>
|
/// </value>
|
||||||
public static bool IsUsingMonoRuntime => Type.GetType("Mono.Runtime") != null;
|
public static Boolean IsUsingMonoRuntime => Type.GetType("Mono.Runtime") != null;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the property type cache.
|
/// Gets the property type cache.
|
||||||
@ -236,12 +218,10 @@
|
|||||||
/// <value>
|
/// <value>
|
||||||
/// The entry assembly directory.
|
/// The entry assembly directory.
|
||||||
/// </value>
|
/// </value>
|
||||||
public static string EntryAssemblyDirectory
|
public static String EntryAssemblyDirectory {
|
||||||
{
|
get {
|
||||||
get
|
UriBuilder uri = new UriBuilder(EntryAssembly.CodeBase);
|
||||||
{
|
String path = Uri.UnescapeDataString(uri.Path);
|
||||||
var uri = new UriBuilder(EntryAssembly.CodeBase);
|
|
||||||
var path = Uri.UnescapeDataString(uri.Path);
|
|
||||||
return Path.GetDirectoryName(path);
|
return Path.GetDirectoryName(path);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -252,7 +232,7 @@
|
|||||||
/// <value>
|
/// <value>
|
||||||
/// The name of the company.
|
/// The name of the company.
|
||||||
/// </value>
|
/// </value>
|
||||||
public static string CompanyName => CompanyNameLazy.Value;
|
public static String CompanyName => CompanyNameLazy.Value;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the name of the product.
|
/// Gets the name of the product.
|
||||||
@ -260,7 +240,7 @@
|
|||||||
/// <value>
|
/// <value>
|
||||||
/// The name of the product.
|
/// The name of the product.
|
||||||
/// </value>
|
/// </value>
|
||||||
public static string ProductName => ProductNameLazy.Value;
|
public static String ProductName => ProductNameLazy.Value;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the trademark.
|
/// Gets the trademark.
|
||||||
@ -268,7 +248,7 @@
|
|||||||
/// <value>
|
/// <value>
|
||||||
/// The product trademark.
|
/// The product trademark.
|
||||||
/// </value>
|
/// </value>
|
||||||
public static string ProductTrademark => ProductTrademarkLazy.Value;
|
public static String ProductTrademark => ProductTrademarkLazy.Value;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -277,12 +257,10 @@
|
|||||||
/// <value>
|
/// <value>
|
||||||
/// The local storage path.
|
/// The local storage path.
|
||||||
/// </value>
|
/// </value>
|
||||||
public static string LocalStoragePath
|
public static String LocalStoragePath {
|
||||||
{
|
get {
|
||||||
get
|
|
||||||
{
|
|
||||||
#if !NETSTANDARD1_3
|
#if !NETSTANDARD1_3
|
||||||
var localAppDataPath =
|
String localAppDataPath =
|
||||||
#if NET452
|
#if NET452
|
||||||
Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
|
Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
|
||||||
EntryAssemblyName.Name);
|
EntryAssemblyName.Name);
|
||||||
@ -290,13 +268,12 @@
|
|||||||
Path.GetDirectoryName(EntryAssembly.Location);
|
Path.GetDirectoryName(EntryAssembly.Location);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
var returnPath = Path.Combine(localAppDataPath, EntryAssemblyVersion.ToString());
|
String returnPath = Path.Combine(localAppDataPath, EntryAssemblyVersion.ToString());
|
||||||
#else
|
#else
|
||||||
var returnPath = Directory.GetCurrentDirectory(); // Use current path...
|
var returnPath = Directory.GetCurrentDirectory(); // Use current path...
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (Directory.Exists(returnPath) == false)
|
if(Directory.Exists(returnPath) == false) {
|
||||||
{
|
|
||||||
Directory.CreateDirectory(returnPath);
|
Directory.CreateDirectory(returnPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -330,8 +307,7 @@
|
|||||||
/// containing the company name, product name, assembly version and trademark.
|
/// containing the company name, product name, assembly version and trademark.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="color">The color.</param>
|
/// <param name="color">The color.</param>
|
||||||
public static void WriteWelcomeBanner(ConsoleColor color = ConsoleColor.Gray)
|
public static void WriteWelcomeBanner(ConsoleColor color = ConsoleColor.Gray) {
|
||||||
{
|
|
||||||
$"{CompanyName} {ProductName} [Version {EntryAssemblyVersion}]".WriteLine(color);
|
$"{CompanyName} {ProductName} [Version {EntryAssemblyVersion}]".WriteLine(color);
|
||||||
$"{ProductTrademark}".WriteLine(color);
|
$"{ProductTrademark}".WriteLine(color);
|
||||||
}
|
}
|
||||||
@ -350,12 +326,12 @@
|
|||||||
/// The fully qualified location of path, such as "C:\MyFile.txt".
|
/// The fully qualified location of path, such as "C:\MyFile.txt".
|
||||||
/// </returns>
|
/// </returns>
|
||||||
/// <exception cref="ArgumentNullException">filename.</exception>
|
/// <exception cref="ArgumentNullException">filename.</exception>
|
||||||
public static string GetDesktopFilePath(string filename)
|
public static String GetDesktopFilePath(String filename) {
|
||||||
{
|
if(String.IsNullOrWhiteSpace(filename)) {
|
||||||
if (string.IsNullOrWhiteSpace(filename))
|
|
||||||
throw new ArgumentNullException(nameof(filename));
|
throw new ArgumentNullException(nameof(filename));
|
||||||
|
}
|
||||||
|
|
||||||
var pathWithFilename = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory),
|
String pathWithFilename = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory),
|
||||||
filename);
|
filename);
|
||||||
return Path.GetFullPath(pathWithFilename);
|
return Path.GetFullPath(pathWithFilename);
|
||||||
}
|
}
|
||||||
|
@ -1,13 +1,11 @@
|
|||||||
namespace Unosquare.Swan
|
using System;
|
||||||
{
|
|
||||||
using System;
|
|
||||||
|
|
||||||
|
namespace Unosquare.Swan {
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Defines a set of bitwise standard terminal writers.
|
/// Defines a set of bitwise standard terminal writers.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Flags]
|
[Flags]
|
||||||
public enum TerminalWriters
|
public enum TerminalWriters {
|
||||||
{
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Prevents output
|
/// Prevents output
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -49,8 +47,7 @@
|
|||||||
/// which types of messages get printed on the current console.
|
/// which types of messages get printed on the current console.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Flags]
|
[Flags]
|
||||||
public enum LogMessageType
|
public enum LogMessageType {
|
||||||
{
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The none message type
|
/// The none message type
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -1,18 +1,15 @@
|
|||||||
namespace Unosquare.Swan
|
using System;
|
||||||
{
|
|
||||||
using System;
|
|
||||||
|
|
||||||
|
namespace Unosquare.Swan {
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A console terminal helper to create nicer output and receive input from the user
|
/// A console terminal helper to create nicer output and receive input from the user
|
||||||
/// This class is thread-safe :).
|
/// This class is thread-safe :).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static partial class Terminal
|
public static partial class Terminal {
|
||||||
{
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents a Table to print in console.
|
/// Represents a Table to print in console.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private static class Table
|
private static class Table {
|
||||||
{
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the color of the border.
|
/// Gets or sets the color of the border.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -21,27 +18,27 @@
|
|||||||
/// </value>
|
/// </value>
|
||||||
private static ConsoleColor BorderColor { get; } = ConsoleColor.DarkGreen;
|
private static ConsoleColor BorderColor { get; } = ConsoleColor.DarkGreen;
|
||||||
|
|
||||||
public static void Vertical() => ((byte)179).Write(BorderColor);
|
public static void Vertical() => ((Byte)179).Write(BorderColor);
|
||||||
|
|
||||||
public static void RightTee() => ((byte)180).Write(BorderColor);
|
public static void RightTee() => ((Byte)180).Write(BorderColor);
|
||||||
|
|
||||||
public static void TopRight() => ((byte)191).Write(BorderColor);
|
public static void TopRight() => ((Byte)191).Write(BorderColor);
|
||||||
|
|
||||||
public static void BottomLeft() => ((byte)192).Write(BorderColor);
|
public static void BottomLeft() => ((Byte)192).Write(BorderColor);
|
||||||
|
|
||||||
public static void BottomTee() => ((byte)193).Write(BorderColor);
|
public static void BottomTee() => ((Byte)193).Write(BorderColor);
|
||||||
|
|
||||||
public static void TopTee() => ((byte)194).Write(BorderColor);
|
public static void TopTee() => ((Byte)194).Write(BorderColor);
|
||||||
|
|
||||||
public static void LeftTee() => ((byte)195).Write(BorderColor);
|
public static void LeftTee() => ((Byte)195).Write(BorderColor);
|
||||||
|
|
||||||
public static void Horizontal(int length) => ((byte)196).Write(BorderColor, length);
|
public static void Horizontal(Int32 length) => ((Byte)196).Write(BorderColor, length);
|
||||||
|
|
||||||
public static void Tee() => ((byte)197).Write(BorderColor);
|
public static void Tee() => ((Byte)197).Write(BorderColor);
|
||||||
|
|
||||||
public static void BottomRight() => ((byte)217).Write(BorderColor);
|
public static void BottomRight() => ((Byte)217).Write(BorderColor);
|
||||||
|
|
||||||
public static void TopLeft() => ((byte)218).Write(BorderColor);
|
public static void TopLeft() => ((Byte)218).Write(BorderColor);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,14 +1,12 @@
|
|||||||
namespace Unosquare.Swan
|
using System;
|
||||||
{
|
using System.Collections.Generic;
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
|
|
||||||
|
namespace Unosquare.Swan {
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A console terminal helper to create nicer output and receive input from the user
|
/// A console terminal helper to create nicer output and receive input from the user
|
||||||
/// This class is thread-safe :).
|
/// This class is thread-safe :).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static partial class Terminal
|
public static partial class Terminal {
|
||||||
{
|
|
||||||
#region ReadKey
|
#region ReadKey
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -18,22 +16,22 @@
|
|||||||
/// <param name="disableLocking">if set to <c>true</c> the output will continue to be shown.
|
/// <param name="disableLocking">if set to <c>true</c> the output will continue to be shown.
|
||||||
/// This is useful for services and daemons that are running as console applications and wait for a key to exit the program.</param>
|
/// This is useful for services and daemons that are running as console applications and wait for a key to exit the program.</param>
|
||||||
/// <returns>The console key information.</returns>
|
/// <returns>The console key information.</returns>
|
||||||
public static ConsoleKeyInfo ReadKey(bool intercept, bool disableLocking = false)
|
public static ConsoleKeyInfo ReadKey(Boolean intercept, Boolean disableLocking = false) {
|
||||||
{
|
if(IsConsolePresent == false) {
|
||||||
if (IsConsolePresent == false) return default;
|
return default;
|
||||||
if (disableLocking) return Console.ReadKey(intercept);
|
}
|
||||||
|
|
||||||
lock (SyncLock)
|
if(disableLocking) {
|
||||||
{
|
|
||||||
Flush();
|
|
||||||
InputDone.Reset();
|
|
||||||
try
|
|
||||||
{
|
|
||||||
Console.CursorVisible = true;
|
|
||||||
return Console.ReadKey(intercept);
|
return Console.ReadKey(intercept);
|
||||||
}
|
}
|
||||||
finally
|
|
||||||
{
|
lock(SyncLock) {
|
||||||
|
Flush();
|
||||||
|
InputDone.Reset();
|
||||||
|
try {
|
||||||
|
Console.CursorVisible = true;
|
||||||
|
return Console.ReadKey(intercept);
|
||||||
|
} finally {
|
||||||
Console.CursorVisible = false;
|
Console.CursorVisible = false;
|
||||||
InputDone.Set();
|
InputDone.Set();
|
||||||
}
|
}
|
||||||
@ -46,20 +44,19 @@
|
|||||||
/// <param name="prompt">The prompt.</param>
|
/// <param name="prompt">The prompt.</param>
|
||||||
/// <param name="preventEcho">if set to <c>true</c> [prevent echo].</param>
|
/// <param name="preventEcho">if set to <c>true</c> [prevent echo].</param>
|
||||||
/// <returns>The console key information.</returns>
|
/// <returns>The console key information.</returns>
|
||||||
public static ConsoleKeyInfo ReadKey(this string prompt, bool preventEcho)
|
public static ConsoleKeyInfo ReadKey(this String prompt, Boolean preventEcho) {
|
||||||
{
|
if(IsConsolePresent == false) {
|
||||||
if (IsConsolePresent == false) return default;
|
return default;
|
||||||
|
}
|
||||||
|
|
||||||
lock (SyncLock)
|
lock(SyncLock) {
|
||||||
{
|
if(prompt != null) {
|
||||||
if (prompt != null)
|
($" {(String.IsNullOrWhiteSpace(Settings.LoggingTimeFormat) ? String.Empty : DateTime.Now.ToString(Settings.LoggingTimeFormat) + " ")}" +
|
||||||
{
|
|
||||||
($" {(string.IsNullOrWhiteSpace(Settings.LoggingTimeFormat) ? string.Empty : DateTime.Now.ToString(Settings.LoggingTimeFormat) + " ")}" +
|
|
||||||
$"{Settings.UserInputPrefix} << {prompt} ").Write(ConsoleColor.White);
|
$"{Settings.UserInputPrefix} << {prompt} ").Write(ConsoleColor.White);
|
||||||
}
|
}
|
||||||
|
|
||||||
var input = ReadKey(true);
|
ConsoleKeyInfo input = ReadKey(true);
|
||||||
var echo = preventEcho ? string.Empty : input.Key.ToString();
|
String echo = preventEcho ? String.Empty : input.Key.ToString();
|
||||||
echo.WriteLine();
|
echo.WriteLine();
|
||||||
return input;
|
return input;
|
||||||
}
|
}
|
||||||
@ -70,7 +67,7 @@
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="prompt">The prompt.</param>
|
/// <param name="prompt">The prompt.</param>
|
||||||
/// <returns>A value that identifies the console key.</returns>
|
/// <returns>A value that identifies the console key.</returns>
|
||||||
public static ConsoleKeyInfo ReadKey(this string prompt) => prompt.ReadKey(true);
|
public static ConsoleKeyInfo ReadKey(this String prompt) => prompt.ReadKey(true);
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
@ -80,22 +77,19 @@
|
|||||||
/// Reads a line of text from the console.
|
/// Reads a line of text from the console.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>The read line.</returns>
|
/// <returns>The read line.</returns>
|
||||||
public static string ReadLine()
|
public static String ReadLine() {
|
||||||
{
|
if(IsConsolePresent == false) {
|
||||||
if (IsConsolePresent == false) return default;
|
return default;
|
||||||
|
}
|
||||||
|
|
||||||
lock (SyncLock)
|
lock(SyncLock) {
|
||||||
{
|
|
||||||
Flush();
|
Flush();
|
||||||
InputDone.Reset();
|
InputDone.Reset();
|
||||||
|
|
||||||
try
|
try {
|
||||||
{
|
|
||||||
Console.CursorVisible = true;
|
Console.CursorVisible = true;
|
||||||
return Console.ReadLine();
|
return Console.ReadLine();
|
||||||
}
|
} finally {
|
||||||
finally
|
|
||||||
{
|
|
||||||
Console.CursorVisible = false;
|
Console.CursorVisible = false;
|
||||||
InputDone.Set();
|
InputDone.Set();
|
||||||
}
|
}
|
||||||
@ -110,16 +104,16 @@
|
|||||||
/// <returns>
|
/// <returns>
|
||||||
/// Conversions of string representation of a number to its 32-bit signed integer equivalent.
|
/// Conversions of string representation of a number to its 32-bit signed integer equivalent.
|
||||||
/// </returns>
|
/// </returns>
|
||||||
public static int ReadNumber(this string prompt, int defaultNumber)
|
public static Int32 ReadNumber(this String prompt, Int32 defaultNumber) {
|
||||||
{
|
if(IsConsolePresent == false) {
|
||||||
if (IsConsolePresent == false) return defaultNumber;
|
return defaultNumber;
|
||||||
|
}
|
||||||
|
|
||||||
lock (SyncLock)
|
lock(SyncLock) {
|
||||||
{
|
|
||||||
$" {DateTime.Now:HH:mm:ss} USR << {prompt} (default is {defaultNumber}): ".Write(ConsoleColor.White);
|
$" {DateTime.Now:HH:mm:ss} USR << {prompt} (default is {defaultNumber}): ".Write(ConsoleColor.White);
|
||||||
|
|
||||||
var input = ReadLine();
|
String input = ReadLine();
|
||||||
return int.TryParse(input, out var parsedInt) ? parsedInt : defaultNumber;
|
return Int32.TryParse(input, out Int32 parsedInt) ? parsedInt : defaultNumber;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -130,18 +124,18 @@
|
|||||||
/// <param name="options">The options.</param>
|
/// <param name="options">The options.</param>
|
||||||
/// <param name="anyKeyOption">Any key option.</param>
|
/// <param name="anyKeyOption">Any key option.</param>
|
||||||
/// <returns>A value that identifies the console key that was pressed.</returns>
|
/// <returns>A value that identifies the console key that was pressed.</returns>
|
||||||
public static ConsoleKeyInfo ReadPrompt(this string title, Dictionary<ConsoleKey, string> options, string anyKeyOption)
|
public static ConsoleKeyInfo ReadPrompt(this String title, Dictionary<ConsoleKey, String> options, String anyKeyOption) {
|
||||||
{
|
if(IsConsolePresent == false) {
|
||||||
if (IsConsolePresent == false) return default;
|
return default;
|
||||||
|
}
|
||||||
|
|
||||||
const ConsoleColor textColor = ConsoleColor.White;
|
const ConsoleColor textColor = ConsoleColor.White;
|
||||||
var lineLength = Console.BufferWidth;
|
Int32 lineLength = Console.BufferWidth;
|
||||||
var lineAlign = -(lineLength - 2);
|
Int32 lineAlign = -(lineLength - 2);
|
||||||
var textFormat = "{0," + lineAlign + "}";
|
String textFormat = "{0," + lineAlign + "}";
|
||||||
|
|
||||||
// lock the output as an atomic operation
|
// lock the output as an atomic operation
|
||||||
lock (SyncLock)
|
lock(SyncLock) {
|
||||||
{
|
|
||||||
{ // Top border
|
{ // Top border
|
||||||
Table.TopLeft();
|
Table.TopLeft();
|
||||||
Table.Horizontal(-lineAlign);
|
Table.Horizontal(-lineAlign);
|
||||||
@ -150,9 +144,9 @@
|
|||||||
|
|
||||||
{ // Title
|
{ // Title
|
||||||
Table.Vertical();
|
Table.Vertical();
|
||||||
var titleText = string.Format(
|
String titleText = String.Format(
|
||||||
textFormat,
|
textFormat,
|
||||||
string.IsNullOrWhiteSpace(title) ? " Select an option from the list below." : $" {title}");
|
String.IsNullOrWhiteSpace(title) ? " Select an option from the list below." : $" {title}");
|
||||||
titleText.Write(textColor);
|
titleText.Write(textColor);
|
||||||
Table.Vertical();
|
Table.Vertical();
|
||||||
}
|
}
|
||||||
@ -164,23 +158,21 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Options
|
// Options
|
||||||
foreach (var kvp in options)
|
foreach(KeyValuePair<ConsoleKey, String> kvp in options) {
|
||||||
{
|
|
||||||
Table.Vertical();
|
Table.Vertical();
|
||||||
string.Format(textFormat,
|
String.Format(textFormat,
|
||||||
$" {"[ " + kvp.Key + " ]",-10} {kvp.Value}").Write(textColor);
|
$" {"[ " + kvp.Key + " ]",-10} {kvp.Value}").Write(textColor);
|
||||||
Table.Vertical();
|
Table.Vertical();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Any Key Options
|
// Any Key Options
|
||||||
if (string.IsNullOrWhiteSpace(anyKeyOption) == false)
|
if(String.IsNullOrWhiteSpace(anyKeyOption) == false) {
|
||||||
{
|
|
||||||
Table.Vertical();
|
Table.Vertical();
|
||||||
string.Format(textFormat, " ").Write(ConsoleColor.Gray);
|
String.Format(textFormat, " ").Write(ConsoleColor.Gray);
|
||||||
Table.Vertical();
|
Table.Vertical();
|
||||||
|
|
||||||
Table.Vertical();
|
Table.Vertical();
|
||||||
string.Format(textFormat,
|
String.Format(textFormat,
|
||||||
$" {" ",-10} {anyKeyOption}").Write(ConsoleColor.Gray);
|
$" {" ",-10} {anyKeyOption}").Write(ConsoleColor.Gray);
|
||||||
Table.Vertical();
|
Table.Vertical();
|
||||||
}
|
}
|
||||||
@ -191,7 +183,7 @@
|
|||||||
Table.RightTee();
|
Table.RightTee();
|
||||||
|
|
||||||
Table.Vertical();
|
Table.Vertical();
|
||||||
string.Format(textFormat,
|
String.Format(textFormat,
|
||||||
Settings.UserOptionText).Write(ConsoleColor.Green);
|
Settings.UserOptionText).Write(ConsoleColor.Green);
|
||||||
Table.Vertical();
|
Table.Vertical();
|
||||||
|
|
||||||
@ -201,10 +193,10 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var inputLeft = Settings.UserOptionText.Length + 3;
|
Int32 inputLeft = Settings.UserOptionText.Length + 3;
|
||||||
|
|
||||||
SetCursorPosition(inputLeft, CursorTop - 2);
|
SetCursorPosition(inputLeft, CursorTop - 2);
|
||||||
var userInput = ReadKey(true);
|
ConsoleKeyInfo userInput = ReadKey(true);
|
||||||
userInput.Key.ToString().Write(ConsoleColor.Gray);
|
userInput.Key.ToString().Write(ConsoleColor.Gray);
|
||||||
|
|
||||||
SetCursorPosition(0, CursorTop + 2);
|
SetCursorPosition(0, CursorTop + 2);
|
||||||
|
@ -1,18 +1,16 @@
|
|||||||
namespace Unosquare.Swan
|
using System;
|
||||||
{
|
using System.Runtime.CompilerServices;
|
||||||
using System;
|
using System.Threading.Tasks;
|
||||||
using System.Runtime.CompilerServices;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
|
namespace Unosquare.Swan {
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A console terminal helper to create nicer output and receive input from the user
|
/// A console terminal helper to create nicer output and receive input from the user
|
||||||
/// This class is thread-safe :).
|
/// This class is thread-safe :).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static partial class Terminal
|
public static partial class Terminal {
|
||||||
{
|
|
||||||
#region Private Declarations
|
#region Private Declarations
|
||||||
|
|
||||||
private static ulong _loggingSequence;
|
private static UInt64 _loggingSequence;
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
@ -50,33 +48,32 @@
|
|||||||
/// <param name="callerLineNumber">The caller line number.</param>
|
/// <param name="callerLineNumber">The caller line number.</param>
|
||||||
private static void LogMessage(
|
private static void LogMessage(
|
||||||
LogMessageType messageType,
|
LogMessageType messageType,
|
||||||
string message,
|
String message,
|
||||||
string sourceName,
|
String sourceName,
|
||||||
object extendedData,
|
Object extendedData,
|
||||||
string callerMemberName,
|
String callerMemberName,
|
||||||
string callerFilePath,
|
String callerFilePath,
|
||||||
int callerLineNumber)
|
Int32 callerLineNumber) {
|
||||||
{
|
lock(SyncLock) {
|
||||||
lock (SyncLock)
|
if(!Settings.GlobalLoggingMessageType.HasFlag(messageType)) {
|
||||||
{
|
|
||||||
if (!Settings.GlobalLoggingMessageType.HasFlag(messageType))
|
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
var prefix = GetConsoleColorAndPrefix(messageType, out var color);
|
String prefix = GetConsoleColorAndPrefix(messageType, out ConsoleColor color);
|
||||||
|
|
||||||
#region Create and Format the Output
|
#region Create and Format the Output
|
||||||
|
|
||||||
var sequence = _loggingSequence;
|
UInt64 sequence = _loggingSequence;
|
||||||
var date = DateTime.UtcNow;
|
DateTime date = DateTime.UtcNow;
|
||||||
_loggingSequence++;
|
_loggingSequence++;
|
||||||
|
|
||||||
var loggerMessage = string.IsNullOrWhiteSpace(message) ?
|
String loggerMessage = String.IsNullOrWhiteSpace(message) ?
|
||||||
string.Empty : message.RemoveControlCharsExcept('\n');
|
String.Empty : message.RemoveControlCharsExcept('\n');
|
||||||
|
|
||||||
var outputMessage = CreateOutputMessage(sourceName, loggerMessage, prefix, date);
|
String outputMessage = CreateOutputMessage(sourceName, loggerMessage, prefix, date);
|
||||||
|
|
||||||
// Log the message asynchronously with the appropriate event args
|
// Log the message asynchronously with the appropriate event args
|
||||||
var eventArgs = new LogMessageReceivedEventArgs(
|
LogMessageReceivedEventArgs eventArgs = new LogMessageReceivedEventArgs(
|
||||||
sequence,
|
sequence,
|
||||||
messageType,
|
messageType,
|
||||||
date,
|
date,
|
||||||
@ -91,16 +88,11 @@
|
|||||||
|
|
||||||
#region Fire Up External Logging Logic (Asynchronously)
|
#region Fire Up External Logging Logic (Asynchronously)
|
||||||
|
|
||||||
if (OnLogMessageReceived != null)
|
if(OnLogMessageReceived != null) {
|
||||||
{
|
_ = Task.Run(() => {
|
||||||
Task.Run(() =>
|
try {
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
OnLogMessageReceived?.Invoke(sourceName, eventArgs);
|
OnLogMessageReceived?.Invoke(sourceName, eventArgs);
|
||||||
}
|
} catch {
|
||||||
catch
|
|
||||||
{
|
|
||||||
// Ignore
|
// Ignore
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -111,8 +103,9 @@
|
|||||||
#region Display the Message by Writing to the Output Queue
|
#region Display the Message by Writing to the Output Queue
|
||||||
|
|
||||||
// Check if we are skipping these messages to be displayed based on settings
|
// Check if we are skipping these messages to be displayed based on settings
|
||||||
if (!Settings.DisplayLoggingMessageType.HasFlag(messageType))
|
if(!Settings.DisplayLoggingMessageType.HasFlag(messageType)) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
Write(messageType, sourceName, eventArgs, outputMessage, color);
|
Write(messageType, sourceName, eventArgs, outputMessage, color);
|
||||||
|
|
||||||
@ -122,55 +115,52 @@
|
|||||||
|
|
||||||
private static void Write(
|
private static void Write(
|
||||||
LogMessageType messageType,
|
LogMessageType messageType,
|
||||||
string sourceName,
|
String sourceName,
|
||||||
LogMessageReceivedEventArgs eventArgs,
|
LogMessageReceivedEventArgs eventArgs,
|
||||||
string outputMessage,
|
String outputMessage,
|
||||||
ConsoleColor color)
|
ConsoleColor color) {
|
||||||
{
|
|
||||||
// Select the writer based on the message type
|
// Select the writer based on the message type
|
||||||
var writer = IsConsolePresent
|
TerminalWriters writer = IsConsolePresent
|
||||||
? messageType.HasFlag(LogMessageType.Error) ? TerminalWriters.StandardError : TerminalWriters.StandardOutput
|
? messageType.HasFlag(LogMessageType.Error) ? TerminalWriters.StandardError : TerminalWriters.StandardOutput
|
||||||
: TerminalWriters.None;
|
: TerminalWriters.None;
|
||||||
|
|
||||||
// Set the writer to Diagnostics if appropriate (Error and Debugging data go to the Diagnostics debugger
|
// Set the writer to Diagnostics if appropriate (Error and Debugging data go to the Diagnostics debugger
|
||||||
// if it is attached at all
|
// if it is attached at all
|
||||||
if (IsDebuggerAttached
|
if(IsDebuggerAttached
|
||||||
&& (IsConsolePresent == false || messageType.HasFlag(LogMessageType.Debug) ||
|
&& (IsConsolePresent == false || messageType.HasFlag(LogMessageType.Debug) ||
|
||||||
messageType.HasFlag(LogMessageType.Error)))
|
messageType.HasFlag(LogMessageType.Error))) {
|
||||||
writer = writer | TerminalWriters.Diagnostics;
|
writer |= TerminalWriters.Diagnostics;
|
||||||
|
}
|
||||||
|
|
||||||
// Check if we really need to write this out
|
// Check if we really need to write this out
|
||||||
if (writer == TerminalWriters.None) return;
|
if(writer == TerminalWriters.None) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Further format the output in the case there is an exception being logged
|
// Further format the output in the case there is an exception being logged
|
||||||
if (writer.HasFlag(TerminalWriters.StandardError) && eventArgs.Exception != null)
|
if(writer.HasFlag(TerminalWriters.StandardError) && eventArgs.Exception != null) {
|
||||||
{
|
try {
|
||||||
try
|
|
||||||
{
|
|
||||||
outputMessage =
|
outputMessage =
|
||||||
$"{outputMessage}{Environment.NewLine}{eventArgs.Exception.Stringify().Indent()}";
|
$"{outputMessage}{Environment.NewLine}{eventArgs.Exception.Stringify().Indent()}";
|
||||||
}
|
} catch {
|
||||||
catch
|
|
||||||
{
|
|
||||||
// Ignore
|
// Ignore
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Filter output messages via events
|
// Filter output messages via events
|
||||||
var displayingEventArgs = new LogMessageDisplayingEventArgs(eventArgs);
|
LogMessageDisplayingEventArgs displayingEventArgs = new LogMessageDisplayingEventArgs(eventArgs);
|
||||||
OnLogMessageDisplaying?.Invoke(sourceName, displayingEventArgs);
|
OnLogMessageDisplaying?.Invoke(sourceName, displayingEventArgs);
|
||||||
if (displayingEventArgs.CancelOutput == false)
|
if(displayingEventArgs.CancelOutput == false) {
|
||||||
outputMessage.WriteLine(color, writer);
|
outputMessage.WriteLine(color, writer);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static string GetConsoleColorAndPrefix(LogMessageType messageType, out ConsoleColor color)
|
private static String GetConsoleColorAndPrefix(LogMessageType messageType, out ConsoleColor color) {
|
||||||
{
|
String prefix;
|
||||||
string prefix;
|
|
||||||
|
|
||||||
// Select color and prefix based on message type
|
// Select color and prefix based on message type
|
||||||
// and settings
|
// and settings
|
||||||
switch (messageType)
|
switch(messageType) {
|
||||||
{
|
|
||||||
case LogMessageType.Debug:
|
case LogMessageType.Debug:
|
||||||
color = Settings.DebugColor;
|
color = Settings.DebugColor;
|
||||||
prefix = Settings.DebugPrefix;
|
prefix = Settings.DebugPrefix;
|
||||||
@ -197,24 +187,23 @@
|
|||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
color = Settings.DefaultColor;
|
color = Settings.DefaultColor;
|
||||||
prefix = new string(' ', Settings.InfoPrefix.Length);
|
prefix = new String(' ', Settings.InfoPrefix.Length);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return prefix;
|
return prefix;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static string CreateOutputMessage(string sourceName, string loggerMessage, string prefix, DateTime date)
|
private static String CreateOutputMessage(String sourceName, String loggerMessage, String prefix, DateTime date) {
|
||||||
{
|
String friendlySourceName = String.IsNullOrWhiteSpace(sourceName)
|
||||||
var friendlySourceName = string.IsNullOrWhiteSpace(sourceName)
|
? String.Empty
|
||||||
? string.Empty
|
|
||||||
: sourceName.SliceLength(sourceName.LastIndexOf('.') + 1, sourceName.Length);
|
: sourceName.SliceLength(sourceName.LastIndexOf('.') + 1, sourceName.Length);
|
||||||
|
|
||||||
var outputMessage = string.IsNullOrWhiteSpace(sourceName)
|
String outputMessage = String.IsNullOrWhiteSpace(sourceName)
|
||||||
? loggerMessage
|
? loggerMessage
|
||||||
: $"[{friendlySourceName}] {loggerMessage}";
|
: $"[{friendlySourceName}] {loggerMessage}";
|
||||||
|
|
||||||
return string.IsNullOrWhiteSpace(Settings.LoggingTimeFormat)
|
return String.IsNullOrWhiteSpace(Settings.LoggingTimeFormat)
|
||||||
? $" {prefix} >> {outputMessage}"
|
? $" {prefix} >> {outputMessage}"
|
||||||
: $" {date.ToLocalTime().ToString(Settings.LoggingTimeFormat)} {prefix} >> {outputMessage}";
|
: $" {date.ToLocalTime().ToString(Settings.LoggingTimeFormat)} {prefix} >> {outputMessage}";
|
||||||
}
|
}
|
||||||
@ -235,15 +224,12 @@
|
|||||||
/// <param name="callerFilePath">The caller file path. This is automatically populated.</param>
|
/// <param name="callerFilePath">The caller file path. This is automatically populated.</param>
|
||||||
/// <param name="callerLineNumber">The caller line number. This is automatically populated.</param>
|
/// <param name="callerLineNumber">The caller line number. This is automatically populated.</param>
|
||||||
public static void Debug(
|
public static void Debug(
|
||||||
this string message,
|
this String message,
|
||||||
string source = null,
|
String source = null,
|
||||||
object extendedData = null,
|
Object extendedData = null,
|
||||||
[CallerMemberName] string callerMemberName = "",
|
[CallerMemberName] String callerMemberName = "",
|
||||||
[CallerFilePath] string callerFilePath = "",
|
[CallerFilePath] String callerFilePath = "",
|
||||||
[CallerLineNumber] int callerLineNumber = 0)
|
[CallerLineNumber] Int32 callerLineNumber = 0) => LogMessage(LogMessageType.Debug, message, source, extendedData, callerMemberName, callerFilePath, callerLineNumber);
|
||||||
{
|
|
||||||
LogMessage(LogMessageType.Debug, message, source, extendedData, callerMemberName, callerFilePath, callerLineNumber);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Logs a debug message to the console.
|
/// Logs a debug message to the console.
|
||||||
@ -255,15 +241,12 @@
|
|||||||
/// <param name="callerFilePath">The caller file path.</param>
|
/// <param name="callerFilePath">The caller file path.</param>
|
||||||
/// <param name="callerLineNumber">The caller line number.</param>
|
/// <param name="callerLineNumber">The caller line number.</param>
|
||||||
public static void Debug(
|
public static void Debug(
|
||||||
this string message,
|
this String message,
|
||||||
Type source,
|
Type source,
|
||||||
object extendedData = null,
|
Object extendedData = null,
|
||||||
[CallerMemberName] string callerMemberName = "",
|
[CallerMemberName] String callerMemberName = "",
|
||||||
[CallerFilePath] string callerFilePath = "",
|
[CallerFilePath] String callerFilePath = "",
|
||||||
[CallerLineNumber] int callerLineNumber = 0)
|
[CallerLineNumber] Int32 callerLineNumber = 0) => LogMessage(LogMessageType.Debug, message, source?.FullName, extendedData, callerMemberName, callerFilePath, callerLineNumber);
|
||||||
{
|
|
||||||
LogMessage(LogMessageType.Debug, message, source?.FullName, extendedData, callerMemberName, callerFilePath, callerLineNumber);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Logs a debug message to the console.
|
/// Logs a debug message to the console.
|
||||||
@ -276,14 +259,11 @@
|
|||||||
/// <param name="callerLineNumber">The caller line number. This is automatically populated.</param>
|
/// <param name="callerLineNumber">The caller line number. This is automatically populated.</param>
|
||||||
public static void Debug(
|
public static void Debug(
|
||||||
this Exception extendedData,
|
this Exception extendedData,
|
||||||
string source,
|
String source,
|
||||||
string message,
|
String message,
|
||||||
[CallerMemberName] string callerMemberName = "",
|
[CallerMemberName] String callerMemberName = "",
|
||||||
[CallerFilePath] string callerFilePath = "",
|
[CallerFilePath] String callerFilePath = "",
|
||||||
[CallerLineNumber] int callerLineNumber = 0)
|
[CallerLineNumber] Int32 callerLineNumber = 0) => LogMessage(LogMessageType.Debug, message, source, extendedData, callerMemberName, callerFilePath, callerLineNumber);
|
||||||
{
|
|
||||||
LogMessage(LogMessageType.Debug, message, source, extendedData, callerMemberName, callerFilePath, callerLineNumber);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
@ -299,15 +279,12 @@
|
|||||||
/// <param name="callerFilePath">The caller file path. This is automatically populated.</param>
|
/// <param name="callerFilePath">The caller file path. This is automatically populated.</param>
|
||||||
/// <param name="callerLineNumber">The caller line number. This is automatically populated.</param>
|
/// <param name="callerLineNumber">The caller line number. This is automatically populated.</param>
|
||||||
public static void Trace(
|
public static void Trace(
|
||||||
this string message,
|
this String message,
|
||||||
string source = null,
|
String source = null,
|
||||||
object extendedData = null,
|
Object extendedData = null,
|
||||||
[CallerMemberName] string callerMemberName = "",
|
[CallerMemberName] String callerMemberName = "",
|
||||||
[CallerFilePath] string callerFilePath = "",
|
[CallerFilePath] String callerFilePath = "",
|
||||||
[CallerLineNumber] int callerLineNumber = 0)
|
[CallerLineNumber] Int32 callerLineNumber = 0) => LogMessage(LogMessageType.Trace, message, source, extendedData, callerMemberName, callerFilePath, callerLineNumber);
|
||||||
{
|
|
||||||
LogMessage(LogMessageType.Trace, message, source, extendedData, callerMemberName, callerFilePath, callerLineNumber);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Logs a trace message to the console.
|
/// Logs a trace message to the console.
|
||||||
@ -319,15 +296,12 @@
|
|||||||
/// <param name="callerFilePath">The caller file path.</param>
|
/// <param name="callerFilePath">The caller file path.</param>
|
||||||
/// <param name="callerLineNumber">The caller line number.</param>
|
/// <param name="callerLineNumber">The caller line number.</param>
|
||||||
public static void Trace(
|
public static void Trace(
|
||||||
this string message,
|
this String message,
|
||||||
Type source,
|
Type source,
|
||||||
object extendedData = null,
|
Object extendedData = null,
|
||||||
[CallerMemberName] string callerMemberName = "",
|
[CallerMemberName] String callerMemberName = "",
|
||||||
[CallerFilePath] string callerFilePath = "",
|
[CallerFilePath] String callerFilePath = "",
|
||||||
[CallerLineNumber] int callerLineNumber = 0)
|
[CallerLineNumber] Int32 callerLineNumber = 0) => LogMessage(LogMessageType.Trace, message, source?.FullName, extendedData, callerMemberName, callerFilePath, callerLineNumber);
|
||||||
{
|
|
||||||
LogMessage(LogMessageType.Trace, message, source?.FullName, extendedData, callerMemberName, callerFilePath, callerLineNumber);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Logs a trace message to the console.
|
/// Logs a trace message to the console.
|
||||||
@ -340,14 +314,11 @@
|
|||||||
/// <param name="callerLineNumber">The caller line number. This is automatically populated.</param>
|
/// <param name="callerLineNumber">The caller line number. This is automatically populated.</param>
|
||||||
public static void Trace(
|
public static void Trace(
|
||||||
this Exception extendedData,
|
this Exception extendedData,
|
||||||
string source,
|
String source,
|
||||||
string message,
|
String message,
|
||||||
[CallerMemberName] string callerMemberName = "",
|
[CallerMemberName] String callerMemberName = "",
|
||||||
[CallerFilePath] string callerFilePath = "",
|
[CallerFilePath] String callerFilePath = "",
|
||||||
[CallerLineNumber] int callerLineNumber = 0)
|
[CallerLineNumber] Int32 callerLineNumber = 0) => LogMessage(LogMessageType.Trace, message, source, extendedData, callerMemberName, callerFilePath, callerLineNumber);
|
||||||
{
|
|
||||||
LogMessage(LogMessageType.Trace, message, source, extendedData, callerMemberName, callerFilePath, callerLineNumber);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
@ -363,15 +334,12 @@
|
|||||||
/// <param name="callerFilePath">The caller file path. This is automatically populated.</param>
|
/// <param name="callerFilePath">The caller file path. This is automatically populated.</param>
|
||||||
/// <param name="callerLineNumber">The caller line number. This is automatically populated.</param>
|
/// <param name="callerLineNumber">The caller line number. This is automatically populated.</param>
|
||||||
public static void Warn(
|
public static void Warn(
|
||||||
this string message,
|
this String message,
|
||||||
string source = null,
|
String source = null,
|
||||||
object extendedData = null,
|
Object extendedData = null,
|
||||||
[CallerMemberName] string callerMemberName = "",
|
[CallerMemberName] String callerMemberName = "",
|
||||||
[CallerFilePath] string callerFilePath = "",
|
[CallerFilePath] String callerFilePath = "",
|
||||||
[CallerLineNumber] int callerLineNumber = 0)
|
[CallerLineNumber] Int32 callerLineNumber = 0) => LogMessage(LogMessageType.Warning, message, source, extendedData, callerMemberName, callerFilePath, callerLineNumber);
|
||||||
{
|
|
||||||
LogMessage(LogMessageType.Warning, message, source, extendedData, callerMemberName, callerFilePath, callerLineNumber);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Logs a warning message to the console.
|
/// Logs a warning message to the console.
|
||||||
@ -383,15 +351,12 @@
|
|||||||
/// <param name="callerFilePath">The caller file path.</param>
|
/// <param name="callerFilePath">The caller file path.</param>
|
||||||
/// <param name="callerLineNumber">The caller line number.</param>
|
/// <param name="callerLineNumber">The caller line number.</param>
|
||||||
public static void Warn(
|
public static void Warn(
|
||||||
this string message,
|
this String message,
|
||||||
Type source,
|
Type source,
|
||||||
object extendedData = null,
|
Object extendedData = null,
|
||||||
[CallerMemberName] string callerMemberName = "",
|
[CallerMemberName] String callerMemberName = "",
|
||||||
[CallerFilePath] string callerFilePath = "",
|
[CallerFilePath] String callerFilePath = "",
|
||||||
[CallerLineNumber] int callerLineNumber = 0)
|
[CallerLineNumber] Int32 callerLineNumber = 0) => LogMessage(LogMessageType.Warning, message, source?.FullName, extendedData, callerMemberName, callerFilePath, callerLineNumber);
|
||||||
{
|
|
||||||
LogMessage(LogMessageType.Warning, message, source?.FullName, extendedData, callerMemberName, callerFilePath, callerLineNumber);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Logs a warning message to the console.
|
/// Logs a warning message to the console.
|
||||||
@ -404,14 +369,11 @@
|
|||||||
/// <param name="callerLineNumber">The caller line number. This is automatically populated.</param>
|
/// <param name="callerLineNumber">The caller line number. This is automatically populated.</param>
|
||||||
public static void Warn(
|
public static void Warn(
|
||||||
this Exception extendedData,
|
this Exception extendedData,
|
||||||
string source,
|
String source,
|
||||||
string message,
|
String message,
|
||||||
[CallerMemberName] string callerMemberName = "",
|
[CallerMemberName] String callerMemberName = "",
|
||||||
[CallerFilePath] string callerFilePath = "",
|
[CallerFilePath] String callerFilePath = "",
|
||||||
[CallerLineNumber] int callerLineNumber = 0)
|
[CallerLineNumber] Int32 callerLineNumber = 0) => LogMessage(LogMessageType.Warning, message, source, extendedData, callerMemberName, callerFilePath, callerLineNumber);
|
||||||
{
|
|
||||||
LogMessage(LogMessageType.Warning, message, source, extendedData, callerMemberName, callerFilePath, callerLineNumber);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
@ -427,15 +389,12 @@
|
|||||||
/// <param name="callerFilePath">The caller file path. This is automatically populated.</param>
|
/// <param name="callerFilePath">The caller file path. This is automatically populated.</param>
|
||||||
/// <param name="callerLineNumber">The caller line number. This is automatically populated.</param>
|
/// <param name="callerLineNumber">The caller line number. This is automatically populated.</param>
|
||||||
public static void Fatal(
|
public static void Fatal(
|
||||||
this string message,
|
this String message,
|
||||||
string source = null,
|
String source = null,
|
||||||
object extendedData = null,
|
Object extendedData = null,
|
||||||
[CallerMemberName] string callerMemberName = "",
|
[CallerMemberName] String callerMemberName = "",
|
||||||
[CallerFilePath] string callerFilePath = "",
|
[CallerFilePath] String callerFilePath = "",
|
||||||
[CallerLineNumber] int callerLineNumber = 0)
|
[CallerLineNumber] Int32 callerLineNumber = 0) => LogMessage(LogMessageType.Fatal, message, source, extendedData, callerMemberName, callerFilePath, callerLineNumber);
|
||||||
{
|
|
||||||
LogMessage(LogMessageType.Fatal, message, source, extendedData, callerMemberName, callerFilePath, callerLineNumber);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Logs a warning message to the console.
|
/// Logs a warning message to the console.
|
||||||
@ -447,15 +406,12 @@
|
|||||||
/// <param name="callerFilePath">The caller file path.</param>
|
/// <param name="callerFilePath">The caller file path.</param>
|
||||||
/// <param name="callerLineNumber">The caller line number.</param>
|
/// <param name="callerLineNumber">The caller line number.</param>
|
||||||
public static void Fatal(
|
public static void Fatal(
|
||||||
this string message,
|
this String message,
|
||||||
Type source,
|
Type source,
|
||||||
object extendedData = null,
|
Object extendedData = null,
|
||||||
[CallerMemberName] string callerMemberName = "",
|
[CallerMemberName] String callerMemberName = "",
|
||||||
[CallerFilePath] string callerFilePath = "",
|
[CallerFilePath] String callerFilePath = "",
|
||||||
[CallerLineNumber] int callerLineNumber = 0)
|
[CallerLineNumber] Int32 callerLineNumber = 0) => LogMessage(LogMessageType.Fatal, message, source?.FullName, extendedData, callerMemberName, callerFilePath, callerLineNumber);
|
||||||
{
|
|
||||||
LogMessage(LogMessageType.Fatal, message, source?.FullName, extendedData, callerMemberName, callerFilePath, callerLineNumber);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Logs a warning message to the console.
|
/// Logs a warning message to the console.
|
||||||
@ -468,14 +424,11 @@
|
|||||||
/// <param name="callerLineNumber">The caller line number. This is automatically populated.</param>
|
/// <param name="callerLineNumber">The caller line number. This is automatically populated.</param>
|
||||||
public static void Fatal(
|
public static void Fatal(
|
||||||
this Exception extendedData,
|
this Exception extendedData,
|
||||||
string source,
|
String source,
|
||||||
string message,
|
String message,
|
||||||
[CallerMemberName] string callerMemberName = "",
|
[CallerMemberName] String callerMemberName = "",
|
||||||
[CallerFilePath] string callerFilePath = "",
|
[CallerFilePath] String callerFilePath = "",
|
||||||
[CallerLineNumber] int callerLineNumber = 0)
|
[CallerLineNumber] Int32 callerLineNumber = 0) => LogMessage(LogMessageType.Fatal, message, source, extendedData, callerMemberName, callerFilePath, callerLineNumber);
|
||||||
{
|
|
||||||
LogMessage(LogMessageType.Fatal, message, source, extendedData, callerMemberName, callerFilePath, callerLineNumber);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
@ -491,15 +444,12 @@
|
|||||||
/// <param name="callerFilePath">The caller file path. This is automatically populated.</param>
|
/// <param name="callerFilePath">The caller file path. This is automatically populated.</param>
|
||||||
/// <param name="callerLineNumber">The caller line number. This is automatically populated.</param>
|
/// <param name="callerLineNumber">The caller line number. This is automatically populated.</param>
|
||||||
public static void Info(
|
public static void Info(
|
||||||
this string message,
|
this String message,
|
||||||
string source = null,
|
String source = null,
|
||||||
object extendedData = null,
|
Object extendedData = null,
|
||||||
[CallerMemberName] string callerMemberName = "",
|
[CallerMemberName] String callerMemberName = "",
|
||||||
[CallerFilePath] string callerFilePath = "",
|
[CallerFilePath] String callerFilePath = "",
|
||||||
[CallerLineNumber] int callerLineNumber = 0)
|
[CallerLineNumber] Int32 callerLineNumber = 0) => LogMessage(LogMessageType.Info, message, source, extendedData, callerMemberName, callerFilePath, callerLineNumber);
|
||||||
{
|
|
||||||
LogMessage(LogMessageType.Info, message, source, extendedData, callerMemberName, callerFilePath, callerLineNumber);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Logs an info message to the console.
|
/// Logs an info message to the console.
|
||||||
@ -511,15 +461,12 @@
|
|||||||
/// <param name="callerFilePath">The caller file path.</param>
|
/// <param name="callerFilePath">The caller file path.</param>
|
||||||
/// <param name="callerLineNumber">The caller line number.</param>
|
/// <param name="callerLineNumber">The caller line number.</param>
|
||||||
public static void Info(
|
public static void Info(
|
||||||
this string message,
|
this String message,
|
||||||
Type source,
|
Type source,
|
||||||
object extendedData = null,
|
Object extendedData = null,
|
||||||
[CallerMemberName] string callerMemberName = "",
|
[CallerMemberName] String callerMemberName = "",
|
||||||
[CallerFilePath] string callerFilePath = "",
|
[CallerFilePath] String callerFilePath = "",
|
||||||
[CallerLineNumber] int callerLineNumber = 0)
|
[CallerLineNumber] Int32 callerLineNumber = 0) => LogMessage(LogMessageType.Info, message, source?.FullName, extendedData, callerMemberName, callerFilePath, callerLineNumber);
|
||||||
{
|
|
||||||
LogMessage(LogMessageType.Info, message, source?.FullName, extendedData, callerMemberName, callerFilePath, callerLineNumber);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Logs an info message to the console.
|
/// Logs an info message to the console.
|
||||||
@ -532,14 +479,11 @@
|
|||||||
/// <param name="callerLineNumber">The caller line number. This is automatically populated.</param>
|
/// <param name="callerLineNumber">The caller line number. This is automatically populated.</param>
|
||||||
public static void Info(
|
public static void Info(
|
||||||
this Exception extendedData,
|
this Exception extendedData,
|
||||||
string source,
|
String source,
|
||||||
string message,
|
String message,
|
||||||
[CallerMemberName] string callerMemberName = "",
|
[CallerMemberName] String callerMemberName = "",
|
||||||
[CallerFilePath] string callerFilePath = "",
|
[CallerFilePath] String callerFilePath = "",
|
||||||
[CallerLineNumber] int callerLineNumber = 0)
|
[CallerLineNumber] Int32 callerLineNumber = 0) => LogMessage(LogMessageType.Info, message, source, extendedData, callerMemberName, callerFilePath, callerLineNumber);
|
||||||
{
|
|
||||||
LogMessage(LogMessageType.Info, message, source, extendedData, callerMemberName, callerFilePath, callerLineNumber);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
@ -555,15 +499,12 @@
|
|||||||
/// <param name="callerFilePath">The caller file path. This is automatically populated.</param>
|
/// <param name="callerFilePath">The caller file path. This is automatically populated.</param>
|
||||||
/// <param name="callerLineNumber">The caller line number. This is automatically populated.</param>
|
/// <param name="callerLineNumber">The caller line number. This is automatically populated.</param>
|
||||||
public static void Error(
|
public static void Error(
|
||||||
this string message,
|
this String message,
|
||||||
string source = null,
|
String source = null,
|
||||||
object extendedData = null,
|
Object extendedData = null,
|
||||||
[CallerMemberName] string callerMemberName = "",
|
[CallerMemberName] String callerMemberName = "",
|
||||||
[CallerFilePath] string callerFilePath = "",
|
[CallerFilePath] String callerFilePath = "",
|
||||||
[CallerLineNumber] int callerLineNumber = 0)
|
[CallerLineNumber] Int32 callerLineNumber = 0) => LogMessage(LogMessageType.Error, message, source, extendedData, callerMemberName, callerFilePath, callerLineNumber);
|
||||||
{
|
|
||||||
LogMessage(LogMessageType.Error, message, source, extendedData, callerMemberName, callerFilePath, callerLineNumber);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Logs an error message to the console's standard error.
|
/// Logs an error message to the console's standard error.
|
||||||
@ -575,15 +516,12 @@
|
|||||||
/// <param name="callerFilePath">The caller file path.</param>
|
/// <param name="callerFilePath">The caller file path.</param>
|
||||||
/// <param name="callerLineNumber">The caller line number.</param>
|
/// <param name="callerLineNumber">The caller line number.</param>
|
||||||
public static void Error(
|
public static void Error(
|
||||||
this string message,
|
this String message,
|
||||||
Type source,
|
Type source,
|
||||||
object extendedData = null,
|
Object extendedData = null,
|
||||||
[CallerMemberName] string callerMemberName = "",
|
[CallerMemberName] String callerMemberName = "",
|
||||||
[CallerFilePath] string callerFilePath = "",
|
[CallerFilePath] String callerFilePath = "",
|
||||||
[CallerLineNumber] int callerLineNumber = 0)
|
[CallerLineNumber] Int32 callerLineNumber = 0) => LogMessage(LogMessageType.Error, message, source?.FullName, extendedData, callerMemberName, callerFilePath, callerLineNumber);
|
||||||
{
|
|
||||||
LogMessage(LogMessageType.Error, message, source?.FullName, extendedData, callerMemberName, callerFilePath, callerLineNumber);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Logs an error message to the console's standard error.
|
/// Logs an error message to the console's standard error.
|
||||||
@ -596,14 +534,11 @@
|
|||||||
/// <param name="callerLineNumber">The caller line number. This is automatically populated.</param>
|
/// <param name="callerLineNumber">The caller line number. This is automatically populated.</param>
|
||||||
public static void Error(
|
public static void Error(
|
||||||
this Exception ex,
|
this Exception ex,
|
||||||
string source,
|
String source,
|
||||||
string message,
|
String message,
|
||||||
[CallerMemberName] string callerMemberName = "",
|
[CallerMemberName] String callerMemberName = "",
|
||||||
[CallerFilePath] string callerFilePath = "",
|
[CallerFilePath] String callerFilePath = "",
|
||||||
[CallerLineNumber] int callerLineNumber = 0)
|
[CallerLineNumber] Int32 callerLineNumber = 0) => LogMessage(LogMessageType.Error, message, source, ex, callerMemberName, callerFilePath, callerLineNumber);
|
||||||
{
|
|
||||||
LogMessage(LogMessageType.Error, message, source, ex, callerMemberName, callerFilePath, callerLineNumber);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
@ -622,16 +557,13 @@
|
|||||||
/// <param name="callerFilePath">The caller file path. This is automatically populated.</param>
|
/// <param name="callerFilePath">The caller file path. This is automatically populated.</param>
|
||||||
/// <param name="callerLineNumber">The caller line number. This is automatically populated.</param>
|
/// <param name="callerLineNumber">The caller line number. This is automatically populated.</param>
|
||||||
public static void Log(
|
public static void Log(
|
||||||
this string message,
|
this String message,
|
||||||
string source,
|
String source,
|
||||||
LogMessageType messageType,
|
LogMessageType messageType,
|
||||||
object extendedData = null,
|
Object extendedData = null,
|
||||||
[CallerMemberName] string callerMemberName = "",
|
[CallerMemberName] String callerMemberName = "",
|
||||||
[CallerFilePath] string callerFilePath = "",
|
[CallerFilePath] String callerFilePath = "",
|
||||||
[CallerLineNumber] int callerLineNumber = 0)
|
[CallerLineNumber] Int32 callerLineNumber = 0) => LogMessage(messageType, message, source, extendedData, callerMemberName, callerFilePath, callerLineNumber);
|
||||||
{
|
|
||||||
LogMessage(messageType, message, source, extendedData, callerMemberName, callerFilePath, callerLineNumber);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Logs the specified message.
|
/// Logs the specified message.
|
||||||
@ -644,16 +576,13 @@
|
|||||||
/// <param name="callerFilePath">The caller file path.</param>
|
/// <param name="callerFilePath">The caller file path.</param>
|
||||||
/// <param name="callerLineNumber">The caller line number.</param>
|
/// <param name="callerLineNumber">The caller line number.</param>
|
||||||
public static void Log(
|
public static void Log(
|
||||||
this string message,
|
this String message,
|
||||||
Type source,
|
Type source,
|
||||||
LogMessageType messageType,
|
LogMessageType messageType,
|
||||||
object extendedData = null,
|
Object extendedData = null,
|
||||||
[CallerMemberName] string callerMemberName = "",
|
[CallerMemberName] String callerMemberName = "",
|
||||||
[CallerFilePath] string callerFilePath = "",
|
[CallerFilePath] String callerFilePath = "",
|
||||||
[CallerLineNumber] int callerLineNumber = 0)
|
[CallerLineNumber] Int32 callerLineNumber = 0) => LogMessage(messageType, message, source?.FullName, extendedData, callerMemberName, callerFilePath, callerLineNumber);
|
||||||
{
|
|
||||||
LogMessage(messageType, message, source?.FullName, extendedData, callerMemberName, callerFilePath, callerLineNumber);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Logs an error message to the console's standard error.
|
/// Logs an error message to the console's standard error.
|
||||||
@ -666,14 +595,11 @@
|
|||||||
/// <param name="callerLineNumber">The caller line number. This is automatically populated.</param>
|
/// <param name="callerLineNumber">The caller line number. This is automatically populated.</param>
|
||||||
public static void Log(
|
public static void Log(
|
||||||
this Exception ex,
|
this Exception ex,
|
||||||
string source = null,
|
String source = null,
|
||||||
string message = null,
|
String message = null,
|
||||||
[CallerMemberName] string callerMemberName = "",
|
[CallerMemberName] String callerMemberName = "",
|
||||||
[CallerFilePath] string callerFilePath = "",
|
[CallerFilePath] String callerFilePath = "",
|
||||||
[CallerLineNumber] int callerLineNumber = 0)
|
[CallerLineNumber] Int32 callerLineNumber = 0) => LogMessage(LogMessageType.Error, message ?? ex.Message, source ?? ex.Source, ex, callerMemberName, callerFilePath, callerLineNumber);
|
||||||
{
|
|
||||||
LogMessage(LogMessageType.Error, message ?? ex.Message, source ?? ex.Source, ex, callerMemberName, callerFilePath, callerLineNumber);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Logs an error message to the console's standard error.
|
/// Logs an error message to the console's standard error.
|
||||||
@ -687,13 +613,10 @@
|
|||||||
public static void Log(
|
public static void Log(
|
||||||
this Exception ex,
|
this Exception ex,
|
||||||
Type source = null,
|
Type source = null,
|
||||||
string message = null,
|
String message = null,
|
||||||
[CallerMemberName] string callerMemberName = "",
|
[CallerMemberName] String callerMemberName = "",
|
||||||
[CallerFilePath] string callerFilePath = "",
|
[CallerFilePath] String callerFilePath = "",
|
||||||
[CallerLineNumber] int callerLineNumber = 0)
|
[CallerLineNumber] Int32 callerLineNumber = 0) => LogMessage(LogMessageType.Error, message ?? ex.Message, source?.FullName ?? ex.Source, ex, callerMemberName, callerFilePath, callerLineNumber);
|
||||||
{
|
|
||||||
LogMessage(LogMessageType.Error, message ?? ex.Message, source?.FullName ?? ex.Source, ex, callerMemberName, callerFilePath, callerLineNumber);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Logs a trace message showing all possible non-null properties of the given object
|
/// Logs a trace message showing all possible non-null properties of the given object
|
||||||
@ -706,15 +629,17 @@
|
|||||||
/// <param name="callerFilePath">The caller file path. This is automatically populated.</param>
|
/// <param name="callerFilePath">The caller file path. This is automatically populated.</param>
|
||||||
/// <param name="callerLineNumber">The caller line number. This is automatically populated.</param>
|
/// <param name="callerLineNumber">The caller line number. This is automatically populated.</param>
|
||||||
public static void Dump(
|
public static void Dump(
|
||||||
this object obj,
|
this Object obj,
|
||||||
string source,
|
String source,
|
||||||
string text = "Object Dump",
|
String text = "Object Dump",
|
||||||
[CallerMemberName] string callerMemberName = "",
|
[CallerMemberName] String callerMemberName = "",
|
||||||
[CallerFilePath] string callerFilePath = "",
|
[CallerFilePath] String callerFilePath = "",
|
||||||
[CallerLineNumber] int callerLineNumber = 0)
|
[CallerLineNumber] Int32 callerLineNumber = 0) {
|
||||||
{
|
if(obj == null) {
|
||||||
if (obj == null) return;
|
return;
|
||||||
var message = $"{text} ({obj.GetType()}): {Environment.NewLine}{obj.Stringify().Indent(5)}";
|
}
|
||||||
|
|
||||||
|
String message = $"{text} ({obj.GetType()}): {Environment.NewLine}{obj.Stringify().Indent(5)}";
|
||||||
LogMessage(LogMessageType.Trace, message, source, obj, callerMemberName, callerFilePath, callerLineNumber);
|
LogMessage(LogMessageType.Trace, message, source, obj, callerMemberName, callerFilePath, callerLineNumber);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -729,15 +654,17 @@
|
|||||||
/// <param name="callerFilePath">The caller file path.</param>
|
/// <param name="callerFilePath">The caller file path.</param>
|
||||||
/// <param name="callerLineNumber">The caller line number.</param>
|
/// <param name="callerLineNumber">The caller line number.</param>
|
||||||
public static void Dump(
|
public static void Dump(
|
||||||
this object obj,
|
this Object obj,
|
||||||
Type source,
|
Type source,
|
||||||
string text = "Object Dump",
|
String text = "Object Dump",
|
||||||
[CallerMemberName] string callerMemberName = "",
|
[CallerMemberName] String callerMemberName = "",
|
||||||
[CallerFilePath] string callerFilePath = "",
|
[CallerFilePath] String callerFilePath = "",
|
||||||
[CallerLineNumber] int callerLineNumber = 0)
|
[CallerLineNumber] Int32 callerLineNumber = 0) {
|
||||||
{
|
if(obj == null) {
|
||||||
if (obj == null) return;
|
return;
|
||||||
var message = $"{text} ({obj.GetType()}): {Environment.NewLine}{obj.Stringify().Indent(5)}";
|
}
|
||||||
|
|
||||||
|
String message = $"{text} ({obj.GetType()}): {Environment.NewLine}{obj.Stringify().Indent(5)}";
|
||||||
LogMessage(LogMessageType.Trace, message, source?.FullName, obj, callerMemberName, callerFilePath, callerLineNumber);
|
LogMessage(LogMessageType.Trace, message, source?.FullName, obj, callerMemberName, callerFilePath, callerLineNumber);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,33 +1,29 @@
|
|||||||
namespace Unosquare.Swan
|
using System;
|
||||||
{
|
using System.Linq;
|
||||||
using System;
|
|
||||||
using System.Linq;
|
|
||||||
|
|
||||||
|
namespace Unosquare.Swan {
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A console terminal helper to create nicer output and receive input from the user
|
/// A console terminal helper to create nicer output and receive input from the user
|
||||||
/// This class is thread-safe :).
|
/// This class is thread-safe :).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static partial class Terminal
|
public static partial class Terminal {
|
||||||
{
|
|
||||||
#region Helper Methods
|
#region Helper Methods
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Prints all characters in the current code page.
|
/// Prints all characters in the current code page.
|
||||||
/// This is provided for debugging purposes only.
|
/// This is provided for debugging purposes only.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static void PrintCurrentCodePage()
|
public static void PrintCurrentCodePage() {
|
||||||
{
|
if(!IsConsolePresent) {
|
||||||
if (!IsConsolePresent) return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
lock (SyncLock)
|
lock(SyncLock) {
|
||||||
{
|
|
||||||
$"Output Encoding: {OutputEncoding}".WriteLine();
|
$"Output Encoding: {OutputEncoding}".WriteLine();
|
||||||
for (byte byteValue = 0; byteValue < byte.MaxValue; byteValue++)
|
for(Byte byteValue = 0; byteValue < Byte.MaxValue; byteValue++) {
|
||||||
{
|
Char charValue = OutputEncoding.GetChars(new[] { byteValue })[0];
|
||||||
var charValue = OutputEncoding.GetChars(new[] { byteValue })[0];
|
|
||||||
|
|
||||||
switch (byteValue)
|
switch(byteValue) {
|
||||||
{
|
|
||||||
case 8: // Backspace
|
case 8: // Backspace
|
||||||
case 9: // Tab
|
case 9: // Tab
|
||||||
case 10: // Line feed
|
case 10: // Line feed
|
||||||
@ -39,11 +35,14 @@
|
|||||||
$"{byteValue:000} {charValue} ".Write();
|
$"{byteValue:000} {charValue} ".Write();
|
||||||
|
|
||||||
// 7 is a beep -- Console.Beep() also works
|
// 7 is a beep -- Console.Beep() also works
|
||||||
if (byteValue == 7) " ".Write();
|
if(byteValue == 7) {
|
||||||
|
" ".Write();
|
||||||
|
}
|
||||||
|
|
||||||
if ((byteValue + 1) % 8 == 0)
|
if((byteValue + 1) % 8 == 0) {
|
||||||
WriteLine();
|
WriteLine();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
WriteLine();
|
WriteLine();
|
||||||
}
|
}
|
||||||
@ -61,25 +60,20 @@
|
|||||||
/// <param name="count">The count.</param>
|
/// <param name="count">The count.</param>
|
||||||
/// <param name="newLine">if set to <c>true</c> [new line].</param>
|
/// <param name="newLine">if set to <c>true</c> [new line].</param>
|
||||||
/// <param name="writerFlags">The writer flags.</param>
|
/// <param name="writerFlags">The writer flags.</param>
|
||||||
public static void Write(this byte charCode, ConsoleColor? color = null, int count = 1, bool newLine = false, TerminalWriters writerFlags = TerminalWriters.StandardOutput)
|
public static void Write(this Byte charCode, ConsoleColor? color = null, Int32 count = 1, Boolean newLine = false, TerminalWriters writerFlags = TerminalWriters.StandardOutput) {
|
||||||
{
|
lock(SyncLock) {
|
||||||
lock (SyncLock)
|
Byte[] bytes = new Byte[count];
|
||||||
{
|
for(Int32 i = 0; i < bytes.Length; i++) {
|
||||||
var bytes = new byte[count];
|
|
||||||
for (var i = 0; i < bytes.Length; i++)
|
|
||||||
{
|
|
||||||
bytes[i] = charCode;
|
bytes[i] = charCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (newLine)
|
if(newLine) {
|
||||||
{
|
Byte[] newLineBytes = OutputEncoding.GetBytes(Environment.NewLine);
|
||||||
var newLineBytes = OutputEncoding.GetBytes(Environment.NewLine);
|
|
||||||
bytes = bytes.Union(newLineBytes).ToArray();
|
bytes = bytes.Union(newLineBytes).ToArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
var buffer = OutputEncoding.GetChars(bytes);
|
Char[] buffer = OutputEncoding.GetChars(bytes);
|
||||||
var context = new OutputContext
|
OutputContext context = new OutputContext {
|
||||||
{
|
|
||||||
OutputColor = color ?? Settings.DefaultColor,
|
OutputColor = color ?? Settings.DefaultColor,
|
||||||
OutputText = buffer,
|
OutputText = buffer,
|
||||||
OutputWriters = writerFlags,
|
OutputWriters = writerFlags,
|
||||||
@ -95,12 +89,9 @@
|
|||||||
/// <param name="charCode">The character code.</param>
|
/// <param name="charCode">The character code.</param>
|
||||||
/// <param name="color">The color.</param>
|
/// <param name="color">The color.</param>
|
||||||
/// <param name="writerFlags">The writer flags.</param>
|
/// <param name="writerFlags">The writer flags.</param>
|
||||||
public static void Write(this char charCode, ConsoleColor? color = null, TerminalWriters writerFlags = TerminalWriters.StandardOutput)
|
public static void Write(this Char charCode, ConsoleColor? color = null, TerminalWriters writerFlags = TerminalWriters.StandardOutput) {
|
||||||
{
|
lock(SyncLock) {
|
||||||
lock (SyncLock)
|
OutputContext context = new OutputContext {
|
||||||
{
|
|
||||||
var context = new OutputContext
|
|
||||||
{
|
|
||||||
OutputColor = color ?? Settings.DefaultColor,
|
OutputColor = color ?? Settings.DefaultColor,
|
||||||
OutputText = new[] { charCode },
|
OutputText = new[] { charCode },
|
||||||
OutputWriters = writerFlags,
|
OutputWriters = writerFlags,
|
||||||
@ -116,15 +107,14 @@
|
|||||||
/// <param name="text">The text.</param>
|
/// <param name="text">The text.</param>
|
||||||
/// <param name="color">The color.</param>
|
/// <param name="color">The color.</param>
|
||||||
/// <param name="writerFlags">The writer flags.</param>
|
/// <param name="writerFlags">The writer flags.</param>
|
||||||
public static void Write(this string text, ConsoleColor? color = null, TerminalWriters writerFlags = TerminalWriters.StandardOutput)
|
public static void Write(this String text, ConsoleColor? color = null, TerminalWriters writerFlags = TerminalWriters.StandardOutput) {
|
||||||
{
|
if(text == null) {
|
||||||
if (text == null) return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
lock (SyncLock)
|
lock(SyncLock) {
|
||||||
{
|
Byte[] buffer = OutputEncoding.GetBytes(text);
|
||||||
var buffer = OutputEncoding.GetBytes(text);
|
OutputContext context = new OutputContext {
|
||||||
var context = new OutputContext
|
|
||||||
{
|
|
||||||
OutputColor = color ?? Settings.DefaultColor,
|
OutputColor = color ?? Settings.DefaultColor,
|
||||||
OutputText = OutputEncoding.GetChars(buffer),
|
OutputText = OutputEncoding.GetChars(buffer),
|
||||||
OutputWriters = writerFlags,
|
OutputWriters = writerFlags,
|
||||||
@ -152,8 +142,8 @@
|
|||||||
/// <param name="text">The text.</param>
|
/// <param name="text">The text.</param>
|
||||||
/// <param name="color">The color.</param>
|
/// <param name="color">The color.</param>
|
||||||
/// <param name="writerFlags">The writer flags.</param>
|
/// <param name="writerFlags">The writer flags.</param>
|
||||||
public static void WriteLine(this string text, ConsoleColor? color = null, TerminalWriters writerFlags = TerminalWriters.StandardOutput)
|
public static void WriteLine(this String text, ConsoleColor? color = null, TerminalWriters writerFlags = TerminalWriters.StandardOutput)
|
||||||
=> Write($"{text ?? string.Empty}{Environment.NewLine}", color, writerFlags);
|
=> Write($"{text ?? String.Empty}{Environment.NewLine}", color, writerFlags);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// As opposed to WriteLine methods, it prepends a Carriage Return character to the text
|
/// As opposed to WriteLine methods, it prepends a Carriage Return character to the text
|
||||||
@ -162,9 +152,8 @@
|
|||||||
/// <param name="text">The text.</param>
|
/// <param name="text">The text.</param>
|
||||||
/// <param name="color">The color.</param>
|
/// <param name="color">The color.</param>
|
||||||
/// <param name="writerFlags">The writer flags.</param>
|
/// <param name="writerFlags">The writer flags.</param>
|
||||||
public static void OverwriteLine(this string text, ConsoleColor? color = null, TerminalWriters writerFlags = TerminalWriters.StandardOutput)
|
public static void OverwriteLine(this String text, ConsoleColor? color = null, TerminalWriters writerFlags = TerminalWriters.StandardOutput) {
|
||||||
{
|
Write($"\r{text ?? String.Empty}", color, writerFlags);
|
||||||
Write($"\r{text ?? string.Empty}", color, writerFlags);
|
|
||||||
Flush();
|
Flush();
|
||||||
CursorLeft = 0;
|
CursorLeft = 0;
|
||||||
}
|
}
|
||||||
|
@ -1,38 +1,27 @@
|
|||||||
namespace Unosquare.Swan
|
using System;
|
||||||
{
|
|
||||||
using System;
|
|
||||||
|
|
||||||
|
namespace Unosquare.Swan {
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A console terminal helper to create nicer output and receive input from the user
|
/// A console terminal helper to create nicer output and receive input from the user
|
||||||
/// This class is thread-safe :).
|
/// This class is thread-safe :).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static partial class Terminal
|
public static partial class Terminal {
|
||||||
{
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Terminal global settings.
|
/// Terminal global settings.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static class Settings
|
public static class Settings {
|
||||||
{
|
static Settings() {
|
||||||
static Settings()
|
DisplayLoggingMessageType = IsDebuggerAttached
|
||||||
{
|
? LogMessageType.Debug |
|
||||||
if (IsDebuggerAttached)
|
|
||||||
{
|
|
||||||
DisplayLoggingMessageType =
|
|
||||||
LogMessageType.Debug |
|
|
||||||
LogMessageType.Error |
|
LogMessageType.Error |
|
||||||
LogMessageType.Info |
|
LogMessageType.Info |
|
||||||
LogMessageType.Trace |
|
LogMessageType.Trace |
|
||||||
LogMessageType.Warning |
|
LogMessageType.Warning |
|
||||||
LogMessageType.Fatal;
|
LogMessageType.Fatal
|
||||||
}
|
: LogMessageType.Error |
|
||||||
else
|
|
||||||
{
|
|
||||||
DisplayLoggingMessageType =
|
|
||||||
LogMessageType.Error |
|
|
||||||
LogMessageType.Info |
|
LogMessageType.Info |
|
||||||
LogMessageType.Warning |
|
LogMessageType.Warning |
|
||||||
LogMessageType.Fatal;
|
LogMessageType.Fatal;
|
||||||
}
|
|
||||||
|
|
||||||
GlobalLoggingMessageType = DisplayLoggingMessageType;
|
GlobalLoggingMessageType = DisplayLoggingMessageType;
|
||||||
}
|
}
|
||||||
@ -99,7 +88,7 @@
|
|||||||
/// <value>
|
/// <value>
|
||||||
/// The information prefix.
|
/// The information prefix.
|
||||||
/// </value>
|
/// </value>
|
||||||
public static string InfoPrefix { get; set; } = "INF";
|
public static String InfoPrefix { get; set; } = "INF";
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the user input prefix.
|
/// Gets or sets the user input prefix.
|
||||||
@ -107,7 +96,7 @@
|
|||||||
/// <value>
|
/// <value>
|
||||||
/// The user input prefix.
|
/// The user input prefix.
|
||||||
/// </value>
|
/// </value>
|
||||||
public static string UserInputPrefix { get; set; } = "USR";
|
public static String UserInputPrefix { get; set; } = "USR";
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the user option text.
|
/// Gets or sets the user option text.
|
||||||
@ -115,7 +104,7 @@
|
|||||||
/// <value>
|
/// <value>
|
||||||
/// The user option text.
|
/// The user option text.
|
||||||
/// </value>
|
/// </value>
|
||||||
public static string UserOptionText { get; set; } = " Option: ";
|
public static String UserOptionText { get; set; } = " Option: ";
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the debug logging prefix.
|
/// Gets or sets the debug logging prefix.
|
||||||
@ -123,7 +112,7 @@
|
|||||||
/// <value>
|
/// <value>
|
||||||
/// The debug prefix.
|
/// The debug prefix.
|
||||||
/// </value>
|
/// </value>
|
||||||
public static string DebugPrefix { get; set; } = "DBG";
|
public static String DebugPrefix { get; set; } = "DBG";
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the trace logging prefix.
|
/// Gets or sets the trace logging prefix.
|
||||||
@ -131,7 +120,7 @@
|
|||||||
/// <value>
|
/// <value>
|
||||||
/// The trace prefix.
|
/// The trace prefix.
|
||||||
/// </value>
|
/// </value>
|
||||||
public static string TracePrefix { get; set; } = "TRC";
|
public static String TracePrefix { get; set; } = "TRC";
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the warning logging prefix.
|
/// Gets or sets the warning logging prefix.
|
||||||
@ -139,7 +128,7 @@
|
|||||||
/// <value>
|
/// <value>
|
||||||
/// The warn prefix.
|
/// The warn prefix.
|
||||||
/// </value>
|
/// </value>
|
||||||
public static string WarnPrefix { get; set; } = "WRN";
|
public static String WarnPrefix { get; set; } = "WRN";
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the fatal logging prefix.
|
/// Gets or sets the fatal logging prefix.
|
||||||
@ -147,7 +136,7 @@
|
|||||||
/// <value>
|
/// <value>
|
||||||
/// The fatal prefix.
|
/// The fatal prefix.
|
||||||
/// </value>
|
/// </value>
|
||||||
public static string FatalPrefix { get; set; } = "FAT";
|
public static String FatalPrefix { get; set; } = "FAT";
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the error logging prefix.
|
/// Gets or sets the error logging prefix.
|
||||||
@ -155,7 +144,7 @@
|
|||||||
/// <value>
|
/// <value>
|
||||||
/// The error prefix.
|
/// The error prefix.
|
||||||
/// </value>
|
/// </value>
|
||||||
public static string ErrorPrefix { get; set; } = "ERR";
|
public static String ErrorPrefix { get; set; } = "ERR";
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the logging time format.
|
/// Gets or sets the logging time format.
|
||||||
@ -164,7 +153,7 @@
|
|||||||
/// <value>
|
/// <value>
|
||||||
/// The logging time format.
|
/// The logging time format.
|
||||||
/// </value>
|
/// </value>
|
||||||
public static string LoggingTimeFormat { get; set; } = "HH:mm:ss.fff";
|
public static String LoggingTimeFormat { get; set; } = "HH:mm:ss.fff";
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the logging message types (in a bitwise mask)
|
/// Gets or sets the logging message types (in a bitwise mask)
|
||||||
@ -173,7 +162,9 @@
|
|||||||
/// <value>
|
/// <value>
|
||||||
/// The console options.
|
/// The console options.
|
||||||
/// </value>
|
/// </value>
|
||||||
public static LogMessageType DisplayLoggingMessageType { get; set; }
|
public static LogMessageType DisplayLoggingMessageType {
|
||||||
|
get; set;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the logging message types (in a bitwise mask) to global logging.
|
/// Gets or sets the logging message types (in a bitwise mask) to global logging.
|
||||||
@ -181,7 +172,9 @@
|
|||||||
/// <value>
|
/// <value>
|
||||||
/// The type of the global logging message.
|
/// The type of the global logging message.
|
||||||
/// </value>
|
/// </value>
|
||||||
public static LogMessageType GlobalLoggingMessageType { get; set; }
|
public static LogMessageType GlobalLoggingMessageType {
|
||||||
|
get; set;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets a value indicating whether [override is console present].
|
/// Gets or sets a value indicating whether [override is console present].
|
||||||
@ -189,7 +182,9 @@
|
|||||||
/// <value>
|
/// <value>
|
||||||
/// <c>true</c> if [override is console present]; otherwise, <c>false</c>.
|
/// <c>true</c> if [override is console present]; otherwise, <c>false</c>.
|
||||||
/// </value>
|
/// </value>
|
||||||
public static bool OverrideIsConsolePresent { get; set; }
|
public static Boolean OverrideIsConsolePresent {
|
||||||
|
get; set;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,28 +1,26 @@
|
|||||||
namespace Unosquare.Swan
|
using Unosquare.Swan.Abstractions;
|
||||||
{
|
using System;
|
||||||
using Abstractions;
|
using System.Collections.Concurrent;
|
||||||
using System;
|
using System.Text;
|
||||||
using System.Collections.Concurrent;
|
using System.Threading;
|
||||||
using System.Text;
|
|
||||||
using System.Threading;
|
|
||||||
|
|
||||||
|
namespace Unosquare.Swan {
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A console terminal helper to create nicer output and receive input from the user.
|
/// A console terminal helper to create nicer output and receive input from the user.
|
||||||
/// This class is thread-safe :).
|
/// This class is thread-safe :).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static partial class Terminal
|
public static partial class Terminal {
|
||||||
{
|
|
||||||
#region Private Declarations
|
#region Private Declarations
|
||||||
|
|
||||||
private const int OutputFlushInterval = 15;
|
private const Int32 OutputFlushInterval = 15;
|
||||||
private static readonly ExclusiveTimer DequeueOutputTimer;
|
private static readonly ExclusiveTimer DequeueOutputTimer;
|
||||||
private static readonly object SyncLock = new object();
|
private static readonly Object SyncLock = new Object();
|
||||||
private static readonly ConcurrentQueue<OutputContext> OutputQueue = new ConcurrentQueue<OutputContext>();
|
private static readonly ConcurrentQueue<OutputContext> OutputQueue = new ConcurrentQueue<OutputContext>();
|
||||||
|
|
||||||
private static readonly ManualResetEventSlim OutputDone = new ManualResetEventSlim(false);
|
private static readonly ManualResetEventSlim OutputDone = new ManualResetEventSlim(false);
|
||||||
private static readonly ManualResetEventSlim InputDone = new ManualResetEventSlim(true);
|
private static readonly ManualResetEventSlim InputDone = new ManualResetEventSlim(true);
|
||||||
|
|
||||||
private static bool? _isConsolePresent;
|
private static Boolean? _isConsolePresent;
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
@ -31,14 +29,13 @@ namespace Unosquare.Swan
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes static members of the <see cref="Terminal"/> class.
|
/// Initializes static members of the <see cref="Terminal"/> class.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
static Terminal()
|
static Terminal() {
|
||||||
{
|
lock(SyncLock) {
|
||||||
lock (SyncLock)
|
if(DequeueOutputTimer != null) {
|
||||||
{
|
return;
|
||||||
if (DequeueOutputTimer != null) return;
|
}
|
||||||
|
|
||||||
if (IsConsolePresent)
|
if(IsConsolePresent) {
|
||||||
{
|
|
||||||
#if !NET452
|
#if !NET452
|
||||||
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
|
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
|
||||||
#endif
|
#endif
|
||||||
@ -61,22 +58,23 @@ namespace Unosquare.Swan
|
|||||||
/// <value>
|
/// <value>
|
||||||
/// The cursor left.
|
/// The cursor left.
|
||||||
/// </value>
|
/// </value>
|
||||||
public static int CursorLeft
|
public static Int32 CursorLeft {
|
||||||
{
|
get {
|
||||||
get
|
if(IsConsolePresent == false) {
|
||||||
{
|
return -1;
|
||||||
if (IsConsolePresent == false) return -1;
|
}
|
||||||
lock (SyncLock)
|
|
||||||
{
|
lock(SyncLock) {
|
||||||
Flush();
|
Flush();
|
||||||
return Console.CursorLeft;
|
return Console.CursorLeft;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
set
|
set {
|
||||||
{
|
if(IsConsolePresent == false) {
|
||||||
if (IsConsolePresent == false) return;
|
return;
|
||||||
lock (SyncLock)
|
}
|
||||||
{
|
|
||||||
|
lock(SyncLock) {
|
||||||
Flush();
|
Flush();
|
||||||
Console.CursorLeft = value;
|
Console.CursorLeft = value;
|
||||||
}
|
}
|
||||||
@ -89,23 +87,23 @@ namespace Unosquare.Swan
|
|||||||
/// <value>
|
/// <value>
|
||||||
/// The cursor top.
|
/// The cursor top.
|
||||||
/// </value>
|
/// </value>
|
||||||
public static int CursorTop
|
public static Int32 CursorTop {
|
||||||
{
|
get {
|
||||||
get
|
if(IsConsolePresent == false) {
|
||||||
{
|
return -1;
|
||||||
if (IsConsolePresent == false) return -1;
|
}
|
||||||
lock (SyncLock)
|
|
||||||
{
|
lock(SyncLock) {
|
||||||
Flush();
|
Flush();
|
||||||
return Console.CursorTop;
|
return Console.CursorTop;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
set
|
set {
|
||||||
{
|
if(IsConsolePresent == false) {
|
||||||
if (IsConsolePresent == false) return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
lock (SyncLock)
|
lock(SyncLock) {
|
||||||
{
|
|
||||||
Flush();
|
Flush();
|
||||||
Console.CursorTop = value;
|
Console.CursorTop = value;
|
||||||
}
|
}
|
||||||
@ -122,22 +120,18 @@ namespace Unosquare.Swan
|
|||||||
/// <value>
|
/// <value>
|
||||||
/// <c>true</c> if this instance is console present; otherwise, <c>false</c>.
|
/// <c>true</c> if this instance is console present; otherwise, <c>false</c>.
|
||||||
/// </value>
|
/// </value>
|
||||||
public static bool IsConsolePresent
|
public static Boolean IsConsolePresent {
|
||||||
{
|
get {
|
||||||
get
|
if(Settings.OverrideIsConsolePresent) {
|
||||||
{
|
return true;
|
||||||
if (Settings.OverrideIsConsolePresent) return true;
|
|
||||||
|
|
||||||
if (_isConsolePresent == null)
|
|
||||||
{
|
|
||||||
_isConsolePresent = true;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var windowHeight = Console.WindowHeight;
|
|
||||||
_isConsolePresent = windowHeight >= 0;
|
|
||||||
}
|
}
|
||||||
catch
|
|
||||||
{
|
if(_isConsolePresent == null) {
|
||||||
|
_isConsolePresent = true;
|
||||||
|
try {
|
||||||
|
Int32 windowHeight = Console.WindowHeight;
|
||||||
|
_isConsolePresent = windowHeight >= 0;
|
||||||
|
} catch {
|
||||||
_isConsolePresent = false;
|
_isConsolePresent = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -152,7 +146,7 @@ namespace Unosquare.Swan
|
|||||||
/// <value>
|
/// <value>
|
||||||
/// <c>true</c> if this instance is debugger attached; otherwise, <c>false</c>.
|
/// <c>true</c> if this instance is debugger attached; otherwise, <c>false</c>.
|
||||||
/// </value>
|
/// </value>
|
||||||
public static bool IsDebuggerAttached => System.Diagnostics.Debugger.IsAttached;
|
public static Boolean IsDebuggerAttached => System.Diagnostics.Debugger.IsAttached;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the available output writers in a bitwise mask.
|
/// Gets the available output writers in a bitwise mask.
|
||||||
@ -160,16 +154,16 @@ namespace Unosquare.Swan
|
|||||||
/// <value>
|
/// <value>
|
||||||
/// The available writers.
|
/// The available writers.
|
||||||
/// </value>
|
/// </value>
|
||||||
public static TerminalWriters AvailableWriters
|
public static TerminalWriters AvailableWriters {
|
||||||
{
|
get {
|
||||||
get
|
TerminalWriters writers = TerminalWriters.None;
|
||||||
{
|
if(IsConsolePresent) {
|
||||||
var writers = TerminalWriters.None;
|
|
||||||
if (IsConsolePresent)
|
|
||||||
writers = TerminalWriters.StandardError | TerminalWriters.StandardOutput;
|
writers = TerminalWriters.StandardError | TerminalWriters.StandardOutput;
|
||||||
|
}
|
||||||
|
|
||||||
if (IsDebuggerAttached)
|
if(IsDebuggerAttached) {
|
||||||
writers = writers | TerminalWriters.Diagnostics;
|
writers |= TerminalWriters.Diagnostics;
|
||||||
|
}
|
||||||
|
|
||||||
return writers;
|
return writers;
|
||||||
}
|
}
|
||||||
@ -181,8 +175,7 @@ namespace Unosquare.Swan
|
|||||||
/// <value>
|
/// <value>
|
||||||
/// The output encoding.
|
/// The output encoding.
|
||||||
/// </value>
|
/// </value>
|
||||||
public static Encoding OutputEncoding
|
public static Encoding OutputEncoding {
|
||||||
{
|
|
||||||
get => Console.OutputEncoding;
|
get => Console.OutputEncoding;
|
||||||
set => Console.OutputEncoding = value;
|
set => Console.OutputEncoding = value;
|
||||||
}
|
}
|
||||||
@ -198,41 +191,45 @@ namespace Unosquare.Swan
|
|||||||
/// Set the timeout to null or TimeSpan.Zero to wait indefinitely.
|
/// Set the timeout to null or TimeSpan.Zero to wait indefinitely.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="timeout">The timeout. Set the amount of time to black before this method exits.</param>
|
/// <param name="timeout">The timeout. Set the amount of time to black before this method exits.</param>
|
||||||
public static void Flush(TimeSpan? timeout = null)
|
public static void Flush(TimeSpan? timeout = null) {
|
||||||
{
|
if(timeout == null) {
|
||||||
if (timeout == null) timeout = TimeSpan.Zero;
|
timeout = TimeSpan.Zero;
|
||||||
var startTime = DateTime.UtcNow;
|
}
|
||||||
|
|
||||||
while (OutputQueue.Count > 0)
|
DateTime startTime = DateTime.UtcNow;
|
||||||
{
|
|
||||||
|
while(OutputQueue.Count > 0) {
|
||||||
// Manually trigger a timer cycle to run immediately
|
// Manually trigger a timer cycle to run immediately
|
||||||
DequeueOutputTimer.Change(0, OutputFlushInterval);
|
DequeueOutputTimer.Change(0, OutputFlushInterval);
|
||||||
|
|
||||||
// Wait for the output to finish
|
// Wait for the output to finish
|
||||||
if (OutputDone.Wait(OutputFlushInterval))
|
if(OutputDone.Wait(OutputFlushInterval)) {
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
// infinite timeout
|
// infinite timeout
|
||||||
if (timeout.Value == TimeSpan.Zero)
|
if(timeout.Value == TimeSpan.Zero) {
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
// break if we have reached a timeout condition
|
// break if we have reached a timeout condition
|
||||||
if (DateTime.UtcNow.Subtract(startTime) >= timeout.Value)
|
if(DateTime.UtcNow.Subtract(startTime) >= timeout.Value) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Sets the cursor position.
|
/// Sets the cursor position.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="left">The left.</param>
|
/// <param name="left">The left.</param>
|
||||||
/// <param name="top">The top.</param>
|
/// <param name="top">The top.</param>
|
||||||
public static void SetCursorPosition(int left, int top)
|
public static void SetCursorPosition(Int32 left, Int32 top) {
|
||||||
{
|
if(IsConsolePresent == false) {
|
||||||
if (IsConsolePresent == false) return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
lock (SyncLock)
|
lock(SyncLock) {
|
||||||
{
|
|
||||||
Flush();
|
Flush();
|
||||||
Console.SetCursorPosition(left.Clamp(0, left), top.Clamp(0, top));
|
Console.SetCursorPosition(left.Clamp(0, left), top.Clamp(0, top));
|
||||||
}
|
}
|
||||||
@ -252,20 +249,18 @@ namespace Unosquare.Swan
|
|||||||
/// Please note that if AvailableWriters is None, then no output will be enqueued.
|
/// Please note that if AvailableWriters is None, then no output will be enqueued.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="context">The context.</param>
|
/// <param name="context">The context.</param>
|
||||||
private static void EnqueueOutput(OutputContext context)
|
private static void EnqueueOutput(OutputContext context) {
|
||||||
{
|
lock(SyncLock) {
|
||||||
lock (SyncLock)
|
TerminalWriters availableWriters = AvailableWriters;
|
||||||
{
|
|
||||||
var availableWriters = AvailableWriters;
|
|
||||||
|
|
||||||
if (availableWriters == TerminalWriters.None || context.OutputWriters == TerminalWriters.None)
|
if(availableWriters == TerminalWriters.None || context.OutputWriters == TerminalWriters.None) {
|
||||||
{
|
|
||||||
OutputDone.Set();
|
OutputDone.Set();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((context.OutputWriters & availableWriters) == TerminalWriters.None)
|
if((context.OutputWriters & availableWriters) == TerminalWriters.None) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
OutputDone.Reset();
|
OutputDone.Reset();
|
||||||
OutputQueue.Enqueue(context);
|
OutputQueue.Enqueue(context);
|
||||||
@ -275,34 +270,30 @@ namespace Unosquare.Swan
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Runs a Terminal I/O cycle in the <see cref="ThreadPool"/> thread.
|
/// Runs a Terminal I/O cycle in the <see cref="ThreadPool"/> thread.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private static void DequeueOutputCycle()
|
private static void DequeueOutputCycle() {
|
||||||
{
|
if(AvailableWriters == TerminalWriters.None) {
|
||||||
if (AvailableWriters == TerminalWriters.None)
|
|
||||||
{
|
|
||||||
OutputDone.Set();
|
OutputDone.Set();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
InputDone.Wait();
|
InputDone.Wait();
|
||||||
|
|
||||||
if (OutputQueue.Count <= 0)
|
if(OutputQueue.Count <= 0) {
|
||||||
{
|
|
||||||
OutputDone.Set();
|
OutputDone.Set();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
OutputDone.Reset();
|
OutputDone.Reset();
|
||||||
|
|
||||||
while (OutputQueue.Count > 0)
|
while(OutputQueue.Count > 0) {
|
||||||
{
|
if(OutputQueue.TryDequeue(out OutputContext context) == false) {
|
||||||
if (OutputQueue.TryDequeue(out var context) == false) continue;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
// Process Console output and Skip over stuff we can't display so we don't stress the output too much.
|
// Process Console output and Skip over stuff we can't display so we don't stress the output too much.
|
||||||
if (IsConsolePresent && (Settings.OverrideIsConsolePresent || OutputQueue.Count <= Console.BufferHeight))
|
if(IsConsolePresent && (Settings.OverrideIsConsolePresent || OutputQueue.Count <= Console.BufferHeight)) {
|
||||||
{
|
|
||||||
// Output to the standard output
|
// Output to the standard output
|
||||||
if (context.OutputWriters.HasFlag(TerminalWriters.StandardOutput))
|
if(context.OutputWriters.HasFlag(TerminalWriters.StandardOutput)) {
|
||||||
{
|
|
||||||
Console.ForegroundColor = context.OutputColor;
|
Console.ForegroundColor = context.OutputColor;
|
||||||
Console.Out.Write(context.OutputText);
|
Console.Out.Write(context.OutputText);
|
||||||
Console.ResetColor();
|
Console.ResetColor();
|
||||||
@ -310,8 +301,7 @@ namespace Unosquare.Swan
|
|||||||
}
|
}
|
||||||
|
|
||||||
// output to the standard error
|
// output to the standard error
|
||||||
if (context.OutputWriters.HasFlag(TerminalWriters.StandardError))
|
if(context.OutputWriters.HasFlag(TerminalWriters.StandardError)) {
|
||||||
{
|
|
||||||
Console.ForegroundColor = context.OutputColor;
|
Console.ForegroundColor = context.OutputColor;
|
||||||
Console.Error.Write(context.OutputText);
|
Console.Error.Write(context.OutputText);
|
||||||
Console.ResetColor();
|
Console.ResetColor();
|
||||||
@ -320,9 +310,8 @@ namespace Unosquare.Swan
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Process Debugger output
|
// Process Debugger output
|
||||||
if (IsDebuggerAttached && context.OutputWriters.HasFlag(TerminalWriters.Diagnostics))
|
if(IsDebuggerAttached && context.OutputWriters.HasFlag(TerminalWriters.Diagnostics)) {
|
||||||
{
|
System.Diagnostics.Debug.Write(new String(context.OutputText));
|
||||||
System.Diagnostics.Debug.Write(new string(context.OutputText));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -334,25 +323,31 @@ namespace Unosquare.Swan
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents an asynchronous output context.
|
/// Represents an asynchronous output context.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private sealed class OutputContext
|
private sealed class OutputContext {
|
||||||
{
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="OutputContext"/> class.
|
/// Initializes a new instance of the <see cref="OutputContext"/> class.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public OutputContext()
|
public OutputContext() {
|
||||||
{
|
this.OriginalColor = Settings.DefaultColor;
|
||||||
OriginalColor = Settings.DefaultColor;
|
this.OutputWriters = IsConsolePresent
|
||||||
OutputWriters = IsConsolePresent
|
|
||||||
? TerminalWriters.StandardOutput
|
? TerminalWriters.StandardOutput
|
||||||
: IsDebuggerAttached
|
: IsDebuggerAttached
|
||||||
? TerminalWriters.Diagnostics
|
? TerminalWriters.Diagnostics
|
||||||
: TerminalWriters.None;
|
: TerminalWriters.None;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ConsoleColor OriginalColor { get; }
|
public ConsoleColor OriginalColor {
|
||||||
public ConsoleColor OutputColor { get; set; }
|
get;
|
||||||
public char[] OutputText { get; set; }
|
}
|
||||||
public TerminalWriters OutputWriters { get; set; }
|
public ConsoleColor OutputColor {
|
||||||
|
get; set;
|
||||||
|
}
|
||||||
|
public Char[] OutputText {
|
||||||
|
get; set;
|
||||||
|
}
|
||||||
|
public TerminalWriters OutputWriters {
|
||||||
|
get; set;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
Loading…
Reference in New Issue
Block a user