Compare commits

...
3 Commits
Author SHA1 Message Date
BlubbFish 2f74732924 Coding style 2019-12-04 17:10:06 +01:00
BlubbFish c1e8637516 Coding Styles 2019-12-03 18:44:25 +01:00
BlubbFish 186792fde8 Coding styles 2019-12-03 18:43:54 +01:00
181 changed files with 33438 additions and 35028 deletions
+137 -131
View File
@@ -1,134 +1,140 @@
namespace Unosquare.RaspberryIO.Camera using Unosquare.Swan;
{ using System;
using Swan; using System.Linq;
using System;
using System.Linq; namespace Unosquare.RaspberryIO.Camera {
/// <summary>
/// A simple RGB color class to represent colors in RGB and YUV colorspaces.
/// </summary>
public class CameraColor {
/// <summary> /// <summary>
/// A simple RGB color class to represent colors in RGB and YUV colorspaces. /// Initializes a new instance of the <see cref="CameraColor"/> class.
/// </summary> /// </summary>
public class CameraColor /// <param name="r">The red.</param>
{ /// <param name="g">The green.</param>
/// <summary> /// <param name="b">The blue.</param>
/// Initializes a new instance of the <see cref="CameraColor"/> class. public CameraColor(Int32 r, Int32 g, Int32 b)
/// </summary> : this(r, g, b, String.Empty) {
/// <param name="r">The red.</param> }
/// <param name="g">The green.</param>
/// <param name="b">The blue.</param> /// <summary>
public CameraColor(int r, int g, int b) /// Initializes a new instance of the <see cref="CameraColor"/> class.
: this(r, g, b, string.Empty) /// </summary>
{ /// <param name="r">The red.</param>
} /// <param name="g">The green.</param>
/// <param name="b">The blue.</param>
/// <summary> /// <param name="name">The well-known color name.</param>
/// Initializes a new instance of the <see cref="CameraColor"/> class. public CameraColor(Int32 r, Int32 g, Int32 b, String name) {
/// </summary> this.RGB = new[] { Convert.ToByte(r.Clamp(0, 255)), Convert.ToByte(g.Clamp(0, 255)), Convert.ToByte(b.Clamp(0, 255)) };
/// <param name="r">The red.</param>
/// <param name="g">The green.</param> Single y = this.R * .299000f + this.G * .587000f + this.B * .114000f;
/// <param name="b">The blue.</param> Single u = this.R * -.168736f + this.G * -.331264f + this.B * .500000f + 128f;
/// <param name="name">The well-known color name.</param> Single v = this.R * .500000f + this.G * -.418688f + this.B * -.081312f + 128f;
public CameraColor(int r, int g, int b, string name)
{ this.YUV = new Byte[] { (Byte)y.Clamp(0, 255), (Byte)u.Clamp(0, 255), (Byte)v.Clamp(0, 255) };
RGB = new[] { Convert.ToByte(r.Clamp(0, 255)), Convert.ToByte(g.Clamp(0, 255)), Convert.ToByte(b.Clamp(0, 255)) }; this.Name = name;
}
var y = (R * .299000f) + (G * .587000f) + (B * .114000f);
var u = (R * -.168736f) + (G * -.331264f) + (B * .500000f) + 128f; #region Static Definitions
var v = (R * .500000f) + (G * -.418688f) + (B * -.081312f) + 128f;
/// <summary>
YUV = new byte[] { (byte)y.Clamp(0, 255), (byte)u.Clamp(0, 255), (byte)v.Clamp(0, 255) }; /// Gets the predefined white color.
Name = name; /// </summary>
} public static CameraColor White => new CameraColor(255, 255, 255, nameof(White));
#region Static Definitions /// <summary>
/// Gets the predefined red color.
/// <summary> /// </summary>
/// Gets the predefined white color. public static CameraColor Red => new CameraColor(255, 0, 0, nameof(Red));
/// </summary>
public static CameraColor White => new CameraColor(255, 255, 255, nameof(White)); /// <summary>
/// Gets the predefined green color.
/// <summary> /// </summary>
/// Gets the predefined red color. public static CameraColor Green => new CameraColor(0, 255, 0, nameof(Green));
/// </summary>
public static CameraColor Red => new CameraColor(255, 0, 0, nameof(Red)); /// <summary>
/// Gets the predefined blue color.
/// <summary> /// </summary>
/// Gets the predefined green color. public static CameraColor Blue => new CameraColor(0, 0, 255, nameof(Blue));
/// </summary>
public static CameraColor Green => new CameraColor(0, 255, 0, nameof(Green)); /// <summary>
/// Gets the predefined black color.
/// <summary> /// </summary>
/// Gets the predefined blue color. public static CameraColor Black => new CameraColor(0, 0, 0, nameof(Black));
/// </summary>
public static CameraColor Blue => new CameraColor(0, 0, 255, nameof(Blue)); #endregion
/// <summary> /// <summary>
/// Gets the predefined black color. /// Gets the well-known color name.
/// </summary> /// </summary>
public static CameraColor Black => new CameraColor(0, 0, 0, nameof(Black)); public String Name {
get;
#endregion }
/// <summary> /// <summary>
/// Gets the well-known color name. /// Gets the red byte.
/// </summary> /// </summary>
public string Name { get; } public Byte R => this.RGB[0];
/// <summary> /// <summary>
/// Gets the red byte. /// Gets the green byte.
/// </summary> /// </summary>
public byte R => RGB[0]; public Byte G => this.RGB[1];
/// <summary> /// <summary>
/// Gets the green byte. /// Gets the blue byte.
/// </summary> /// </summary>
public byte G => RGB[1]; public Byte B => this.RGB[2];
/// <summary> /// <summary>
/// Gets the blue byte. /// Gets the RGB byte array (3 bytes).
/// </summary> /// </summary>
public byte B => RGB[2]; public Byte[] RGB {
get;
/// <summary> }
/// Gets the RGB byte array (3 bytes).
/// </summary> /// <summary>
public byte[] RGB { get; } /// Gets the YUV byte array (3 bytes).
/// </summary>
/// <summary> public Byte[] YUV {
/// Gets the YUV byte array (3 bytes). get;
/// </summary> }
public byte[] YUV { get; }
/// <summary>
/// <summary> /// Returns a hexadecimal representation of the RGB byte array.
/// Returns a hexadecimal representation of the RGB byte array. /// Preceded by 0x and all in lowercase
/// Preceded by 0x and all in lowercase /// </summary>
/// </summary> /// <param name="reverse">if set to <c>true</c> [reverse].</param>
/// <param name="reverse">if set to <c>true</c> [reverse].</param> /// <returns>A string</returns>
/// <returns>A string</returns> public String ToRgbHex(Boolean reverse) {
public string ToRgbHex(bool reverse) Byte[] data = this.RGB.ToArray();
{ if(reverse) {
var data = RGB.ToArray(); Array.Reverse(data);
if (reverse) Array.Reverse(data); }
return ToHex(data);
} return ToHex(data);
}
/// <summary>
/// Returns a hexadecimal representation of the YUV byte array. /// <summary>
/// Preceded by 0x and all in lowercase /// Returns a hexadecimal representation of the YUV byte array.
/// </summary> /// Preceded by 0x and all in lowercase
/// <param name="reverse">if set to <c>true</c> [reverse].</param> /// </summary>
/// <returns>A string</returns> /// <param name="reverse">if set to <c>true</c> [reverse].</param>
public string ToYuvHex(bool reverse) /// <returns>A string</returns>
{ public String ToYuvHex(Boolean reverse) {
var data = YUV.ToArray(); Byte[] data = this.YUV.ToArray();
if (reverse) Array.Reverse(data); if(reverse) {
return ToHex(data); Array.Reverse(data);
} }
/// <summary> return ToHex(data);
/// Returns a hexadecimal representation of the data byte array }
/// </summary>
/// <param name="data">The data.</param> /// <summary>
/// <returns>A string</returns> /// Returns a hexadecimal representation of the data byte array
private static string ToHex(byte[] data) => $"0x{BitConverter.ToString(data).Replace("-", string.Empty).ToLowerInvariant()}"; /// </summary>
} /// <param name="data">The data.</param>
/// <returns>A string</returns>
private static String ToHex(Byte[] data) => $"0x{BitConverter.ToString(data).Replace("-", String.Empty).ToLowerInvariant()}";
}
} }
+187 -213
View File
@@ -1,216 +1,190 @@
namespace Unosquare.RaspberryIO.Camera using Unosquare.Swan.Abstractions;
{ using System;
using Swan.Abstractions; using Unosquare.Swan.Components;
using System; using System.IO;
using Swan.Components; using System.Threading;
using System.IO; using System.Threading.Tasks;
using System.Threading;
using System.Threading.Tasks; namespace Unosquare.RaspberryIO.Camera {
/// <summary>
/// The Raspberry Pi's camera controller wrapping raspistill and raspivid programs.
/// This class is a singleton
/// </summary>
public class CameraController : SingletonBase<CameraController> {
#region Private Declarations
private static readonly ManualResetEventSlim OperationDone = new ManualResetEventSlim(true);
private static readonly Object SyncRoot = new Object();
private static CancellationTokenSource _videoTokenSource = new CancellationTokenSource();
private static Task<Task> _videoStreamTask;
#endregion
#region Properties
/// <summary> /// <summary>
/// The Raspberry Pi's camera controller wrapping raspistill and raspivid programs. /// Gets a value indicating whether the camera module is busy.
/// This class is a singleton
/// </summary> /// </summary>
public class CameraController : SingletonBase<CameraController> /// <value>
{ /// <c>true</c> if this instance is busy; otherwise, <c>false</c>.
#region Private Declarations /// </value>
public Boolean IsBusy => OperationDone.IsSet == false;
private static readonly ManualResetEventSlim OperationDone = new ManualResetEventSlim(true);
private static readonly object SyncRoot = new object(); #endregion
private static CancellationTokenSource _videoTokenSource = new CancellationTokenSource();
private static Task<Task> _videoStreamTask; #region Image Capture Methods
#endregion /// <summary>
/// Captures an image asynchronously.
#region Properties /// </summary>
/// <param name="settings">The settings.</param>
/// <summary> /// <param name="ct">The ct.</param>
/// Gets a value indicating whether the camera module is busy. /// <returns>The image bytes</returns>
/// </summary> /// <exception cref="InvalidOperationException">Cannot use camera module because it is currently busy.</exception>
/// <value> [System.Diagnostics.CodeAnalysis.SuppressMessage("Codequalität", "IDE0067:Objekte verwerfen, bevor Bereich verloren geht", Justification = "<Ausstehend>")]
/// <c>true</c> if this instance is busy; otherwise, <c>false</c>. public async Task<Byte[]> CaptureImageAsync(CameraStillSettings settings, CancellationToken ct = default) {
/// </value> if(Instance.IsBusy) {
public bool IsBusy => OperationDone.IsSet == false; throw new InvalidOperationException("Cannot use camera module because it is currently busy.");
}
#endregion
if(settings.CaptureTimeoutMilliseconds <= 0) {
#region Image Capture Methods throw new ArgumentException($"{nameof(settings.CaptureTimeoutMilliseconds)} needs to be greater than 0");
}
/// <summary>
/// Captures an image asynchronously. try {
/// </summary> OperationDone.Reset();
/// <param name="settings">The settings.</param>
/// <param name="ct">The ct.</param> MemoryStream output = new MemoryStream();
/// <returns>The image bytes</returns> Int32 exitCode = await ProcessRunner.RunProcessAsync(
/// <exception cref="InvalidOperationException">Cannot use camera module because it is currently busy.</exception> settings.CommandName,
public async Task<byte[]> CaptureImageAsync(CameraStillSettings settings, CancellationToken ct = default) settings.CreateProcessArguments(),
{ (data, proc) => output.Write(data, 0, data.Length),
if (Instance.IsBusy) null,
throw new InvalidOperationException("Cannot use camera module because it is currently busy."); true,
ct);
if (settings.CaptureTimeoutMilliseconds <= 0)
throw new ArgumentException($"{nameof(settings.CaptureTimeoutMilliseconds)} needs to be greater than 0"); return exitCode != 0 ? new Byte[] { } : output.ToArray();
} finally {
try OperationDone.Set();
{ }
OperationDone.Reset(); }
var output = new MemoryStream(); /// <summary>
var exitCode = await ProcessRunner.RunProcessAsync( /// Captures an image.
settings.CommandName, /// </summary>
settings.CreateProcessArguments(), /// <param name="settings">The settings.</param>
(data, proc) => /// <returns>The image bytes</returns>
{ public Byte[] CaptureImage(CameraStillSettings settings) => this.CaptureImageAsync(settings).GetAwaiter().GetResult();
output.Write(data, 0, data.Length);
}, /// <summary>
null, /// Captures a JPEG encoded image asynchronously at 90% quality.
true, /// </summary>
ct); /// <param name="width">The width.</param>
/// <param name="height">The height.</param>
return exitCode != 0 ? new byte[] { } : output.ToArray(); /// <param name="ct">The ct.</param>
} /// <returns>The image bytes</returns>
finally public Task<Byte[]> CaptureImageJpegAsync(Int32 width, Int32 height, CancellationToken ct = default) {
{ CameraStillSettings settings = new CameraStillSettings {
OperationDone.Set(); CaptureWidth = width,
} CaptureHeight = height,
} CaptureJpegQuality = 90,
CaptureTimeoutMilliseconds = 300,
/// <summary> };
/// Captures an image.
/// </summary> return this.CaptureImageAsync(settings, ct);
/// <param name="settings">The settings.</param> }
/// <returns>The image bytes</returns>
public byte[] CaptureImage(CameraStillSettings settings) /// <summary>
{ /// Captures a JPEG encoded image at 90% quality.
return CaptureImageAsync(settings).GetAwaiter().GetResult(); /// </summary>
} /// <param name="width">The width.</param>
/// <param name="height">The height.</param>
/// <summary> /// <returns>The image bytes</returns>
/// Captures a JPEG encoded image asynchronously at 90% quality. public Byte[] CaptureImageJpeg(Int32 width, Int32 height) => this.CaptureImageJpegAsync(width, height).GetAwaiter().GetResult();
/// </summary>
/// <param name="width">The width.</param> #endregion
/// <param name="height">The height.</param>
/// <param name="ct">The ct.</param> #region Video Capture Methods
/// <returns>The image bytes</returns>
public Task<byte[]> CaptureImageJpegAsync(int width, int height, CancellationToken ct = default) /// <summary>
{ /// Opens the video stream with a timeout of 0 (running indefinitely) at 1080p resolution, variable bitrate and 25 FPS.
var settings = new CameraStillSettings /// No preview is shown
{ /// </summary>
CaptureWidth = width, /// <param name="onDataCallback">The on data callback.</param>
CaptureHeight = height, /// <param name="onExitCallback">The on exit callback.</param>
CaptureJpegQuality = 90, public void OpenVideoStream(Action<Byte[]> onDataCallback, Action onExitCallback = null) {
CaptureTimeoutMilliseconds = 300, CameraVideoSettings settings = new CameraVideoSettings {
}; CaptureTimeoutMilliseconds = 0,
CaptureDisplayPreview = false,
return CaptureImageAsync(settings, ct); CaptureWidth = 1920,
} CaptureHeight = 1080
};
/// <summary>
/// Captures a JPEG encoded image at 90% quality. this.OpenVideoStream(settings, onDataCallback, onExitCallback);
/// </summary> }
/// <param name="width">The width.</param>
/// <param name="height">The height.</param> /// <summary>
/// <returns>The image bytes</returns> /// Opens the video stream with the supplied settings. Capture Timeout Milliseconds has to be 0 or greater
public byte[] CaptureImageJpeg(int width, int height) => CaptureImageJpegAsync(width, height).GetAwaiter().GetResult(); /// </summary>
/// <param name="settings">The settings.</param>
#endregion /// <param name="onDataCallback">The on data callback.</param>
/// <param name="onExitCallback">The on exit callback.</param>
#region Video Capture Methods /// <exception cref="InvalidOperationException">Cannot use camera module because it is currently busy.</exception>
/// <exception cref="ArgumentException">CaptureTimeoutMilliseconds</exception>
/// <summary> public void OpenVideoStream(CameraVideoSettings settings, Action<Byte[]> onDataCallback, Action onExitCallback) {
/// Opens the video stream with a timeout of 0 (running indefinitely) at 1080p resolution, variable bitrate and 25 FPS. if(Instance.IsBusy) {
/// No preview is shown throw new InvalidOperationException("Cannot use camera module because it is currently busy.");
/// </summary> }
/// <param name="onDataCallback">The on data callback.</param>
/// <param name="onExitCallback">The on exit callback.</param> if(settings.CaptureTimeoutMilliseconds < 0) {
public void OpenVideoStream(Action<byte[]> onDataCallback, Action onExitCallback = null) throw new ArgumentException($"{nameof(settings.CaptureTimeoutMilliseconds)} needs to be greater than or equal to 0");
{ }
var settings = new CameraVideoSettings
{ try {
CaptureTimeoutMilliseconds = 0, OperationDone.Reset();
CaptureDisplayPreview = false, _videoStreamTask = Task.Factory.StartNew(() => VideoWorkerDoWork(settings, onDataCallback, onExitCallback), _videoTokenSource.Token);
CaptureWidth = 1920, } catch {
CaptureHeight = 1080 OperationDone.Set();
}; throw;
}
OpenVideoStream(settings, onDataCallback, onExitCallback); }
}
/// <summary>
/// <summary> /// Closes the video stream of a video stream is open.
/// Opens the video stream with the supplied settings. Capture Timeout Milliseconds has to be 0 or greater /// </summary>
/// </summary> public void CloseVideoStream() {
/// <param name="settings">The settings.</param> lock(SyncRoot) {
/// <param name="onDataCallback">The on data callback.</param> if(this.IsBusy == false) {
/// <param name="onExitCallback">The on exit callback.</param> return;
/// <exception cref="InvalidOperationException">Cannot use camera module because it is currently busy.</exception> }
/// <exception cref="ArgumentException">CaptureTimeoutMilliseconds</exception> }
public void OpenVideoStream(CameraVideoSettings settings, Action<byte[]> onDataCallback, Action onExitCallback)
{ if(_videoTokenSource.IsCancellationRequested == false) {
if (Instance.IsBusy) _videoTokenSource.Cancel();
throw new InvalidOperationException("Cannot use camera module because it is currently busy."); _videoStreamTask.Wait();
}
if (settings.CaptureTimeoutMilliseconds < 0)
throw new ArgumentException($"{nameof(settings.CaptureTimeoutMilliseconds)} needs to be greater than or equal to 0"); _videoTokenSource = new CancellationTokenSource();
}
try
{ private static async Task VideoWorkerDoWork(CameraVideoSettings settings, Action<Byte[]> onDataCallback, Action onExitCallback) {
OperationDone.Reset(); try {
_videoStreamTask = Task.Factory.StartNew(() => VideoWorkerDoWork(settings, onDataCallback, onExitCallback), _videoTokenSource.Token); await ProcessRunner.RunProcessAsync(
} settings.CommandName,
catch settings.CreateProcessArguments(),
{ (data, proc) => onDataCallback?.Invoke(data),
OperationDone.Set(); null,
throw; true,
} _videoTokenSource.Token);
}
onExitCallback?.Invoke();
/// <summary> } catch {
/// Closes the video stream of a video stream is open. // swallow
/// </summary> } finally {
public void CloseVideoStream() Instance.CloseVideoStream();
{ OperationDone.Set();
lock (SyncRoot) }
{ }
if (IsBusy == false) #endregion
return; }
}
if (_videoTokenSource.IsCancellationRequested == false)
{
_videoTokenSource.Cancel();
_videoStreamTask.Wait();
}
_videoTokenSource = new CancellationTokenSource();
}
private static async Task VideoWorkerDoWork(
CameraVideoSettings settings,
Action<byte[]> onDataCallback,
Action onExitCallback)
{
try
{
await ProcessRunner.RunProcessAsync(
settings.CommandName,
settings.CreateProcessArguments(),
(data, proc) => onDataCallback?.Invoke(data),
null,
true,
_videoTokenSource.Token);
onExitCallback?.Invoke();
}
catch
{
// swallow
}
finally
{
Instance.CloseVideoStream();
OperationDone.Set();
}
}
#endregion
}
} }
+83 -79
View File
@@ -1,82 +1,86 @@
namespace Unosquare.RaspberryIO.Camera using Unosquare.Swan;
{ using System;
using Swan; using System.Globalization;
using System.Globalization;
namespace Unosquare.RaspberryIO.Camera {
/// <summary>
/// Defines the Raspberry Pi camera's sensor ROI (Region of Interest)
/// </summary>
public struct CameraRect {
/// <summary> /// <summary>
/// Defines the Raspberry Pi camera's sensor ROI (Region of Interest) /// The default ROI which is the entire area.
/// </summary> /// </summary>
public struct CameraRect public static readonly CameraRect Default = new CameraRect { X = 0M, Y = 0M, W = 1.0M, H = 1.0M };
{
/// <summary> /// <summary>
/// The default ROI which is the entire area. /// Gets or sets the x in relative coordinates. (0.0 to 1.0)
/// </summary> /// </summary>
public static readonly CameraRect Default = new CameraRect { X = 0M, Y = 0M, W = 1.0M, H = 1.0M }; /// <value>
/// The x.
/// <summary> /// </value>
/// Gets or sets the x in relative coordinates. (0.0 to 1.0) public Decimal X {
/// </summary> get; set;
/// <value> }
/// The x.
/// </value> /// <summary>
public decimal X { get; set; } /// Gets or sets the y location in relative coordinates. (0.0 to 1.0)
/// </summary>
/// <summary> /// <value>
/// Gets or sets the y location in relative coordinates. (0.0 to 1.0) /// The y.
/// </summary> /// </value>
/// <value> public Decimal Y {
/// The y. get; set;
/// </value> }
public decimal Y { get; set; }
/// <summary>
/// <summary> /// Gets or sets the width in relative coordinates. (0.0 to 1.0)
/// Gets or sets the width in relative coordinates. (0.0 to 1.0) /// </summary>
/// </summary> /// <value>
/// <value> /// The w.
/// The w. /// </value>
/// </value> public Decimal W {
public decimal W { get; set; } get; set;
}
/// <summary>
/// Gets or sets the height in relative coordinates. (0.0 to 1.0) /// <summary>
/// </summary> /// Gets or sets the height in relative coordinates. (0.0 to 1.0)
/// <value> /// </summary>
/// The h. /// <value>
/// </value> /// The h.
public decimal H { get; set; } /// </value>
public Decimal H {
/// <summary> get; set;
/// Gets a value indicating whether this instance is equal to the default (The entire area). }
/// </summary>
/// <value> /// <summary>
/// <c>true</c> if this instance is default; otherwise, <c>false</c>. /// Gets a value indicating whether this instance is equal to the default (The entire area).
/// </value> /// </summary>
public bool IsDefault /// <value>
{ /// <c>true</c> if this instance is default; otherwise, <c>false</c>.
get /// </value>
{ public Boolean IsDefault {
Clamp(); get {
return X == Default.X && Y == Default.Y && W == Default.W && H == Default.H; this.Clamp();
} return this.X == Default.X && this.Y == Default.Y && this.W == Default.W && this.H == Default.H;
} }
}
/// <summary>
/// Clamps the members of this ROI to their minimum and maximum values /// <summary>
/// </summary> /// Clamps the members of this ROI to their minimum and maximum values
public void Clamp() /// </summary>
{ public void Clamp() {
X = X.Clamp(0M, 1M); this.X = this.X.Clamp(0M, 1M);
Y = Y.Clamp(0M, 1M); this.Y = this.Y.Clamp(0M, 1M);
W = W.Clamp(0M, 1M - X); this.W = this.W.Clamp(0M, 1M - this.X);
H = H.Clamp(0M, 1M - Y); this.H = this.H.Clamp(0M, 1M - this.Y);
} }
/// <summary> /// <summary>
/// Returns a <see cref="string" /> that represents this instance. /// Returns a <see cref="String" /> that represents this instance.
/// </summary> /// </summary>
/// <returns> /// <returns>
/// A <see cref="string" /> that represents this instance. /// A <see cref="String" /> that represents this instance.
/// </returns> /// </returns>
public override string ToString() => $"{X.ToString(CultureInfo.InvariantCulture)},{Y.ToString(CultureInfo.InvariantCulture)},{W.ToString(CultureInfo.InvariantCulture)},{H.ToString(CultureInfo.InvariantCulture)}"; public override String ToString() => $"{this.X.ToString(CultureInfo.InvariantCulture)},{this.Y.ToString(CultureInfo.InvariantCulture)},{this.W.ToString(CultureInfo.InvariantCulture)},{this.H.ToString(CultureInfo.InvariantCulture)}";
} }
} }
+358 -337
View File
@@ -1,340 +1,361 @@
namespace Unosquare.RaspberryIO.Camera using Unosquare.Swan;
{ using System.Globalization;
using Swan; using System.Text;
using System.Globalization; using System;
using System.Text;
namespace Unosquare.RaspberryIO.Camera {
/// <summary>
/// A base class to implement raspistill and raspivid wrappers
/// Full documentation available at
/// https://www.raspberrypi.org/documentation/raspbian/applications/camera.md
/// </summary>
public abstract class CameraSettingsBase {
/// <summary> /// <summary>
/// A base class to implement raspistill and raspivid wrappers /// The Invariant Culture shorthand
/// Full documentation available at
/// https://www.raspberrypi.org/documentation/raspbian/applications/camera.md
/// </summary> /// </summary>
public abstract class CameraSettingsBase protected static readonly CultureInfo Ci = CultureInfo.InvariantCulture;
{
/// <summary> #region Capture Settings
/// The Invariant Culture shorthand
/// </summary> /// <summary>
protected static readonly CultureInfo Ci = CultureInfo.InvariantCulture; /// Gets or sets the timeout milliseconds.
/// Default value is 5000
#region Capture Settings /// Recommended value is at least 300 in order to let the light collectors open
/// </summary>
/// <summary> public Int32 CaptureTimeoutMilliseconds { get; set; } = 5000;
/// Gets or sets the timeout milliseconds.
/// Default value is 5000 /// <summary>
/// Recommended value is at least 300 in order to let the light collectors open /// Gets or sets a value indicating whether or not to show a preview window on the screen
/// </summary> /// </summary>
public int CaptureTimeoutMilliseconds { get; set; } = 5000; public Boolean CaptureDisplayPreview { get; set; } = false;
/// <summary> /// <summary>
/// Gets or sets a value indicating whether or not to show a preview window on the screen /// Gets or sets a value indicating whether a preview window is shown in full screen mode if enabled
/// </summary> /// </summary>
public bool CaptureDisplayPreview { get; set; } = false; public Boolean CaptureDisplayPreviewInFullScreen { get; set; } = true;
/// <summary> /// <summary>
/// Gets or sets a value indicating whether a preview window is shown in full screen mode if enabled /// Gets or sets a value indicating whether video stabilization should be enabled.
/// </summary> /// </summary>
public bool CaptureDisplayPreviewInFullScreen { get; set; } = true; public Boolean CaptureVideoStabilizationEnabled { get; set; } = false;
/// <summary> /// <summary>
/// Gets or sets a value indicating whether video stabilization should be enabled. /// Gets or sets the display preview opacity only if the display preview property is enabled.
/// </summary> /// </summary>
public bool CaptureVideoStabilizationEnabled { get; set; } = false; public Byte CaptureDisplayPreviewOpacity { get; set; } = 255;
/// <summary> /// <summary>
/// Gets or sets the display preview opacity only if the display preview property is enabled. /// Gets or sets the capture sensor region of interest in relative coordinates.
/// </summary> /// </summary>
public byte CaptureDisplayPreviewOpacity { get; set; } = 255; public CameraRect CaptureSensorRoi { get; set; } = CameraRect.Default;
/// <summary> /// <summary>
/// Gets or sets the capture sensor region of interest in relative coordinates. /// Gets or sets the capture shutter speed in microseconds.
/// </summary> /// Default -1, Range 0 to 6000000 (equivalent to 6 seconds)
public CameraRect CaptureSensorRoi { get; set; } = CameraRect.Default; /// </summary>
public Int32 CaptureShutterSpeedMicroseconds { get; set; } = -1;
/// <summary>
/// Gets or sets the capture shutter speed in microseconds. /// <summary>
/// Default -1, Range 0 to 6000000 (equivalent to 6 seconds) /// Gets or sets the exposure mode.
/// </summary> /// </summary>
public int CaptureShutterSpeedMicroseconds { get; set; } = -1; public CameraExposureMode CaptureExposure { get; set; } = CameraExposureMode.Auto;
/// <summary> /// <summary>
/// Gets or sets the exposure mode. /// Gets or sets the picture EV compensation. Default is 0, Range is -10 to 10
/// </summary> /// Camera exposure compensation is commonly stated in terms of EV units;
public CameraExposureMode CaptureExposure { get; set; } = CameraExposureMode.Auto; /// 1 EV is equal to one exposure step (or stop), corresponding to a doubling of exposure.
/// Exposure can be adjusted by changing either the lens f-number or the exposure time;
/// <summary> /// which one is changed usually depends on the camera's exposure mode.
/// Gets or sets the picture EV compensation. Default is 0, Range is -10 to 10 /// </summary>
/// Camera exposure compensation is commonly stated in terms of EV units; public Int32 CaptureExposureCompensation { get; set; } = 0;
/// 1 EV is equal to one exposure step (or stop), corresponding to a doubling of exposure.
/// Exposure can be adjusted by changing either the lens f-number or the exposure time; /// <summary>
/// which one is changed usually depends on the camera's exposure mode. /// Gets or sets the capture metering mode.
/// </summary> /// </summary>
public int CaptureExposureCompensation { get; set; } = 0; public CameraMeteringMode CaptureMeteringMode { get; set; } = CameraMeteringMode.Average;
/// <summary> /// <summary>
/// Gets or sets the capture metering mode. /// Gets or sets the automatic white balance mode. By default it is set to Auto
/// </summary> /// </summary>
public CameraMeteringMode CaptureMeteringMode { get; set; } = CameraMeteringMode.Average; public CameraWhiteBalanceMode CaptureWhiteBalanceControl { get; set; } = CameraWhiteBalanceMode.Auto;
/// <summary> /// <summary>
/// Gets or sets the automatic white balance mode. By default it is set to Auto /// Gets or sets the capture white balance gain on the blue channel. Example: 1.25
/// </summary> /// Only takes effect if White balance control is set to off.
public CameraWhiteBalanceMode CaptureWhiteBalanceControl { get; set; } = CameraWhiteBalanceMode.Auto; /// Default is 0
/// </summary>
/// <summary> public Decimal CaptureWhiteBalanceGainBlue { get; set; } = 0M;
/// Gets or sets the capture white balance gain on the blue channel. Example: 1.25
/// Only takes effect if White balance control is set to off. /// <summary>
/// Default is 0 /// Gets or sets the capture white balance gain on the red channel. Example: 1.75
/// </summary> /// Only takes effect if White balance control is set to off.
public decimal CaptureWhiteBalanceGainBlue { get; set; } = 0M; /// Default is 0
/// </summary>
/// <summary> public Decimal CaptureWhiteBalanceGainRed { get; set; } = 0M;
/// Gets or sets the capture white balance gain on the red channel. Example: 1.75
/// Only takes effect if White balance control is set to off. /// <summary>
/// Default is 0 /// Gets or sets the dynamic range compensation.
/// </summary> /// DRC changes the images by increasing the range of dark areas, and decreasing the brighter areas. This can improve the image in low light areas.
public decimal CaptureWhiteBalanceGainRed { get; set; } = 0M; /// </summary>
public CameraDynamicRangeCompensation CaptureDynamicRangeCompensation {
/// <summary> get; set;
/// Gets or sets the dynamic range compensation. } =
/// DRC changes the images by increasing the range of dark areas, and decreasing the brighter areas. This can improve the image in low light areas. CameraDynamicRangeCompensation.Off;
/// </summary>
public CameraDynamicRangeCompensation CaptureDynamicRangeCompensation { get; set; } = #endregion
CameraDynamicRangeCompensation.Off;
#region Image Properties
#endregion
/// <summary>
#region Image Properties /// Gets or sets the width of the picture to take.
/// Less than or equal to 0 in either width or height means maximum resolution available.
/// <summary> /// </summary>
/// Gets or sets the width of the picture to take. public Int32 CaptureWidth { get; set; } = 640;
/// Less than or equal to 0 in either width or height means maximum resolution available.
/// </summary> /// <summary>
public int CaptureWidth { get; set; } = 640; /// Gets or sets the height of the picture to take.
/// Less than or equal to 0 in either width or height means maximum resolution available.
/// <summary> /// </summary>
/// Gets or sets the height of the picture to take. public Int32 CaptureHeight { get; set; } = 480;
/// Less than or equal to 0 in either width or height means maximum resolution available.
/// </summary> /// <summary>
public int CaptureHeight { get; set; } = 480; /// Gets or sets the picture sharpness. Default is 0, Range form -100 to 100
/// </summary>
/// <summary> public Int32 ImageSharpness { get; set; } = 0;
/// Gets or sets the picture sharpness. Default is 0, Range form -100 to 100
/// </summary> /// <summary>
public int ImageSharpness { get; set; } = 0; /// Gets or sets the picture contrast. Default is 0, Range form -100 to 100
/// </summary>
/// <summary> public Int32 ImageContrast { get; set; } = 0;
/// Gets or sets the picture contrast. Default is 0, Range form -100 to 100
/// </summary> /// <summary>
public int ImageContrast { get; set; } = 0; /// Gets or sets the picture brightness. Default is 50, Range form 0 to 100
/// </summary>
/// <summary> public Int32 ImageBrightness { get; set; } = 50; // from 0 to 100
/// Gets or sets the picture brightness. Default is 50, Range form 0 to 100
/// </summary> /// <summary>
public int ImageBrightness { get; set; } = 50; // from 0 to 100 /// Gets or sets the picture saturation. Default is 0, Range form -100 to 100
/// </summary>
/// <summary> public Int32 ImageSaturation { get; set; } = 0;
/// Gets or sets the picture saturation. Default is 0, Range form -100 to 100
/// </summary> /// <summary>
public int ImageSaturation { get; set; } = 0; /// Gets or sets the picture ISO. Default is -1 Range is 100 to 800
/// The higher the value, the more light the sensor absorbs
/// <summary> /// </summary>
/// Gets or sets the picture ISO. Default is -1 Range is 100 to 800 public Int32 ImageIso { get; set; } = -1;
/// The higher the value, the more light the sensor absorbs
/// </summary> /// <summary>
public int ImageIso { get; set; } = -1; /// Gets or sets the image capture effect to be applied.
/// </summary>
/// <summary> public CameraImageEffect ImageEffect { get; set; } = CameraImageEffect.None;
/// Gets or sets the image capture effect to be applied.
/// </summary> /// <summary>
public CameraImageEffect ImageEffect { get; set; } = CameraImageEffect.None; /// Gets or sets the color effect U coordinates.
/// Default is -1, Range is 0 to 255
/// <summary> /// 128:128 should be effectively a monochrome image.
/// Gets or sets the color effect U coordinates. /// </summary>
/// Default is -1, Range is 0 to 255 public Int32 ImageColorEffectU { get; set; } = -1; // 0 to 255
/// 128:128 should be effectively a monochrome image.
/// </summary> /// <summary>
public int ImageColorEffectU { get; set; } = -1; // 0 to 255 /// Gets or sets the color effect V coordinates.
/// Default is -1, Range is 0 to 255
/// <summary> /// 128:128 should be effectively a monochrome image.
/// Gets or sets the color effect V coordinates. /// </summary>
/// Default is -1, Range is 0 to 255 public Int32 ImageColorEffectV { get; set; } = -1; // 0 to 255
/// 128:128 should be effectively a monochrome image.
/// </summary> /// <summary>
public int ImageColorEffectV { get; set; } = -1; // 0 to 255 /// Gets or sets the image rotation. Default is no rotation
/// </summary>
/// <summary> public CameraImageRotation ImageRotation { get; set; } = CameraImageRotation.None;
/// Gets or sets the image rotation. Default is no rotation
/// </summary> /// <summary>
public CameraImageRotation ImageRotation { get; set; } = CameraImageRotation.None; /// Gets or sets a value indicating whether the image should be flipped horizontally.
/// </summary>
/// <summary> public Boolean ImageFlipHorizontally {
/// Gets or sets a value indicating whether the image should be flipped horizontally. get; set;
/// </summary> }
public bool ImageFlipHorizontally { get; set; }
/// <summary>
/// <summary> /// Gets or sets a value indicating whether the image should be flipped vertically.
/// Gets or sets a value indicating whether the image should be flipped vertically. /// </summary>
/// </summary> public Boolean ImageFlipVertically {
public bool ImageFlipVertically { get; set; } get; set;
}
/// <summary>
/// Gets or sets the image annotations using a bitmask (or flags) notation. /// <summary>
/// Apply a bitwise OR to the enumeration to include multiple annotations /// Gets or sets the image annotations using a bitmask (or flags) notation.
/// </summary> /// Apply a bitwise OR to the enumeration to include multiple annotations
public CameraAnnotation ImageAnnotations { get; set; } = CameraAnnotation.None; /// </summary>
public CameraAnnotation ImageAnnotations { get; set; } = CameraAnnotation.None;
/// <summary>
/// Gets or sets the image annotations text. /// <summary>
/// Text may include date/time placeholders by using the '%' character, as used by strftime. /// Gets or sets the image annotations text.
/// Example: ABC %Y-%m-%d %X will output ABC 2015-10-28 20:09:33 /// Text may include date/time placeholders by using the '%' character, as used by strftime.
/// </summary> /// Example: ABC %Y-%m-%d %X will output ABC 2015-10-28 20:09:33
public string ImageAnnotationsText { get; set; } = string.Empty; /// </summary>
public String ImageAnnotationsText { get; set; } = String.Empty;
/// <summary>
/// Gets or sets the font size of the text annotations /// <summary>
/// Default is -1, range is 6 to 160 /// Gets or sets the font size of the text annotations
/// </summary> /// Default is -1, range is 6 to 160
public int ImageAnnotationFontSize { get; set; } = -1; /// </summary>
public Int32 ImageAnnotationFontSize { get; set; } = -1;
/// <summary>
/// Gets or sets the color of the text annotations. /// <summary>
/// </summary> /// Gets or sets the color of the text annotations.
/// <value> /// </summary>
/// The color of the image annotation font. /// <value>
/// </value> /// The color of the image annotation font.
public CameraColor ImageAnnotationFontColor { get; set; } = null; /// </value>
public CameraColor ImageAnnotationFontColor { get; set; } = null;
/// <summary>
/// Gets or sets the background color for text annotations. /// <summary>
/// </summary> /// Gets or sets the background color for text annotations.
/// <value> /// </summary>
/// The image annotation background. /// <value>
/// </value> /// The image annotation background.
public CameraColor ImageAnnotationBackground { get; set; } = null; /// </value>
public CameraColor ImageAnnotationBackground { get; set; } = null;
#endregion
#endregion
#region Interface
#region Interface
/// <summary>
/// Gets the command file executable. /// <summary>
/// </summary> /// Gets the command file executable.
public abstract string CommandName { get; } /// </summary>
public abstract String CommandName {
/// <summary> get;
/// Creates the process arguments. }
/// </summary>
/// <returns>The string that represents the process arguments</returns> /// <summary>
public virtual string CreateProcessArguments() /// Creates the process arguments.
{ /// </summary>
var sb = new StringBuilder(); /// <returns>The string that represents the process arguments</returns>
sb.Append("-o -"); // output to standard output as opposed to a file. public virtual String CreateProcessArguments() {
sb.Append($" -t {(CaptureTimeoutMilliseconds < 0 ? "0" : CaptureTimeoutMilliseconds.ToString(Ci))}"); StringBuilder sb = new StringBuilder();
_ = sb.Append("-o -"); // output to standard output as opposed to a file.
// Basic Width and height _ = sb.Append($" -t {(this.CaptureTimeoutMilliseconds < 0 ? "0" : this.CaptureTimeoutMilliseconds.ToString(Ci))}");
if (CaptureWidth > 0 && CaptureHeight > 0)
{ // Basic Width and height
sb.Append($" -w {CaptureWidth.ToString(Ci)}"); if(this.CaptureWidth > 0 && this.CaptureHeight > 0) {
sb.Append($" -h {CaptureHeight.ToString(Ci)}"); _ = sb.Append($" -w {this.CaptureWidth.ToString(Ci)}");
} _ = sb.Append($" -h {this.CaptureHeight.ToString(Ci)}");
}
// Display Preview
if (CaptureDisplayPreview) // Display Preview
{ if(this.CaptureDisplayPreview) {
if (CaptureDisplayPreviewInFullScreen) if(this.CaptureDisplayPreviewInFullScreen) {
sb.Append(" -f"); _ = sb.Append(" -f");
}
if (CaptureDisplayPreviewOpacity != byte.MaxValue)
sb.Append($" -op {CaptureDisplayPreviewOpacity.ToString(Ci)}"); if(this.CaptureDisplayPreviewOpacity != Byte.MaxValue) {
} _ = sb.Append($" -op {this.CaptureDisplayPreviewOpacity.ToString(Ci)}");
else }
{ } else {
sb.Append(" -n"); // no preview _ = sb.Append(" -n"); // no preview
} }
// Picture Settings // Picture Settings
if (ImageSharpness != 0) if(this.ImageSharpness != 0) {
sb.Append($" -sh {ImageSharpness.Clamp(-100, 100).ToString(Ci)}"); _ = sb.Append($" -sh {this.ImageSharpness.Clamp(-100, 100).ToString(Ci)}");
}
if (ImageContrast != 0)
sb.Append($" -co {ImageContrast.Clamp(-100, 100).ToString(Ci)}"); if(this.ImageContrast != 0) {
_ = sb.Append($" -co {this.ImageContrast.Clamp(-100, 100).ToString(Ci)}");
if (ImageBrightness != 50) }
sb.Append($" -br {ImageBrightness.Clamp(0, 100).ToString(Ci)}");
if(this.ImageBrightness != 50) {
if (ImageSaturation != 0) _ = sb.Append($" -br {this.ImageBrightness.Clamp(0, 100).ToString(Ci)}");
sb.Append($" -sa {ImageSaturation.Clamp(-100, 100).ToString(Ci)}"); }
if (ImageIso >= 100) if(this.ImageSaturation != 0) {
sb.Append($" -ISO {ImageIso.Clamp(100, 800).ToString(Ci)}"); _ = sb.Append($" -sa {this.ImageSaturation.Clamp(-100, 100).ToString(Ci)}");
}
if (CaptureVideoStabilizationEnabled)
sb.Append(" -vs"); if(this.ImageIso >= 100) {
_ = sb.Append($" -ISO {this.ImageIso.Clamp(100, 800).ToString(Ci)}");
if (CaptureExposureCompensation != 0) }
sb.Append($" -ev {CaptureExposureCompensation.Clamp(-10, 10).ToString(Ci)}");
if(this.CaptureVideoStabilizationEnabled) {
if (CaptureExposure != CameraExposureMode.Auto) _ = sb.Append(" -vs");
sb.Append($" -ex {CaptureExposure.ToString().ToLowerInvariant()}"); }
if (CaptureWhiteBalanceControl != CameraWhiteBalanceMode.Auto) if(this.CaptureExposureCompensation != 0) {
sb.Append($" -awb {CaptureWhiteBalanceControl.ToString().ToLowerInvariant()}"); _ = sb.Append($" -ev {this.CaptureExposureCompensation.Clamp(-10, 10).ToString(Ci)}");
}
if (ImageEffect != CameraImageEffect.None)
sb.Append($" -ifx {ImageEffect.ToString().ToLowerInvariant()}"); if(this.CaptureExposure != CameraExposureMode.Auto) {
_ = sb.Append($" -ex {this.CaptureExposure.ToString().ToLowerInvariant()}");
if (ImageColorEffectU >= 0 && ImageColorEffectV >= 0) }
{
sb.Append( if(this.CaptureWhiteBalanceControl != CameraWhiteBalanceMode.Auto) {
$" -cfx {ImageColorEffectU.Clamp(0, 255).ToString(Ci)}:{ImageColorEffectV.Clamp(0, 255).ToString(Ci)}"); _ = sb.Append($" -awb {this.CaptureWhiteBalanceControl.ToString().ToLowerInvariant()}");
} }
if (CaptureMeteringMode != CameraMeteringMode.Average) if(this.ImageEffect != CameraImageEffect.None) {
sb.Append($" -mm {CaptureMeteringMode.ToString().ToLowerInvariant()}"); _ = sb.Append($" -ifx {this.ImageEffect.ToString().ToLowerInvariant()}");
}
if (ImageRotation != CameraImageRotation.None)
sb.Append($" -rot {((int)ImageRotation).ToString(Ci)}"); if(this.ImageColorEffectU >= 0 && this.ImageColorEffectV >= 0) {
_ = sb.Append(
if (ImageFlipHorizontally) $" -cfx {this.ImageColorEffectU.Clamp(0, 255).ToString(Ci)}:{this.ImageColorEffectV.Clamp(0, 255).ToString(Ci)}");
sb.Append(" -hf"); }
if (ImageFlipVertically) if(this.CaptureMeteringMode != CameraMeteringMode.Average) {
sb.Append(" -vf"); _ = sb.Append($" -mm {this.CaptureMeteringMode.ToString().ToLowerInvariant()}");
}
if (CaptureSensorRoi.IsDefault == false)
sb.Append($" -roi {CaptureSensorRoi}"); if(this.ImageRotation != CameraImageRotation.None) {
_ = sb.Append($" -rot {((Int32)this.ImageRotation).ToString(Ci)}");
if (CaptureShutterSpeedMicroseconds > 0) }
sb.Append($" -ss {CaptureShutterSpeedMicroseconds.Clamp(0, 6000000).ToString(Ci)}");
if(this.ImageFlipHorizontally) {
if (CaptureDynamicRangeCompensation != CameraDynamicRangeCompensation.Off) _ = sb.Append(" -hf");
sb.Append($" -drc {CaptureDynamicRangeCompensation.ToString().ToLowerInvariant()}"); }
if (CaptureWhiteBalanceControl == CameraWhiteBalanceMode.Off && if(this.ImageFlipVertically) {
(CaptureWhiteBalanceGainBlue != 0M || CaptureWhiteBalanceGainRed != 0M)) _ = sb.Append(" -vf");
sb.Append($" -awbg {CaptureWhiteBalanceGainBlue.ToString(Ci)},{CaptureWhiteBalanceGainRed.ToString(Ci)}"); }
if (ImageAnnotationFontSize > 0) if(this.CaptureSensorRoi.IsDefault == false) {
{ _ = sb.Append($" -roi {this.CaptureSensorRoi}");
sb.Append($" -ae {ImageAnnotationFontSize.Clamp(6, 160).ToString(Ci)}"); }
sb.Append($",{(ImageAnnotationFontColor == null ? "0xff" : ImageAnnotationFontColor.ToYuvHex(true))}");
if(this.CaptureShutterSpeedMicroseconds > 0) {
if (ImageAnnotationBackground != null) _ = sb.Append($" -ss {this.CaptureShutterSpeedMicroseconds.Clamp(0, 6000000).ToString(Ci)}");
{ }
ImageAnnotations |= CameraAnnotation.SolidBackground;
sb.Append($",{ImageAnnotationBackground.ToYuvHex(true)}"); if(this.CaptureDynamicRangeCompensation != CameraDynamicRangeCompensation.Off) {
} _ = sb.Append($" -drc {this.CaptureDynamicRangeCompensation.ToString().ToLowerInvariant()}");
} }
if (ImageAnnotations != CameraAnnotation.None) if(this.CaptureWhiteBalanceControl == CameraWhiteBalanceMode.Off &&
sb.Append($" -a {((int)ImageAnnotations).ToString(Ci)}"); (this.CaptureWhiteBalanceGainBlue != 0M || this.CaptureWhiteBalanceGainRed != 0M)) {
_ = sb.Append($" -awbg {this.CaptureWhiteBalanceGainBlue.ToString(Ci)},{this.CaptureWhiteBalanceGainRed.ToString(Ci)}");
if (string.IsNullOrWhiteSpace(ImageAnnotationsText) == false) }
sb.Append($" -a \"{ImageAnnotationsText.Replace("\"", "'")}\"");
if(this.ImageAnnotationFontSize > 0) {
return sb.ToString(); _ = sb.Append($" -ae {this.ImageAnnotationFontSize.Clamp(6, 160).ToString(Ci)}");
} _ = sb.Append($",{(this.ImageAnnotationFontColor == null ? "0xff" : this.ImageAnnotationFontColor.ToYuvHex(true))}");
#endregion if(this.ImageAnnotationBackground != null) {
} this.ImageAnnotations |= CameraAnnotation.SolidBackground;
_ = sb.Append($",{this.ImageAnnotationBackground.ToYuvHex(true)}");
}
}
if(this.ImageAnnotations != CameraAnnotation.None) {
_ = sb.Append($" -a {((Int32)this.ImageAnnotations).ToString(Ci)}");
}
if(String.IsNullOrWhiteSpace(this.ImageAnnotationsText) == false) {
_ = sb.Append($" -a \"{this.ImageAnnotationsText.Replace("\"", "'")}\"");
}
return sb.ToString();
}
#endregion
}
} }
@@ -1,120 +1,121 @@
namespace Unosquare.RaspberryIO.Camera using Unosquare.Swan;
{ using System;
using Swan; using System.Collections.Generic;
using System; using System.Text;
using System.Collections.Generic;
using System.Text; namespace Unosquare.RaspberryIO.Camera {
/// <summary>
/// Defines a wrapper for the raspistill program and its settings (command-line arguments)
/// </summary>
/// <seealso cref="CameraSettingsBase" />
public class CameraStillSettings : CameraSettingsBase {
private Int32 _rotate;
/// <inheritdoc />
public override String CommandName => "raspistill";
/// <summary> /// <summary>
/// Defines a wrapper for the raspistill program and its settings (command-line arguments) /// Gets or sets a value indicating whether the preview window (if enabled) uses native capture resolution
/// This may slow down preview FPS
/// </summary> /// </summary>
/// <seealso cref="CameraSettingsBase" /> public Boolean CaptureDisplayPreviewAtResolution { get; set; } = false;
public class CameraStillSettings : CameraSettingsBase
{ /// <summary>
private int _rotate; /// Gets or sets the encoding format the hardware will use for the output.
/// </summary>
/// <inheritdoc /> public CameraImageEncodingFormat CaptureEncoding { get; set; } = CameraImageEncodingFormat.Jpg;
public override string CommandName => "raspistill";
/// <summary>
/// <summary> /// Gets or sets the quality for JPEG only encoding mode.
/// Gets or sets a value indicating whether the preview window (if enabled) uses native capture resolution /// Value ranges from 0 to 100
/// This may slow down preview FPS /// </summary>
/// </summary> public Int32 CaptureJpegQuality { get; set; } = 90;
public bool CaptureDisplayPreviewAtResolution { get; set; } = false;
/// <summary>
/// <summary> /// Gets or sets a value indicating whether the JPEG encoder should add raw bayer metadata.
/// Gets or sets the encoding format the hardware will use for the output. /// </summary>
/// </summary> public Boolean CaptureJpegIncludeRawBayerMetadata { get; set; } = false;
public CameraImageEncodingFormat CaptureEncoding { get; set; } = CameraImageEncodingFormat.Jpg;
/// <summary>
/// <summary> /// JPEG EXIF data
/// Gets or sets the quality for JPEG only encoding mode. /// Keys and values must be already properly escaped. Otherwise the command will fail.
/// Value ranges from 0 to 100 /// </summary>
/// </summary> public Dictionary<String, String> CaptureJpegExtendedInfo { get; } = new Dictionary<String, String>();
public int CaptureJpegQuality { get; set; } = 90;
/// <summary>
/// <summary> /// Gets or sets a value indicating whether [horizontal flip].
/// Gets or sets a value indicating whether the JPEG encoder should add raw bayer metadata. /// </summary>
/// </summary> /// <value>
public bool CaptureJpegIncludeRawBayerMetadata { get; set; } = false; /// <c>true</c> if [horizontal flip]; otherwise, <c>false</c>.
/// </value>
/// <summary> public Boolean HorizontalFlip { get; set; } = false;
/// JPEG EXIF data
/// Keys and values must be already properly escaped. Otherwise the command will fail. /// <summary>
/// </summary> /// Gets or sets a value indicating whether [vertical flip].
public Dictionary<string, string> CaptureJpegExtendedInfo { get; } = new Dictionary<string, string>(); /// </summary>
/// <value>
/// <summary> /// <c>true</c> if [vertical flip]; otherwise, <c>false</c>.
/// Gets or sets a value indicating whether [horizontal flip]. /// </value>
/// </summary> public Boolean VerticalFlip { get; set; } = false;
/// <value>
/// <c>true</c> if [horizontal flip]; otherwise, <c>false</c>. /// <summary>
/// </value> /// Gets or sets the rotation.
public bool HorizontalFlip { get; set; } = false; /// </summary>
/// <exception cref="ArgumentOutOfRangeException">Valid range 0-359</exception>
/// <summary> public Int32 Rotation {
/// Gets or sets a value indicating whether [vertical flip]. get => this._rotate;
/// </summary> set {
/// <value> if(value < 0 || value > 359) {
/// <c>true</c> if [vertical flip]; otherwise, <c>false</c>. throw new ArgumentOutOfRangeException(nameof(value), "Valid range 0-359");
/// </value> }
public bool VerticalFlip { get; set; } = false;
this._rotate = value;
/// <summary> }
/// Gets or sets the rotation. }
/// </summary>
/// <exception cref="ArgumentOutOfRangeException">Valid range 0-359</exception> /// <inheritdoc />
public int Rotation public override String CreateProcessArguments() {
{ StringBuilder sb = new StringBuilder(base.CreateProcessArguments());
get => _rotate; _ = sb.Append($" -e {this.CaptureEncoding.ToString().ToLowerInvariant()}");
set
{ // JPEG Encoder specific arguments
if (value < 0 || value > 359) if(this.CaptureEncoding == CameraImageEncodingFormat.Jpg) {
{ _ = sb.Append($" -q {this.CaptureJpegQuality.Clamp(0, 100).ToString(Ci)}");
throw new ArgumentOutOfRangeException(nameof(value), "Valid range 0-359");
} if(this.CaptureJpegIncludeRawBayerMetadata) {
_ = sb.Append(" -r");
_rotate = value; }
}
} // JPEG EXIF data
if(this.CaptureJpegExtendedInfo.Count > 0) {
/// <inheritdoc /> foreach(KeyValuePair<String, String> kvp in this.CaptureJpegExtendedInfo) {
public override string CreateProcessArguments() if(String.IsNullOrWhiteSpace(kvp.Key) || String.IsNullOrWhiteSpace(kvp.Value)) {
{ continue;
var sb = new StringBuilder(base.CreateProcessArguments()); }
sb.Append($" -e {CaptureEncoding.ToString().ToLowerInvariant()}");
_ = sb.Append($" -x \"{kvp.Key.Replace("\"", "'")}={kvp.Value.Replace("\"", "'")}\"");
// JPEG Encoder specific arguments }
if (CaptureEncoding == CameraImageEncodingFormat.Jpg) }
{ }
sb.Append($" -q {CaptureJpegQuality.Clamp(0, 100).ToString(Ci)}");
// Display preview settings
if (CaptureJpegIncludeRawBayerMetadata) if(this.CaptureDisplayPreview && this.CaptureDisplayPreviewAtResolution) {
sb.Append(" -r"); _ = sb.Append(" -fp");
}
// JPEG EXIF data
if (CaptureJpegExtendedInfo.Count > 0) if(this.Rotation != 0) {
{ _ = sb.Append($" -rot {this.Rotation}");
foreach (var kvp in CaptureJpegExtendedInfo) }
{
if (string.IsNullOrWhiteSpace(kvp.Key) || string.IsNullOrWhiteSpace(kvp.Value)) if(this.HorizontalFlip) {
continue; _ = sb.Append(" -hf");
}
sb.Append($" -x \"{kvp.Key.Replace("\"", "'")}={kvp.Value.Replace("\"", "'")}\"");
} if(this.VerticalFlip) {
} _ = sb.Append(" -vf");
} }
// Display preview settings return sb.ToString();
if (CaptureDisplayPreview && CaptureDisplayPreviewAtResolution) sb.Append(" -fp"); }
}
if (Rotation != 0) sb.Append($" -rot {Rotation}");
if (HorizontalFlip) sb.Append(" -hf");
if (VerticalFlip) sb.Append(" -vf");
return sb.ToString();
}
}
} }
@@ -1,94 +1,98 @@
namespace Unosquare.RaspberryIO.Camera using Unosquare.Swan;
{ using System;
using Swan; using System.Text;
using System.Text;
namespace Unosquare.RaspberryIO.Camera {
/// <summary>
/// Represents the raspivid camera settings for video capture functionality
/// </summary>
/// <seealso cref="CameraSettingsBase" />
public class CameraVideoSettings : CameraSettingsBase {
/// <inheritdoc />
public override String CommandName => "raspivid";
/// <summary> /// <summary>
/// Represents the raspivid camera settings for video capture functionality /// Use bits per second, so 10Mbits/s would be -b 10000000. For H264, 1080p30 a high quality bitrate would be 15Mbits/s or more.
/// Maximum bitrate is 25Mbits/s (-b 25000000), but much over 17Mbits/s won't show noticeable improvement at 1080p30.
/// Default -1
/// </summary> /// </summary>
/// <seealso cref="CameraSettingsBase" /> public Int32 CaptureBitrate { get; set; } = -1;
public class CameraVideoSettings : CameraSettingsBase
{ /// <summary>
/// <inheritdoc /> /// Gets or sets the framerate.
public override string CommandName => "raspivid"; /// Default 25, range 2 to 30
/// </summary>
/// <summary> public Int32 CaptureFramerate { get; set; } = 25;
/// Use bits per second, so 10Mbits/s would be -b 10000000. For H264, 1080p30 a high quality bitrate would be 15Mbits/s or more.
/// Maximum bitrate is 25Mbits/s (-b 25000000), but much over 17Mbits/s won't show noticeable improvement at 1080p30. /// <summary>
/// Default -1 /// Sets the intra refresh period (GoP) rate for the recorded video. H264 video uses a complete frame (I-frame) every intra
/// </summary> /// refresh period, from which subsequent frames are based. This option specifies the number of frames between each I-frame.
public int CaptureBitrate { get; set; } = -1; /// Larger numbers here will reduce the size of the resulting video, and smaller numbers make the stream less error-prone.
/// </summary>
/// <summary> public Int32 CaptureKeyframeRate { get; set; } = 25;
/// Gets or sets the framerate.
/// Default 25, range 2 to 30 /// <summary>
/// </summary> /// Sets the initial quantisation parameter for the stream. Varies from approximately 10 to 40, and will greatly affect
public int CaptureFramerate { get; set; } = 25; /// the quality of the recording. Higher values reduce quality and decrease file size. Combine this setting with a
/// bitrate of 0 to set a completely variable bitrate.
/// <summary> /// </summary>
/// Sets the intra refresh period (GoP) rate for the recorded video. H264 video uses a complete frame (I-frame) every intra public Int32 CaptureQuantisation { get; set; } = 23;
/// refresh period, from which subsequent frames are based. This option specifies the number of frames between each I-frame.
/// Larger numbers here will reduce the size of the resulting video, and smaller numbers make the stream less error-prone. /// <summary>
/// </summary> /// Gets or sets the profile.
public int CaptureKeyframeRate { get; set; } = 25; /// Sets the H264 profile to be used for the encoding.
/// Default is Main mode
/// <summary> /// </summary>
/// Sets the initial quantisation parameter for the stream. Varies from approximately 10 to 40, and will greatly affect public CameraH264Profile CaptureProfile { get; set; } = CameraH264Profile.Main;
/// the quality of the recording. Higher values reduce quality and decrease file size. Combine this setting with a
/// bitrate of 0 to set a completely variable bitrate. /// <summary>
/// </summary> /// Forces the stream to include PPS and SPS headers on every I-frame. Needed for certain streaming cases
public int CaptureQuantisation { get; set; } = 23; /// e.g. Apple HLS. These headers are small, so don't greatly increase the file size.
/// </summary>
/// <summary> /// <value>
/// Gets or sets the profile. /// <c>true</c> if [interleave headers]; otherwise, <c>false</c>.
/// Sets the H264 profile to be used for the encoding. /// </value>
/// Default is Main mode public Boolean CaptureInterleaveHeaders { get; set; } = true;
/// </summary>
public CameraH264Profile CaptureProfile { get; set; } = CameraH264Profile.Main; /// <summary>
/// Switch on an option to display the preview after compression. This will show any compression artefacts in the preview window. In normal operation,
/// <summary> /// the preview will show the camera output prior to being compressed. This option is not guaranteed to work in future releases.
/// Forces the stream to include PPS and SPS headers on every I-frame. Needed for certain streaming cases /// </summary>
/// e.g. Apple HLS. These headers are small, so don't greatly increase the file size. /// <value>
/// </summary> /// <c>true</c> if [capture display preview encoded]; otherwise, <c>false</c>.
/// <value> /// </value>
/// <c>true</c> if [interleave headers]; otherwise, <c>false</c>. public Boolean CaptureDisplayPreviewEncoded { get; set; } = false;
/// </value>
public bool CaptureInterleaveHeaders { get; set; } = true; /// <inheritdoc />
public override String CreateProcessArguments() {
/// <summary> StringBuilder sb = new StringBuilder(base.CreateProcessArguments());
/// Switch on an option to display the preview after compression. This will show any compression artefacts in the preview window. In normal operation,
/// the preview will show the camera output prior to being compressed. This option is not guaranteed to work in future releases. _ = sb.Append($" -pf {this.CaptureProfile.ToString().ToLowerInvariant()}");
/// </summary> if(this.CaptureBitrate < 0) {
/// <value> _ = sb.Append($" -b {this.CaptureBitrate.Clamp(0, 25000000).ToString(Ci)}");
/// <c>true</c> if [capture display preview encoded]; otherwise, <c>false</c>. }
/// </value>
public bool CaptureDisplayPreviewEncoded { get; set; } = false; if(this.CaptureFramerate >= 2) {
_ = sb.Append($" -fps {this.CaptureFramerate.Clamp(2, 30).ToString(Ci)}");
/// <inheritdoc /> }
public override string CreateProcessArguments()
{ if(this.CaptureDisplayPreview && this.CaptureDisplayPreviewEncoded) {
var sb = new StringBuilder(base.CreateProcessArguments()); _ = sb.Append(" -e");
}
sb.Append($" -pf {CaptureProfile.ToString().ToLowerInvariant()}");
if (CaptureBitrate < 0) if(this.CaptureKeyframeRate > 0) {
sb.Append($" -b {CaptureBitrate.Clamp(0, 25000000).ToString(Ci)}"); _ = sb.Append($" -g {this.CaptureKeyframeRate.ToString(Ci)}");
}
if (CaptureFramerate >= 2)
sb.Append($" -fps {CaptureFramerate.Clamp(2, 30).ToString(Ci)}"); if(this.CaptureQuantisation >= 0) {
_ = sb.Append($" -qp {this.CaptureQuantisation.Clamp(0, 40).ToString(Ci)}");
if (CaptureDisplayPreview && CaptureDisplayPreviewEncoded) }
sb.Append(" -e");
if(this.CaptureInterleaveHeaders) {
if (CaptureKeyframeRate > 0) _ = sb.Append(" -ih");
sb.Append($" -g {CaptureKeyframeRate.ToString(Ci)}"); }
if (CaptureQuantisation >= 0) return sb.ToString();
sb.Append($" -qp {CaptureQuantisation.Clamp(0, 40).ToString(Ci)}"); }
}
if (CaptureInterleaveHeaders)
sb.Append(" -ih");
return sb.ToString();
}
}
} }
+394 -404
View File
@@ -1,423 +1,413 @@
namespace Unosquare.RaspberryIO.Camera using System;
{
using System; namespace Unosquare.RaspberryIO.Camera {
/// <summary>
/// Defines the available encoding formats for the Raspberry Pi camera module
/// </summary>
public enum CameraImageEncodingFormat {
/// <summary> /// <summary>
/// Defines the available encoding formats for the Raspberry Pi camera module /// The JPG
/// </summary> /// </summary>
public enum CameraImageEncodingFormat Jpg,
{
/// <summary>
/// The JPG
/// </summary>
Jpg,
/// <summary>
/// The BMP
/// </summary>
Bmp,
/// <summary>
/// The GIF
/// </summary>
Gif,
/// <summary>
/// The PNG
/// </summary>
Png,
}
/// <summary> /// <summary>
/// Defines the different exposure modes for the Raspberry Pi's camera module /// The BMP
/// </summary> /// </summary>
public enum CameraExposureMode Bmp,
{
/// <summary>
/// The automatic
/// </summary>
Auto,
/// <summary>
/// The night
/// </summary>
Night,
/// <summary>
/// The night preview
/// </summary>
NightPreview,
/// <summary>
/// The backlight
/// </summary>
Backlight,
/// <summary>
/// The spotlight
/// </summary>
Spotlight,
/// <summary>
/// The sports
/// </summary>
Sports,
/// <summary>
/// The snow
/// </summary>
Snow,
/// <summary>
/// The beach
/// </summary>
Beach,
/// <summary>
/// The very long
/// </summary>
VeryLong,
/// <summary>
/// The fixed FPS
/// </summary>
FixedFps,
/// <summary>
/// The anti shake
/// </summary>
AntiShake,
/// <summary>
/// The fireworks
/// </summary>
Fireworks
}
/// <summary> /// <summary>
/// Defines the different AWB (Auto White Balance) modes for the Raspberry Pi's camera module /// The GIF
/// </summary> /// </summary>
public enum CameraWhiteBalanceMode Gif,
{
/// <summary>
/// No white balance
/// </summary>
Off,
/// <summary>
/// The automatic
/// </summary>
Auto,
/// <summary>
/// The sun
/// </summary>
Sun,
/// <summary>
/// The cloud
/// </summary>
Cloud,
/// <summary>
/// The shade
/// </summary>
Shade,
/// <summary>
/// The tungsten
/// </summary>
Tungsten,
/// <summary>
/// The fluorescent
/// </summary>
Fluorescent,
/// <summary>
/// The incandescent
/// </summary>
Incandescent,
/// <summary>
/// The flash
/// </summary>
Flash,
/// <summary>
/// The horizon
/// </summary>
Horizon
}
/// <summary> /// <summary>
/// Defines the available image effects for the Raspberry Pi's camera module /// The PNG
/// </summary> /// </summary>
public enum CameraImageEffect Png,
{ }
/// <summary>
/// No effect /// <summary>
/// </summary> /// Defines the different exposure modes for the Raspberry Pi's camera module
None, /// </summary>
public enum CameraExposureMode {
/// <summary>
/// The negative
/// </summary>
Negative,
/// <summary>
/// The solarise
/// </summary>
Solarise,
/// <summary>
/// The whiteboard
/// </summary>
Whiteboard,
/// <summary>
/// The blackboard
/// </summary>
Blackboard,
/// <summary>
/// The sketch
/// </summary>
Sketch,
/// <summary>
/// The denoise
/// </summary>
Denoise,
/// <summary>
/// The emboss
/// </summary>
Emboss,
/// <summary>
/// The oil paint
/// </summary>
OilPaint,
/// <summary>
/// The hatch
/// </summary>
Hatch,
/// <summary>
/// Graphite Pen
/// </summary>
GPen,
/// <summary>
/// The pastel
/// </summary>
Pastel,
/// <summary>
/// The water colour
/// </summary>
WaterColour,
/// <summary>
/// The film
/// </summary>
Film,
/// <summary>
/// The blur
/// </summary>
Blur,
/// <summary>
/// The saturation
/// </summary>
Saturation,
/// <summary>
/// The solour swap
/// </summary>
SolourSwap,
/// <summary>
/// The washed out
/// </summary>
WashedOut,
/// <summary>
/// The colour point
/// </summary>
ColourPoint,
/// <summary>
/// The colour balance
/// </summary>
ColourBalance,
/// <summary>
/// The cartoon
/// </summary>
Cartoon
}
/// <summary> /// <summary>
/// Defines the different metering modes for the Raspberry Pi's camera module /// The automatic
/// </summary> /// </summary>
public enum CameraMeteringMode Auto,
{
/// <summary>
/// The average
/// </summary>
Average,
/// <summary>
/// The spot
/// </summary>
Spot,
/// <summary>
/// The backlit
/// </summary>
Backlit,
/// <summary>
/// The matrix
/// </summary>
Matrix,
}
/// <summary> /// <summary>
/// Defines the different image rotation modes for the Raspberry Pi's camera module /// The night
/// </summary> /// </summary>
public enum CameraImageRotation Night,
{
/// <summary>
/// No rerotation
/// </summary>
None = 0,
/// <summary>
/// 90 Degrees
/// </summary>
Degrees90 = 90,
/// <summary>
/// 180 Degrees
/// </summary>
Degrees180 = 180,
/// <summary>
/// 270 degrees
/// </summary>
Degrees270 = 270
}
/// <summary> /// <summary>
/// Defines the different DRC (Dynamic Range Compensation) modes for the Raspberry Pi's camera module /// The night preview
/// Helpful for low light photos
/// </summary> /// </summary>
public enum CameraDynamicRangeCompensation NightPreview,
{
/// <summary>
/// The off setting
/// </summary>
Off,
/// <summary>
/// The low
/// </summary>
Low,
/// <summary>
/// The medium
/// </summary>
Medium,
/// <summary>
/// The high
/// </summary>
High
}
/// <summary> /// <summary>
/// Defines the bit-wise mask flags for the available annotation elements for the Raspberry Pi's camera module /// The backlight
/// </summary> /// </summary>
[Flags] Backlight,
public enum CameraAnnotation
{
/// <summary>
/// The none
/// </summary>
None = 0,
/// <summary>
/// The time
/// </summary>
Time = 4,
/// <summary>
/// The date
/// </summary>
Date = 8,
/// <summary>
/// The shutter settings
/// </summary>
ShutterSettings = 16,
/// <summary>
/// The caf settings
/// </summary>
CafSettings = 32,
/// <summary>
/// The gain settings
/// </summary>
GainSettings = 64,
/// <summary>
/// The lens settings
/// </summary>
LensSettings = 128,
/// <summary>
/// The motion settings
/// </summary>
MotionSettings = 256,
/// <summary>
/// The frame number
/// </summary>
FrameNumber = 512,
/// <summary>
/// The solid background
/// </summary>
SolidBackground = 1024,
}
/// <summary> /// <summary>
/// Defines the different H.264 encoding profiles to be used when capturing video. /// The spotlight
/// </summary> /// </summary>
public enum CameraH264Profile Spotlight,
{
/// <summary> /// <summary>
/// BP: Primarily for lower-cost applications with limited computing resources, /// The sports
/// this profile is used widely in videoconferencing and mobile applications. /// </summary>
/// </summary> Sports,
Baseline,
/// <summary>
/// <summary> /// The snow
/// MP: Originally intended as the mainstream consumer profile for broadcast /// </summary>
/// and storage applications, the importance of this profile faded when the High profile was developed for those applications. Snow,
/// </summary>
Main, /// <summary>
/// The beach
/// <summary> /// </summary>
/// HiP: The primary profile for broadcast and disc storage applications, particularly Beach,
/// for high-definition television applications (this is the profile adopted into HD DVD and Blu-ray Disc, for example).
/// </summary> /// <summary>
High /// The very long
} /// </summary>
VeryLong,
/// <summary>
/// The fixed FPS
/// </summary>
FixedFps,
/// <summary>
/// The anti shake
/// </summary>
AntiShake,
/// <summary>
/// The fireworks
/// </summary>
Fireworks
}
/// <summary>
/// Defines the different AWB (Auto White Balance) modes for the Raspberry Pi's camera module
/// </summary>
public enum CameraWhiteBalanceMode {
/// <summary>
/// No white balance
/// </summary>
Off,
/// <summary>
/// The automatic
/// </summary>
Auto,
/// <summary>
/// The sun
/// </summary>
Sun,
/// <summary>
/// The cloud
/// </summary>
Cloud,
/// <summary>
/// The shade
/// </summary>
Shade,
/// <summary>
/// The tungsten
/// </summary>
Tungsten,
/// <summary>
/// The fluorescent
/// </summary>
Fluorescent,
/// <summary>
/// The incandescent
/// </summary>
Incandescent,
/// <summary>
/// The flash
/// </summary>
Flash,
/// <summary>
/// The horizon
/// </summary>
Horizon
}
/// <summary>
/// Defines the available image effects for the Raspberry Pi's camera module
/// </summary>
public enum CameraImageEffect {
/// <summary>
/// No effect
/// </summary>
None,
/// <summary>
/// The negative
/// </summary>
Negative,
/// <summary>
/// The solarise
/// </summary>
Solarise,
/// <summary>
/// The whiteboard
/// </summary>
Whiteboard,
/// <summary>
/// The blackboard
/// </summary>
Blackboard,
/// <summary>
/// The sketch
/// </summary>
Sketch,
/// <summary>
/// The denoise
/// </summary>
Denoise,
/// <summary>
/// The emboss
/// </summary>
Emboss,
/// <summary>
/// The oil paint
/// </summary>
OilPaint,
/// <summary>
/// The hatch
/// </summary>
Hatch,
/// <summary>
/// Graphite Pen
/// </summary>
GPen,
/// <summary>
/// The pastel
/// </summary>
Pastel,
/// <summary>
/// The water colour
/// </summary>
WaterColour,
/// <summary>
/// The film
/// </summary>
Film,
/// <summary>
/// The blur
/// </summary>
Blur,
/// <summary>
/// The saturation
/// </summary>
Saturation,
/// <summary>
/// The solour swap
/// </summary>
SolourSwap,
/// <summary>
/// The washed out
/// </summary>
WashedOut,
/// <summary>
/// The colour point
/// </summary>
ColourPoint,
/// <summary>
/// The colour balance
/// </summary>
ColourBalance,
/// <summary>
/// The cartoon
/// </summary>
Cartoon
}
/// <summary>
/// Defines the different metering modes for the Raspberry Pi's camera module
/// </summary>
public enum CameraMeteringMode {
/// <summary>
/// The average
/// </summary>
Average,
/// <summary>
/// The spot
/// </summary>
Spot,
/// <summary>
/// The backlit
/// </summary>
Backlit,
/// <summary>
/// The matrix
/// </summary>
Matrix,
}
/// <summary>
/// Defines the different image rotation modes for the Raspberry Pi's camera module
/// </summary>
public enum CameraImageRotation {
/// <summary>
/// No rerotation
/// </summary>
None = 0,
/// <summary>
/// 90 Degrees
/// </summary>
Degrees90 = 90,
/// <summary>
/// 180 Degrees
/// </summary>
Degrees180 = 180,
/// <summary>
/// 270 degrees
/// </summary>
Degrees270 = 270
}
/// <summary>
/// Defines the different DRC (Dynamic Range Compensation) modes for the Raspberry Pi's camera module
/// Helpful for low light photos
/// </summary>
public enum CameraDynamicRangeCompensation {
/// <summary>
/// The off setting
/// </summary>
Off,
/// <summary>
/// The low
/// </summary>
Low,
/// <summary>
/// The medium
/// </summary>
Medium,
/// <summary>
/// The high
/// </summary>
High
}
/// <summary>
/// Defines the bit-wise mask flags for the available annotation elements for the Raspberry Pi's camera module
/// </summary>
[Flags]
public enum CameraAnnotation {
/// <summary>
/// The none
/// </summary>
None = 0,
/// <summary>
/// The time
/// </summary>
Time = 4,
/// <summary>
/// The date
/// </summary>
Date = 8,
/// <summary>
/// The shutter settings
/// </summary>
ShutterSettings = 16,
/// <summary>
/// The caf settings
/// </summary>
CafSettings = 32,
/// <summary>
/// The gain settings
/// </summary>
GainSettings = 64,
/// <summary>
/// The lens settings
/// </summary>
LensSettings = 128,
/// <summary>
/// The motion settings
/// </summary>
MotionSettings = 256,
/// <summary>
/// The frame number
/// </summary>
FrameNumber = 512,
/// <summary>
/// The solid background
/// </summary>
SolidBackground = 1024,
}
/// <summary>
/// Defines the different H.264 encoding profiles to be used when capturing video.
/// </summary>
public enum CameraH264Profile {
/// <summary>
/// BP: Primarily for lower-cost applications with limited computing resources,
/// this profile is used widely in videoconferencing and mobile applications.
/// </summary>
Baseline,
/// <summary>
/// MP: Originally intended as the mainstream consumer profile for broadcast
/// and storage applications, the importance of this profile faded when the High profile was developed for those applications.
/// </summary>
Main,
/// <summary>
/// HiP: The primary profile for broadcast and disc storage applications, particularly
/// for high-definition television applications (this is the profile adopted into HD DVD and Blu-ray Disc, for example).
/// </summary>
High
}
} }
+63 -77
View File
@@ -1,80 +1,66 @@
namespace Unosquare.RaspberryIO.Computer using Unosquare.Swan.Abstractions;
{ using System.Globalization;
using Swan.Abstractions; using System.IO;
using System.Globalization; using System;
using System.IO;
namespace Unosquare.RaspberryIO.Computer {
/// <summary>
/// The Official Raspberry Pi 7-inch touch display from the foundation
/// Some docs available here:
/// http://forums.pimoroni.com/t/official-7-raspberry-pi-touch-screen-faq/959
/// </summary>
public class DsiDisplay : SingletonBase<DsiDisplay> {
private const String BacklightFilename = "/sys/class/backlight/rpi_backlight/bl_power";
private const String BrightnessFilename = "/sys/class/backlight/rpi_backlight/brightness";
/// <summary> /// <summary>
/// The Official Raspberry Pi 7-inch touch display from the foundation /// Prevents a default instance of the <see cref="DsiDisplay"/> class from being created.
/// Some docs available here:
/// http://forums.pimoroni.com/t/official-7-raspberry-pi-touch-screen-faq/959
/// </summary> /// </summary>
public class DsiDisplay : SingletonBase<DsiDisplay> private DsiDisplay() {
{ // placeholder
private const string BacklightFilename = "/sys/class/backlight/rpi_backlight/bl_power"; }
private const string BrightnessFilename = "/sys/class/backlight/rpi_backlight/brightness";
/// <summary>
/// <summary> /// Gets a value indicating whether the Pi Foundation Display files are present.
/// Prevents a default instance of the <see cref="DsiDisplay"/> class from being created. /// </summary>
/// </summary> /// <value>
private DsiDisplay() /// <c>true</c> if this instance is present; otherwise, <c>false</c>.
{ /// </value>
// placeholder public Boolean IsPresent => File.Exists(BrightnessFilename);
}
/// <summary>
/// <summary> /// Gets or sets the brightness of the DSI display via filesystem.
/// Gets a value indicating whether the Pi Foundation Display files are present. /// </summary>
/// </summary> /// <value>
/// <value> /// The brightness.
/// <c>true</c> if this instance is present; otherwise, <c>false</c>. /// </value>
/// </value> public Byte Brightness {
public bool IsPresent => File.Exists(BrightnessFilename); get => this.IsPresent == false ? (Byte)0 : Byte.TryParse(File.ReadAllText(BrightnessFilename).Trim(), out Byte brightness) ? brightness : (Byte)0;
set {
/// <summary> if(this.IsPresent == false) {
/// Gets or sets the brightness of the DSI display via filesystem. return;
/// </summary> }
/// <value>
/// The brightness. File.WriteAllText(BrightnessFilename, value.ToString(CultureInfo.InvariantCulture));
/// </value> }
public byte Brightness }
{
get /// <summary>
{ /// Gets or sets a value indicating whether the backlight of the DSI display on.
if (IsPresent == false) return 0; /// This operation is performed via the file system
/// </summary>
return byte.TryParse(File.ReadAllText(BrightnessFilename).Trim(), out var brightness) ? brightness : (byte)0; /// <value>
} /// <c>true</c> if this instance is backlight on; otherwise, <c>false</c>.
set /// </value>
{ public Boolean IsBacklightOn {
if (IsPresent == false) return; get => this.IsPresent == false ? false : Int32.TryParse(File.ReadAllText(BacklightFilename).Trim(), out Int32 backlight) ? backlight == 0 : false;
File.WriteAllText(BrightnessFilename, value.ToString(CultureInfo.InvariantCulture)); set {
} if(this.IsPresent == false) {
} return;
}
/// <summary>
/// Gets or sets a value indicating whether the backlight of the DSI display on. File.WriteAllText(BacklightFilename, value ? "0" : "1");
/// This operation is performed via the file system }
/// </summary> }
/// <value> }
/// <c>true</c> if this instance is backlight on; otherwise, <c>false</c>.
/// </value>
public bool IsBacklightOn
{
get
{
if (IsPresent == false) return false;
if (int.TryParse(File.ReadAllText(BacklightFilename).Trim(), out var backlight))
return backlight == 0;
return false;
}
set
{
if (IsPresent == false) return;
File.WriteAllText(BacklightFilename, value ? "0" : "1");
}
}
}
} }
@@ -1,40 +1,51 @@
namespace Unosquare.RaspberryIO.Computer using System;
{ using System.Net;
using System.Net;
namespace Unosquare.RaspberryIO.Computer {
/// <summary>
/// Represents a Network Adapter
/// </summary>
public class NetworkAdapterInfo {
/// <summary> /// <summary>
/// Represents a Network Adapter /// Gets the name.
/// </summary> /// </summary>
public class NetworkAdapterInfo public String Name {
{ get; internal set;
/// <summary> }
/// Gets the name.
/// </summary> /// <summary>
public string Name { get; internal set; } /// Gets the IP V4 address.
/// </summary>
/// <summary> public IPAddress IPv4 {
/// Gets the IP V4 address. get; internal set;
/// </summary> }
public IPAddress IPv4 { get; internal set; }
/// <summary>
/// <summary> /// Gets the IP V6 address.
/// Gets the IP V6 address. /// </summary>
/// </summary> public IPAddress IPv6 {
public IPAddress IPv6 { get; internal set; } get; internal set;
}
/// <summary>
/// Gets the name of the access point. /// <summary>
/// </summary> /// Gets the name of the access point.
public string AccessPointName { get; internal set; } /// </summary>
public String AccessPointName {
/// <summary> get; internal set;
/// Gets the MAC (Physical) address. }
/// </summary>
public string MacAddress { get; internal set; } /// <summary>
/// Gets the MAC (Physical) address.
/// <summary> /// </summary>
/// Gets a value indicating whether this instance is wireless. public String MacAddress {
/// </summary> get; internal set;
public bool IsWireless { get; internal set; } }
}
/// <summary>
/// Gets a value indicating whether this instance is wireless.
/// </summary>
public Boolean IsWireless {
get; internal set;
}
}
} }
+249 -263
View File
@@ -1,266 +1,252 @@
namespace Unosquare.RaspberryIO.Computer using Unosquare.Swan;
{ using Unosquare.Swan.Abstractions;
using Swan; using Unosquare.Swan.Components;
using Swan.Abstractions; using System;
using Swan.Components; using System.Collections.Generic;
using System; using System.IO;
using System.Collections.Generic; using System.Linq;
using System.IO; using System.Net;
using System.Linq; using System.Text;
using System.Net;
using System.Text; namespace Unosquare.RaspberryIO.Computer {
/// <summary>
/// Represents the network information
/// </summary>
public class NetworkSettings : SingletonBase<NetworkSettings> {
private const String EssidTag = "ESSID:";
/// <summary> /// <summary>
/// Represents the network information /// Gets the local machine Host Name.
/// </summary> /// </summary>
public class NetworkSettings : SingletonBase<NetworkSettings> public String HostName => Network.HostName;
{
private const string EssidTag = "ESSID:"; /// <summary>
/// Retrieves the wireless networks.
/// <summary> /// </summary>
/// Gets the local machine Host Name. /// <param name="adapter">The adapter.</param>
/// </summary> /// <returns>A list of WiFi networks</returns>
public string HostName => Network.HostName; public List<WirelessNetworkInfo> RetrieveWirelessNetworks(String adapter) => this.RetrieveWirelessNetworks(new[] { adapter });
/// <summary> /// <summary>
/// Retrieves the wireless networks. /// Retrieves the wireless networks.
/// </summary> /// </summary>
/// <param name="adapter">The adapter.</param> /// <param name="adapters">The adapters.</param>
/// <returns>A list of WiFi networks</returns> /// <returns>A list of WiFi networks</returns>
public List<WirelessNetworkInfo> RetrieveWirelessNetworks(string adapter) => RetrieveWirelessNetworks(new[] { adapter }); public List<WirelessNetworkInfo> RetrieveWirelessNetworks(String[] adapters = null) {
List<WirelessNetworkInfo> result = new List<WirelessNetworkInfo>();
/// <summary>
/// Retrieves the wireless networks. foreach(String networkAdapter in adapters ?? this.RetrieveAdapters().Where(x => x.IsWireless).Select(x => x.Name)) {
/// </summary> String wirelessOutput = ProcessRunner.GetProcessOutputAsync("iwlist", $"{networkAdapter} scanning").Result;
/// <param name="adapters">The adapters.</param> String[] outputLines =
/// <returns>A list of WiFi networks</returns> wirelessOutput.Split('\n')
public List<WirelessNetworkInfo> RetrieveWirelessNetworks(string[] adapters = null) .Select(x => x.Trim())
{ .Where(x => String.IsNullOrWhiteSpace(x) == false)
var result = new List<WirelessNetworkInfo>(); .ToArray();
foreach (var networkAdapter in adapters ?? RetrieveAdapters().Where(x => x.IsWireless).Select(x => x.Name)) for(Int32 i = 0; i < outputLines.Length; i++) {
{ String line = outputLines[i];
var wirelessOutput = ProcessRunner.GetProcessOutputAsync("iwlist", $"{networkAdapter} scanning").Result;
var outputLines = if(line.StartsWith(EssidTag) == false) {
wirelessOutput.Split('\n') continue;
.Select(x => x.Trim()) }
.Where(x => string.IsNullOrWhiteSpace(x) == false)
.ToArray(); WirelessNetworkInfo network = new WirelessNetworkInfo() {
Name = line.Replace(EssidTag, String.Empty).Replace("\"", String.Empty)
for (var i = 0; i < outputLines.Length; i++) };
{
var line = outputLines[i]; while(true) {
if(i + 1 >= outputLines.Length) {
if (line.StartsWith(EssidTag) == false) continue; break;
}
var network = new WirelessNetworkInfo()
{ // should look for two lines before the ESSID acording to the scan
Name = line.Replace(EssidTag, string.Empty).Replace("\"", string.Empty) line = outputLines[i - 2];
};
if(line.StartsWith("Quality=")) {
while (true) network.Quality = line.Replace("Quality=", String.Empty);
{ break;
if (i + 1 >= outputLines.Length) break; }
}
// should look for two lines before the ESSID acording to the scan
line = outputLines[i - 2]; while(true) {
if(i + 1 >= outputLines.Length) {
if (line.StartsWith("Quality=")) break;
{ }
network.Quality = line.Replace("Quality=", string.Empty);
break; // should look for a line before the ESSID acording to the scan
} line = outputLines[i - 1];
}
if(line.StartsWith("Encryption key:")) {
while (true) network.IsEncrypted = line.Replace("Encryption key:", String.Empty).Trim() == "on";
{ break;
if (i + 1 >= outputLines.Length) break; }
}
// should look for a line before the ESSID acording to the scan
line = outputLines[i - 1]; if(result.Any(x => x.Name == network.Name) == false) {
result.Add(network);
if (line.StartsWith("Encryption key:")) }
{ }
network.IsEncrypted = line.Replace("Encryption key:", string.Empty).Trim() == "on"; }
break;
} return result.OrderBy(x => x.Name).ToList();
} }
if (result.Any(x => x.Name == network.Name) == false) /// <summary>
result.Add(network); /// Setups the wireless network.
} /// </summary>
} /// <param name="adapterName">Name of the adapter.</param>
/// <param name="networkSsid">The network ssid.</param>
return result.OrderBy(x => x.Name).ToList(); /// <param name="password">The password.</param>
} /// <param name="countryCode">The 2-letter country code in uppercase. Default is US.</param>
/// <returns>True if successful. Otherwise, false.</returns>
/// <summary> public Boolean SetupWirelessNetwork(String adapterName, String networkSsid, String password = null, String countryCode = "US") {
/// Setups the wireless network. // TODO: Get the country where the device is located to set 'country' param in payload var
/// </summary> String payload = $"country={countryCode}\nctrl_interface=DIR=/var/run/wpa_supplicant GROUP=netdev\nupdate_config=1\n";
/// <param name="adapterName">Name of the adapter.</param> payload += String.IsNullOrEmpty(password)
/// <param name="networkSsid">The network ssid.</param> ? $"network={{\n\tssid=\"{networkSsid}\"\n\t}}\n"
/// <param name="password">The password.</param> : $"network={{\n\tssid=\"{networkSsid}\"\n\tpsk=\"{password}\"\n\t}}\n";
/// <param name="countryCode">The 2-letter country code in uppercase. Default is US.</param> try {
/// <returns>True if successful. Otherwise, false.</returns> File.WriteAllText("/etc/wpa_supplicant/wpa_supplicant.conf", payload);
public bool SetupWirelessNetwork(string adapterName, string networkSsid, string password = null, string countryCode = "US") ProcessRunner.GetProcessOutputAsync("pkill", "-f wpa_supplicant").Wait();
{ ProcessRunner.GetProcessOutputAsync("ifdown", adapterName).Wait();
// TODO: Get the country where the device is located to set 'country' param in payload var ProcessRunner.GetProcessOutputAsync("ifup", adapterName).Wait();
var payload = $"country={countryCode}\nctrl_interface=DIR=/var/run/wpa_supplicant GROUP=netdev\nupdate_config=1\n"; } catch(Exception ex) {
payload += string.IsNullOrEmpty(password) ex.Log(nameof(NetworkSettings));
? $"network={{\n\tssid=\"{networkSsid}\"\n\t}}\n" return false;
: $"network={{\n\tssid=\"{networkSsid}\"\n\tpsk=\"{password}\"\n\t}}\n"; }
try
{ return true;
File.WriteAllText("/etc/wpa_supplicant/wpa_supplicant.conf", payload); }
ProcessRunner.GetProcessOutputAsync("pkill", "-f wpa_supplicant").Wait();
ProcessRunner.GetProcessOutputAsync("ifdown", adapterName).Wait(); /// <summary>
ProcessRunner.GetProcessOutputAsync("ifup", adapterName).Wait(); /// Retrieves the network adapters.
} /// </summary>
catch (Exception ex) /// <returns>A list of network adapters.</returns>
{ public List<NetworkAdapterInfo> RetrieveAdapters() {
ex.Log(nameof(NetworkSettings)); const String hWaddr = "HWaddr ";
return false; const String ether = "ether ";
}
List<NetworkAdapterInfo> result = new List<NetworkAdapterInfo>();
return true; String interfacesOutput = ProcessRunner.GetProcessOutputAsync("ifconfig").Result;
} String[] wlanOutput = ProcessRunner.GetProcessOutputAsync("iwconfig")
.Result.Split('\n')
/// <summary> .Where(x => x.Contains("no wireless extensions.") == false)
/// Retrieves the network adapters. .ToArray();
/// </summary>
/// <returns>A list of network adapters.</returns> String[] outputLines = interfacesOutput.Split('\n').Where(x => String.IsNullOrWhiteSpace(x) == false).ToArray();
public List<NetworkAdapterInfo> RetrieveAdapters()
{ for(Int32 i = 0; i < outputLines.Length; i++) {
const string hWaddr = "HWaddr "; // grab the current line
const string ether = "ether "; String line = outputLines[i];
var result = new List<NetworkAdapterInfo>(); // skip if the line is indented
var interfacesOutput = ProcessRunner.GetProcessOutputAsync("ifconfig").Result; if(Char.IsLetterOrDigit(line[0]) == false) {
var wlanOutput = ProcessRunner.GetProcessOutputAsync("iwconfig") continue;
.Result.Split('\n') }
.Where(x => x.Contains("no wireless extensions.") == false)
.ToArray(); // Read the line as an adatper
NetworkAdapterInfo adapter = new NetworkAdapterInfo {
var outputLines = interfacesOutput.Split('\n').Where(x => string.IsNullOrWhiteSpace(x) == false).ToArray(); Name = line.Substring(0, line.IndexOf(' ')).TrimEnd(':')
};
for (var i = 0; i < outputLines.Length; i++)
{ // Parse the MAC address in old version of ifconfig; it comes in the first line
// grab the current line if(line.IndexOf(hWaddr) >= 0) {
var line = outputLines[i]; Int32 startIndexHwd = line.IndexOf(hWaddr) + hWaddr.Length;
adapter.MacAddress = line.Substring(startIndexHwd, 17).Trim();
// skip if the line is indented }
if (char.IsLetterOrDigit(line[0]) == false)
continue; // Parse the info in lines other than the first
for(Int32 j = i + 1; j < outputLines.Length; j++) {
// Read the line as an adatper // Get the contents of the indented line
var adapter = new NetworkAdapterInfo String indentedLine = outputLines[j];
{
Name = line.Substring(0, line.IndexOf(' ')).TrimEnd(':') // We have hit the next adapter info
}; if(Char.IsLetterOrDigit(indentedLine[0])) {
i = j - 1;
// Parse the MAC address in old version of ifconfig; it comes in the first line break;
if (line.IndexOf(hWaddr) >= 0) }
{
var startIndexHwd = line.IndexOf(hWaddr) + hWaddr.Length; // Parse the MAC address in new versions of ifconfig; it no longer comes in the first line
adapter.MacAddress = line.Substring(startIndexHwd, 17).Trim(); if(indentedLine.IndexOf(ether) >= 0 && String.IsNullOrWhiteSpace(adapter.MacAddress)) {
} Int32 startIndexHwd = indentedLine.IndexOf(ether) + ether.Length;
adapter.MacAddress = indentedLine.Substring(startIndexHwd, 17).Trim();
// Parse the info in lines other than the first }
for (var j = i + 1; j < outputLines.Length; j++)
{ // Parse the IPv4 Address
// Get the contents of the indented line {
var indentedLine = outputLines[j]; String addressText = ParseOutputTagFromLine(indentedLine, "inet addr:") ?? ParseOutputTagFromLine(indentedLine, "inet ");
// We have hit the next adapter info if(addressText != null) {
if (char.IsLetterOrDigit(indentedLine[0])) if(IPAddress.TryParse(addressText, out IPAddress outValue)) {
{ adapter.IPv4 = outValue;
i = j - 1; }
break; }
} }
// Parse the MAC address in new versions of ifconfig; it no longer comes in the first line // Parse the IPv6 Address
if (indentedLine.IndexOf(ether) >= 0 && string.IsNullOrWhiteSpace(adapter.MacAddress)) {
{ String addressText = ParseOutputTagFromLine(indentedLine, "inet6 addr:") ?? ParseOutputTagFromLine(indentedLine, "inet6 ");
var startIndexHwd = indentedLine.IndexOf(ether) + ether.Length;
adapter.MacAddress = indentedLine.Substring(startIndexHwd, 17).Trim(); if(addressText != null) {
} if(IPAddress.TryParse(addressText, out IPAddress outValue)) {
adapter.IPv6 = outValue;
// Parse the IPv4 Address }
{ }
var addressText = ParseOutputTagFromLine(indentedLine, "inet addr:") ?? ParseOutputTagFromLine(indentedLine, "inet "); }
if (addressText != null) // we have hit the end of the output in an indented line
{ if(j >= outputLines.Length - 1) {
if (IPAddress.TryParse(addressText, out var outValue)) i = outputLines.Length;
adapter.IPv4 = outValue; }
} }
}
// Retrieve the wireless LAN info
// Parse the IPv6 Address String wlanInfo = wlanOutput.FirstOrDefault(x => x.StartsWith(adapter.Name));
{
var addressText = ParseOutputTagFromLine(indentedLine, "inet6 addr:") ?? ParseOutputTagFromLine(indentedLine, "inet6 "); if(wlanInfo != null) {
adapter.IsWireless = true;
if (addressText != null) String[] essidParts = wlanInfo.Split(new[] { EssidTag }, StringSplitOptions.RemoveEmptyEntries);
{ if(essidParts.Length >= 2) {
if (IPAddress.TryParse(addressText, out var outValue)) adapter.AccessPointName = essidParts[1].Replace("\"", String.Empty).Trim();
adapter.IPv6 = outValue; }
} }
}
// Add the current adapter to the result
// we have hit the end of the output in an indented line result.Add(adapter);
if (j >= outputLines.Length - 1) }
i = outputLines.Length;
} return result.OrderBy(x => x.Name).ToList();
}
// Retrieve the wireless LAN info
var wlanInfo = wlanOutput.FirstOrDefault(x => x.StartsWith(adapter.Name)); /// <summary>
/// Retrieves current wireless connected network name.
if (wlanInfo != null) /// </summary>
{ /// <returns>The connected network name.</returns>
adapter.IsWireless = true; public String GetWirelessNetworkName() => ProcessRunner.GetProcessOutputAsync("iwgetid", "-r").Result;
var essidParts = wlanInfo.Split(new[] { EssidTag }, StringSplitOptions.RemoveEmptyEntries);
if (essidParts.Length >= 2) /// <summary>
{ /// Parses the output tag from the given line.
adapter.AccessPointName = essidParts[1].Replace("\"", string.Empty).Trim(); /// </summary>
} /// <param name="indentedLine">The indented line.</param>
} /// <param name="tagName">Name of the tag.</param>
/// <returns>The value after the tag identifier</returns>
// Add the current adapter to the result private static String ParseOutputTagFromLine(String indentedLine, String tagName) {
result.Add(adapter); if(indentedLine.IndexOf(tagName) < 0) {
} return null;
}
return result.OrderBy(x => x.Name).ToList();
} Int32 startIndex = indentedLine.IndexOf(tagName) + tagName.Length;
StringBuilder builder = new StringBuilder(1024);
/// <summary> for(Int32 c = startIndex; c < indentedLine.Length; c++) {
/// Retrieves current wireless connected network name. Char currentChar = indentedLine[c];
/// </summary> if(!Char.IsPunctuation(currentChar) && !Char.IsLetterOrDigit(currentChar)) {
/// <returns>The connected network name.</returns> break;
public string GetWirelessNetworkName() => ProcessRunner.GetProcessOutputAsync("iwgetid", "-r").Result; }
/// <summary> _ = builder.Append(currentChar);
/// Parses the output tag from the given line. }
/// </summary>
/// <param name="indentedLine">The indented line.</param> return builder.ToString();
/// <param name="tagName">Name of the tag.</param> }
/// <returns>The value after the tag identifier</returns> }
private static string ParseOutputTagFromLine(string indentedLine, string tagName)
{
if (indentedLine.IndexOf(tagName) < 0)
return null;
var startIndex = indentedLine.IndexOf(tagName) + tagName.Length;
var builder = new StringBuilder(1024);
for (var c = startIndex; c < indentedLine.Length; c++)
{
var currentChar = indentedLine[c];
if (!char.IsPunctuation(currentChar) && !char.IsLetterOrDigit(currentChar))
break;
builder.Append(currentChar);
}
return builder.ToString();
}
}
} }
+55 -43
View File
@@ -1,46 +1,58 @@
namespace Unosquare.RaspberryIO.Computer using System;
{
namespace Unosquare.RaspberryIO.Computer {
/// <summary>
/// Represents the OS Information
/// </summary>
public class OsInfo {
/// <summary> /// <summary>
/// Represents the OS Information /// System name
/// </summary> /// </summary>
public class OsInfo public String SysName {
{ get; set;
/// <summary> }
/// System name
/// </summary> /// <summary>
public string SysName { get; set; } /// Node name
/// </summary>
/// <summary> public String NodeName {
/// Node name get; set;
/// </summary> }
public string NodeName { get; set; }
/// <summary>
/// <summary> /// Release level
/// Release level /// </summary>
/// </summary> public String Release {
public string Release { get; set; } get; set;
}
/// <summary>
/// Version level /// <summary>
/// </summary> /// Version level
public string Version { get; set; } /// </summary>
public String Version {
/// <summary> get; set;
/// Hardware level }
/// </summary>
public string Machine { get; set; } /// <summary>
/// Hardware level
/// <summary> /// </summary>
/// Domain name public String Machine {
/// </summary> get; set;
public string DomainName { get; set; } }
/// <summary> /// <summary>
/// Returns a <see cref="string" /> that represents this instance. /// Domain name
/// </summary> /// </summary>
/// <returns> public String DomainName {
/// A <see cref="string" /> that represents this instance. get; set;
/// </returns> }
public override string ToString() => $"{SysName} {Release} {Version}";
} /// <summary>
/// Returns a <see cref="String" /> that represents this instance.
/// </summary>
/// <returns>
/// A <see cref="String" /> that represents this instance.
/// </returns>
public override String ToString() => $"{this.SysName} {this.Release} {this.Version}";
}
} }
+129 -131
View File
@@ -1,134 +1,132 @@
namespace Unosquare.RaspberryIO.Computer namespace Unosquare.RaspberryIO.Computer {
{ /// <summary>
/// Defines the board revision codes of the different versions of the Raspberry Pi
/// http://www.raspberrypi-spy.co.uk/2012/09/checking-your-raspberry-pi-board-version/
/// </summary>
public enum PiVersion {
/// <summary> /// <summary>
/// Defines the board revision codes of the different versions of the Raspberry Pi /// The unknown version
/// http://www.raspberrypi-spy.co.uk/2012/09/checking-your-raspberry-pi-board-version/
/// </summary> /// </summary>
public enum PiVersion Unknown = 0,
{
/// <summary> /// <summary>
/// The unknown version /// The model b rev1
/// </summary> /// </summary>
Unknown = 0, ModelBRev1 = 0x0002,
/// <summary> /// <summary>
/// The model b rev1 /// The model b rev1 ec N0001
/// </summary> /// </summary>
ModelBRev1 = 0x0002, ModelBRev1ECN0001 = 0x0003,
/// <summary> /// <summary>
/// The model b rev1 ec N0001 /// The model b rev2x04
/// </summary> /// </summary>
ModelBRev1ECN0001 = 0x0003, ModelBRev2x04 = 0x0004,
/// <summary> /// <summary>
/// The model b rev2x04 /// The model b rev2x05
/// </summary> /// </summary>
ModelBRev2x04 = 0x0004, ModelBRev2x05 = 0x0005,
/// <summary> /// <summary>
/// The model b rev2x05 /// The model b rev2x06
/// </summary> /// </summary>
ModelBRev2x05 = 0x0005, ModelBRev2x06 = 0x0006,
/// <summary> /// <summary>
/// The model b rev2x06 /// The model ax07
/// </summary> /// </summary>
ModelBRev2x06 = 0x0006, ModelAx07 = 0x0007,
/// <summary> /// <summary>
/// The model ax07 /// The model ax08
/// </summary> /// </summary>
ModelAx07 = 0x0007, ModelAx08 = 0x0008,
/// <summary> /// <summary>
/// The model ax08 /// The model ax09
/// </summary> /// </summary>
ModelAx08 = 0x0008, ModelAx09 = 0x0009,
/// <summary> /// <summary>
/// The model ax09 /// The model b rev2x0d
/// </summary> /// </summary>
ModelAx09 = 0x0009, ModelBRev2x0d,
/// <summary> /// <summary>
/// The model b rev2x0d /// The model b rev2x0e
/// </summary> /// </summary>
ModelBRev2x0d, ModelBRev2x0e,
/// <summary> /// <summary>
/// The model b rev2x0e /// The model b rev2x0f
/// </summary> /// </summary>
ModelBRev2x0e, ModelBRev2x0f = 0x000f,
/// <summary> /// <summary>
/// The model b rev2x0f /// The model b plus0x10
/// </summary> /// </summary>
ModelBRev2x0f = 0x000f, ModelBPlus0x10 = 0x0010,
/// <summary> /// <summary>
/// The model b plus0x10 /// The model b plus0x13
/// </summary> /// </summary>
ModelBPlus0x10 = 0x0010, ModelBPlus0x13 = 0x0013,
/// <summary> /// <summary>
/// The model b plus0x13 /// The compute module0x11
/// </summary> /// </summary>
ModelBPlus0x13 = 0x0013, ComputeModule0x11 = 0x0011,
/// <summary> /// <summary>
/// The compute module0x11 /// The compute module0x14
/// </summary> /// </summary>
ComputeModule0x11 = 0x0011, ComputeModule0x14 = 0x0014,
/// <summary> /// <summary>
/// The compute module0x14 /// The model a plus0x12
/// </summary> /// </summary>
ComputeModule0x14 = 0x0014, ModelAPlus0x12 = 0x0012,
/// <summary> /// <summary>
/// The model a plus0x12 /// The model a plus0x15
/// </summary> /// </summary>
ModelAPlus0x12 = 0x0012, ModelAPlus0x15 = 0x0015,
/// <summary> /// <summary>
/// The model a plus0x15 /// The pi2 model B1V1 sony
/// </summary> /// </summary>
ModelAPlus0x15 = 0x0015, Pi2ModelB1v1Sony = 0xa01041,
/// <summary> /// <summary>
/// The pi2 model B1V1 sony /// The pi2 model B1V1 embest
/// </summary> /// </summary>
Pi2ModelB1v1Sony = 0xa01041, Pi2ModelB1v1Embest = 0xa21041,
/// <summary> /// <summary>
/// The pi2 model B1V1 embest /// The pi2 model B1V2
/// </summary> /// </summary>
Pi2ModelB1v1Embest = 0xa21041, Pi2ModelB1v2 = 0xa22042,
/// <summary> /// <summary>
/// The pi2 model B1V2 /// The pi zero1v2
/// </summary> /// </summary>
Pi2ModelB1v2 = 0xa22042, PiZero1v2 = 0x900092,
/// <summary> /// <summary>
/// The pi zero1v2 /// The pi zero1v3
/// </summary> /// </summary>
PiZero1v2 = 0x900092, PiZero1v3 = 0x900093,
/// <summary> /// <summary>
/// The pi zero1v3 /// The pi3 model b sony
/// </summary> /// </summary>
PiZero1v3 = 0x900093, Pi3ModelBSony = 0xa02082,
/// <summary> /// <summary>
/// The pi3 model b sony /// The pi3 model b embest
/// </summary> /// </summary>
Pi3ModelBSony = 0xa02082, Pi3ModelBEmbest = 0xa22082
}
/// <summary>
/// The pi3 model b embest
/// </summary>
Pi3ModelBEmbest = 0xa22082
}
} }
+339 -340
View File
@@ -1,344 +1,343 @@
namespace Unosquare.RaspberryIO.Computer using Unosquare.RaspberryIO.Native;
{ using Unosquare.Swan.Abstractions;
using Native; using System;
using Swan.Abstractions; 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.Reflection;
using System.Linq;
using System.Reflection; namespace Unosquare.RaspberryIO.Computer {
/// <summary>
/// http://raspberry-pi-guide.readthedocs.io/en/latest/system.html
/// </summary>
public sealed class SystemInfo : SingletonBase<SystemInfo> {
private const String CpuInfoFilePath = "/proc/cpuinfo";
private const String MemInfoFilePath = "/proc/meminfo";
private const String UptimeFilePath = "/proc/uptime";
private static readonly StringComparer StringComparer = StringComparer.InvariantCultureIgnoreCase;
[System.Diagnostics.CodeAnalysis.SuppressMessage("Codequalität", "IDE0052:Ungelesene private Member entfernen", Justification = "<Ausstehend>")]
private static readonly Object SyncRoot = new Object();
/// <summary> /// <summary>
/// http://raspberry-pi-guide.readthedocs.io/en/latest/system.html /// Prevents a default instance of the <see cref="SystemInfo"/> class from being created.
/// </summary> /// </summary>
public sealed class SystemInfo : SingletonBase<SystemInfo> /// <exception cref="NotSupportedException">Could not initialize the GPIO controller</exception>
{ private SystemInfo() {
private const string CpuInfoFilePath = "/proc/cpuinfo"; #region Obtain and format a property dictionary
private const string MemInfoFilePath = "/proc/meminfo";
private const string UptimeFilePath = "/proc/uptime"; PropertyInfo[] properties =
private static readonly StringComparer StringComparer = StringComparer.InvariantCultureIgnoreCase; typeof(SystemInfo).GetTypeInfo()
.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
private static readonly object SyncRoot = new object(); .Where(
p =>
/// <summary> p.CanWrite && p.CanRead &&
/// Prevents a default instance of the <see cref="SystemInfo"/> class from being created. (p.PropertyType == typeof(String) || p.PropertyType == typeof(String[])))
/// </summary> .ToArray();
/// <exception cref="NotSupportedException">Could not initialize the GPIO controller</exception> Dictionary<String, PropertyInfo> propDictionary = new Dictionary<String, PropertyInfo>(StringComparer);
private SystemInfo()
{ foreach(PropertyInfo prop in properties) {
#region Obtain and format a property dictionary propDictionary[prop.Name.Replace(" ", String.Empty).ToLowerInvariant().Trim()] = prop;
}
var properties =
typeof(SystemInfo).GetTypeInfo() #endregion
.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
.Where( #region Extract CPU information
p =>
p.CanWrite && p.CanRead && if(File.Exists(CpuInfoFilePath)) {
(p.PropertyType == typeof(string) || p.PropertyType == typeof(string[]))) String[] cpuInfoLines = File.ReadAllLines(CpuInfoFilePath);
.ToArray();
var propDictionary = new Dictionary<string, PropertyInfo>(StringComparer); foreach(String line in cpuInfoLines) {
String[] lineParts = line.Split(new[] { ':' }, 2);
foreach (var prop in properties) if(lineParts.Length != 2) {
{ continue;
propDictionary[prop.Name.Replace(" ", string.Empty).ToLowerInvariant().Trim()] = prop; }
}
String propertyKey = lineParts[0].Trim().Replace(" ", String.Empty);
#endregion String propertyStringValue = lineParts[1].Trim();
#region Extract CPU information if(!propDictionary.ContainsKey(propertyKey)) {
continue;
if (File.Exists(CpuInfoFilePath)) }
{
var cpuInfoLines = File.ReadAllLines(CpuInfoFilePath); PropertyInfo property = propDictionary[propertyKey];
if(property.PropertyType == typeof(String)) {
foreach (var line in cpuInfoLines) property.SetValue(this, propertyStringValue);
{ } else if(property.PropertyType == typeof(String[])) {
var lineParts = line.Split(new[] { ':' }, 2); String[] propertyArrayAvalue = propertyStringValue.Split(' ');
if (lineParts.Length != 2) property.SetValue(this, propertyArrayAvalue);
continue; }
}
var propertyKey = lineParts[0].Trim().Replace(" ", string.Empty); }
var propertyStringValue = lineParts[1].Trim();
#endregion
if (!propDictionary.ContainsKey(propertyKey)) continue;
#region Extract Memory Information
var property = propDictionary[propertyKey];
if (property.PropertyType == typeof(string)) if(File.Exists(MemInfoFilePath)) {
{ String[] memInfoLines = File.ReadAllLines(MemInfoFilePath);
property.SetValue(this, propertyStringValue); foreach(String line in memInfoLines) {
} String[] lineParts = line.Split(new[] { ':' }, 2);
else if (property.PropertyType == typeof(string[])) if(lineParts.Length != 2) {
{ continue;
var propertyArrayAvalue = propertyStringValue.Split(' '); }
property.SetValue(this, propertyArrayAvalue);
} if(lineParts[0].ToLowerInvariant().Trim().Equals("memtotal") == false) {
} continue;
} }
#endregion String memKb = lineParts[1].ToLowerInvariant().Trim().Replace("kb", String.Empty).Trim();
#region Extract Memory Information if(Int32.TryParse(memKb, out Int32 parsedMem)) {
this.InstalledRam = parsedMem * 1024;
if (File.Exists(MemInfoFilePath)) break;
{ }
var memInfoLines = File.ReadAllLines(MemInfoFilePath); }
foreach (var line in memInfoLines) }
{
var lineParts = line.Split(new[] { ':' }, 2); #endregion
if (lineParts.Length != 2)
continue; #region Board Version and Form Factor
if (lineParts[0].ToLowerInvariant().Trim().Equals("memtotal") == false) try {
continue; if(String.IsNullOrWhiteSpace(this.Revision) == false &&
Int32.TryParse(
var memKb = lineParts[1].ToLowerInvariant().Trim().Replace("kb", string.Empty).Trim(); this.Revision.ToUpperInvariant(),
NumberStyles.HexNumber,
if (int.TryParse(memKb, out var parsedMem)) CultureInfo.InvariantCulture,
{ out Int32 boardVersion)) {
InstalledRam = parsedMem * 1024; this.RaspberryPiVersion = PiVersion.Unknown;
break; if(Enum.GetValues(typeof(PiVersion)).Cast<Int32>().Contains(boardVersion)) {
} this.RaspberryPiVersion = (PiVersion)boardVersion;
} }
} }
#endregion this.WiringPiBoardRevision = WiringPi.PiBoardRev();
} catch {
#region Board Version and Form Factor /* Ignore */
}
try
{ #endregion
if (string.IsNullOrWhiteSpace(Revision) == false &&
int.TryParse( #region Version Information
Revision.ToUpperInvariant(),
NumberStyles.HexNumber, {
CultureInfo.InvariantCulture, String[] libParts = WiringPi.WiringPiLibrary.Split('.');
out var boardVersion)) Int32 major = Int32.Parse(libParts[libParts.Length - 2]);
{ Int32 minor = Int32.Parse(libParts[libParts.Length - 1]);
RaspberryPiVersion = PiVersion.Unknown; Version version = new Version(major, minor);
if (Enum.GetValues(typeof(PiVersion)).Cast<int>().Contains(boardVersion)) this.WiringPiVersion = version;
{ }
RaspberryPiVersion = (PiVersion)boardVersion;
} #endregion
}
#region Extract OS Info
WiringPiBoardRevision = WiringPi.PiBoardRev();
} try {
catch _ = Standard.Uname(out SystemName unameInfo);
{ this.OperatingSystem = new OsInfo {
/* Ignore */ DomainName = unameInfo.DomainName,
} Machine = unameInfo.Machine,
NodeName = unameInfo.NodeName,
#endregion Release = unameInfo.Release,
SysName = unameInfo.SysName,
#region Version Information Version = unameInfo.Version
};
{ } catch {
var libParts = WiringPi.WiringPiLibrary.Split('.'); this.OperatingSystem = new OsInfo();
var major = int.Parse(libParts[libParts.Length - 2]); }
var minor = int.Parse(libParts[libParts.Length - 1]);
var version = new Version(major, minor); #endregion
WiringPiVersion = version; }
}
/// <summary>
#endregion /// Gets the wiring pi library version.
/// </summary>
#region Extract OS Info public Version WiringPiVersion {
get;
try }
{
Standard.Uname(out var unameInfo); /// <summary>
OperatingSystem = new OsInfo /// Gets the OS information.
{ /// </summary>
DomainName = unameInfo.DomainName, /// <value>
Machine = unameInfo.Machine, /// The os information.
NodeName = unameInfo.NodeName, /// </value>
Release = unameInfo.Release, public OsInfo OperatingSystem {
SysName = unameInfo.SysName, get;
Version = unameInfo.Version }
};
} /// <summary>
catch /// Gets the Raspberry Pi version.
{ /// </summary>
OperatingSystem = new OsInfo(); public PiVersion RaspberryPiVersion {
} get;
}
#endregion
} /// <summary>
/// Gets the Wiring Pi board revision (1 or 2).
/// <summary> /// </summary>
/// Gets the wiring pi library version. /// <value>
/// </summary> /// The wiring pi board revision.
public Version WiringPiVersion { get; } /// </value>
public Int32 WiringPiBoardRevision {
/// <summary> get;
/// Gets the OS information. }
/// </summary>
/// <value> /// <summary>
/// The os information. /// Gets the number of processor cores.
/// </value> /// </summary>
public OsInfo OperatingSystem { get; } public Int32 ProcessorCount => Int32.TryParse(this.Processor, out Int32 outIndex) ? outIndex + 1 : 0;
/// <summary> /// <summary>
/// Gets the Raspberry Pi version. /// Gets the installed ram in bytes.
/// </summary> /// </summary>
public PiVersion RaspberryPiVersion { get; } public Int32 InstalledRam {
get;
/// <summary> }
/// Gets the Wiring Pi board revision (1 or 2).
/// </summary> /// <summary>
/// <value> /// Gets a value indicating whether this CPU is little endian.
/// The wiring pi board revision. /// </summary>
/// </value> public Boolean IsLittleEndian => BitConverter.IsLittleEndian;
public int WiringPiBoardRevision { get; }
/// <summary>
/// <summary> /// Gets the CPU model name.
/// Gets the number of processor cores. /// </summary>
/// </summary> public String ModelName {
public int ProcessorCount get; private set;
{ }
get
{ /// <summary>
if (int.TryParse(Processor, out var outIndex)) /// Gets a list of supported CPU features.
{ /// </summary>
return outIndex + 1; public String[] Features {
} get; private set;
}
return 0;
} /// <summary>
} /// Gets the CPU implementer hex code.
/// </summary>
/// <summary> public String CpuImplementer {
/// Gets the installed ram in bytes. get; private set;
/// </summary> }
public int InstalledRam { get; }
/// <summary>
/// <summary> /// Gets the CPU architecture code.
/// Gets a value indicating whether this CPU is little endian. /// </summary>
/// </summary> public String CpuArchitecture {
public bool IsLittleEndian => BitConverter.IsLittleEndian; get; private set;
}
/// <summary>
/// Gets the CPU model name. /// <summary>
/// </summary> /// Gets the CPU variant code.
public string ModelName { get; private set; } /// </summary>
public String CpuVariant {
/// <summary> get; private set;
/// Gets a list of supported CPU features. }
/// </summary>
public string[] Features { get; private set; } /// <summary>
/// Gets the CPU part code.
/// <summary> /// </summary>
/// Gets the CPU implementer hex code. public String CpuPart {
/// </summary> get; private set;
public string CpuImplementer { get; private set; } }
/// <summary> /// <summary>
/// Gets the CPU architecture code. /// Gets the CPU revision code.
/// </summary> /// </summary>
public string CpuArchitecture { get; private set; } public String CpuRevision {
get; private set;
/// <summary> }
/// Gets the CPU variant code.
/// </summary> /// <summary>
public string CpuVariant { get; private set; } /// Gets the hardware model number.
/// </summary>
/// <summary> public String Hardware {
/// Gets the CPU part code. get; private set;
/// </summary> }
public string CpuPart { get; private set; }
/// <summary>
/// <summary> /// Gets the hardware revision number.
/// Gets the CPU revision code. /// </summary>
/// </summary> public String Revision {
public string CpuRevision { get; private set; } get; private set;
}
/// <summary>
/// Gets the hardware model number. /// <summary>
/// </summary> /// Gets the serial number.
public string Hardware { get; private set; } /// </summary>
public String Serial {
/// <summary> get; private set;
/// Gets the hardware revision number. }
/// </summary>
public string Revision { get; private set; } /// <summary>
/// Gets the system uptime (in seconds).
/// <summary> /// </summary>
/// Gets the serial number. public Double Uptime {
/// </summary> get {
public string Serial { get; private set; } try {
if(File.Exists(UptimeFilePath) == false) {
/// <summary> return 0;
/// Gets the system uptime (in seconds). }
/// </summary>
public double Uptime String[] parts = File.ReadAllText(UptimeFilePath).Trim().Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
{ if(parts.Length >= 1 && Single.TryParse(parts[0], out Single result)) {
get return result;
{ }
try } catch {
{ /* Ignore */
if (File.Exists(UptimeFilePath) == false) return 0; }
var parts = File.ReadAllText(UptimeFilePath).Trim().Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
if (parts.Length >= 1 && float.TryParse(parts[0], out var result)) return 0;
return result; }
} }
catch
{ /// <summary>
/* Ignore */ /// Gets the uptime in TimeSpan.
} /// </summary>
public TimeSpan UptimeTimeSpan => TimeSpan.FromSeconds(this.Uptime);
return 0;
} /// <summary>
} /// Placeholder for processor index
/// </summary>
/// <summary> private String Processor {
/// Gets the uptime in TimeSpan. get; set;
/// </summary> }
public TimeSpan UptimeTimeSpan => TimeSpan.FromSeconds(Uptime);
/// <summary>
/// <summary> /// Returns a <see cref="String" /> that represents this instance.
/// Placeholder for processor index /// </summary>
/// </summary> /// <returns>
private string Processor { get; set; } /// A <see cref="String" /> that represents this instance.
/// </returns>
/// <summary> public override String ToString() {
/// Returns a <see cref="string" /> that represents this instance. PropertyInfo[] properties = typeof(SystemInfo).GetTypeInfo().GetProperties(BindingFlags.Instance | BindingFlags.Public)
/// </summary> .Where(p => p.CanRead && (
/// <returns> p.PropertyType == typeof(String) ||
/// A <see cref="string" /> that represents this instance. p.PropertyType == typeof(String[]) ||
/// </returns> p.PropertyType == typeof(Int32) ||
public override string ToString() p.PropertyType == typeof(Boolean) ||
{ p.PropertyType == typeof(TimeSpan)))
var properties = typeof(SystemInfo).GetTypeInfo().GetProperties(BindingFlags.Instance | BindingFlags.Public) .ToArray();
.Where(p => p.CanRead && (
p.PropertyType == typeof(string) || List<String> properyValues = new List<String>
p.PropertyType == typeof(string[]) || {
p.PropertyType == typeof(int) ||
p.PropertyType == typeof(bool) ||
p.PropertyType == typeof(TimeSpan)))
.ToArray();
var properyValues = new List<string>
{
"System Information", "System Information",
$"\t{nameof(WiringPiVersion),-22}: {WiringPiVersion}", $"\t{nameof(this.WiringPiVersion),-22}: {this.WiringPiVersion}",
$"\t{nameof(RaspberryPiVersion),-22}: {RaspberryPiVersion}" $"\t{nameof(this.RaspberryPiVersion),-22}: {this.RaspberryPiVersion}"
}; };
foreach (var property in properties) foreach(PropertyInfo property in properties) {
{ if(property.PropertyType != typeof(String[])) {
if (property.PropertyType != typeof(string[])) properyValues.Add($"\t{property.Name,-22}: {property.GetValue(this)}");
{ } else if(property.GetValue(this) is String[] allValues) {
properyValues.Add($"\t{property.Name,-22}: {property.GetValue(this)}"); String concatValues = String.Join(" ", allValues);
} properyValues.Add($"\t{property.Name,-22}: {concatValues}");
else if (property.GetValue(this) is string[] allValues) }
{ }
var concatValues = string.Join(" ", allValues);
properyValues.Add($"\t{property.Name,-22}: {concatValues}"); return String.Join(Environment.NewLine, properyValues.ToArray());
} }
} }
return string.Join(Environment.NewLine, properyValues.ToArray());
}
}
} }
@@ -1,23 +1,29 @@
namespace Unosquare.RaspberryIO.Computer using System;
{
namespace Unosquare.RaspberryIO.Computer {
/// <summary>
/// Represents a wireless network information
/// </summary>
public class WirelessNetworkInfo {
/// <summary> /// <summary>
/// Represents a wireless network information /// Gets the ESSID of the Wireless network.
/// </summary> /// </summary>
public class WirelessNetworkInfo public String Name {
{ get; internal set;
/// <summary> }
/// Gets the ESSID of the Wireless network.
/// </summary> /// <summary>
public string Name { get; internal set; } /// Gets the network quality.
/// </summary>
/// <summary> public String Quality {
/// Gets the network quality. get; internal set;
/// </summary> }
public string Quality { get; internal set; }
/// <summary>
/// <summary> /// Gets a value indicating whether this instance is encrypted.
/// Gets a value indicating whether this instance is encrypted. /// </summary>
/// </summary> public Boolean IsEncrypted {
public bool IsEncrypted { get; internal set; } get; internal set;
} }
}
} }
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+166 -200
View File
@@ -1,201 +1,167 @@
namespace Unosquare.RaspberryIO.Gpio using System;
{
using System; namespace Unosquare.RaspberryIO.Gpio {
public partial class GpioPin {
public partial class GpioPin #region Static Pin Definitions
{
#region Static Pin Definitions internal static readonly Lazy<GpioPin> Pin08 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin08, 3) {
Capabilities = new[] { PinCapability.GP, PinCapability.I2CSDA },
internal static readonly Lazy<GpioPin> Pin08 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin08, 3) Name = "BCM 2 (SDA)"
{ });
Capabilities = new[] { PinCapability.GP, PinCapability.I2CSDA },
Name = "BCM 2 (SDA)" internal static readonly Lazy<GpioPin> Pin09 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin09, 5) {
}); Capabilities = new[] { PinCapability.GP, PinCapability.I2CSCL },
Name = "BCM 3 (SCL)"
internal static readonly Lazy<GpioPin> Pin09 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin09, 5) });
{
Capabilities = new[] { PinCapability.GP, PinCapability.I2CSCL }, internal static readonly Lazy<GpioPin> Pin07 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin07, 7) {
Name = "BCM 3 (SCL)" Capabilities = new[] { PinCapability.GP, PinCapability.GPCLK },
}); Name = "BCM 4 (GPCLK0)"
});
internal static readonly Lazy<GpioPin> Pin07 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin07, 7)
{ internal static readonly Lazy<GpioPin> Pin00 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin00, 11) {
Capabilities = new[] { PinCapability.GP, PinCapability.GPCLK }, Capabilities = new[] { PinCapability.GP, PinCapability.UARTRTS },
Name = "BCM 4 (GPCLK0)" Name = "BCM 17"
}); });
internal static readonly Lazy<GpioPin> Pin00 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin00, 11) internal static readonly Lazy<GpioPin> Pin02 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin02, 13) {
{ Capabilities = new[] { PinCapability.GP },
Capabilities = new[] { PinCapability.GP, PinCapability.UARTRTS }, Name = "BCM 27"
Name = "BCM 17" });
});
internal static readonly Lazy<GpioPin> Pin03 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin03, 15) {
internal static readonly Lazy<GpioPin> Pin02 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin02, 13) Capabilities = new[] { PinCapability.GP },
{ Name = "BCM 22"
Capabilities = new[] { PinCapability.GP }, });
Name = "BCM 27"
}); internal static readonly Lazy<GpioPin> Pin12 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin12, 19) {
Capabilities = new[] { PinCapability.GP, PinCapability.SPIMOSI },
internal static readonly Lazy<GpioPin> Pin03 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin03, 15) Name = "BCM 10 (MOSI)"
{ });
Capabilities = new[] { PinCapability.GP },
Name = "BCM 22" internal static readonly Lazy<GpioPin> Pin13 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin13, 21) {
}); Capabilities = new[] { PinCapability.GP, PinCapability.SPIMISO },
Name = "BCM 9 (MISO)"
internal static readonly Lazy<GpioPin> Pin12 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin12, 19) });
{
Capabilities = new[] { PinCapability.GP, PinCapability.SPIMOSI }, internal static readonly Lazy<GpioPin> Pin14 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin14, 23) {
Name = "BCM 10 (MOSI)" Capabilities = new[] { PinCapability.GP, PinCapability.SPICLK },
}); Name = "BCM 11 (SCLCK)"
});
internal static readonly Lazy<GpioPin> Pin13 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin13, 21)
{ internal static readonly Lazy<GpioPin> Pin30 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin30, 27) {
Capabilities = new[] { PinCapability.GP, PinCapability.SPIMISO }, Capabilities = new[] { PinCapability.I2CSDA },
Name = "BCM 9 (MISO)" Name = "BCM 0 (ID_SD)"
}); });
internal static readonly Lazy<GpioPin> Pin14 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin14, 23) internal static readonly Lazy<GpioPin> Pin31 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin31, 28) {
{ Capabilities = new[] { PinCapability.I2CSCL },
Capabilities = new[] { PinCapability.GP, PinCapability.SPICLK }, Name = "BCM 1 (ID_SC)"
Name = "BCM 11 (SCLCK)" });
});
internal static readonly Lazy<GpioPin> Pin11 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin11, 26) {
internal static readonly Lazy<GpioPin> Pin30 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin30, 27) Capabilities = new[] { PinCapability.GP, PinCapability.SPICS },
{ Name = "BCM 7 (CE1)"
Capabilities = new[] { PinCapability.I2CSDA }, });
Name = "BCM 0 (ID_SD)"
}); internal static readonly Lazy<GpioPin> Pin10 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin10, 24) {
Capabilities = new[] { PinCapability.GP, PinCapability.SPICS },
internal static readonly Lazy<GpioPin> Pin31 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin31, 28) Name = "BCM 8 (CE0)"
{ });
Capabilities = new[] { PinCapability.I2CSCL },
Name = "BCM 1 (ID_SC)" internal static readonly Lazy<GpioPin> Pin06 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin06, 22) {
}); Capabilities = new[] { PinCapability.GP },
Name = "BCM 25"
internal static readonly Lazy<GpioPin> Pin11 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin11, 26) });
{ internal static readonly Lazy<GpioPin> Pin05 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin05, 18) {
Capabilities = new[] { PinCapability.GP, PinCapability.SPICS }, Capabilities = new[] { PinCapability.GP },
Name = "BCM 7 (CE1)" Name = "BCM 24"
}); });
internal static readonly Lazy<GpioPin> Pin10 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin10, 24) internal static readonly Lazy<GpioPin> Pin04 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin04, 16) {
{ Capabilities = new[] { PinCapability.GP },
Capabilities = new[] { PinCapability.GP, PinCapability.SPICS }, Name = "BCM 23"
Name = "BCM 8 (CE0)" });
});
internal static readonly Lazy<GpioPin> Pin01 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin01, 12) {
internal static readonly Lazy<GpioPin> Pin06 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin06, 22) Capabilities = new[] { PinCapability.GP, PinCapability.PWM },
{ Name = "BCM 18 (PWM0)"
Capabilities = new[] { PinCapability.GP }, });
Name = "BCM 25"
}); internal static readonly Lazy<GpioPin> Pin16 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin16, 10) {
internal static readonly Lazy<GpioPin> Pin05 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin05, 18) Capabilities = new[] { PinCapability.UARTRXD },
{ Name = "BCM 15 (RXD)"
Capabilities = new[] { PinCapability.GP }, });
Name = "BCM 24"
}); internal static readonly Lazy<GpioPin> Pin15 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin15, 8) {
Capabilities = new[] { PinCapability.UARTTXD },
internal static readonly Lazy<GpioPin> Pin04 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin04, 16) Name = "BCM 14 (TXD)"
{ });
Capabilities = new[] { PinCapability.GP },
Name = "BCM 23" internal static readonly Lazy<GpioPin> Pin21 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin21, 29) {
}); Capabilities = new[] { PinCapability.GP },
Name = "BCM 5"
internal static readonly Lazy<GpioPin> Pin01 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin01, 12) });
{
Capabilities = new[] { PinCapability.GP, PinCapability.PWM }, internal static readonly Lazy<GpioPin> Pin22 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin22, 31) {
Name = "BCM 18 (PWM0)" Capabilities = new[] { PinCapability.GP },
}); Name = "BCM 6"
});
internal static readonly Lazy<GpioPin> Pin16 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin16, 10)
{ internal static readonly Lazy<GpioPin> Pin23 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin23, 33) {
Capabilities = new[] { PinCapability.UARTRXD }, Capabilities = new[] { PinCapability.GP, PinCapability.PWM },
Name = "BCM 15 (RXD)" Name = "BCM 13 (PWM1)"
}); });
internal static readonly Lazy<GpioPin> Pin15 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin15, 8) internal static readonly Lazy<GpioPin> Pin24 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin24, 35) {
{ Capabilities = new[] { PinCapability.GP, PinCapability.SPIMISO },
Capabilities = new[] { PinCapability.UARTTXD }, Name = "BCM 19 (MISO)"
Name = "BCM 14 (TXD)" });
});
internal static readonly Lazy<GpioPin> Pin25 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin25, 37) {
internal static readonly Lazy<GpioPin> Pin21 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin21, 29) Capabilities = new[] { PinCapability.GP },
{ Name = "BCM 26"
Capabilities = new[] { PinCapability.GP }, });
Name = "BCM 5"
}); internal static readonly Lazy<GpioPin> Pin29 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin29, 40) {
Capabilities = new[] { PinCapability.GP, PinCapability.SPICLK },
internal static readonly Lazy<GpioPin> Pin22 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin22, 31) Name = "BCM 21 (SCLK)"
{ });
Capabilities = new[] { PinCapability.GP },
Name = "BCM 6" internal static readonly Lazy<GpioPin> Pin28 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin28, 38) {
}); Capabilities = new[] { PinCapability.GP, PinCapability.SPIMOSI },
Name = "BCM 20 (MOSI)"
internal static readonly Lazy<GpioPin> Pin23 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin23, 33) });
{
Capabilities = new[] { PinCapability.GP, PinCapability.PWM }, internal static readonly Lazy<GpioPin> Pin27 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin27, 36) {
Name = "BCM 13 (PWM1)" Capabilities = new[] { PinCapability.GP },
}); Name = "BCM 16"
});
internal static readonly Lazy<GpioPin> Pin24 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin24, 35) internal static readonly Lazy<GpioPin> Pin26 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin26, 32) {
{ Capabilities = new[] { PinCapability.GP },
Capabilities = new[] { PinCapability.GP, PinCapability.SPIMISO }, Name = "BCM 12 (PWM0)"
Name = "BCM 19 (MISO)" });
});
internal static readonly Lazy<GpioPin> Pin17 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin17, 3) {
internal static readonly Lazy<GpioPin> Pin25 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin25, 37) Capabilities = new[] { PinCapability.GP, PinCapability.I2CSDA },
{ Name = "BCM 28 (SDA)"
Capabilities = new[] { PinCapability.GP }, });
Name = "BCM 26"
}); internal static readonly Lazy<GpioPin> Pin18 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin18, 4) {
Capabilities = new[] { PinCapability.GP, PinCapability.I2CSCL },
internal static readonly Lazy<GpioPin> Pin29 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin29, 40) Name = "BCM 29 (SCL)"
{ });
Capabilities = new[] { PinCapability.GP, PinCapability.SPICLK },
Name = "BCM 21 (SCLK)" internal static readonly Lazy<GpioPin> Pin19 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin19, 5) {
}); Capabilities = new[] { PinCapability.GP },
Name = "BCM 30"
internal static readonly Lazy<GpioPin> Pin28 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin28, 38) });
{
Capabilities = new[] { PinCapability.GP, PinCapability.SPIMOSI }, internal static readonly Lazy<GpioPin> Pin20 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin20, 6) {
Name = "BCM 20 (MOSI)" Capabilities = new[] { PinCapability.GP },
}); Name = "BCM 31"
});
internal static readonly Lazy<GpioPin> Pin27 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin27, 36)
{ #endregion
Capabilities = new[] { PinCapability.GP }, }
Name = "BCM 16"
});
internal static readonly Lazy<GpioPin> Pin26 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin26, 32)
{
Capabilities = new[] { PinCapability.GP },
Name = "BCM 12 (PWM0)"
});
internal static readonly Lazy<GpioPin> Pin17 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin17, 3)
{
Capabilities = new[] { PinCapability.GP, PinCapability.I2CSDA },
Name = "BCM 28 (SDA)"
});
internal static readonly Lazy<GpioPin> Pin18 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin18, 4)
{
Capabilities = new[] { PinCapability.GP, PinCapability.I2CSCL },
Name = "BCM 29 (SCL)"
});
internal static readonly Lazy<GpioPin> Pin19 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin19, 5)
{
Capabilities = new[] { PinCapability.GP },
Name = "BCM 30"
});
internal static readonly Lazy<GpioPin> Pin20 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin20, 6)
{
Capabilities = new[] { PinCapability.GP },
Name = "BCM 31"
});
#endregion
}
} }
File diff suppressed because it is too large Load Diff
+87 -93
View File
@@ -1,93 +1,87 @@
namespace Unosquare.RaspberryIO.Gpio using Unosquare.RaspberryIO.Native;
{ using Unosquare.Swan.Abstractions;
using Native; using System.Collections.Generic;
using Swan.Abstractions; using System.Collections.ObjectModel;
using System.Collections.Generic; using System.Linq;
using System.Collections.ObjectModel; using System;
using System.Linq;
namespace Unosquare.RaspberryIO.Gpio {
/// <summary> /// <summary>
/// A simple wrapper for the I2c bus on the Raspberry Pi /// A simple wrapper for the I2c bus on the Raspberry Pi
/// </summary> /// </summary>
public class I2CBus : SingletonBase<I2CBus> public class I2CBus : SingletonBase<I2CBus> {
{ // TODO: It would be nice to integrate i2c device detection.
// TODO: It would be nice to integrate i2c device detection. private static readonly Object SyncRoot = new Object();
private static readonly object SyncRoot = new object(); private readonly Dictionary<Int32, I2CDevice> _devices = new Dictionary<Int32, I2CDevice>();
private readonly Dictionary<int, I2CDevice> _devices = new Dictionary<int, I2CDevice>();
/// <summary>
/// <summary> /// Prevents a default instance of the <see cref="I2CBus"/> class from being created.
/// Prevents a default instance of the <see cref="I2CBus"/> class from being created. /// </summary>
/// </summary> private I2CBus() {
private I2CBus() // placeholder
{ }
// placeholder
} /// <summary>
/// Gets the registered devices as a read only collection.
/// <summary> /// </summary>
/// Gets the registered devices as a read only collection. public ReadOnlyCollection<I2CDevice> Devices => new ReadOnlyCollection<I2CDevice>(this._devices.Values.ToArray());
/// </summary>
public ReadOnlyCollection<I2CDevice> Devices => new ReadOnlyCollection<I2CDevice>(_devices.Values.ToArray()); /// <summary>
/// Gets the <see cref="I2CDevice"/> with the specified device identifier.
/// <summary> /// </summary>
/// Gets the <see cref="I2CDevice"/> with the specified device identifier. /// <value>
/// </summary> /// The <see cref="I2CDevice"/>.
/// <value> /// </value>
/// The <see cref="I2CDevice"/>. /// <param name="deviceId">The device identifier.</param>
/// </value> /// <returns>A reference to an I2C device</returns>
/// <param name="deviceId">The device identifier.</param> public I2CDevice this[Int32 deviceId] => this.GetDeviceById(deviceId);
/// <returns>A reference to an I2C device</returns>
public I2CDevice this[int deviceId] => GetDeviceById(deviceId); /// <summary>
/// Gets the device by identifier.
/// <summary> /// </summary>
/// Gets the device by identifier. /// <param name="deviceId">The device identifier.</param>
/// </summary> /// <returns>The device reference</returns>
/// <param name="deviceId">The device identifier.</param> public I2CDevice GetDeviceById(Int32 deviceId) {
/// <returns>The device reference</returns> lock(SyncRoot) {
public I2CDevice GetDeviceById(int deviceId) return this._devices[deviceId];
{ }
lock (SyncRoot) }
{
return _devices[deviceId]; /// <summary>
} /// Adds a device to the bus by its Id. If the device is already registered it simply returns the existing device.
} /// </summary>
/// <param name="deviceId">The device identifier.</param>
/// <summary> /// <returns>The device reference</returns>
/// Adds a device to the bus by its Id. If the device is already registered it simply returns the existing device. /// <exception cref="KeyNotFoundException">When the device file descriptor is not found</exception>
/// </summary> public I2CDevice AddDevice(Int32 deviceId) {
/// <param name="deviceId">The device identifier.</param> lock(SyncRoot) {
/// <returns>The device reference</returns> if(this._devices.ContainsKey(deviceId)) {
/// <exception cref="KeyNotFoundException">When the device file descriptor is not found</exception> return this._devices[deviceId];
public I2CDevice AddDevice(int deviceId) }
{
lock (SyncRoot) Int32 fileDescriptor = SetupFileDescriptor(deviceId);
{ if(fileDescriptor < 0) {
if (_devices.ContainsKey(deviceId)) throw new KeyNotFoundException($"Device with id {deviceId} could not be registered with the I2C bus. Error Code: {fileDescriptor}.");
return _devices[deviceId]; }
var fileDescriptor = SetupFileDescriptor(deviceId); I2CDevice device = new I2CDevice(deviceId, fileDescriptor);
if (fileDescriptor < 0) this._devices[deviceId] = device;
throw new KeyNotFoundException($"Device with id {deviceId} could not be registered with the I2C bus. Error Code: {fileDescriptor}."); return device;
}
var device = new I2CDevice(deviceId, fileDescriptor); }
_devices[deviceId] = device;
return device; /// <summary>
} /// This initializes the I2C system with your given device identifier.
} /// The ID is the I2C number of the device and you can use the i2cdetect program to find this out.
/// wiringPiI2CSetup() will work out which revision Raspberry Pi you have and open the appropriate device in /dev.
/// <summary> /// The return value is the standard Linux filehandle, or -1 if any error – in which case, you can consult errno as usual.
/// This initializes the I2C system with your given device identifier. /// </summary>
/// The ID is the I2C number of the device and you can use the i2cdetect program to find this out. /// <param name="deviceId">The device identifier.</param>
/// wiringPiI2CSetup() will work out which revision Raspberry Pi you have and open the appropriate device in /dev. /// <returns>The Linux file handle</returns>
/// The return value is the standard Linux filehandle, or -1 if any error – in which case, you can consult errno as usual. private static Int32 SetupFileDescriptor(Int32 deviceId) {
/// </summary> lock(SyncRoot) {
/// <param name="deviceId">The device identifier.</param> return WiringPi.WiringPiI2CSetup(deviceId);
/// <returns>The Linux file handle</returns> }
private static int SetupFileDescriptor(int deviceId) }
{ }
lock (SyncRoot) }
{
return WiringPi.WiringPiI2CSetup(deviceId);
}
}
}
}
+193 -195
View File
@@ -1,195 +1,193 @@
namespace Unosquare.RaspberryIO.Gpio using System;
{ using System.Threading.Tasks;
using System; using Unosquare.RaspberryIO.Native;
using System.Threading.Tasks;
using Native; namespace Unosquare.RaspberryIO.Gpio {
/// <summary>
/// <summary> /// Represents a device on the I2C Bus
/// Represents a device on the I2C Bus /// </summary>
/// </summary> public class I2CDevice {
public class I2CDevice private readonly Object _syncLock = new Object();
{
private readonly object _syncLock = new object(); /// <summary>
/// Initializes a new instance of the <see cref="I2CDevice"/> class.
/// <summary> /// </summary>
/// Initializes a new instance of the <see cref="I2CDevice"/> class. /// <param name="deviceId">The device identifier.</param>
/// </summary> /// <param name="fileDescriptor">The file descriptor.</param>
/// <param name="deviceId">The device identifier.</param> internal I2CDevice(Int32 deviceId, Int32 fileDescriptor) {
/// <param name="fileDescriptor">The file descriptor.</param> this.DeviceId = deviceId;
internal I2CDevice(int deviceId, int fileDescriptor) this.FileDescriptor = fileDescriptor;
{ }
DeviceId = deviceId;
FileDescriptor = fileDescriptor; /// <summary>
} /// Gets the device identifier.
/// </summary>
/// <summary> /// <value>
/// Gets the device identifier. /// The device identifier.
/// </summary> /// </value>
/// <value> public Int32 DeviceId {
/// The device identifier. get;
/// </value> }
public int DeviceId { get; }
/// <summary>
/// <summary> /// Gets the standard POSIX file descriptor.
/// Gets the standard POSIX file descriptor. /// </summary>
/// </summary> /// <value>
/// <value> /// The file descriptor.
/// The file descriptor. /// </value>
/// </value> public Int32 FileDescriptor {
public int FileDescriptor { get; } get;
}
/// <summary>
/// Reads a byte from the specified file descriptor /// <summary>
/// </summary> /// Reads a byte from the specified file descriptor
/// <returns>The byte from device</returns> /// </summary>
public byte Read() /// <returns>The byte from device</returns>
{ public Byte Read() {
lock (_syncLock) lock(this._syncLock) {
{ Int32 result = WiringPi.WiringPiI2CRead(this.FileDescriptor);
var result = WiringPi.WiringPiI2CRead(FileDescriptor); if(result < 0) {
if (result < 0) HardwareException.Throw(nameof(I2CDevice), nameof(Read)); HardwareException.Throw(nameof(I2CDevice), nameof(Read));
return (byte)result; }
}
} return (Byte)result;
}
/// <summary> }
/// Reads a byte from the specified file descriptor
/// </summary> /// <summary>
/// <returns>The byte from device</returns> /// Reads a byte from the specified file descriptor
public Task<byte> ReadAsync() => Task.Run(() => Read()); /// </summary>
/// <returns>The byte from device</returns>
/// <summary> public Task<Byte> ReadAsync() => Task.Run(() => this.Read());
/// Reads a buffer of the specified length, one byte at a time
/// </summary> /// <summary>
/// <param name="length">The length.</param> /// Reads a buffer of the specified length, one byte at a time
/// <returns>The byte array from device</returns> /// </summary>
public byte[] Read(int length) /// <param name="length">The length.</param>
{ /// <returns>The byte array from device</returns>
lock (_syncLock) public Byte[] Read(Int32 length) {
{ lock(this._syncLock) {
var buffer = new byte[length]; Byte[] buffer = new Byte[length];
for (var i = 0; i < length; i++) for(Int32 i = 0; i < length; i++) {
{ Int32 result = WiringPi.WiringPiI2CRead(this.FileDescriptor);
var result = WiringPi.WiringPiI2CRead(FileDescriptor); if(result < 0) {
if (result < 0) HardwareException.Throw(nameof(I2CDevice), nameof(Read)); HardwareException.Throw(nameof(I2CDevice), nameof(Read));
buffer[i] = (byte)result; }
}
buffer[i] = (Byte)result;
return buffer; }
}
} return buffer;
}
/// <summary> }
/// Reads a buffer of the specified length, one byte at a time
/// </summary> /// <summary>
/// <param name="length">The length.</param> /// Reads a buffer of the specified length, one byte at a time
/// <returns>The byte array from device</returns> /// </summary>
public Task<byte[]> ReadAsync(int length) => Task.Run(() => Read(length)); /// <param name="length">The length.</param>
/// <returns>The byte array from device</returns>
/// <summary> public Task<Byte[]> ReadAsync(Int32 length) => Task.Run(() => this.Read(length));
/// Writes a byte of data the specified file descriptor.
/// </summary> /// <summary>
/// <param name="data">The data.</param> /// Writes a byte of data the specified file descriptor.
public void Write(byte data) /// </summary>
{ /// <param name="data">The data.</param>
lock (_syncLock) public void Write(Byte data) {
{ lock(this._syncLock) {
var result = WiringPi.WiringPiI2CWrite(FileDescriptor, data); Int32 result = WiringPi.WiringPiI2CWrite(this.FileDescriptor, data);
if (result < 0) HardwareException.Throw(nameof(I2CDevice), nameof(Write)); if(result < 0) {
} HardwareException.Throw(nameof(I2CDevice), nameof(Write));
} }
}
/// <summary> }
/// Writes a byte of data the specified file descriptor.
/// </summary> /// <summary>
/// <param name="data">The data.</param> /// Writes a byte of data the specified file descriptor.
/// <returns>The awaitable task</returns> /// </summary>
public Task WriteAsync(byte data) => Task.Run(() => { Write(data); }); /// <param name="data">The data.</param>
/// <returns>The awaitable task</returns>
/// <summary> public Task WriteAsync(Byte data) => Task.Run(() => this.Write(data));
/// Writes a set of bytes to the specified file descriptor.
/// </summary> /// <summary>
/// <param name="data">The data.</param> /// Writes a set of bytes to the specified file descriptor.
public void Write(byte[] data) /// </summary>
{ /// <param name="data">The data.</param>
lock (_syncLock) public void Write(Byte[] data) {
{ lock(this._syncLock) {
foreach (var b in data) foreach(Byte b in data) {
{ Int32 result = WiringPi.WiringPiI2CWrite(this.FileDescriptor, b);
var result = WiringPi.WiringPiI2CWrite(FileDescriptor, b); if(result < 0) {
if (result < 0) HardwareException.Throw(nameof(I2CDevice), nameof(Write)); HardwareException.Throw(nameof(I2CDevice), nameof(Write));
} }
} }
} }
}
/// <summary>
/// Writes a set of bytes to the specified file descriptor. /// <summary>
/// </summary> /// Writes a set of bytes to the specified file descriptor.
/// <param name="data">The data.</param> /// </summary>
/// <returns>The awaitable task</returns> /// <param name="data">The data.</param>
public Task WriteAsync(byte[] data) /// <returns>The awaitable task</returns>
{ public Task WriteAsync(Byte[] data) => Task.Run(() => this.Write(data));
return Task.Run(() => { Write(data); });
} /// <summary>
/// These write an 8 or 16-bit data value into the device register indicated.
/// <summary> /// </summary>
/// These write an 8 or 16-bit data value into the device register indicated. /// <param name="address">The register.</param>
/// </summary> /// <param name="data">The data.</param>
/// <param name="address">The register.</param> public void WriteAddressByte(Int32 address, Byte data) {
/// <param name="data">The data.</param> lock(this._syncLock) {
public void WriteAddressByte(int address, byte data) Int32 result = WiringPi.WiringPiI2CWriteReg8(this.FileDescriptor, address, data);
{ if(result < 0) {
lock (_syncLock) HardwareException.Throw(nameof(I2CDevice), nameof(WriteAddressByte));
{ }
var result = WiringPi.WiringPiI2CWriteReg8(FileDescriptor, address, data); }
if (result < 0) HardwareException.Throw(nameof(I2CDevice), nameof(WriteAddressByte)); }
}
} /// <summary>
/// These write an 8 or 16-bit data value into the device register indicated.
/// <summary> /// </summary>
/// These write an 8 or 16-bit data value into the device register indicated. /// <param name="address">The register.</param>
/// </summary> /// <param name="data">The data.</param>
/// <param name="address">The register.</param> public void WriteAddressWord(Int32 address, UInt16 data) {
/// <param name="data">The data.</param> lock(this._syncLock) {
public void WriteAddressWord(int address, ushort data) Int32 result = WiringPi.WiringPiI2CWriteReg16(this.FileDescriptor, address, data);
{ if(result < 0) {
lock (_syncLock) HardwareException.Throw(nameof(I2CDevice), nameof(WriteAddressWord));
{ }
var result = WiringPi.WiringPiI2CWriteReg16(FileDescriptor, address, data); }
if (result < 0) HardwareException.Throw(nameof(I2CDevice), nameof(WriteAddressWord)); }
}
} /// <summary>
/// These read an 8 or 16-bit value from the device register indicated.
/// <summary> /// </summary>
/// These read an 8 or 16-bit value from the device register indicated. /// <param name="address">The register.</param>
/// </summary> /// <returns>The address byte from device</returns>
/// <param name="address">The register.</param> public Byte ReadAddressByte(Int32 address) {
/// <returns>The address byte from device</returns> lock(this._syncLock) {
public byte ReadAddressByte(int address) Int32 result = WiringPi.WiringPiI2CReadReg8(this.FileDescriptor, address);
{ if(result < 0) {
lock (_syncLock) HardwareException.Throw(nameof(I2CDevice), nameof(ReadAddressByte));
{ }
var result = WiringPi.WiringPiI2CReadReg8(FileDescriptor, address);
if (result < 0) HardwareException.Throw(nameof(I2CDevice), nameof(ReadAddressByte)); return (Byte)result;
}
return (byte)result; }
}
} /// <summary>
/// These read an 8 or 16-bit value from the device register indicated.
/// <summary> /// </summary>
/// These read an 8 or 16-bit value from the device register indicated. /// <param name="address">The register.</param>
/// </summary> /// <returns>The address word from device</returns>
/// <param name="address">The register.</param> public UInt16 ReadAddressWord(Int32 address) {
/// <returns>The address word from device</returns> lock(this._syncLock) {
public ushort ReadAddressWord(int address) Int32 result = WiringPi.WiringPiI2CReadReg16(this.FileDescriptor, address);
{ if(result < 0) {
lock (_syncLock) HardwareException.Throw(nameof(I2CDevice), nameof(ReadAddressWord));
{ }
var result = WiringPi.WiringPiI2CReadReg16(FileDescriptor, address);
if (result < 0) HardwareException.Throw(nameof(I2CDevice), nameof(ReadAddressWord)); return Convert.ToUInt16(result);
}
return Convert.ToUInt16(result); }
} }
} }
}
}
+69 -69
View File
@@ -1,72 +1,72 @@
namespace Unosquare.RaspberryIO.Gpio using System;
{ using Unosquare.Swan.Abstractions;
using Swan.Abstractions;
namespace Unosquare.RaspberryIO.Gpio {
/// <summary>
/// The SPI Bus containing the 2 SPI channels
/// </summary>
public class SpiBus : SingletonBase<SpiBus> {
/// <summary> /// <summary>
/// The SPI Bus containing the 2 SPI channels /// Prevents a default instance of the <see cref="SpiBus"/> class from being created.
/// </summary> /// </summary>
public class SpiBus : SingletonBase<SpiBus> private SpiBus() {
{ // placeholder
/// <summary> }
/// Prevents a default instance of the <see cref="SpiBus"/> class from being created.
/// </summary> #region SPI Access
private SpiBus()
{ /// <summary>
// placeholder /// Gets or sets the channel 0 frequency in Hz.
} /// </summary>
/// <value>
#region SPI Access /// The channel0 frequency.
/// </value>
/// <summary> public Int32 Channel0Frequency {
/// Gets or sets the channel 0 frequency in Hz. get; set;
/// </summary> }
/// <value>
/// The channel0 frequency. /// <summary>
/// </value> /// Gets the SPI bus on channel 1.
public int Channel0Frequency { get; set; } /// </summary>
/// <value>
/// <summary> /// The channel0.
/// Gets the SPI bus on channel 1. /// </value>
/// </summary> public SpiChannel Channel0 {
/// <value> get {
/// The channel0. if(this.Channel0Frequency == 0) {
/// </value> this.Channel0Frequency = SpiChannel.DefaultFrequency;
public SpiChannel Channel0 }
{
get return SpiChannel.Retrieve(SpiChannelNumber.Channel0, this.Channel0Frequency);
{ }
if (Channel0Frequency == 0) }
Channel0Frequency = SpiChannel.DefaultFrequency;
/// <summary>
return SpiChannel.Retrieve(SpiChannelNumber.Channel0, Channel0Frequency); /// Gets or sets the channel 1 frequency in Hz
} /// </summary>
} /// <value>
/// The channel1 frequency.
/// <summary> /// </value>
/// Gets or sets the channel 1 frequency in Hz public Int32 Channel1Frequency {
/// </summary> get; set;
/// <value> }
/// The channel1 frequency.
/// </value> /// <summary>
public int Channel1Frequency { get; set; } /// Gets the SPI bus on channel 1.
/// </summary>
/// <summary> /// <value>
/// Gets the SPI bus on channel 1. /// The channel1.
/// </summary> /// </value>
/// <value> public SpiChannel Channel1 {
/// The channel1. get {
/// </value> if(this.Channel1Frequency == 0) {
public SpiChannel Channel1 this.Channel1Frequency = SpiChannel.DefaultFrequency;
{ }
get
{ return SpiChannel.Retrieve(SpiChannelNumber.Channel1, this.Channel1Frequency);
if (Channel1Frequency == 0) }
Channel1Frequency = SpiChannel.DefaultFrequency; }
return SpiChannel.Retrieve(SpiChannelNumber.Channel1, Channel1Frequency); #endregion
} }
}
#endregion
}
} }
+151 -151
View File
@@ -1,154 +1,154 @@
namespace Unosquare.RaspberryIO.Gpio using Unosquare.RaspberryIO.Native;
{ using Unosquare.Swan;
using Native; using System;
using Swan; using System.Collections.Generic;
using System; using System.Threading.Tasks;
using System.Collections.Generic;
using System.Threading.Tasks; namespace Unosquare.RaspberryIO.Gpio {
/// <summary>
/// Provides access to using the SPI buses on the GPIO.
/// SPI is a bus that works like a ring shift register
/// The number of bytes pushed is equal to the number of bytes received.
/// </summary>
public sealed class SpiChannel {
/// <summary> /// <summary>
/// Provides access to using the SPI buses on the GPIO. /// The minimum frequency of an SPI Channel
/// SPI is a bus that works like a ring shift register
/// The number of bytes pushed is equal to the number of bytes received.
/// </summary> /// </summary>
public sealed class SpiChannel public const Int32 MinFrequency = 500000;
{
/// <summary> /// <summary>
/// The minimum frequency of an SPI Channel /// The maximum frequency of an SPI channel
/// </summary> /// </summary>
public const int MinFrequency = 500000; public const Int32 MaxFrequency = 32000000;
/// <summary> /// <summary>
/// The maximum frequency of an SPI channel /// The default frequency of SPI channels
/// </summary> /// This is set to 8 Mhz wich is typical in modern hardware.
public const int MaxFrequency = 32000000; /// </summary>
public const Int32 DefaultFrequency = 8000000;
/// <summary>
/// The default frequency of SPI channels private static readonly Object SyncRoot = new Object();
/// This is set to 8 Mhz wich is typical in modern hardware. private static readonly Dictionary<SpiChannelNumber, SpiChannel> Buses = new Dictionary<SpiChannelNumber, SpiChannel>();
/// </summary> private readonly Object _syncLock = new Object();
public const int DefaultFrequency = 8000000;
/// <summary>
private static readonly object SyncRoot = new object(); /// Initializes a new instance of the <see cref="SpiChannel"/> class.
private static readonly Dictionary<SpiChannelNumber, SpiChannel> Buses = new Dictionary<SpiChannelNumber, SpiChannel>(); /// </summary>
private readonly object _syncLock = new object(); /// <param name="channel">The channel.</param>
/// <param name="frequency">The frequency.</param>
/// <summary> private SpiChannel(SpiChannelNumber channel, Int32 frequency) {
/// Initializes a new instance of the <see cref="SpiChannel"/> class. lock(SyncRoot) {
/// </summary> this.Frequency = frequency.Clamp(MinFrequency, MaxFrequency);
/// <param name="channel">The channel.</param> this.Channel = (Int32)channel;
/// <param name="frequency">The frequency.</param> this.FileDescriptor = WiringPi.WiringPiSPISetup((Int32)channel, this.Frequency);
private SpiChannel(SpiChannelNumber channel, int frequency)
{ if(this.FileDescriptor < 0) {
lock (SyncRoot) HardwareException.Throw(nameof(SpiChannel), channel.ToString());
{ }
Frequency = frequency.Clamp(MinFrequency, MaxFrequency); }
Channel = (int)channel; }
FileDescriptor = WiringPi.WiringPiSPISetup((int)channel, Frequency);
/// <summary>
if (FileDescriptor < 0) /// Gets the standard initialization file descriptor.
{ /// anything negative means error.
HardwareException.Throw(nameof(SpiChannel), channel.ToString()); /// </summary>
} /// <value>
} /// The file descriptor.
} /// </value>
public Int32 FileDescriptor {
/// <summary> get;
/// Gets the standard initialization file descriptor. }
/// anything negative means error.
/// </summary> /// <summary>
/// <value> /// Gets the channel.
/// The file descriptor. /// </summary>
/// </value> public Int32 Channel {
public int FileDescriptor { get; } get;
}
/// <summary>
/// Gets the channel. /// <summary>
/// </summary> /// Gets the frequency.
public int Channel { get; } /// </summary>
public Int32 Frequency {
/// <summary> get;
/// Gets the frequency. }
/// </summary>
public int Frequency { get; } /// <summary>
/// Sends data and simultaneously receives the data in the return buffer
/// <summary> /// </summary>
/// Sends data and simultaneously receives the data in the return buffer /// <param name="buffer">The buffer.</param>
/// </summary> /// <returns>The read bytes from the ring-style bus</returns>
/// <param name="buffer">The buffer.</param> public Byte[] SendReceive(Byte[] buffer) {
/// <returns>The read bytes from the ring-style bus</returns> if(buffer == null || buffer.Length == 0) {
public byte[] SendReceive(byte[] buffer) return null;
{ }
if (buffer == null || buffer.Length == 0)
return null; lock(this._syncLock) {
Byte[] spiBuffer = new Byte[buffer.Length];
lock (_syncLock) Array.Copy(buffer, spiBuffer, buffer.Length);
{
var spiBuffer = new byte[buffer.Length]; Int32 result = WiringPi.WiringPiSPIDataRW(this.Channel, spiBuffer, spiBuffer.Length);
Array.Copy(buffer, spiBuffer, buffer.Length); if(result < 0) {
HardwareException.Throw(nameof(SpiChannel), nameof(SendReceive));
var result = WiringPi.WiringPiSPIDataRW(Channel, spiBuffer, spiBuffer.Length); }
if (result < 0) HardwareException.Throw(nameof(SpiChannel), nameof(SendReceive));
return spiBuffer;
return spiBuffer; }
} }
}
/// <summary>
/// <summary> /// Sends data and simultaneously receives the data in the return buffer
/// Sends data and simultaneously receives the data in the return buffer /// </summary>
/// </summary> /// <param name="buffer">The buffer.</param>
/// <param name="buffer">The buffer.</param> /// <returns>
/// <returns> /// The read bytes from the ring-style bus
/// The read bytes from the ring-style bus /// </returns>
/// </returns> public Task<Byte[]> SendReceiveAsync(Byte[] buffer) => Task.Run(() => this.SendReceive(buffer));
public Task<byte[]> SendReceiveAsync(byte[] buffer) => Task.Run(() => SendReceive(buffer));
/// <summary>
/// <summary> /// Writes the specified buffer the the underlying FileDescriptor.
/// Writes the specified buffer the the underlying FileDescriptor. /// Do not use this method if you expect data back.
/// Do not use this method if you expect data back. /// This method is efficient if used in a fire-and-forget scenario
/// This method is efficient if used in a fire-and-forget scenario /// like sending data over to those long RGB LED strips
/// like sending data over to those long RGB LED strips /// </summary>
/// </summary> /// <param name="buffer">The buffer.</param>
/// <param name="buffer">The buffer.</param> public void Write(Byte[] buffer) {
public void Write(byte[] buffer) lock(this._syncLock) {
{ Int32 result = Standard.Write(this.FileDescriptor, buffer, buffer.Length);
lock (_syncLock)
{ if(result < 0) {
var result = Standard.Write(FileDescriptor, buffer, buffer.Length); HardwareException.Throw(nameof(SpiChannel), nameof(Write));
}
if (result < 0) }
HardwareException.Throw(nameof(SpiChannel), nameof(Write)); }
}
} /// <summary>
/// Writes the specified buffer the the underlying FileDescriptor.
/// <summary> /// Do not use this method if you expect data back.
/// Writes the specified buffer the the underlying FileDescriptor. /// This method is efficient if used in a fire-and-forget scenario
/// Do not use this method if you expect data back. /// like sending data over to those long RGB LED strips
/// This method is efficient if used in a fire-and-forget scenario /// </summary>
/// like sending data over to those long RGB LED strips /// <param name="buffer">The buffer.</param>
/// </summary> /// <returns>The awaitable task</returns>
/// <param name="buffer">The buffer.</param> public Task WriteAsync(Byte[] buffer) => Task.Run(() => this.Write(buffer));
/// <returns>The awaitable task</returns>
public Task WriteAsync(byte[] buffer) => Task.Run(() => { Write(buffer); }); /// <summary>
/// Retrieves the spi bus. If the bus channel is not registered it sets it up automatically.
/// <summary> /// If it had been previously registered, then the bus is simply returned.
/// Retrieves the spi bus. If the bus channel is not registered it sets it up automatically. /// </summary>
/// If it had been previously registered, then the bus is simply returned. /// <param name="channel">The channel.</param>
/// </summary> /// <param name="frequency">The frequency.</param>
/// <param name="channel">The channel.</param> /// <returns>The usable SPI channel</returns>
/// <param name="frequency">The frequency.</param> internal static SpiChannel Retrieve(SpiChannelNumber channel, Int32 frequency) {
/// <returns>The usable SPI channel</returns> lock(SyncRoot) {
internal static SpiChannel Retrieve(SpiChannelNumber channel, int frequency) if(Buses.ContainsKey(channel)) {
{ return Buses[channel];
lock (SyncRoot) }
{
if (Buses.ContainsKey(channel)) SpiChannel newBus = new SpiChannel(channel, frequency);
return Buses[channel]; Buses[channel] = newBus;
return newBus;
var newBus = new SpiChannel(channel, frequency); }
Buses[channel] = newBus; }
return newBus; }
}
}
}
} }
+10 -11
View File
@@ -1,12 +1,11 @@
namespace Unosquare.RaspberryIO.Native namespace Unosquare.RaspberryIO.Native {
{ /// <summary>
/// <summary> /// A delegate defining a callback for an Interrupt Service Routine
/// A delegate defining a callback for an Interrupt Service Routine /// </summary>
/// </summary> public delegate void InterruptServiceRoutineCallback();
public delegate void InterruptServiceRoutineCallback();
/// <summary>
/// <summary> /// Defines the body of a thread worker
/// Defines the body of a thread worker /// </summary>
/// </summary> public delegate void ThreadWorker();
public delegate void ThreadWorker();
} }
@@ -1,73 +1,73 @@
namespace Unosquare.RaspberryIO.Native using Unosquare.Swan;
{ using System;
using Swan; using System.Runtime.InteropServices;
using System;
using System.Runtime.InteropServices; namespace Unosquare.RaspberryIO.Native {
/// <summary>
/// Represents a low-level exception, typically thrown when return codes from a
/// low-level operation is non-zero or in some cases when it is less than zero.
/// </summary>
/// <seealso cref="Exception" />
public class HardwareException : Exception {
/// <summary> /// <summary>
/// Represents a low-level exception, typically thrown when return codes from a /// Initializes a new instance of the <see cref="HardwareException" /> class.
/// low-level operation is non-zero or in some cases when it is less than zero.
/// </summary> /// </summary>
/// <seealso cref="Exception" /> /// <param name="errorCode">The error code.</param>
public class HardwareException : Exception /// <param name="component">The component.</param>
{ public HardwareException(Int32 errorCode, String component)
/// <summary> : base($"A hardware exception occurred. Error Code: {errorCode}") {
/// Initializes a new instance of the <see cref="HardwareException" /> class. this.ExtendedMessage = null;
/// </summary>
/// <param name="errorCode">The error code.</param> try {
/// <param name="component">The component.</param> this.ExtendedMessage = Standard.Strerror(errorCode);
public HardwareException(int errorCode, string component) } catch {
: base($"A hardware exception occurred. Error Code: {errorCode}") // TODO: strerror not working great...
{ $"Could not retrieve native error description using {nameof(Standard.Strerror)}".Error(Pi.LoggerSource);
ExtendedMessage = null; }
try this.ErrorCode = errorCode;
{ this.Component = component;
ExtendedMessage = Standard.Strerror(errorCode); }
}
catch /// <summary>
{ /// Gets the error code.
// TODO: strerror not working great... /// </summary>
$"Could not retrieve native error description using {nameof(Standard.Strerror)}".Error(Pi.LoggerSource); /// <value>
} /// The error code.
/// </value>
ErrorCode = errorCode; public Int32 ErrorCode {
Component = component; get;
} }
/// <summary> /// <summary>
/// Gets the error code. /// Gets the component.
/// </summary> /// </summary>
/// <value> /// <value>
/// The error code. /// The component.
/// </value> /// </value>
public int ErrorCode { get; } public String Component {
get;
/// <summary> }
/// Gets the component.
/// </summary> /// <summary>
/// <value> /// Gets the extended message (could be null).
/// The component. /// </summary>
/// </value> /// <value>
public string Component { get; } /// The extended message.
/// </value>
/// <summary> public String ExtendedMessage {
/// Gets the extended message (could be null). get;
/// </summary> }
/// <value>
/// The extended message. /// <summary>
/// </value> /// Throws a new instance of a hardware error by retrieving the last error number (errno).
public string ExtendedMessage { get; } /// </summary>
/// <param name="className">Name of the class.</param>
/// <summary> /// <param name="methodName">Name of the method.</param>
/// Throws a new instance of a hardware error by retrieving the last error number (errno). /// <exception cref="HardwareException">When an error thrown by an API call occurs</exception>
/// </summary> public static void Throw(String className, String methodName) => throw new HardwareException(Marshal.GetLastWin32Error(), $"{className}.{methodName}");
/// <param name="className">Name of the class.</param>
/// <param name="methodName">Name of the method.</param> /// <inheritdoc />
/// <exception cref="HardwareException">When an error thrown by an API call occurs</exception> public override String ToString() => $"{this.GetType()}{(String.IsNullOrWhiteSpace(this.Component) ? String.Empty : $" on {this.Component}")}: ({this.ErrorCode}) - {this.Message}";
public static void Throw(string className, string methodName) => throw new HardwareException(Marshal.GetLastWin32Error(), $"{className}.{methodName}"); }
/// <inheritdoc />
public override string ToString() => $"{GetType()}{(string.IsNullOrWhiteSpace(Component) ? string.Empty : $" on {Component}")}: ({ErrorCode}) - {Message}";
}
} }
@@ -1,32 +1,30 @@
namespace Unosquare.RaspberryIO.Native using System;
{ using System.Diagnostics;
using System;
using System.Diagnostics; namespace Unosquare.RaspberryIO.Native {
/// <summary>
/// Provides access to a high- esolution, time measuring device.
/// </summary>
/// <seealso cref="Stopwatch" />
public class HighResolutionTimer : Stopwatch {
/// <summary> /// <summary>
/// Provides access to a high- esolution, time measuring device. /// Initializes a new instance of the <see cref="HighResolutionTimer"/> class.
/// </summary> /// </summary>
/// <seealso cref="Stopwatch" /> /// <exception cref="NotSupportedException">High-resolution timer not available</exception>
public class HighResolutionTimer : Stopwatch public HighResolutionTimer() {
{ if(!IsHighResolution) {
/// <summary> throw new NotSupportedException("High-resolution timer not available");
/// Initializes a new instance of the <see cref="HighResolutionTimer"/> class. }
/// </summary> }
/// <exception cref="NotSupportedException">High-resolution timer not available</exception>
public HighResolutionTimer() /// <summary>
{ /// Gets the numer of microseconds per timer tick.
if (!IsHighResolution) /// </summary>
throw new NotSupportedException("High-resolution timer not available"); public static Double MicrosecondsPerTick { get; } = 1000000d / Frequency;
}
/// <summary>
/// <summary> /// Gets the elapsed microseconds.
/// Gets the numer of microseconds per timer tick. /// </summary>
/// </summary> public Int64 ElapsedMicroseconds => (Int64)(this.ElapsedTicks * MicrosecondsPerTick);
public static double MicrosecondsPerTick { get; } = 1000000d / Frequency; }
/// <summary>
/// Gets the elapsed microseconds.
/// </summary>
public long ElapsedMicroseconds => (long)(ElapsedTicks * MicrosecondsPerTick);
}
} }
+77 -81
View File
@@ -1,84 +1,80 @@
namespace Unosquare.RaspberryIO.Native using Unosquare.Swan;
{ using System;
using Swan; using System.Runtime.InteropServices;
using System; using System.Text;
using System.Runtime.InteropServices;
using System.Text; namespace Unosquare.RaspberryIO.Native {
/// <summary>
/// Provides standard libc calls using platform-invoke
/// </summary>
internal static class Standard {
internal const String LibCLibrary = "libc";
#region LibC Calls
/// <summary> /// <summary>
/// Provides standard libc calls using platform-invoke /// Strerrors the specified error.
/// </summary> /// </summary>
internal static class Standard /// <param name="error">The error.</param>
{ /// <returns></returns>
internal const string LibCLibrary = "libc"; public static String Strerror(Int32 error) {
if(!Runtime.IsUsingMonoRuntime) {
#region LibC Calls return StrError(error);
}
/// <summary>
/// Strerrors the specified error. try {
/// </summary> StringBuilder buffer = new StringBuilder(256);
/// <param name="error">The error.</param> Int32 result = Strerror(error, buffer, (UInt64)buffer.Capacity);
/// <returns></returns> return (result != -1) ? buffer.ToString() : null;
public static string Strerror(int error) } catch(EntryPointNotFoundException) {
{ return null;
if (!Runtime.IsUsingMonoRuntime) return StrError(error); }
}
try
{ /// <summary>
var buffer = new StringBuilder(256); /// Changes file permissions on a Unix file system
var result = Strerror(error, buffer, (ulong)buffer.Capacity); /// </summary>
return (result != -1) ? buffer.ToString() : null; /// <param name="filename">The filename.</param>
} /// <param name="mode">The mode.</param>
catch (EntryPointNotFoundException) /// <returns>The result</returns>
{ [DllImport(LibCLibrary, EntryPoint = "chmod", SetLastError = true)]
return null; public static extern Int32 Chmod(String filename, UInt32 mode);
}
} /// <summary>
/// Converts a string to a 32 bit integer. Use endpointer as IntPtr.Zero
/// <summary> /// </summary>
/// Changes file permissions on a Unix file system /// <param name="numberString">The number string.</param>
/// </summary> /// <param name="endPointer">The end pointer.</param>
/// <param name="filename">The filename.</param> /// <param name="numberBase">The number base.</param>
/// <param name="mode">The mode.</param> /// <returns>The result</returns>
/// <returns>The result</returns> [DllImport(LibCLibrary, EntryPoint = "strtol", SetLastError = true)]
[DllImport(LibCLibrary, EntryPoint = "chmod", SetLastError = true)] public static extern Int32 StringToInteger(String numberString, IntPtr endPointer, Int32 numberBase);
public static extern int Chmod(string filename, uint mode);
/// <summary>
/// <summary> /// The write() function attempts to write nbytes from buffer to the file associated with handle. On text files, it expands each LF to a CR/LF.
/// Converts a string to a 32 bit integer. Use endpointer as IntPtr.Zero /// The function returns the number of bytes written to the file. A return value of -1 indicates an error, with errno set appropriately.
/// </summary> /// </summary>
/// <param name="numberString">The number string.</param> /// <param name="fd">The fd.</param>
/// <param name="endPointer">The end pointer.</param> /// <param name="buffer">The buffer.</param>
/// <param name="numberBase">The number base.</param> /// <param name="count">The count.</param>
/// <returns>The result</returns> /// <returns>The result</returns>
[DllImport(LibCLibrary, EntryPoint = "strtol", SetLastError = true)] [DllImport(LibCLibrary, EntryPoint = "write", SetLastError = true)]
public static extern int StringToInteger(string numberString, IntPtr endPointer, int numberBase); public static extern Int32 Write(Int32 fd, Byte[] buffer, Int32 count);
/// <summary> /// <summary>
/// The write() function attempts to write nbytes from buffer to the file associated with handle. On text files, it expands each LF to a CR/LF. /// Fills in the structure with information about the system.
/// The function returns the number of bytes written to the file. A return value of -1 indicates an error, with errno set appropriately. /// </summary>
/// </summary> /// <param name="name">The name.</param>
/// <param name="fd">The fd.</param> /// <returns>The result</returns>
/// <param name="buffer">The buffer.</param> [DllImport(LibCLibrary, EntryPoint = "uname", SetLastError = true)]
/// <param name="count">The count.</param> public static extern Int32 Uname(out SystemName name);
/// <returns>The result</returns>
[DllImport(LibCLibrary, EntryPoint = "write", SetLastError = true)] [DllImport(LibCLibrary, EntryPoint = "strerror", SetLastError = true)]
public static extern int Write(int fd, byte[] buffer, int count); private static extern String StrError(Int32 errnum);
/// <summary> [DllImport("MonoPosixHelper", EntryPoint = "Mono_Posix_Syscall_strerror_r", SetLastError = true)]
/// Fills in the structure with information about the system. private static extern Int32 Strerror(Int32 error, [Out] StringBuilder buffer, UInt64 length);
/// </summary>
/// <param name="name">The name.</param> #endregion
/// <returns>The result</returns> }
[DllImport(LibCLibrary, EntryPoint = "uname", SetLastError = true)]
public static extern int Uname(out SystemName name);
[DllImport(LibCLibrary, EntryPoint = "strerror", SetLastError = true)]
private static extern string StrError(int errnum);
[DllImport("MonoPosixHelper", EntryPoint = "Mono_Posix_Syscall_strerror_r", SetLastError = true)]
private static extern int Strerror(int error, [Out] StringBuilder buffer, ulong length);
#endregion
}
} }
+43 -44
View File
@@ -1,47 +1,46 @@
namespace Unosquare.RaspberryIO.Native using System;
{ using System.Runtime.InteropServices;
using System.Runtime.InteropServices;
namespace Unosquare.RaspberryIO.Native {
/// <summary>
/// OS uname structure
/// </summary>
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
internal struct SystemName {
/// <summary> /// <summary>
/// OS uname structure /// System name
/// </summary> /// </summary>
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)] [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 65)]
internal struct SystemName public String SysName;
{
/// <summary> /// <summary>
/// System name /// Node name
/// </summary> /// </summary>
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 65)] [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 65)]
public string SysName; public String NodeName;
/// <summary> /// <summary>
/// Node name /// Release level
/// </summary> /// </summary>
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 65)] [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 65)]
public string NodeName; public String Release;
/// <summary> /// <summary>
/// Release level /// Version level
/// </summary> /// </summary>
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 65)] [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 65)]
public string Release; public String Version;
/// <summary> /// <summary>
/// Version level /// Hardware level
/// </summary> /// </summary>
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 65)] [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 65)]
public string Version; public String Machine;
/// <summary> /// <summary>
/// Hardware level /// Domain name
/// </summary> /// </summary>
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 65)] [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 65)]
public string Machine; public String DomainName;
}
/// <summary>
/// Domain name
/// </summary>
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 65)]
public string DomainName;
}
} }
+23 -25
View File
@@ -1,28 +1,26 @@
namespace Unosquare.RaspberryIO.Native namespace Unosquare.RaspberryIO.Native {
{ /// <summary>
/// Defines the different threading locking keys
/// </summary>
public enum ThreadLockKey {
/// <summary> /// <summary>
/// Defines the different threading locking keys /// The lock 0
/// </summary> /// </summary>
public enum ThreadLockKey Lock0 = 0,
{
/// <summary> /// <summary>
/// The lock 0 /// The lock 1
/// </summary> /// </summary>
Lock0 = 0, Lock1 = 1,
/// <summary> /// <summary>
/// The lock 1 /// The lock 2
/// </summary> /// </summary>
Lock1 = 1, Lock2 = 2,
/// <summary> /// <summary>
/// The lock 2 /// The lock 3
/// </summary> /// </summary>
Lock2 = 2, Lock3 = 3,
}
/// <summary>
/// The lock 3
/// </summary>
Lock3 = 3,
}
} }
+105 -105
View File
@@ -1,108 +1,108 @@
namespace Unosquare.RaspberryIO.Native using Unosquare.Swan;
{ using Unosquare.Swan.Abstractions;
using Swan; using System;
using Swan.Abstractions;
using System; namespace Unosquare.RaspberryIO.Native {
/// <summary>
/// Provides access to timing and threading properties and methods
/// </summary>
public class Timing : SingletonBase<Timing> {
/// <summary> /// <summary>
/// Provides access to timing and threading properties and methods /// Prevents a default instance of the <see cref="Timing"/> class from being created.
/// </summary> /// </summary>
public class Timing : SingletonBase<Timing> /// <exception cref="NotSupportedException">Could not initialize the GPIO controller</exception>
{ private Timing() {
/// <summary> // placeholder
/// Prevents a default instance of the <see cref="Timing"/> class from being created. }
/// </summary>
/// <exception cref="NotSupportedException">Could not initialize the GPIO controller</exception> /// <summary>
private Timing() /// This returns a number representing the number of milliseconds since your program
{ /// initialized the GPIO controller.
// placeholder /// It returns an unsigned 32-bit number which wraps after 49 days.
} /// </summary>
/// <value>
/// <summary> /// The milliseconds since setup.
/// This returns a number representing the number of milliseconds since your program /// </value>
/// initialized the GPIO controller. public UInt32 MillisecondsSinceSetup => WiringPi.Millis();
/// It returns an unsigned 32-bit number which wraps after 49 days.
/// </summary> /// <summary>
/// <value> /// This returns a number representing the number of microseconds since your
/// The milliseconds since setup. /// program initialized the GPIO controller
/// </value> /// It returns an unsigned 32-bit number which wraps after approximately 71 minutes.
public uint MillisecondsSinceSetup => WiringPi.Millis(); /// </summary>
/// <value>
/// <summary> /// The microseconds since setup.
/// This returns a number representing the number of microseconds since your /// </value>
/// program initialized the GPIO controller public UInt32 MicrosecondsSinceSetup => WiringPi.Micros();
/// It returns an unsigned 32-bit number which wraps after approximately 71 minutes.
/// </summary> /// <summary>
/// <value> /// This causes program execution to pause for at least howLong milliseconds.
/// The microseconds since setup. /// Due to the multi-tasking nature of Linux it could be longer.
/// </value> /// Note that the maximum delay is an unsigned 32-bit integer or approximately 49 days.
public uint MicrosecondsSinceSetup => WiringPi.Micros(); /// </summary>
/// <param name="value">The value.</param>
/// <summary> public static void SleepMilliseconds(UInt32 value) => WiringPi.Delay(value);
/// This causes program execution to pause for at least howLong milliseconds.
/// Due to the multi-tasking nature of Linux it could be longer. /// <summary>
/// Note that the maximum delay is an unsigned 32-bit integer or approximately 49 days. /// This causes program execution to pause for at least howLong microseconds.
/// </summary> /// Due to the multi-tasking nature of Linux it could be longer.
/// <param name="value">The value.</param> /// Note that the maximum delay is an unsigned 32-bit integer microseconds or approximately 71 minutes.
public static void SleepMilliseconds(uint value) => WiringPi.Delay(value); /// Delays under 100 microseconds are timed using a hard-coded loop continually polling the system time,
/// Delays over 100 microseconds are done using the system nanosleep() function –
/// <summary> /// You may need to consider the implications of very short delays on the overall performance of the system,
/// This causes program execution to pause for at least howLong microseconds. /// especially if using threads.
/// Due to the multi-tasking nature of Linux it could be longer. /// </summary>
/// Note that the maximum delay is an unsigned 32-bit integer microseconds or approximately 71 minutes. /// <param name="value">The value.</param>
/// Delays under 100 microseconds are timed using a hard-coded loop continually polling the system time, public void SleepMicroseconds(UInt32 value) => WiringPi.DelayMicroseconds(value);
/// Delays over 100 microseconds are done using the system nanosleep() function –
/// You may need to consider the implications of very short delays on the overall performance of the system, /// <summary>
/// especially if using threads. /// This attempts to shift your program (or thread in a multi-threaded program) to a higher priority and
/// </summary> /// enables a real-time scheduling. The priority parameter should be from 0 (the default) to 99 (the maximum).
/// <param name="value">The value.</param> /// This won’t make your program go any faster, but it will give it a bigger slice of time when other programs
public void SleepMicroseconds(uint value) => WiringPi.DelayMicroseconds(value); /// are running. The priority parameter works relative to others – so you can make one program priority 1 and
/// another priority 2 and it will have the same effect as setting one to 10 and the other to 90
/// <summary> /// (as long as no other programs are running with elevated priorities)
/// This attempts to shift your program (or thread in a multi-threaded program) to a higher priority and /// </summary>
/// enables a real-time scheduling. The priority parameter should be from 0 (the default) to 99 (the maximum). /// <param name="priority">The priority.</param>
/// This won’t make your program go any faster, but it will give it a bigger slice of time when other programs public void SetThreadPriority(Int32 priority) {
/// are running. The priority parameter works relative to others – so you can make one program priority 1 and priority = priority.Clamp(0, 99);
/// another priority 2 and it will have the same effect as setting one to 10 and the other to 90 Int32 result = WiringPi.PiHiPri(priority);
/// (as long as no other programs are running with elevated priorities) if(result < 0) {
/// </summary> HardwareException.Throw(nameof(Timing), nameof(SetThreadPriority));
/// <param name="priority">The priority.</param> }
public void SetThreadPriority(int priority) }
{
priority = priority.Clamp(0, 99); /// <summary>
var result = WiringPi.PiHiPri(priority); /// This is really nothing more than a simplified interface to the Posix threads mechanism that Linux supports.
if (result < 0) HardwareException.Throw(nameof(Timing), nameof(SetThreadPriority)); /// See the manual pages on Posix threads (man pthread) if you need more control over them.
} /// </summary>
/// <param name="worker">The worker.</param>
/// <summary> /// <exception cref="ArgumentNullException">worker</exception>
/// This is really nothing more than a simplified interface to the Posix threads mechanism that Linux supports. public void CreateThread(ThreadWorker worker) {
/// See the manual pages on Posix threads (man pthread) if you need more control over them. if(worker == null) {
/// </summary> throw new ArgumentNullException(nameof(worker));
/// <param name="worker">The worker.</param> }
/// <exception cref="ArgumentNullException">worker</exception>
public void CreateThread(ThreadWorker worker) Int32 result = WiringPi.PiThreadCreate(worker);
{ if(result != 0) {
if (worker == null) HardwareException.Throw(nameof(Timing), nameof(CreateThread));
throw new ArgumentNullException(nameof(worker)); }
}
var result = WiringPi.PiThreadCreate(worker);
if (result != 0) HardwareException.Throw(nameof(Timing), nameof(CreateThread)); /// <summary>
} /// These allow you to synchronize variable updates from your main program to any threads running in your program.
/// keyNum is a number from 0 to 3 and represents a “key”. When another process tries to lock the same key,
/// <summary> /// it will be stalled until the first process has unlocked the same key.
/// These allow you to synchronize variable updates from your main program to any threads running in your program. /// </summary>
/// keyNum is a number from 0 to 3 and represents a “key”. When another process tries to lock the same key, /// <param name="key">The key.</param>
/// it will be stalled until the first process has unlocked the same key. public void Lock(ThreadLockKey key) => WiringPi.PiLock((Int32)key);
/// </summary>
/// <param name="key">The key.</param> /// <summary>
public void Lock(ThreadLockKey key) => WiringPi.PiLock((int)key); /// These allow you to synchronize variable updates from your main program to any threads running in your program.
/// keyNum is a number from 0 to 3 and represents a “key”. When another process tries to lock the same key,
/// <summary> /// it will be stalled until the first process has unlocked the same key.
/// These allow you to synchronize variable updates from your main program to any threads running in your program. /// </summary>
/// keyNum is a number from 0 to 3 and represents a “key”. When another process tries to lock the same key, /// <param name="key">The key.</param>
/// it will be stalled until the first process has unlocked the same key. public void Unlock(ThreadLockKey key) => WiringPi.PiUnlock((Int32)key);
/// </summary> }
/// <param name="key">The key.</param>
public void Unlock(ThreadLockKey key) => WiringPi.PiUnlock((int)key);
}
} }
+77 -78
View File
@@ -1,79 +1,78 @@
namespace Unosquare.RaspberryIO.Native using System;
{ using System.Runtime.InteropServices;
using System.Runtime.InteropServices;
namespace Unosquare.RaspberryIO.Native {
public partial class WiringPi public partial class WiringPi {
{ #region WiringPi - I2C Library Calls
#region WiringPi - I2C Library Calls
/// <summary>
/// <summary> /// Simple device read. Some devices present data when you read them without having to do any register transactions.
/// Simple device read. Some devices present data when you read them without having to do any register transactions. /// </summary>
/// </summary> /// <param name="fd">The fd.</param>
/// <param name="fd">The fd.</param> /// <returns>The result</returns>
/// <returns>The result</returns> [DllImport(WiringPiLibrary, EntryPoint = "wiringPiI2CRead", SetLastError = true)]
[DllImport(WiringPiLibrary, EntryPoint = "wiringPiI2CRead", SetLastError = true)] public static extern Int32 WiringPiI2CRead(Int32 fd);
public static extern int WiringPiI2CRead(int fd);
/// <summary>
/// <summary> /// These read an 8-bit value from the device register indicated.
/// These read an 8-bit value from the device register indicated. /// </summary>
/// </summary> /// <param name="fd">The fd.</param>
/// <param name="fd">The fd.</param> /// <param name="reg">The reg.</param>
/// <param name="reg">The reg.</param> /// <returns>The result</returns>
/// <returns>The result</returns> [DllImport(WiringPiLibrary, EntryPoint = "wiringPiI2CReadReg8", SetLastError = true)]
[DllImport(WiringPiLibrary, EntryPoint = "wiringPiI2CReadReg8", SetLastError = true)] public static extern Int32 WiringPiI2CReadReg8(Int32 fd, Int32 reg);
public static extern int WiringPiI2CReadReg8(int fd, int reg);
/// <summary>
/// <summary> /// These read a 16-bit value from the device register indicated.
/// These read a 16-bit value from the device register indicated. /// </summary>
/// </summary> /// <param name="fd">The fd.</param>
/// <param name="fd">The fd.</param> /// <param name="reg">The reg.</param>
/// <param name="reg">The reg.</param> /// <returns>The result</returns>
/// <returns>The result</returns> [DllImport(WiringPiLibrary, EntryPoint = "wiringPiI2CReadReg16", SetLastError = true)]
[DllImport(WiringPiLibrary, EntryPoint = "wiringPiI2CReadReg16", SetLastError = true)] public static extern Int32 WiringPiI2CReadReg16(Int32 fd, Int32 reg);
public static extern int WiringPiI2CReadReg16(int fd, int reg);
/// <summary>
/// <summary> /// Simple device write. Some devices accept data this way without needing to access any internal registers.
/// Simple device write. Some devices accept data this way without needing to access any internal registers. /// </summary>
/// </summary> /// <param name="fd">The fd.</param>
/// <param name="fd">The fd.</param> /// <param name="data">The data.</param>
/// <param name="data">The data.</param> /// <returns>The result</returns>
/// <returns>The result</returns> [DllImport(WiringPiLibrary, EntryPoint = "wiringPiI2CWrite", SetLastError = true)]
[DllImport(WiringPiLibrary, EntryPoint = "wiringPiI2CWrite", SetLastError = true)] public static extern Int32 WiringPiI2CWrite(Int32 fd, Int32 data);
public static extern int WiringPiI2CWrite(int fd, int data);
/// <summary>
/// <summary> /// These write an 8-bit data value into the device register indicated.
/// These write an 8-bit data value into the device register indicated. /// </summary>
/// </summary> /// <param name="fd">The fd.</param>
/// <param name="fd">The fd.</param> /// <param name="reg">The reg.</param>
/// <param name="reg">The reg.</param> /// <param name="data">The data.</param>
/// <param name="data">The data.</param> /// <returns>The result</returns>
/// <returns>The result</returns> [DllImport(WiringPiLibrary, EntryPoint = "wiringPiI2CWriteReg8", SetLastError = true)]
[DllImport(WiringPiLibrary, EntryPoint = "wiringPiI2CWriteReg8", SetLastError = true)] public static extern Int32 WiringPiI2CWriteReg8(Int32 fd, Int32 reg, Int32 data);
public static extern int WiringPiI2CWriteReg8(int fd, int reg, int data);
/// <summary>
/// <summary> /// These write a 16-bit data value into the device register indicated.
/// These write a 16-bit data value into the device register indicated. /// </summary>
/// </summary> /// <param name="fd">The fd.</param>
/// <param name="fd">The fd.</param> /// <param name="reg">The reg.</param>
/// <param name="reg">The reg.</param> /// <param name="data">The data.</param>
/// <param name="data">The data.</param> /// <returns>The result</returns>
/// <returns>The result</returns> [DllImport(WiringPiLibrary, EntryPoint = "wiringPiI2CWriteReg16", SetLastError = true)]
[DllImport(WiringPiLibrary, EntryPoint = "wiringPiI2CWriteReg16", SetLastError = true)] public static extern Int32 WiringPiI2CWriteReg16(Int32 fd, Int32 reg, Int32 data);
public static extern int WiringPiI2CWriteReg16(int fd, int reg, int data);
/// <summary>
/// <summary> /// This initialises the I2C system with your given device identifier.
/// This initialises the I2C system with your given device identifier. /// The ID is the I2C number of the device and you can use the i2cdetect program to find this out. wiringPiI2CSetup()
/// The ID is the I2C number of the device and you can use the i2cdetect program to find this out. wiringPiI2CSetup() /// will work out which revision Raspberry Pi you have and open the appropriate device in /dev.
/// will work out which revision Raspberry Pi you have and open the appropriate device in /dev. /// The return value is the standard Linux filehandle, or -1 if any error – in which case, you can consult errno as usual.
/// The return value is the standard Linux filehandle, or -1 if any error – in which case, you can consult errno as usual. /// E.g. the popular MCP23017 GPIO expander is usually device Id 0x20, so this is the number you would pass into wiringPiI2CSetup().
/// E.g. the popular MCP23017 GPIO expander is usually device Id 0x20, so this is the number you would pass into wiringPiI2CSetup(). /// </summary>
/// </summary> /// <param name="devId">The dev identifier.</param>
/// <param name="devId">The dev identifier.</param> /// <returns>The result</returns>
/// <returns>The result</returns> [DllImport(WiringPiLibrary, EntryPoint = "wiringPiI2CSetup", SetLastError = true)]
[DllImport(WiringPiLibrary, EntryPoint = "wiringPiI2CSetup", SetLastError = true)] public static extern Int32 WiringPiI2CSetup(Int32 devId);
public static extern int WiringPiI2CSetup(int devId);
#endregion
#endregion
}
}
} }
@@ -1,73 +1,72 @@
namespace Unosquare.RaspberryIO.Native using System;
{ using System.Runtime.InteropServices;
using System.Runtime.InteropServices;
namespace Unosquare.RaspberryIO.Native {
public partial class WiringPi public partial class WiringPi {
{ #region WiringPi - Serial Port
#region WiringPi - Serial Port
/// <summary>
/// <summary> /// This opens and initialises the serial device and sets the baud rate. It sets the port into “raw” mode (character at a time and no translations),
/// This opens and initialises the serial device and sets the baud rate. It sets the port into “raw” mode (character at a time and no translations), /// and sets the read timeout to 10 seconds. The return value is the file descriptor or -1 for any error, in which case errno will be set as appropriate.
/// and sets the read timeout to 10 seconds. The return value is the file descriptor or -1 for any error, in which case errno will be set as appropriate. /// The wiringSerial library is intended to provide simplified control – suitable for most applications, however if you need advanced control
/// The wiringSerial library is intended to provide simplified control – suitable for most applications, however if you need advanced control /// – e.g. parity control, modem control lines (via a USB adapter, there are none on the Pi’s on-board UART!) and so on,
/// – e.g. parity control, modem control lines (via a USB adapter, there are none on the Pi’s on-board UART!) and so on, /// then you need to do some of this the old fashioned way.
/// then you need to do some of this the old fashioned way. /// </summary>
/// </summary> /// <param name="device">The device.</param>
/// <param name="device">The device.</param> /// <param name="baud">The baud.</param>
/// <param name="baud">The baud.</param> /// <returns>The result</returns>
/// <returns>The result</returns> [DllImport(WiringPiLibrary, EntryPoint = "serialOpen", SetLastError = true)]
[DllImport(WiringPiLibrary, EntryPoint = "serialOpen", SetLastError = true)] public static extern Int32 SerialOpen(String device, Int32 baud);
public static extern int SerialOpen(string device, int baud);
/// <summary>
/// <summary> /// Closes the device identified by the file descriptor given.
/// Closes the device identified by the file descriptor given. /// </summary>
/// </summary> /// <param name="fd">The fd.</param>
/// <param name="fd">The fd.</param> /// <returns>The result</returns>
/// <returns>The result</returns> [DllImport(WiringPiLibrary, EntryPoint = "serialClose", SetLastError = true)]
[DllImport(WiringPiLibrary, EntryPoint = "serialClose", SetLastError = true)] public static extern Int32 SerialClose(Int32 fd);
public static extern int SerialClose(int fd);
/// <summary>
/// <summary> /// Sends the single byte to the serial device identified by the given file descriptor.
/// Sends the single byte to the serial device identified by the given file descriptor. /// </summary>
/// </summary> /// <param name="fd">The fd.</param>
/// <param name="fd">The fd.</param> /// <param name="c">The c.</param>
/// <param name="c">The c.</param> [DllImport(WiringPiLibrary, EntryPoint = "serialPutchar", SetLastError = true)]
[DllImport(WiringPiLibrary, EntryPoint = "serialPutchar", SetLastError = true)] public static extern void SerialPutchar(Int32 fd, Byte c);
public static extern void SerialPutchar(int fd, byte c);
/// <summary>
/// <summary> /// Sends the nul-terminated string to the serial device identified by the given file descriptor.
/// Sends the nul-terminated string to the serial device identified by the given file descriptor. /// </summary>
/// </summary> /// <param name="fd">The fd.</param>
/// <param name="fd">The fd.</param> /// <param name="s">The s.</param>
/// <param name="s">The s.</param> [DllImport(WiringPiLibrary, EntryPoint = "serialPuts", SetLastError = true)]
[DllImport(WiringPiLibrary, EntryPoint = "serialPuts", SetLastError = true)] public static extern void SerialPuts(Int32 fd, String s);
public static extern void SerialPuts(int fd, string s);
/// <summary>
/// <summary> /// Returns the number of characters available for reading, or -1 for any error condition,
/// Returns the number of characters available for reading, or -1 for any error condition, /// in which case errno will be set appropriately.
/// in which case errno will be set appropriately. /// </summary>
/// </summary> /// <param name="fd">The fd.</param>
/// <param name="fd">The fd.</param> /// <returns>The result</returns>
/// <returns>The result</returns> [DllImport(WiringPiLibrary, EntryPoint = "serialDataAvail", SetLastError = true)]
[DllImport(WiringPiLibrary, EntryPoint = "serialDataAvail", SetLastError = true)] public static extern Int32 SerialDataAvail(Int32 fd);
public static extern int SerialDataAvail(int fd);
/// <summary>
/// <summary> /// Returns the next character available on the serial device.
/// Returns the next character available on the serial device. /// This call will block for up to 10 seconds if no data is available (when it will return -1)
/// This call will block for up to 10 seconds if no data is available (when it will return -1) /// </summary>
/// </summary> /// <param name="fd">The fd.</param>
/// <param name="fd">The fd.</param> /// <returns>The result</returns>
/// <returns>The result</returns> [DllImport(WiringPiLibrary, EntryPoint = "serialGetchar", SetLastError = true)]
[DllImport(WiringPiLibrary, EntryPoint = "serialGetchar", SetLastError = true)] public static extern Int32 SerialGetchar(Int32 fd);
public static extern int SerialGetchar(int fd);
/// <summary>
/// <summary> /// This discards all data received, or waiting to be send down the given device.
/// This discards all data received, or waiting to be send down the given device. /// </summary>
/// </summary> /// <param name="fd">The fd.</param>
/// <param name="fd">The fd.</param> [DllImport(WiringPiLibrary, EntryPoint = "serialFlush", SetLastError = true)]
[DllImport(WiringPiLibrary, EntryPoint = "serialFlush", SetLastError = true)] public static extern void SerialFlush(Int32 fd);
public static extern void SerialFlush(int fd);
#endregion
#endregion }
}
} }
+34 -35
View File
@@ -1,36 +1,35 @@
namespace Unosquare.RaspberryIO.Native using System;
{ using System.Runtime.InteropServices;
using System.Runtime.InteropServices;
namespace Unosquare.RaspberryIO.Native {
public partial class WiringPi public partial class WiringPi {
{ #region WiringPi - Shift Library
#region WiringPi - Shift Library
/// <summary>
/// <summary> /// This shifts an 8-bit data value in with the data appearing on the dPin and the clock being sent out on the cPin.
/// This shifts an 8-bit data value in with the data appearing on the dPin and the clock being sent out on the cPin. /// Order is either LSBFIRST or MSBFIRST. The data is sampled after the cPin goes high.
/// Order is either LSBFIRST or MSBFIRST. The data is sampled after the cPin goes high. /// (So cPin high, sample data, cPin low, repeat for 8 bits) The 8-bit value is returned by the function.
/// (So cPin high, sample data, cPin low, repeat for 8 bits) The 8-bit value is returned by the function. /// </summary>
/// </summary> /// <param name="dPin">The d pin.</param>
/// <param name="dPin">The d pin.</param> /// <param name="cPin">The c pin.</param>
/// <param name="cPin">The c pin.</param> /// <param name="order">The order.</param>
/// <param name="order">The order.</param> /// <returns>The result</returns>
/// <returns>The result</returns> [DllImport(WiringPiLibrary, EntryPoint = "shiftIn", SetLastError = true)]
[DllImport(WiringPiLibrary, EntryPoint = "shiftIn", SetLastError = true)] public static extern Byte ShiftIn(Byte dPin, Byte cPin, Byte order);
public static extern byte ShiftIn(byte dPin, byte cPin, byte order);
/// <summary>
/// <summary> /// The shifts an 8-bit data value val out with the data being sent out on dPin and the clock being sent out on the cPin.
/// The shifts an 8-bit data value val out with the data being sent out on dPin and the clock being sent out on the cPin. /// order is as above. Data is clocked out on the rising or falling edge – ie. dPin is set, then cPin is taken high then low
/// order is as above. Data is clocked out on the rising or falling edge – ie. dPin is set, then cPin is taken high then low /// – repeated for the 8 bits.
/// – repeated for the 8 bits. /// </summary>
/// </summary> /// <param name="dPin">The d pin.</param>
/// <param name="dPin">The d pin.</param> /// <param name="cPin">The c pin.</param>
/// <param name="cPin">The c pin.</param> /// <param name="order">The order.</param>
/// <param name="order">The order.</param> /// <param name="val">The value.</param>
/// <param name="val">The value.</param> [DllImport(WiringPiLibrary, EntryPoint = "shiftOut", SetLastError = true)]
[DllImport(WiringPiLibrary, EntryPoint = "shiftOut", SetLastError = true)] public static extern void ShiftOut(Byte dPin, Byte cPin, Byte order, Byte val);
public static extern void ShiftOut(byte dPin, byte cPin, byte order, byte val);
#endregion
#endregion
}
}
} }
@@ -1,64 +1,63 @@
namespace Unosquare.RaspberryIO.Native using System;
{ using System.Runtime.InteropServices;
using System.Runtime.InteropServices;
namespace Unosquare.RaspberryIO.Native {
public partial class WiringPi public partial class WiringPi {
{ #region WiringPi - Soft PWM (https://github.com/WiringPi/WiringPi/blob/master/wiringPi/softPwm.h)
#region WiringPi - Soft PWM (https://github.com/WiringPi/WiringPi/blob/master/wiringPi/softPwm.h)
/// <summary>
/// <summary> /// This creates a software controlled PWM pin. You can use any GPIO pin and the pin numbering will be that of the wiringPiSetup()
/// This creates a software controlled PWM pin. You can use any GPIO pin and the pin numbering will be that of the wiringPiSetup() /// function you used. Use 100 for the pwmRange, then the value can be anything from 0 (off) to 100 (fully on) for the given pin.
/// function you used. Use 100 for the pwmRange, then the value can be anything from 0 (off) to 100 (fully on) for the given pin. /// The return value is 0 for success. Anything else and you should check the global errno variable to see what went wrong.
/// The return value is 0 for success. Anything else and you should check the global errno variable to see what went wrong. /// </summary>
/// </summary> /// <param name="pin">The pin.</param>
/// <param name="pin">The pin.</param> /// <param name="initialValue">The initial value.</param>
/// <param name="initialValue">The initial value.</param> /// <param name="pwmRange">The PWM range.</param>
/// <param name="pwmRange">The PWM range.</param> /// <returns>The result</returns>
/// <returns>The result</returns> [DllImport(WiringPiLibrary, EntryPoint = "softPwmCreate", SetLastError = true)]
[DllImport(WiringPiLibrary, EntryPoint = "softPwmCreate", SetLastError = true)] public static extern Int32 SoftPwmCreate(Int32 pin, Int32 initialValue, Int32 pwmRange);
public static extern int SoftPwmCreate(int pin, int initialValue, int pwmRange);
/// <summary>
/// <summary> /// This updates the PWM value on the given pin. The value is checked to be in-range and pins that haven’t previously
/// This updates the PWM value on the given pin. The value is checked to be in-range and pins that haven’t previously /// been initialized via softPwmCreate will be silently ignored.
/// been initialized via softPwmCreate will be silently ignored. /// </summary>
/// </summary> /// <param name="pin">The pin.</param>
/// <param name="pin">The pin.</param> /// <param name="value">The value.</param>
/// <param name="value">The value.</param> [DllImport(WiringPiLibrary, EntryPoint = "softPwmWrite", SetLastError = true)]
[DllImport(WiringPiLibrary, EntryPoint = "softPwmWrite", SetLastError = true)] public static extern void SoftPwmWrite(Int32 pin, Int32 value);
public static extern void SoftPwmWrite(int pin, int value);
/// <summary>
/// <summary> /// This function is undocumented
/// This function is undocumented /// </summary>
/// </summary> /// <param name="pin">The pin.</param>
/// <param name="pin">The pin.</param> [DllImport(WiringPiLibrary, EntryPoint = "softPwmStop", SetLastError = true)]
[DllImport(WiringPiLibrary, EntryPoint = "softPwmStop", SetLastError = true)] public static extern void SoftPwmStop(Int32 pin);
public static extern void SoftPwmStop(int pin);
/// <summary>
/// <summary> /// This creates a software controlled tone pin. You can use any GPIO pin and the pin numbering will be that of the wiringPiSetup() function you used.
/// This creates a software controlled tone pin. You can use any GPIO pin and the pin numbering will be that of the wiringPiSetup() function you used. /// The return value is 0 for success. Anything else and you should check the global errno variable to see what went wrong.
/// The return value is 0 for success. Anything else and you should check the global errno variable to see what went wrong. /// </summary>
/// </summary> /// <param name="pin">The pin.</param>
/// <param name="pin">The pin.</param> /// <returns>The result</returns>
/// <returns>The result</returns> [DllImport(WiringPiLibrary, EntryPoint = "softToneCreate", SetLastError = true)]
[DllImport(WiringPiLibrary, EntryPoint = "softToneCreate", SetLastError = true)] public static extern Int32 SoftToneCreate(Int32 pin);
public static extern int SoftToneCreate(int pin);
/// <summary>
/// <summary> /// This function is undocumented
/// This function is undocumented /// </summary>
/// </summary> /// <param name="pin">The pin.</param>
/// <param name="pin">The pin.</param> [DllImport(WiringPiLibrary, EntryPoint = "softToneStop", SetLastError = true)]
[DllImport(WiringPiLibrary, EntryPoint = "softToneStop", SetLastError = true)] public static extern void SoftToneStop(Int32 pin);
public static extern void SoftToneStop(int pin);
/// <summary>
/// <summary> /// This updates the tone frequency value on the given pin. The tone will be played until you set the frequency to 0.
/// This updates the tone frequency value on the given pin. The tone will be played until you set the frequency to 0. /// </summary>
/// </summary> /// <param name="pin">The pin.</param>
/// <param name="pin">The pin.</param> /// <param name="freq">The freq.</param>
/// <param name="freq">The freq.</param> [DllImport(WiringPiLibrary, EntryPoint = "softToneWrite", SetLastError = true)]
[DllImport(WiringPiLibrary, EntryPoint = "softToneWrite", SetLastError = true)] public static extern void SoftToneWrite(Int32 pin, Int32 freq);
public static extern void SoftToneWrite(int pin, int freq);
#endregion
#endregion
}
}
} }
+51 -52
View File
@@ -1,53 +1,52 @@
namespace Unosquare.RaspberryIO.Native using System;
{ using System.Runtime.InteropServices;
using System.Runtime.InteropServices;
namespace Unosquare.RaspberryIO.Native {
public partial class WiringPi public partial class WiringPi {
{ #region WiringPi - SPI Library Calls
#region WiringPi - SPI Library Calls
/// <summary>
/// <summary> /// This function is undocumented
/// This function is undocumented /// </summary>
/// </summary> /// <param name="channel">The channel.</param>
/// <param name="channel">The channel.</param> /// <returns>Unknown</returns>
/// <returns>Unknown</returns> [DllImport(WiringPiLibrary, EntryPoint = "wiringPiSPIGetFd", SetLastError = true)]
[DllImport(WiringPiLibrary, EntryPoint = "wiringPiSPIGetFd", SetLastError = true)] public static extern Int32 WiringPiSPIGetFd(Int32 channel);
public static extern int WiringPiSPIGetFd(int channel);
/// <summary>
/// <summary> /// This performs a simultaneous write/read transaction over the selected SPI bus. Data that was in your buffer is overwritten by data returned from the SPI bus.
/// This performs a simultaneous write/read transaction over the selected SPI bus. Data that was in your buffer is overwritten by data returned from the SPI bus. /// That’s all there is in the helper library. It is possible to do simple read and writes over the SPI bus using the standard read() and write() system calls though –
/// That’s all there is in the helper library. It is possible to do simple read and writes over the SPI bus using the standard read() and write() system calls though – /// write() may be better to use for sending data to chains of shift registers, or those LED strings where you send RGB triplets of data.
/// write() may be better to use for sending data to chains of shift registers, or those LED strings where you send RGB triplets of data. /// Devices such as A/D and D/A converters usually need to perform a concurrent write/read transaction to work.
/// Devices such as A/D and D/A converters usually need to perform a concurrent write/read transaction to work. /// </summary>
/// </summary> /// <param name="channel">The channel.</param>
/// <param name="channel">The channel.</param> /// <param name="data">The data.</param>
/// <param name="data">The data.</param> /// <param name="len">The length.</param>
/// <param name="len">The length.</param> /// <returns>The result</returns>
/// <returns>The result</returns> [DllImport(WiringPiLibrary, EntryPoint = "wiringPiSPIDataRW", SetLastError = true)]
[DllImport(WiringPiLibrary, EntryPoint = "wiringPiSPIDataRW", SetLastError = true)] public static extern Int32 WiringPiSPIDataRW(Int32 channel, Byte[] data, Int32 len);
public static extern int WiringPiSPIDataRW(int channel, byte[] data, int len);
/// <summary>
/// <summary> /// This function is undocumented
/// This function is undocumented /// </summary>
/// </summary> /// <param name="channel">The channel.</param>
/// <param name="channel">The channel.</param> /// <param name="speed">The speed.</param>
/// <param name="speed">The speed.</param> /// <param name="mode">The mode.</param>
/// <param name="mode">The mode.</param> /// <returns>Unkown</returns>
/// <returns>Unkown</returns> [DllImport(WiringPiLibrary, EntryPoint = "wiringPiSPISetupMode", SetLastError = true)]
[DllImport(WiringPiLibrary, EntryPoint = "wiringPiSPISetupMode", SetLastError = true)] public static extern Int32 WiringPiSPISetupMode(Int32 channel, Int32 speed, Int32 mode);
public static extern int WiringPiSPISetupMode(int channel, int speed, int mode);
/// <summary>
/// <summary> /// This is the way to initialize a channel (The Pi has 2 channels; 0 and 1). The speed parameter is an integer
/// This is the way to initialize a channel (The Pi has 2 channels; 0 and 1). The speed parameter is an integer /// in the range 500,000 through 32,000,000 and represents the SPI clock speed in Hz.
/// in the range 500,000 through 32,000,000 and represents the SPI clock speed in Hz. /// The returned value is the Linux file-descriptor for the device, or -1 on error. If an error has happened, you may use the standard errno global variable to see why.
/// The returned value is the Linux file-descriptor for the device, or -1 on error. If an error has happened, you may use the standard errno global variable to see why. /// </summary>
/// </summary> /// <param name="channel">The channel.</param>
/// <param name="channel">The channel.</param> /// <param name="speed">The speed.</param>
/// <param name="speed">The speed.</param> /// <returns>The Linux file descriptor for the device or -1 for error</returns>
/// <returns>The Linux file descriptor for the device or -1 for error</returns> [DllImport(WiringPiLibrary, EntryPoint = "wiringPiSPISetup", SetLastError = true)]
[DllImport(WiringPiLibrary, EntryPoint = "wiringPiSPISetup", SetLastError = true)] public static extern Int32 WiringPiSPISetup(Int32 channel, Int32 speed);
public static extern int WiringPiSPISetup(int channel, int speed);
#endregion
#endregion }
}
} }
+389 -391
View File
@@ -1,394 +1,392 @@
namespace Unosquare.RaspberryIO.Native using System;
{ using System.Runtime.InteropServices;
using System;
using System.Runtime.InteropServices; namespace Unosquare.RaspberryIO.Native {
/// <summary>
/// Provides native C WiringPi Library function call wrappers
/// All credit for the native library goes to the author of http://wiringpi.com/
/// The wrappers were written based on https://github.com/WiringPi/WiringPi/blob/master/wiringPi/wiringPi.h
/// </summary>
public partial class WiringPi {
internal const String WiringPiLibrary = "libwiringPi.so.2.46";
#region WiringPi - Core Functions (https://github.com/WiringPi/WiringPi/blob/master/wiringPi/wiringPi.h)
/// <summary> /// <summary>
/// Provides native C WiringPi Library function call wrappers /// This initialises wiringPi and assumes that the calling program is going to be using the wiringPi pin numbering scheme.
/// All credit for the native library goes to the author of http://wiringpi.com/ /// This is a simplified numbering scheme which provides a mapping from virtual pin numbers 0 through 16 to the real underlying Broadcom GPIO pin numbers.
/// The wrappers were written based on https://github.com/WiringPi/WiringPi/blob/master/wiringPi/wiringPi.h /// See the pins page for a table which maps the wiringPi pin number to the Broadcom GPIO pin number to the physical location on the edge connector.
/// This function needs to be called with root privileges.
/// </summary> /// </summary>
public partial class WiringPi /// <returns>The result code</returns>
{ [DllImport(WiringPiLibrary, EntryPoint = "wiringPiSetup", SetLastError = true)]
internal const string WiringPiLibrary = "libwiringPi.so.2.46"; public static extern Int32 WiringPiSetup();
#region WiringPi - Core Functions (https://github.com/WiringPi/WiringPi/blob/master/wiringPi/wiringPi.h) /// <summary>
/// This initialises wiringPi but uses the /sys/class/gpio interface rather than accessing the hardware directly.
/// <summary> /// This can be called as a non-root user provided the GPIO pins have been exported before-hand using the gpio program.
/// This initialises wiringPi and assumes that the calling program is going to be using the wiringPi pin numbering scheme. /// Pin numbering in this mode is the native Broadcom GPIO numbers – the same as wiringPiSetupGpio() above,
/// This is a simplified numbering scheme which provides a mapping from virtual pin numbers 0 through 16 to the real underlying Broadcom GPIO pin numbers. /// so be aware of the differences between Rev 1 and Rev 2 boards.
/// See the pins page for a table which maps the wiringPi pin number to the Broadcom GPIO pin number to the physical location on the edge connector. ///
/// This function needs to be called with root privileges. /// Note: In this mode you can only use the pins which have been exported via the /sys/class/gpio interface before you run your program.
/// </summary> /// You can do this in a separate shell-script, or by using the system() function from inside your program to call the gpio program.
/// <returns>The result code</returns> /// Also note that some functions have no effect when using this mode as they’re not currently possible to action unless called with root privileges.
[DllImport(WiringPiLibrary, EntryPoint = "wiringPiSetup", SetLastError = true)] /// (although you can use system() to call gpio to set/change modes if needed)
public static extern int WiringPiSetup(); /// </summary>
/// <returns>The result code</returns>
/// <summary> [DllImport(WiringPiLibrary, EntryPoint = "wiringPiSetupSys", SetLastError = true)]
/// This initialises wiringPi but uses the /sys/class/gpio interface rather than accessing the hardware directly. public static extern Int32 WiringPiSetupSys();
/// This can be called as a non-root user provided the GPIO pins have been exported before-hand using the gpio program.
/// Pin numbering in this mode is the native Broadcom GPIO numbers – the same as wiringPiSetupGpio() above, /// <summary>
/// so be aware of the differences between Rev 1 and Rev 2 boards. /// This is identical to wiringPiSetup, however it allows the calling programs to use the Broadcom GPIO
/// /// pin numbers directly with no re-mapping.
/// Note: In this mode you can only use the pins which have been exported via the /sys/class/gpio interface before you run your program. /// As above, this function needs to be called with root privileges, and note that some pins are different
/// You can do this in a separate shell-script, or by using the system() function from inside your program to call the gpio program. /// from revision 1 to revision 2 boards.
/// Also note that some functions have no effect when using this mode as they’re not currently possible to action unless called with root privileges. /// </summary>
/// (although you can use system() to call gpio to set/change modes if needed) /// <returns>The result code</returns>
/// </summary> [DllImport(WiringPiLibrary, EntryPoint = "wiringPiSetupGpio", SetLastError = true)]
/// <returns>The result code</returns> public static extern Int32 WiringPiSetupGpio();
[DllImport(WiringPiLibrary, EntryPoint = "wiringPiSetupSys", SetLastError = true)]
public static extern int WiringPiSetupSys(); /// <summary>
/// Identical to wiringPiSetup, however it allows the calling programs to use the physical pin numbers on the P1 connector only.
/// <summary> /// This function needs to be called with root privileges.
/// This is identical to wiringPiSetup, however it allows the calling programs to use the Broadcom GPIO /// </summary>
/// pin numbers directly with no re-mapping. /// <returns>The result code</returns>
/// As above, this function needs to be called with root privileges, and note that some pins are different [DllImport(WiringPiLibrary, EntryPoint = "wiringPiSetupPhys", SetLastError = true)]
/// from revision 1 to revision 2 boards. public static extern Int32 WiringPiSetupPhys();
/// </summary>
/// <returns>The result code</returns> /// <summary>
[DllImport(WiringPiLibrary, EntryPoint = "wiringPiSetupGpio", SetLastError = true)] /// This function is undocumented
public static extern int WiringPiSetupGpio(); /// </summary>
/// <param name="pin">The pin.</param>
/// <summary> /// <param name="mode">The mode.</param>
/// Identical to wiringPiSetup, however it allows the calling programs to use the physical pin numbers on the P1 connector only. [DllImport(WiringPiLibrary, EntryPoint = "pinModeAlt", SetLastError = true)]
/// This function needs to be called with root privileges. public static extern void PinModeAlt(Int32 pin, Int32 mode);
/// </summary>
/// <returns>The result code</returns> /// <summary>
[DllImport(WiringPiLibrary, EntryPoint = "wiringPiSetupPhys", SetLastError = true)] /// This sets the mode of a pin to either INPUT, OUTPUT, PWM_OUTPUT or GPIO_CLOCK.
public static extern int WiringPiSetupPhys(); /// Note that only wiringPi pin 1 (BCM_GPIO 18) supports PWM output and only wiringPi pin 7 (BCM_GPIO 4)
/// supports CLOCK output modes.
/// <summary> ///
/// This function is undocumented /// This function has no effect when in Sys mode. If you need to change the pin mode, then you can
/// </summary> /// do it with the gpio program in a script before you start your program.
/// <param name="pin">The pin.</param> /// </summary>
/// <param name="mode">The mode.</param> /// <param name="pin">The pin.</param>
[DllImport(WiringPiLibrary, EntryPoint = "pinModeAlt", SetLastError = true)] /// <param name="mode">The mode.</param>
public static extern void PinModeAlt(int pin, int mode); [DllImport(WiringPiLibrary, EntryPoint = "pinMode", SetLastError = true)]
public static extern void PinMode(Int32 pin, Int32 mode);
/// <summary>
/// This sets the mode of a pin to either INPUT, OUTPUT, PWM_OUTPUT or GPIO_CLOCK. /// <summary>
/// Note that only wiringPi pin 1 (BCM_GPIO 18) supports PWM output and only wiringPi pin 7 (BCM_GPIO 4) /// This sets the pull-up or pull-down resistor mode on the given pin, which should be set as an input.
/// supports CLOCK output modes. /// Unlike the Arduino, the BCM2835 has both pull-up an down internal resistors. The parameter pud should be; PUD_OFF,
/// /// (no pull up/down), PUD_DOWN (pull to ground) or PUD_UP (pull to 3.3v) The internal pull up/down resistors
/// This function has no effect when in Sys mode. If you need to change the pin mode, then you can /// have a value of approximately 50KΩ on the Raspberry Pi.
/// do it with the gpio program in a script before you start your program. ///
/// </summary> /// This function has no effect on the Raspberry Pi’s GPIO pins when in Sys mode.
/// <param name="pin">The pin.</param> /// If you need to activate a pull-up/pull-down, then you can do it with the gpio program in a script before you start your program.
/// <param name="mode">The mode.</param> /// </summary>
[DllImport(WiringPiLibrary, EntryPoint = "pinMode", SetLastError = true)] /// <param name="pin">The pin.</param>
public static extern void PinMode(int pin, int mode); /// <param name="pud">The pud.</param>
[DllImport(WiringPiLibrary, EntryPoint = "pullUpDnControl", SetLastError = true)]
/// <summary> public static extern void PullUpDnControl(Int32 pin, Int32 pud);
/// This sets the pull-up or pull-down resistor mode on the given pin, which should be set as an input.
/// Unlike the Arduino, the BCM2835 has both pull-up an down internal resistors. The parameter pud should be; PUD_OFF, /// <summary>
/// (no pull up/down), PUD_DOWN (pull to ground) or PUD_UP (pull to 3.3v) The internal pull up/down resistors /// This function returns the value read at the given pin. It will be HIGH or LOW (1 or 0) depending on the logic level at the pin.
/// have a value of approximately 50KΩ on the Raspberry Pi. /// </summary>
/// /// <param name="pin">The pin.</param>
/// This function has no effect on the Raspberry Pi’s GPIO pins when in Sys mode. /// <returns>The result code</returns>
/// If you need to activate a pull-up/pull-down, then you can do it with the gpio program in a script before you start your program. [DllImport(WiringPiLibrary, EntryPoint = "digitalRead", SetLastError = true)]
/// </summary> public static extern Int32 DigitalRead(Int32 pin);
/// <param name="pin">The pin.</param>
/// <param name="pud">The pud.</param> /// <summary>
[DllImport(WiringPiLibrary, EntryPoint = "pullUpDnControl", SetLastError = true)] /// Writes the value HIGH or LOW (1 or 0) to the given pin which must have been previously set as an output.
public static extern void PullUpDnControl(int pin, int pud); /// WiringPi treats any non-zero number as HIGH, however 0 is the only representation of LOW.
/// </summary>
/// <summary> /// <param name="pin">The pin.</param>
/// This function returns the value read at the given pin. It will be HIGH or LOW (1 or 0) depending on the logic level at the pin. /// <param name="value">The value.</param>
/// </summary> [DllImport(WiringPiLibrary, EntryPoint = "digitalWrite", SetLastError = true)]
/// <param name="pin">The pin.</param> public static extern void DigitalWrite(Int32 pin, Int32 value);
/// <returns>The result code</returns>
[DllImport(WiringPiLibrary, EntryPoint = "digitalRead", SetLastError = true)] /// <summary>
public static extern int DigitalRead(int pin); /// Writes the value to the PWM register for the given pin. The Raspberry Pi has one
/// on-board PWM pin, pin 1 (BMC_GPIO 18, Phys 12) and the range is 0-1024.
/// <summary> /// Other PWM devices may have other PWM ranges.
/// Writes the value HIGH or LOW (1 or 0) to the given pin which must have been previously set as an output. /// This function is not able to control the Pi’s on-board PWM when in Sys mode.
/// WiringPi treats any non-zero number as HIGH, however 0 is the only representation of LOW. /// </summary>
/// </summary> /// <param name="pin">The pin.</param>
/// <param name="pin">The pin.</param> /// <param name="value">The value.</param>
/// <param name="value">The value.</param> [DllImport(WiringPiLibrary, EntryPoint = "pwmWrite", SetLastError = true)]
[DllImport(WiringPiLibrary, EntryPoint = "digitalWrite", SetLastError = true)] public static extern void PwmWrite(Int32 pin, Int32 value);
public static extern void DigitalWrite(int pin, int value);
/// <summary>
/// <summary> /// This returns the value read on the supplied analog input pin. You will need to
/// Writes the value to the PWM register for the given pin. The Raspberry Pi has one /// register additional analog modules to enable this function for devices such as the Gertboard, quick2Wire analog board, etc.
/// on-board PWM pin, pin 1 (BMC_GPIO 18, Phys 12) and the range is 0-1024. /// </summary>
/// Other PWM devices may have other PWM ranges. /// <param name="pin">The pin.</param>
/// This function is not able to control the Pi’s on-board PWM when in Sys mode. /// <returns>The result code</returns>
/// </summary> [DllImport(WiringPiLibrary, EntryPoint = "analogRead", SetLastError = true)]
/// <param name="pin">The pin.</param> public static extern Int32 AnalogRead(Int32 pin);
/// <param name="value">The value.</param>
[DllImport(WiringPiLibrary, EntryPoint = "pwmWrite", SetLastError = true)] /// <summary>
public static extern void PwmWrite(int pin, int value); /// This writes the given value to the supplied analog pin. You will need to register additional
/// analog modules to enable this function for devices such as the Gertboard.
/// <summary> /// </summary>
/// This returns the value read on the supplied analog input pin. You will need to /// <param name="pin">The pin.</param>
/// register additional analog modules to enable this function for devices such as the Gertboard, quick2Wire analog board, etc. /// <param name="value">The value.</param>
/// </summary> [DllImport(WiringPiLibrary, EntryPoint = "analogWrite", SetLastError = true)]
/// <param name="pin">The pin.</param> public static extern void AnalogWrite(Int32 pin, Int32 value);
/// <returns>The result code</returns>
[DllImport(WiringPiLibrary, EntryPoint = "analogRead", SetLastError = true)] /// <summary>
public static extern int AnalogRead(int pin); /// This returns the board revision of the Raspberry Pi. It will be either 1 or 2. Some of the BCM_GPIO pins changed number and
/// function when moving from board revision 1 to 2, so if you are using BCM_GPIO pin numbers, then you need to be aware of the differences.
/// <summary> /// </summary>
/// This writes the given value to the supplied analog pin. You will need to register additional /// <returns>The result code</returns>
/// analog modules to enable this function for devices such as the Gertboard. [DllImport(WiringPiLibrary, EntryPoint = "piBoardRev", SetLastError = true)]
/// </summary> public static extern Int32 PiBoardRev();
/// <param name="pin">The pin.</param>
/// <param name="value">The value.</param> /// <summary>
[DllImport(WiringPiLibrary, EntryPoint = "analogWrite", SetLastError = true)] /// This function is undocumented
public static extern void AnalogWrite(int pin, int value); /// </summary>
/// <param name="model">The model.</param>
/// <summary> /// <param name="mem">The memory.</param>
/// This returns the board revision of the Raspberry Pi. It will be either 1 or 2. Some of the BCM_GPIO pins changed number and /// <param name="maker">The maker.</param>
/// function when moving from board revision 1 to 2, so if you are using BCM_GPIO pin numbers, then you need to be aware of the differences. /// <param name="overVolted">The over volted.</param>
/// </summary> /// <returns>The result code</returns>
/// <returns>The result code</returns> [DllImport(WiringPiLibrary, EntryPoint = "piBoardId", SetLastError = true)]
[DllImport(WiringPiLibrary, EntryPoint = "piBoardRev", SetLastError = true)] public static extern Int32 PiBoardId(ref Int32 model, ref Int32 mem, ref Int32 maker, ref Int32 overVolted);
public static extern int PiBoardRev();
/// <summary>
/// <summary> /// This returns the BCM_GPIO pin number of the supplied wiringPi pin. It takes the board revision into account.
/// This function is undocumented /// </summary>
/// </summary> /// <param name="wPiPin">The w pi pin.</param>
/// <param name="model">The model.</param> /// <returns>The result code</returns>
/// <param name="mem">The memory.</param> [DllImport(WiringPiLibrary, EntryPoint = "wpiPinToGpio", SetLastError = true)]
/// <param name="maker">The maker.</param> public static extern Int32 WpiPinToGpio(Int32 wPiPin);
/// <param name="overVolted">The over volted.</param>
/// <returns>The result code</returns> /// <summary>
[DllImport(WiringPiLibrary, EntryPoint = "piBoardId", SetLastError = true)] /// This returns the BCM_GPIO pin number of the supplied physical pin on the P1 connector.
public static extern int PiBoardId(ref int model, ref int mem, ref int maker, ref int overVolted); /// </summary>
/// <param name="physPin">The physical pin.</param>
/// <summary> /// <returns>The result code</returns>
/// This returns the BCM_GPIO pin number of the supplied wiringPi pin. It takes the board revision into account. [DllImport(WiringPiLibrary, EntryPoint = "physPinToGpio", SetLastError = true)]
/// </summary> public static extern Int32 PhysPinToGpio(Int32 physPin);
/// <param name="wPiPin">The w pi pin.</param>
/// <returns>The result code</returns> /// <summary>
[DllImport(WiringPiLibrary, EntryPoint = "wpiPinToGpio", SetLastError = true)] /// This sets the “strength” of the pad drivers for a particular group of pins.
public static extern int WpiPinToGpio(int wPiPin); /// There are 3 groups of pins and the drive strength is from 0 to 7. Do not use this unless you know what you are doing.
/// </summary>
/// <summary> /// <param name="group">The group.</param>
/// This returns the BCM_GPIO pin number of the supplied physical pin on the P1 connector. /// <param name="value">The value.</param>
/// </summary> /// <returns>The result code</returns>
/// <param name="physPin">The physical pin.</param> [DllImport(WiringPiLibrary, EntryPoint = "setPadDrive", SetLastError = true)]
/// <returns>The result code</returns> public static extern Int32 SetPadDrive(Int32 group, Int32 value);
[DllImport(WiringPiLibrary, EntryPoint = "physPinToGpio", SetLastError = true)]
public static extern int PhysPinToGpio(int physPin); /// <summary>
/// Undocumented function
/// <summary> /// </summary>
/// This sets the “strength” of the pad drivers for a particular group of pins. /// <param name="pin">The pin.</param>
/// There are 3 groups of pins and the drive strength is from 0 to 7. Do not use this unless you know what you are doing. /// <returns>The result code</returns>
/// </summary> [DllImport(WiringPiLibrary, EntryPoint = "getAlt", SetLastError = true)]
/// <param name="group">The group.</param> public static extern Int32 GetAlt(Int32 pin);
/// <param name="value">The value.</param>
/// <returns>The result code</returns> /// <summary>
[DllImport(WiringPiLibrary, EntryPoint = "setPadDrive", SetLastError = true)] /// Undocumented function
public static extern int SetPadDrive(int group, int value); /// </summary>
/// <param name="pin">The pin.</param>
/// <summary> /// <param name="freq">The freq.</param>
/// Undocumented function /// <returns>The result code</returns>
/// </summary> [DllImport(WiringPiLibrary, EntryPoint = "pwmToneWrite", SetLastError = true)]
/// <param name="pin">The pin.</param> public static extern Int32 PwmToneWrite(Int32 pin, Int32 freq);
/// <returns>The result code</returns>
[DllImport(WiringPiLibrary, EntryPoint = "getAlt", SetLastError = true)] /// <summary>
public static extern int GetAlt(int pin); /// This writes the 8-bit byte supplied to the first 8 GPIO pins.
/// It’s the fastest way to set all 8 bits at once to a particular value, although it still takes two write operations to the Pi’s GPIO hardware.
/// <summary> /// </summary>
/// Undocumented function /// <param name="value">The value.</param>
/// </summary> [DllImport(WiringPiLibrary, EntryPoint = "digitalWriteByte", SetLastError = true)]
/// <param name="pin">The pin.</param> public static extern void DigitalWriteByte(Int32 value);
/// <param name="freq">The freq.</param>
/// <returns>The result code</returns> /// <summary>
[DllImport(WiringPiLibrary, EntryPoint = "pwmToneWrite", SetLastError = true)] /// This writes the 8-bit byte supplied to the first 8 GPIO pins.
public static extern int PwmToneWrite(int pin, int freq); /// It’s the fastest way to set all 8 bits at once to a particular value, although it still takes two write operations to the Pi’s GPIO hardware.
/// </summary>
/// <summary> /// <param name="value">The value.</param>
/// This writes the 8-bit byte supplied to the first 8 GPIO pins. [DllImport(WiringPiLibrary, EntryPoint = "digitalWriteByte2", SetLastError = true)]
/// It’s the fastest way to set all 8 bits at once to a particular value, although it still takes two write operations to the Pi’s GPIO hardware. public static extern void DigitalWriteByte2(Int32 value);
/// </summary>
/// <param name="value">The value.</param> /// <summary>
[DllImport(WiringPiLibrary, EntryPoint = "digitalWriteByte", SetLastError = true)] /// Undocumented function
public static extern void DigitalWriteByte(int value); /// This reads the 8-bit byte supplied to the first 8 GPIO pins.
/// It’s the fastest way to get all 8 bits at once to a particular value.
/// <summary> /// </summary>
/// This writes the 8-bit byte supplied to the first 8 GPIO pins. /// <returns>The result code</returns>
/// It’s the fastest way to set all 8 bits at once to a particular value, although it still takes two write operations to the Pi’s GPIO hardware. [DllImport(WiringPiLibrary, EntryPoint = "digitalReadByte", SetLastError = true)]
/// </summary> public static extern UInt32 DigitalReadByte();
/// <param name="value">The value.</param>
[DllImport(WiringPiLibrary, EntryPoint = "digitalWriteByte2", SetLastError = true)] /// <summary>
public static extern void DigitalWriteByte2(int value); /// Undocumented function
/// This reads the 8-bit byte supplied to the first 8 GPIO pins.
/// <summary> /// It’s the fastest way to get all 8 bits at once to a particular value.
/// Undocumented function /// </summary>
/// This reads the 8-bit byte supplied to the first 8 GPIO pins. /// <returns>The result code</returns>
/// It’s the fastest way to get all 8 bits at once to a particular value. [DllImport(WiringPiLibrary, EntryPoint = "digitalReadByte2", SetLastError = true)]
/// </summary> public static extern UInt32 DigitalReadByte2();
/// <returns>The result code</returns>
[DllImport(WiringPiLibrary, EntryPoint = "digitalReadByte", SetLastError = true)] /// <summary>
public static extern uint DigitalReadByte(); /// The PWM generator can run in 2 modes – “balanced” and “mark:space”. The mark:space mode is traditional,
/// however the default mode in the Pi is “balanced”. You can switch modes by supplying the parameter: PWM_MODE_BAL or PWM_MODE_MS.
/// <summary> /// </summary>
/// Undocumented function /// <param name="mode">The mode.</param>
/// This reads the 8-bit byte supplied to the first 8 GPIO pins. [DllImport(WiringPiLibrary, EntryPoint = "pwmSetMode", SetLastError = true)]
/// It’s the fastest way to get all 8 bits at once to a particular value. public static extern void PwmSetMode(Int32 mode);
/// </summary>
/// <returns>The result code</returns> /// <summary>
[DllImport(WiringPiLibrary, EntryPoint = "digitalReadByte2", SetLastError = true)] /// This sets the range register in the PWM generator. The default is 1024.
public static extern uint DigitalReadByte2(); /// </summary>
/// <param name="range">The range.</param>
/// <summary> [DllImport(WiringPiLibrary, EntryPoint = "pwmSetRange", SetLastError = true)]
/// The PWM generator can run in 2 modes – “balanced” and “mark:space”. The mark:space mode is traditional, public static extern void PwmSetRange(UInt32 range);
/// however the default mode in the Pi is “balanced”. You can switch modes by supplying the parameter: PWM_MODE_BAL or PWM_MODE_MS.
/// </summary> /// <summary>
/// <param name="mode">The mode.</param> /// This sets the divisor for the PWM clock.
[DllImport(WiringPiLibrary, EntryPoint = "pwmSetMode", SetLastError = true)] /// Note: The PWM control functions can not be used when in Sys mode.
public static extern void PwmSetMode(int mode); /// To understand more about the PWM system, you’ll need to read the Broadcom ARM peripherals manual.
/// </summary>
/// <summary> /// <param name="divisor">The divisor.</param>
/// This sets the range register in the PWM generator. The default is 1024. [DllImport(WiringPiLibrary, EntryPoint = "pwmSetClock", SetLastError = true)]
/// </summary> public static extern void PwmSetClock(Int32 divisor);
/// <param name="range">The range.</param>
[DllImport(WiringPiLibrary, EntryPoint = "pwmSetRange", SetLastError = true)] /// <summary>
public static extern void PwmSetRange(uint range); /// Undocumented function
/// </summary>
/// <summary> /// <param name="pin">The pin.</param>
/// This sets the divisor for the PWM clock. /// <param name="freq">The freq.</param>
/// Note: The PWM control functions can not be used when in Sys mode. [DllImport(WiringPiLibrary, EntryPoint = "gpioClockSet", SetLastError = true)]
/// To understand more about the PWM system, you’ll need to read the Broadcom ARM peripherals manual. public static extern void GpioClockSet(Int32 pin, Int32 freq);
/// </summary>
/// <param name="divisor">The divisor.</param> /// <summary>
[DllImport(WiringPiLibrary, EntryPoint = "pwmSetClock", SetLastError = true)] /// Note: Jan 2013: The waitForInterrupt() function is deprecated – you should use the newer and easier to use wiringPiISR() function below.
public static extern void PwmSetClock(int divisor); /// When called, it will wait for an interrupt event to happen on that pin and your program will be stalled. The timeOut parameter is given in milliseconds,
/// or can be -1 which means to wait forever.
/// <summary> /// The return value is -1 if an error occurred (and errno will be set appropriately), 0 if it timed out, or 1 on a successful interrupt event.
/// Undocumented function /// Before you call waitForInterrupt, you must first initialise the GPIO pin and at present the only way to do this is to use the gpio program, either
/// </summary> /// in a script, or using the system() call from inside your program.
/// <param name="pin">The pin.</param> /// e.g. We want to wait for a falling-edge interrupt on GPIO pin 0, so to setup the hardware, we need to run: gpio edge 0 falling
/// <param name="freq">The freq.</param> /// before running the program.
[DllImport(WiringPiLibrary, EntryPoint = "gpioClockSet", SetLastError = true)] /// </summary>
public static extern void GpioClockSet(int pin, int freq); /// <param name="pin">The pin.</param>
/// <param name="timeout">The timeout.</param>
/// <summary> /// <returns>The result code</returns>
/// Note: Jan 2013: The waitForInterrupt() function is deprecated – you should use the newer and easier to use wiringPiISR() function below. [Obsolete]
/// When called, it will wait for an interrupt event to happen on that pin and your program will be stalled. The timeOut parameter is given in milliseconds, [DllImport(WiringPiLibrary, EntryPoint = "waitForInterrupt", SetLastError = true)]
/// or can be -1 which means to wait forever. public static extern Int32 WaitForInterrupt(Int32 pin, Int32 timeout);
/// The return value is -1 if an error occurred (and errno will be set appropriately), 0 if it timed out, or 1 on a successful interrupt event.
/// Before you call waitForInterrupt, you must first initialise the GPIO pin and at present the only way to do this is to use the gpio program, either /// <summary>
/// in a script, or using the system() call from inside your program. /// This function registers a function to received interrupts on the specified pin.
/// e.g. We want to wait for a falling-edge interrupt on GPIO pin 0, so to setup the hardware, we need to run: gpio edge 0 falling /// The edgeType parameter is either INT_EDGE_FALLING, INT_EDGE_RISING, INT_EDGE_BOTH or INT_EDGE_SETUP.
/// before running the program. /// If it is INT_EDGE_SETUP then no initialisation of the pin will happen – it’s assumed that you have already setup the pin elsewhere
/// </summary> /// (e.g. with the gpio program), but if you specify one of the other types, then the pin will be exported and initialised as specified.
/// <param name="pin">The pin.</param> /// This is accomplished via a suitable call to the gpio utility program, so it need to be available.
/// <param name="timeout">The timeout.</param> /// The pin number is supplied in the current mode – native wiringPi, BCM_GPIO, physical or Sys modes.
/// <returns>The result code</returns> /// This function will work in any mode, and does not need root privileges to work.
[Obsolete] /// The function will be called when the interrupt triggers. When it is triggered, it’s cleared in the dispatcher before calling your function,
[DllImport(WiringPiLibrary, EntryPoint = "waitForInterrupt", SetLastError = true)] /// so if a subsequent interrupt fires before you finish your handler, then it won’t be missed. (However it can only track one more interrupt,
public static extern int WaitForInterrupt(int pin, int timeout); /// if more than one interrupt fires while one is being handled then they will be ignored)
/// This function is run at a high priority (if the program is run using sudo, or as root) and executes concurrently with the main program.
/// <summary> /// It has full access to all the global variables, open file handles and so on.
/// This function registers a function to received interrupts on the specified pin. /// </summary>
/// The edgeType parameter is either INT_EDGE_FALLING, INT_EDGE_RISING, INT_EDGE_BOTH or INT_EDGE_SETUP. /// <param name="pin">The pin.</param>
/// If it is INT_EDGE_SETUP then no initialisation of the pin will happen – it’s assumed that you have already setup the pin elsewhere /// <param name="mode">The mode.</param>
/// (e.g. with the gpio program), but if you specify one of the other types, then the pin will be exported and initialised as specified. /// <param name="method">The method.</param>
/// This is accomplished via a suitable call to the gpio utility program, so it need to be available. /// <returns>The result code</returns>
/// The pin number is supplied in the current mode – native wiringPi, BCM_GPIO, physical or Sys modes. [DllImport(WiringPiLibrary, EntryPoint = "wiringPiISR", SetLastError = true)]
/// This function will work in any mode, and does not need root privileges to work. public static extern Int32 WiringPiISR(Int32 pin, Int32 mode, InterruptServiceRoutineCallback method);
/// The function will be called when the interrupt triggers. When it is triggered, it’s cleared in the dispatcher before calling your function,
/// so if a subsequent interrupt fires before you finish your handler, then it won’t be missed. (However it can only track one more interrupt, /// <summary>
/// if more than one interrupt fires while one is being handled then they will be ignored) /// This function creates a thread which is another function in your program previously declared using the PI_THREAD declaration.
/// This function is run at a high priority (if the program is run using sudo, or as root) and executes concurrently with the main program. /// This function is then run concurrently with your main program. An example may be to have this function wait for an interrupt while
/// It has full access to all the global variables, open file handles and so on. /// your program carries on doing other tasks. The thread can indicate an event, or action by using global variables to
/// </summary> /// communicate back to the main program, or other threads.
/// <param name="pin">The pin.</param> /// </summary>
/// <param name="mode">The mode.</param> /// <param name="method">The method.</param>
/// <param name="method">The method.</param> /// <returns>The result code</returns>
/// <returns>The result code</returns> [DllImport(WiringPiLibrary, EntryPoint = "piThreadCreate", SetLastError = true)]
[DllImport(WiringPiLibrary, EntryPoint = "wiringPiISR", SetLastError = true)] public static extern Int32 PiThreadCreate(ThreadWorker method);
public static extern int WiringPiISR(int pin, int mode, InterruptServiceRoutineCallback method);
/// <summary>
/// <summary> /// These allow you to synchronise variable updates from your main program to any threads running in your program. keyNum is a number from 0 to 3 and represents a key.
/// This function creates a thread which is another function in your program previously declared using the PI_THREAD declaration. /// When another process tries to lock the same key, it will be stalled until the first process has unlocked the same key.
/// This function is then run concurrently with your main program. An example may be to have this function wait for an interrupt while /// You may need to use these functions to ensure that you get valid data when exchanging data between your main program and a thread
/// your program carries on doing other tasks. The thread can indicate an event, or action by using global variables to /// – otherwise it’s possible that the thread could wake-up halfway during your data copy and change the data –
/// communicate back to the main program, or other threads. /// so the data you end up copying is incomplete, or invalid. See the wfi.c program in the examples directory for an example.
/// </summary> /// </summary>
/// <param name="method">The method.</param> /// <param name="key">The key.</param>
/// <returns>The result code</returns> [DllImport(WiringPiLibrary, EntryPoint = "piLock", SetLastError = true)]
[DllImport(WiringPiLibrary, EntryPoint = "piThreadCreate", SetLastError = true)] public static extern void PiLock(Int32 key);
public static extern int PiThreadCreate(ThreadWorker method);
/// <summary>
/// <summary> /// These allow you to synchronise variable updates from your main program to any threads running in your program. keyNum is a number from 0 to 3 and represents a key.
/// These allow you to synchronise variable updates from your main program to any threads running in your program. keyNum is a number from 0 to 3 and represents a key. /// When another process tries to lock the same key, it will be stalled until the first process has unlocked the same key.
/// When another process tries to lock the same key, it will be stalled until the first process has unlocked the same key. /// You may need to use these functions to ensure that you get valid data when exchanging data between your main program and a thread
/// You may need to use these functions to ensure that you get valid data when exchanging data between your main program and a thread /// – otherwise it’s possible that the thread could wake-up halfway during your data copy and change the data –
/// – otherwise it’s possible that the thread could wake-up halfway during your data copy and change the data – /// so the data you end up copying is incomplete, or invalid. See the wfi.c program in the examples directory for an example.
/// so the data you end up copying is incomplete, or invalid. See the wfi.c program in the examples directory for an example. /// </summary>
/// </summary> /// <param name="key">The key.</param>
/// <param name="key">The key.</param> [DllImport(WiringPiLibrary, EntryPoint = "piUnlock", SetLastError = true)]
[DllImport(WiringPiLibrary, EntryPoint = "piLock", SetLastError = true)] public static extern void PiUnlock(Int32 key);
public static extern void PiLock(int key);
/// <summary>
/// <summary> /// This attempts to shift your program (or thread in a multi-threaded program) to a higher priority
/// These allow you to synchronise variable updates from your main program to any threads running in your program. keyNum is a number from 0 to 3 and represents a key. /// and enables a real-time scheduling. The priority parameter should be from 0 (the default) to 99 (the maximum).
/// When another process tries to lock the same key, it will be stalled until the first process has unlocked the same key. /// This won’t make your program go any faster, but it will give it a bigger slice of time when other programs are running.
/// You may need to use these functions to ensure that you get valid data when exchanging data between your main program and a thread /// The priority parameter works relative to others – so you can make one program priority 1 and another priority 2
/// – otherwise it’s possible that the thread could wake-up halfway during your data copy and change the data – /// and it will have the same effect as setting one to 10 and the other to 90 (as long as no other
/// so the data you end up copying is incomplete, or invalid. See the wfi.c program in the examples directory for an example. /// programs are running with elevated priorities)
/// </summary> /// The return value is 0 for success and -1 for error. If an error is returned, the program should then consult the errno global variable, as per the usual conventions.
/// <param name="key">The key.</param> /// Note: Only programs running as root can change their priority. If called from a non-root program then nothing happens.
[DllImport(WiringPiLibrary, EntryPoint = "piUnlock", SetLastError = true)] /// </summary>
public static extern void PiUnlock(int key); /// <param name="priority">The priority.</param>
/// <returns>The result code</returns>
/// <summary> [DllImport(WiringPiLibrary, EntryPoint = "piHiPri", SetLastError = true)]
/// This attempts to shift your program (or thread in a multi-threaded program) to a higher priority public static extern Int32 PiHiPri(Int32 priority);
/// and enables a real-time scheduling. The priority parameter should be from 0 (the default) to 99 (the maximum).
/// This won’t make your program go any faster, but it will give it a bigger slice of time when other programs are running. /// <summary>
/// The priority parameter works relative to others – so you can make one program priority 1 and another priority 2 /// This causes program execution to pause for at least howLong milliseconds.
/// and it will have the same effect as setting one to 10 and the other to 90 (as long as no other /// Due to the multi-tasking nature of Linux it could be longer.
/// programs are running with elevated priorities) /// Note that the maximum delay is an unsigned 32-bit integer or approximately 49 days.
/// The return value is 0 for success and -1 for error. If an error is returned, the program should then consult the errno global variable, as per the usual conventions. /// </summary>
/// Note: Only programs running as root can change their priority. If called from a non-root program then nothing happens. /// <param name="howLong">The how long.</param>
/// </summary> [DllImport(WiringPiLibrary, EntryPoint = "delay", SetLastError = true)]
/// <param name="priority">The priority.</param> public static extern void Delay(UInt32 howLong);
/// <returns>The result code</returns>
[DllImport(WiringPiLibrary, EntryPoint = "piHiPri", SetLastError = true)] /// <summary>
public static extern int PiHiPri(int priority); /// This causes program execution to pause for at least howLong microseconds.
/// Due to the multi-tasking nature of Linux it could be longer.
/// <summary> /// Note that the maximum delay is an unsigned 32-bit integer microseconds or approximately 71 minutes.
/// This causes program execution to pause for at least howLong milliseconds. /// Delays under 100 microseconds are timed using a hard-coded loop continually polling the system time,
/// Due to the multi-tasking nature of Linux it could be longer. /// Delays over 100 microseconds are done using the system nanosleep() function – You may need to consider the implications
/// Note that the maximum delay is an unsigned 32-bit integer or approximately 49 days. /// of very short delays on the overall performance of the system, especially if using threads.
/// </summary> /// </summary>
/// <param name="howLong">The how long.</param> /// <param name="howLong">The how long.</param>
[DllImport(WiringPiLibrary, EntryPoint = "delay", SetLastError = true)] [DllImport(WiringPiLibrary, EntryPoint = "delayMicroseconds", SetLastError = true)]
public static extern void Delay(uint howLong); public static extern void DelayMicroseconds(UInt32 howLong);
/// <summary> /// <summary>
/// This causes program execution to pause for at least howLong microseconds. /// This returns a number representing the number of milliseconds since your program called one of the wiringPiSetup functions.
/// Due to the multi-tasking nature of Linux it could be longer. /// It returns an unsigned 32-bit number which wraps after 49 days.
/// Note that the maximum delay is an unsigned 32-bit integer microseconds or approximately 71 minutes. /// </summary>
/// Delays under 100 microseconds are timed using a hard-coded loop continually polling the system time, /// <returns>The result code</returns>
/// Delays over 100 microseconds are done using the system nanosleep() function – You may need to consider the implications [DllImport(WiringPiLibrary, EntryPoint = "millis", SetLastError = true)]
/// of very short delays on the overall performance of the system, especially if using threads. public static extern UInt32 Millis();
/// </summary>
/// <param name="howLong">The how long.</param> /// <summary>
[DllImport(WiringPiLibrary, EntryPoint = "delayMicroseconds", SetLastError = true)] /// This returns a number representing the number of microseconds since your program called one of
public static extern void DelayMicroseconds(uint howLong); /// the wiringPiSetup functions. It returns an unsigned 32-bit number which wraps after approximately 71 minutes.
/// </summary>
/// <summary> /// <returns>The result code</returns>
/// This returns a number representing the number of milliseconds since your program called one of the wiringPiSetup functions. [DllImport(WiringPiLibrary, EntryPoint = "micros", SetLastError = true)]
/// It returns an unsigned 32-bit number which wraps after 49 days. public static extern UInt32 Micros();
/// </summary>
/// <returns>The result code</returns> #endregion
[DllImport(WiringPiLibrary, EntryPoint = "millis", SetLastError = true)] }
public static extern uint Millis();
/// <summary>
/// This returns a number representing the number of microseconds since your program called one of
/// the wiringPiSetup functions. It returns an unsigned 32-bit number which wraps after approximately 71 minutes.
/// </summary>
/// <returns>The result code</returns>
[DllImport(WiringPiLibrary, EntryPoint = "micros", SetLastError = true)]
public static extern uint Micros();
#endregion
}
} }
+121 -110
View File
@@ -1,110 +1,121 @@
namespace Unosquare.RaspberryIO using Unosquare.RaspberryIO.Camera;
{ using Unosquare.RaspberryIO.Computer;
using Camera; using Unosquare.RaspberryIO.Gpio;
using Computer; using Unosquare.RaspberryIO.Native;
using Gpio; using System.Threading.Tasks;
using Native; using Unosquare.Swan.Components;
using System.Threading.Tasks; using System;
using Swan.Components;
namespace Unosquare.RaspberryIO {
/// <summary> /// <summary>
/// Our main character. Provides access to the Raspberry Pi's GPIO, system and board information and Camera /// Our main character. Provides access to the Raspberry Pi's GPIO, system and board information and Camera
/// </summary> /// </summary>
public static class Pi public static class Pi {
{ private static readonly Object SyncLock = new Object();
private static readonly object SyncLock = new object();
/// <summary>
/// <summary> /// Initializes static members of the <see cref="Pi" /> class.
/// Initializes static members of the <see cref="Pi" /> class. /// </summary>
/// </summary> static Pi() {
static Pi() lock(SyncLock) {
{ // Extraction of embedded resources
lock (SyncLock) Resources.EmbeddedResources.ExtractAll();
{
// Extraction of embedded resources // Instance assignments
Resources.EmbeddedResources.ExtractAll(); Gpio = GpioController.Instance;
Info = SystemInfo.Instance;
// Instance assignments Timing = Timing.Instance;
Gpio = GpioController.Instance; Spi = SpiBus.Instance;
Info = SystemInfo.Instance; I2C = I2CBus.Instance;
Timing = Timing.Instance; Camera = CameraController.Instance;
Spi = SpiBus.Instance; PiDisplay = DsiDisplay.Instance;
I2C = I2CBus.Instance; }
Camera = CameraController.Instance; }
PiDisplay = DsiDisplay.Instance;
} #region Components
}
/// <summary>
#region Components /// Provides access to the Raspberry Pi's GPIO as a collection of GPIO Pins.
/// </summary>
/// <summary> public static GpioController Gpio {
/// Provides access to the Raspberry Pi's GPIO as a collection of GPIO Pins. get;
/// </summary> }
public static GpioController Gpio { get; }
/// <summary>
/// <summary> /// Provides information on this Raspberry Pi's CPU and form factor.
/// Provides information on this Raspberry Pi's CPU and form factor. /// </summary>
/// </summary> public static SystemInfo Info {
public static SystemInfo Info { get; } get;
}
/// <summary>
/// Provides access to The PI's Timing and threading API /// <summary>
/// </summary> /// Provides access to The PI's Timing and threading API
public static Timing Timing { get; } /// </summary>
public static Timing Timing {
/// <summary> get;
/// Provides access to the 2-channel SPI bus }
/// </summary>
public static SpiBus Spi { get; } /// <summary>
/// Provides access to the 2-channel SPI bus
/// <summary> /// </summary>
/// Provides access to the functionality of the i2c bus. public static SpiBus Spi {
/// </summary> get;
public static I2CBus I2C { get; } }
/// <summary> /// <summary>
/// Provides access to the official Raspberry Pi Camera /// Provides access to the functionality of the i2c bus.
/// </summary> /// </summary>
public static CameraController Camera { get; } public static I2CBus I2C {
get;
/// <summary> }
/// Provides access to the official Raspberry Pi 7-inch DSI Display
/// </summary> /// <summary>
public static DsiDisplay PiDisplay { get; } /// Provides access to the official Raspberry Pi Camera
/// </summary>
/// <summary> public static CameraController Camera {
/// Gets the logger source name. get;
/// </summary> }
internal static string LoggerSource => typeof(Pi).Namespace;
/// <summary>
#endregion /// Provides access to the official Raspberry Pi 7-inch DSI Display
/// </summary>
#region Methods public static DsiDisplay PiDisplay {
get;
/// <summary> }
/// Restarts the Pi. Must be running as SU
/// </summary> /// <summary>
/// <returns>The process result</returns> /// Gets the logger source name.
public static async Task<ProcessResult> RestartAsync() => await ProcessRunner.GetProcessResultAsync("reboot", null, null); /// </summary>
internal static String LoggerSource => typeof(Pi).Namespace;
/// <summary>
/// Restarts the Pi. Must be running as SU #endregion
/// </summary>
/// <returns>The process result</returns> #region Methods
public static ProcessResult Restart() => RestartAsync().GetAwaiter().GetResult();
/// <summary>
/// <summary> /// Restarts the Pi. Must be running as SU
/// Halts the Pi. Must be running as SU /// </summary>
/// </summary> /// <returns>The process result</returns>
/// <returns>The process result</returns> public static async Task<ProcessResult> RestartAsync() => await ProcessRunner.GetProcessResultAsync("reboot", null, null);
public static async Task<ProcessResult> ShutdownAsync() => await ProcessRunner.GetProcessResultAsync("halt", null, null);
/// <summary>
/// <summary> /// Restarts the Pi. Must be running as SU
/// Halts the Pi. Must be running as SU /// </summary>
/// </summary> /// <returns>The process result</returns>
/// <returns>The process result</returns> public static ProcessResult Restart() => RestartAsync().GetAwaiter().GetResult();
public static ProcessResult Shutdown() => ShutdownAsync().GetAwaiter().GetResult();
/// <summary>
#endregion /// Halts the Pi. Must be running as SU
} /// </summary>
} /// <returns>The process result</returns>
public static async Task<ProcessResult> ShutdownAsync() => await ProcessRunner.GetProcessResultAsync("halt", null, null);
/// <summary>
/// Halts the Pi. Must be running as SU
/// </summary>
/// <returns>The process result</returns>
public static ProcessResult Shutdown() => ShutdownAsync().GetAwaiter().GetResult();
#endregion
}
}
@@ -1,65 +1,55 @@
namespace Unosquare.RaspberryIO.Resources using Unosquare.RaspberryIO.Native;
{ using Unosquare.Swan;
using Native; using System;
using Swan; using System.Collections.ObjectModel;
using System; using System.IO;
using System.Collections.ObjectModel;
using System.IO; namespace Unosquare.RaspberryIO.Resources {
/// <summary>
/// Provides access to embedded assembly files
/// </summary>
internal static class EmbeddedResources {
/// <summary> /// <summary>
/// Provides access to embedded assembly files /// Initializes static members of the <see cref="EmbeddedResources"/> class.
/// </summary> /// </summary>
internal static class EmbeddedResources static EmbeddedResources() => ResourceNames = new ReadOnlyCollection<String>(typeof(EmbeddedResources).Assembly().GetManifestResourceNames());
{
/// <summary> /// <summary>
/// Initializes static members of the <see cref="EmbeddedResources"/> class. /// Gets the resource names.
/// </summary> /// </summary>
static EmbeddedResources() /// <value>
{ /// The resource names.
ResourceNames = /// </value>
new ReadOnlyCollection<string>(typeof(EmbeddedResources).Assembly().GetManifestResourceNames()); public static ReadOnlyCollection<String> ResourceNames {
} get;
}
/// <summary>
/// Gets the resource names. /// <summary>
/// </summary> /// Extracts all the file resources to the specified base path.
/// <value> /// </summary>
/// The resource names. public static void ExtractAll() {
/// </value> String basePath = Runtime.EntryAssemblyDirectory;
public static ReadOnlyCollection<string> ResourceNames { get; } Int32 executablePermissions = Standard.StringToInteger("0777", IntPtr.Zero, 8);
/// <summary> foreach(String resourceName in ResourceNames) {
/// Extracts all the file resources to the specified base path. String filename = resourceName.Substring($"{typeof(EmbeddedResources).Namespace}.".Length);
/// </summary> String targetPath = Path.Combine(basePath, filename);
public static void ExtractAll() if(File.Exists(targetPath)) {
{ return;
var basePath = Runtime.EntryAssemblyDirectory; }
var executablePermissions = Standard.StringToInteger("0777", IntPtr.Zero, 8);
using(Stream stream = typeof(EmbeddedResources).Assembly().GetManifestResourceStream($"{typeof(EmbeddedResources).Namespace}.{filename}")) {
foreach (var resourceName in ResourceNames) using(FileStream outputStream = File.OpenWrite(targetPath)) {
{ stream?.CopyTo(outputStream);
var filename = resourceName.Substring($"{typeof(EmbeddedResources).Namespace}.".Length); }
var targetPath = Path.Combine(basePath, filename);
if (File.Exists(targetPath)) return; try {
_ = Standard.Chmod(targetPath, (UInt32)executablePermissions);
using (var stream = typeof(EmbeddedResources).Assembly() } catch {
.GetManifestResourceStream($"{typeof(EmbeddedResources).Namespace}.{filename}")) /* Ignore */
{ }
using (var outputStream = File.OpenWrite(targetPath)) }
{ }
stream?.CopyTo(outputStream); }
} }
try
{
Standard.Chmod(targetPath, (uint)executablePermissions);
}
catch
{
/* Ignore */
}
}
}
}
}
} }
+225 -240
View File
@@ -1,243 +1,228 @@
namespace Unosquare.Swan.Abstractions using System;
{ using System.Threading;
using System;
using System.Threading; namespace Unosquare.Swan.Abstractions {
/// <summary>
/// Provides a generic implementation of an Atomic (interlocked) type
///
/// Idea taken from Memory model and .NET operations in article:
/// http://igoro.com/archive/volatile-keyword-in-c-memory-model-explained/.
/// </summary>
/// <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>>
where T : struct, IComparable, IComparable<T>, IEquatable<T> {
private Int64 _backingValue;
/// <summary> /// <summary>
/// Provides a generic implementation of an Atomic (interlocked) type /// Initializes a new instance of the <see cref="AtomicTypeBase{T}"/> class.
///
/// Idea taken from Memory model and .NET operations in article:
/// http://igoro.com/archive/volatile-keyword-in-c-memory-model-explained/.
/// </summary> /// </summary>
/// <typeparam name="T">The structure type backed by a 64-bit value.</typeparam> /// <param name="initialValue">The initial value.</param>
public abstract class AtomicTypeBase<T> : IComparable, IComparable<T>, IComparable<AtomicTypeBase<T>>, IEquatable<T>, IEquatable<AtomicTypeBase<T>> protected AtomicTypeBase(Int64 initialValue) => this.BackingValue = initialValue;
where T : struct, IComparable, IComparable<T>, IEquatable<T>
{ /// <summary>
private long _backingValue; /// Gets or sets the value.
/// </summary>
/// <summary> public T Value {
/// Initializes a new instance of the <see cref="AtomicTypeBase{T}"/> class. get => this.FromLong(this.BackingValue);
/// </summary> set => this.BackingValue = this.ToLong(value);
/// <param name="initialValue">The initial value.</param> }
protected AtomicTypeBase(long initialValue)
{ /// <summary>
BackingValue = initialValue; /// Gets or sets the backing value.
} /// </summary>
protected Int64 BackingValue {
/// <summary> get => Interlocked.Read(ref this._backingValue);
/// Gets or sets the value. set => Interlocked.Exchange(ref this._backingValue, value);
/// </summary> }
public T Value
{ /// <summary>
get => FromLong(BackingValue); /// Implements the operator ==.
set => BackingValue = ToLong(value); /// </summary>
} /// <param name="a">a.</param>
/// <param name="b">The b.</param>
/// <summary> /// <returns>
/// Gets or sets the backing value. /// The result of the operator.
/// </summary> /// </returns>
protected long BackingValue public static Boolean operator ==(AtomicTypeBase<T> a, T b) => a?.Equals(b) == true;
{
get => Interlocked.Read(ref _backingValue); /// <summary>
set => Interlocked.Exchange(ref _backingValue, value); /// Implements the operator !=.
} /// </summary>
/// <param name="a">a.</param>
/// <summary> /// <param name="b">The b.</param>
/// Implements the operator ==. /// <returns>
/// </summary> /// The result of the operator.
/// <param name="a">a.</param> /// </returns>
/// <param name="b">The b.</param> public static Boolean operator !=(AtomicTypeBase<T> a, T b) => a?.Equals(b) == false;
/// <returns>
/// The result of the operator. /// <summary>
/// </returns> /// Implements the operator &gt;.
public static bool operator ==(AtomicTypeBase<T> a, T b) => a?.Equals(b) == true; /// </summary>
/// <param name="a">a.</param>
/// <summary> /// <param name="b">The b.</param>
/// Implements the operator !=. /// <returns>
/// </summary> /// The result of the operator.
/// <param name="a">a.</param> /// </returns>
/// <param name="b">The b.</param> public static Boolean operator >(AtomicTypeBase<T> a, T b) => a.CompareTo(b) > 0;
/// <returns>
/// The result of the operator. /// <summary>
/// </returns> /// Implements the operator &lt;.
public static bool operator !=(AtomicTypeBase<T> a, T b) => a?.Equals(b) == false; /// </summary>
/// <param name="a">a.</param>
/// <summary> /// <param name="b">The b.</param>
/// Implements the operator &gt;. /// <returns>
/// </summary> /// The result of the operator.
/// <param name="a">a.</param> /// </returns>
/// <param name="b">The b.</param> public static Boolean operator <(AtomicTypeBase<T> a, T b) => a.CompareTo(b) < 0;
/// <returns>
/// The result of the operator. /// <summary>
/// </returns> /// Implements the operator &gt;=.
public static bool operator >(AtomicTypeBase<T> a, T b) => a.CompareTo(b) > 0; /// </summary>
/// <param name="a">a.</param>
/// <summary> /// <param name="b">The b.</param>
/// Implements the operator &lt;. /// <returns>
/// </summary> /// The result of the operator.
/// <param name="a">a.</param> /// </returns>
/// <param name="b">The b.</param> public static Boolean operator >=(AtomicTypeBase<T> a, T b) => a.CompareTo(b) >= 0;
/// <returns>
/// The result of the operator. /// <summary>
/// </returns> /// Implements the operator &lt;=.
public static bool operator <(AtomicTypeBase<T> a, T b) => a.CompareTo(b) < 0; /// </summary>
/// <param name="a">a.</param>
/// <summary> /// <param name="b">The b.</param>
/// Implements the operator &gt;=. /// <returns>
/// </summary> /// The result of the operator.
/// <param name="a">a.</param> /// </returns>
/// <param name="b">The b.</param> public static Boolean operator <=(AtomicTypeBase<T> a, T b) => a.CompareTo(b) <= 0;
/// <returns>
/// The result of the operator. /// <summary>
/// </returns> /// Implements the operator ++.
public static bool operator >=(AtomicTypeBase<T> a, T b) => a.CompareTo(b) >= 0; /// </summary>
/// <param name="instance">The instance.</param>
/// <summary> /// <returns>
/// Implements the operator &lt;=. /// The result of the operator.
/// </summary> /// </returns>
/// <param name="a">a.</param> public static AtomicTypeBase<T> operator ++(AtomicTypeBase<T> instance) {
/// <param name="b">The b.</param> _ = Interlocked.Increment(ref instance._backingValue);
/// <returns> return instance;
/// The result of the operator. }
/// </returns>
public static bool operator <=(AtomicTypeBase<T> a, T b) => a.CompareTo(b) <= 0; /// <summary>
/// Implements the operator --.
/// <summary> /// </summary>
/// Implements the operator ++. /// <param name="instance">The instance.</param>
/// </summary> /// <returns>
/// <param name="instance">The instance.</param> /// The result of the operator.
/// <returns> /// </returns>
/// The result of the operator. public static AtomicTypeBase<T> operator --(AtomicTypeBase<T> instance) {
/// </returns> _ = Interlocked.Decrement(ref instance._backingValue);
public static AtomicTypeBase<T> operator ++(AtomicTypeBase<T> instance) return instance;
{ }
Interlocked.Increment(ref instance._backingValue);
return instance; /// <summary>
} /// Implements the operator -&lt;.
/// </summary>
/// <summary> /// <param name="instance">The instance.</param>
/// Implements the operator --. /// <param name="operand">The operand.</param>
/// </summary> /// <returns>
/// <param name="instance">The instance.</param> /// The result of the operator.
/// <returns> /// </returns>
/// The result of the operator. public static AtomicTypeBase<T> operator +(AtomicTypeBase<T> instance, Int64 operand) {
/// </returns> instance.BackingValue += operand;
public static AtomicTypeBase<T> operator --(AtomicTypeBase<T> instance) return instance;
{ }
Interlocked.Decrement(ref instance._backingValue);
return instance; /// <summary>
} /// Implements the operator -.
/// </summary>
/// <summary> /// <param name="instance">The instance.</param>
/// Implements the operator -&lt;. /// <param name="operand">The operand.</param>
/// </summary> /// <returns>
/// <param name="instance">The instance.</param> /// The result of the operator.
/// <param name="operand">The operand.</param> /// </returns>
/// <returns> public static AtomicTypeBase<T> operator -(AtomicTypeBase<T> instance, Int64 operand) {
/// The result of the operator. instance.BackingValue -= operand;
/// </returns> return instance;
public static AtomicTypeBase<T> operator +(AtomicTypeBase<T> instance, long operand) }
{
instance.BackingValue = instance.BackingValue + operand; /// <summary>
return instance; /// Compares the value to the other instance.
} /// </summary>
/// <param name="other">The other instance.</param>
/// <summary> /// <returns>0 if equal, 1 if this instance is greater, -1 if this instance is less than.</returns>
/// Implements the operator -. /// <exception cref="ArgumentException">When types are incompatible.</exception>
/// </summary> public Int32 CompareTo(Object other) {
/// <param name="instance">The instance.</param> switch(other) {
/// <param name="operand">The operand.</param> case null:
/// <returns> return 1;
/// The result of the operator. case AtomicTypeBase<T> atomic:
/// </returns> return this.BackingValue.CompareTo(atomic.BackingValue);
public static AtomicTypeBase<T> operator -(AtomicTypeBase<T> instance, long operand) case T variable:
{ return this.Value.CompareTo(variable);
instance.BackingValue = instance.BackingValue - operand; }
return instance;
} throw new ArgumentException("Incompatible comparison types");
}
/// <summary>
/// Compares the value to the other instance. /// <summary>
/// </summary> /// Compares the value to the other instance.
/// <param name="other">The other instance.</param> /// </summary>
/// <returns>0 if equal, 1 if this instance is greater, -1 if this instance is less than.</returns> /// <param name="other">The other instance.</param>
/// <exception cref="ArgumentException">When types are incompatible.</exception> /// <returns>0 if equal, 1 if this instance is greater, -1 if this instance is less than.</returns>
public int CompareTo(object other) public Int32 CompareTo(T other) => this.Value.CompareTo(other);
{
switch (other) /// <summary>
{ /// Compares the value to the other instance.
case null: /// </summary>
return 1; /// <param name="other">The other instance.</param>
case AtomicTypeBase<T> atomic: /// <returns>0 if equal, 1 if this instance is greater, -1 if this instance is less than.</returns>
return BackingValue.CompareTo(atomic.BackingValue); public Int32 CompareTo(AtomicTypeBase<T> other) => this.BackingValue.CompareTo(other?.BackingValue ?? default);
case T variable:
return Value.CompareTo(variable); /// <summary>
} /// Determines whether the specified <see cref="Object" />, is equal to this instance.
/// </summary>
throw new ArgumentException("Incompatible comparison types"); /// <param name="other">The <see cref="Object" /> to compare with this instance.</param>
} /// <returns>
/// <c>true</c> if the specified <see cref="Object" /> is equal to this instance; otherwise, <c>false</c>.
/// <summary> /// </returns>
/// Compares the value to the other instance. public override Boolean Equals(Object other) {
/// </summary> switch(other) {
/// <param name="other">The other instance.</param> case AtomicTypeBase<T> atomic:
/// <returns>0 if equal, 1 if this instance is greater, -1 if this instance is less than.</returns> return this.Equals(atomic);
public int CompareTo(T other) => Value.CompareTo(other); case T variable:
return this.Equals(variable);
/// <summary> }
/// Compares the value to the other instance.
/// </summary> return false;
/// <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>
public int CompareTo(AtomicTypeBase<T> other) => BackingValue.CompareTo(other?.BackingValue ?? default); /// <summary>
/// Returns a hash code for this instance.
/// <summary> /// </summary>
/// Determines whether the specified <see cref="object" />, is equal to this instance. /// <returns>
/// </summary> /// A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table.
/// <param name="other">The <see cref="object" /> to compare with this instance.</param> /// </returns>
/// <returns> public override Int32 GetHashCode() => this.BackingValue.GetHashCode();
/// <c>true</c> if the specified <see cref="object" /> is equal to this instance; otherwise, <c>false</c>.
/// </returns> /// <inheritdoc />
public override bool Equals(object other) public Boolean Equals(AtomicTypeBase<T> other) =>
{ this.BackingValue == (other?.BackingValue ?? default);
switch (other)
{ /// <inheritdoc />
case AtomicTypeBase<T> atomic: public Boolean Equals(T other) => Equals(this.Value, other);
return Equals(atomic);
case T variable: /// <summary>
return Equals(variable); /// Converts from a long value to the target type.
} /// </summary>
/// <param name="backingValue">The backing value.</param>
return false; /// <returns>The value converted form a long value.</returns>
} protected abstract T FromLong(Int64 backingValue);
/// <summary> /// <summary>
/// Returns a hash code for this instance. /// Converts from the target type to a long value.
/// </summary> /// </summary>
/// <returns> /// <param name="value">The value.</param>
/// A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table. /// <returns>The value converted to a long value.</returns>
/// </returns> protected abstract Int64 ToLong(T value);
public override int GetHashCode() => BackingValue.GetHashCode(); }
/// <inheritdoc />
public bool Equals(AtomicTypeBase<T> other) =>
BackingValue == (other?.BackingValue ?? default);
/// <inheritdoc />
public bool Equals(T other) => Equals(Value, other);
/// <summary>
/// Converts from a long value to the target type.
/// </summary>
/// <param name="backingValue">The backing value.</param>
/// <returns>The value converted form a long value.</returns>
protected abstract T FromLong(long backingValue);
/// <summary>
/// Converts from the target type to a long value.
/// </summary>
/// <param name="value">The value.</param>
/// <returns>The value converted to a long value.</returns>
protected abstract long ToLong(T value);
}
} }
+178 -194
View File
@@ -1,197 +1,181 @@
namespace Unosquare.Swan.Abstractions using System;
{ using System.Threading;
using System;
using System.Threading; namespace Unosquare.Swan.Abstractions {
/// <summary>
/// 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
/// on the same <see cref="ThreadPool"/> thread every time the timer fires.
/// </summary>
public sealed class ExclusiveTimer : IDisposable {
private readonly Object _syncLock = new Object();
private readonly ManualResetEventSlim _cycleDoneEvent = new ManualResetEventSlim(true);
private readonly Timer _backingTimer;
private readonly TimerCallback _userCallback;
private readonly AtomicBoolean _isDisposing = new AtomicBoolean();
private readonly AtomicBoolean _isDisposed = new AtomicBoolean();
private Int32 _period;
/// <summary> /// <summary>
/// A threading <see cref="_backingTimer"/> implementation that executes at most one cycle at a time /// Initializes a new instance of the <see cref="ExclusiveTimer"/> class.
/// 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.
/// </summary> /// </summary>
public sealed class ExclusiveTimer : IDisposable /// <param name="timerCallback">The timer callback.</param>
{ /// <param name="state">The state.</param>
private readonly object _syncLock = new object(); /// <param name="dueTime">The due time.</param>
private readonly ManualResetEventSlim _cycleDoneEvent = new ManualResetEventSlim(true); /// <param name="period">The period.</param>
private readonly Timer _backingTimer; public ExclusiveTimer(TimerCallback timerCallback, Object state, Int32 dueTime, Int32 period) {
private readonly TimerCallback _userCallback; this._period = period;
private readonly AtomicBoolean _isDisposing = new AtomicBoolean(); this._userCallback = timerCallback;
private readonly AtomicBoolean _isDisposed = new AtomicBoolean(); this._backingTimer = new Timer(this.InternalCallback, state ?? this, dueTime, Timeout.Infinite);
private int _period; }
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="ExclusiveTimer"/> class. /// Initializes a new instance of the <see cref="ExclusiveTimer"/> class.
/// </summary> /// </summary>
/// <param name="timerCallback">The timer callback.</param> /// <param name="timerCallback">The timer callback.</param>
/// <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, TimeSpan dueTime, TimeSpan period)
{ : this(timerCallback, state, Convert.ToInt32(dueTime.TotalMilliseconds), Convert.ToInt32(period.TotalMilliseconds)) {
_period = period; // placeholder
_userCallback = timerCallback; }
_backingTimer = new Timer(InternalCallback, state ?? this, dueTime, Timeout.Infinite);
} /// <summary>
/// Initializes a new instance of the <see cref="ExclusiveTimer"/> class.
/// <summary> /// </summary>
/// Initializes a new instance of the <see cref="ExclusiveTimer"/> class. /// <param name="timerCallback">The timer callback.</param>
/// </summary> public ExclusiveTimer(TimerCallback timerCallback)
/// <param name="timerCallback">The timer callback.</param> : this(timerCallback, null, Timeout.Infinite, Timeout.Infinite) {
/// <param name="state">The state.</param> // placholder
/// <param name="dueTime">The due time.</param> }
/// <param name="period">The period.</param>
public ExclusiveTimer(TimerCallback timerCallback, object state, TimeSpan dueTime, TimeSpan period) /// <summary>
: this(timerCallback, state, Convert.ToInt32(dueTime.TotalMilliseconds), Convert.ToInt32(period.TotalMilliseconds)) /// Initializes a new instance of the <see cref="ExclusiveTimer"/> class.
{ /// </summary>
// placeholder /// <param name="timerCallback">The timer callback.</param>
} /// <param name="dueTime">The due time.</param>
/// <param name="period">The period.</param>
/// <summary> public ExclusiveTimer(Action timerCallback, Int32 dueTime, Int32 period)
/// Initializes a new instance of the <see cref="ExclusiveTimer"/> class. : this(s => timerCallback?.Invoke(), null, dueTime, period) {
/// </summary> // placeholder
/// <param name="timerCallback">The timer callback.</param> }
public ExclusiveTimer(TimerCallback timerCallback)
: this(timerCallback, null, Timeout.Infinite, Timeout.Infinite) /// <summary>
{ /// Initializes a new instance of the <see cref="ExclusiveTimer"/> class.
// placholder /// </summary>
} /// <param name="timerCallback">The timer callback.</param>
/// <param name="dueTime">The due time.</param>
/// <summary> /// <param name="period">The period.</param>
/// Initializes a new instance of the <see cref="ExclusiveTimer"/> class. public ExclusiveTimer(Action timerCallback, TimeSpan dueTime, TimeSpan period)
/// </summary> : this(s => timerCallback?.Invoke(), null, dueTime, period) {
/// <param name="timerCallback">The timer callback.</param> // placeholder
/// <param name="dueTime">The due time.</param> }
/// <param name="period">The period.</param>
public ExclusiveTimer(Action timerCallback, int dueTime, int period) /// <summary>
: this(s => { timerCallback?.Invoke(); }, null, dueTime, period) /// Initializes a new instance of the <see cref="ExclusiveTimer"/> class.
{ /// </summary>
// placeholder /// <param name="timerCallback">The timer callback.</param>
} public ExclusiveTimer(Action timerCallback)
: this(timerCallback, Timeout.Infinite, Timeout.Infinite) {
/// <summary> // placeholder
/// Initializes a new instance of the <see cref="ExclusiveTimer"/> class. }
/// </summary>
/// <param name="timerCallback">The timer callback.</param> /// <summary>
/// <param name="dueTime">The due time.</param> /// Gets a value indicating whether this instance is disposing.
/// <param name="period">The period.</param> /// </summary>
public ExclusiveTimer(Action timerCallback, TimeSpan dueTime, TimeSpan period) /// <value>
: this(s => { timerCallback?.Invoke(); }, null, dueTime, period) /// <c>true</c> if this instance is disposing; otherwise, <c>false</c>.
{ /// </value>
// placeholder public Boolean IsDisposing => this._isDisposing.Value;
}
/// <summary>
/// <summary> /// Gets a value indicating whether this instance is disposed.
/// Initializes a new instance of the <see cref="ExclusiveTimer"/> class. /// </summary>
/// </summary> /// <value>
/// <param name="timerCallback">The timer callback.</param> /// <c>true</c> if this instance is disposed; otherwise, <c>false</c>.
public ExclusiveTimer(Action timerCallback) /// </value>
: this(timerCallback, Timeout.Infinite, Timeout.Infinite) public Boolean IsDisposed => this._isDisposed.Value;
{
// placeholder /// <summary>
} /// Changes the start time and the interval between method invocations for the internal timer.
/// </summary>
/// <summary> /// <param name="dueTime">The due time.</param>
/// Gets a value indicating whether this instance is disposing. /// <param name="period">The period.</param>
/// </summary> public void Change(Int32 dueTime, Int32 period) {
/// <value> this._period = period;
/// <c>true</c> if this instance is disposing; otherwise, <c>false</c>.
/// </value> _ = this._backingTimer.Change(dueTime, Timeout.Infinite);
public bool IsDisposing => _isDisposing.Value; }
/// <summary> /// <summary>
/// Gets a value indicating whether this instance is disposed. /// Changes the start time and the interval between method invocations for the internal timer.
/// </summary> /// </summary>
/// <value> /// <param name="dueTime">The due time.</param>
/// <c>true</c> if this instance is disposed; otherwise, <c>false</c>. /// <param name="period">The period.</param>
/// </value> public void Change(TimeSpan dueTime, TimeSpan period)
public bool IsDisposed => _isDisposed.Value; => this.Change(Convert.ToInt32(dueTime.TotalMilliseconds), Convert.ToInt32(period.TotalMilliseconds));
/// <summary> /// <summary>
/// Changes the start time and the interval between method invocations for the internal timer. /// Changes the interval between method invocations for the internal timer.
/// </summary> /// </summary>
/// <param name="dueTime">The due time.</param> /// <param name="period">The period.</param>
/// <param name="period">The period.</param> public void Resume(Int32 period) => this.Change(0, period);
public void Change(int dueTime, int period)
{ /// <summary>
_period = period; /// Changes the interval between method invocations for the internal timer.
/// </summary>
_backingTimer.Change(dueTime, Timeout.Infinite); /// <param name="period">The period.</param>
} public void Resume(TimeSpan period) => this.Change(TimeSpan.Zero, period);
/// <summary> /// <summary>
/// Changes the start time and the interval between method invocations for the internal timer. /// Pauses this instance.
/// </summary> /// </summary>
/// <param name="dueTime">The due time.</param> public void Pause() => this.Change(Timeout.Infinite, Timeout.Infinite);
/// <param name="period">The period.</param>
public void Change(TimeSpan dueTime, TimeSpan period) /// <inheritdoc />
=> Change(Convert.ToInt32(dueTime.TotalMilliseconds), Convert.ToInt32(period.TotalMilliseconds)); public void Dispose() {
lock(this._syncLock) {
/// <summary> if(this._isDisposed == true || this._isDisposing == true) {
/// Changes the interval between method invocations for the internal timer. return;
/// </summary> }
/// <param name="period">The period.</param>
public void Resume(int period) => Change(0, period); this._isDisposing.Value = true;
}
/// <summary>
/// Changes the interval between method invocations for the internal timer. try {
/// </summary> this._backingTimer.Dispose();
/// <param name="period">The period.</param> this._cycleDoneEvent.Wait();
public void Resume(TimeSpan period) => Change(TimeSpan.Zero, period); this._cycleDoneEvent.Dispose();
} finally {
/// <summary> this._isDisposed.Value = true;
/// Pauses this instance. this._isDisposing.Value = false;
/// </summary> }
public void Pause() => Change(Timeout.Infinite, Timeout.Infinite); }
/// <inheritdoc /> /// <summary>
public void Dispose() /// Logic that runs every time the timer hits the due time.
{ /// </summary>
lock (_syncLock) /// <param name="state">The state.</param>
{ private void InternalCallback(Object state) {
if (_isDisposed == true || _isDisposing == true) lock(this._syncLock) {
return; if(this.IsDisposed || this.IsDisposing) {
return;
_isDisposing.Value = true; }
} }
try if(this._cycleDoneEvent.IsSet == false) {
{ return;
_backingTimer.Dispose(); }
_cycleDoneEvent.Wait();
_cycleDoneEvent.Dispose(); this._cycleDoneEvent.Reset();
}
finally try {
{ this._userCallback(state);
_isDisposed.Value = true; } finally {
_isDisposing.Value = false; this._cycleDoneEvent?.Set();
} _ = this._backingTimer?.Change(this._period, Timeout.Infinite);
} }
}
/// <summary> }
/// Logic that runs every time the timer hits the due time.
/// </summary>
/// <param name="state">The state.</param>
private void InternalCallback(object state)
{
lock (_syncLock)
{
if (IsDisposed || IsDisposing)
return;
}
if (_cycleDoneEvent.IsSet == false)
return;
_cycleDoneEvent.Reset();
try
{
_userCallback(state);
}
finally
{
_cycleDoneEvent?.Set();
_backingTimer?.Change(_period, Timeout.Infinite);
}
}
}
} }
@@ -1,94 +1,88 @@
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>
/// Represents a generic expression parser.
/// </summary>
public abstract class ExpressionParser {
/// <summary> /// <summary>
/// Represents a generic expression parser. /// Resolves the expression.
/// </summary> /// </summary>
public abstract class ExpressionParser /// <typeparam name="T">The type of expression result.</typeparam>
{ /// <param name="tokens">The tokens.</param>
/// <summary> /// <returns>The representation of the expression parsed.</returns>
/// Resolves the expression. public virtual T ResolveExpression<T>(IEnumerable<Token> tokens) {
/// </summary> UnaryExpression conversion = Expression.Convert(this.Parse(tokens), typeof(T));
/// <typeparam name="T">The type of expression result.</typeparam> return Expression.Lambda<Func<T>>(conversion).Compile()();
/// <param name="tokens">The tokens.</param> }
/// <returns>The representation of the expression parsed.</returns>
public virtual T ResolveExpression<T>(IEnumerable<Token> tokens) /// <summary>
{ /// Parses the specified tokens.
var conversion = Expression.Convert(Parse(tokens), typeof(T)); /// </summary>
return Expression.Lambda<Func<T>>(conversion).Compile()(); /// <param name="tokens">The tokens.</param>
} /// <returns>The final expression.</returns>
public virtual Expression Parse(IEnumerable<Token> tokens) {
/// <summary> List<Stack<Expression>> expressionStack = new List<Stack<Expression>>();
/// Parses the specified tokens.
/// </summary> foreach(Token token in tokens) {
/// <param name="tokens">The tokens.</param> if(expressionStack.Any() == false) {
/// <returns>The final expression.</returns> expressionStack.Add(new Stack<Expression>());
public virtual Expression Parse(IEnumerable<Token> tokens) }
{
var expressionStack = new List<Stack<Expression>>(); switch(token.Type) {
case TokenType.Wall:
foreach (var token in tokens) expressionStack.Add(new Stack<Expression>());
{ break;
if (expressionStack.Any() == false) case TokenType.Number:
expressionStack.Add(new Stack<Expression>()); expressionStack.Last().Push(Expression.Constant(Convert.ToDecimal(token.Value)));
break;
switch (token.Type) case TokenType.Variable:
{ this.ResolveVariable(token.Value, expressionStack.Last());
case TokenType.Wall: break;
expressionStack.Add(new Stack<Expression>()); case TokenType.String:
break; expressionStack.Last().Push(Expression.Constant(token.Value));
case TokenType.Number: break;
expressionStack.Last().Push(Expression.Constant(Convert.ToDecimal(token.Value))); case TokenType.Operator:
break; this.ResolveOperator(token.Value, expressionStack.Last());
case TokenType.Variable: break;
ResolveVariable(token.Value, expressionStack.Last()); case TokenType.Function:
break; this.ResolveFunction(token.Value, expressionStack.Last());
case TokenType.String:
expressionStack.Last().Push(Expression.Constant(token.Value)); if(expressionStack.Count > 1 && expressionStack.Last().Count == 1) {
break; Expression lastValue = expressionStack.Last().Pop();
case TokenType.Operator: _ = expressionStack.Remove(expressionStack.Last());
ResolveOperator(token.Value, expressionStack.Last()); expressionStack.Last().Push(lastValue);
break; }
case TokenType.Function:
ResolveFunction(token.Value, expressionStack.Last()); break;
}
if (expressionStack.Count > 1 && expressionStack.Last().Count == 1) }
{
var lastValue = expressionStack.Last().Pop(); return expressionStack.Last().Pop();
expressionStack.Remove(expressionStack.Last()); }
expressionStack.Last().Push(lastValue);
} /// <summary>
/// Resolves the variable.
break; /// </summary>
} /// <param name="value">The value.</param>
} /// <param name="expressionStack">The expression stack.</param>
public abstract void ResolveVariable(String value, Stack<Expression> expressionStack);
return expressionStack.Last().Pop();
} /// <summary>
/// Resolves the operator.
/// <summary> /// </summary>
/// Resolves the variable. /// <param name="value">The value.</param>
/// </summary> /// <param name="expressionStack">The expression stack.</param>
/// <param name="value">The value.</param> public abstract void ResolveOperator(String value, Stack<Expression> expressionStack);
/// <param name="expressionStack">The expression stack.</param>
public abstract void ResolveVariable(string value, Stack<Expression> expressionStack); /// <summary>
/// Resolves the function.
/// <summary> /// </summary>
/// Resolves the operator. /// <param name="value">The value.</param>
/// </summary> /// <param name="expressionStack">The expression stack.</param>
/// <param name="value">The value.</param> public abstract void ResolveFunction(String value, Stack<Expression> expressionStack);
/// <param name="expressionStack">The expression stack.</param> }
public abstract void ResolveOperator(string value, Stack<Expression> expressionStack);
/// <summary>
/// Resolves the function.
/// </summary>
/// <param name="value">The value.</param>
/// <param name="expressionStack">The expression stack.</param>
public abstract void ResolveFunction(string value, Stack<Expression> expressionStack);
}
} }
+28 -24
View File
@@ -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>
/// Interface object map.
/// </summary>
public interface IObjectMap {
/// <summary> /// <summary>
/// Interface object map. /// Gets or sets the map.
/// </summary> /// </summary>
public interface IObjectMap Dictionary<PropertyInfo, List<PropertyInfo>> Map {
{ get;
/// <summary> }
/// Gets or sets the map.
/// </summary> /// <summary>
Dictionary<PropertyInfo, List<PropertyInfo>> Map { get; } /// Gets or sets the type of the source.
/// </summary>
/// <summary> Type SourceType {
/// Gets or sets the type of the source. get;
/// </summary> }
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 {
Type DestinationType { get; } get;
} }
}
} }
+19 -21
View File
@@ -1,24 +1,22 @@
namespace Unosquare.Swan.Abstractions using System;
{
using System; namespace Unosquare.Swan.Abstractions {
/// <summary>
/// Defines a generic interface for synchronized locking mechanisms.
/// </summary>
public interface ISyncLocker : IDisposable {
/// <summary> /// <summary>
/// Defines a generic interface for synchronized locking mechanisms. /// Acquires a writer lock.
/// The lock is released when the returned locking object is disposed.
/// </summary> /// </summary>
public interface ISyncLocker : IDisposable /// <returns>A disposable locking object.</returns>
{ IDisposable AcquireWriterLock();
/// <summary>
/// Acquires a writer lock. /// <summary>
/// The lock is released when the returned locking object is disposed. /// Acquires a reader lock.
/// </summary> /// The lock is released when the returned locking object is disposed.
/// <returns>A disposable locking object.</returns> /// </summary>
IDisposable AcquireWriterLock(); /// <returns>A disposable locking object.</returns>
IDisposable AcquireReaderLock();
/// <summary> }
/// Acquires a reader lock.
/// The lock is released when the returned locking object is disposed.
/// </summary>
/// <returns>A disposable locking object.</returns>
IDisposable AcquireReaderLock();
}
} }
+20 -18
View File
@@ -1,21 +1,23 @@
namespace Unosquare.Swan.Abstractions using System;
{
namespace Unosquare.Swan.Abstractions {
/// <summary>
/// A simple Validator interface.
/// </summary>
public interface IValidator {
/// <summary> /// <summary>
/// A simple Validator interface. /// The error message.
/// </summary> /// </summary>
public interface IValidator String ErrorMessage {
{ get;
/// <summary> }
/// The error message.
/// </summary> /// <summary>
string ErrorMessage { get; } /// Checks if a value is valid.
/// </summary>
/// <summary> /// <typeparam name="T">The type.</typeparam>
/// Checks if a value is valid. /// <param name="value"> The value.</param>
/// </summary> /// <returns>True if it is valid.False if it is not.</returns>
/// <typeparam name="T">The type.</typeparam> Boolean IsValid<T>(T value);
/// <param name="value"> The value.</param> }
/// <returns>True if it is valid.False if it is not.</returns>
bool IsValid<T>(T value);
}
} }
+60 -54
View File
@@ -1,57 +1,63 @@
namespace Unosquare.Swan.Abstractions using System;
{
using System; namespace Unosquare.Swan.Abstractions {
/// <summary>
/// Provides a generalized API for ManualResetEvent and ManualResetEventSlim.
/// </summary>
/// <seealso cref="IDisposable" />
public interface IWaitEvent : IDisposable {
/// <summary> /// <summary>
/// Provides a generalized API for ManualResetEvent and ManualResetEventSlim. /// Gets a value indicating whether the event is in the completed state.
/// </summary> /// </summary>
/// <seealso cref="IDisposable" /> Boolean IsCompleted {
public interface IWaitEvent : IDisposable get;
{ }
/// <summary>
/// Gets a value indicating whether the event is in the completed state. /// <summary>
/// </summary> /// Gets a value indicating whether the Begin method has been called.
bool IsCompleted { get; } /// It returns false after the Complete method is called.
/// </summary>
/// <summary> Boolean IsInProgress {
/// Gets a value indicating whether the Begin method has been called. get;
/// It returns false after the Complete method is called. }
/// </summary>
bool IsInProgress { get; } /// <summary>
/// Returns true if the underlying handle is not closed and it is still valid.
/// <summary> /// </summary>
/// Returns true if the underlying handle is not closed and it is still valid. Boolean IsValid {
/// </summary> get;
bool 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> }
/// Enters the state in which waiters need to wait.
/// All future waiters will block when they call the Wait method. /// <summary>
/// </summary> /// Enters the state in which waiters need to wait.
void Begin(); /// All future waiters will block when they call the Wait method.
/// </summary>
/// <summary> void Begin();
/// Leaves the state in which waiters need to wait.
/// All current waiters will continue. /// <summary>
/// </summary> /// Leaves the state in which waiters need to wait.
void Complete(); /// All current waiters will continue.
/// </summary>
/// <summary> void Complete();
/// Waits for the event to be completed.
/// </summary> /// <summary>
void Wait(); /// Waits for the event to be completed.
/// </summary>
/// <summary> void Wait();
/// Waits for the event to be completed.
/// Returns <c>true</c> when there was no timeout. False if the timeout was reached. /// <summary>
/// </summary> /// Waits for the event to be completed.
/// <param name="timeout">The maximum amount of time to wait for.</param> /// Returns <c>true</c> when there was no timeout. False if the timeout was reached.
/// <returns><c>true</c> when there was no timeout. <c>false</c> if the timeout was reached.</returns> /// </summary>
bool Wait(TimeSpan timeout); /// <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>
Boolean Wait(TimeSpan timeout);
}
} }
+13 -15
View File
@@ -1,18 +1,16 @@
namespace Unosquare.Swan.Abstractions namespace Unosquare.Swan.Abstractions {
{ /// <summary>
/// A simple interface for application workers.
/// </summary>
public interface IWorker {
/// <summary> /// <summary>
/// A simple interface for application workers. /// Should start the task immediately and asynchronously.
/// </summary> /// </summary>
public interface IWorker void Start();
{
/// <summary> /// <summary>
/// Should start the task immediately and asynchronously. /// Should stop the task immediately and synchronously.
/// </summary> /// </summary>
void Start(); void Stop();
}
/// <summary>
/// Should stop the task immediately and synchronously.
/// </summary>
void Stop();
}
} }
+150 -164
View File
@@ -1,169 +1,155 @@
#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; /// <summary>
using Swan; /// Represents an background worker abstraction with a life cycle and running at a independent thread.
/// </summary>
public abstract class RunnerBase {
private Thread _worker;
private CancellationTokenSource _cancelTokenSource;
private ManualResetEvent _workFinished;
/// <summary> /// <summary>
/// Represents an background worker abstraction with a life cycle and running at a independent thread. /// Initializes a new instance of the <see cref="RunnerBase"/> class.
/// </summary> /// </summary>
public abstract class RunnerBase /// <param name="isEnabled">if set to <c>true</c> [is enabled].</param>
{ protected RunnerBase(Boolean isEnabled) {
private Thread _worker; this.Name = this.GetType().Name;
private CancellationTokenSource _cancelTokenSource; this.IsEnabled = isEnabled;
private ManualResetEvent _workFinished; }
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="RunnerBase"/> class. /// Gets the error messages.
/// </summary> /// </summary>
/// <param name="isEnabled">if set to <c>true</c> [is enabled].</param> /// <value>
protected RunnerBase(bool isEnabled) /// The error messages.
{ /// </value>
Name = GetType().Name; public List<String> ErrorMessages { get; } = new List<String>();
IsEnabled = isEnabled;
} /// <summary>
/// Gets the name.
/// <summary> /// </summary>
/// Gets the error messages. /// <value>
/// </summary> /// The name.
/// <value> /// </value>
/// The error messages. public String Name {
/// </value> get;
public List<string> ErrorMessages { get; } = new List<string>(); }
/// <summary> /// <summary>
/// Gets the name. /// Gets a value indicating whether this instance is running.
/// </summary> /// </summary>
/// <value> /// <value>
/// The name. /// <c>true</c> if this instance is running; otherwise, <c>false</c>.
/// </value> /// </value>
public string Name { get; } public Boolean IsRunning {
get; private set;
/// <summary> }
/// Gets a value indicating whether this instance is running.
/// </summary> /// <summary>
/// <value> /// Gets a value indicating whether this instance is enabled.
/// <c>true</c> if this instance is running; otherwise, <c>false</c>. /// </summary>
/// </value> /// <value>
public bool IsRunning { get; private set; } /// <c>true</c> if this instance is enabled; otherwise, <c>false</c>.
/// </value>
/// <summary> public Boolean IsEnabled {
/// Gets a value indicating whether this instance is enabled. get;
/// </summary> }
/// <value>
/// <c>true</c> if this instance is enabled; otherwise, <c>false</c>. /// <summary>
/// </value> /// Starts this instance.
public bool IsEnabled { get; } /// </summary>
public virtual void Start() {
/// <summary> if(this.IsEnabled == false) {
/// Starts this instance. return;
/// </summary> }
public virtual void Start()
{ $"Start Requested".Debug(this.Name);
if (IsEnabled == false) this._cancelTokenSource = new CancellationTokenSource();
return; this._workFinished = new ManualResetEvent(false);
$"Start Requested".Debug(Name); this._worker = new Thread(() => {
_cancelTokenSource = new CancellationTokenSource(); _ = this._workFinished.Reset();
_workFinished = new ManualResetEvent(false); this.IsRunning = true;
try {
_worker = new Thread(() => this.Setup();
{ this.DoBackgroundWork(this._cancelTokenSource.Token);
_workFinished.Reset(); } catch(ThreadAbortException) {
IsRunning = true; $"{nameof(ThreadAbortException)} caught.".Warn(this.Name);
try } catch(Exception ex) {
{ $"{ex.GetType()}: {ex.Message}\r\n{ex.StackTrace}".Error(this.Name);
Setup(); } finally {
DoBackgroundWork(_cancelTokenSource.Token); this.Cleanup();
} _ = this._workFinished?.Set();
catch (ThreadAbortException) this.IsRunning = false;
{ "Stopped Completely".Debug(this.Name);
$"{nameof(ThreadAbortException)} caught.".Warn(Name); }
} }) {
catch (Exception ex) IsBackground = true,
{ Name = $"{this.Name}Thread",
$"{ex.GetType()}: {ex.Message}\r\n{ex.StackTrace}".Error(Name); };
}
finally this._worker.Start();
{ }
Cleanup();
_workFinished?.Set(); /// <summary>
IsRunning = false; /// Stops this instance.
"Stopped Completely".Debug(Name); /// </summary>
} public virtual void Stop() {
}) if(this.IsEnabled == false || this.IsRunning == false) {
{ return;
IsBackground = true, }
Name = $"{Name}Thread",
}; $"Stop Requested".Debug(this.Name);
this._cancelTokenSource.Cancel();
_worker.Start(); Int32 waitRetries = 5;
} while(waitRetries >= 1) {
if(this._workFinished.WaitOne(250)) {
/// <summary> waitRetries = -1;
/// Stops this instance. break;
/// </summary> }
public virtual void Stop()
{ waitRetries--;
if (IsEnabled == false || IsRunning == false) }
return;
if(waitRetries < 0) {
$"Stop Requested".Debug(Name); "Workbench stopped gracefully".Debug(this.Name);
_cancelTokenSource.Cancel(); } else {
var waitRetries = 5; "Did not respond to stop request. Aborting thread and waiting . . .".Warn(this.Name);
while (waitRetries >= 1) this._worker.Abort();
{
if (_workFinished.WaitOne(250)) if(this._workFinished.WaitOne(5000) == false) {
{ "Waited and no response. Worker might have been left in an inconsistent state.".Error(this.Name);
waitRetries = -1; } else {
break; "Waited for worker and it finally responded (OK).".Debug(this.Name);
} }
}
waitRetries--;
} this._workFinished.Dispose();
this._workFinished = null;
if (waitRetries < 0) }
{
"Workbench stopped gracefully".Debug(Name); /// <summary>
} /// Setups this instance.
else /// </summary>
{ protected virtual void Setup() {
"Did not respond to stop request. Aborting thread and waiting . . .".Warn(Name); // empty
_worker.Abort(); }
if (_workFinished.WaitOne(5000) == false) /// <summary>
"Waited and no response. Worker might have been left in an inconsistent state.".Error(Name); /// Cleanups this instance.
else /// </summary>
"Waited for worker and it finally responded (OK).".Debug(Name); protected virtual void Cleanup() {
} // empty
}
_workFinished.Dispose();
_workFinished = null; /// <summary>
} /// Does the background work.
/// </summary>
/// <summary> /// <param name="ct">The ct.</param>
/// Setups this instance. protected abstract void DoBackgroundWork(CancellationToken ct);
/// </summary> }
protected virtual void Setup()
{
// empty
}
/// <summary>
/// Cleanups this instance.
/// </summary>
protected virtual void Cleanup()
{
// empty
}
/// <summary>
/// Does the background work.
/// </summary>
/// <param name="ct">The ct.</param>
protected abstract void DoBackgroundWork(CancellationToken ct);
}
} }
#endif #endif
@@ -1,188 +1,184 @@
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>
/// Represents a provider to save and load settings using a plain JSON file.
/// </summary>
/// <example>
/// The following example shows how to save and load settings.
/// <code>
/// using Unosquare.Swan.Abstractions;
///
/// public class Example
/// {
/// public static void Main()
/// {
/// // get user from settings
/// var user = SettingsProvider&lt;Settings&gt;.Instance.Global.User;
///
/// // modify the port
/// SettingsProvider&lt;Settings&gt;.Instance.Global.Port = 20;
///
/// // if we want these settings to persist
/// SettingsProvider&lt;Settings&gt;.Instance.PersistGlobalSettings();
/// }
///
/// public class Settings
/// {
/// public int Port { get; set; } = 9696;
///
/// public string User { get; set; } = "User";
/// }
/// }
/// </code>
/// </example>
/// <typeparam name="T">The type of settings model.</typeparam>
public sealed class SettingsProvider<T>
: SingletonBase<SettingsProvider<T>> {
private readonly Object _syncRoot = new Object();
private T _global;
/// <summary> /// <summary>
/// Represents a provider to save and load settings using a plain JSON file. /// Gets or sets the configuration file path. By default the entry assembly directory is used
/// and the filename is 'appsettings.json'.
/// </summary> /// </summary>
/// <example> /// <value>
/// The following example shows how to save and load settings. /// The configuration file path.
/// <code> /// </value>
/// using Unosquare.Swan.Abstractions; public String ConfigurationFilePath {
/// get; set;
/// public class Example } =
/// {
/// public static void Main()
/// {
/// // get user from settings
/// var user = SettingsProvider&lt;Settings&gt;.Instance.Global.User;
///
/// // modify the port
/// SettingsProvider&lt;Settings&gt;.Instance.Global.Port = 20;
///
/// // if we want these settings to persist
/// SettingsProvider&lt;Settings&gt;.Instance.PersistGlobalSettings();
/// }
///
/// public class Settings
/// {
/// public int Port { get; set; } = 9696;
///
/// public string User { get; set; } = "User";
/// }
/// }
/// </code>
/// </example>
/// <typeparam name="T">The type of settings model.</typeparam>
public sealed class SettingsProvider<T>
: SingletonBase<SettingsProvider<T>>
{
private readonly object _syncRoot = new object();
private T _global;
/// <summary>
/// Gets or sets the configuration file path. By default the entry assembly directory is used
/// and the filename is 'appsettings.json'.
/// </summary>
/// <value>
/// The configuration file path.
/// </value>
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
Path.Combine(Runtime.EntryAssemblyDirectory, "appsettings.json"); Path.Combine(Runtime.EntryAssemblyDirectory, "appsettings.json");
#endif #endif
/// <summary> /// <summary>
/// Gets the global settings object. /// Gets the global settings object.
/// </summary> /// </summary>
/// <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 this._global;
}
return _global; }
} }
}
} /// <summary>
/// Reloads the global settings.
/// <summary> /// </summary>
/// Reloads the global settings. public void ReloadGlobalSettings() {
/// </summary> if(File.Exists(this.ConfigurationFilePath) == false || File.ReadAllText(this.ConfigurationFilePath).Length == 0) {
public void ReloadGlobalSettings() this.ResetGlobalSettings();
{ return;
if (File.Exists(ConfigurationFilePath) == false || File.ReadAllText(ConfigurationFilePath).Length == 0) }
{
ResetGlobalSettings(); lock(this._syncRoot) {
return; this._global = Json.Deserialize<T>(File.ReadAllText(this.ConfigurationFilePath));
} }
}
lock (_syncRoot)
_global = Json.Deserialize<T>(File.ReadAllText(ConfigurationFilePath)); /// <summary>
} /// Persists the global settings.
/// </summary>
/// <summary> public void PersistGlobalSettings() => File.WriteAllText(this.ConfigurationFilePath, Json.Serialize(this.Global, true));
/// Persists the global settings.
/// </summary> /// <summary>
public void PersistGlobalSettings() => File.WriteAllText(ConfigurationFilePath, Json.Serialize(Global, true)); /// Updates settings from list.
/// </summary>
/// <summary> /// <param name="propertyList">The list.</param>
/// Updates settings from list. /// <returns>
/// </summary> /// A list of settings of type ref="ExtendedPropertyInfo".
/// <param name="propertyList">The list.</param> /// </returns>
/// <returns> /// <exception cref="ArgumentNullException">propertyList.</exception>
/// A list of settings of type ref="ExtendedPropertyInfo". public List<String> RefreshFromList(List<ExtendedPropertyInfo<T>> propertyList) {
/// </returns> if(propertyList == null) {
/// <exception cref="ArgumentNullException">propertyList.</exception> throw new ArgumentNullException(nameof(propertyList));
public List<string> RefreshFromList(List<ExtendedPropertyInfo<T>> propertyList) }
{
if (propertyList == null) List<String> changedSettings = new List<String>();
throw new ArgumentNullException(nameof(propertyList)); IEnumerable<PropertyInfo> globalProps = Runtime.PropertyTypeCache.RetrieveAllProperties<T>();
var changedSettings = new List<string>(); foreach(ExtendedPropertyInfo<T> property in propertyList) {
var globalProps = Runtime.PropertyTypeCache.RetrieveAllProperties<T>(); PropertyInfo propertyInfo = globalProps.FirstOrDefault(x => x.Name == property.Property);
foreach (var property in propertyList) if(propertyInfo == null) {
{ continue;
var propertyInfo = globalProps.FirstOrDefault(x => x.Name == property.Property); }
if (propertyInfo == null) continue; Object originalValue = propertyInfo.GetValue(this.Global);
Boolean isChanged = propertyInfo.PropertyType.IsArray
var originalValue = propertyInfo.GetValue(Global); ? property.Value is IEnumerable enumerable && propertyInfo.TrySetArray(enumerable.Cast<Object>(), this.Global)
var isChanged = propertyInfo.PropertyType.IsArray : this.SetValue(property.Value, originalValue, propertyInfo);
? property.Value is IEnumerable enumerable && propertyInfo.TrySetArray(enumerable.Cast<object>(), Global)
: 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;
} }
/// <summary> /// <summary>
/// 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] }) .ToList();
.ToList(); }
}
/// <summary>
/// <summary> /// Resets the global settings.
/// Resets the global settings. /// </summary>
/// </summary> public void ResetGlobalSettings() {
public void ResetGlobalSettings() lock(this._syncRoot) {
{ this._global = Activator.CreateInstance<T>();
lock (_syncRoot) }
_global = Activator.CreateInstance<T>();
this.PersistGlobalSettings();
PersistGlobalSettings(); }
}
private Boolean SetValue(Object property, Object originalValue, PropertyInfo propertyInfo) {
private bool SetValue(object property, object originalValue, PropertyInfo propertyInfo) switch(property) {
{ case null when originalValue == null:
switch (property) break;
{ case null:
case null when originalValue == null: propertyInfo.SetValue(this.Global, null);
break; return true;
case null: default:
propertyInfo.SetValue(Global, null); if(propertyInfo.PropertyType.TryParseBasicType(property, out Object propertyValue) &&
return true; !propertyValue.Equals(originalValue)) {
default: propertyInfo.SetValue(this.Global, propertyValue);
if (propertyInfo.PropertyType.TryParseBasicType(property, out var propertyValue) && return true;
!propertyValue.Equals(originalValue)) }
{
propertyInfo.SetValue(Global, propertyValue); break;
return true; }
}
return false;
break; }
} }
return false;
}
}
} }
@@ -1,59 +1,57 @@
namespace Unosquare.Swan.Abstractions using System;
{
using System; namespace Unosquare.Swan.Abstractions {
/// <summary>
/// Represents a singleton pattern abstract class.
/// </summary>
/// <typeparam name="T">The type of class.</typeparam>
public abstract class SingletonBase<T> : IDisposable
where T : class {
/// <summary> /// <summary>
/// Represents a singleton pattern abstract class. /// The static, singleton instance reference.
/// </summary> /// </summary>
/// <typeparam name="T">The type of class.</typeparam> protected static readonly Lazy<T> LazyInstance = new Lazy<T>(
public abstract class SingletonBase<T> : IDisposable valueFactory: () => Activator.CreateInstance(typeof(T), true) as T,
where T : class isThreadSafe: true);
{
/// <summary> private Boolean _isDisposing; // To detect redundant calls
/// The static, singleton instance reference.
/// </summary> /// <summary>
protected static readonly Lazy<T> LazyInstance = new Lazy<T>( /// Gets the instance that this singleton represents.
valueFactory: () => Activator.CreateInstance(typeof(T), true) as T, /// If the instance is null, it is constructed and assigned when this member is accessed.
isThreadSafe: true); /// </summary>
/// <value>
private bool _isDisposing; // To detect redundant calls /// The instance.
/// </value>
/// <summary> public static T Instance => LazyInstance.Value;
/// Gets the instance that this singleton represents.
/// If the instance is null, it is constructed and assigned when this member is accessed. /// <inheritdoc />
/// </summary> public void Dispose() => this.Dispose(true);
/// <value>
/// The instance. /// <summary>
/// </value> /// Releases unmanaged and - optionally - managed resources.
public static T Instance => LazyInstance.Value; /// Call the GC.SuppressFinalize if you override this method and use
/// a non-default class finalizer (destructor).
/// <inheritdoc /> /// </summary>
public void Dispose() => Dispose(true); /// <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(Boolean disposeManaged) {
/// <summary> if(this._isDisposing) {
/// Releases unmanaged and - optionally - managed resources. return;
/// Call the GC.SuppressFinalize if you override this method and use }
/// a non-default class finalizer (destructor).
/// </summary> this._isDisposing = true;
/// <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) // free managed resources
{ if(LazyInstance == null) {
if (_isDisposing) return; return;
}
_isDisposing = true;
try {
// free managed resources IDisposable disposableInstance = LazyInstance.Value as IDisposable;
if (LazyInstance == null) return; disposableInstance?.Dispose();
} catch {
try // swallow
{ }
var disposableInstance = LazyInstance.Value as IDisposable; }
disposableInstance?.Dispose(); }
}
catch
{
// swallow
}
}
}
} }
+427 -436
View File
@@ -1,143 +1,139 @@
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>
/// Represents a generic tokenizer.
/// </summary>
public abstract class Tokenizer {
private const Char PeriodChar = '.';
private const Char CommaChar = ',';
private const Char StringQuotedChar = '"';
private const Char OpenFuncChar = '(';
private const Char CloseFuncChar = ')';
private const Char NegativeChar = '-';
private const String OpenFuncStr = "(";
private readonly List<Operator> _operators = new List<Operator>();
/// <summary> /// <summary>
/// Represents a generic tokenizer. /// Initializes a new instance of the <see cref="Tokenizer"/> class.
/// This constructor will use the following default operators:
///
/// <list type="table">
/// <listheader>
/// <term>Operator</term>
/// <description>Precedence</description>
/// </listheader>
/// <item>
/// <term>=</term>
/// <description>1</description>
/// </item>
/// <item>
/// <term>!=</term>
/// <description>1</description>
/// </item>
/// <item>
/// <term>&gt;</term>
/// <description>2</description>
/// </item>
/// <item>
/// <term>&lt;</term>
/// <description>2</description>
/// </item>
/// <item>
/// <term>&gt;=</term>
/// <description>2</description>
/// </item>
/// <item>
/// <term>&lt;=</term>
/// <description>2</description>
/// </item>
/// <item>
/// <term>+</term>
/// <description>3</description>
/// </item>
/// <item>
/// <term>&amp;</term>
/// <description>3</description>
/// </item>
/// <item>
/// <term>-</term>
/// <description>3</description>
/// </item>
/// <item>
/// <term>*</term>
/// <description>4</description>
/// </item>
/// <item>
/// <term>(backslash)</term>
/// <description>4</description>
/// </item>
/// <item>
/// <term>/</term>
/// <description>4</description>
/// </item>
/// <item>
/// <term>^</term>
/// <description>4</description>
/// </item>
/// </list>
/// </summary> /// </summary>
public abstract class Tokenizer /// <param name="input">The input.</param>
protected Tokenizer(String input) {
this._operators.AddRange(this.GetDefaultOperators());
this.Tokenize(input);
}
/// <summary>
/// Initializes a new instance of the <see cref="Tokenizer" /> class.
/// </summary>
/// <param name="input">The input.</param>
/// <param name="operators">The operators to use.</param>
protected Tokenizer(String input, IEnumerable<Operator> operators) {
this._operators.AddRange(operators);
this.Tokenize(input);
}
/// <summary>
/// Gets the tokens.
/// </summary>
/// <value>
/// The tokens.
/// </value>
public List<Token> Tokens { get; } = new List<Token>();
/// <summary>
/// Validates the input and return the start index for tokenizer.
/// </summary>
/// <param name="input">The input.</param>
/// <param name="startIndex">The start index.</param>
/// <returns><c>true</c> if the input is valid, otherwise <c>false</c>.</returns>
public abstract Boolean ValidateInput(String input, out Int32 startIndex);
/// <summary>
/// Resolves the type of the function or member.
/// </summary>
/// <param name="input">The input.</param>
/// <returns>The token type.</returns>
public abstract TokenType ResolveFunctionOrMemberType(String input);
/// <summary>
/// Evaluates the function or member.
/// </summary>
/// <param name="input">The input.</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>
public virtual Boolean EvaluateFunctionOrMember(String input, Int32 position) => false;
/// <summary>
/// Gets the default operators.
/// </summary>
/// <returns>An array with the operators to use for the tokenizer.</returns>
public virtual Operator[] GetDefaultOperators() => new[]
{ {
private const char PeriodChar = '.';
private const char CommaChar = ',';
private const char StringQuotedChar = '"';
private const char OpenFuncChar = '(';
private const char CloseFuncChar = ')';
private const char NegativeChar = '-';
private const string OpenFuncStr = "(";
private readonly List<Operator> _operators = new List<Operator>();
/// <summary>
/// Initializes a new instance of the <see cref="Tokenizer"/> class.
/// This constructor will use the following default operators:
///
/// <list type="table">
/// <listheader>
/// <term>Operator</term>
/// <description>Precedence</description>
/// </listheader>
/// <item>
/// <term>=</term>
/// <description>1</description>
/// </item>
/// <item>
/// <term>!=</term>
/// <description>1</description>
/// </item>
/// <item>
/// <term>&gt;</term>
/// <description>2</description>
/// </item>
/// <item>
/// <term>&lt;</term>
/// <description>2</description>
/// </item>
/// <item>
/// <term>&gt;=</term>
/// <description>2</description>
/// </item>
/// <item>
/// <term>&lt;=</term>
/// <description>2</description>
/// </item>
/// <item>
/// <term>+</term>
/// <description>3</description>
/// </item>
/// <item>
/// <term>&amp;</term>
/// <description>3</description>
/// </item>
/// <item>
/// <term>-</term>
/// <description>3</description>
/// </item>
/// <item>
/// <term>*</term>
/// <description>4</description>
/// </item>
/// <item>
/// <term>(backslash)</term>
/// <description>4</description>
/// </item>
/// <item>
/// <term>/</term>
/// <description>4</description>
/// </item>
/// <item>
/// <term>^</term>
/// <description>4</description>
/// </item>
/// </list>
/// </summary>
/// <param name="input">The input.</param>
protected Tokenizer(string input)
{
_operators.AddRange(GetDefaultOperators());
Tokenize(input);
}
/// <summary>
/// Initializes a new instance of the <see cref="Tokenizer" /> class.
/// </summary>
/// <param name="input">The input.</param>
/// <param name="operators">The operators to use.</param>
protected Tokenizer(string input, IEnumerable<Operator> operators)
{
_operators.AddRange(operators);
Tokenize(input);
}
/// <summary>
/// Gets the tokens.
/// </summary>
/// <value>
/// The tokens.
/// </value>
public List<Token> Tokens { get; } = new List<Token>();
/// <summary>
/// Validates the input and return the start index for tokenizer.
/// </summary>
/// <param name="input">The input.</param>
/// <param name="startIndex">The start index.</param>
/// <returns><c>true</c> if the input is valid, otherwise <c>false</c>.</returns>
public abstract bool ValidateInput(string input, out int startIndex);
/// <summary>
/// Resolves the type of the function or member.
/// </summary>
/// <param name="input">The input.</param>
/// <returns>The token type.</returns>
public abstract TokenType ResolveFunctionOrMemberType(string input);
/// <summary>
/// Evaluates the function or member.
/// </summary>
/// <param name="input">The input.</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>
public virtual bool EvaluateFunctionOrMember(string input, int position) => false;
/// <summary>
/// Gets the default operators.
/// </summary>
/// <returns>An array with the operators to use for the tokenizer.</returns>
public virtual Operator[] GetDefaultOperators() => new[]
{
new Operator {Name = "=", Precedence = 1}, new Operator {Name = "=", Precedence = 1},
new Operator {Name = "!=", Precedence = 1}, new Operator {Name = "!=", Precedence = 1},
new Operator {Name = ">", Precedence = 2}, new Operator {Name = ">", Precedence = 2},
@@ -151,309 +147,304 @@
new Operator {Name = "/", Precedence = 4}, new Operator {Name = "/", Precedence = 4},
new Operator {Name = "\\", Precedence = 4}, new Operator {Name = "\\", Precedence = 4},
new Operator {Name = "^", Precedence = 4}, new Operator {Name = "^", Precedence = 4},
}; };
/// <summary>
/// Shunting the yard.
/// </summary>
/// <param name="includeFunctionStopper">if set to <c>true</c> [include function stopper] (Token type <c>Wall</c>).</param>
/// <returns>
/// Enumerable of the token in in.
/// </returns>
/// <exception cref="InvalidOperationException">
/// Wrong token
/// or
/// Mismatched parenthesis.
/// </exception>
public virtual IEnumerable<Token> ShuntingYard(bool includeFunctionStopper = true)
{
var stack = new Stack<Token>();
foreach (var tok in Tokens)
{
switch (tok.Type)
{
case TokenType.Number:
case TokenType.Variable:
case TokenType.String:
yield return tok;
break;
case TokenType.Function:
stack.Push(tok);
break;
case TokenType.Operator:
while (stack.Any() && stack.Peek().Type == TokenType.Operator &&
CompareOperators(tok.Value, stack.Peek().Value))
yield return stack.Pop();
stack.Push(tok);
break;
case TokenType.Comma:
while (stack.Any() && (stack.Peek().Type != TokenType.Comma &&
stack.Peek().Type != TokenType.Parenthesis))
yield return stack.Pop();
break;
case TokenType.Parenthesis:
if (tok.Value == OpenFuncStr)
{
if (stack.Any() && stack.Peek().Type == TokenType.Function)
{
if (includeFunctionStopper)
yield return new Token(TokenType.Wall, tok.Value);
}
stack.Push(tok);
}
else
{
while (stack.Peek().Value != OpenFuncStr)
yield return stack.Pop();
stack.Pop();
if (stack.Any() && stack.Peek().Type == TokenType.Function)
{
yield return stack.Pop();
}
}
break;
default:
throw new InvalidOperationException("Wrong token");
}
}
while (stack.Any())
{
var tok = stack.Pop();
if (tok.Type == TokenType.Parenthesis)
throw new InvalidOperationException("Mismatched parenthesis");
yield return tok;
}
}
private static bool CompareOperators(Operator op1, Operator op2) => op1.RightAssociative
? op1.Precedence < op2.Precedence
: op1.Precedence <= op2.Precedence;
private void Tokenize(string input)
{
if (!ValidateInput(input, out var startIndex))
{
return;
}
for (var i = startIndex; i < input.Length; i++)
{
if (char.IsWhiteSpace(input, i)) continue;
if (input[i] == CommaChar)
{
Tokens.Add(new Token(TokenType.Comma, new string(new[] { input[i] })));
continue;
}
if (input[i] == StringQuotedChar)
{
i = ExtractString(input, i);
continue;
}
if (char.IsLetter(input, i) || EvaluateFunctionOrMember(input, i))
{
i = ExtractFunctionOrMember(input, i);
continue;
}
if (char.IsNumber(input, i) || (
input[i] == NegativeChar &&
((Tokens.Any() && Tokens.Last().Type != TokenType.Number) || !Tokens.Any())))
{
i = ExtractNumber(input, i);
continue;
}
if (input[i] == OpenFuncChar ||
input[i] == CloseFuncChar)
{
Tokens.Add(new Token(TokenType.Parenthesis, new string(new[] { input[i] })));
continue;
}
i = ExtractOperator(input, i);
}
}
private int ExtractData(
string input,
int i,
Func<string, TokenType> tokenTypeEvaluation,
Func<char, bool> evaluation,
int right = 0,
int left = -1)
{
var charCount = 0;
for (var j = i + right; j < input.Length; j++)
{
if (evaluation(input[j]))
break;
charCount++;
}
// Extract and set the value
var value = input.SliceLength(i + right, charCount);
Tokens.Add(new Token(tokenTypeEvaluation(value), value));
i += charCount + left;
return i;
}
private int ExtractOperator(string input, int i) =>
ExtractData(input, i, x => TokenType.Operator, x => x == OpenFuncChar ||
x == CommaChar ||
x == PeriodChar ||
x == StringQuotedChar ||
char.IsWhiteSpace(x) ||
char.IsNumber(x));
private int ExtractFunctionOrMember(string input, int i) =>
ExtractData(input, i, ResolveFunctionOrMemberType, x => x == OpenFuncChar ||
x == CloseFuncChar ||
x == CommaChar ||
char.IsWhiteSpace(x));
private int ExtractNumber(string input, int i) =>
ExtractData(input, i, x => TokenType.Number,
x => !char.IsNumber(x) && x != PeriodChar && x != NegativeChar);
private int ExtractString(string input, int i)
{
var length = ExtractData(input, i, x => TokenType.String, x => x == StringQuotedChar, 1, 1);
// open string, report issue
if (length == input.Length && input[length - 1] != StringQuotedChar)
throw new FormatException($"Parser error (Position {i}): Expected '\"' but got '{input[length - 1]}'.");
return length;
}
private bool CompareOperators(string op1, string op2)
=> CompareOperators(GetOperatorOrDefault(op1), GetOperatorOrDefault(op2));
private Operator GetOperatorOrDefault(string op)
=> _operators.FirstOrDefault(x => x.Name == op) ?? new Operator { Name = op, Precedence = 0 };
}
/// <summary> /// <summary>
/// Represents an operator with precedence. /// Shunting the yard.
/// </summary> /// </summary>
public class Operator /// <param name="includeFunctionStopper">if set to <c>true</c> [include function stopper] (Token type <c>Wall</c>).</param>
{ /// <returns>
/// <summary> /// Enumerable of the token in in.
/// Gets or sets the name. /// </returns>
/// </summary> /// <exception cref="InvalidOperationException">
/// <value> /// Wrong token
/// The name. /// or
/// </value> /// Mismatched parenthesis.
public string Name { get; set; } /// </exception>
public virtual IEnumerable<Token> ShuntingYard(Boolean includeFunctionStopper = true) {
/// <summary> Stack<Token> stack = new Stack<Token>();
/// Gets or sets the precedence.
/// </summary> foreach(Token tok in this.Tokens) {
/// <value> switch(tok.Type) {
/// The precedence. case TokenType.Number:
/// </value> case TokenType.Variable:
public int Precedence { get; set; } case TokenType.String:
yield return tok;
/// <summary> break;
/// Gets or sets a value indicating whether [right associative]. case TokenType.Function:
/// </summary> stack.Push(tok);
/// <value> break;
/// <c>true</c> if [right associative]; otherwise, <c>false</c>. case TokenType.Operator:
/// </value> while(stack.Any() && stack.Peek().Type == TokenType.Operator &&
public bool RightAssociative { get; set; } this.CompareOperators(tok.Value, stack.Peek().Value)) {
} yield return stack.Pop();
}
stack.Push(tok);
break;
case TokenType.Comma:
while(stack.Any() && stack.Peek().Type != TokenType.Comma &&
stack.Peek().Type != TokenType.Parenthesis) {
yield return stack.Pop();
}
break;
case TokenType.Parenthesis:
if(tok.Value == OpenFuncStr) {
if(stack.Any() && stack.Peek().Type == TokenType.Function) {
if(includeFunctionStopper) {
yield return new Token(TokenType.Wall, tok.Value);
}
}
stack.Push(tok);
} else {
while(stack.Peek().Value != OpenFuncStr) {
yield return stack.Pop();
}
_ = stack.Pop();
if(stack.Any() && stack.Peek().Type == TokenType.Function) {
yield return stack.Pop();
}
}
break;
default:
throw new InvalidOperationException("Wrong token");
}
}
while(stack.Any()) {
Token tok = stack.Pop();
if(tok.Type == TokenType.Parenthesis) {
throw new InvalidOperationException("Mismatched parenthesis");
}
yield return tok;
}
}
private static Boolean CompareOperators(Operator op1, Operator op2) => op1.RightAssociative
? op1.Precedence < op2.Precedence
: op1.Precedence <= op2.Precedence;
private void Tokenize(String input) {
if(!this.ValidateInput(input, out Int32 startIndex)) {
return;
}
for(Int32 i = startIndex; i < input.Length; i++) {
if(Char.IsWhiteSpace(input, i)) {
continue;
}
if(input[i] == CommaChar) {
this.Tokens.Add(new Token(TokenType.Comma, new String(new[] { input[i] })));
continue;
}
if(input[i] == StringQuotedChar) {
i = this.ExtractString(input, i);
continue;
}
if(Char.IsLetter(input, i) || this.EvaluateFunctionOrMember(input, i)) {
i = this.ExtractFunctionOrMember(input, i);
continue;
}
if(Char.IsNumber(input, i) ||
input[i] == NegativeChar &&
(this.Tokens.Any() && this.Tokens.Last().Type != TokenType.Number || !this.Tokens.Any())) {
i = this.ExtractNumber(input, i);
continue;
}
if(input[i] == OpenFuncChar ||
input[i] == CloseFuncChar) {
this.Tokens.Add(new Token(TokenType.Parenthesis, new String(new[] { input[i] })));
continue;
}
i = this.ExtractOperator(input, i);
}
}
private Int32 ExtractData(
String input,
Int32 i,
Func<String, TokenType> tokenTypeEvaluation,
Func<Char, Boolean> evaluation,
Int32 right = 0,
Int32 left = -1) {
Int32 charCount = 0;
for(Int32 j = i + right; j < input.Length; j++) {
if(evaluation(input[j])) {
break;
}
charCount++;
}
// Extract and set the value
String value = input.SliceLength(i + right, charCount);
this.Tokens.Add(new Token(tokenTypeEvaluation(value), value));
i += charCount + left;
return i;
}
private Int32 ExtractOperator(String input, Int32 i) =>
this.ExtractData(input, i, x => TokenType.Operator, x => x == OpenFuncChar ||
x == CommaChar ||
x == PeriodChar ||
x == StringQuotedChar ||
Char.IsWhiteSpace(x) ||
Char.IsNumber(x));
private Int32 ExtractFunctionOrMember(String input, Int32 i) =>
this.ExtractData(input, i, this.ResolveFunctionOrMemberType, x => x == OpenFuncChar ||
x == CloseFuncChar ||
x == CommaChar ||
Char.IsWhiteSpace(x));
private Int32 ExtractNumber(String input, Int32 i) =>
this.ExtractData(input, i, x => TokenType.Number,
x => !Char.IsNumber(x) && x != PeriodChar && x != NegativeChar);
private Int32 ExtractString(String input, Int32 i) {
Int32 length = this.ExtractData(input, i, x => TokenType.String, x => x == StringQuotedChar, 1, 1);
// open string, report issue
if(length == input.Length && input[length - 1] != StringQuotedChar) {
throw new FormatException($"Parser error (Position {i}): Expected '\"' but got '{input[length - 1]}'.");
}
return length;
}
private Boolean CompareOperators(String op1, String op2)
=> CompareOperators(this.GetOperatorOrDefault(op1), this.GetOperatorOrDefault(op2));
private Operator GetOperatorOrDefault(String op)
=> this._operators.FirstOrDefault(x => x.Name == op) ?? new Operator { Name = op, Precedence = 0 };
}
/// <summary>
/// Represents an operator with precedence.
/// </summary>
public class Operator {
/// <summary> /// <summary>
/// Represents a Token structure. /// Gets or sets the name.
/// </summary> /// </summary>
public struct Token /// <value>
{ /// The name.
/// <summary> /// </value>
/// Initializes a new instance of the <see cref="Token"/> struct. public String Name {
/// </summary> get; set;
/// <param name="type">The type.</param> }
/// <param name="value">The value.</param>
public Token(TokenType type, string value)
{
Type = type;
Value = type == TokenType.Function || type == TokenType.Operator ? value.ToLowerInvariant() : value;
}
/// <summary>
/// Gets or sets the type.
/// </summary>
/// <value>
/// The type.
/// </value>
public TokenType Type { get; set; }
/// <summary>
/// Gets the value.
/// </summary>
/// <value>
/// The value.
/// </value>
public string Value { get; }
}
/// <summary> /// <summary>
/// Enums the token types. /// Gets or sets the precedence.
/// </summary> /// </summary>
public enum TokenType /// <value>
{ /// The precedence.
/// <summary> /// </value>
/// The number public Int32 Precedence {
/// </summary> get; set;
Number, }
/// <summary> /// <summary>
/// The string /// Gets or sets a value indicating whether [right associative].
/// </summary> /// </summary>
String, /// <value>
/// <c>true</c> if [right associative]; otherwise, <c>false</c>.
/// <summary> /// </value>
/// The variable public Boolean RightAssociative {
/// </summary> get; set;
Variable, }
}
/// <summary>
/// The function /// <summary>
/// </summary> /// Represents a Token structure.
Function, /// </summary>
public struct Token {
/// <summary> /// <summary>
/// The parenthesis /// Initializes a new instance of the <see cref="Token"/> struct.
/// </summary> /// </summary>
Parenthesis, /// <param name="type">The type.</param>
/// <param name="value">The value.</param>
/// <summary> public Token(TokenType type, String value) {
/// The operator this.Type = type;
/// </summary> this.Value = type == TokenType.Function || type == TokenType.Operator ? value.ToLowerInvariant() : value;
Operator, }
/// <summary> /// <summary>
/// The comma /// Gets or sets the type.
/// </summary> /// </summary>
Comma, /// <value>
/// The type.
/// <summary> /// </value>
/// The wall, used to specified the end of argument list of the following function public TokenType Type {
/// </summary> get; set;
Wall, }
}
/// <summary>
/// Gets the value.
/// </summary>
/// <value>
/// The value.
/// </value>
public String Value {
get;
}
}
/// <summary>
/// Enums the token types.
/// </summary>
public enum TokenType {
/// <summary>
/// The number
/// </summary>
Number,
/// <summary>
/// The string
/// </summary>
String,
/// <summary>
/// The variable
/// </summary>
Variable,
/// <summary>
/// The function
/// </summary>
Function,
/// <summary>
/// The parenthesis
/// </summary>
Parenthesis,
/// <summary>
/// The operator
/// </summary>
Operator,
/// <summary>
/// The comma
/// </summary>
Comma,
/// <summary>
/// The wall, used to specified the end of argument list of the following function
/// </summary>
Wall,
}
} }
+120 -124
View File
@@ -1,127 +1,123 @@
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>
/// A base class for implementing models that fire notifications when their properties change.
/// This class is ideal for implementing MVVM driven UIs.
/// </summary>
/// <seealso cref="INotifyPropertyChanged" />
public abstract class ViewModelBase : INotifyPropertyChanged {
private readonly ConcurrentDictionary<String, Boolean> QueuedNotifications = new ConcurrentDictionary<String, Boolean>();
private readonly Boolean UseDeferredNotifications;
/// <summary> /// <summary>
/// A base class for implementing models that fire notifications when their properties change. /// Initializes a new instance of the <see cref="ViewModelBase"/> class.
/// This class is ideal for implementing MVVM driven UIs.
/// </summary> /// </summary>
/// <seealso cref="INotifyPropertyChanged" /> /// <param name="useDeferredNotifications">Set to <c>true</c> to use deferred notifications in the background.</param>
public abstract class ViewModelBase : INotifyPropertyChanged protected ViewModelBase(Boolean useDeferredNotifications) => this.UseDeferredNotifications = useDeferredNotifications;
{
private readonly ConcurrentDictionary<string, bool> QueuedNotifications = new ConcurrentDictionary<string, bool>(); /// <summary>
private readonly bool UseDeferredNotifications; /// Initializes a new instance of the <see cref="ViewModelBase"/> class.
/// </summary>
/// <summary> protected ViewModelBase()
/// Initializes a new instance of the <see cref="ViewModelBase"/> class. : this(false) {
/// </summary> // placeholder
/// <param name="useDeferredNotifications">Set to <c>true</c> to use deferred notifications in the background.</param> }
protected ViewModelBase(bool useDeferredNotifications)
{ /// <summary>
UseDeferredNotifications = useDeferredNotifications; /// Occurs when a property value changes.
} /// </summary>
/// <returns></returns>
/// <summary> public event PropertyChangedEventHandler PropertyChanged;
/// Initializes a new instance of the <see cref="ViewModelBase"/> class.
/// </summary> /// <summary>Checks if a property already matches a desired value. Sets the property and
protected ViewModelBase() /// notifies listeners only when necessary.</summary>
: this(false) /// <typeparam name="T">Type of the property.</typeparam>
{ /// <param name="storage">Reference to a property with both getter and setter.</param>
// placeholder /// <param name="value">Desired value for the property.</param>
} /// <param name="propertyName">Name of the property used to notify listeners. This
/// value is optional and can be provided automatically when invoked from compilers that
/// <summary> /// support CallerMemberName.</param>
/// Occurs when a property value changes. /// <param name="notifyAlso">An rray of property names to notify in addition to notifying the changes on the current property name.</param>
/// </summary> /// <returns>True if the value was changed, false if the existing value matched the
/// <returns></returns> /// desired value.</returns>
public event PropertyChangedEventHandler PropertyChanged; protected Boolean SetProperty<T>(ref T storage, T value, [CallerMemberName] String propertyName = "", String[] notifyAlso = null) {
if(EqualityComparer<T>.Default.Equals(storage, value)) {
/// <summary>Checks if a property already matches a desired value. Sets the property and return false;
/// notifies listeners only when necessary.</summary> }
/// <typeparam name="T">Type of the property.</typeparam>
/// <param name="storage">Reference to a property with both getter and setter.</param> storage = value;
/// <param name="value">Desired value for the property.</param> this.NotifyPropertyChanged(propertyName, notifyAlso);
/// <param name="propertyName">Name of the property used to notify listeners. This return true;
/// value is optional and can be provided automatically when invoked from compilers that }
/// support CallerMemberName.</param>
/// <param name="notifyAlso">An rray of property names to notify in addition to notifying the changes on the current property name.</param> /// <summary>
/// <returns>True if the value was changed, false if the existing value matched the /// Notifies one or more properties changed.
/// desired value.</returns> /// </summary>
protected bool SetProperty<T>(ref T storage, T value, [CallerMemberName] string propertyName = "", string[] notifyAlso = null) /// <param name="propertyNames">The property names.</param>
{ protected void NotifyPropertyChanged(params String[] propertyNames) => this.NotifyPropertyChanged(null, propertyNames);
if (EqualityComparer<T>.Default.Equals(storage, value))
return false; /// <summary>
/// Notifies one or more properties changed.
storage = value; /// </summary>
NotifyPropertyChanged(propertyName, notifyAlso); /// <param name="mainProperty">The main property.</param>
return true; /// <param name="auxiliaryProperties">The auxiliary properties.</param>
} private void NotifyPropertyChanged(String mainProperty, String[] auxiliaryProperties) {
// Queue property notification
/// <summary> if(String.IsNullOrWhiteSpace(mainProperty) == false) {
/// Notifies one or more properties changed. this.QueuedNotifications[mainProperty] = true;
/// </summary> }
/// <param name="propertyNames">The property names.</param>
protected void NotifyPropertyChanged(params string[] propertyNames) => NotifyPropertyChanged(null, propertyNames); // Set the state for notification properties
if(auxiliaryProperties != null) {
/// <summary> foreach(String property in auxiliaryProperties) {
/// Notifies one or more properties changed. if(String.IsNullOrWhiteSpace(property) == false) {
/// </summary> this.QueuedNotifications[property] = true;
/// <param name="mainProperty">The main property.</param> }
/// <param name="auxiliaryProperties">The auxiliary properties.</param> }
private void NotifyPropertyChanged(string mainProperty, string[] auxiliaryProperties) }
{
// Queue property notification // Depending on operation mode, either fire the notifications in the background
if (string.IsNullOrWhiteSpace(mainProperty) == false) // or fire them immediately
QueuedNotifications[mainProperty] = true; if(this.UseDeferredNotifications) {
_ = Task.Run(() => this.NotifyQueuedProperties());
// Set the state for notification properties } else {
if (auxiliaryProperties != null) this.NotifyQueuedProperties();
{ }
foreach (var property in auxiliaryProperties) }
{
if (string.IsNullOrWhiteSpace(property) == false) /// <summary>
QueuedNotifications[property] = true; /// Notifies the queued properties and resets the property name to a non-queued stated.
} /// </summary>
} private void NotifyQueuedProperties() {
// get a snapshot of property names.
// Depending on operation mode, either fire the notifications in the background String[] propertyNames = this.QueuedNotifications.Keys.ToArray();
// or fire them immediately
if (UseDeferredNotifications) // Iterate through the properties
Task.Run(() => NotifyQueuedProperties()); foreach(String property in propertyNames) {
else // don't notify if we don't have a change
NotifyQueuedProperties(); if(!this.QueuedNotifications[property]) {
} continue;
}
/// <summary>
/// Notifies the queued properties and resets the property name to a non-queued stated. // notify and reset queued state to false
/// </summary> try {
private void NotifyQueuedProperties() this.OnPropertyChanged(property);
{ } finally { this.QueuedNotifications[property] = false; }
// get a snapshot of property names. }
var propertyNames = QueuedNotifications.Keys.ToArray(); }
// Iterate through the properties /// <summary>
foreach (var property in propertyNames) /// Called when a property changes its backing value.
{ /// </summary>
// don't notify if we don't have a change /// <param name="propertyName">Name of the property.</param>
if (!QueuedNotifications[property]) continue; private void OnPropertyChanged(String propertyName) =>
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName ?? String.Empty));
// notify and reset queued state to false }
try { OnPropertyChanged(property); }
finally { QueuedNotifications[property] = false; }
}
}
/// <summary>
/// Called when a property changes its backing value.
/// </summary>
/// <param name="propertyName">Name of the property.</param>
private void OnPropertyChanged(string propertyName) =>
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName ?? string.Empty));
}
} }
+21 -23
View File
@@ -1,26 +1,24 @@
namespace Unosquare.Swan using System;
{ using Unosquare.Swan.Abstractions;
using Abstractions;
namespace Unosquare.Swan {
/// <summary>
/// Fast, atomic boolean combining interlocked to write value and volatile to read values.
/// </summary>
public sealed class AtomicBoolean : AtomicTypeBase<Boolean> {
/// <summary> /// <summary>
/// Fast, atomic boolean combining interlocked to write value and volatile to read values. /// Initializes a new instance of the <see cref="AtomicBoolean"/> class.
/// </summary> /// </summary>
public sealed class AtomicBoolean : AtomicTypeBase<bool> /// <param name="initialValue">if set to <c>true</c> [initial value].</param>
{ public AtomicBoolean(Boolean initialValue = default)
/// <summary> : base(initialValue ? 1 : 0) {
/// Initializes a new instance of the <see cref="AtomicBoolean"/> class. // placeholder
/// </summary> }
/// <param name="initialValue">if set to <c>true</c> [initial value].</param>
public AtomicBoolean(bool initialValue = default) /// <inheritdoc/>
: base(initialValue ? 1 : 0) protected override Boolean FromLong(Int64 backingValue) => backingValue != 0;
{
// placeholder /// <inheritdoc/>
} protected override Int64 ToLong(Boolean value) => value ? 1 : 0;
}
/// <inheritdoc/>
protected override bool FromLong(long backingValue) => backingValue != 0;
/// <inheritdoc/>
protected override long ToLong(bool value) => value ? 1 : 0;
}
} }
+23 -26
View File
@@ -1,29 +1,26 @@
namespace Unosquare.Swan using System;
{ using Unosquare.Swan.Abstractions;
using System;
using Abstractions; namespace Unosquare.Swan {
/// <summary>
/// Fast, atomic double combining interlocked to write value and volatile to read values.
/// </summary>
public sealed class AtomicDouble : AtomicTypeBase<Double> {
/// <summary> /// <summary>
/// Fast, atomic double combining interlocked to write value and volatile to read values. /// Initializes a new instance of the <see cref="AtomicDouble"/> class.
/// </summary> /// </summary>
public sealed class AtomicDouble : AtomicTypeBase<double> /// <param name="initialValue">if set to <c>true</c> [initial value].</param>
{ public AtomicDouble(Double initialValue = default)
/// <summary> : base(BitConverter.DoubleToInt64Bits(initialValue)) {
/// Initializes a new instance of the <see cref="AtomicDouble"/> class. // placeholder
/// </summary> }
/// <param name="initialValue">if set to <c>true</c> [initial value].</param>
public AtomicDouble(double initialValue = default) /// <inheritdoc/>
: base(BitConverter.DoubleToInt64Bits(initialValue)) protected override Double FromLong(Int64 backingValue) =>
{ BitConverter.Int64BitsToDouble(backingValue);
// placeholder
} /// <inheritdoc/>
protected override Int64 ToLong(Double value) =>
/// <inheritdoc/> BitConverter.DoubleToInt64Bits(value);
protected override double FromLong(long backingValue) => }
BitConverter.Int64BitsToDouble(backingValue);
/// <inheritdoc/>
protected override long ToLong(double value) =>
BitConverter.DoubleToInt64Bits(value);
}
} }
+23 -26
View File
@@ -1,29 +1,26 @@
namespace Unosquare.Swan using System;
{ using Unosquare.Swan.Abstractions;
using System;
using Abstractions; namespace Unosquare.Swan {
/// <summary>
/// Represents an atomically readable or writable integer.
/// </summary>
public class AtomicInteger : AtomicTypeBase<Int32> {
/// <summary> /// <summary>
/// Represents an atomically readable or writable integer. /// Initializes a new instance of the <see cref="AtomicInteger"/> class.
/// </summary> /// </summary>
public class AtomicInteger : AtomicTypeBase<int> /// <param name="initialValue">if set to <c>true</c> [initial value].</param>
{ public AtomicInteger(Int32 initialValue = default)
/// <summary> : base(Convert.ToInt64(initialValue)) {
/// Initializes a new instance of the <see cref="AtomicInteger"/> class. // placeholder
/// </summary> }
/// <param name="initialValue">if set to <c>true</c> [initial value].</param>
public AtomicInteger(int initialValue = default) /// <inheritdoc/>
: base(Convert.ToInt64(initialValue)) protected override Int32 FromLong(Int64 backingValue) =>
{ Convert.ToInt32(backingValue);
// placeholder
} /// <inheritdoc/>
protected override Int64 ToLong(Int32 value) =>
/// <inheritdoc/> Convert.ToInt64(value);
protected override int FromLong(long backingValue) => }
Convert.ToInt32(backingValue);
/// <inheritdoc/>
protected override long ToLong(int value) =>
Convert.ToInt64(value);
}
} }
+21 -23
View File
@@ -1,26 +1,24 @@
namespace Unosquare.Swan using System;
{ using Unosquare.Swan.Abstractions;
using Abstractions;
namespace Unosquare.Swan {
/// <summary>
/// Fast, atomioc long combining interlocked to write value and volatile to read values.
/// </summary>
public sealed class AtomicLong : AtomicTypeBase<Int64> {
/// <summary> /// <summary>
/// Fast, atomioc long combining interlocked to write value and volatile to read values. /// Initializes a new instance of the <see cref="AtomicLong"/> class.
/// </summary> /// </summary>
public sealed class AtomicLong : AtomicTypeBase<long> /// <param name="initialValue">if set to <c>true</c> [initial value].</param>
{ public AtomicLong(Int64 initialValue = default)
/// <summary> : base(initialValue) {
/// Initializes a new instance of the <see cref="AtomicLong"/> class. // placeholder
/// </summary> }
/// <param name="initialValue">if set to <c>true</c> [initial value].</param>
public AtomicLong(long initialValue = default) /// <inheritdoc />
: base(initialValue) protected override Int64 FromLong(Int64 backingValue) => backingValue;
{
// placeholder /// <inheritdoc />
} protected override Int64 ToLong(Int64 value) => value;
}
/// <inheritdoc />
protected override long FromLong(long backingValue) => backingValue;
/// <inheritdoc />
protected override long ToLong(long value) => value;
}
} }
@@ -1,102 +1,105 @@
namespace Unosquare.Swan.Attributes using System;
{
using System; namespace Unosquare.Swan.Attributes {
/// <summary>
/// Models an option specification.
/// Based on CommandLine (Copyright 2005-2015 Giacomo Stelluti Scala and Contributors.).
/// </summary>
[AttributeUsage(AttributeTargets.Property)]
public sealed class ArgumentOptionAttribute
: Attribute {
/// <summary> /// <summary>
/// Models an option specification. /// Initializes a new instance of the <see cref="ArgumentOptionAttribute"/> class.
/// Based on CommandLine (Copyright 2005-2015 Giacomo Stelluti Scala and Contributors.). /// The default long name will be inferred from target property.
/// </summary> /// </summary>
[AttributeUsage(AttributeTargets.Property)] public ArgumentOptionAttribute()
public sealed class ArgumentOptionAttribute : this(String.Empty, String.Empty) {
: 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. /// </summary>
/// </summary> /// <param name="longName">The long name of the option.</param>
public ArgumentOptionAttribute() public ArgumentOptionAttribute(String longName)
: this(string.Empty, string.Empty) : this(String.Empty, 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="longName">The long name of the option.</param> /// <param name="longName">The long name of the option or null if not used.</param>
public ArgumentOptionAttribute(string longName) public ArgumentOptionAttribute(Char shortName, String longName)
: this(string.Empty, 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)
/// <param name="longName">The long name of the option or null if not used.</param> : this(new String(shortName, 1), String.Empty) {
public ArgumentOptionAttribute(char shortName, string longName) }
: this(new string(shortName, 1), longName)
{ private ArgumentOptionAttribute(String shortName, String longName) {
} this.ShortName = shortName ?? throw new ArgumentNullException(nameof(shortName));
this.LongName = longName ?? throw new ArgumentNullException(nameof(longName));
/// <summary> }
/// Initializes a new instance of the <see cref="ArgumentOptionAttribute"/> class.
/// </summary> /// <summary>
/// <param name="shortName">The short name of the option..</param> /// Gets long name of this command line option. This name is usually a single English word.
public ArgumentOptionAttribute(char shortName) /// </summary>
: this(new string(shortName, 1), string.Empty) /// <value>
{ /// The long name.
} /// </value>
public String LongName {
private ArgumentOptionAttribute(string shortName, string longName) get;
{ }
ShortName = shortName ?? throw new ArgumentNullException(nameof(shortName));
LongName = longName ?? throw new ArgumentNullException(nameof(longName)); /// <summary>
} /// Gets a short name of this command line option, made of one character.
/// </summary>
/// <summary> /// <value>
/// Gets long name of this command line option. This name is usually a single English word. /// The short name.
/// </summary> /// </value>
/// <value> public String ShortName {
/// The long name. get;
/// </value> }
public string LongName { get; }
/// <summary>
/// <summary> /// When applying attribute to <see cref="System.Collections.Generic.IEnumerable{T}"/> target properties,
/// Gets a short name of this command line option, made of one character. /// it allows you to split an argument and consume its content as a sequence.
/// </summary> /// </summary>
/// <value> public Char Separator { get; set; } = '\0';
/// The short name.
/// </value> /// <summary>
public string ShortName { get; } /// Gets or sets mapped property default value.
/// </summary>
/// <summary> /// <value>
/// When applying attribute to <see cref="System.Collections.Generic.IEnumerable{T}"/> target properties, /// The default value.
/// it allows you to split an argument and consume its content as a sequence. /// </value>
/// </summary> public Object DefaultValue {
public char Separator { get; set; } = '\0'; get; set;
}
/// <summary>
/// Gets or sets mapped property default value. /// <summary>
/// </summary> /// Gets or sets a value indicating whether a command line option is required.
/// <value> /// </summary>
/// The default value. /// <value>
/// </value> /// <c>true</c> if required; otherwise, <c>false</c>.
public object DefaultValue { get; set; } /// </value>
public Boolean Required {
/// <summary> get; set;
/// Gets or sets a value indicating whether a command line option is required. }
/// </summary>
/// <value> /// <summary>
/// <c>true</c> if required; otherwise, <c>false</c>. /// Gets or sets a short description of this command line option. Usually a sentence summary.
/// </value> /// </summary>
public bool Required { get; set; } /// <value>
/// The help text.
/// <summary> /// </value>
/// Gets or sets a short description of this command line option. Usually a sentence summary. public String HelpText {
/// </summary> get; set;
/// <value> }
/// The help text. }
/// </value>
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,39 +1,40 @@
namespace Unosquare.Swan.Attributes using System;
{
using System; namespace Unosquare.Swan.Attributes {
/// <summary>
/// An attribute used to help setup a property behavior when serialize/deserialize JSON.
/// </summary>
/// <seealso cref="Attribute" />
[AttributeUsage(AttributeTargets.Property)]
public sealed class JsonPropertyAttribute : Attribute {
/// <summary> /// <summary>
/// An attribute used to help setup a property behavior when serialize/deserialize JSON. /// Initializes a new instance of the <see cref="JsonPropertyAttribute" /> class.
/// </summary> /// </summary>
/// <seealso cref="Attribute" /> /// <param name="propertyName">Name of the property.</param>
[AttributeUsage(AttributeTargets.Property)] /// <param name="ignored">if set to <c>true</c> [ignored].</param>
public sealed class JsonPropertyAttribute : Attribute public JsonPropertyAttribute(String propertyName, Boolean ignored = false) {
{ this.PropertyName = propertyName ?? throw new ArgumentNullException(nameof(propertyName));
/// <summary> this.Ignored = ignored;
/// Initializes a new instance of the <see cref="JsonPropertyAttribute" /> class. }
/// </summary>
/// <param name="propertyName">Name of the property.</param> /// <summary>
/// <param name="ignored">if set to <c>true</c> [ignored].</param> /// Gets or sets the name of the property.
public JsonPropertyAttribute(string propertyName, bool ignored = false) /// </summary>
{ /// <value>
PropertyName = propertyName ?? throw new ArgumentNullException(nameof(propertyName)); /// The name of the property.
Ignored = ignored; /// </value>
} public String PropertyName {
get;
/// <summary> }
/// Gets or sets the name of the property.
/// </summary> /// <summary>
/// <value> /// Gets or sets a value indicating whether this <see cref="JsonPropertyAttribute" /> is ignored.
/// The name of the property. /// </summary>
/// </value> /// <value>
public string PropertyName { get; } /// <c>true</c> if ignored; otherwise, <c>false</c>.
/// </value>
/// <summary> public Boolean Ignored {
/// Gets or sets a value indicating whether this <see cref="JsonPropertyAttribute" /> is ignored. get;
/// </summary> }
/// <value> }
/// <c>true</c> if ignored; otherwise, <c>false</c>.
/// </value>
public bool Ignored { get; }
}
} }
@@ -1,54 +1,62 @@
namespace Unosquare.Swan.Attributes using System;
{
using System; namespace Unosquare.Swan.Attributes {
/// <summary>
/// An attribute used to include additional information to a Property for serialization.
///
/// Previously we used DisplayAttribute from DataAnnotation.
/// </summary>
/// <seealso cref="System.Attribute" />
[AttributeUsage(AttributeTargets.Property)]
public sealed class PropertyDisplayAttribute : Attribute {
/// <summary> /// <summary>
/// An attribute used to include additional information to a Property for serialization. /// Gets or sets the name.
///
/// Previously we used DisplayAttribute from DataAnnotation.
/// </summary> /// </summary>
/// <seealso cref="System.Attribute" /> /// <value>
[AttributeUsage(AttributeTargets.Property)] /// The name.
public sealed class PropertyDisplayAttribute : Attribute /// </value>
{ public String Name {
/// <summary> get; set;
/// Gets or sets the name. }
/// </summary>
/// <value> /// <summary>
/// The name. /// Gets or sets the description.
/// </value> /// </summary>
public string Name { get; set; } /// <value>
/// The description.
/// <summary> /// </value>
/// Gets or sets the description. public String Description {
/// </summary> get; set;
/// <value> }
/// The description.
/// </value> /// <summary>
public string Description { get; set; } /// Gets or sets the name of the group.
/// </summary>
/// <summary> /// <value>
/// Gets or sets the name of the group. /// The name of the group.
/// </summary> /// </value>
/// <value> public String GroupName {
/// The name of the group. get; set;
/// </value> }
public string GroupName { get; set; }
/// <summary>
/// <summary> /// Gets or sets the default value.
/// Gets or sets the default value. /// </summary>
/// </summary> /// <value>
/// <value> /// The default value.
/// The default value. /// </value>
/// </value> public Object DefaultValue {
public object DefaultValue { get; set; } get; set;
}
/// <summary>
/// Gets or sets the format string to call with method <c>ToString</c>. /// <summary>
/// </summary> /// Gets or sets the format string to call with method <c>ToString</c>.
/// <value> /// </summary>
/// The format. /// <value>
/// </value> /// The format.
public string Format { get; set; } /// </value>
} public String Format {
get; set;
}
}
} }
@@ -1,30 +1,27 @@
namespace Unosquare.Swan.Attributes using System;
{
using System; namespace Unosquare.Swan.Attributes {
/// <summary>
/// An attribute used to help conversion structs back and forth into arrays of bytes via
/// extension methods included in this library ToStruct and ToBytes.
/// </summary>
/// <seealso cref="Attribute" />
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Struct)]
public class StructEndiannessAttribute : Attribute {
/// <summary> /// <summary>
/// An attribute used to help conversion structs back and forth into arrays of bytes via /// Initializes a new instance of the <see cref="StructEndiannessAttribute"/> class.
/// extension methods included in this library ToStruct and ToBytes.
/// </summary> /// </summary>
/// <seealso cref="Attribute" /> /// <param name="endianness">The endianness.</param>
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Struct)] public StructEndiannessAttribute(Endianness endianness) => this.Endianness = endianness;
public class StructEndiannessAttribute : Attribute
{ /// <summary>
/// <summary> /// Gets the endianness.
/// Initializes a new instance of the <see cref="StructEndiannessAttribute"/> class. /// </summary>
/// </summary> /// <value>
/// <param name="endianness">The endianness.</param> /// The endianness.
public StructEndiannessAttribute(Endianness endianness) /// </value>
{ public Endianness Endianness {
Endianness = endianness; get;
} }
}
/// <summary>
/// Gets the endianness.
/// </summary>
/// <value>
/// The endianness.
/// </value>
public Endianness Endianness { get; }
}
} }
+120 -123
View File
@@ -1,133 +1,130 @@
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>
/// Regex validator.
/// </summary>
[AttributeUsage(AttributeTargets.Property)]
public class MatchAttribute : Attribute, IValidator {
/// <summary> /// <summary>
/// Regex validator. /// Initializes a new instance of the <see cref="MatchAttribute" /> class.
/// </summary> /// </summary>
[AttributeUsage(AttributeTargets.Property)] /// <param name="regex">A regex string.</param>
public class MatchAttribute : Attribute, IValidator /// <param name="errorMessage">The error message.</param>
{ /// <exception cref="ArgumentNullException">Expression.</exception>
/// <summary> public MatchAttribute(String regex, String errorMessage = null) {
/// Initializes a new instance of the <see cref="MatchAttribute" /> class. this.Expression = regex ?? throw new ArgumentNullException(nameof(this.Expression));
/// </summary> this.ErrorMessage = errorMessage ?? "String does not match the specified regular expression";
/// <param name="regex">A regex string.</param> }
/// <param name="errorMessage">The error message.</param>
/// <exception cref="ArgumentNullException">Expression.</exception> /// <summary>
public MatchAttribute(string regex, string errorMessage = null) /// The string regex used to find a match.
{ /// </summary>
Expression = regex ?? throw new ArgumentNullException(nameof(Expression)); public String Expression {
ErrorMessage = errorMessage ?? "String does not match the specified regular expression"; get;
} }
/// <summary> /// <inheritdoc/>
/// The string regex used to find a match. public String ErrorMessage {
/// </summary> get; internal set;
public string Expression { get; } }
/// <inheritdoc/> /// <inheritdoc/>
public string ErrorMessage { get; internal set; } public Boolean IsValid<T>(T value) => Equals(value, default(T))
? false
/// <inheritdoc/> : !(value is String)
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") ? throw new ArgumentException("Property is not a string")
: Regex.IsMatch(value.ToString(), Expression); : Regex.IsMatch(value.ToString(), this.Expression);
} }
}
/// <summary>
/// Email validator.
/// </summary>
[AttributeUsage(AttributeTargets.Property)]
public class EmailAttribute : MatchAttribute {
private const String EmailRegExp =
@"^(?("")("".+?(?<!\\)""@)|(([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]))$";
/// <summary> /// <summary>
/// Email validator. /// Initializes a new instance of the <see cref="EmailAttribute"/> class.
/// </summary> /// </summary>
[AttributeUsage(AttributeTargets.Property)] /// <param name="errorMessage">The error message.</param>
public class EmailAttribute : MatchAttribute public EmailAttribute(String errorMessage = null)
{ : base(EmailRegExp, errorMessage ?? "String is not an email") {
private const string EmailRegExp = }
@"^(?("")("".+?(?<!\\)""@)|(([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]))$";
/// <summary>
/// <summary> /// A not null validator.
/// Initializes a new instance of the <see cref="EmailAttribute"/> class. /// </summary>
/// </summary> [AttributeUsage(AttributeTargets.Property)]
/// <param name="errorMessage">The error message.</param> public class NotNullAttribute : Attribute, IValidator {
public EmailAttribute(string errorMessage = null) /// <inheritdoc/>
: base(EmailRegExp, errorMessage ?? "String is not an email") public String ErrorMessage => "Value is null";
{
} /// <inheritdoc/>
} public Boolean IsValid<T>(T value) => !Equals(default(T), value);
}
/// <summary>
/// A range constraint validator.
/// </summary>
[AttributeUsage(AttributeTargets.Property)]
public class RangeAttribute : Attribute, IValidator {
/// <summary> /// <summary>
/// A not null validator. /// Initializes a new instance of the <see cref="RangeAttribute"/> class.
/// Constructor that takes integer minimum and maximum values.
/// </summary> /// </summary>
[AttributeUsage(AttributeTargets.Property)] /// <param name="min">The minimum value.</param>
public class NotNullAttribute : Attribute, IValidator /// <param name="max">The maximum value.</param>
{ public RangeAttribute(Int32 min, Int32 max) {
/// <inheritdoc/> if(min >= max) {
public string ErrorMessage => "Value is null"; throw new InvalidOperationException("Maximum value must be greater than minimum");
}
/// <inheritdoc/>
public bool IsValid<T>(T value) => !Equals(default(T), value); this.Maximum = max;
} this.Minimum = min;
}
/// <summary> /// <summary>
/// A range constraint validator. /// Initializes a new instance of the <see cref="RangeAttribute"/> class.
/// Constructor that takes double minimum and maximum values.
/// </summary> /// </summary>
[AttributeUsage(AttributeTargets.Property)] /// <param name="min">The minimum value.</param>
public class RangeAttribute : Attribute, IValidator /// <param name="max">The maximum value.</param>
{ public RangeAttribute(Double min, Double max) {
/// <summary> if(min >= max) {
/// Initializes a new instance of the <see cref="RangeAttribute"/> class. throw new InvalidOperationException("Maximum value must be greater than minimum");
/// Constructor that takes integer minimum and maximum values. }
/// </summary>
/// <param name="min">The minimum value.</param> this.Maximum = max;
/// <param name="max">The maximum value.</param> this.Minimum = min;
public RangeAttribute(int min, int max) }
{
if (min >= max) /// <inheritdoc/>
throw new InvalidOperationException("Maximum value must be greater than minimum"); public String ErrorMessage => "Value is not within the specified range";
Maximum = max; /// <summary>
Minimum = min; /// Maximum value for the range.
} /// </summary>
public IComparable Maximum {
/// <summary> get;
/// Initializes a new instance of the <see cref="RangeAttribute"/> class. }
/// Constructor that takes double minimum and maximum values.
/// </summary> /// <summary>
/// <param name="min">The minimum value.</param> /// Minimum value for the range.
/// <param name="max">The maximum value.</param> /// </summary>
public RangeAttribute(double min, double max) public IComparable Minimum {
{ get;
if (min >= max) }
throw new InvalidOperationException("Maximum value must be greater than minimum");
/// <inheritdoc/>
Maximum = max; public Boolean IsValid<T>(T value)
Minimum = min; => value is IComparable comparable
} ? comparable.CompareTo(this.Minimum) >= 0 && comparable.CompareTo(this.Maximum) <= 0
: throw new ArgumentException(nameof(value));
/// <inheritdoc/> }
public string ErrorMessage => "Value is not within the specified range";
/// <summary>
/// Maximum value for the range.
/// </summary>
public IComparable Maximum { get; }
/// <summary>
/// Minimum value for the range.
/// </summary>
public IComparable Minimum { get; }
/// <inheritdoc/>
public bool IsValid<T>(T value)
=> value is IComparable comparable
? comparable.CompareTo(Minimum) >= 0 && comparable.CompareTo(Maximum) <= 0
: throw new ArgumentException(nameof(value));
}
} }
@@ -1,40 +1,39 @@
namespace Unosquare.Swan.Attributes using System;
{
using System; namespace Unosquare.Swan.Attributes {
/// <summary>
/// Models a verb option.
/// </summary>
[AttributeUsage(AttributeTargets.Property)]
public sealed class VerbOptionAttribute : Attribute {
/// <summary> /// <summary>
/// Models a verb option. /// Initializes a new instance of the <see cref="VerbOptionAttribute" /> class.
/// </summary> /// </summary>
[AttributeUsage(AttributeTargets.Property)] /// <param name="name">The name.</param>
public sealed class VerbOptionAttribute : Attribute /// <exception cref="ArgumentNullException">name.</exception>
{ public VerbOptionAttribute(String name) => this.Name = name ?? throw new ArgumentNullException(nameof(name));
/// <summary>
/// Initializes a new instance of the <see cref="VerbOptionAttribute" /> class. /// <summary>
/// </summary> /// Gets the name of the verb option.
/// <param name="name">The name.</param> /// </summary>
/// <exception cref="ArgumentNullException">name.</exception> /// <value>
public VerbOptionAttribute(string name) /// Name.
{ /// </value>
Name = name ?? throw new ArgumentNullException(nameof(name)); public String Name {
} get;
}
/// <summary>
/// Gets the name of the verb option. /// <summary>
/// </summary> /// Gets or sets a short description of this command line verb. Usually a sentence summary.
/// <value> /// </summary>
/// Name. /// <value>
/// </value> /// The help text.
public string Name { get; } /// </value>
public String HelpText {
/// <summary> get; set;
/// Gets or sets a short description of this command line verb. Usually a sentence summary. }
/// </summary>
/// <value> /// <inheritdoc />
/// The help text. public override String ToString() => $" {this.Name}\t\t{this.HelpText}";
/// </value> }
public string HelpText { get; set; }
/// <inheritdoc />
public override string ToString() => $" {Name}\t\t{HelpText}";
}
} }
@@ -1,159 +1,146 @@
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 readonly Object _instance;
private sealed class Validator private readonly IEnumerable<String> _args;
{ private readonly List<PropertyInfo> _updatedList = new List<PropertyInfo>();
private readonly object _instance; private readonly ArgumentParserSettings _settings;
private readonly IEnumerable<string> _args;
private readonly List<PropertyInfo> _updatedList = new List<PropertyInfo>(); private readonly PropertyInfo[] _properties;
private readonly ArgumentParserSettings _settings;
public Validator(
private readonly PropertyInfo[] _properties; PropertyInfo[] properties,
IEnumerable<String> args,
public Validator( Object instance,
PropertyInfo[] properties, ArgumentParserSettings settings) {
IEnumerable<string> args, this._args = args;
object instance, this._instance = instance;
ArgumentParserSettings settings) this._settings = settings;
{ this._properties = properties;
_args = args;
_instance = instance; this.PopulateInstance();
_settings = settings; this.SetDefaultValues();
_properties = properties; this.GetRequiredList();
}
PopulateInstance();
SetDefaultValues(); public List<String> UnknownList { get; } = new List<String>();
GetRequiredList(); public List<String> RequiredList { get; } = new List<String>();
}
public Boolean IsValid() => (this._settings.IgnoreUnknownArguments || !this.UnknownList.Any()) && !this.RequiredList.Any();
public List<string> UnknownList { get; } = new List<string>();
public List<string> RequiredList { get; } = new List<string>(); public IEnumerable<ArgumentOptionAttribute> GetPropertiesOptions()
=> this._properties.Select(p => Runtime.AttributeCache.RetrieveOne<ArgumentOptionAttribute>(p))
public bool IsValid() => (_settings.IgnoreUnknownArguments || !UnknownList.Any()) && !RequiredList.Any(); .Where(x => x != null);
public IEnumerable<ArgumentOptionAttribute> GetPropertiesOptions() private void GetRequiredList() {
=> _properties.Select(p => Runtime.AttributeCache.RetrieveOne<ArgumentOptionAttribute>(p)) foreach(PropertyInfo targetProperty in this._properties) {
.Where(x => x != null); ArgumentOptionAttribute optionAttr = Runtime.AttributeCache.RetrieveOne<ArgumentOptionAttribute>(targetProperty);
private void GetRequiredList() if(optionAttr == null || optionAttr.Required == false) {
{ continue;
foreach (var targetProperty in _properties) }
{
var optionAttr = Runtime.AttributeCache.RetrieveOne<ArgumentOptionAttribute>(targetProperty); if(targetProperty.GetValue(this._instance) == null) {
this.RequiredList.Add(optionAttr.LongName ?? optionAttr.ShortName);
if (optionAttr == null || optionAttr.Required == false) }
continue; }
}
if (targetProperty.GetValue(_instance) == null)
{ private void SetDefaultValues() {
RequiredList.Add(optionAttr.LongName ?? optionAttr.ShortName); foreach(PropertyInfo targetProperty in this._properties.Except(this._updatedList)) {
} ArgumentOptionAttribute optionAttr = Runtime.AttributeCache.RetrieveOne<ArgumentOptionAttribute>(targetProperty);
}
} Object defaultValue = optionAttr?.DefaultValue;
private void SetDefaultValues() if(defaultValue == null) {
{ continue;
foreach (var targetProperty in _properties.Except(_updatedList)) }
{
var optionAttr = Runtime.AttributeCache.RetrieveOne<ArgumentOptionAttribute>(targetProperty); if(this.SetPropertyValue(targetProperty, defaultValue.ToString(), this._instance, optionAttr)) {
this._updatedList.Add(targetProperty);
var defaultValue = optionAttr?.DefaultValue; }
}
if (defaultValue == null) }
continue;
private void PopulateInstance() {
if (SetPropertyValue(targetProperty, defaultValue.ToString(), _instance, optionAttr)) const Char dash = '-';
_updatedList.Add(targetProperty); String propertyName = String.Empty;
}
} foreach(String arg in this._args) {
Boolean ignoreSetValue = String.IsNullOrWhiteSpace(propertyName);
private void PopulateInstance()
{ if(ignoreSetValue) {
const char dash = '-'; if(String.IsNullOrWhiteSpace(arg) || arg[0] != dash) {
var propertyName = string.Empty; continue;
}
foreach (var arg in _args)
{ propertyName = arg.Substring(1);
var ignoreSetValue = string.IsNullOrWhiteSpace(propertyName);
if(!String.IsNullOrWhiteSpace(propertyName) && propertyName[0] == dash) {
if (ignoreSetValue) propertyName = propertyName.Substring(1);
{ }
if (string.IsNullOrWhiteSpace(arg) || arg[0] != dash) continue; }
propertyName = arg.Substring(1); PropertyInfo targetProperty = this.TryGetProperty(propertyName);
if (!string.IsNullOrWhiteSpace(propertyName) && propertyName[0] == dash) if(targetProperty == null) {
propertyName = propertyName.Substring(1); // Skip if the property is not found
} this.UnknownList.Add(propertyName);
continue;
var targetProperty = TryGetProperty(propertyName); }
if (targetProperty == null) if(!ignoreSetValue && this.SetPropertyValue(targetProperty, arg, this._instance)) {
{ this._updatedList.Add(targetProperty);
// Skip if the property is not found propertyName = String.Empty;
UnknownList.Add(propertyName); } else if(targetProperty.PropertyType == typeof(Boolean)) {
continue; // If the arg is a boolean property set it to true.
} targetProperty.SetValue(this._instance, true);
if (!ignoreSetValue && SetPropertyValue(targetProperty, arg, _instance)) this._updatedList.Add(targetProperty);
{ propertyName = String.Empty;
_updatedList.Add(targetProperty); }
propertyName = string.Empty; }
}
else if (targetProperty.PropertyType == typeof(bool)) if(!String.IsNullOrEmpty(propertyName)) {
{ this.UnknownList.Add(propertyName);
// If the arg is a boolean property set it to true. }
targetProperty.SetValue(_instance, true); }
_updatedList.Add(targetProperty); private Boolean SetPropertyValue(
propertyName = string.Empty; PropertyInfo targetProperty,
} String propertyValueString,
} Object result,
ArgumentOptionAttribute optionAttr = null) {
if (!string.IsNullOrEmpty(propertyName)) if(targetProperty.PropertyType.GetTypeInfo().IsEnum) {
{ Object parsedValue = Enum.Parse(
UnknownList.Add(propertyName); targetProperty.PropertyType,
} propertyValueString,
} this._settings.CaseInsensitiveEnumValues);
private bool SetPropertyValue( targetProperty.SetValue(result, Enum.ToObject(targetProperty.PropertyType, parsedValue));
PropertyInfo targetProperty,
string propertyValueString, return true;
object result, }
ArgumentOptionAttribute optionAttr = null)
{ return targetProperty.PropertyType.IsArray
if (targetProperty.PropertyType.GetTypeInfo().IsEnum) ? targetProperty.TrySetArray(propertyValueString.Split(optionAttr?.Separator ?? ','), result)
{ : targetProperty.TrySetBasicType(propertyValueString, result);
var parsedValue = Enum.Parse( }
targetProperty.PropertyType,
propertyValueString, private PropertyInfo TryGetProperty(String propertyName)
_settings.CaseInsensitiveEnumValues); => this._properties.FirstOrDefault(p =>
String.Equals(Runtime.AttributeCache.RetrieveOne<ArgumentOptionAttribute>(p)?.LongName, propertyName, this._settings.NameComparer) ||
targetProperty.SetValue(result, Enum.ToObject(targetProperty.PropertyType, parsedValue)); String.Equals(Runtime.AttributeCache.RetrieveOne<ArgumentOptionAttribute>(p)?.ShortName, propertyName, this._settings.NameComparer));
}
return true; }
}
return targetProperty.PropertyType.IsArray
? targetProperty.TrySetArray(propertyValueString.Split(optionAttr?.Separator ?? ','), result)
: targetProperty.TrySetBasicType(propertyValueString, result);
}
private PropertyInfo TryGetProperty(string propertyName)
=> _properties.FirstOrDefault(p =>
string.Equals(Runtime.AttributeCache.RetrieveOne<ArgumentOptionAttribute>(p)?.LongName, propertyName, _settings.NameComparer) ||
string.Equals(Runtime.AttributeCache.RetrieveOne<ArgumentOptionAttribute>(p)?.ShortName, propertyName, _settings.NameComparer));
}
}
} }
@@ -1,57 +1,52 @@
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 readonly String _selectedVerb;
private sealed class TypeResolver<T>
{ private PropertyInfo[] _properties;
private readonly string _selectedVerb;
public TypeResolver(String selectedVerb) => this._selectedVerb = selectedVerb;
private PropertyInfo[] _properties;
public PropertyInfo[] GetProperties() => this._properties?.Any() == true ? this._properties : null;
public TypeResolver(string selectedVerb)
{ public Object GetOptionsObject(T instance) {
_selectedVerb = selectedVerb; this._properties = Runtime.PropertyTypeCache.RetrieveAllProperties<T>(true).ToArray();
}
if(!this._properties.Any(x => x.GetCustomAttributes(typeof(VerbOptionAttribute), false).Any())) {
public PropertyInfo[] GetProperties() => _properties?.Any() == true ? _properties : null; return instance;
}
public object GetOptionsObject(T instance)
{ PropertyInfo selectedVerb = String.IsNullOrWhiteSpace(this._selectedVerb)
_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(); }
var verbProperty = type.GetProperty(selectedVerb.Name); Type type = instance.GetType();
if (verbProperty?.GetValue(instance) == null) PropertyInfo verbProperty = type.GetProperty(selectedVerb.Name);
{
var propertyInstance = Activator.CreateInstance(selectedVerb.PropertyType); if(verbProperty?.GetValue(instance) == null) {
verbProperty?.SetValue(instance, propertyInstance); Object propertyInstance = Activator.CreateInstance(selectedVerb.PropertyType);
} verbProperty?.SetValue(instance, propertyInstance);
}
_properties = Runtime.PropertyTypeCache.RetrieveAllProperties(selectedVerb.PropertyType, true)
.ToArray(); this._properties = Runtime.PropertyTypeCache.RetrieveAllProperties(selectedVerb.PropertyType, true)
.ToArray();
return verbProperty?.GetValue(instance);
} return verbProperty?.GetValue(instance);
} }
} }
}
} }
+224 -226
View File
@@ -1,230 +1,228 @@
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>
/// Provides methods to parse command line arguments.
/// Based on CommandLine (Copyright 2005-2015 Giacomo Stelluti Scala and Contributors.).
/// </summary>
/// <example>
/// The following example shows how to parse CLI arguments into objects.
/// <code>
/// class Example
/// {
/// using System;
/// using Unosquare.Swan;
/// using Unosquare.Swan.Attributes;
///
/// static void Main(string[] args)
/// {
/// // create an instance of the Options class
/// var options = new Options();
///
/// // parse the supplied command-line arguments into the options object
/// var res = Runtime.ArgumentParser.ParseArguments(args, options);
/// }
///
/// class Options
/// {
/// [ArgumentOption('v', "verbose", HelpText = "Set verbose mode.")]
/// public bool Verbose { get; set; }
///
/// [ArgumentOption('u', Required = true, HelpText = "Set user name.")]
/// public string Username { get; set; }
///
/// [ArgumentOption('n', "names", Separator = ',',
/// Required = true, HelpText = "A list of files separated by a comma")]
/// public string[] Files { get; set; }
///
/// [ArgumentOption('p', "port", DefaultValue = 22, HelpText = "Set port.")]
/// public int Port { get; set; }
///
/// [ArgumentOption("color", DefaultValue = ConsoleColor.Red,
/// HelpText = "Set a color.")]
/// public ConsoleColor Color { get; set; }
/// }
/// }
/// </code>
/// The following code describes how to parse CLI verbs.
/// <code>
/// class Example2
/// {
/// using Unosquare.Swan;
/// using Unosquare.Swan.Attributes;
///
/// static void Main(string[] args)
/// {
/// // create an instance of the VerbOptions class
/// var options = new VerbOptions();
///
/// // parse the supplied command-line arguments into the options object
/// var res = Runtime.ArgumentParser.ParseArguments(args, options);
///
/// // if there were no errors parsing
/// if (res)
/// {
/// if(options.Run != null)
/// {
/// // run verb was selected
/// }
///
/// if(options.Print != null)
/// {
/// // print verb was selected
/// }
/// }
///
/// // flush all error messages
/// Terminal.Flush();
/// }
///
/// class VerbOptions
/// {
/// [VerbOption("run", HelpText = "Run verb.")]
/// public RunVerbOption Run { get; set; }
///
/// [VerbOption("print", HelpText = "Print verb.")]
/// public PrintVerbOption Print { get; set; }
/// }
///
/// class RunVerbOption
/// {
/// [ArgumentOption('o', "outdir", HelpText = "Output directory",
/// DefaultValue = "", Required = false)]
/// public string OutDir { get; set; }
/// }
///
/// class PrintVerbOption
/// {
/// [ArgumentOption('t', "text", HelpText = "Text to print",
/// DefaultValue = "", Required = false)]
/// public string Text { get; set; }
/// }
/// }
/// </code>
/// </example>
public partial class ArgumentParser {
/// <summary> /// <summary>
/// Provides methods to parse command line arguments. /// Initializes a new instance of the <see cref="ArgumentParser"/> class.
/// Based on CommandLine (Copyright 2005-2015 Giacomo Stelluti Scala and Contributors.).
/// </summary> /// </summary>
/// <example> public ArgumentParser()
/// The following example shows how to parse CLI arguments into objects. : this(new ArgumentParserSettings()) {
/// <code> }
/// class Example
/// { /// <summary>
/// using System; /// Initializes a new instance of the <see cref="ArgumentParser" /> class,
/// using Unosquare.Swan; /// configurable with <see cref="ArgumentParserSettings" /> using a delegate.
/// using Unosquare.Swan.Attributes; /// </summary>
/// /// <param name="parseSettings">The parse settings.</param>
/// static void Main(string[] args) public ArgumentParser(ArgumentParserSettings parseSettings) => this.Settings = parseSettings ?? throw new ArgumentNullException(nameof(parseSettings));
/// {
/// // create an instance of the Options class /// <summary>
/// var options = new Options(); /// Gets the instance that implements <see cref="ArgumentParserSettings" /> in use.
/// /// </summary>
/// // parse the supplied command-line arguments into the options object /// <value>
/// var res = Runtime.ArgumentParser.ParseArguments(args, options); /// The settings.
/// } /// </value>
/// public ArgumentParserSettings Settings {
/// class Options get;
/// { }
/// [ArgumentOption('v', "verbose", HelpText = "Set verbose mode.")]
/// public bool Verbose { get; set; } /// <summary>
/// /// Parses a string array of command line arguments constructing values in an instance of type <typeparamref name="T" />.
/// [ArgumentOption('u', Required = true, HelpText = "Set user name.")] /// </summary>
/// public string Username { get; set; } /// <typeparam name="T">The type of the options.</typeparam>
/// /// <param name="args">The arguments.</param>
/// [ArgumentOption('n', "names", Separator = ',', /// <param name="instance">The instance.</param>
/// Required = true, HelpText = "A list of files separated by a comma")] /// <returns>
/// public string[] Files { get; set; } /// <c>true</c> if was converted successfully; otherwise, <c>false</c>.
/// /// </returns>
/// [ArgumentOption('p', "port", DefaultValue = 22, HelpText = "Set port.")] /// <exception cref="ArgumentNullException">
/// public int Port { get; set; } /// The exception that is thrown when a null reference (Nothing in Visual Basic)
/// /// is passed to a method that does not accept it as a valid argument.
/// [ArgumentOption("color", DefaultValue = ConsoleColor.Red, /// </exception>
/// HelpText = "Set a color.")] /// <exception cref="InvalidOperationException">
/// public ConsoleColor Color { get; set; } /// The exception that is thrown when a method call is invalid for the object's current state.
/// } /// </exception>
/// } public Boolean ParseArguments<T>(IEnumerable<String> args, T instance) {
/// </code> if(args == null) {
/// The following code describes how to parse CLI verbs. throw new ArgumentNullException(nameof(args));
/// <code> }
/// class Example2
/// { if(Equals(instance, default(T))) {
/// using Unosquare.Swan; throw new ArgumentNullException(nameof(instance));
/// using Unosquare.Swan.Attributes; }
///
/// static void Main(string[] args) TypeResolver<T> typeResolver = new TypeResolver<T>(args.FirstOrDefault());
/// { Object options = typeResolver.GetOptionsObject(instance);
/// // create an instance of the VerbOptions class
/// var options = new VerbOptions(); if(options == null) {
/// ReportUnknownVerb<T>();
/// // parse the supplied command-line arguments into the options object return false;
/// var res = Runtime.ArgumentParser.ParseArguments(args, options); }
///
/// // if there were no errors parsing System.Reflection.PropertyInfo[] properties = typeResolver.GetProperties();
/// if (res)
/// { if(properties == null) {
/// if(options.Run != null) throw new InvalidOperationException($"Type {typeof(T).Name} is not valid");
/// { }
/// // run verb was selected
/// } Validator validator = new Validator(properties, args, options, this.Settings);
///
/// if(options.Print != null) if(validator.IsValid()) {
/// { return true;
/// // print verb was selected }
/// }
/// } this.ReportIssues(validator);
/// return false;
/// // flush all error messages }
/// Terminal.Flush();
/// } private static void ReportUnknownVerb<T>() {
/// "No verb was specified".WriteLine(ConsoleColor.Red);
/// class VerbOptions "Valid verbs:".WriteLine(ConsoleColor.Cyan);
/// {
/// [VerbOption("run", HelpText = "Run verb.")] Runtime.PropertyTypeCache.RetrieveAllProperties<T>(true)
/// public RunVerbOption Run { get; set; } .Select(x => Runtime.AttributeCache.RetrieveOne<VerbOptionAttribute>(x))
/// .Where(x => x != null)
/// [VerbOption("print", HelpText = "Print verb.")] .ToList()
/// public PrintVerbOption Print { get; set; } .ForEach(x => x.ToString().WriteLine(ConsoleColor.Cyan));
/// } }
///
/// class RunVerbOption private void ReportIssues(Validator validator) {
/// { #if !NETSTANDARD1_3
/// [ArgumentOption('o', "outdir", HelpText = "Output directory", if(this.Settings.WriteBanner) {
/// DefaultValue = "", Required = false)] Runtime.WriteWelcomeBanner();
/// public string OutDir { get; set; } }
/// }
///
/// class PrintVerbOption
/// {
/// [ArgumentOption('t', "text", HelpText = "Text to print",
/// DefaultValue = "", Required = false)]
/// public string Text { get; set; }
/// }
/// }
/// </code>
/// </example>
public partial class ArgumentParser
{
/// <summary>
/// Initializes a new instance of the <see cref="ArgumentParser"/> class.
/// </summary>
public ArgumentParser()
: this(new ArgumentParserSettings())
{
}
/// <summary>
/// Initializes a new instance of the <see cref="ArgumentParser" /> class,
/// configurable with <see cref="ArgumentParserSettings" /> using a delegate.
/// </summary>
/// <param name="parseSettings">The parse settings.</param>
public ArgumentParser(ArgumentParserSettings parseSettings)
{
Settings = parseSettings ?? throw new ArgumentNullException(nameof(parseSettings));
}
/// <summary>
/// Gets the instance that implements <see cref="ArgumentParserSettings" /> in use.
/// </summary>
/// <value>
/// The settings.
/// </value>
public ArgumentParserSettings Settings { get; }
/// <summary>
/// Parses a string array of command line arguments constructing values in an instance of type <typeparamref name="T" />.
/// </summary>
/// <typeparam name="T">The type of the options.</typeparam>
/// <param name="args">The arguments.</param>
/// <param name="instance">The instance.</param>
/// <returns>
/// <c>true</c> if was converted successfully; otherwise, <c>false</c>.
/// </returns>
/// <exception cref="ArgumentNullException">
/// The exception that is thrown when a null reference (Nothing in Visual Basic)
/// is passed to a method that does not accept it as a valid argument.
/// </exception>
/// <exception cref="InvalidOperationException">
/// The exception that is thrown when a method call is invalid for the object's current state.
/// </exception>
public bool ParseArguments<T>(IEnumerable<string> args, T instance)
{
if (args == null)
throw new ArgumentNullException(nameof(args));
if (Equals(instance, default(T)))
throw new ArgumentNullException(nameof(instance));
var typeResolver = new TypeResolver<T>(args.FirstOrDefault());
var options = typeResolver.GetOptionsObject(instance);
if (options == null)
{
ReportUnknownVerb<T>();
return false;
}
var properties = typeResolver.GetProperties();
if (properties == null)
throw new InvalidOperationException($"Type {typeof(T).Name} is not valid");
var validator = new Validator(properties, args, options, Settings);
if (validator.IsValid())
return true;
ReportIssues(validator);
return false;
}
private static void ReportUnknownVerb<T>()
{
"No verb was specified".WriteLine(ConsoleColor.Red);
"Valid verbs:".WriteLine(ConsoleColor.Cyan);
Runtime.PropertyTypeCache.RetrieveAllProperties<T>(true)
.Select(x => Runtime.AttributeCache.RetrieveOne<VerbOptionAttribute>(x))
.Where(x => x != null)
.ToList()
.ForEach(x => x.ToString().WriteLine(ConsoleColor.Cyan));
}
private void ReportIssues(Validator validator)
{
#if !NETSTANDARD1_3
if (Settings.WriteBanner)
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 String shortName = String.IsNullOrWhiteSpace(option.ShortName) ? String.Empty : $"-{option.ShortName}";
var shortName = string.IsNullOrWhiteSpace(option.ShortName) ? string.Empty : $"-{option.ShortName}"; String longName = String.IsNullOrWhiteSpace(option.LongName) ? String.Empty : $"--{option.LongName}";
var longName = string.IsNullOrWhiteSpace(option.LongName) ? string.Empty : $"--{option.LongName}"; String comma = String.IsNullOrWhiteSpace(shortName) || String.IsNullOrWhiteSpace(longName)
var comma = string.IsNullOrWhiteSpace(shortName) || string.IsNullOrWhiteSpace(longName) ? String.Empty
? string.Empty : ", ";
: ", "; String defaultValue = option.DefaultValue == null ? String.Empty : $"(Default: {option.DefaultValue}) ";
var 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,53 +1,51 @@
namespace Unosquare.Swan.Components using System;
{
using System; namespace Unosquare.Swan.Components {
/// <summary>
/// Provides settings for <see cref="ArgumentParser"/>.
/// Based on CommandLine (Copyright 2005-2015 Giacomo Stelluti Scala and Contributors.).
/// </summary>
public class ArgumentParserSettings {
/// <summary> /// <summary>
/// Provides settings for <see cref="ArgumentParser"/>. /// Gets or sets a value indicating whether [write banner].
/// Based on CommandLine (Copyright 2005-2015 Giacomo Stelluti Scala and Contributors.).
/// </summary> /// </summary>
public class ArgumentParserSettings /// <value>
{ /// <c>true</c> if [write banner]; otherwise, <c>false</c>.
/// <summary> /// </value>
/// Gets or sets a value indicating whether [write banner]. public Boolean WriteBanner { get; set; } = true;
/// </summary>
/// <value> /// <summary>
/// <c>true</c> if [write banner]; otherwise, <c>false</c>. /// Gets or sets a value indicating whether perform case sensitive comparisons.
/// </value> /// Note that case insensitivity only applies to <i>parameters</i>, not the values
public bool WriteBanner { get; set; } = true; /// assigned to them (for example, enum parsing).
/// </summary>
/// <summary> /// <value>
/// Gets or sets a value indicating whether perform case sensitive comparisons. /// <c>true</c> if [case sensitive]; otherwise, <c>false</c>.
/// Note that case insensitivity only applies to <i>parameters</i>, not the values /// </value>
/// assigned to them (for example, enum parsing). public Boolean CaseSensitive { get; set; } = false;
/// </summary>
/// <value> /// <summary>
/// <c>true</c> if [case sensitive]; otherwise, <c>false</c>. /// Gets or sets a value indicating whether perform case sensitive comparisons of <i>values</i>.
/// </value> /// Note that case insensitivity only applies to <i>values</i>, not the parameters.
public bool CaseSensitive { get; set; } = false; /// </summary>
/// <value>
/// <summary> /// <c>true</c> if [case insensitive enum values]; otherwise, <c>false</c>.
/// Gets or sets a value indicating whether perform case sensitive comparisons of <i>values</i>. /// </value>
/// Note that case insensitivity only applies to <i>values</i>, not the parameters. public Boolean CaseInsensitiveEnumValues { get; set; } = true;
/// </summary>
/// <value> /// <summary>
/// <c>true</c> if [case insensitive enum values]; otherwise, <c>false</c>. /// Gets or sets a value indicating whether the parser shall move on to the next argument and ignore the given argument if it
/// </value> /// encounter an unknown arguments.
public bool CaseInsensitiveEnumValues { get; set; } = true; /// </summary>
/// <value>
/// <summary> /// <c>true</c> to allow parsing the arguments with different class options that do not have all the arguments.
/// Gets or sets a value indicating whether the parser shall move on to the next argument and ignore the given argument if it /// </value>
/// encounter an unknown arguments. /// <remarks>
/// </summary> /// This allows fragmented version class parsing, useful for project with add-on where add-ons also requires command line arguments but
/// <value> /// when these are unknown by the main program at build time.
/// <c>true</c> to allow parsing the arguments with different class options that do not have all the arguments. /// </remarks>
/// </value> public Boolean IgnoreUnknownArguments { get; set; } = true;
/// <remarks>
/// This allows fragmented version class parsing, useful for project with add-on where add-ons also requires command line arguments but internal StringComparison NameComparer => this.CaseSensitive ? StringComparison.Ordinal : StringComparison.OrdinalIgnoreCase;
/// when these are unknown by the main program at build time. }
/// </remarks>
public bool IgnoreUnknownArguments { get; set; } = true;
internal StringComparison NameComparer => CaseSensitive ? StringComparison.Ordinal : StringComparison.OrdinalIgnoreCase;
}
} }
+119 -127
View File
@@ -1,130 +1,122 @@
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>
/// A simple benchmarking class.
/// </summary>
/// <example>
/// The following code demonstrates how to create a simple benchmark.
/// <code>
/// namespace Examples.Benchmark.Simple
/// {
/// using Unosquare.Swan.Components;
///
/// public class SimpleBenchmark
/// {
/// public static void Main()
/// {
/// using (Benchmark.Start("Test"))
/// {
/// // do some logic in here
/// }
///
/// // dump results into a string
/// var results = Benchmark.Dump();
/// }
/// }
///
/// }
/// </code>
/// </example>
public static class Benchmark {
private static readonly Object SyncLock = new Object();
private static readonly Dictionary<String, List<TimeSpan>> Measures = new Dictionary<String, List<TimeSpan>>();
/// <summary> /// <summary>
/// A simple benchmarking class. /// Starts measuring with the given identifier.
/// </summary> /// </summary>
/// <example> /// <param name="identifier">The identifier.</param>
/// The following code demonstrates how to create a simple benchmark. /// <returns>A disposable object that when disposed, adds a benchmark result.</returns>
/// <code> public static IDisposable Start(String identifier) => new BenchmarkUnit(identifier);
/// namespace Examples.Benchmark.Simple
/// { /// <summary>
/// using Unosquare.Swan.Components; /// Outputs the benchmark statistics.
/// /// </summary>
/// public class SimpleBenchmark /// <returns>A string containing human-readable statistics.</returns>
/// { public static String Dump() {
/// public static void Main() StringBuilder builder = new StringBuilder();
/// {
/// using (Benchmark.Start("Test")) lock(SyncLock) {
/// { foreach(KeyValuePair<String, List<TimeSpan>> kvp in Measures) {
/// // do some logic in here _ = builder.Append($"BID: {kvp.Key,-30} | ")
/// } .Append($"CNT: {kvp.Value.Count,6} | ")
/// .Append($"AVG: {kvp.Value.Average(t => t.TotalMilliseconds),8:0.000} ms. | ")
/// // dump results into a string .Append($"MAX: {kvp.Value.Max(t => t.TotalMilliseconds),8:0.000} ms. | ")
/// var results = Benchmark.Dump(); .Append($"MIN: {kvp.Value.Min(t => t.TotalMilliseconds),8:0.000} ms. | ")
/// } .Append(Environment.NewLine);
/// } }
/// }
/// }
/// </code> return builder.ToString().TrimEnd();
/// </example> }
public static class Benchmark
{ /// <summary>
private static readonly object SyncLock = new object(); /// Adds the specified result to the given identifier.
private static readonly Dictionary<string, List<TimeSpan>> Measures = new Dictionary<string, List<TimeSpan>>(); /// </summary>
/// <param name="identifier">The identifier.</param>
/// <summary> /// <param name="elapsed">The elapsed.</param>
/// Starts measuring with the given identifier. private static void Add(String identifier, TimeSpan elapsed) {
/// </summary> lock(SyncLock) {
/// <param name="identifier">The identifier.</param> if(Measures.ContainsKey(identifier) == false) {
/// <returns>A disposable object that when disposed, adds a benchmark result.</returns> Measures[identifier] = new List<TimeSpan>(1024 * 1024);
public static IDisposable Start(string identifier) => new BenchmarkUnit(identifier); }
/// <summary> Measures[identifier].Add(elapsed);
/// Outputs the benchmark statistics. }
/// </summary> }
/// <returns>A string containing human-readable statistics.</returns>
public static string Dump() /// <summary>
{ /// Represents a disposable benchmark unit.
var builder = new StringBuilder(); /// </summary>
/// <seealso cref="IDisposable" />
lock (SyncLock) private sealed class BenchmarkUnit : IDisposable {
{ private readonly String _identifier;
foreach (var kvp in Measures) private Boolean _isDisposed; // To detect redundant calls
{ private Stopwatch _stopwatch = new Stopwatch();
builder.Append($"BID: {kvp.Key,-30} | ")
.Append($"CNT: {kvp.Value.Count,6} | ") /// <summary>
.Append($"AVG: {kvp.Value.Average(t => t.TotalMilliseconds),8:0.000} ms. | ") /// Initializes a new instance of the <see cref="BenchmarkUnit" /> class.
.Append($"MAX: {kvp.Value.Max(t => t.TotalMilliseconds),8:0.000} ms. | ") /// </summary>
.Append($"MIN: {kvp.Value.Min(t => t.TotalMilliseconds),8:0.000} ms. | ") /// <param name="identifier">The identifier.</param>
.Append(Environment.NewLine); public BenchmarkUnit(String identifier) {
} this._identifier = identifier;
} this._stopwatch.Start();
}
return builder.ToString().TrimEnd();
} /// <inheritdoc />
public void Dispose() => this.Dispose(true);
/// <summary>
/// Adds the specified result to the given identifier. /// <summary>
/// </summary> /// Releases unmanaged and - optionally - managed resources.
/// <param name="identifier">The identifier.</param> /// </summary>
/// <param name="elapsed">The elapsed.</param> /// <param name="alsoManaged"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
private static void Add(string identifier, TimeSpan elapsed) private void Dispose(Boolean alsoManaged) {
{ if(this._isDisposed) {
lock (SyncLock) return;
{ }
if (Measures.ContainsKey(identifier) == false)
Measures[identifier] = new List<TimeSpan>(1024 * 1024); if(alsoManaged) {
Add(this._identifier, this._stopwatch.Elapsed);
Measures[identifier].Add(elapsed); this._stopwatch?.Stop();
} }
}
this._stopwatch = null;
/// <summary> this._isDisposed = true;
/// Represents a disposable benchmark unit. }
/// </summary> }
/// <seealso cref="IDisposable" /> }
private sealed class BenchmarkUnit : IDisposable
{
private readonly string _identifier;
private bool _isDisposed; // To detect redundant calls
private Stopwatch _stopwatch = new Stopwatch();
/// <summary>
/// Initializes a new instance of the <see cref="BenchmarkUnit" /> class.
/// </summary>
/// <param name="identifier">The identifier.</param>
public BenchmarkUnit(string identifier)
{
_identifier = identifier;
_stopwatch.Start();
}
/// <inheritdoc />
public void Dispose() => Dispose(true);
/// <summary>
/// Releases unmanaged and - optionally - managed resources.
/// </summary>
/// <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)
{
if (_isDisposed) return;
if (alsoManaged)
{
Add(_identifier, _stopwatch.Elapsed);
_stopwatch?.Stop();
}
_stopwatch = null;
_isDisposed = true;
}
}
}
} }
@@ -1,44 +1,42 @@
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>
/// A thread-safe collection cache repository for types.
/// </summary>
/// <typeparam name="TValue">The type of member to cache.</typeparam>
public class CollectionCacheRepository<TValue> {
private readonly Lazy<ConcurrentDictionary<Type, IEnumerable<TValue>>> _data =
new Lazy<ConcurrentDictionary<Type, IEnumerable<TValue>>>(() =>
new ConcurrentDictionary<Type, IEnumerable<TValue>>(), true);
/// <summary> /// <summary>
/// A thread-safe collection cache repository for types. /// Determines whether the cache contains the specified key.
/// </summary> /// </summary>
/// <typeparam name="TValue">The type of member to cache.</typeparam> /// <param name="key">The key.</param>
public class CollectionCacheRepository<TValue> /// <returns><c>true</c> if the cache contains the key, otherwise <c>false</c>.</returns>
{ public Boolean ContainsKey(Type key) => this._data.Value.ContainsKey(key);
private readonly Lazy<ConcurrentDictionary<Type, IEnumerable<TValue>>> _data =
new Lazy<ConcurrentDictionary<Type, IEnumerable<TValue>>>(() => /// <summary>
new ConcurrentDictionary<Type, IEnumerable<TValue>>(), true); /// Retrieves the properties stored for the specified type.
/// If the properties are not available, it calls the factory method to retrieve them
/// <summary> /// and returns them as an array of PropertyInfo.
/// Determines whether the cache contains the specified key. /// </summary>
/// </summary> /// <param name="key">The key.</param>
/// <param name="key">The key.</param> /// <param name="factory">The factory.</param>
/// <returns><c>true</c> if the cache contains the key, otherwise <c>false</c>.</returns> /// <returns>
public bool ContainsKey(Type key) => _data.Value.ContainsKey(key); /// An array of the properties stored for the specified type.
/// </returns>
/// <summary> /// <exception cref="System.ArgumentNullException">type.</exception>
/// Retrieves the properties stored for the specified type. public IEnumerable<TValue> Retrieve(Type key, Func<Type, IEnumerable<TValue>> factory) {
/// If the properties are not available, it calls the factory method to retrieve them if(factory == null) {
/// and returns them as an array of PropertyInfo. throw new ArgumentNullException(nameof(factory));
/// </summary> }
/// <param name="key">The key.</param>
/// <param name="factory">The factory.</param> return this._data.Value.GetOrAdd(key, k => factory.Invoke(k).Where(item => item != null));
/// <returns> }
/// An array of the properties stored for the specified type. }
/// </returns>
/// <exception cref="System.ArgumentNullException">type.</exception>
public IEnumerable<TValue> Retrieve(Type key, Func<Type, IEnumerable<TValue>> factory)
{
if (factory == null)
throw new ArgumentNullException(nameof(factory));
return _data.Value.GetOrAdd(key, k => factory.Invoke(k).Where(item => item != null));
}
}
} }
+141 -168
View File
@@ -1,171 +1,144 @@
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>
/// Provide Enumerations helpers with internal cache.
/// </summary>
public class EnumHelper
: SingletonBase<CollectionCacheRepository<Tuple<String, Object>>> {
/// <summary> /// <summary>
/// Provide Enumerations helpers with internal cache. /// Gets all the names and enumerators from a specific Enum type.
/// </summary> /// </summary>
public class EnumHelper /// <typeparam name="T">The type of the attribute to be retrieved.</typeparam>
: SingletonBase<CollectionCacheRepository<Tuple<string, object>>> /// <returns>A tuple of enumerator names and their value stored for the specified type.</returns>
{ public static IEnumerable<Tuple<String, Object>> Retrieve<T>()
/// <summary> where T : struct, IConvertible => Instance.Retrieve(typeof(T), t => Enum.GetValues(t)
/// Gets all the names and enumerators from a specific Enum type. .Cast<Object>()
/// </summary> .Select(item => Tuple.Create(Enum.GetName(t, item), item)));
/// <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> /// <summary>
public static IEnumerable<Tuple<string, object>> Retrieve<T>() /// Gets the cached items with the enum item value.
where T : struct, IConvertible /// </summary>
{ /// <typeparam name="T">The type of enumeration.</typeparam>
return Instance.Retrieve(typeof(T), t => Enum.GetValues(t) /// <param name="humanize">if set to <c>true</c> [humanize].</param>
.Cast<object>() /// <returns>
.Select(item => Tuple.Create(Enum.GetName(t, item), item))); /// A collection of Type/Tuple pairs
} /// that represents items with the enum item value.
/// </returns>
/// <summary> public static IEnumerable<Tuple<Int32, String>> GetItemsWithValue<T>(Boolean humanize = true)
/// Gets the cached items with the enum item value. where T : struct, IConvertible => Retrieve<T>()
/// </summary> .Select(x => Tuple.Create((Int32)x.Item2, humanize ? x.Item1.Humanize() : x.Item1));
/// <typeparam name="T">The type of enumeration.</typeparam>
/// <param name="humanize">if set to <c>true</c> [humanize].</param> /// <summary>
/// <returns> /// Gets the flag values.
/// A collection of Type/Tuple pairs /// </summary>
/// that represents items with the enum item value. /// <typeparam name="TEnum">The type of the enum.</typeparam>
/// </returns> /// <param name="value">The value.</param>
public static IEnumerable<Tuple<int, string>> GetItemsWithValue<T>(bool humanize = true) /// <param name="ignoreZero">if set to <c>true</c> [ignore zero].</param>
where T : struct, IConvertible /// <returns>
{ /// A list of values in the flag.
return Retrieve<T>() /// </returns>
.Select(x => Tuple.Create((int) x.Item2, humanize ? x.Item1.Humanize() : x.Item1)); public static IEnumerable<Int32> GetFlagValues<TEnum>(Int32 value, Boolean ignoreZero = false)
} where TEnum : struct, IConvertible => Retrieve<TEnum>()
.Select(x => (Int32)x.Item2)
/// <summary> .When(() => ignoreZero, q => q.Where(f => f != 0))
/// Gets the flag values. .Where(x => (x & value) == x);
/// </summary>
/// <typeparam name="TEnum">The type of the enum.</typeparam> /// <summary>
/// <param name="value">The value.</param> /// Gets the flag values.
/// <param name="ignoreZero">if set to <c>true</c> [ignore zero].</param> /// </summary>
/// <returns> /// <typeparam name="TEnum">The type of the enum.</typeparam>
/// A list of values in the flag. /// <param name="value">The value.</param>
/// </returns> /// <param name="ignoreZero">if set to <c>true</c> [ignore zero].</param>
public static IEnumerable<int> GetFlagValues<TEnum>(int value, bool ignoreZero = false) /// <returns>
where TEnum : struct, IConvertible /// A list of values in the flag.
{ /// </returns>
return Retrieve<TEnum>() public static IEnumerable<Int64> GetFlagValues<TEnum>(Int64 value, Boolean ignoreZero = false)
.Select(x => (int) x.Item2) where TEnum : struct, IConvertible => Retrieve<TEnum>()
.When(() => ignoreZero, q => q.Where(f => f != 0)) .Select(x => (Int64)x.Item2)
.Where(x => (x & value) == x); .When(() => ignoreZero, q => q.Where(f => f != 0))
} .Where(x => (x & value) == x);
/// <summary> /// <summary>
/// Gets the flag values. /// Gets the flag values.
/// </summary> /// </summary>
/// <typeparam name="TEnum">The type of the enum.</typeparam> /// <typeparam name="TEnum">The type of the enum.</typeparam>
/// <param name="value">The value.</param> /// <param name="value">The value.</param>
/// <param name="ignoreZero">if set to <c>true</c> [ignore zero].</param> /// <param name="ignoreZero">if set to <c>true</c> [ignore zero].</param>
/// <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<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>() .When(() => ignoreZero, q => q.Where(f => f != 0))
.Select(x => (long) x.Item2) .Where(x => (x & value) == x);
.When(() => ignoreZero, q => q.Where(f => f != 0))
.Where(x => (x & value) == x); /// <summary>
} /// Gets the flag names.
/// </summary>
/// <summary> /// <typeparam name="TEnum">The type of the enum.</typeparam>
/// Gets the flag values. /// <param name="value">the value.</param>
/// </summary> /// <param name="ignoreZero">if set to <c>true</c> [ignore zero].</param>
/// <typeparam name="TEnum">The type of the enum.</typeparam> /// <param name="humanize">if set to <c>true</c> [humanize].</param>
/// <param name="value">The value.</param> /// <returns>
/// <param name="ignoreZero">if set to <c>true</c> [ignore zero].</param> /// A list of flag names.
/// <returns> /// </returns>
/// A list of values in the flag. public static IEnumerable<String> GetFlagNames<TEnum>(Int32 value, Boolean ignoreZero = false, Boolean humanize = true)
/// </returns> where TEnum : struct, IConvertible => Retrieve<TEnum>()
public static IEnumerable<byte> GetFlagValues<TEnum>(byte value, bool ignoreZero = false) .When(() => ignoreZero, q => q.Where(f => (Int32)f.Item2 != 0))
where TEnum : struct, IConvertible .Where(x => ((Int32)x.Item2 & value) == (Int32)x.Item2)
{ .Select(x => humanize ? x.Item1.Humanize() : x.Item1);
return Retrieve<TEnum>()
.Select(x => (byte) x.Item2) /// <summary>
.When(() => ignoreZero, q => q.Where(f => f != 0)) /// Gets the flag names.
.Where(x => (x & value) == x); /// </summary>
} /// <typeparam name="TEnum">The type of the enum.</typeparam>
/// <param name="value">The value.</param>
/// <summary> /// <param name="ignoreZero">if set to <c>true</c> [ignore zero].</param>
/// Gets the flag names. /// <param name="humanize">if set to <c>true</c> [humanize].</param>
/// </summary> /// <returns>
/// <typeparam name="TEnum">The type of the enum.</typeparam> /// A list of flag names.
/// <param name="value">the value.</param> /// </returns>
/// <param name="ignoreZero">if set to <c>true</c> [ignore zero].</param> public static IEnumerable<String> GetFlagNames<TEnum>(Int64 value, Boolean ignoreZero = false, Boolean humanize = true)
/// <param name="humanize">if set to <c>true</c> [humanize].</param> where TEnum : struct, IConvertible => Retrieve<TEnum>()
/// <returns> .When(() => ignoreZero, q => q.Where(f => (Int64)f.Item2 != 0))
/// A list of flag names. .Where(x => ((Int64)x.Item2 & value) == (Int64)x.Item2)
/// </returns> .Select(x => humanize ? x.Item1.Humanize() : x.Item1);
public static IEnumerable<string> GetFlagNames<TEnum>(int value, bool ignoreZero = false, bool humanize = true)
where TEnum : struct, IConvertible /// <summary>
{ /// Gets the flag names.
return Retrieve<TEnum>() /// </summary>
.When(() => ignoreZero, q => q.Where(f => (int) f.Item2 != 0)) /// <typeparam name="TEnum">The type of the enum.</typeparam>
.Where(x => ((int) x.Item2 & value) == (int) x.Item2) /// <param name="value">The value.</param>
.Select(x => humanize ? x.Item1.Humanize() : x.Item1); /// <param name="ignoreZero">if set to <c>true</c> [ignore zero].</param>
} /// <param name="humanize">if set to <c>true</c> [humanize].</param>
/// <returns>
/// <summary> /// A list of flag names.
/// Gets the flag names. /// </returns>
/// </summary> public static IEnumerable<String> GetFlagNames<TEnum>(Byte value, Boolean ignoreZero = false, Boolean humanize = true)
/// <typeparam name="TEnum">The type of the enum.</typeparam> where TEnum : struct, IConvertible => Retrieve<TEnum>()
/// <param name="value">The value.</param> .When(() => ignoreZero, q => q.Where(f => (Byte)f.Item2 != 0))
/// <param name="ignoreZero">if set to <c>true</c> [ignore zero].</param> .Where(x => ((Byte)x.Item2 & value) == (Byte)x.Item2)
/// <param name="humanize">if set to <c>true</c> [humanize].</param> .Select(x => humanize ? x.Item1.Humanize() : x.Item1);
/// <returns>
/// A list of flag names. /// <summary>
/// </returns> /// Gets the cached items with the enum item index.
public static IEnumerable<string> GetFlagNames<TEnum>(long value, bool ignoreZero = false, bool humanize = true) /// </summary>
where TEnum : struct, IConvertible /// <typeparam name="T">The type of enumeration.</typeparam>
{ /// <param name="humanize">if set to <c>true</c> [humanize].</param>
return Retrieve<TEnum>() /// <returns>
.When(() => ignoreZero, q => q.Where(f => (long) f.Item2 != 0)) /// A collection of Type/Tuple pairs that represents items with the enum item value.
.Where(x => ((long) x.Item2 & value) == (long) x.Item2) /// </returns>
.Select(x => humanize ? x.Item1.Humanize() : x.Item1); public static IEnumerable<Tuple<Int32, String>> GetItemsWithIndex<T>(Boolean humanize = true)
} where T : struct, IConvertible {
Int32 i = 0;
/// <summary>
/// Gets the flag names. return Retrieve<T>()
/// </summary> .Select(x => Tuple.Create(i++, humanize ? x.Item1.Humanize() : x.Item1));
/// <typeparam name="TEnum">The type of the enum.</typeparam> }
/// <param name="value">The value.</param> }
/// <param name="ignoreZero">if set to <c>true</c> [ignore zero].</param>
/// <param name="humanize">if set to <c>true</c> [humanize].</param>
/// <returns>
/// A list of flag names.
/// </returns>
public static IEnumerable<string> GetFlagNames<TEnum>(byte value, bool ignoreZero = false, bool humanize = true)
where TEnum : struct, IConvertible
{
return Retrieve<TEnum>()
.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);
}
/// <summary>
/// Gets the cached items with the enum item index.
/// </summary>
/// <typeparam name="T">The type of enumeration.</typeparam>
/// <param name="humanize">if set to <c>true</c> [humanize].</param>
/// <returns>
/// A collection of Type/Tuple pairs that represents items with the enum item value.
/// </returns>
public static IEnumerable<Tuple<int, string>> GetItemsWithIndex<T>(bool humanize = true)
where T : struct, IConvertible
{
var i = 0;
return Retrieve<T>()
.Select(x => Tuple.Create(i++, humanize ? x.Item1.Humanize() : x.Item1));
}
}
} }
+180 -190
View File
@@ -1,193 +1,183 @@
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>
/// Represents a quick object comparer using the public properties of an object
/// or the public members in a structure.
/// </summary>
public static class ObjectComparer {
/// <summary> /// <summary>
/// Represents a quick object comparer using the public properties of an object /// Compare if two variables of the same type are equal.
/// or the public members in a structure.
/// </summary> /// </summary>
public static class ObjectComparer /// <typeparam name="T">The type of objects to compare.</typeparam>
{ /// <param name="left">The left.</param>
/// <summary> /// <param name="right">The right.</param>
/// Compare if two variables of the same type are equal. /// <returns><c>true</c> if the variables are equal; otherwise, <c>false</c>.</returns>
/// </summary> public static Boolean AreEqual<T>(T left, T right) => AreEqual(left, right, typeof(T));
/// <typeparam name="T">The type of objects to compare.</typeparam>
/// <param name="left">The left.</param> /// <summary>
/// <param name="right">The right.</param> /// Compare if two variables of the same type are equal.
/// <returns><c>true</c> if the variables are equal; otherwise, <c>false</c>.</returns> /// </summary>
public static bool AreEqual<T>(T left, T right) => AreEqual(left, right, typeof(T)); /// <param name="left">The left.</param>
/// <param name="right">The right.</param>
/// <summary> /// <param name="targetType">Type of the target.</param>
/// Compare if two variables of the same type are equal. /// <returns>
/// </summary> /// <c>true</c> if the variables are equal; otherwise, <c>false</c>.
/// <param name="left">The left.</param> /// </returns>
/// <param name="right">The right.</param> /// <exception cref="ArgumentNullException">targetType.</exception>
/// <param name="targetType">Type of the target.</param> public static Boolean AreEqual(Object left, Object right, Type targetType) {
/// <returns> if(targetType == null) {
/// <c>true</c> if the variables are equal; otherwise, <c>false</c>. throw new ArgumentNullException(nameof(targetType));
/// </returns> }
/// <exception cref="ArgumentNullException">targetType.</exception>
public static bool AreEqual(object left, object right, Type targetType) return Definitions.BasicTypesInfo.ContainsKey(targetType)
{ ? Equals(left, right)
if (targetType == null) : targetType.IsValueType() || targetType.IsArray
throw new ArgumentNullException(nameof(targetType)); ? AreStructsEqual(left, right, targetType)
: AreObjectsEqual(left, right, targetType);
if (Definitions.BasicTypesInfo.ContainsKey(targetType)) }
return Equals(left, right);
/// <summary>
if (targetType.IsValueType() || targetType.IsArray) /// Compare if two objects of the same type are equal.
return AreStructsEqual(left, right, targetType); /// </summary>
/// <typeparam name="T">The type of objects to compare.</typeparam>
return AreObjectsEqual(left, right, targetType); /// <param name="left">The left.</param>
} /// <param name="right">The right.</param>
/// <returns><c>true</c> if the objects are equal; otherwise, <c>false</c>.</returns>
/// <summary> public static Boolean AreObjectsEqual<T>(T left, T right)
/// Compare if two objects of the same type are equal. where T : class => AreObjectsEqual(left, right, typeof(T));
/// </summary>
/// <typeparam name="T">The type of objects to compare.</typeparam> /// <summary>
/// <param name="left">The left.</param> /// Compare if two objects of the same type are equal.
/// <param name="right">The right.</param> /// </summary>
/// <returns><c>true</c> if the objects are equal; otherwise, <c>false</c>.</returns> /// <param name="left">The left.</param>
public static bool AreObjectsEqual<T>(T left, T right) /// <param name="right">The right.</param>
where T : class /// <param name="targetType">Type of the target.</param>
{ /// <returns><c>true</c> if the objects are equal; otherwise, <c>false</c>.</returns>
return AreObjectsEqual(left, right, typeof(T)); /// <exception cref="ArgumentNullException">targetType.</exception>
} public static Boolean AreObjectsEqual(Object left, Object right, Type targetType) {
if(targetType == null) {
/// <summary> throw new ArgumentNullException(nameof(targetType));
/// Compare if two objects of the same type are equal. }
/// </summary>
/// <param name="left">The left.</param> PropertyInfo[] properties = Runtime.PropertyTypeCache.RetrieveAllProperties(targetType).ToArray();
/// <param name="right">The right.</param>
/// <param name="targetType">Type of the target.</param> foreach(PropertyInfo propertyTarget in properties) {
/// <returns><c>true</c> if the objects are equal; otherwise, <c>false</c>.</returns> Func<Object, Object> targetPropertyGetMethod = propertyTarget.GetCacheGetMethod();
/// <exception cref="ArgumentNullException">targetType.</exception>
public static bool AreObjectsEqual(object left, object right, Type targetType) if(propertyTarget.PropertyType.IsArray) {
{ IEnumerable leftObj = targetPropertyGetMethod(left) as IEnumerable;
if (targetType == null) IEnumerable rightObj = targetPropertyGetMethod(right) as IEnumerable;
throw new ArgumentNullException(nameof(targetType));
if(!AreEnumerationsEquals(leftObj, rightObj)) {
var properties = Runtime.PropertyTypeCache.RetrieveAllProperties(targetType).ToArray(); return false;
}
foreach (var propertyTarget in properties) } else {
{ if(!Equals(targetPropertyGetMethod(left), targetPropertyGetMethod(right))) {
var targetPropertyGetMethod = propertyTarget.GetCacheGetMethod(); return false;
}
if (propertyTarget.PropertyType.IsArray) }
{ }
var leftObj = targetPropertyGetMethod(left) as IEnumerable;
var rightObj = targetPropertyGetMethod(right) as IEnumerable; return true;
}
if (!AreEnumerationsEquals(leftObj, rightObj))
return false; /// <summary>
} /// Compare if two structures of the same type are equal.
else /// </summary>
{ /// <typeparam name="T">The type of structs to compare.</typeparam>
if (!Equals(targetPropertyGetMethod(left), targetPropertyGetMethod(right))) /// <param name="left">The left.</param>
return false; /// <param name="right">The right.</param>
} /// <returns><c>true</c> if the structs are equal; otherwise, <c>false</c>.</returns>
} public static Boolean AreStructsEqual<T>(T left, T right)
where T : struct => AreStructsEqual(left, right, typeof(T));
return true;
} /// <summary>
/// Compare if two structures of the same type are equal.
/// <summary> /// </summary>
/// Compare if two structures of the same type are equal. /// <param name="left">The left.</param>
/// </summary> /// <param name="right">The right.</param>
/// <typeparam name="T">The type of structs to compare.</typeparam> /// <param name="targetType">Type of the target.</param>
/// <param name="left">The left.</param> /// <returns>
/// <param name="right">The right.</param> /// <c>true</c> if the structs are equal; otherwise, <c>false</c>.
/// <returns><c>true</c> if the structs are equal; otherwise, <c>false</c>.</returns> /// </returns>
public static bool AreStructsEqual<T>(T left, T right) /// <exception cref="ArgumentNullException">targetType.</exception>
where T : struct public static Boolean AreStructsEqual(Object left, Object right, Type targetType) {
{ if(targetType == null) {
return AreStructsEqual(left, right, typeof(T)); throw new ArgumentNullException(nameof(targetType));
} }
/// <summary> IEnumerable<MemberInfo> fields = new List<MemberInfo>(Runtime.FieldTypeCache.RetrieveAllFields(targetType))
/// Compare if two structures of the same type are equal. .Union(Runtime.PropertyTypeCache.RetrieveAllProperties(targetType));
/// </summary>
/// <param name="left">The left.</param> foreach(MemberInfo targetMember in fields) {
/// <param name="right">The right.</param> switch(targetMember) {
/// <param name="targetType">Type of the target.</param> case FieldInfo field:
/// <returns> if(Equals(field.GetValue(left), field.GetValue(right)) == false) {
/// <c>true</c> if the structs are equal; otherwise, <c>false</c>. return false;
/// </returns> }
/// <exception cref="ArgumentNullException">targetType.</exception>
public static bool AreStructsEqual(object left, object right, Type targetType) break;
{ case PropertyInfo property:
if (targetType == null) Func<Object, Object> targetPropertyGetMethod = property.GetCacheGetMethod();
throw new ArgumentNullException(nameof(targetType));
if(targetPropertyGetMethod != null &&
var fields = new List<MemberInfo>(Runtime.FieldTypeCache.RetrieveAllFields(targetType)) !Equals(targetPropertyGetMethod(left), targetPropertyGetMethod(right))) {
.Union(Runtime.PropertyTypeCache.RetrieveAllProperties(targetType)); return false;
}
foreach (var targetMember in fields)
{ break;
switch (targetMember) }
{ }
case FieldInfo field:
if (Equals(field.GetValue(left), field.GetValue(right)) == false) return true;
return false; }
break;
case PropertyInfo property: /// <summary>
var targetPropertyGetMethod = property.GetCacheGetMethod(); /// Compare if two enumerables are equal.
/// </summary>
if (targetPropertyGetMethod != null && /// <typeparam name="T">The type of enums to compare.</typeparam>
!Equals(targetPropertyGetMethod(left), targetPropertyGetMethod(right))) /// <param name="left">The left.</param>
return false; /// <param name="right">The right.</param>
break; /// <returns>
} /// True if two specified types are equal; otherwise, false.
} /// </returns>
/// <exception cref="ArgumentNullException">
return true; /// left
} /// or
/// right.
/// <summary> /// </exception>
/// Compare if two enumerables are equal. public static Boolean AreEnumerationsEquals<T>(T left, T right)
/// </summary> where T : IEnumerable {
/// <typeparam name="T">The type of enums to compare.</typeparam> if(Equals(left, default(T))) {
/// <param name="left">The left.</param> throw new ArgumentNullException(nameof(left));
/// <param name="right">The right.</param> }
/// <returns>
/// True if two specified types are equal; otherwise, false. if(Equals(right, default(T))) {
/// </returns> throw new ArgumentNullException(nameof(right));
/// <exception cref="ArgumentNullException"> }
/// left
/// or Object[] leftEnumerable = left.Cast<Object>().ToArray();
/// right. Object[] rightEnumerable = right.Cast<Object>().ToArray();
/// </exception>
public static bool AreEnumerationsEquals<T>(T left, T right) if(leftEnumerable.Length != rightEnumerable.Length) {
where T : IEnumerable return false;
{ }
if (Equals(left, default(T)))
throw new ArgumentNullException(nameof(left)); for(Int32 i = 0; i < leftEnumerable.Length; i++) {
Object leftEl = leftEnumerable[i];
if (Equals(right, default(T))) Object rightEl = rightEnumerable[i];
throw new ArgumentNullException(nameof(right));
if(!AreEqual(leftEl, rightEl, leftEl.GetType())) {
var leftEnumerable = left.Cast<object>().ToArray(); return false;
var rightEnumerable = right.Cast<object>().ToArray(); }
}
if (leftEnumerable.Length != rightEnumerable.Length)
return false; return true;
}
for (var i = 0; i < leftEnumerable.Length; i++) }
{
var leftEl = leftEnumerable[i];
var rightEl = rightEnumerable[i];
if (!AreEqual(leftEl, rightEl, leftEl.GetType()))
{
return false;
}
}
return true;
}
}
} }
+113 -113
View File
@@ -1,116 +1,116 @@
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>
/// Represents an object map.
/// </summary>
/// <typeparam name="TSource">The type of the source.</typeparam>
/// <typeparam name="TDestination">The type of the destination.</typeparam>
/// <seealso cref="Unosquare.Swan.Abstractions.IObjectMap" />
public class ObjectMap<TSource, TDestination> : IObjectMap {
internal ObjectMap(IEnumerable<PropertyInfo> intersect) {
this.SourceType = typeof(TSource);
this.DestinationType = typeof(TDestination);
this.Map = intersect.ToDictionary(
property => this.DestinationType.GetProperty(property.Name),
property => new List<PropertyInfo> { this.SourceType.GetProperty(property.Name) });
}
/// <inheritdoc/>
public Dictionary<PropertyInfo, List<PropertyInfo>> Map {
get;
}
/// <inheritdoc/>
public Type SourceType {
get;
}
/// <inheritdoc/>
public Type DestinationType {
get;
}
/// <summary> /// <summary>
/// Represents an object map. /// Maps the property.
/// </summary> /// </summary>
/// <typeparam name="TSource">The type of the source.</typeparam> /// <typeparam name="TDestinationProperty">The type of the destination property.</typeparam>
/// <typeparam name="TDestination">The type of the destination.</typeparam> /// <typeparam name="TSourceProperty">The type of the source property.</typeparam>
/// <seealso cref="Unosquare.Swan.Abstractions.IObjectMap" /> /// <param name="destinationProperty">The destination property.</param>
public class ObjectMap<TSource, TDestination> : IObjectMap /// <param name="sourceProperty">The source property.</param>
{ /// <returns>
internal ObjectMap(IEnumerable<PropertyInfo> intersect) /// An object map representation of type of the destination property
{ /// and type of the source property.
SourceType = typeof(TSource); /// </returns>
DestinationType = typeof(TDestination); public ObjectMap<TSource, TDestination> MapProperty
Map = intersect.ToDictionary( <TDestinationProperty, TSourceProperty>(
property => DestinationType.GetProperty(property.Name), Expression<Func<TDestination, TDestinationProperty>> destinationProperty,
property => new List<PropertyInfo> {SourceType.GetProperty(property.Name)}); Expression<Func<TSource, TSourceProperty>> sourceProperty) {
} PropertyInfo propertyDestinationInfo = (destinationProperty.Body as MemberExpression)?.Member as PropertyInfo;
/// <inheritdoc/> if(propertyDestinationInfo == null) {
public Dictionary<PropertyInfo, List<PropertyInfo>> Map { get; } throw new ArgumentException("Invalid destination expression", nameof(destinationProperty));
}
/// <inheritdoc/>
public Type SourceType { get; } List<PropertyInfo> sourceMembers = GetSourceMembers(sourceProperty);
/// <inheritdoc/> if(sourceMembers.Any() == false) {
public Type DestinationType { get; } throw new ArgumentException("Invalid source expression", nameof(sourceProperty));
}
/// <summary>
/// Maps the property. // reverse order
/// </summary> sourceMembers.Reverse();
/// <typeparam name="TDestinationProperty">The type of the destination property.</typeparam> this.Map[propertyDestinationInfo] = sourceMembers;
/// <typeparam name="TSourceProperty">The type of the source property.</typeparam>
/// <param name="destinationProperty">The destination property.</param> return this;
/// <param name="sourceProperty">The source property.</param> }
/// <returns>
/// An object map representation of type of the destination property /// <summary>
/// and type of the source property. /// Removes the map property.
/// </returns> /// </summary>
public ObjectMap<TSource, TDestination> MapProperty /// <typeparam name="TDestinationProperty">The type of the destination property.</typeparam>
<TDestinationProperty, TSourceProperty>( /// <param name="destinationProperty">The destination property.</param>
Expression<Func<TDestination, TDestinationProperty>> destinationProperty, /// <returns>
Expression<Func<TSource, TSourceProperty>> sourceProperty) /// An object map representation of type of the destination property
{ /// and type of the source property.
var propertyDestinationInfo = (destinationProperty.Body as MemberExpression)?.Member as PropertyInfo; /// </returns>
/// <exception cref="System.Exception">Invalid destination expression.</exception>
if (propertyDestinationInfo == null) public ObjectMap<TSource, TDestination> RemoveMapProperty<TDestinationProperty>(
{ Expression<Func<TDestination, TDestinationProperty>> destinationProperty) {
throw new ArgumentException("Invalid destination expression", nameof(destinationProperty)); PropertyInfo propertyDestinationInfo = (destinationProperty.Body as MemberExpression)?.Member as PropertyInfo;
}
if(propertyDestinationInfo == null) {
var sourceMembers = GetSourceMembers(sourceProperty); throw new ArgumentException("Invalid destination expression", nameof(destinationProperty));
}
if (sourceMembers.Any() == false)
{ if(this.Map.ContainsKey(propertyDestinationInfo)) {
throw new ArgumentException("Invalid source expression", nameof(sourceProperty)); _ = this.Map.Remove(propertyDestinationInfo);
} }
// reverse order return this;
sourceMembers.Reverse(); }
Map[propertyDestinationInfo] = sourceMembers;
private static List<PropertyInfo> GetSourceMembers<TSourceProperty>(Expression<Func<TSource, TSourceProperty>> sourceProperty) {
return this; List<PropertyInfo> sourceMembers = new List<PropertyInfo>();
} MemberExpression initialExpression = sourceProperty.Body as MemberExpression;
/// <summary> while(true) {
/// Removes the map property. PropertyInfo propertySourceInfo = initialExpression?.Member as PropertyInfo;
/// </summary>
/// <typeparam name="TDestinationProperty">The type of the destination property.</typeparam> if(propertySourceInfo == null) {
/// <param name="destinationProperty">The destination property.</param> break;
/// <returns> }
/// An object map representation of type of the destination property
/// and type of the source property. sourceMembers.Add(propertySourceInfo);
/// </returns> initialExpression = initialExpression.Expression as MemberExpression;
/// <exception cref="System.Exception">Invalid destination expression.</exception> }
public ObjectMap<TSource, TDestination> RemoveMapProperty<TDestinationProperty>(
Expression<Func<TDestination, TDestinationProperty>> destinationProperty) return sourceMembers;
{ }
var propertyDestinationInfo = (destinationProperty.Body as MemberExpression)?.Member as PropertyInfo; }
if (propertyDestinationInfo == null)
throw new ArgumentException("Invalid destination expression", nameof(destinationProperty));
if (Map.ContainsKey(propertyDestinationInfo))
{
Map.Remove(propertyDestinationInfo);
}
return this;
}
private static List<PropertyInfo> GetSourceMembers<TSourceProperty>(Expression<Func<TSource, TSourceProperty>> sourceProperty)
{
var sourceMembers = new List<PropertyInfo>();
var initialExpression = sourceProperty.Body as MemberExpression;
while (true)
{
var propertySourceInfo = initialExpression?.Member as PropertyInfo;
if (propertySourceInfo == null) break;
sourceMembers.Add(propertySourceInfo);
initialExpression = initialExpression.Expression as MemberExpression;
}
return sourceMembers;
}
}
} }
+374 -400
View File
@@ -1,411 +1,385 @@
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>
/// Represents an AutoMapper-like object to map from one object type
/// to another using defined properties map or using the default behaviour
/// to copy same named properties from one object to another.
///
/// The extension methods like CopyPropertiesTo use the default behaviour.
/// </summary>
/// <example>
/// The following code explains how to map an object's properties into an instance of type T.
/// <code>
/// using Unosquare.Swan
///
/// class Example
/// {
/// class Person
/// {
/// public string Name { get; set; }
/// public int Age { get; set; }
/// }
///
/// static void Main()
/// {
/// var obj = new { Name = "Søren", Age = 42 };
///
/// var person = Runtime.ObjectMapper.Map&lt;Person&gt;(obj);
/// }
/// }
/// </code>
/// The following code explains how to explicitly map certain properties.
/// <code>
/// using Unosquare.Swan
///
/// class Example
/// {
/// class User
/// {
/// public string Name { get; set; }
/// public Role Role { get; set; }
/// }
///
/// public class Role
/// {
/// public string Name { get; set; }
/// }
///
/// class UserDto
/// {
/// public string Name { get; set; }
/// public string Role { get; set; }
/// }
///
/// static void Main()
/// {
/// // create a User object
/// var person =
/// new User { Name = "Phillip", Role = new Role { Name = "Admin" } };
///
/// // create an Object Mapper
/// var mapper = new ObjectMapper();
///
/// // map the User's Role.Name to UserDto's Role
/// mapper.CreateMap&lt;User, UserDto&gt;()
/// .MapProperty(d => d.Role, x => x.Role.Name);
///
/// // apply the previous map and retrieve a UserDto object
/// var destination = mapper.Map&lt;UserDto&gt;(person);
/// }
/// }
/// </code>
/// </example>
public class ObjectMapper {
private readonly List<IObjectMap> _maps = new List<IObjectMap>();
/// <summary> /// <summary>
/// Represents an AutoMapper-like object to map from one object type /// Copies the specified source.
/// to another using defined properties map or using the default behaviour
/// to copy same named properties from one object to another.
///
/// The extension methods like CopyPropertiesTo use the default behaviour.
/// </summary> /// </summary>
/// <example> /// <param name="source">The source.</param>
/// The following code explains how to map an object's properties into an instance of type T. /// <param name="target">The target.</param>
/// <code> /// <param name="propertiesToCopy">The properties to copy.</param>
/// using Unosquare.Swan /// <param name="ignoreProperties">The ignore properties.</param>
/// /// <returns>
/// class Example /// Copied properties count.
/// { /// </returns>
/// class Person /// <exception cref="ArgumentNullException">
/// { /// source
/// public string Name { get; set; } /// or
/// public int Age { get; set; } /// target.
/// } /// </exception>
/// public static Int32 Copy(
/// static void Main() Object source,
/// { Object target,
/// var obj = new { Name = "Søren", Age = 42 }; String[] propertiesToCopy = null,
/// String[] ignoreProperties = null) {
/// var person = Runtime.ObjectMapper.Map&lt;Person&gt;(obj); if(source == null) {
/// } throw new ArgumentNullException(nameof(source));
/// } }
/// </code>
/// The following code explains how to explicitly map certain properties. if(target == null) {
/// <code> throw new ArgumentNullException(nameof(target));
/// using Unosquare.Swan }
///
/// class Example return Copy(
/// {
/// class User
/// {
/// public string Name { get; set; }
/// public Role Role { get; set; }
/// }
///
/// public class Role
/// {
/// public string Name { get; set; }
/// }
///
/// class UserDto
/// {
/// public string Name { get; set; }
/// public string Role { get; set; }
/// }
///
/// static void Main()
/// {
/// // create a User object
/// var person =
/// new User { Name = "Phillip", Role = new Role { Name = "Admin" } };
///
/// // create an Object Mapper
/// var mapper = new ObjectMapper();
///
/// // map the User's Role.Name to UserDto's Role
/// mapper.CreateMap&lt;User, UserDto&gt;()
/// .MapProperty(d => d.Role, x => x.Role.Name);
///
/// // apply the previous map and retrieve a UserDto object
/// var destination = mapper.Map&lt;UserDto&gt;(person);
/// }
/// }
/// </code>
/// </example>
public class ObjectMapper
{
private readonly List<IObjectMap> _maps = new List<IObjectMap>();
/// <summary>
/// Copies the specified source.
/// </summary>
/// <param name="source">The source.</param>
/// <param name="target">The target.</param>
/// <param name="propertiesToCopy">The properties to copy.</param>
/// <param name="ignoreProperties">The ignore properties.</param>
/// <returns>
/// Copied properties count.
/// </returns>
/// <exception cref="ArgumentNullException">
/// source
/// or
/// target.
/// </exception>
public static int Copy(
object source,
object target,
string[] propertiesToCopy = null,
string[] ignoreProperties = null)
{
if (source == null)
throw new ArgumentNullException(nameof(source));
if (target == null)
throw new ArgumentNullException(nameof(target));
return Copy(
target, target,
propertiesToCopy, propertiesToCopy,
ignoreProperties, ignoreProperties,
GetSourceMap(source)); GetSourceMap(source));
} }
/// <summary> /// <summary>
/// Copies the specified source. /// Copies the specified source.
/// </summary> /// </summary>
/// <param name="source">The source.</param> /// <param name="source">The source.</param>
/// <param name="target">The target.</param> /// <param name="target">The target.</param>
/// <param name="propertiesToCopy">The properties to copy.</param> /// <param name="propertiesToCopy">The properties to copy.</param>
/// <param name="ignoreProperties">The ignore properties.</param> /// <param name="ignoreProperties">The ignore properties.</param>
/// <returns> /// <returns>
/// Copied properties count. /// Copied properties count.
/// </returns> /// </returns>
/// <exception cref="ArgumentNullException"> /// <exception cref="ArgumentNullException">
/// source /// source
/// 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,
propertiesToCopy, propertiesToCopy,
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>
/// Creates the map. /// Creates the 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>
/// <returns> /// <returns>
/// An object map representation of type of the destination property /// An object map representation of type of the destination property
/// and type of the source property. /// and type of the source property.
/// </returns> /// </returns>
/// <exception cref="System.InvalidOperationException"> /// <exception cref="System.InvalidOperationException">
/// You can't create an existing map /// You can't create an existing map
/// 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");
} IEnumerable<PropertyInfo> sourceType = Runtime.PropertyTypeCache.RetrieveAllProperties<TSource>(true);
IEnumerable<PropertyInfo> destinationType = Runtime.PropertyTypeCache.RetrieveAllProperties<TDestination>(true);
var sourceType = Runtime.PropertyTypeCache.RetrieveAllProperties<TSource>(true);
var destinationType = Runtime.PropertyTypeCache.RetrieveAllProperties<TDestination>(true); PropertyInfo[] intersect = sourceType.Intersect(destinationType, new PropertyInfoComparer()).ToArray();
var intersect = sourceType.Intersect(destinationType, new PropertyInfoComparer()).ToArray(); if(intersect.Any() == false) {
throw new InvalidOperationException("Types doesn't match");
if (intersect.Any() == false) }
{
throw new InvalidOperationException("Types doesn't match"); ObjectMap<TSource, TDestination> map = new ObjectMap<TSource, TDestination>(intersect);
}
this._maps.Add(map);
var map = new ObjectMap<TSource, TDestination>(intersect);
return map;
_maps.Add(map); }
return map; /// <summary>
} /// Maps the specified source.
/// </summary>
/// <summary> /// <typeparam name="TDestination">The type of the destination.</typeparam>
/// Maps the specified source. /// <param name="source">The source.</param>
/// </summary> /// <param name="autoResolve">if set to <c>true</c> [automatic resolve].</param>
/// <typeparam name="TDestination">The type of the destination.</typeparam> /// <returns>
/// <param name="source">The source.</param> /// A new instance of the map.
/// <param name="autoResolve">if set to <c>true</c> [automatic resolve].</param> /// </returns>
/// <returns> /// <exception cref="ArgumentNullException">source.</exception>
/// A new instance of the map. /// <exception cref="InvalidOperationException">You can't map from type {source.GetType().Name} to {typeof(TDestination).Name}.</exception>
/// </returns> public TDestination Map<TDestination>(Object source, Boolean autoResolve = true) {
/// <exception cref="ArgumentNullException">source.</exception> if(source == null) {
/// <exception cref="InvalidOperationException">You can't map from type {source.GetType().Name} to {typeof(TDestination).Name}.</exception> throw new ArgumentNullException(nameof(source));
public TDestination Map<TDestination>(object source, bool autoResolve = true) }
{
if (source == null) TDestination destination = Activator.CreateInstance<TDestination>();
{ IObjectMap map = this._maps
throw new ArgumentNullException(nameof(source)); .FirstOrDefault(x => x.SourceType == source.GetType() && x.DestinationType == typeof(TDestination));
}
if(map != null) {
var destination = Activator.CreateInstance<TDestination>(); foreach(KeyValuePair<PropertyInfo, List<PropertyInfo>> property in map.Map) {
var map = _maps Object finalSource = property.Value.Aggregate(source,
.FirstOrDefault(x => x.SourceType == source.GetType() && x.DestinationType == typeof(TDestination)); (current, sourceProperty) => sourceProperty.GetValue(current));
if (map != null) property.Key.SetValue(destination, finalSource);
{ }
foreach (var property in map.Map) } else {
{ if(!autoResolve) {
var finalSource = property.Value.Aggregate(source, throw new InvalidOperationException(
(current, sourceProperty) => sourceProperty.GetValue(current)); $"You can't map from type {source.GetType().Name} to {typeof(TDestination).Name}");
}
property.Key.SetValue(destination, finalSource);
} // Missing mapping, try to use default behavior
} _ = Copy(source, destination);
else }
{
if (!autoResolve) return destination;
{ }
throw new InvalidOperationException(
$"You can't map from type {source.GetType().Name} to {typeof(TDestination).Name}"); private static Int32 Copy(
} Object target,
IEnumerable<String> propertiesToCopy,
// Missing mapping, try to use default behavior IEnumerable<String> ignoreProperties,
Copy(source, destination); Dictionary<String, TypeValuePair> sourceProperties) {
} // Filter properties
IEnumerable<String> requiredProperties = propertiesToCopy?
return destination; .Where(p => !String.IsNullOrWhiteSpace(p))
} .Select(p => p.ToLowerInvariant());
private static int Copy( IEnumerable<String> ignoredProperties = ignoreProperties?
object target, .Where(p => !String.IsNullOrWhiteSpace(p))
IEnumerable<string> propertiesToCopy, .Select(p => p.ToLowerInvariant());
IEnumerable<string> ignoreProperties,
Dictionary<string, TypeValuePair> sourceProperties) IEnumerable<PropertyInfo> properties = Runtime.PropertyTypeCache
{ .RetrieveFilteredProperties(target.GetType(), true, x => x.CanWrite);
// Filter properties
var requiredProperties = propertiesToCopy? return properties
.Where(p => !string.IsNullOrWhiteSpace(p)) .Select(x => x.Name)
.Select(p => p.ToLowerInvariant()); .Distinct()
.ToDictionary(x => x.ToLowerInvariant(), x => properties.First(y => y.Name == x))
var ignoredProperties = ignoreProperties? .Where(x => sourceProperties.Keys.Contains(x.Key))
.Where(p => !string.IsNullOrWhiteSpace(p)) .When(() => requiredProperties != null, q => q.Where(y => requiredProperties.Contains(y.Key)))
.Select(p => p.ToLowerInvariant()); .When(() => ignoredProperties != null, q => q.Where(y => !ignoredProperties.Contains(y.Key)))
.ToDictionary(x => x.Value, x => sourceProperties[x.Key])
var properties = Runtime.PropertyTypeCache .Sum(x => TrySetValue(x, target) ? 1 : 0);
.RetrieveFilteredProperties(target.GetType(), true, x => x.CanWrite); }
return properties private static Boolean TrySetValue(KeyValuePair<PropertyInfo, TypeValuePair> property, Object target) {
.Select(x => x.Name) try {
.Distinct() SetValue(property, target);
.ToDictionary(x => x.ToLowerInvariant(), x => properties.First(y => y.Name == x)) return true;
.Where(x => sourceProperties.Keys.Contains(x.Key)) } catch {
.When(() => requiredProperties != null, q => q.Where(y => requiredProperties.Contains(y.Key))) // swallow
.When(() => ignoredProperties != null, q => q.Where(y => !ignoredProperties.Contains(y.Key))) }
.ToDictionary(x => x.Value, x => sourceProperties[x.Key])
.Sum(x => TrySetValue(x, target) ? 1 : 0); return false;
} }
private static bool TrySetValue(KeyValuePair<PropertyInfo, TypeValuePair> property, object target) private static void SetValue(KeyValuePair<PropertyInfo, TypeValuePair> property, Object target) {
{ if(property.Value.Type.GetTypeInfo().IsEnum) {
try property.Key.SetValue(target,
{ Enum.ToObject(property.Key.PropertyType, property.Value.Value));
SetValue(property, target);
return true; return;
} }
catch
{ if(!property.Value.Type.IsValueType() && property.Key.PropertyType == property.Value.Type) {
// swallow property.Key.SetValue(target, GetValue(property.Value.Value, property.Key.PropertyType));
}
return;
return false; }
}
if(property.Key.PropertyType == typeof(Boolean)) {
private static void SetValue(KeyValuePair<PropertyInfo, TypeValuePair> property, object target) property.Key.SetValue(target,
{ Convert.ToBoolean(property.Value.Value));
if (property.Value.Type.GetTypeInfo().IsEnum)
{ return;
property.Key.SetValue(target, }
Enum.ToObject(property.Key.PropertyType, property.Value.Value));
_ = property.Key.TrySetBasicType(property.Value.Value, target);
return; }
}
private static Object GetValue(Object source, Type targetType) {
if (!property.Value.Type.IsValueType() && property.Key.PropertyType == property.Value.Type) if(source == null) {
{ return null;
property.Key.SetValue(target, GetValue(property.Value.Value, property.Key.PropertyType)); }
return; Object target = null;
}
source.CreateTarget(targetType, false, ref target);
if (property.Key.PropertyType == typeof(bool))
{ switch(source) {
property.Key.SetValue(target, case String _:
Convert.ToBoolean(property.Value.Value)); target = source;
break;
return; case IList sourceList when target is Array targetArray:
} for(Int32 i = 0; i < sourceList.Count; i++) {
try {
property.Key.TrySetBasicType(property.Value.Value, target); targetArray.SetValue(
} sourceList[i].GetType().IsValueType()
? sourceList[i]
private static object GetValue(object source, Type targetType) : sourceList[i].CopyPropertiesToNew<Object>(), i);
{ } catch {
if (source == null) // ignored
return null; }
}
object target = null;
break;
source.CreateTarget(targetType, false, ref target); case IList sourceList when target is IList targetList:
MethodInfo addMethod = targetType.GetMethods()
switch (source) .FirstOrDefault(
{ m => m.Name.Equals(Formatters.Json.AddMethodName) && m.IsPublic &&
case string _: m.GetParameters().Length == 1);
target = source;
break; if(addMethod == null) {
case IList sourceList when target is Array targetArray: return target;
for (var i = 0; i < sourceList.Count; i++) }
{
try foreach(Object item in sourceList) {
{ try {
targetArray.SetValue( _ = targetList.Add(item.GetType().IsValueType()
sourceList[i].GetType().IsValueType() ? item
? sourceList[i] : item.CopyPropertiesToNew<Object>());
: sourceList[i].CopyPropertiesToNew<object>(), i); } catch {
} // ignored
catch }
{ }
// ignored
} break;
} default:
_ = source.CopyPropertiesTo(target);
break; break;
case IList sourceList when target is IList targetList: }
var addMethod = targetType.GetMethods()
.FirstOrDefault( return target;
m => m.Name.Equals(Formatters.Json.AddMethodName) && m.IsPublic && }
m.GetParameters().Length == 1);
private static Dictionary<String, TypeValuePair> GetSourceMap(Object source) {
if (addMethod == null) return target; // select distinct properties because they can be duplicated by inheritance
PropertyInfo[] sourceProperties = Runtime.PropertyTypeCache
foreach (var item in sourceList) .RetrieveFilteredProperties(source.GetType(), true, x => x.CanRead)
{ .ToArray();
try
{ return sourceProperties
targetList.Add(item.GetType().IsValueType() .Select(x => x.Name)
? item .Distinct()
: item.CopyPropertiesToNew<object>()); .ToDictionary(
} x => x.ToLowerInvariant(),
catch x => new TypeValuePair(sourceProperties.First(y => y.Name == x).PropertyType,
{ sourceProperties.First(y => y.Name == x).GetValue(source)));
// ignored }
}
} internal class TypeValuePair {
public TypeValuePair(Type type, Object value) {
break; this.Type = type;
default: this.Value = value;
source.CopyPropertiesTo(target); }
break;
} public Type Type {
get;
return target; }
}
public Object Value {
private static Dictionary<string, TypeValuePair> GetSourceMap(object source) get;
{ }
// select distinct properties because they can be duplicated by inheritance }
var sourceProperties = Runtime.PropertyTypeCache
.RetrieveFilteredProperties(source.GetType(), true, x => x.CanRead) internal class PropertyInfoComparer : IEqualityComparer<PropertyInfo> {
.ToArray(); public Boolean Equals(PropertyInfo x, PropertyInfo y)
=> x != null && y != null && x.Name == y.Name && x.PropertyType == y.PropertyType;
return sourceProperties
.Select(x => x.Name) public Int32 GetHashCode(PropertyInfo obj)
.Distinct() => obj.Name.GetHashCode() + obj.PropertyType.Name.GetHashCode();
.ToDictionary( }
x => x.ToLowerInvariant(), }
x => new TypeValuePair(sourceProperties.First(y => y.Name == x).PropertyType,
sourceProperties.First(y => y.Name == x).GetValue(source)));
}
internal class TypeValuePair
{
public TypeValuePair(Type type, object value)
{
Type = type;
Value = value;
}
public Type Type { get; }
public object Value { get; }
}
internal class PropertyInfoComparer : IEqualityComparer<PropertyInfo>
{
public bool Equals(PropertyInfo x, PropertyInfo y)
=> x != null && y != null && x.Name == y.Name && x.PropertyType == y.PropertyType;
public int GetHashCode(PropertyInfo obj)
=> obj.Name.GetHashCode() + obj.PropertyType.Name.GetHashCode();
}
}
} }
+202 -199
View File
@@ -1,204 +1,207 @@
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>
/// Represents an object validator.
/// </summary>
/// <example>
/// The following code describes how to perform a simple object validation.
/// <code>
/// using Unosquare.Swan.Components;
///
/// class Example
/// {
/// public static void Main()
/// {
/// // create an instance of ObjectValidator
/// var obj = new ObjectValidator();
///
/// // Add a validation to the 'Simple' class with a custom error message
/// obj.AddValidator&lt;Simple&gt;(x =>
/// !string.IsNullOrEmpty(x.Name), "Name must not be empty");
///
/// // check if object is valid
/// var res = obj.IsValid(new Simple { Name = "Name" });
/// }
///
/// class Simple
/// {
/// public string Name { get; set; }
/// }
/// }
/// </code>
///
/// The following code shows of to validate an object with a custom validator and some attributes using the Runtime ObjectValidator singleton.
/// <code>
/// using Unosquare.Swan.Components;
///
/// class Example
/// {
/// public static void Main()
/// {
/// // create an instance of ObjectValidator
/// Runtime.ObjectValidator
/// .AddValidator&lt;Simple&gt;(x =>
/// !x.Name.Equals("Name"), "Name must not be 'Name'");
///
/// // validate object
/// var res = Runtime.ObjectValidator
/// .Validate(new Simple{ Name = "name", Number = 5, Email ="email@mail.com"})
/// }
///
/// class Simple
/// {
/// [NotNull]
/// public string Name { get; set; }
///
/// [Range(1, 10)]
/// public int Number { get; set; }
///
/// [Email]
/// public string Email { get; set; }
/// }
/// }
/// </code>
/// </example>
public class ObjectValidator {
private readonly ConcurrentDictionary<Type, List<Tuple<Delegate, String>>> _predicates =
new ConcurrentDictionary<Type, List<Tuple<Delegate, String>>>();
/// <summary> /// <summary>
/// Represents an object validator. /// Validates an object given the specified validators and attributes.
/// </summary> /// </summary>
/// <example> /// <typeparam name="T">The type of the object.</typeparam>
/// The following code describes how to perform a simple object validation. /// <param name="obj">The object.</param>
/// <code> /// <returns cref="ObjectValidationResult">A validation result. </returns>
/// using Unosquare.Swan.Components; public ObjectValidationResult Validate<T>(T obj) {
/// ObjectValidationResult errorList = new ObjectValidationResult();
/// class Example _ = this.ValidateObject(obj, false, errorList.Add);
/// {
/// public static void Main() return errorList;
/// { }
/// // create an instance of ObjectValidator
/// var obj = new ObjectValidator();
///
/// // Add a validation to the 'Simple' class with a custom error message
/// obj.AddValidator&lt;Simple&gt;(x =>
/// !string.IsNullOrEmpty(x.Name), "Name must not be empty");
///
/// // check if object is valid
/// var res = obj.IsValid(new Simple { Name = "Name" });
/// }
///
/// class Simple
/// {
/// public string Name { get; set; }
/// }
/// }
/// </code>
///
/// The following code shows of to validate an object with a custom validator and some attributes using the Runtime ObjectValidator singleton.
/// <code>
/// using Unosquare.Swan.Components;
///
/// class Example
/// {
/// public static void Main()
/// {
/// // create an instance of ObjectValidator
/// Runtime.ObjectValidator
/// .AddValidator&lt;Simple&gt;(x =>
/// !x.Name.Equals("Name"), "Name must not be 'Name'");
///
/// // validate object
/// var res = Runtime.ObjectValidator
/// .Validate(new Simple{ Name = "name", Number = 5, Email ="email@mail.com"})
/// }
///
/// class Simple
/// {
/// [NotNull]
/// public string Name { get; set; }
///
/// [Range(1, 10)]
/// public int Number { get; set; }
///
/// [Email]
/// public string Email { get; set; }
/// }
/// }
/// </code>
/// </example>
public class ObjectValidator
{
private readonly ConcurrentDictionary<Type, List<Tuple<Delegate, string>>> _predicates =
new ConcurrentDictionary<Type, List<Tuple<Delegate, string>>>();
/// <summary>
/// Validates an object given the specified validators and attributes.
/// </summary>
/// <typeparam name="T">The type of the object.</typeparam>
/// <param name="obj">The object.</param>
/// <returns cref="ObjectValidationResult">A validation result. </returns>
public ObjectValidationResult Validate<T>(T obj)
{
var errorList = new ObjectValidationResult();
ValidateObject(obj, false, errorList.Add);
return errorList;
}
/// <summary>
/// Validates an object given the specified validators and attributes.
/// </summary>
/// <typeparam name="T">The type.</typeparam>
/// <param name="obj">The object.</param>
/// <returns>
/// <c>true</c> if the specified object is valid; otherwise, <c>false</c>.
/// </returns>
/// <exception cref="ArgumentNullException">obj.</exception>
public bool IsValid<T>(T obj) => ValidateObject(obj);
/// <summary>
/// Adds a validator to a specific class.
/// </summary>
/// <typeparam name="T">The type of the object.</typeparam>
/// <param name="predicate">The predicate that will be evaluated.</param>
/// <param name="message">The message.</param>
/// <exception cref="ArgumentNullException">
/// predicate
/// or
/// message.
/// </exception>
public void AddValidator<T>(Predicate<T> predicate, string message)
where T : class
{
if (predicate == null)
throw new ArgumentNullException(nameof(predicate));
if (string.IsNullOrEmpty(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));
}
private bool ValidateObject<T>(T obj, bool returnOnError = true, Action<string, string> action = null)
{
if (Equals(obj, null))
throw new ArgumentNullException(nameof(obj));
if (_predicates.ContainsKey(typeof(T)))
{
foreach (var validation in _predicates[typeof(T)])
{
if ((bool) validation.Item1.DynamicInvoke(obj)) continue;
action?.Invoke(string.Empty, validation.Item2);
if (returnOnError) return false;
}
}
var properties = Runtime.AttributeCache.RetrieveFromType<T>(typeof(IValidator));
foreach (var prop in properties)
{
foreach (var attribute in prop.Value)
{
var val = (IValidator) attribute;
if (val.IsValid(prop.Key.GetValue(obj, null))) continue;
action?.Invoke(prop.Key.Name, val.ErrorMessage);
if (returnOnError) return false;
}
}
return true;
}
}
/// <summary> /// <summary>
/// Defines a validation result containing all validation errors and their properties. /// Validates an object given the specified validators and attributes.
/// </summary> /// </summary>
public class ObjectValidationResult /// <typeparam name="T">The type.</typeparam>
{ /// <param name="obj">The object.</param>
/// <summary> /// <returns>
/// A list of errors. /// <c>true</c> if the specified object is valid; otherwise, <c>false</c>.
/// </summary> /// </returns>
public List<ValidationError> Errors { get; set; } = new List<ValidationError>(); /// <exception cref="ArgumentNullException">obj.</exception>
public Boolean IsValid<T>(T obj) => this.ValidateObject(obj);
/// <summary>
/// <c>true</c> if there are no errors; otherwise, <c>false</c>. /// <summary>
/// </summary> /// Adds a validator to a specific class.
public bool IsValid => !Errors.Any(); /// </summary>
/// <typeparam name="T">The type of the object.</typeparam>
/// <summary> /// <param name="predicate">The predicate that will be evaluated.</param>
/// Adds an error with a specified property name. /// <param name="message">The message.</param>
/// </summary> /// <exception cref="ArgumentNullException">
/// <param name="propertyName">The property name.</param> /// predicate
/// <param name="errorMessage">The error message.</param> /// or
public void Add(string propertyName, string errorMessage) => /// message.
Errors.Add(new ValidationError {ErrorMessage = errorMessage, PropertyName = propertyName}); /// </exception>
public void AddValidator<T>(Predicate<T> predicate, String message)
/// <summary> where T : class {
/// Defines a validation error. if(predicate == null) {
/// </summary> throw new ArgumentNullException(nameof(predicate));
public class ValidationError }
{
/// <summary> if(String.IsNullOrEmpty(message)) {
/// The property name. throw new ArgumentNullException(message);
/// </summary> }
public string PropertyName { get; set; }
if(!this._predicates.TryGetValue(typeof(T), out List<Tuple<Delegate, String>> existing)) {
/// <summary> existing = new List<Tuple<Delegate, String>>();
/// The message error. this._predicates[typeof(T)] = existing;
/// </summary> }
public string ErrorMessage { get; set; }
} existing.Add(Tuple.Create((Delegate)predicate, message));
} }
private Boolean ValidateObject<T>(T obj, Boolean returnOnError = true, Action<String, String> action = null) {
if(Equals(obj, null)) {
throw new ArgumentNullException(nameof(obj));
}
if(this._predicates.ContainsKey(typeof(T))) {
foreach(Tuple<Delegate, String> validation in this._predicates[typeof(T)]) {
if((Boolean)validation.Item1.DynamicInvoke(obj)) {
continue;
}
action?.Invoke(String.Empty, validation.Item2);
if(returnOnError) {
return false;
}
}
}
Dictionary<System.Reflection.PropertyInfo, IEnumerable<Object>> properties = Runtime.AttributeCache.RetrieveFromType<T>(typeof(IValidator));
foreach(KeyValuePair<System.Reflection.PropertyInfo, IEnumerable<Object>> prop in properties) {
foreach(Object attribute in prop.Value) {
IValidator val = (IValidator)attribute;
if(val.IsValid(prop.Key.GetValue(obj, null))) {
continue;
}
action?.Invoke(prop.Key.Name, val.ErrorMessage);
if(returnOnError) {
return false;
}
}
}
return true;
}
}
/// <summary>
/// Defines a validation result containing all validation errors and their properties.
/// </summary>
public class ObjectValidationResult {
/// <summary>
/// A list of errors.
/// </summary>
public List<ValidationError> Errors { get; set; } = new List<ValidationError>();
/// <summary>
/// <c>true</c> if there are no errors; otherwise, <c>false</c>.
/// </summary>
public Boolean IsValid => !this.Errors.Any();
/// <summary>
/// Adds an error with a specified property name.
/// </summary>
/// <param name="propertyName">The property name.</param>
/// <param name="errorMessage">The error message.</param>
public void Add(String propertyName, String errorMessage) =>
this.Errors.Add(new ValidationError { ErrorMessage = errorMessage, PropertyName = propertyName });
/// <summary>
/// Defines a validation error.
/// </summary>
public class ValidationError {
/// <summary>
/// The property name.
/// </summary>
public String PropertyName {
get; set;
}
/// <summary>
/// The message error.
/// </summary>
public String ErrorMessage {
get; set;
}
}
}
} }
@@ -1,52 +1,48 @@
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>
/// Provides factory methods to create synchronized reader-writer locks
/// that support a generalized locking and releasing api and syntax.
/// </summary>
public static class SyncLockerFactory {
#region Enums and Interfaces
/// <summary> /// <summary>
/// Provides factory methods to create synchronized reader-writer locks /// Enumerates the locking operations.
/// that support a generalized locking and releasing api and syntax.
/// </summary> /// </summary>
public static class SyncLockerFactory private enum LockHolderType {
{ Read,
#region Enums and Interfaces Write,
}
/// <summary>
/// Enumerates the locking operations. /// <summary>
/// </summary> /// Defines methods for releasing locks.
private enum LockHolderType /// </summary>
{ private interface ISyncReleasable {
Read, /// <summary>
Write, /// Releases the writer lock.
} /// </summary>
void ReleaseWriterLock();
/// <summary>
/// Defines methods for releasing locks. /// <summary>
/// </summary> /// Releases the reader lock.
private interface ISyncReleasable /// </summary>
{ void ReleaseReaderLock();
/// <summary> }
/// Releases the writer lock.
/// </summary> #endregion
void ReleaseWriterLock();
#region Factory Methods
/// <summary>
/// Releases the reader lock. #if !NETSTANDARD1_3
/// </summary> /// <summary>
void ReleaseReaderLock(); /// Creates a reader-writer lock backed by a standard ReaderWriterLock.
} /// </summary>
/// <returns>The synchronized locker.</returns>
#endregion public static ISyncLocker Create() => new SyncLocker();
#region Factory Methods
#if !NETSTANDARD1_3
/// <summary>
/// Creates a reader-writer lock backed by a standard ReaderWriterLock.
/// </summary>
/// <returns>The synchronized locker.</returns>
public static ISyncLocker Create() => new SyncLocker();
#else #else
/// <summary> /// <summary>
/// Creates a reader-writer lock backed by a standard ReaderWriterLockSlim when /// Creates a reader-writer lock backed by a standard ReaderWriterLockSlim when
@@ -55,143 +51,142 @@
/// <returns>The synchronized locker</returns> /// <returns>The synchronized locker</returns>
public static ISyncLocker Create() => new SyncLockerSlim(); public static ISyncLocker Create() => new SyncLockerSlim();
#endif #endif
/// <summary> /// <summary>
/// Creates a reader-writer lock backed by a ReaderWriterLockSlim. /// Creates a reader-writer lock backed by a ReaderWriterLockSlim.
/// </summary> /// </summary>
/// <returns>The synchronized locker.</returns> /// <returns>The synchronized locker.</returns>
public static ISyncLocker CreateSlim() => new SyncLockerSlim(); public static ISyncLocker CreateSlim() => new SyncLockerSlim();
/// <summary> /// <summary>
/// Creates a reader-writer lock. /// Creates a reader-writer lock.
/// </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
#region Private Classes #region Private Classes
/// <summary> /// <summary>
/// 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 Boolean _isDisposed;
private bool _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;
{ this._operation = operation;
_parent = parent; }
_operation = operation;
} /// <inheritdoc />
public void Dispose() {
/// <inheritdoc /> if(this._isDisposed) {
public void Dispose() return;
{ }
if (_isDisposed) return;
_isDisposed = true; this._isDisposed = true;
if (_operation == LockHolderType.Read) if(this._operation == LockHolderType.Read) {
_parent.ReleaseReaderLock(); this._parent.ReleaseReaderLock();
else } else {
_parent.ReleaseWriterLock(); this._parent.ReleaseWriterLock();
} }
} }
}
#if !NETSTANDARD1_3
/// <summary> #if !NETSTANDARD1_3
/// The Sync Locker backed by a ReaderWriterLock. /// <summary>
/// </summary> /// The Sync Locker backed by a ReaderWriterLock.
/// <seealso cref="ISyncLocker" /> /// </summary>
/// <seealso cref="ISyncReleasable" /> /// <seealso cref="ISyncLocker" />
private sealed class SyncLocker : ISyncLocker, ISyncReleasable /// <seealso cref="ISyncReleasable" />
{ private sealed class SyncLocker : ISyncLocker, ISyncReleasable {
private bool _isDisposed; private Boolean _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);
{ return new SyncLockReleaser(this, LockHolderType.Write);
_locker?.AcquireWriterLock(Timeout.Infinite); }
return new SyncLockReleaser(this, LockHolderType.Write);
} /// <inheritdoc />
public void ReleaseWriterLock() => this._locker?.ReleaseWriterLock();
/// <inheritdoc />
public void ReleaseWriterLock() => _locker?.ReleaseWriterLock(); /// <inheritdoc />
public void ReleaseReaderLock() => this._locker?.ReleaseReaderLock();
/// <inheritdoc />
public void ReleaseReaderLock() => _locker?.ReleaseReaderLock(); /// <inheritdoc />
public void Dispose() {
/// <inheritdoc /> if(this._isDisposed) {
public void Dispose() return;
{ }
if (_isDisposed) return;
_isDisposed = true; this._isDisposed = true;
_locker?.ReleaseLock(); _ = this._locker?.ReleaseLock();
_locker = null; this._locker = null;
} }
} }
#endif #endif
/// <summary> /// <summary>
/// The Sync Locker backed by ReaderWriterLockSlim. /// The Sync Locker backed by ReaderWriterLockSlim.
/// </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();
{ return new SyncLockReleaser(this, LockHolderType.Read);
_locker?.EnterReadLock(); }
return new SyncLockReleaser(this, LockHolderType.Read);
} /// <inheritdoc />
public IDisposable AcquireWriterLock() {
/// <inheritdoc /> this._locker?.EnterWriteLock();
public IDisposable AcquireWriterLock() return new SyncLockReleaser(this, LockHolderType.Write);
{ }
_locker?.EnterWriteLock();
return new SyncLockReleaser(this, LockHolderType.Write); /// <inheritdoc />
} public void ReleaseWriterLock() => this._locker?.ExitWriteLock();
/// <inheritdoc /> /// <inheritdoc />
public void ReleaseWriterLock() => _locker?.ExitWriteLock(); public void ReleaseReaderLock() => this._locker?.ExitReadLock();
/// <inheritdoc /> /// <inheritdoc />
public void ReleaseReaderLock() => _locker?.ExitReadLock(); public void Dispose() {
if(this._isDisposed) {
/// <inheritdoc /> return;
public void Dispose() }
{
if (_isDisposed) return; this._isDisposed = true;
_isDisposed = true; this._locker?.Dispose();
_locker?.Dispose(); this._locker = null;
_locker = null; }
} }
}
#endregion
#endregion }
}
} }
+46 -54
View File
@@ -1,63 +1,55 @@
#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>
/// Use this singleton to wait for a specific <c>TimeSpan</c> or time.
///
/// Internally this class will use a <c>Timer</c> and a <c>ManualResetEvent</c> to block until
/// the time condition is satisfied.
/// </summary>
/// <seealso cref="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 IWaitEvent _delayLock = WaitEventFactory.Create(true);
/// <summary> /// <summary>
/// Use this singleton to wait for a specific <c>TimeSpan</c> or time. /// Initializes a new instance of the <see cref="TimerControl"/> class.
///
/// Internally this class will use a <c>Timer</c> and a <c>ManualResetEvent</c> to block until
/// the time condition is satisfied.
/// </summary> /// </summary>
/// <seealso cref="SingletonBase{TimerControl}" /> protected TimerControl() => this._innerTimer = new Timer(
public class TimerControl : SingletonBase<TimerControl> x => {
{ try {
private readonly Timer _innerTimer; this._delayLock.Complete();
private readonly IWaitEvent _delayLock = WaitEventFactory.Create(true); this._delayLock.Begin();
} catch {
/// <summary> // ignore
/// Initializes a new instance of the <see cref="TimerControl"/> class. }
/// </summary>
protected TimerControl()
{
_innerTimer = new Timer(
x =>
{
try
{
_delayLock.Complete();
_delayLock.Begin();
}
catch
{
// 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) {
{ this._delayLock.Wait();
while (!ct.IsCancellationRequested && DateTime.UtcNow < untilDate) }
_delayLock.Wait(); }
}
/// <summary>
/// <summary> /// Waits the specified wait time.
/// Waits the specified wait time. /// </summary>
/// </summary> /// <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) => this.WaitUntil(DateTime.UtcNow.Add(waitTime), ct);
WaitUntil(DateTime.UtcNow.Add(waitTime), ct); }
}
} }
#endif #endif
+192 -218
View File
@@ -1,222 +1,196 @@
#if !NETSTANDARD1_3 
namespace Unosquare.Swan.Components
{ #if !NETSTANDARD1_3
using System; using System;
using System.Threading; using System.Threading;
using Abstractions; using Unosquare.Swan.Abstractions;
namespace Unosquare.Swan.Components {
/// <summary>
/// Provides a Manual Reset Event factory with a unified API.
/// </summary>
/// <example>
/// The following example shows how to use the WaitEventFactory class.
/// <code>
/// using Unosquare.Swan.Components;
///
/// public class Example
/// {
/// // create a WaitEvent using the slim version
/// private static readonly IWaitEvent waitEvent = WaitEventFactory.CreateSlim(false);
///
/// public static void Main()
/// {
/// Task.Factory.StartNew(() =>
/// {
/// DoWork(1);
/// });
///
/// Task.Factory.StartNew(() =>
/// {
/// DoWork(2);
/// });
///
/// // send first signal
/// waitEvent.Complete();
/// waitEvent.Begin();
///
/// Thread.Sleep(TimeSpan.FromSeconds(2));
///
/// // send second signal
/// waitEvent.Complete();
///
/// Console.Readline();
/// }
///
/// public static void DoWork(int taskNumber)
/// {
/// $"Data retrieved:{taskNumber}".WriteLine();
/// waitEvent.Wait();
///
/// Thread.Sleep(TimeSpan.FromSeconds(2));
/// $"All finished up {taskNumber}".WriteLine();
/// }
/// }
/// </code>
/// </example>
public static class WaitEventFactory {
#region Factory Methods
/// <summary> /// <summary>
/// Provides a Manual Reset Event factory with a unified API. /// Creates a Wait Event backed by a standard ManualResetEvent.
/// </summary> /// </summary>
/// <example> /// <param name="isCompleted">if initially set to completed. Generally true.</param>
/// The following example shows how to use the WaitEventFactory class. /// <returns>The Wait Event.</returns>
/// <code> public static IWaitEvent Create(Boolean isCompleted) => new WaitEvent(isCompleted);
/// using Unosquare.Swan.Components;
/// /// <summary>
/// public class Example /// Creates a Wait Event backed by a ManualResetEventSlim.
/// { /// </summary>
/// // create a WaitEvent using the slim version /// <param name="isCompleted">if initially set to completed. Generally true.</param>
/// private static readonly IWaitEvent waitEvent = WaitEventFactory.CreateSlim(false); /// <returns>The Wait Event.</returns>
/// public static IWaitEvent CreateSlim(Boolean isCompleted) => new WaitEventSlim(isCompleted);
/// public static void Main()
/// { /// <summary>
/// Task.Factory.StartNew(() => /// Creates a Wait Event backed by a ManualResetEventSlim.
/// { /// </summary>
/// DoWork(1); /// <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>
/// /// <returns>The Wait Event.</returns>
/// Task.Factory.StartNew(() => public static IWaitEvent Create(Boolean isCompleted, Boolean useSlim) => useSlim ? CreateSlim(isCompleted) : Create(isCompleted);
/// {
/// DoWork(2); #endregion
/// });
/// #region Backing Classes
/// // send first signal
/// waitEvent.Complete(); /// <summary>
/// waitEvent.Begin(); /// Defines a WaitEvent backed by a ManualResetEvent.
/// /// </summary>
/// Thread.Sleep(TimeSpan.FromSeconds(2)); private class WaitEvent : IWaitEvent {
/// private ManualResetEvent _event;
/// // send second signal
/// waitEvent.Complete(); /// <summary>
/// /// Initializes a new instance of the <see cref="WaitEvent"/> class.
/// Console.Readline(); /// </summary>
/// } /// <param name="isCompleted">if set to <c>true</c> [is completed].</param>
/// public WaitEvent(Boolean isCompleted) => this._event = new ManualResetEvent(isCompleted);
/// public static void DoWork(int taskNumber)
/// { /// <inheritdoc />
/// $"Data retrieved:{taskNumber}".WriteLine(); public Boolean IsDisposed {
/// waitEvent.Wait(); get; private set;
/// }
/// Thread.Sleep(TimeSpan.FromSeconds(2));
/// $"All finished up {taskNumber}".WriteLine(); /// <inheritdoc />
/// } public Boolean IsValid => this.IsDisposed || this._event == null
/// } ? false
/// </code> : this._event?.SafeWaitHandle?.IsClosed ?? true ? false : !(this._event?.SafeWaitHandle?.IsInvalid ?? true);
/// </example>
public static class WaitEventFactory /// <inheritdoc />
{ public Boolean IsCompleted => this.IsValid == false ? true : this._event?.WaitOne(0) ?? true;
#region Factory Methods
/// <inheritdoc />
/// <summary> public Boolean IsInProgress => !this.IsCompleted;
/// Creates a Wait Event backed by a standard ManualResetEvent.
/// </summary> /// <inheritdoc />
/// <param name="isCompleted">if initially set to completed. Generally true.</param> public void Begin() => this._event?.Reset();
/// <returns>The Wait Event.</returns>
public static IWaitEvent Create(bool isCompleted) => new WaitEvent(isCompleted); /// <inheritdoc />
public void Complete() => this._event?.Set();
/// <summary>
/// Creates a Wait Event backed by a ManualResetEventSlim. /// <inheritdoc />
/// </summary> void IDisposable.Dispose() {
/// <param name="isCompleted">if initially set to completed. Generally true.</param> if(this.IsDisposed) {
/// <returns>The Wait Event.</returns> return;
public static IWaitEvent CreateSlim(bool isCompleted) => new WaitEventSlim(isCompleted); }
/// <summary> this.IsDisposed = true;
/// Creates a Wait Event backed by a ManualResetEventSlim.
/// </summary> _ = this._event?.Set();
/// <param name="isCompleted">if initially set to completed. Generally true.</param> this._event?.Dispose();
/// <param name="useSlim">if set to <c>true</c> creates a slim version of the wait event.</param> this._event = null;
/// <returns>The Wait Event.</returns> }
public static IWaitEvent Create(bool isCompleted, bool useSlim) => useSlim ? CreateSlim(isCompleted) : Create(isCompleted);
/// <inheritdoc />
#endregion public void Wait() => this._event?.WaitOne();
#region Backing Classes /// <inheritdoc />
public Boolean Wait(TimeSpan timeout) => this._event?.WaitOne(timeout) ?? true;
/// <summary> }
/// Defines a WaitEvent backed by a ManualResetEvent.
/// </summary> /// <summary>
private class WaitEvent : IWaitEvent /// Defines a WaitEvent backed by a ManualResetEventSlim.
{ /// </summary>
private ManualResetEvent _event; private class WaitEventSlim : IWaitEvent {
private ManualResetEventSlim _event;
/// <summary>
/// Initializes a new instance of the <see cref="WaitEvent"/> class. /// <summary>
/// </summary> /// Initializes a new instance of the <see cref="WaitEventSlim"/> class.
/// <param name="isCompleted">if set to <c>true</c> [is completed].</param> /// </summary>
public WaitEvent(bool isCompleted) /// <param name="isCompleted">if set to <c>true</c> [is completed].</param>
{ public WaitEventSlim(Boolean isCompleted) => this._event = new ManualResetEventSlim(isCompleted);
_event = new ManualResetEvent(isCompleted);
} /// <inheritdoc />
public Boolean IsDisposed {
/// <inheritdoc /> get; private set;
public bool IsDisposed { get; private set; } }
/// <inheritdoc /> /// <inheritdoc />
public bool IsValid public Boolean IsValid => this.IsDisposed || this._event?.WaitHandle?.SafeWaitHandle == null
{ ? false
get : !this._event.WaitHandle.SafeWaitHandle.IsClosed && !this._event.WaitHandle.SafeWaitHandle.IsInvalid;
{
if (IsDisposed || _event == null) /// <inheritdoc />
return false; public Boolean IsCompleted => this.IsValid == false || this._event.IsSet;
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 />
/// <inheritdoc /> public void Complete() => this._event?.Set();
public bool IsCompleted
{ /// <inheritdoc />
get void IDisposable.Dispose() {
{ if(this.IsDisposed) {
if (IsValid == false) return;
return true; }
return _event?.WaitOne(0) ?? true; this.IsDisposed = true;
}
} this._event?.Set();
this._event?.Dispose();
/// <inheritdoc /> this._event = null;
public bool IsInProgress => !IsCompleted; }
/// <inheritdoc /> /// <inheritdoc />
public void Begin() => _event?.Reset(); public void Wait() => this._event?.Wait();
/// <inheritdoc /> /// <inheritdoc />
public void Complete() => _event?.Set(); public Boolean Wait(TimeSpan timeout) => this._event?.Wait(timeout) ?? true;
}
/// <inheritdoc />
void IDisposable.Dispose() #endregion
{ }
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>
/// Defines a WaitEvent backed by a ManualResetEventSlim.
/// </summary>
private class WaitEventSlim : IWaitEvent
{
private ManualResetEventSlim _event;
/// <summary>
/// Initializes a new instance of the <see cref="WaitEventSlim"/> class.
/// </summary>
/// <param name="isCompleted">if set to <c>true</c> [is completed].</param>
public WaitEventSlim(bool isCompleted)
{
_event = new ManualResetEventSlim(isCompleted);
}
/// <inheritdoc />
public bool IsDisposed { get; private set; }
/// <inheritdoc />
public bool IsValid
{
get
{
if (IsDisposed || _event?.WaitHandle?.SafeWaitHandle == null) return false;
return !_event.WaitHandle.SafeWaitHandle.IsClosed && !_event.WaitHandle.SafeWaitHandle.IsInvalid;
}
}
/// <inheritdoc />
public bool IsCompleted => IsValid == false || _event.IsSet;
/// <inheritdoc />
public bool IsInProgress => !IsCompleted;
/// <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
}
} }
#endif #endif
+169 -171
View File
@@ -1,174 +1,172 @@
namespace Unosquare.Swan using System;
{
using System; namespace Unosquare.Swan {
/// <summary>
/// Represents a struct of DateTimeSpan to compare dates and get in
/// separate fields the amount of time between those dates.
///
/// Based on https://stackoverflow.com/a/9216404/1096693.
/// </summary>
public struct DateTimeSpan {
/// <summary> /// <summary>
/// Represents a struct of DateTimeSpan to compare dates and get in /// Initializes a new instance of the <see cref="DateTimeSpan"/> struct.
/// separate fields the amount of time between those dates.
///
/// Based on https://stackoverflow.com/a/9216404/1096693.
/// </summary> /// </summary>
public struct DateTimeSpan /// <param name="years">The years.</param>
{ /// <param name="months">The months.</param>
/// <summary> /// <param name="days">The days.</param>
/// Initializes a new instance of the <see cref="DateTimeSpan"/> struct. /// <param name="hours">The hours.</param>
/// </summary> /// <param name="minutes">The minutes.</param>
/// <param name="years">The years.</param> /// <param name="seconds">The seconds.</param>
/// <param name="months">The months.</param> /// <param name="milliseconds">The milliseconds.</param>
/// <param name="days">The days.</param> public DateTimeSpan(Int32 years, Int32 months, Int32 days, Int32 hours, Int32 minutes, Int32 seconds, Int32 milliseconds) {
/// <param name="hours">The hours.</param> this.Years = years;
/// <param name="minutes">The minutes.</param> this.Months = months;
/// <param name="seconds">The seconds.</param> this.Days = days;
/// <param name="milliseconds">The milliseconds.</param> this.Hours = hours;
public DateTimeSpan(int years, int months, int days, int hours, int minutes, int seconds, int milliseconds) this.Minutes = minutes;
{ this.Seconds = seconds;
Years = years; this.Milliseconds = milliseconds;
Months = months; }
Days = days;
Hours = hours; /// <summary>
Minutes = minutes; /// Gets the years.
Seconds = seconds; /// </summary>
Milliseconds = milliseconds; /// <value>
} /// The years.
/// </value>
/// <summary> public Int32 Years {
/// Gets the years. get;
/// </summary> }
/// <value>
/// The years. /// <summary>
/// </value> /// Gets the months.
public int Years { get; } /// </summary>
/// <value>
/// <summary> /// The months.
/// Gets the months. /// </value>
/// </summary> public Int32 Months {
/// <value> get;
/// The months. }
/// </value>
public int Months { get; } /// <summary>
/// Gets the days.
/// <summary> /// </summary>
/// Gets the days. /// <value>
/// </summary> /// The days.
/// <value> /// </value>
/// The days. public Int32 Days {
/// </value> get;
public int Days { get; } }
/// <summary> /// <summary>
/// Gets the hours. /// Gets the hours.
/// </summary> /// </summary>
/// <value> /// <value>
/// The hours. /// The hours.
/// </value> /// </value>
public int Hours { get; } public Int32 Hours {
get;
/// <summary> }
/// Gets the minutes.
/// </summary> /// <summary>
/// <value> /// Gets the minutes.
/// The minutes. /// </summary>
/// </value> /// <value>
public int Minutes { get; } /// The minutes.
/// </value>
/// <summary> public Int32 Minutes {
/// Gets the seconds. get;
/// </summary> }
/// <value>
/// The seconds. /// <summary>
/// </value> /// Gets the seconds.
public int Seconds { get; } /// </summary>
/// <value>
/// <summary> /// The seconds.
/// Gets the milliseconds. /// </value>
/// </summary> public Int32 Seconds {
/// <value> get;
/// The milliseconds. }
/// </value>
public int Milliseconds { get; } /// <summary>
/// Gets the milliseconds.
internal static DateTimeSpan CompareDates(DateTime date1, DateTime date2) /// </summary>
{ /// <value>
if (date2 < date1) /// The milliseconds.
{ /// </value>
var sub = date1; public Int32 Milliseconds {
date1 = date2; get;
date2 = sub; }
}
internal static DateTimeSpan CompareDates(DateTime date1, DateTime date2) {
var current = date1; if(date2 < date1) {
var years = 0; DateTime sub = date1;
var months = 0; date1 = date2;
var days = 0; date2 = sub;
}
var phase = Phase.Years;
var span = new DateTimeSpan(); DateTime current = date1;
var officialDay = current.Day; Int32 years = 0;
Int32 months = 0;
while (phase != Phase.Done) Int32 days = 0;
{
switch (phase) Phase phase = Phase.Years;
{ DateTimeSpan span = new DateTimeSpan();
case Phase.Years: Int32 officialDay = current.Day;
if (current.AddYears(years + 1) > date2)
{ while(phase != Phase.Done) {
phase = Phase.Months; switch(phase) {
current = current.AddYears(years); case Phase.Years:
} if(current.AddYears(years + 1) > date2) {
else phase = Phase.Months;
{ current = current.AddYears(years);
years++; } else {
} years++;
}
break;
case Phase.Months: break;
if (current.AddMonths(months + 1) > date2) case Phase.Months:
{ 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);
{ TimeSpan timespan = date2 - current;
current = current.AddDays(days); span = new DateTimeSpan(
var timespan = date2 - current; years,
span = new DateTimeSpan( months,
years, days,
months, timespan.Hours,
days, timespan.Minutes,
timespan.Hours, timespan.Seconds,
timespan.Minutes, timespan.Milliseconds);
timespan.Seconds, phase = Phase.Done;
timespan.Milliseconds); } else {
phase = Phase.Done; days++;
} }
else
{ break;
days++; }
} }
break; return span;
} }
}
private enum Phase {
return span; Years,
} Months,
Days,
private enum Phase Done,
{ }
Years, }
Months,
Days,
Done,
}
}
} }
+124 -116
View File
@@ -1,132 +1,140 @@
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>
/// Contains useful constants and definitions.
/// </summary>
public static partial class Definitions {
#region Main Dictionary Definition
/// <summary> /// <summary>
/// Contains useful constants and definitions. /// The basic types information.
/// </summary> /// </summary>
public static partial class Definitions public static readonly Dictionary<Type, ExtendedTypeInfo> BasicTypesInfo =
{ new Dictionary<Type, ExtendedTypeInfo>
#region Main Dictionary Definition {
/// <summary>
/// The basic types information.
/// </summary>
public static readonly Dictionary<Type, ExtendedTypeInfo> BasicTypesInfo =
new Dictionary<Type, ExtendedTypeInfo>
{
// 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
{typeof(TimeSpan), new ExtendedTypeInfo<TimeSpan>()}, {typeof(TimeSpan), new ExtendedTypeInfo<TimeSpan>()},
{typeof(TimeSpan?), new ExtendedTypeInfo<TimeSpan?>()}, {typeof(TimeSpan?), new ExtendedTypeInfo<TimeSpan?>()},
{typeof(IPAddress), new ExtendedTypeInfo<IPAddress>()}, {typeof(IPAddress), new ExtendedTypeInfo<IPAddress>()},
}; };
#endregion #endregion
/// <summary> /// <summary>
/// Contains all basic types, including string, date time, and all of their nullable counterparts. /// Contains all basic types, including string, date time, and all of their nullable counterparts.
/// </summary> /// </summary>
/// <value> /// <value>
/// All basic types. /// All basic types.
/// </value> /// </value>
public static List<Type> AllBasicTypes { get; } = new List<Type>(BasicTypesInfo.Keys.ToArray()); public static List<Type> AllBasicTypes { get; } = new List<Type>(BasicTypesInfo.Keys.ToArray());
/// <summary> /// <summary>
/// Gets all numeric types including their nullable counterparts. /// Gets all numeric types including their nullable counterparts.
/// Note that Booleans and Guids are not considered numeric types. /// Note that Booleans and Guids are not considered numeric types.
/// </summary> /// </summary>
/// <value> /// <value>
/// All numeric types. /// All numeric types.
/// </value> /// </value>
public static List<Type> AllNumericTypes { get; } = new List<Type>( public static List<Type> AllNumericTypes {
BasicTypesInfo get;
.Where(kvp => kvp.Value.IsNumeric) } = new List<Type>(
.Select(kvp => kvp.Key).ToArray()); BasicTypesInfo
.Where(kvp => kvp.Value.IsNumeric)
/// <summary> .Select(kvp => kvp.Key).ToArray());
/// Gets all numeric types without their nullable counterparts.
/// Note that Booleans and Guids are not considered numeric types. /// <summary>
/// </summary> /// Gets all numeric types without their nullable counterparts.
/// <value> /// Note that Booleans and Guids are not considered numeric types.
/// All numeric value types. /// </summary>
/// </value> /// <value>
public static List<Type> AllNumericValueTypes { get; } = new List<Type>( /// All numeric value types.
BasicTypesInfo /// </value>
.Where(kvp => kvp.Value.IsNumeric && kvp.Value.IsNullableValueType == false) public static List<Type> AllNumericValueTypes {
.Select(kvp => kvp.Key).ToArray()); get;
} = new List<Type>(
/// <summary> BasicTypesInfo
/// Contains all basic value types. i.e. excludes string and nullables. .Where(kvp => kvp.Value.IsNumeric && kvp.Value.IsNullableValueType == false)
/// </summary> .Select(kvp => kvp.Key).ToArray());
/// <value>
/// All basic value types. /// <summary>
/// </value> /// Contains all basic value types. i.e. excludes string and nullables.
public static List<Type> AllBasicValueTypes { get; } = new List<Type>( /// </summary>
BasicTypesInfo /// <value>
.Where(kvp => kvp.Value.IsValueType) /// All basic value types.
.Select(kvp => kvp.Key).ToArray()); /// </value>
public static List<Type> AllBasicValueTypes {
/// <summary> get;
/// Contains all basic value types including the string type. i.e. excludes nullables. } = new List<Type>(
/// </summary> BasicTypesInfo
/// <value> .Where(kvp => kvp.Value.IsValueType)
/// All basic value and string types. .Select(kvp => kvp.Key).ToArray());
/// </value>
public static List<Type> AllBasicValueAndStringTypes { get; } = new List<Type>( /// <summary>
BasicTypesInfo /// Contains all basic value types including the string type. i.e. excludes nullables.
.Where(kvp => kvp.Value.IsValueType || kvp.Key == typeof(string)) /// </summary>
.Select(kvp => kvp.Key).ToArray()); /// <value>
/// All basic value and string types.
/// <summary> /// </value>
/// Gets all nullable value types. i.e. excludes string and all basic value types. public static List<Type> AllBasicValueAndStringTypes {
/// </summary> get;
/// <value> } = new List<Type>(
/// All basic nullable value types. BasicTypesInfo
/// </value> .Where(kvp => kvp.Value.IsValueType || kvp.Key == typeof(String))
public static List<Type> AllBasicNullableValueTypes { get; } = new List<Type>( .Select(kvp => kvp.Key).ToArray());
BasicTypesInfo
.Where(kvp => kvp.Value.IsNullableValueType) /// <summary>
.Select(kvp => kvp.Key).ToArray()); /// Gets all nullable value types. i.e. excludes string and all basic value types.
} /// </summary>
/// <value>
/// All basic nullable value types.
/// </value>
public static List<Type> AllBasicNullableValueTypes {
get;
} = new List<Type>(
BasicTypesInfo
.Where(kvp => kvp.Value.IsNullableValueType)
.Select(kvp => kvp.Key).ToArray());
}
} }
+31 -36
View File
@@ -1,39 +1,34 @@
namespace Unosquare.Swan using System;
{ using System.Text;
using System.Text;
namespace Unosquare.Swan {
/// <summary>
/// Contains useful constants and definitions.
/// </summary>
public static partial class Definitions {
/// <summary> /// <summary>
/// Contains useful constants and definitions. /// The MS Windows codepage 1252 encoding used in some legacy scenarios
/// such as default CSV text encoding from Excel.
/// </summary> /// </summary>
public static partial class Definitions public static readonly Encoding Windows1252Encoding;
{
/// <summary> /// <summary>
/// The MS Windows codepage 1252 encoding used in some legacy scenarios /// The encoding associated with the default ANSI code page in the operating
/// such as default CSV text encoding from Excel. /// system's regional and language settings.
/// </summary> /// </summary>
public static readonly Encoding Windows1252Encoding; public static readonly Encoding CurrentAnsiEncoding;
/// <summary> /// <summary>
/// The encoding associated with the default ANSI code page in the operating /// Initializes the <see cref="Definitions"/> class.
/// system's regional and language settings. /// </summary>
/// </summary> static Definitions() {
public static readonly Encoding CurrentAnsiEncoding; CurrentAnsiEncoding = Encoding.GetEncoding(default(Int32));
try {
/// <summary> Windows1252Encoding = Encoding.GetEncoding(1252);
/// Initializes the <see cref="Definitions"/> class. } catch {
/// </summary> // ignore, the codepage is not available use default
static Definitions() Windows1252Encoding = CurrentAnsiEncoding;
{ }
CurrentAnsiEncoding = Encoding.GetEncoding(default(int)); }
try }
{
Windows1252Encoding = Encoding.GetEncoding(1252);
}
catch
{
// ignore, the codepage is not available use default
Windows1252Encoding = CurrentAnsiEncoding;
}
}
}
} }
+49 -53
View File
@@ -1,60 +1,56 @@
namespace Unosquare.Swan namespace Unosquare.Swan {
{ /// <summary>
/// Enumeration of Operating Systems.
/// </summary>
public enum OperatingSystem {
/// <summary> /// <summary>
/// Enumeration of Operating Systems. /// Unknown OS
/// </summary> /// </summary>
public enum OperatingSystem Unknown,
{
/// <summary>
/// Unknown OS
/// </summary>
Unknown,
/// <summary>
/// Windows
/// </summary>
Windows,
/// <summary>
/// UNIX/Linux
/// </summary>
Unix,
/// <summary>
/// macOS (OSX)
/// </summary>
Osx,
}
/// <summary> /// <summary>
/// Enumerates the different Application Worker States. /// Windows
/// </summary> /// </summary>
public enum AppWorkerState Windows,
{
/// <summary>
/// The stopped
/// </summary>
Stopped,
/// <summary>
/// The running
/// </summary>
Running,
}
/// <summary> /// <summary>
/// Defines Endianness, big or little. /// UNIX/Linux
/// </summary> /// </summary>
public enum Endianness Unix,
{
/// <summary> /// <summary>
/// In big endian, you store the most significant byte in the smallest address. /// macOS (OSX)
/// </summary> /// </summary>
Big, Osx,
}
/// <summary>
/// In little endian, you store the least significant byte in the smallest address. /// <summary>
/// </summary> /// Enumerates the different Application Worker States.
Little, /// </summary>
} public enum AppWorkerState {
/// <summary>
/// The stopped
/// </summary>
Stopped,
/// <summary>
/// The running
/// </summary>
Running,
}
/// <summary>
/// Defines Endianness, big or little.
/// </summary>
public enum Endianness {
/// <summary>
/// In big endian, you store the most significant byte in the smallest address.
/// </summary>
Big,
/// <summary>
/// In little endian, you store the least significant byte in the smallest address.
/// </summary>
Little,
}
} }
+176 -163
View File
@@ -1,168 +1,181 @@
namespace Unosquare.Swan using System;
{
using System; namespace Unosquare.Swan {
/// <summary>
/// Event arguments representing the message that is logged
/// on to the terminal. Use the properties to forward the data to
/// your logger of choice.
/// </summary>
/// <seealso cref="System.EventArgs" />
public class LogMessageReceivedEventArgs : EventArgs {
/// <summary> /// <summary>
/// Event arguments representing the message that is logged /// Initializes a new instance of the <see cref="LogMessageReceivedEventArgs" /> class.
/// on to the terminal. Use the properties to forward the data to
/// your logger of choice.
/// </summary> /// </summary>
/// <seealso cref="System.EventArgs" /> /// <param name="sequence">The sequence.</param>
public class LogMessageReceivedEventArgs : EventArgs /// <param name="messageType">Type of the message.</param>
{ /// <param name="utcDate">The UTC date.</param>
/// <summary> /// <param name="source">The source.</param>
/// Initializes a new instance of the <see cref="LogMessageReceivedEventArgs" /> class. /// <param name="message">The message.</param>
/// </summary> /// <param name="extendedData">The extended data.</param>
/// <param name="sequence">The sequence.</param> /// <param name="callerMemberName">Name of the caller member.</param>
/// <param name="messageType">Type of the message.</param> /// <param name="callerFilePath">The caller file path.</param>
/// <param name="utcDate">The UTC date.</param> /// <param name="callerLineNumber">The caller line number.</param>
/// <param name="source">The source.</param> public LogMessageReceivedEventArgs(
/// <param name="message">The message.</param> UInt64 sequence,
/// <param name="extendedData">The extended data.</param> LogMessageType messageType,
/// <param name="callerMemberName">Name of the caller member.</param> DateTime utcDate,
/// <param name="callerFilePath">The caller file path.</param> String source,
/// <param name="callerLineNumber">The caller line number.</param> String message,
public LogMessageReceivedEventArgs( Object extendedData,
ulong sequence, String callerMemberName,
LogMessageType messageType, String callerFilePath,
DateTime utcDate, Int32 callerLineNumber) {
string source, this.Sequence = sequence;
string message, this.MessageType = messageType;
object extendedData, this.UtcDate = utcDate;
string callerMemberName, this.Source = source;
string callerFilePath, this.Message = message;
int callerLineNumber) this.CallerMemberName = callerMemberName;
{ this.CallerFilePath = callerFilePath;
Sequence = sequence; this.CallerLineNumber = callerLineNumber;
MessageType = messageType; this.ExtendedData = extendedData;
UtcDate = utcDate; }
Source = source;
Message = message;
CallerMemberName = callerMemberName;
CallerFilePath = callerFilePath;
CallerLineNumber = callerLineNumber;
ExtendedData = extendedData;
}
/// <summary>
/// Gets logging message sequence.
/// </summary>
/// <value>
/// The sequence.
/// </value>
public ulong Sequence { get; }
/// <summary>
/// Gets the type of the message.
/// It can be a combination as the enumeration is a set of bitwise flags.
/// </summary>
/// <value>
/// The type of the message.
/// </value>
public LogMessageType MessageType { get; }
/// <summary>
/// Gets the UTC date at which the event at which the message was logged.
/// </summary>
/// <value>
/// The UTC date.
/// </value>
public DateTime UtcDate { get; }
/// <summary>
/// Gets the name of the source where the logging message
/// came from. This can come empty if the logger did not set it.
/// </summary>
/// <value>
/// The source.
/// </value>
public string Source { get; }
/// <summary>
/// Gets the body of the message.
/// </summary>
/// <value>
/// The message.
/// </value>
public string Message { get; }
/// <summary>
/// Gets the name of the caller member.
/// </summary>
/// <value>
/// The name of the caller member.
/// </value>
public string CallerMemberName { get; }
/// <summary>
/// Gets the caller file path.
/// </summary>
/// <value>
/// The caller file path.
/// </value>
public string CallerFilePath { get; }
/// <summary>
/// Gets the caller line number.
/// </summary>
/// <value>
/// The caller line number.
/// </value>
public int CallerLineNumber { get; }
/// <summary>
/// Gets an object representing extended data.
/// It could be an exception or anything else.
/// </summary>
/// <value>
/// The extended data.
/// </value>
public object ExtendedData { get; }
/// <summary>
/// Gets the Extended Data properties cast as an Exception (if possible)
/// Otherwise, it return null.
/// </summary>
/// <value>
/// The exception.
/// </value>
public Exception Exception => ExtendedData as Exception;
}
/// <summary> /// <summary>
/// Event arguments representing a message logged and about to be /// Gets logging message sequence.
/// displayed on the terminal (console). Set the CancelOutput property in the
/// event handler to prevent the terminal from displaying the message.
/// </summary> /// </summary>
/// <seealso cref="LogMessageReceivedEventArgs" /> /// <value>
public class LogMessageDisplayingEventArgs : LogMessageReceivedEventArgs /// The sequence.
{ /// </value>
/// <summary> public UInt64 Sequence {
/// Initializes a new instance of the <see cref="LogMessageDisplayingEventArgs"/> class. get;
/// </summary> }
/// <param name="data">The <see cref="LogMessageReceivedEventArgs"/> instance containing the event data.</param>
public LogMessageDisplayingEventArgs(LogMessageReceivedEventArgs data) /// <summary>
: base( /// Gets the type of the message.
data.Sequence, /// It can be a combination as the enumeration is a set of bitwise flags.
data.MessageType, /// </summary>
data.UtcDate, /// <value>
data.Source, /// The type of the message.
data.Message, /// </value>
data.ExtendedData, public LogMessageType MessageType {
data.CallerMemberName, get;
data.CallerFilePath, }
data.CallerLineNumber)
{ /// <summary>
CancelOutput = false; /// Gets the UTC date at which the event at which the message was logged.
} /// </summary>
/// <value>
/// <summary> /// The UTC date.
/// Gets or sets a value indicating whether the displaying of the /// </value>
/// logging message should be canceled. public DateTime UtcDate {
/// </summary> get;
/// <value> }
/// <c>true</c> if [cancel output]; otherwise, <c>false</c>.
/// </value> /// <summary>
public bool CancelOutput { get; set; } /// Gets the name of the source where the logging message
} /// came from. This can come empty if the logger did not set it.
/// </summary>
/// <value>
/// The source.
/// </value>
public String Source {
get;
}
/// <summary>
/// Gets the body of the message.
/// </summary>
/// <value>
/// The message.
/// </value>
public String Message {
get;
}
/// <summary>
/// Gets the name of the caller member.
/// </summary>
/// <value>
/// The name of the caller member.
/// </value>
public String CallerMemberName {
get;
}
/// <summary>
/// Gets the caller file path.
/// </summary>
/// <value>
/// The caller file path.
/// </value>
public String CallerFilePath {
get;
}
/// <summary>
/// Gets the caller line number.
/// </summary>
/// <value>
/// The caller line number.
/// </value>
public Int32 CallerLineNumber {
get;
}
/// <summary>
/// Gets an object representing extended data.
/// It could be an exception or anything else.
/// </summary>
/// <value>
/// The extended data.
/// </value>
public Object ExtendedData {
get;
}
/// <summary>
/// Gets the Extended Data properties cast as an Exception (if possible)
/// Otherwise, it return null.
/// </summary>
/// <value>
/// The exception.
/// </value>
public Exception Exception => this.ExtendedData as Exception;
}
/// <summary>
/// Event arguments representing a message logged and about to be
/// displayed on the terminal (console). Set the CancelOutput property in the
/// event handler to prevent the terminal from displaying the message.
/// </summary>
/// <seealso cref="LogMessageReceivedEventArgs" />
public class LogMessageDisplayingEventArgs : LogMessageReceivedEventArgs {
/// <summary>
/// Initializes a new instance of the <see cref="LogMessageDisplayingEventArgs"/> class.
/// </summary>
/// <param name="data">The <see cref="LogMessageReceivedEventArgs"/> instance containing the event data.</param>
public LogMessageDisplayingEventArgs(LogMessageReceivedEventArgs data)
: base(
data.Sequence,
data.MessageType,
data.UtcDate,
data.Source,
data.Message,
data.ExtendedData,
data.CallerMemberName,
data.CallerFilePath,
data.CallerLineNumber) => this.CancelOutput = false;
/// <summary>
/// Gets or sets a value indicating whether the displaying of the
/// logging message should be canceled.
/// </summary>
/// <value>
/// <c>true</c> if [cancel output]; otherwise, <c>false</c>.
/// </value>
public Boolean CancelOutput {
get; set;
}
}
} }
File diff suppressed because it is too large Load Diff
+210 -209
View File
@@ -1,230 +1,231 @@
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},
{ "dayOfMonth", 31}, { "dayOfMonth", 31},
{ "month", 12}, { "month", 12},
{ "dayOfWeek", 6}, { "dayOfWeek", 6},
}; };
/// <summary> /// <summary>
/// Converts the date to a YYYY-MM-DD string. /// Converts the date to a YYYY-MM-DD string.
/// </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>
/// Converts the date to a YYYY-MM-DD HH:II:SS string. /// Converts the date to a YYYY-MM-DD HH:II:SS string.
/// </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>
/// Parses a YYYY-MM-DD and optionally it time part, HH:II:SS into a DateTime. /// Parses a YYYY-MM-DD and optionally it time part, HH:II:SS into a DateTime.
/// </summary> /// </summary>
/// <param name="sortableDate">The sortable date.</param> /// <param name="sortableDate">The sortable date.</param>
/// <returns> /// <returns>
/// A new instance of the DateTime structure to /// A new instance of the DateTime structure to
/// the specified year, month, day, hour, minute and second. /// the specified year, month, day, hour, minute and second.
/// </returns> /// </returns>
/// <exception cref="ArgumentNullException">sortableDate.</exception> /// <exception cref="ArgumentNullException">sortableDate.</exception>
/// <exception cref="Exception"> /// <exception cref="Exception">
/// Represents errors that occur during application execution. /// Represents errors that occur during application execution.
/// </exception> /// </exception>
/// <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]); }
var month = int.Parse(dateParts[1]);
var day = int.Parse(dateParts[2]); Int32 year = Int32.Parse(dateParts[0]);
Int32 month = Int32.Parse(dateParts[1]);
if (dateTimeParts.Length > 1) Int32 day = Int32.Parse(dateParts[2]);
{
var timeParts = dateTimeParts[1].Split(':'); if(dateTimeParts.Length > 1) {
if (timeParts.Length != 3) throw new Exception(); String[] timeParts = dateTimeParts[1].Split(':');
if(timeParts.Length != 3) {
hour = int.Parse(timeParts[0]); throw new Exception();
minute = int.Parse(timeParts[1]); }
second = int.Parse(timeParts[2]);
} hour = Int32.Parse(timeParts[0]);
minute = Int32.Parse(timeParts[1]);
return new DateTime(year, month, day, hour, minute, second); second = Int32.Parse(timeParts[2]);
} }
catch (Exception)
{ return new DateTime(year, month, day, hour, minute, second);
throw new ArgumentException("Unable to parse sortable date and time.", nameof(sortableDate)); } catch(Exception) {
} throw new ArgumentException("Unable to parse sortable date and time.", nameof(sortableDate));
} }
}
/// <summary>
/// Creates a date's range. /// <summary>
/// </summary> /// Creates a date's range.
/// <param name="startDate">The start date.</param> /// </summary>
/// <param name="endDate">The end date.</param> /// <param name="startDate">The start date.</param>
/// <returns> /// <param name="endDate">The end date.</param>
/// A sequence of integral numbers within a specified date's range. /// <returns>
/// </returns> /// A sequence of integral numbers within a specified date's range.
public static IEnumerable<DateTime> DateRange(this DateTime startDate, DateTime endDate) /// </returns>
=> Enumerable.Range(0, (endDate - startDate).Days + 1).Select(d => startDate.AddDays(d)); public static IEnumerable<DateTime> DateRange(this DateTime startDate, DateTime endDate)
=> Enumerable.Range(0, (endDate - startDate).Days + 1).Select(d => startDate.AddDays(d));
/// <summary>
/// Rounds up a date to match a timespan. /// <summary>
/// </summary> /// Rounds up a date to match a timespan.
/// <param name="date">The datetime.</param> /// </summary>
/// <param name="timeSpan">The timespan to match.</param> /// <param name="date">The datetime.</param>
/// <returns> /// <param name="timeSpan">The timespan to match.</param>
/// A new instance of the DateTime structure to the specified datetime and timespan ticks. /// <returns>
/// </returns> /// A new instance of the DateTime structure to the specified datetime and timespan ticks.
public static DateTime RoundUp(this DateTime date, TimeSpan timeSpan) /// </returns>
=> new DateTime(((date.Ticks + timeSpan.Ticks - 1) / timeSpan.Ticks) * timeSpan.Ticks); public static DateTime RoundUp(this DateTime date, TimeSpan timeSpan)
=> new DateTime((date.Ticks + timeSpan.Ticks - 1) / timeSpan.Ticks * timeSpan.Ticks);
/// <summary>
/// Get this datetime as a Unix epoch timestamp (seconds since Jan 1, 1970, midnight UTC). /// <summary>
/// </summary> /// Get this datetime as a Unix epoch timestamp (seconds since Jan 1, 1970, midnight UTC).
/// <param name="date">The date to convert.</param> /// </summary>
/// <returns>Seconds since Unix epoch.</returns> /// <param name="date">The date to convert.</param>
public static long ToUnixEpochDate(this DateTime date) /// <returns>Seconds since Unix epoch.</returns>
{ 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
} }
/// <summary> /// <summary>
/// Compares a Date to another and returns a <c>DateTimeSpan</c>. /// Compares a Date to another and returns a <c>DateTimeSpan</c>.
/// </summary> /// </summary>
/// <param name="dateStart">The date start.</param> /// <param name="dateStart">The date start.</param>
/// <param name="dateEnd">The date end.</param> /// <param name="dateEnd">The date end.</param>
/// <returns>A DateTimeSpan with the Years, Months, Days, Hours, Minutes, Seconds and Milliseconds between the dates.</returns> /// <returns>A DateTimeSpan with the Years, Months, Days, Hours, Minutes, Seconds and Milliseconds between the dates.</returns>
public static DateTimeSpan GetDateTimeSpan(this DateTime dateStart, DateTime dateEnd) public static DateTimeSpan GetDateTimeSpan(this DateTime dateStart, DateTime dateEnd)
=> DateTimeSpan.CompareDates(dateStart, dateEnd); => DateTimeSpan.CompareDates(dateStart, dateEnd);
/// <summary> /// <summary>
/// Compare the Date elements(Months, Days, Hours, Minutes). /// Compare the Date elements(Months, Days, Hours, Minutes).
/// </summary> /// </summary>
/// <param name="date">The date.</param> /// <param name="date">The date.</param>
/// <param name="minute">The minute (0-59).</param> /// <param name="minute">The minute (0-59).</param>
/// <param name="hour">The hour. (0-23).</param> /// <param name="hour">The hour. (0-23).</param>
/// <param name="dayOfMonth">The day of month. (1-31).</param> /// <param name="dayOfMonth">The day of month. (1-31).</param>
/// <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);
} }
/// <summary> /// <summary>
/// Compare the Date elements(Months, Days, Hours, Minutes). /// Compare the Date elements(Months, Days, Hours, Minutes).
/// </summary> /// </summary>
/// <param name="date">The date.</param> /// <param name="date">The date.</param>
/// <param name="minute">The minute (0-59).</param> /// <param name="minute">The minute (0-59).</param>
/// <param name="hour">The hour. (0-23).</param> /// <param name="hour">The hour. (0-23).</param>
/// <param name="dayOfMonth">The day of month. (1-31).</param> /// <param name="dayOfMonth">The day of month. (1-31).</param>
/// <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(","))
{ if(parts.Contains(",")) {
return parts.Split(',').Select(int.Parse).Contains(value); return parts.Split(',').Select(Int32.Parse).Contains(value);
} }
var stop = DateRanges[type]; Int32 stop = DateRanges[type];
if (parts.Contains("/")) if(parts.Contains("/")) {
{ Int32 multiple = Int32.Parse(parts.Split('/').Last());
var multiple = int.Parse(parts.Split('/').Last()); Int32 start = type == "dayOfMonth" || type == "month" ? 1 : 0;
var start = type == "dayOfMonth" || type == "month" ? 1 : 0;
for(Int32 i = start; i <= stop; i += multiple) {
for (var i = start; i <= stop; i += multiple) if(i == value) {
if (i == value) return true; return true;
}
return false; }
}
return false;
if (parts.Contains("-")) }
{
var range = parts.Split('-'); if(parts.Contains("-")) {
var start = int.Parse(range.First()); String[] range = parts.Split('-');
stop = Math.Max(stop, int.Parse(range.Last())); Int32 start = Int32.Parse(range.First());
stop = Math.Max(stop, Int32.Parse(range.Last()));
if ((type == "dayOfMonth" || type == "month") && start == 0)
start = 1; if((type == "dayOfMonth" || type == "month") && start == 0) {
start = 1;
for (var i = start; i <= stop; i++) }
if (i == value) return true;
for(Int32 i = start; i <= stop; i++) {
return false; if(i == value) {
} return true;
}
return int.Parse(parts) == value; }
}
} return false;
}
return Int32.Parse(parts) == value;
}
}
} }
+73 -74
View File
@@ -1,77 +1,76 @@
namespace Unosquare.Swan using System;
{ using System.Collections.Generic;
using System;
using System.Collections.Generic; namespace Unosquare.Swan {
/// <summary>
/// Extension methods.
/// </summary>
public static partial class Extensions {
/// <summary> /// <summary>
/// Extension methods. /// Gets the value if exists or default.
/// </summary> /// </summary>
public static partial class Extensions /// <typeparam name="TKey">The type of the key.</typeparam>
{ /// <typeparam name="TValue">The type of the value.</typeparam>
/// <summary> /// <param name="dict">The dictionary.</param>
/// Gets the value if exists or default. /// <param name="key">The key.</param>
/// </summary> /// <param name="defaultValue">The default value.</param>
/// <typeparam name="TKey">The type of the key.</typeparam> /// <returns>
/// <typeparam name="TValue">The type of the value.</typeparam> /// The value of the provided key or default.
/// <param name="dict">The dictionary.</param> /// </returns>
/// <param name="key">The key.</param> /// <exception cref="ArgumentNullException">dict.</exception>
/// <param name="defaultValue">The default value.</param> public static TValue GetValueOrDefault<TKey, TValue>(this IDictionary<TKey, TValue> dict, TKey key, TValue defaultValue = default) {
/// <returns> if(dict == null) {
/// The value of the provided key or default. throw new ArgumentNullException(nameof(dict));
/// </returns> }
/// <exception cref="ArgumentNullException">dict.</exception>
public static TValue GetValueOrDefault<TKey, TValue>(this IDictionary<TKey, TValue> dict, TKey key, TValue defaultValue = default) return dict.ContainsKey(key) ? dict[key] : defaultValue;
{ }
if (dict == null)
throw new ArgumentNullException(nameof(dict)); /// <summary>
/// Adds a key/value pair to the Dictionary if the key does not already exist.
return dict.ContainsKey(key) ? dict[key] : defaultValue; /// If the value is null, the key will not be updated.
} ///
/// Based on <c>ConcurrentDictionary.GetOrAdd</c> method.
/// <summary> /// </summary>
/// Adds a key/value pair to the Dictionary if the key does not already exist. /// <typeparam name="TKey">The type of the key.</typeparam>
/// If the value is null, the key will not be updated. /// <typeparam name="TValue">The type of the value.</typeparam>
/// /// <param name="dict">The dictionary.</param>
/// Based on <c>ConcurrentDictionary.GetOrAdd</c> method. /// <param name="key">The key.</param>
/// </summary> /// <param name="valueFactory">The value factory.</param>
/// <typeparam name="TKey">The type of the key.</typeparam> /// <returns>The value for the key.</returns>
/// <typeparam name="TValue">The type of the value.</typeparam> public static TValue GetOrAdd<TKey, TValue>(this IDictionary<TKey, TValue> dict, TKey key, Func<TKey, TValue> valueFactory) {
/// <param name="dict">The dictionary.</param> if(dict == null) {
/// <param name="key">The key.</param> throw new ArgumentNullException(nameof(dict));
/// <param name="valueFactory">The value factory.</param> }
/// <returns>The value for the key.</returns>
public static TValue GetOrAdd<TKey, TValue>(this IDictionary<TKey, TValue> dict, TKey key, Func<TKey, TValue> valueFactory) if(!dict.ContainsKey(key)) {
{ TValue value = valueFactory(key);
if (dict == null) if(Equals(value, default)) {
throw new ArgumentNullException(nameof(dict)); return default;
}
if (!dict.ContainsKey(key))
{ dict[key] = value;
var value = valueFactory(key); }
if (Equals(value, default)) return default;
dict[key] = value; return dict[key];
} }
return dict[key]; /// <summary>
} /// Executes the item action for each element in the Dictionary.
/// </summary>
/// <summary> /// <typeparam name="TKey">The type of the key.</typeparam>
/// Executes the item action for each element in the Dictionary. /// <typeparam name="TValue">The type of the value.</typeparam>
/// </summary> /// <param name="dict">The dictionary.</param>
/// <typeparam name="TKey">The type of the key.</typeparam> /// <param name="itemAction">The item action.</param>
/// <typeparam name="TValue">The type of the value.</typeparam> /// <exception cref="ArgumentNullException">dict.</exception>
/// <param name="dict">The dictionary.</param> public static void ForEach<TKey, TValue>(this IDictionary<TKey, TValue> dict, Action<TKey, TValue> itemAction) {
/// <param name="itemAction">The item action.</param> if(dict == null) {
/// <exception cref="ArgumentNullException">dict.</exception> throw new ArgumentNullException(nameof(dict));
public static void ForEach<TKey, TValue>(this IDictionary<TKey, TValue> dict, Action<TKey, TValue> itemAction) }
{
if (dict == null) foreach(KeyValuePair<TKey, TValue> kvp in dict) {
throw new ArgumentNullException(nameof(dict)); itemAction(kvp.Key, kvp.Value);
}
foreach (var kvp in dict) }
{ }
itemAction(kvp.Key, kvp.Value);
}
}
}
} }
+185 -176
View File
@@ -1,179 +1,188 @@
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>
/// Functional programming extension methods.
/// </summary>
public static class FunctionalExtensions {
/// <summary> /// <summary>
/// Functional programming extension methods. /// Whens the specified condition.
/// </summary> /// </summary>
public static class FunctionalExtensions /// <typeparam name="T">The type of IQueryable.</typeparam>
{ /// <param name="list">The list.</param>
/// <summary> /// <param name="condition">The condition.</param>
/// Whens the specified condition. /// <param name="fn">The function.</param>
/// </summary> /// <returns>
/// <typeparam name="T">The type of IQueryable.</typeparam> /// The IQueryable.
/// <param name="list">The list.</param> /// </returns>
/// <param name="condition">The condition.</param> /// <exception cref="ArgumentNullException">
/// <param name="fn">The function.</param> /// this
/// <returns> /// or
/// The IQueryable. /// condition
/// </returns> /// or
/// <exception cref="ArgumentNullException"> /// fn.
/// this /// </exception>
/// or public static IQueryable<T> When<T>(
/// condition this IQueryable<T> list,
/// or Func<Boolean> condition,
/// fn. Func<IQueryable<T>, IQueryable<T>> fn) {
/// </exception> if(list == null) {
public static IQueryable<T> When<T>( throw new ArgumentNullException(nameof(list));
this IQueryable<T> list, }
Func<bool> condition,
Func<IQueryable<T>, IQueryable<T>> fn) if(condition == null) {
{ throw new ArgumentNullException(nameof(condition));
if (list == null) }
throw new ArgumentNullException(nameof(list));
if(fn == null) {
if (condition == null) throw new ArgumentNullException(nameof(fn));
throw new ArgumentNullException(nameof(condition)); }
if (fn == null) return condition() ? fn(list) : list;
throw new ArgumentNullException(nameof(fn)); }
return condition() ? fn(list) : list; /// <summary>
} /// Whens the specified condition.
/// </summary>
/// <summary> /// <typeparam name="T">The type of IEnumerable.</typeparam>
/// Whens the specified condition. /// <param name="list">The list.</param>
/// </summary> /// <param name="condition">The condition.</param>
/// <typeparam name="T">The type of IEnumerable.</typeparam> /// <param name="fn">The function.</param>
/// <param name="list">The list.</param> /// <returns>
/// <param name="condition">The condition.</param> /// The IEnumerable.
/// <param name="fn">The function.</param> /// </returns>
/// <returns> /// <exception cref="ArgumentNullException">
/// The IEnumerable. /// this
/// </returns> /// or
/// <exception cref="ArgumentNullException"> /// condition
/// this /// or
/// or /// fn.
/// condition /// </exception>
/// or public static IEnumerable<T> When<T>(
/// fn. this IEnumerable<T> list,
/// </exception> Func<Boolean> condition,
public static IEnumerable<T> When<T>( Func<IEnumerable<T>, IEnumerable<T>> fn) {
this IEnumerable<T> list, if(list == null) {
Func<bool> condition, throw new ArgumentNullException(nameof(list));
Func<IEnumerable<T>, IEnumerable<T>> fn) }
{
if (list == null) if(condition == null) {
throw new ArgumentNullException(nameof(list)); throw new ArgumentNullException(nameof(condition));
}
if (condition == null)
throw new ArgumentNullException(nameof(condition)); if(fn == null) {
throw new ArgumentNullException(nameof(fn));
if (fn == null) }
throw new ArgumentNullException(nameof(fn));
return condition() ? fn(list) : list;
return condition() ? fn(list) : list; }
}
/// <summary>
/// <summary> /// Adds the value when the condition is true.
/// Adds the value when the condition is true. /// </summary>
/// </summary> /// <typeparam name="T">The type of IList element.</typeparam>
/// <typeparam name="T">The type of IList element.</typeparam> /// <param name="list">The list.</param>
/// <param name="list">The list.</param> /// <param name="condition">The condition.</param>
/// <param name="condition">The condition.</param> /// <param name="value">The value.</param>
/// <param name="value">The value.</param> /// <returns>
/// <returns> /// The IList.
/// The IList. /// </returns>
/// </returns> /// <exception cref="ArgumentNullException">
/// <exception cref="ArgumentNullException"> /// this
/// this /// or
/// or /// condition
/// condition /// or
/// or /// value.
/// value. /// </exception>
/// </exception> public static IList<T> AddWhen<T>(
public static IList<T> AddWhen<T>( this IList<T> list,
this IList<T> list, Func<Boolean> condition,
Func<bool> condition, Func<T> value) {
Func<T> value) if(list == null) {
{ throw new ArgumentNullException(nameof(list));
if (list == null) }
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())
list.Add(value()); if(condition()) {
list.Add(value());
return list; }
}
return list;
/// <summary> }
/// Adds the value when the condition is true.
/// </summary> /// <summary>
/// <typeparam name="T">The type of IList element.</typeparam> /// Adds the value when the condition is true.
/// <param name="list">The list.</param> /// </summary>
/// <param name="condition">if set to <c>true</c> [condition].</param> /// <typeparam name="T">The type of IList element.</typeparam>
/// <param name="value">The value.</param> /// <param name="list">The list.</param>
/// <returns> /// <param name="condition">if set to <c>true</c> [condition].</param>
/// The IList. /// <param name="value">The value.</param>
/// </returns> /// <returns>
/// <exception cref="ArgumentNullException">list.</exception> /// The IList.
public static IList<T> AddWhen<T>( /// </returns>
this IList<T> list, /// <exception cref="ArgumentNullException">list.</exception>
bool condition, public static IList<T> AddWhen<T>(
T value) this IList<T> list,
{ Boolean condition,
if (list == null) T value) {
throw new ArgumentNullException(nameof(list)); if(list == null) {
throw new ArgumentNullException(nameof(list));
if (condition) }
list.Add(value);
if(condition) {
return list; list.Add(value);
} }
/// <summary> return list;
/// Adds the range when the condition is true. }
/// </summary>
/// <typeparam name="T">The type of List element.</typeparam> /// <summary>
/// <param name="list">The list.</param> /// Adds the range when the condition is true.
/// <param name="condition">The condition.</param> /// </summary>
/// <param name="value">The value.</param> /// <typeparam name="T">The type of List element.</typeparam>
/// <returns> /// <param name="list">The list.</param>
/// The List. /// <param name="condition">The condition.</param>
/// </returns> /// <param name="value">The value.</param>
/// <exception cref="ArgumentNullException"> /// <returns>
/// this /// The List.
/// or /// </returns>
/// condition /// <exception cref="ArgumentNullException">
/// or /// this
/// value. /// or
/// </exception> /// condition
public static List<T> AddRangeWhen<T>( /// or
this List<T> list, /// value.
Func<bool> condition, /// </exception>
Func<IEnumerable<T>> value) public static List<T> AddRangeWhen<T>(
{ this List<T> list,
if (list == null) Func<Boolean> condition,
throw new ArgumentNullException(nameof(list)); Func<IEnumerable<T>> value) {
if(list == null) {
if (condition == null) throw new ArgumentNullException(nameof(list));
throw new ArgumentNullException(nameof(condition)); }
if (value == null) if(condition == null) {
throw new ArgumentNullException(nameof(value)); throw new ArgumentNullException(nameof(condition));
}
if (condition())
list.AddRange(value()); if(value == null) {
throw new ArgumentNullException(nameof(value));
return list; }
}
} if(condition()) {
list.AddRange(value());
}
return list;
}
}
} }
+438 -460
View File
@@ -1,469 +1,447 @@
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>
/// Provides various extension methods for Reflection and Types.
/// </summary>
public static class ReflectionExtensions {
private static readonly Lazy<ConcurrentDictionary<Tuple<Boolean, PropertyInfo>, Func<Object, Object>>> CacheGetMethods =
new Lazy<ConcurrentDictionary<Tuple<Boolean, PropertyInfo>, Func<Object, Object>>>(() => new ConcurrentDictionary<Tuple<Boolean, PropertyInfo>, Func<Object, Object>>(), true);
private static readonly Lazy<ConcurrentDictionary<Tuple<Boolean, PropertyInfo>, Action<Object, Object[]>>> CacheSetMethods =
new Lazy<ConcurrentDictionary<Tuple<Boolean, PropertyInfo>, Action<Object, Object[]>>>(() => new ConcurrentDictionary<Tuple<Boolean, PropertyInfo>, Action<Object, Object[]>>(), true);
#region Assembly Extensions
/// <summary> /// <summary>
/// Provides various extension methods for Reflection and Types. /// Gets all types within an assembly in a safe manner.
/// </summary> /// </summary>
public static class ReflectionExtensions /// <param name="assembly">The assembly.</param>
{ /// <returns>
private static readonly Lazy<ConcurrentDictionary<Tuple<bool, PropertyInfo>, Func<object, object>>> CacheGetMethods = /// Array of Type objects representing the types specified by an assembly.
new Lazy<ConcurrentDictionary<Tuple<bool, PropertyInfo>, Func<object, object>>>(() => new ConcurrentDictionary<Tuple<bool, PropertyInfo>, Func<object, object>>(), true); /// </returns>
/// <exception cref="ArgumentNullException">assembly.</exception>
private static readonly Lazy<ConcurrentDictionary<Tuple<bool, PropertyInfo>, Action<object, object[]>>> CacheSetMethods = public static IEnumerable<Type> GetAllTypes(this Assembly assembly) {
new Lazy<ConcurrentDictionary<Tuple<bool, PropertyInfo>, Action<object, object[]>>>(() => new ConcurrentDictionary<Tuple<bool, PropertyInfo>, Action<object, object[]>>(), true); if(assembly == null) {
throw new ArgumentNullException(nameof(assembly));
#region Assembly Extensions }
/// <summary> try {
/// Gets all types within an assembly in a safe manner. return assembly.GetTypes();
/// </summary> } catch(ReflectionTypeLoadException e) {
/// <param name="assembly">The assembly.</param> return e.Types.Where(t => t != null);
/// <returns> }
/// Array of Type objects representing the types specified by an assembly. }
/// </returns>
/// <exception cref="ArgumentNullException">assembly.</exception> #endregion
public static IEnumerable<Type> GetAllTypes(this Assembly assembly)
{ #region Type Extensions
if (assembly == null)
throw new ArgumentNullException(nameof(assembly)); /// <summary>
/// The closest programmatic equivalent of default(T).
try /// </summary>
{ /// <param name="type">The type.</param>
return assembly.GetTypes(); /// <returns>
} /// Default value of this type.
catch (ReflectionTypeLoadException e) /// </returns>
{ /// <exception cref="ArgumentNullException">type.</exception>
return e.Types.Where(t => t != null); public static Object GetDefault(this Type type) {
} if(type == null) {
} throw new ArgumentNullException(nameof(type));
}
#endregion
return type.IsValueType() ? Activator.CreateInstance(type) : null;
#region Type Extensions }
/// <summary> /// <summary>
/// The closest programmatic equivalent of default(T). /// Determines whether this type is compatible with ICollection.
/// </summary> /// </summary>
/// <param name="type">The type.</param> /// <param name="sourceType">The type.</param>
/// <returns> /// <returns>
/// Default value of this type. /// <c>true</c> if the specified source type is collection; otherwise, <c>false</c>.
/// </returns> /// </returns>
/// <exception cref="ArgumentNullException">type.</exception> /// <exception cref="ArgumentNullException">sourceType.</exception>
public static object GetDefault(this Type type) public static Boolean IsCollection(this Type sourceType) {
{ if(sourceType == null) {
if (type == null) throw new ArgumentNullException(nameof(sourceType));
throw new ArgumentNullException(nameof(type)); }
return type.IsValueType() ? Activator.CreateInstance(type) : null; return sourceType != typeof(String) &&
} typeof(IEnumerable).IsAssignableFrom(sourceType);
}
/// <summary>
/// Determines whether this type is compatible with ICollection. /// <summary>
/// </summary> /// Gets a method from a type given the method name, binding flags, generic types and parameter types.
/// <param name="sourceType">The type.</param> /// </summary>
/// <returns> /// <param name="type">Type of the source.</param>
/// <c>true</c> if the specified source type is collection; otherwise, <c>false</c>. /// <param name="bindingFlags">The binding flags.</param>
/// </returns> /// <param name="methodName">Name of the method.</param>
/// <exception cref="ArgumentNullException">sourceType.</exception> /// <param name="genericTypes">The generic types.</param>
public static bool IsCollection(this Type sourceType) /// <param name="parameterTypes">The parameter types.</param>
{ /// <returns>
if (sourceType == null) /// An object that represents the method with the specified name.
throw new ArgumentNullException(nameof(sourceType)); /// </returns>
/// <exception cref="System.Reflection.AmbiguousMatchException">
return sourceType != typeof(string) && /// The exception that is thrown when binding to a member results in more than one member matching the
typeof(IEnumerable).IsAssignableFrom(sourceType); /// binding criteria. This class cannot be inherited.
} /// </exception>
public static MethodInfo GetMethod(
/// <summary> this Type type,
/// Gets a method from a type given the method name, binding flags, generic types and parameter types. BindingFlags bindingFlags,
/// </summary> String methodName,
/// <param name="type">Type of the source.</param> Type[] genericTypes,
/// <param name="bindingFlags">The binding flags.</param> Type[] parameterTypes) {
/// <param name="methodName">Name of the method.</param> if(type == null) {
/// <param name="genericTypes">The generic types.</param> throw new ArgumentNullException(nameof(type));
/// <param name="parameterTypes">The parameter types.</param> }
/// <returns>
/// An object that represents the method with the specified name. if(methodName == null) {
/// </returns> throw new ArgumentNullException(nameof(methodName));
/// <exception cref="System.Reflection.AmbiguousMatchException"> }
/// The exception that is thrown when binding to a member results in more than one member matching the
/// binding criteria. This class cannot be inherited. if(genericTypes == null) {
/// </exception> throw new ArgumentNullException(nameof(genericTypes));
public static MethodInfo GetMethod( }
this Type type,
BindingFlags bindingFlags, if(parameterTypes == null) {
string methodName, throw new ArgumentNullException(nameof(parameterTypes));
Type[] genericTypes, }
Type[] parameterTypes)
{ List<MethodInfo> methods = type
if (type == null)
throw new ArgumentNullException(nameof(type));
if (methodName == null)
throw new ArgumentNullException(nameof(methodName));
if (genericTypes == null)
throw new ArgumentNullException(nameof(genericTypes));
if (parameterTypes == null)
throw new ArgumentNullException(nameof(parameterTypes));
var 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)
.Select(mi => mi.MakeGenericMethod(genericTypes)) .Select(mi => mi.MakeGenericMethod(genericTypes))
.Where(mi => mi.GetParameters().Select(pi => pi.ParameterType).SequenceEqual(parameterTypes)) .Where(mi => mi.GetParameters().Select(pi => pi.ParameterType).SequenceEqual(parameterTypes))
.ToList(); .ToList();
return methods.Count > 1 ? throw new AmbiguousMatchException() : methods.FirstOrDefault(); return methods.Count > 1 ? throw new AmbiguousMatchException() : methods.FirstOrDefault();
} }
/// <summary> /// <summary>
/// Determines whether this instance is class. /// Determines whether this instance is class.
/// </summary> /// </summary>
/// <param name="type">The type.</param> /// <param name="type">The type.</param>
/// <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.
/// </summary> /// </summary>
/// <param name="type">The type.</param> /// <param name="type">The type.</param>
/// <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.
/// </summary> /// </summary>
/// <param name="type">The type.</param> /// <param name="type">The type.</param>
/// <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.
/// </summary> /// </summary>
/// <param name="type">The type.</param> /// <param name="type">The type.</param>
/// <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].
/// </summary> /// </summary>
/// <param name="type">The type.</param> /// <param name="type">The type.</param>
/// <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].
/// </summary> /// </summary>
/// <param name="type">The type.</param> /// <param name="type">The type.</param>
/// <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].
/// </summary> /// </summary>
/// <param name="type">The type.</param> /// <param name="type">The type.</param>
/// <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.
/// </summary> /// </summary>
/// <param name="type">The type.</param> /// <param name="type">The type.</param>
/// <param name="attributeType">Type of the attribute.</param> /// <param name="attributeType">Type of the attribute.</param>
/// <param name="inherit">if set to <c>true</c> [inherit].</param> /// <param name="inherit">if set to <c>true</c> [inherit].</param>
/// <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>
/// Gets the custom attributes. /// Gets the custom attributes.
/// </summary> /// </summary>
/// <param name="type">The type.</param> /// <param name="type">The type.</param>
/// <param name="attributeType">Type of the attribute.</param> /// <param name="attributeType">Type of the attribute.</param>
/// <param name="inherit">if set to <c>true</c> [inherit].</param> /// <param name="inherit">if set to <c>true</c> [inherit].</param>
/// <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>
/// Determines whether [is generic type definition]. /// Determines whether [is generic type definition].
/// </summary> /// </summary>
/// <param name="type">The type.</param> /// <param name="type">The type.</param>
/// <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.
/// </summary> /// </summary>
/// <param name="type">The type.</param> /// <param name="type">The type.</param>
/// <returns>returns a type of data.</returns> /// <returns>returns a type of data.</returns>
public static Type BaseType(this Type type) => type.GetTypeInfo().BaseType; public static Type BaseType(this Type type) => type.GetTypeInfo().BaseType;
/// <summary> /// <summary>
/// Assemblies the specified type. /// Assemblies the specified type.
/// </summary> /// </summary>
/// <param name="type">The type.</param> /// <param name="type">The type.</param>
/// <returns>returns an Assembly object.</returns> /// <returns>returns an Assembly object.</returns>
public static Assembly Assembly(this Type type) => type.GetTypeInfo().Assembly; public static Assembly Assembly(this Type type) => type.GetTypeInfo().Assembly;
/// <summary> /// <summary>
/// Determines whether [is i enumerable request]. /// Determines whether [is i enumerable request].
/// </summary> /// </summary>
/// <param name="type">The type.</param> /// <param name="type">The type.</param>
/// <returns> /// <returns>
/// <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<>);
#endregion #endregion
/// <summary> /// <summary>
/// Tries to parse using the basic types. /// Tries to parse using the basic types.
/// </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>
/// <param name="result">The result.</param> /// <param name="result">The result.</param>
/// <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>
/// Tries to parse using the basic types. /// Tries to parse using the basic types.
/// </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>
/// <param name="result">The result.</param> /// <param name="result">The result.</param>
/// <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); }
}
/// <summary>
/// <summary> /// Tries the type of the set basic value to a property.
/// Tries the type of the set basic value to a property. /// </summary>
/// </summary> /// <param name="property">The property.</param>
/// <param name="property">The property.</param> /// <param name="value">The value.</param>
/// <param name="value">The value.</param> /// <param name="obj">The object.</param>
/// <param name="obj">The object.</param> /// <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 Boolean TrySetBasicType(this PropertyInfo property, Object value, Object obj) {
public static bool TrySetBasicType(this PropertyInfo property, object value, object obj) try {
{ if(property.PropertyType.TryParseBasicType(value, out Object propertyValue)) {
try property.SetValue(obj, propertyValue);
{ return true;
if (property.PropertyType.TryParseBasicType(value, out var propertyValue)) }
{ } catch {
property.SetValue(obj, propertyValue); // swallow
return true; }
}
} return false;
catch }
{
// swallow /// <summary>
} /// Tries the type of the set to an array a basic type.
/// </summary>
return false; /// <param name="type">The type.</param>
} /// <param name="value">The value.</param>
/// <param name="array">The array.</param>
/// <summary> /// <param name="index">The index.</param>
/// Tries the type of the set to an array a basic type. /// <returns>
/// </summary> /// <c>true</c> if parsing was successful; otherwise, <c>false</c>.
/// <param name="type">The type.</param> /// </returns>
/// <param name="value">The value.</param> public static Boolean TrySetArrayBasicType(this Type type, Object value, Array array, Int32 index) {
/// <param name="array">The array.</param> try {
/// <param name="index">The index.</param> if(value == null) {
/// <returns> array.SetValue(null, index);
/// <c>true</c> if parsing was successful; otherwise, <c>false</c>. return true;
/// </returns> }
public static bool TrySetArrayBasicType(this Type type, object value, Array array, int index)
{ if(type.TryParseBasicType(value, out Object propertyValue)) {
try array.SetValue(propertyValue, index);
{ return true;
if (value == null) }
{
array.SetValue(null, index); if(type.GetTypeInfo().IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>)) {
return true; array.SetValue(null, index);
} return true;
}
if (type.TryParseBasicType(value, out var propertyValue)) } catch {
{ // swallow
array.SetValue(propertyValue, index); }
return true;
} return false;
}
if (type.GetTypeInfo().IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>))
{ /// <summary>
array.SetValue(null, index); /// Tries to set a property array with another array.
return true; /// </summary>
} /// <param name="propertyInfo">The property.</param>
} /// <param name="value">The value.</param>
catch /// <param name="obj">The object.</param>
{ /// <returns>
// swallow /// <c>true</c> if parsing was successful; otherwise, <c>false</c>.
} /// </returns>
public static Boolean TrySetArray(this PropertyInfo propertyInfo, IEnumerable<Object> value, Object obj) {
return false; Type elementType = propertyInfo.PropertyType.GetElementType();
}
if(elementType == null) {
/// <summary> return false;
/// Tries to set a property array with another array. }
/// </summary>
/// <param name="propertyInfo">The property.</param> Array targetArray = Array.CreateInstance(elementType, value.Count());
/// <param name="value">The value.</param>
/// <param name="obj">The object.</param> Int32 i = 0;
/// <returns>
/// <c>true</c> if parsing was successful; otherwise, <c>false</c>. foreach(Object sourceElement in value) {
/// </returns> Boolean result = elementType.TrySetArrayBasicType(sourceElement, targetArray, i++);
public static bool TrySetArray(this PropertyInfo propertyInfo, IEnumerable<object> value, object obj)
{ if(!result) {
var elementType = propertyInfo.PropertyType.GetElementType(); return false;
}
if (elementType == null) }
return false;
propertyInfo.SetValue(obj, targetArray);
var targetArray = Array.CreateInstance(elementType, value.Count());
return true;
var i = 0; }
foreach (var sourceElement in value) /// <summary>
{ /// Gets property actual value or <c>PropertyDisplayAttribute.DefaultValue</c> if presented.
var result = elementType.TrySetArrayBasicType(sourceElement, targetArray, i++); ///
/// If the <c>PropertyDisplayAttribute.Format</c> value is presented, the property value
if (!result) return false; /// will be formatted accordingly.
} ///
/// If the object contains a null value, a empty string will be returned.
propertyInfo.SetValue(obj, targetArray); /// </summary>
/// <param name="propertyInfo">The property information.</param>
return true; /// <param name="obj">The object.</param>
} /// <returns>The property value or null.</returns>
public static String ToFormattedString(this PropertyInfo propertyInfo, Object obj) {
/// <summary> try {
/// Gets property actual value or <c>PropertyDisplayAttribute.DefaultValue</c> if presented. Object value = propertyInfo.GetValue(obj);
/// PropertyDisplayAttribute attr = Runtime.AttributeCache.RetrieveOne<PropertyDisplayAttribute>(propertyInfo);
/// If the <c>PropertyDisplayAttribute.Format</c> value is presented, the property value
/// will be formatted accordingly. if(attr == null) {
/// return value?.ToString() ?? String.Empty;
/// If the object contains a null value, a empty string will be returned. }
/// </summary>
/// <param name="propertyInfo">The property information.</param> Object valueToFormat = value ?? attr.DefaultValue;
/// <param name="obj">The object.</param>
/// <returns>The property value or null.</returns> return String.IsNullOrEmpty(attr.Format)
public static string ToFormattedString(this PropertyInfo propertyInfo, object obj) ? (valueToFormat?.ToString() ?? String.Empty)
{ : ConvertObjectAndFormat(propertyInfo.PropertyType, valueToFormat, attr.Format);
try } catch {
{ return null;
var value = propertyInfo.GetValue(obj); }
var attr = Runtime.AttributeCache.RetrieveOne<PropertyDisplayAttribute>(propertyInfo); }
if (attr == null) return value?.ToString() ?? string.Empty; /// <summary>
/// Gets a MethodInfo from a Property Get method.
var valueToFormat = value ?? attr.DefaultValue; /// </summary>
/// <param name="propertyInfo">The property information.</param>
return string.IsNullOrEmpty(attr.Format) /// <param name="nonPublic">if set to <c>true</c> [non public].</param>
? (valueToFormat?.ToString() ?? string.Empty) /// <returns>
: ConvertObjectAndFormat(propertyInfo.PropertyType, valueToFormat, attr.Format); /// The cached MethodInfo.
} /// </returns>
catch public static Func<Object, Object> GetCacheGetMethod(this PropertyInfo propertyInfo, Boolean nonPublic = false) {
{ Tuple<Boolean, PropertyInfo> key = Tuple.Create(!nonPublic, propertyInfo);
return null;
} return !nonPublic && !CacheGetMethods.Value.ContainsKey(key) && !propertyInfo.GetGetMethod(true).IsPublic
} ? null
: CacheGetMethods.Value
/// <summary> .GetOrAdd(key,
/// Gets a MethodInfo from a Property Get method. x => y => x.Item2.GetGetMethod(nonPublic).Invoke(y, null));
/// </summary> }
/// <param name="propertyInfo">The property information.</param>
/// <param name="nonPublic">if set to <c>true</c> [non public].</param> /// <summary>
/// <returns> /// Gets a MethodInfo from a Property Set method.
/// The cached MethodInfo. /// </summary>
/// </returns> /// <param name="propertyInfo">The property information.</param>
public static Func<object, object> GetCacheGetMethod(this PropertyInfo propertyInfo, bool nonPublic = false) /// <param name="nonPublic">if set to <c>true</c> [non public].</param>
{ /// <returns>
var key = Tuple.Create(!nonPublic, propertyInfo); /// The cached MethodInfo.
/// </returns>
return !nonPublic && !CacheGetMethods.Value.ContainsKey(key) && !propertyInfo.GetGetMethod(true).IsPublic public static Action<Object, Object[]> GetCacheSetMethod(this PropertyInfo propertyInfo, Boolean nonPublic = false) {
? null Tuple<Boolean, PropertyInfo> key = Tuple.Create(!nonPublic, propertyInfo);
: CacheGetMethods.Value
.GetOrAdd(key, return !nonPublic && !CacheSetMethods.Value.ContainsKey(key) && !propertyInfo.GetSetMethod(true).IsPublic
x => y => x.Item2.GetGetMethod(nonPublic).Invoke(y, null)); ? null
} : CacheSetMethods.Value
.GetOrAdd(key,
/// <summary> x => (obj, args) => x.Item2.GetSetMethod(nonPublic).Invoke(obj, args));
/// Gets a MethodInfo from a Property Set method. }
/// </summary>
/// <param name="propertyInfo">The property information.</param> private static String ConvertObjectAndFormat(Type propertyType, Object value, String format) => propertyType == typeof(DateTime) || propertyType == typeof(DateTime?)
/// <param name="nonPublic">if set to <c>true</c> [non public].</param> ? Convert.ToDateTime(value).ToString(format)
/// <returns> : propertyType == typeof(Int32) || propertyType == typeof(Int32?)
/// The cached MethodInfo. ? Convert.ToInt32(value).ToString(format)
/// </returns> : propertyType == typeof(Decimal) || propertyType == typeof(Decimal?)
public static Action<object, object[]> GetCacheSetMethod(this PropertyInfo propertyInfo, bool nonPublic = false) ? Convert.ToDecimal(value).ToString(format)
{ : propertyType == typeof(Double) || propertyType == typeof(Double?)
var key = Tuple.Create(!nonPublic, propertyInfo); ? Convert.ToDouble(value).ToString(format)
: propertyType == typeof(Byte) || propertyType == typeof(Byte?)
return !nonPublic && !CacheSetMethods.Value.ContainsKey(key) && !propertyInfo.GetSetMethod(true).IsPublic ? Convert.ToByte(value).ToString(format)
? null : value?.ToString() ?? String.Empty;
: CacheSetMethods.Value }
.GetOrAdd(key,
x => (obj, args) => x.Item2.GetSetMethod(nonPublic).Invoke(obj, args));
}
private static string ConvertObjectAndFormat(Type propertyType, object value, string format)
{
if (propertyType == typeof(DateTime) || propertyType == typeof(DateTime?))
return Convert.ToDateTime(value).ToString(format);
if (propertyType == typeof(int) || propertyType == typeof(int?))
return Convert.ToInt32(value).ToString(format);
if (propertyType == typeof(decimal) || propertyType == typeof(decimal?))
return Convert.ToDecimal(value).ToString(format);
if (propertyType == typeof(double) || propertyType == typeof(double?))
return Convert.ToDouble(value).ToString(format);
if (propertyType == typeof(byte) || propertyType == typeof(byte?))
return Convert.ToByte(value).ToString(format);
return value?.ToString() ?? string.Empty;
}
}
} }
File diff suppressed because it is too large Load Diff
+141 -162
View File
@@ -1,168 +1,147 @@
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>
/// Provides various extension methods for value types and structs.
/// </summary>
public static class ValueTypeExtensions {
/// <summary> /// <summary>
/// Provides various extension methods for value types and structs. /// Clamps the specified value between the minimum and the maximum.
/// </summary> /// </summary>
public static class ValueTypeExtensions /// <typeparam name="T">The type of value to clamp.</typeparam>
{ /// <param name="value">The value.</param>
/// <summary> /// <param name="min">The minimum.</param>
/// Clamps the specified value between the minimum and the maximum. /// <param name="max">The maximum.</param>
/// </summary> /// <returns>A value that indicates the relative order of the objects being compared.</returns>
/// <typeparam name="T">The type of value to clamp.</typeparam> public static T Clamp<T>(this T value, T min, T max)
/// <param name="value">The value.</param> where T : struct, IComparable => value.CompareTo(min) < 0 ? min : value.CompareTo(max) > 0 ? max : value;
/// <param name="min">The minimum.</param>
/// <param name="max">The maximum.</param> /// <summary>
/// <returns>A value that indicates the relative order of the objects being compared.</returns> /// Clamps the specified value between the minimum and the maximum.
public static T Clamp<T>(this T value, T min, T max) /// </summary>
where T : struct, IComparable /// <param name="value">The value.</param>
{ /// <param name="min">The minimum.</param>
if (value.CompareTo(min) < 0) return min; /// <param name="max">The maximum.</param>
/// <returns>A value that indicates the relative order of the objects being compared.</returns>
return value.CompareTo(max) > 0 ? max : value; public static Int32 Clamp(this Int32 value, Int32 min, Int32 max)
} => value < min ? min : (value > max ? max : value);
/// <summary> /// <summary>
/// Clamps the specified value between the minimum and the maximum. /// Determines whether the specified value is between a minimum and a maximum value.
/// </summary> /// </summary>
/// <param name="value">The value.</param> /// <typeparam name="T">The type of value to check.</typeparam>
/// <param name="min">The minimum.</param> /// <param name="value">The value.</param>
/// <param name="max">The maximum.</param> /// <param name="min">The minimum.</param>
/// <returns>A value that indicates the relative order of the objects being compared.</returns> /// <param name="max">The maximum.</param>
public static int Clamp(this int value, int min, int max) /// <returns>
=> value < min ? min : (value > max ? max : value); /// <c>true</c> if the specified minimum is between; otherwise, <c>false</c>.
/// </returns>
/// <summary> public static Boolean IsBetween<T>(this T value, T min, T max)
/// Determines whether the specified value is between a minimum and a maximum value. where T : struct, IComparable => value.CompareTo(min) >= 0 && value.CompareTo(max) <= 0;
/// </summary>
/// <typeparam name="T">The type of value to check.</typeparam> /// <summary>
/// <param name="value">The value.</param> /// Converts an array of bytes into the given struct type.
/// <param name="min">The minimum.</param> /// </summary>
/// <param name="max">The maximum.</param> /// <typeparam name="T">The type of structure to convert.</typeparam>
/// <returns> /// <param name="data">The data.</param>
/// <c>true</c> if the specified minimum is between; otherwise, <c>false</c>. /// <returns>a struct type derived from convert an array of bytes ref=ToStruct".</returns>
/// </returns> public static T ToStruct<T>(this Byte[] data)
public static bool IsBetween<T>(this T value, T min, T max) where T : struct => ToStruct<T>(data, 0, data.Length);
where T : struct, IComparable
{ /// <summary>
return value.CompareTo(min) >= 0 && value.CompareTo(max) <= 0; /// Converts an array of bytes into the given struct type.
} /// </summary>
/// <typeparam name="T">The type of structure to convert.</typeparam>
/// <summary> /// <param name="data">The data.</param>
/// Converts an array of bytes into the given struct type. /// <param name="offset">The offset.</param>
/// </summary> /// <param name="length">The length.</param>
/// <typeparam name="T">The type of structure to convert.</typeparam> /// <returns>
/// <param name="data">The data.</param> /// A managed object containing the data pointed to by the ptr parameter.
/// <returns>a struct type derived from convert an array of bytes ref=ToStruct".</returns> /// </returns>
public static T ToStruct<T>(this byte[] data) /// <exception cref="ArgumentNullException">data.</exception>
where T : struct public static T ToStruct<T>(this Byte[] data, Int32 offset, Int32 length)
{ where T : struct {
return ToStruct<T>(data, 0, data.Length); if(data == null) {
} throw new ArgumentNullException(nameof(data));
}
/// <summary>
/// Converts an array of bytes into the given struct type. Byte[] buffer = new Byte[length];
/// </summary> Array.Copy(data, offset, buffer, 0, buffer.Length);
/// <typeparam name="T">The type of structure to convert.</typeparam> GCHandle handle = GCHandle.Alloc(GetStructBytes<T>(buffer), GCHandleType.Pinned);
/// <param name="data">The data.</param>
/// <param name="offset">The offset.</param> try {
/// <param name="length">The length.</param> return Marshal.PtrToStructure<T>(handle.AddrOfPinnedObject());
/// <returns> } finally {
/// A managed object containing the data pointed to by the ptr parameter. handle.Free();
/// </returns> }
/// <exception cref="ArgumentNullException">data.</exception> }
public static T ToStruct<T>(this byte[] data, int offset, int length)
where T : struct /// <summary>
{ /// Converts a struct to an array of bytes.
if (data == null) /// </summary>
throw new ArgumentNullException(nameof(data)); /// <typeparam name="T">The type of structure to convert.</typeparam>
/// <param name="obj">The object.</param>
var buffer = new byte[length]; /// <returns>A byte array containing the results of encoding the specified set of characters.</returns>
Array.Copy(data, offset, buffer, 0, buffer.Length); public static Byte[] ToBytes<T>(this T obj)
var handle = GCHandle.Alloc(GetStructBytes<T>(buffer), GCHandleType.Pinned); where T : struct {
Byte[] data = new Byte[Marshal.SizeOf(obj)];
try GCHandle handle = GCHandle.Alloc(data, GCHandleType.Pinned);
{
return Marshal.PtrToStructure<T>(handle.AddrOfPinnedObject()); try {
} Marshal.StructureToPtr(obj, handle.AddrOfPinnedObject(), false);
finally return GetStructBytes<T>(data);
{ } finally {
handle.Free(); handle.Free();
} }
} }
/// <summary> /// <summary>
/// Converts a struct to an array of bytes. /// Swaps the endianness of an unsigned long to an unsigned integer.
/// </summary> /// </summary>
/// <typeparam name="T">The type of structure to convert.</typeparam> /// <param name="longBytes">The bytes contained in a long.</param>
/// <param name="obj">The object.</param> /// <returns>
/// <returns>A byte array containing the results of encoding the specified set of characters.</returns> /// A 32-bit unsigned integer equivalent to the ulong
public static byte[] ToBytes<T>(this T obj) /// contained in longBytes.
where T : struct /// </returns>
{ public static UInt32 SwapEndianness(this UInt64 longBytes)
var data = new byte[Marshal.SizeOf(obj)]; => (UInt32)(((longBytes & 0x000000ff) << 24) +
var handle = GCHandle.Alloc(data, GCHandleType.Pinned); ((longBytes & 0x0000ff00) << 8) +
((longBytes & 0x00ff0000) >> 8) +
try ((longBytes & 0xff000000) >> 24));
{
Marshal.StructureToPtr(obj, handle.AddrOfPinnedObject(), false); private static Byte[] GetStructBytes<T>(Byte[] data) {
return GetStructBytes<T>(data); if(data == null) {
} throw new ArgumentNullException(nameof(data));
finally }
{
handle.Free(); #if !NETSTANDARD1_3
} FieldInfo[] fields = typeof(T).GetTypeInfo()
} .GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
/// <summary>
/// Swaps the endianness of an unsigned long to an unsigned integer.
/// </summary>
/// <param name="longBytes">The bytes contained in a long.</param>
/// <returns>
/// A 32-bit unsigned integer equivalent to the ulong
/// contained in longBytes.
/// </returns>
public static uint SwapEndianness(this ulong longBytes)
=> (uint) (((longBytes & 0x000000ff) << 24) +
((longBytes & 0x0000ff00) << 8) +
((longBytes & 0x00ff0000) >> 8) +
((longBytes & 0xff000000) >> 24));
private static byte[] GetStructBytes<T>(byte[] data)
{
if (data == null)
throw new ArgumentNullException(nameof(data));
#if !NETSTANDARD1_3
var fields = typeof(T).GetTypeInfo()
.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); }
} }
}
return data;
return data; }
} }
}
} }
+301 -327
View File
@@ -1,331 +1,305 @@
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>
/// Extension methods.
/// </summary>
public static partial class Extensions {
/// <summary> /// <summary>
/// Extension methods. /// 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.
/// </summary> /// </summary>
public static partial class Extensions /// <typeparam name="T">The type of the source.</typeparam>
{ /// <param name="source">The source.</param>
/// <summary> /// <param name="target">The target.</param>
/// Iterates over the public, instance, readable properties of the source and /// <returns>Number of properties that was copied successful.</returns>
/// tries to write a compatible value to a public, instance, writable property in the destination. public static Int32 CopyPropertiesTo<T>(this T source, Object target)
/// </summary> where T : class {
/// <typeparam name="T">The type of the source.</typeparam> IEnumerable<String> copyable = GetCopyableProperties(target);
/// <param name="source">The source.</param> return copyable.Any()
/// <param name="target">The target.</param> ? CopyOnlyPropertiesTo(source, target, copyable.ToArray())
/// <returns>Number of properties that was copied successful.</returns> : CopyPropertiesTo(source, target, null);
public static int CopyPropertiesTo<T>(this T source, object target) }
where T : class
{ /// <summary>
var copyable = GetCopyableProperties(target); /// Iterates over the public, instance, readable properties of the source and
return copyable.Any() /// tries to write a compatible value to a public, instance, writable property in the destination.
? CopyOnlyPropertiesTo(source, target, copyable.ToArray()) /// </summary>
: CopyPropertiesTo(source, target, null); /// <param name="source">The source.</param>
} /// <param name="target">The destination.</param>
/// <param name="ignoreProperties">The ignore properties.</param>
/// <summary> /// <returns>
/// Iterates over the public, instance, readable properties of the source and /// Number of properties that were successfully copied.
/// tries to write a compatible value to a public, instance, writable property in the destination. /// </returns>
/// </summary> public static Int32 CopyPropertiesTo(this Object source, Object target, String[] ignoreProperties = null)
/// <param name="source">The source.</param> => Components.ObjectMapper.Copy(source, target, null, ignoreProperties);
/// <param name="target">The destination.</param>
/// <param name="ignoreProperties">The ignore properties.</param> /// <summary>
/// <returns> /// Iterates over the public, instance, readable properties of the source and
/// Number of properties that were successfully copied. /// tries to write a compatible value to a public, instance, writable property in the destination.
/// </returns> /// </summary>
public static int CopyPropertiesTo(this object source, object target, string[] ignoreProperties = null) /// <typeparam name="T">The type of the source.</typeparam>
=> Components.ObjectMapper.Copy(source, target, null, ignoreProperties); /// <param name="source">The source.</param>
/// <param name="target">The target.</param>
/// <summary> /// <returns>Number of properties that was copied successful.</returns>
/// Iterates over the public, instance, readable properties of the source and public static Int32 CopyOnlyPropertiesTo<T>(this T source, Object target)
/// tries to write a compatible value to a public, instance, writable property in the destination. where T : class => CopyOnlyPropertiesTo(source, target, null);
/// </summary>
/// <typeparam name="T">The type of the source.</typeparam> /// <summary>
/// <param name="source">The source.</param> /// Iterates over the public, instance, readable properties of the source and
/// <param name="target">The target.</param> /// tries to write a compatible value to a public, instance, writable property in the destination.
/// <returns>Number of properties that was copied successful.</returns> /// </summary>
public static int CopyOnlyPropertiesTo<T>(this T source, object target) /// <param name="source">The source.</param>
where T : class /// <param name="target">The destination.</param>
{ /// <param name="propertiesToCopy">Properties to copy.</param>
return CopyOnlyPropertiesTo(source, target, null); /// <returns>
} /// Number of properties that were successfully copied.
/// </returns>
/// <summary> public static Int32 CopyOnlyPropertiesTo(this Object source, Object target, String[] propertiesToCopy) => Components.ObjectMapper.Copy(source, target, propertiesToCopy);
/// 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. /// <summary>
/// </summary> /// Copies the properties to new instance of T.
/// <param name="source">The source.</param> /// </summary>
/// <param name="target">The destination.</param> /// <typeparam name="T">The new object type.</typeparam>
/// <param name="propertiesToCopy">Properties to copy.</param> /// <param name="source">The source.</param>
/// <returns> /// <param name="ignoreProperties">The ignore properties.</param>
/// Number of properties that were successfully copied. /// <returns>
/// </returns> /// The specified type with properties copied.
public static int CopyOnlyPropertiesTo(this object source, object target, string[] propertiesToCopy) /// </returns>
{ /// <exception cref="ArgumentNullException">source.</exception>
return Components.ObjectMapper.Copy(source, target, propertiesToCopy); public static T DeepClone<T>(this T source, String[] ignoreProperties = null)
} where T : class => source.CopyPropertiesToNew<T>(ignoreProperties);
/// <summary> /// <summary>
/// Copies the properties to new instance of T. /// Copies the properties to new instance of T.
/// </summary> /// </summary>
/// <typeparam name="T">The new object type.</typeparam> /// <typeparam name="T">The new object type.</typeparam>
/// <param name="source">The source.</param> /// <param name="source">The source.</param>
/// <param name="ignoreProperties">The ignore properties.</param> /// <param name="ignoreProperties">The ignore properties.</param>
/// <returns> /// <returns>
/// 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 CopyPropertiesToNew<T>(this Object source, String[] ignoreProperties = null)
where T : class where T : class {
{ if(source == null) {
return source.CopyPropertiesToNew<T>(ignoreProperties); throw new ArgumentNullException(nameof(source));
} }
/// <summary> T target = Activator.CreateInstance<T>();
/// Copies the properties to new instance of T. IEnumerable<String> copyable = target.GetCopyableProperties();
/// </summary>
/// <typeparam name="T">The new object type.</typeparam> _ = copyable.Any() ? source.CopyOnlyPropertiesTo(target, copyable.ToArray()) : source.CopyPropertiesTo(target, ignoreProperties);
/// <param name="source">The source.</param>
/// <param name="ignoreProperties">The ignore properties.</param> return target;
/// <returns> }
/// The specified type with properties copied.
/// </returns> /// <summary>
/// <exception cref="ArgumentNullException">source.</exception> /// Copies the only properties to new instance of T.
public static T CopyPropertiesToNew<T>(this object source, string[] ignoreProperties = null) /// </summary>
where T : class /// <typeparam name="T">Object Type.</typeparam>
{ /// <param name="source">The source.</param>
if (source == null) /// <param name="propertiesToCopy">The properties to copy.</param>
throw new ArgumentNullException(nameof(source)); /// <returns>
/// The specified type with properties copied.
var target = Activator.CreateInstance<T>(); /// </returns>
var copyable = target.GetCopyableProperties(); /// <exception cref="ArgumentNullException">source.</exception>
public static T CopyOnlyPropertiesToNew<T>(this Object source, String[] propertiesToCopy)
if (copyable.Any()) where T : class {
source.CopyOnlyPropertiesTo(target, copyable.ToArray()); if(source == null) {
else throw new ArgumentNullException(nameof(source));
source.CopyPropertiesTo(target, ignoreProperties); }
return target; T target = Activator.CreateInstance<T>();
} _ = source.CopyOnlyPropertiesTo(target, propertiesToCopy);
return target;
/// <summary> }
/// Copies the only properties to new instance of T.
/// </summary> /// <summary>
/// <typeparam name="T">Object Type.</typeparam> /// Iterates over the keys of the source and tries to write a compatible value to a public,
/// <param name="source">The source.</param> /// instance, writable property in the destination.
/// <param name="propertiesToCopy">The properties to copy.</param> /// </summary>
/// <returns> /// <param name="source">The source.</param>
/// The specified type with properties copied. /// <param name="target">The target.</param>
/// </returns> /// <param name="ignoreKeys">The ignore keys.</param>
/// <exception cref="ArgumentNullException">source.</exception> /// <returns>Number of properties that was copied successful.</returns>
public static T CopyOnlyPropertiesToNew<T>(this object source, string[] propertiesToCopy) public static Int32 CopyKeyValuePairTo(
where T : class this IDictionary<String, Object> source,
{ Object target,
if (source == null) String[] ignoreKeys = null) => Components.ObjectMapper.Copy(source, target, null, ignoreKeys);
throw new ArgumentNullException(nameof(source));
/// <summary>
var target = Activator.CreateInstance<T>(); /// Measures the elapsed time of the given action as a TimeSpan
source.CopyOnlyPropertiesTo(target, propertiesToCopy); /// This method uses a high precision Stopwatch.
return target; /// </summary>
} /// <param name="target">The target.</param>
/// <returns>
/// <summary> /// A time interval that represents a specified time, where the specification is in units of ticks.
/// Iterates over the keys of the source and tries to write a compatible value to a public, /// </returns>
/// instance, writable property in the destination. /// <exception cref="ArgumentNullException">target.</exception>
/// </summary> public static TimeSpan Benchmark(this Action target) {
/// <param name="source">The source.</param> if(target == null) {
/// <param name="target">The target.</param> throw new ArgumentNullException(nameof(target));
/// <param name="ignoreKeys">The ignore keys.</param> }
/// <returns>Number of properties that was copied successful.</returns>
public static int CopyKeyValuePairTo( Stopwatch sw = new Stopwatch();
this IDictionary<string, object> source,
object target, try {
string[] ignoreKeys = null) sw.Start();
{ target.Invoke();
return Components.ObjectMapper.Copy(source, target, null, ignoreKeys); } catch {
} // swallow
} finally {
/// <summary> sw.Stop();
/// Measures the elapsed time of the given action as a TimeSpan }
/// This method uses a high precision Stopwatch.
/// </summary> return TimeSpan.FromTicks(sw.ElapsedTicks);
/// <param name="target">The target.</param> }
/// <returns>
/// A time interval that represents a specified time, where the specification is in units of ticks. /// <summary>
/// </returns> /// Does the specified action.
/// <exception cref="ArgumentNullException">target.</exception> /// </summary>
public static TimeSpan Benchmark(this Action target) /// <param name="action">The action.</param>
{ /// <param name="retryInterval">The retry interval.</param>
if (target == null) /// <param name="retryCount">The retry count.</param>
throw new ArgumentNullException(nameof(target)); public static void Retry(
this Action action,
var sw = new Stopwatch(); TimeSpan retryInterval = default,
Int32 retryCount = 3) {
try if(action == null) {
{ throw new ArgumentNullException(nameof(action));
sw.Start(); }
target.Invoke();
} _ = Retry<Object>(() => {
catch action();
{ return null;
// swallow },
} retryInterval,
finally retryCount);
{ }
sw.Stop();
} /// <summary>
/// Does the specified action.
return TimeSpan.FromTicks(sw.ElapsedTicks); /// </summary>
} /// <typeparam name="T">The type of the source.</typeparam>
/// <param name="action">The action.</param>
/// <summary> /// <param name="retryInterval">The retry interval.</param>
/// Does the specified action. /// <param name="retryCount">The retry count.</param>
/// </summary> /// <returns>
/// <param name="action">The action.</param> /// The return value of the method that this delegate encapsulates.
/// <param name="retryInterval">The retry interval.</param> /// </returns>
/// <param name="retryCount">The retry count.</param> /// <exception cref="ArgumentNullException">action.</exception>
public static void Retry( /// <exception cref="AggregateException">Represents one or many errors that occur during application execution.</exception>
this Action action, public static T Retry<T>(
TimeSpan retryInterval = default, this Func<T> action,
int retryCount = 3) TimeSpan retryInterval = default,
{ Int32 retryCount = 3) {
if (action == null) if(action == null) {
throw new ArgumentNullException(nameof(action)); throw new ArgumentNullException(nameof(action));
}
Retry<object>(() =>
{ if(retryInterval == default) {
action(); retryInterval = TimeSpan.FromSeconds(1);
return null; }
},
retryInterval, List<Exception> exceptions = new List<Exception>();
retryCount);
} for(Int32 retry = 0; retry < retryCount; retry++) {
try {
/// <summary> if(retry > 0) {
/// Does the specified action. Task.Delay(retryInterval).Wait();
/// </summary> }
/// <typeparam name="T">The type of the source.</typeparam>
/// <param name="action">The action.</param> return action();
/// <param name="retryInterval">The retry interval.</param> } catch(Exception ex) {
/// <param name="retryCount">The retry count.</param> exceptions.Add(ex);
/// <returns> }
/// The return value of the method that this delegate encapsulates. }
/// </returns>
/// <exception cref="ArgumentNullException">action.</exception> throw new AggregateException(exceptions);
/// <exception cref="AggregateException">Represents one or many errors that occur during application execution.</exception> }
public static T Retry<T>(
this Func<T> action, /// <summary>
TimeSpan retryInterval = default, /// Retrieves the exception message, plus all the inner exception messages separated by new lines.
int retryCount = 3) /// </summary>
{ /// <param name="ex">The ex.</param>
if (action == null) /// <param name="priorMessage">The prior message.</param>
throw new ArgumentNullException(nameof(action)); /// <returns>A <see cref="System.String" /> that represents this instance.</returns>
public static String ExceptionMessage(this Exception ex, String priorMessage = "") {
if (retryInterval == default) while(true) {
retryInterval = TimeSpan.FromSeconds(1); if(ex == null) {
throw new ArgumentNullException(nameof(ex));
var exceptions = new List<Exception>(); }
for (var retry = 0; retry < retryCount; retry++) String fullMessage = String.IsNullOrWhiteSpace(priorMessage)
{
try
{
if (retry > 0)
Task.Delay(retryInterval).Wait();
return action();
}
catch (Exception ex)
{
exceptions.Add(ex);
}
}
throw new AggregateException(exceptions);
}
/// <summary>
/// Retrieves the exception message, plus all the inner exception messages separated by new lines.
/// </summary>
/// <param name="ex">The ex.</param>
/// <param name="priorMessage">The prior message.</param>
/// <returns>A <see cref="System.String" /> that represents this instance.</returns>
public static string ExceptionMessage(this Exception ex, string priorMessage = "")
{
while (true)
{
if (ex == null)
throw new ArgumentNullException(nameof(ex));
var 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;
priorMessage = fullMessage; ex = ex.InnerException;
} priorMessage = fullMessage;
} }
}
/// <summary>
/// Gets the copyable properties. /// <summary>
/// </summary> /// Gets the copyable properties.
/// <param name="obj">The object.</param> /// </summary>
/// <returns> /// <param name="obj">The object.</param>
/// Array of properties. /// <returns>
/// </returns> /// Array of properties.
/// <exception cref="ArgumentNullException">model.</exception> /// </returns>
public static IEnumerable<string> GetCopyableProperties(this object obj) /// <exception cref="ArgumentNullException">model.</exception>
{ 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
.RetrieveAllProperties(obj.GetType(), true) return Runtime.PropertyTypeCache
.Select(x => new { x.Name, HasAttribute = Runtime.AttributeCache.RetrieveOne<CopyableAttribute>(x) != null}) .RetrieveAllProperties(obj.GetType(), true)
.Where(x => x.HasAttribute) .Select(x => new { x.Name, HasAttribute = Runtime.AttributeCache.RetrieveOne<CopyableAttribute>(x) != null })
.Select(x => x.Name); .Where(x => x.HasAttribute)
} .Select(x => x.Name);
}
/// <summary>
/// Returns true if the object is valid. /// <summary>
/// </summary> /// Returns true if the object is valid.
/// <param name="obj">The object.</param> /// </summary>
/// <returns> /// <param name="obj">The object.</param>
/// <c>true</c> if the specified model is valid; otherwise, <c>false</c>. /// <returns>
/// </returns> /// <c>true</c> if the specified model is valid; otherwise, <c>false</c>.
public static bool IsValid(this object obj) => Runtime.ObjectValidator.IsValid(obj); /// </returns>
public static Boolean IsValid(this Object obj) => Runtime.ObjectValidator.IsValid(obj);
internal static void CreateTarget(
this object source, internal static void CreateTarget(
Type targetType, this Object source,
bool includeNonPublic, Type targetType,
ref object target) Boolean includeNonPublic,
{ 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 Type elementType = targetType.GetElementType();
var elementType = targetType.GetElementType();
if(elementType != null) {
if (elementType != null) target = Array.CreateInstance(elementType, sourceObjectList.Count);
target = Array.CreateInstance(elementType, sourceObjectList.Count); }
break;
default: break;
target = Activator.CreateInstance(targetType, includeNonPublic); default:
break; target = Activator.CreateInstance(targetType, includeNonPublic);
} break;
} }
} }
}
} }
File diff suppressed because it is too large Load Diff
+445 -460
View File
@@ -1,415 +1,401 @@
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>
/// A CSV writer useful for exporting a set of objects.
/// </summary>
/// <example>
/// The following code describes how to save a list of objects into a CSV file.
/// <code>
/// using System.Collections.Generic;
/// using Unosquare.Swan.Formatters;
///
/// class Example
/// {
/// class Person
/// {
/// public string Name { get; set; }
/// public int Age { get; set; }
/// }
///
/// static void Main()
/// {
/// // create a list of people
/// var people = new List&lt;Person&gt;
/// {
/// new Person { Name = "Artyom", Age = 20 },
/// new Person { Name = "Aloy", Age = 18 }
/// }
///
/// // write items inside file.csv
/// CsvWriter.SaveRecords(people, "C:\\Users\\user\\Documents\\file.csv");
///
/// // output
/// // | Name | Age |
/// // | Artyom | 20 |
/// // | Aloy | 18 |
/// }
/// }
/// </code>
/// </example>
public class CsvWriter : IDisposable {
private static readonly PropertyTypeCache TypeCache = new PropertyTypeCache();
private readonly Object _syncLock = new Object();
private readonly Stream _outputStream;
private readonly Encoding _encoding;
private readonly Boolean _leaveStreamOpen;
private Boolean _isDisposing;
private UInt64 _mCount;
#region Constructors
/// <summary> /// <summary>
/// A CSV writer useful for exporting a set of objects. /// Initializes a new instance of the <see cref="CsvWriter" /> class.
/// </summary> /// </summary>
/// <example> /// <param name="outputStream">The output stream.</param>
/// The following code describes how to save a list of objects into a CSV file. /// <param name="leaveOpen">if set to <c>true</c> [leave open].</param>
/// <code> /// <param name="encoding">The encoding.</param>
/// using System.Collections.Generic; public CsvWriter(Stream outputStream, Boolean leaveOpen, Encoding encoding) {
/// using Unosquare.Swan.Formatters; this._outputStream = outputStream;
/// this._encoding = encoding;
/// class Example this._leaveStreamOpen = leaveOpen;
/// { }
/// class Person
/// { /// <summary>
/// public string Name { get; set; } /// Initializes a new instance of the <see cref="CsvWriter"/> class.
/// public int Age { get; set; } /// It automatically closes the stream when disposing this writer.
/// } /// </summary>
/// /// <param name="outputStream">The output stream.</param>
/// static void Main() /// <param name="encoding">The encoding.</param>
/// { public CsvWriter(Stream outputStream, Encoding encoding)
/// // create a list of people : this(outputStream, false, encoding) {
/// var people = new List&lt;Person&gt; // placeholder
/// { }
/// new Person { Name = "Artyom", Age = 20 },
/// new Person { Name = "Aloy", Age = 18 } /// <summary>
/// } /// Initializes a new instance of the <see cref="CsvWriter"/> class.
/// /// It uses the Windows 1252 encoding and automatically closes
/// // write items inside file.csv /// the stream upon disposing this writer.
/// CsvWriter.SaveRecords(people, "C:\\Users\\user\\Documents\\file.csv"); /// </summary>
/// /// <param name="outputStream">The output stream.</param>
/// // output public CsvWriter(Stream outputStream)
/// // | Name | Age | : this(outputStream, false, Definitions.Windows1252Encoding) {
/// // | Artyom | 20 | // placeholder
/// // | Aloy | 18 | }
/// }
/// } /// <summary>
/// </code> /// Initializes a new instance of the <see cref="CsvWriter"/> class.
/// </example> /// It opens the file given file, automatically closes the stream upon
public class CsvWriter : IDisposable /// disposing of this writer, and uses the Windows 1252 encoding.
{ /// </summary>
private static readonly PropertyTypeCache TypeCache = new PropertyTypeCache(); /// <param name="filename">The filename.</param>
public CsvWriter(String filename)
private readonly object _syncLock = new object(); : this(File.OpenWrite(filename), false, Definitions.Windows1252Encoding) {
private readonly Stream _outputStream; // placeholder
private readonly Encoding _encoding; }
private readonly bool _leaveStreamOpen;
private bool _isDisposing; /// <summary>
private ulong _mCount; /// Initializes a new instance of the <see cref="CsvWriter"/> class.
/// It opens the file given file, automatically closes the stream upon
#region Constructors /// disposing of this writer, and uses the given text encoding for output.
/// </summary>
/// <summary> /// <param name="filename">The filename.</param>
/// Initializes a new instance of the <see cref="CsvWriter" /> class. /// <param name="encoding">The encoding.</param>
/// </summary> public CsvWriter(String filename, Encoding encoding)
/// <param name="outputStream">The output stream.</param> : this(File.OpenWrite(filename), false, encoding) {
/// <param name="leaveOpen">if set to <c>true</c> [leave open].</param> // placeholder
/// <param name="encoding">The encoding.</param> }
public CsvWriter(Stream outputStream, bool leaveOpen, Encoding encoding)
{ #endregion
_outputStream = outputStream;
_encoding = encoding; #region Properties
_leaveStreamOpen = leaveOpen;
} /// <summary>
/// Gets or sets the field separator character.
/// <summary> /// </summary>
/// Initializes a new instance of the <see cref="CsvWriter"/> class. /// <value>
/// It automatically closes the stream when disposing this writer. /// The separator character.
/// </summary> /// </value>
/// <param name="outputStream">The output stream.</param> public Char SeparatorCharacter { get; set; } = ',';
/// <param name="encoding">The encoding.</param>
public CsvWriter(Stream outputStream, Encoding encoding) /// <summary>
: this(outputStream, false, encoding) /// Gets or sets the escape character to use to escape field values.
{ /// </summary>
// placeholder /// <value>
} /// The escape character.
/// </value>
/// <summary> public Char EscapeCharacter { get; set; } = '"';
/// Initializes a new instance of the <see cref="CsvWriter"/> class.
/// It uses the Windows 1252 encoding and automatically closes /// <summary>
/// the stream upon disposing this writer. /// Gets or sets the new line character sequence to use when writing a line.
/// </summary> /// </summary>
/// <param name="outputStream">The output stream.</param> /// <value>
public CsvWriter(Stream outputStream) /// The new line sequence.
: this(outputStream, false, Definitions.Windows1252Encoding) /// </value>
{ public String NewLineSequence { get; set; } = Environment.NewLine;
// placeholder
} /// <summary>
/// Defines a list of properties to ignore when outputting CSV lines.
/// <summary> /// </summary>
/// Initializes a new instance of the <see cref="CsvWriter"/> class. /// <value>
/// It opens the file given file, automatically closes the stream upon /// The ignore property names.
/// disposing of this writer, and uses the Windows 1252 encoding. /// </value>
/// </summary> public List<String> IgnorePropertyNames { get; } = new List<String>();
/// <param name="filename">The filename.</param>
public CsvWriter(string filename) /// <summary>
: this(File.OpenWrite(filename), false, Definitions.Windows1252Encoding) /// Gets number of lines that have been written, including the headings line.
{ /// </summary>
// placeholder /// <value>
} /// The count.
/// </value>
/// <summary> public UInt64 Count {
/// Initializes a new instance of the <see cref="CsvWriter"/> class. get {
/// It opens the file given file, automatically closes the stream upon lock(this._syncLock) {
/// disposing of this writer, and uses the given text encoding for output. return this._mCount;
/// </summary> }
/// <param name="filename">The filename.</param> }
/// <param name="encoding">The encoding.</param> }
public CsvWriter(string filename, Encoding encoding)
: this(File.OpenWrite(filename), false, encoding) #endregion
{
// placeholder #region Helpers
}
/// <summary>
#endregion /// Saves the items to a stream.
/// It uses the Windows 1252 text encoding for output.
#region Properties /// </summary>
/// <typeparam name="T">The type of enumeration.</typeparam>
/// <summary> /// <param name="items">The items.</param>
/// Gets or sets the field separator character. /// <param name="stream">The stream.</param>
/// </summary> /// <param name="truncateData"><c>true</c> if stream is truncated, default <c>false</c>.</param>
/// <value> /// <returns>Number of item saved.</returns>
/// The separator character. public static Int32 SaveRecords<T>(IEnumerable<T> items, Stream stream, Boolean truncateData = false) {
/// </value> // truncate the file if it had data
public char SeparatorCharacter { get; set; } = ','; if(truncateData && stream.Length > 0) {
stream.SetLength(0);
/// <summary> }
/// Gets or sets the escape character to use to escape field values.
/// </summary> using(CsvWriter writer = new CsvWriter(stream)) {
/// <value> writer.WriteHeadings<T>();
/// The escape character. writer.WriteObjects(items);
/// </value> return (Int32)writer.Count;
public char EscapeCharacter { get; set; } = '"'; }
}
/// <summary>
/// Gets or sets the new line character sequence to use when writing a line. /// <summary>
/// </summary> /// Saves the items to a CSV file.
/// <value> /// If the file exits, it overwrites it. If it does not, it creates it.
/// The new line sequence. /// It uses the Windows 1252 text encoding for output.
/// </value> /// </summary>
public string NewLineSequence { get; set; } = Environment.NewLine; /// <typeparam name="T">The type of enumeration.</typeparam>
/// <param name="items">The items.</param>
/// <summary> /// <param name="filePath">The file path.</param>
/// Defines a list of properties to ignore when outputting CSV lines. /// <returns>Number of item saved.</returns>
/// </summary> public static Int32 SaveRecords<T>(IEnumerable<T> items, String filePath) => SaveRecords(items, File.OpenWrite(filePath), true);
/// <value>
/// The ignore property names. #endregion
/// </value>
public List<string> IgnorePropertyNames { get; } = new List<string>(); #region Generic, main Write Line Method
/// <summary> /// <summary>
/// Gets number of lines that have been written, including the headings line. /// Writes a line of CSV text. Items are converted to strings.
/// </summary> /// If items are found to be null, empty strings are written out.
/// <value> /// If items are not string, the ToStringInvariant() method is called on them.
/// The count. /// </summary>
/// </value> /// <param name="items">The items.</param>
public ulong Count public void WriteLine(params Object[] items)
{ => this.WriteLine(items.Select(x => x == null ? String.Empty : x.ToStringInvariant()));
get
{ /// <summary>
lock (_syncLock) /// Writes a line of CSV text. Items are converted to strings.
{ /// If items are found to be null, empty strings are written out.
return _mCount; /// If items are not string, the ToStringInvariant() method is called on them.
} /// </summary>
} /// <param name="items">The items.</param>
} public void WriteLine(IEnumerable<Object> items)
=> this.WriteLine(items.Select(x => x == null ? String.Empty : x.ToStringInvariant()));
#endregion
/// <summary>
#region Helpers /// Writes a line of CSV text.
/// If items are found to be null, empty strings are written out.
/// <summary> /// </summary>
/// Saves the items to a stream. /// <param name="items">The items.</param>
/// It uses the Windows 1252 text encoding for output. public void WriteLine(params String[] items) => this.WriteLine((IEnumerable<String>)items);
/// </summary>
/// <typeparam name="T">The type of enumeration.</typeparam> /// <summary>
/// <param name="items">The items.</param> /// Writes a line of CSV text.
/// <param name="stream">The stream.</param> /// If items are found to be null, empty strings are written out.
/// <param name="truncateData"><c>true</c> if stream is truncated, default <c>false</c>.</param> /// </summary>
/// <returns>Number of item saved.</returns> /// <param name="items">The items.</param>
public static int SaveRecords<T>(IEnumerable<T> items, Stream stream, bool truncateData = false) public void WriteLine(IEnumerable<String> items) {
{ lock(this._syncLock) {
// truncate the file if it had data Int32 length = items.Count();
if (truncateData && stream.Length > 0) Byte[] separatorBytes = this._encoding.GetBytes(new[] { this.SeparatorCharacter });
stream.SetLength(0); Byte[] endOfLineBytes = this._encoding.GetBytes(this.NewLineSequence);
using (var writer = new CsvWriter(stream)) // Declare state variables here to avoid recreation, allocation and
{ // reassignment in every loop
writer.WriteHeadings<T>(); Boolean needsEnclosing;
writer.WriteObjects(items); String textValue;
return (int)writer.Count; Byte[] output;
}
} for(Int32 i = 0; i < length; i++) {
textValue = items.ElementAt(i);
/// <summary>
/// Saves the items to a CSV file. // Determine if we need the string to be enclosed
/// If the file exits, it overwrites it. If it does not, it creates it. // (it either contains an escape, new line, or separator char)
/// It uses the Windows 1252 text encoding for output. needsEnclosing = textValue.IndexOf(this.SeparatorCharacter) >= 0
/// </summary> || textValue.IndexOf(this.EscapeCharacter) >= 0
/// <typeparam name="T">The type of enumeration.</typeparam> || textValue.IndexOf('\r') >= 0
/// <param name="items">The items.</param> || textValue.IndexOf('\n') >= 0;
/// <param name="filePath">The file path.</param>
/// <returns>Number of item saved.</returns> // Escape the escape characters by repeating them twice for every instance
public static int SaveRecords<T>(IEnumerable<T> items, string filePath) => SaveRecords(items, File.OpenWrite(filePath), true); textValue = textValue.Replace($"{this.EscapeCharacter}",
$"{this.EscapeCharacter}{this.EscapeCharacter}");
#endregion
// Enclose the text value if we need to
#region Generic, main Write Line Method if(needsEnclosing) {
textValue = String.Format($"{this.EscapeCharacter}{textValue}{this.EscapeCharacter}", textValue);
/// <summary> }
/// Writes a line of CSV text. Items are converted to strings.
/// If items are found to be null, empty strings are written out. // Get the bytes to write to the stream and write them
/// If items are not string, the ToStringInvariant() method is called on them. output = this._encoding.GetBytes(textValue);
/// </summary> this._outputStream.Write(output, 0, output.Length);
/// <param name="items">The items.</param>
public void WriteLine(params object[] items) // only write a separator if we are moving in between values.
=> WriteLine(items.Select(x => x == null ? string.Empty : x.ToStringInvariant())); // the last value should not be written.
if(i < length - 1) {
/// <summary> this._outputStream.Write(separatorBytes, 0, separatorBytes.Length);
/// Writes a line of CSV text. Items are converted to strings. }
/// If items are found to be null, empty strings are written out. }
/// If items are not string, the ToStringInvariant() method is called on them.
/// </summary> // output the newline sequence
/// <param name="items">The items.</param> this._outputStream.Write(endOfLineBytes, 0, endOfLineBytes.Length);
public void WriteLine(IEnumerable<object> items) this._mCount += 1;
=> WriteLine(items.Select(x => x == null ? string.Empty : x.ToStringInvariant())); }
}
/// <summary>
/// Writes a line of CSV text. #endregion
/// If items are found to be null, empty strings are written out.
/// </summary> #region Write Object Method
/// <param name="items">The items.</param>
public void WriteLine(params string[] items) => WriteLine((IEnumerable<string>) items); /// <summary>
/// Writes a row of CSV text. It handles the special cases where the object is
/// <summary> /// a dynamic object or and array. It also handles non-collection objects fine.
/// Writes a line of CSV text. /// If you do not like the way the output is handled, you can simply write an extension
/// If items are found to be null, empty strings are written out. /// method of this class and use the WriteLine method instead.
/// </summary> /// </summary>
/// <param name="items">The items.</param> /// <param name="item">The item.</param>
public void WriteLine(IEnumerable<string> items) /// <exception cref="System.ArgumentNullException">item.</exception>
{ public void WriteObject(Object item) {
lock (_syncLock) if(item == null) {
{ throw new ArgumentNullException(nameof(item));
var length = items.Count(); }
var separatorBytes = _encoding.GetBytes(new[] { SeparatorCharacter });
var endOfLineBytes = _encoding.GetBytes(NewLineSequence); lock(this._syncLock) {
switch(item) {
// Declare state variables here to avoid recreation, allocation and case IDictionary typedItem:
// reassignment in every loop this.WriteLine(this.GetFilteredDictionary(typedItem));
bool needsEnclosing; return;
string textValue; case ICollection typedItem:
byte[] output; this.WriteLine(typedItem.Cast<Object>());
return;
for (var i = 0; i < length; i++) default:
{ this.WriteLine(this.GetFilteredTypeProperties(item.GetType())
textValue = items.ElementAt(i); .Select(x => x.ToFormattedString(item)));
break;
// Determine if we need the string to be enclosed }
// (it either contains an escape, new line, or separator char) }
needsEnclosing = textValue.IndexOf(SeparatorCharacter) >= 0 }
|| textValue.IndexOf(EscapeCharacter) >= 0
|| textValue.IndexOf('\r') >= 0 /// <summary>
|| textValue.IndexOf('\n') >= 0; /// Writes a row of CSV text. It handles the special cases where the object is
/// a dynamic object or and array. It also handles non-collection objects fine.
// Escape the escape characters by repeating them twice for every instance /// If you do not like the way the output is handled, you can simply write an extension
textValue = textValue.Replace($"{EscapeCharacter}", /// method of this class and use the WriteLine method instead.
$"{EscapeCharacter}{EscapeCharacter}"); /// </summary>
/// <typeparam name="T">The type of object to write.</typeparam>
// Enclose the text value if we need to /// <param name="item">The item.</param>
if (needsEnclosing) public void WriteObject<T>(T item) => this.WriteObject(item as Object);
textValue = string.Format($"{EscapeCharacter}{textValue}{EscapeCharacter}", textValue);
/// <summary>
// Get the bytes to write to the stream and write them /// Writes a set of items, one per line and atomically by repeatedly calling the
output = _encoding.GetBytes(textValue); /// WriteObject method. For more info check out the description of the WriteObject
_outputStream.Write(output, 0, output.Length); /// method.
/// </summary>
// only write a separator if we are moving in between values. /// <typeparam name="T">The type of object to write.</typeparam>
// the last value should not be written. /// <param name="items">The items.</param>
if (i < length - 1) public void WriteObjects<T>(IEnumerable<T> items) {
_outputStream.Write(separatorBytes, 0, separatorBytes.Length); lock(this._syncLock) {
} foreach(T item in items) {
this.WriteObject(item);
// output the newline sequence }
_outputStream.Write(endOfLineBytes, 0, endOfLineBytes.Length); }
_mCount += 1; }
}
} #endregion
#endregion #region Write Headings Methods
#region Write Object Method /// <summary>
/// Writes the headings.
/// <summary> /// </summary>
/// Writes a row of CSV text. It handles the special cases where the object is /// <param name="type">The type of object to extract headings.</param>
/// a dynamic object or and array. It also handles non-collection objects fine. /// <exception cref="System.ArgumentNullException">type.</exception>
/// If you do not like the way the output is handled, you can simply write an extension public void WriteHeadings(Type type) {
/// method of this class and use the WriteLine method instead. if(type == null) {
/// </summary> throw new ArgumentNullException(nameof(type));
/// <param name="item">The item.</param> }
/// <exception cref="System.ArgumentNullException">item.</exception>
public void WriteObject(object item) IEnumerable<Object> properties = this.GetFilteredTypeProperties(type).Select(p => p.Name).Cast<Object>();
{ this.WriteLine(properties);
if (item == null) }
throw new ArgumentNullException(nameof(item));
/// <summary>
lock (_syncLock) /// Writes the headings.
{ /// </summary>
switch (item) /// <typeparam name="T">The type of object to extract headings.</typeparam>
{ public void WriteHeadings<T>() => this.WriteHeadings(typeof(T));
case IDictionary typedItem:
WriteLine(GetFilteredDictionary(typedItem)); /// <summary>
return; /// Writes the headings.
case ICollection typedItem: /// </summary>
WriteLine(typedItem.Cast<object>()); /// <param name="dictionary">The dictionary to extract headings.</param>
return; /// <exception cref="System.ArgumentNullException">dictionary.</exception>
default: public void WriteHeadings(IDictionary dictionary) {
WriteLine(GetFilteredTypeProperties(item.GetType()) if(dictionary == null) {
.Select(x => x.ToFormattedString(item))); throw new ArgumentNullException(nameof(dictionary));
break; }
}
} this.WriteLine(this.GetFilteredDictionary(dictionary, true));
} }
/// <summary>
/// Writes a row of CSV text. It handles the special cases where the object is
/// a dynamic object or and array. It also handles non-collection objects fine.
/// If you do not like the way the output is handled, you can simply write an extension
/// method of this class and use the WriteLine method instead.
/// </summary>
/// <typeparam name="T">The type of object to write.</typeparam>
/// <param name="item">The item.</param>
public void WriteObject<T>(T item) => WriteObject(item as object);
/// <summary>
/// Writes a set of items, one per line and atomically by repeatedly calling the
/// WriteObject method. For more info check out the description of the WriteObject
/// method.
/// </summary>
/// <typeparam name="T">The type of object to write.</typeparam>
/// <param name="items">The items.</param>
public void WriteObjects<T>(IEnumerable<T> items)
{
lock (_syncLock)
{
foreach (var item in items)
WriteObject(item);
}
}
#endregion
#region Write Headings Methods
/// <summary>
/// Writes the headings.
/// </summary>
/// <param name="type">The type of object to extract headings.</param>
/// <exception cref="System.ArgumentNullException">type.</exception>
public void WriteHeadings(Type type)
{
if (type == null)
throw new ArgumentNullException(nameof(type));
var properties = GetFilteredTypeProperties(type).Select(p => p.Name).Cast<object>();
WriteLine(properties);
}
/// <summary>
/// Writes the headings.
/// </summary>
/// <typeparam name="T">The type of object to extract headings.</typeparam>
public void WriteHeadings<T>() => WriteHeadings(typeof(T));
/// <summary>
/// Writes the headings.
/// </summary>
/// <param name="dictionary">The dictionary to extract headings.</param>
/// <exception cref="System.ArgumentNullException">dictionary.</exception>
public void WriteHeadings(IDictionary dictionary)
{
if (dictionary == null)
throw new ArgumentNullException(nameof(dictionary));
WriteLine(GetFilteredDictionary(dictionary, true));
}
#if NET452 #if NET452
/// <summary> /// <summary>
/// Writes the headings. /// Writes the headings.
/// </summary> /// </summary>
/// <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>
/// Writes the headings. /// Writes the headings.
@@ -424,55 +410,54 @@
WriteHeadings(obj.GetType()); WriteHeadings(obj.GetType());
} }
#endif #endif
#endregion #endregion
#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 (_leaveStreamOpen == false) if(this._leaveStreamOpen == false) {
{ this._outputStream.Dispose();
_outputStream.Dispose(); }
} }
}
this._isDisposing = true;
_isDisposing = true; }
}
#endregion
#endregion
#region Support Methods
#region Support Methods
private IEnumerable<String> GetFilteredDictionary(IDictionary dictionary, Boolean filterKeys = false)
private IEnumerable<string> GetFilteredDictionary(IDictionary dictionary, bool 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 => !this.IgnorePropertyNames.Contains(stringKey))
.Where(stringKey => !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 => !this.IgnorePropertyNames.Contains(p.Name));
.Where(p => !IgnorePropertyNames.Contains(p.Name));
#endregion
#endregion
}
}
} }
+133 -149
View File
@@ -1,150 +1,134 @@
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;
namespace Unosquare.Swan.Formatters {
internal class HumanizeJson internal class HumanizeJson {
{ private readonly StringBuilder _builder = new StringBuilder();
private readonly StringBuilder _builder = new StringBuilder(); private readonly Int32 _indent;
private readonly int _indent; private readonly String _indentStr;
private readonly string _indentStr; private readonly Object _obj;
private readonly object _obj;
public HumanizeJson(Object obj, Int32 indent) {
public HumanizeJson(object obj, int indent) if(obj == null) {
{ return;
if (obj == null) }
{
return; this._indent = indent;
} this._indentStr = new String(' ', indent * 4);
this._obj = obj;
_indent = indent;
_indentStr = new string(' ', indent * 4); this.ParseObject();
_obj = obj; }
ParseObject(); public String GetResult() => this._builder == null ? String.Empty : this._builder.ToString().TrimEnd();
}
private void ParseObject() {
public string GetResult() => _builder == null ? string.Empty : _builder.ToString().TrimEnd(); switch(this._obj) {
case Dictionary<String, Object> dictionary:
private void ParseObject() this.AppendDictionary(dictionary);
{ break;
switch (_obj) case List<Object> list:
{ this.AppendList(list);
case Dictionary<string, object> dictionary: break;
AppendDictionary(dictionary); default:
break; this.AppendString();
case List<object> list: break;
AppendList(list); }
break; }
default:
AppendString(); private void AppendDictionary(Dictionary<String, Object> objects) {
break; foreach(KeyValuePair<String, Object> kvp in objects) {
} if(kvp.Value == null) {
} continue;
}
private void AppendDictionary(Dictionary<string, object> objects)
{ Boolean writeOutput = false;
foreach (var kvp in objects)
{ switch(kvp.Value) {
if (kvp.Value == null) continue; case Dictionary<String, Object> valueDictionary:
if(valueDictionary.Count > 0) {
var writeOutput = false; writeOutput = true;
_ = this._builder
switch (kvp.Value) .Append($"{this._indentStr}{kvp.Key,-16}: object")
{ .AppendLine();
case Dictionary<string, object> valueDictionary: }
if (valueDictionary.Count > 0)
{ break;
writeOutput = true; case List<Object> valueList:
_builder if(valueList.Count > 0) {
.Append($"{_indentStr}{kvp.Key,-16}: object") writeOutput = true;
.AppendLine(); _ = this._builder
} .Append($"{this._indentStr}{kvp.Key,-16}: array[{valueList.Count}]")
.AppendLine();
break; }
case List<object> valueList:
if (valueList.Count > 0) break;
{ default:
writeOutput = true; writeOutput = true;
_builder _ = this._builder.Append($"{this._indentStr}{kvp.Key,-16}: ");
.Append($"{_indentStr}{kvp.Key,-16}: array[{valueList.Count}]") break;
.AppendLine(); }
}
if(writeOutput) {
break; _ = this._builder.AppendLine(new HumanizeJson(kvp.Value, this._indent + 1).GetResult());
default: }
writeOutput = true; }
_builder.Append($"{_indentStr}{kvp.Key,-16}: "); }
break;
} private void AppendList(List<Object> objects) {
Int32 index = 0;
if (writeOutput) foreach(Object value in objects) {
_builder.AppendLine(new HumanizeJson(kvp.Value, _indent + 1).GetResult()); Boolean writeOutput = false;
}
} switch(value) {
case Dictionary<String, Object> valueDictionary:
private void AppendList(List<object> objects) if(valueDictionary.Count > 0) {
{ writeOutput = true;
var index = 0; _ = this._builder
foreach (var value in objects) .Append($"{this._indentStr}[{index}]: object")
{ .AppendLine();
var writeOutput = false; }
switch (value) break;
{ case List<Object> valueList:
case Dictionary<string, object> valueDictionary: if(valueList.Count > 0) {
if (valueDictionary.Count > 0) writeOutput = true;
{ _ = this._builder
writeOutput = true; .Append($"{this._indentStr}[{index}]: array[{valueList.Count}]")
_builder .AppendLine();
.Append($"{_indentStr}[{index}]: object") }
.AppendLine();
} break;
default:
break; writeOutput = true;
case List<object> valueList: _ = this._builder.Append($"{this._indentStr}[{index}]: ");
if (valueList.Count > 0) break;
{ }
writeOutput = true;
_builder index++;
.Append($"{_indentStr}[{index}]: array[{valueList.Count}]")
.AppendLine(); if(writeOutput) {
} _ = this._builder.AppendLine(new HumanizeJson(value, this._indent + 1).GetResult());
}
break; }
default: }
writeOutput = true;
_builder.Append($"{_indentStr}[{index}]: "); private void AppendString() {
break; String stringValue = this._obj.ToString();
}
if(stringValue.Length + this._indentStr.Length > 96 || stringValue.IndexOf('\r') >= 0 ||
index++; stringValue.IndexOf('\n') >= 0) {
_ = this._builder.AppendLine();
if (writeOutput) IEnumerable<String> stringLines = stringValue.ToLines().Select(l => l.Trim());
_builder.AppendLine(new HumanizeJson(value, _indent + 1).GetResult());
} foreach(String line in stringLines) {
} _ = this._builder.AppendLine($"{this._indentStr}{line}");
}
private void AppendString() } else {
{ _ = this._builder.Append($"{stringValue}");
var stringValue = _obj.ToString(); }
}
if (stringValue.Length + _indentStr.Length > 96 || stringValue.IndexOf('\r') >= 0 || }
stringValue.IndexOf('\n') >= 0)
{
_builder.AppendLine();
var stringLines = stringValue.ToLines().Select(l => l.Trim());
foreach (var line in stringLines)
{
_builder.AppendLine($"{_indentStr}{line}");
}
}
else
{
_builder.Append($"{stringValue}");
}
}
}
} }
+299 -332
View File
@@ -1,335 +1,302 @@
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 ///
/// /// 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 static readonly ConcurrentDictionary<MemberInfo, String> MemberInfoNameCache =
private class Converter new ConcurrentDictionary<MemberInfo, String>();
{
private static readonly ConcurrentDictionary<MemberInfo, string> MemberInfoNameCache = private static readonly ConcurrentDictionary<Type, Type> ListAddMethodCache = new ConcurrentDictionary<Type, Type>();
new ConcurrentDictionary<MemberInfo, string>();
private readonly Object _target;
private static readonly ConcurrentDictionary<Type, Type> ListAddMethodCache = new ConcurrentDictionary<Type, Type>(); private readonly Type _targetType;
private readonly Boolean _includeNonPublic;
private readonly object _target;
private readonly Type _targetType; private Converter(
private readonly bool _includeNonPublic; Object source,
Type targetType,
private Converter( ref Object targetInstance,
object source, Boolean includeNonPublic) {
Type targetType, this._targetType = targetInstance != null ? targetInstance.GetType() : targetType;
ref object targetInstance, this._includeNonPublic = includeNonPublic;
bool includeNonPublic)
{ if(source == null) {
_targetType = targetInstance != null ? targetInstance.GetType() : targetType; return;
_includeNonPublic = includeNonPublic; }
if (source == null) Type sourceType = source.GetType();
{
return; if(this._targetType == null || this._targetType == typeof(Object)) {
} this._targetType = sourceType;
}
var sourceType = source.GetType();
if(sourceType == this._targetType) {
if (_targetType == null || _targetType == typeof(object)) _targetType = sourceType; this._target = source;
if (sourceType == _targetType) return;
{ }
_target = source;
return; if(!this.TrySetInstance(targetInstance, source, ref this._target)) {
} return;
}
if (!TrySetInstance(targetInstance, source, ref _target))
return; this.ResolveObject(source, ref this._target);
}
ResolveObject(source, ref _target);
} /// <summary>
/// Converts a json deserialized object (simple type, dictionary or list) to a new instance of the specified target type.
/// <summary> /// </summary>
/// Converts a json deserialized object (simple type, dictionary or list) to a new instance of the specified target type. /// <param name="source">The source.</param>
/// </summary> /// <param name="targetType">Type of the target.</param>
/// <param name="source">The source.</param> /// <param name="includeNonPublic">if set to <c>true</c> [include non public].</param>
/// <param name="targetType">Type of the target.</param> /// <returns>The target object.</returns>
/// <param name="includeNonPublic">if set to <c>true</c> [include non public].</param> internal static Object FromJsonResult(Object source,
/// <returns>The target object.</returns> Type targetType,
internal static object FromJsonResult(object source, Boolean includeNonPublic) {
Type targetType, Object nullRef = null;
bool includeNonPublic) return new Converter(source, targetType, ref nullRef, includeNonPublic).GetResult();
{ }
object nullRef = null;
return new Converter(source, targetType, ref nullRef, includeNonPublic).GetResult(); private static Object FromJsonResult(Object source,
} Type targetType,
ref Object targetInstance,
private static object FromJsonResult(object source, Boolean includeNonPublic) => new Converter(source, targetType, ref targetInstance, includeNonPublic).GetResult();
Type targetType,
ref object targetInstance, private static Type GetAddMethodParameterType(Type targetType)
bool includeNonPublic) => ListAddMethodCache.GetOrAdd(targetType,
{ x => x.GetMethods()
return new Converter(source, targetType, ref targetInstance, includeNonPublic).GetResult(); .FirstOrDefault(
} m => m.Name.Equals(AddMethodName) && m.IsPublic && m.GetParameters().Length == 1)?
.GetParameters()[0]
private static Type GetAddMethodParameterType(Type targetType) .ParameterType);
=> ListAddMethodCache.GetOrAdd(targetType,
x => x.GetMethods() private static void GetByteArray(String sourceString, ref Object target) {
.FirstOrDefault( try {
m => m.Name.Equals(AddMethodName) && m.IsPublic && m.GetParameters().Length == 1)? target = Convert.FromBase64String(sourceString);
.GetParameters()[0] } // Try conversion from Base 64
.ParameterType); catch {
target = Encoding.UTF8.GetBytes(sourceString);
private static void GetByteArray(string sourceString, ref object target) } // Get the string bytes in UTF8
{ }
try
{ private static Object GetSourcePropertyValue(IDictionary<String, Object> sourceProperties,
target = Convert.FromBase64String(sourceString); MemberInfo targetProperty) {
} // Try conversion from Base 64 String targetPropertyName = MemberInfoNameCache.GetOrAdd(
catch targetProperty,
{ x => Runtime.AttributeCache.RetrieveOne<JsonPropertyAttribute>(x)?.PropertyName ?? x.Name);
target = Encoding.UTF8.GetBytes(sourceString);
} // Get the string bytes in UTF8 return sourceProperties.GetValueOrDefault(targetPropertyName);
} }
private static object GetSourcePropertyValue(IDictionary<string, object> sourceProperties, private Boolean TrySetInstance(Object targetInstance, Object source, ref Object target) {
MemberInfo targetProperty) if(targetInstance == null) {
{ // Try to create a default instance
var targetPropertyName = MemberInfoNameCache.GetOrAdd( try {
targetProperty, source.CreateTarget(this._targetType, this._includeNonPublic, ref target);
x => Runtime.AttributeCache.RetrieveOne<JsonPropertyAttribute>(x)?.PropertyName ?? x.Name); } catch {
return false;
return sourceProperties.GetValueOrDefault(targetPropertyName); }
} } else {
target = targetInstance;
private bool TrySetInstance(object targetInstance, object source, ref object target) }
{
if (targetInstance == null) return true;
{ }
// Try to create a default instance
try private Object GetResult() => this._target ?? this._targetType.GetDefault();
{
source.CreateTarget(_targetType, _includeNonPublic, ref target); private void ResolveObject(Object source, ref Object target) {
} switch(source) {
catch // Case 0: Special Cases Handling (Source and Target are of specific convertible types)
{ // Case 0.1: Source is string, Target is byte[]
return false; case String sourceString when this._targetType == typeof(Byte[]):
} GetByteArray(sourceString, ref target);
} break;
else
{ // Case 1.1: Source is Dictionary, Target is IDictionary
target = targetInstance; case Dictionary<String, Object> sourceProperties when target is IDictionary targetDictionary:
} this.PopulateDictionary(sourceProperties, targetDictionary);
break;
return true;
} // Case 1.2: Source is Dictionary, Target is not IDictionary (i.e. it is a complex type)
case Dictionary<String, Object> sourceProperties:
private object GetResult() => _target ?? _targetType.GetDefault(); this.PopulateObject(sourceProperties);
break;
private void ResolveObject(object source, ref object target)
{ // Case 2.1: Source is List, Target is Array
switch (source) case List<Object> sourceList when target is Array targetArray:
{ this.PopulateArray(sourceList, targetArray);
// Case 0: Special Cases Handling (Source and Target are of specific convertible types) break;
// Case 0.1: Source is string, Target is byte[]
case string sourceString when _targetType == typeof(byte[]): // Case 2.2: Source is List, Target is IList
GetByteArray(sourceString, ref target); case List<Object> sourceList when target is IList targetList:
break; this.PopulateIList(sourceList, targetList);
break;
// Case 1.1: Source is Dictionary, Target is IDictionary
case Dictionary<string, object> sourceProperties when target is IDictionary targetDictionary: // Case 3: Source is a simple type; Attempt conversion
PopulateDictionary(sourceProperties, targetDictionary); default:
break; String sourceStringValue = source.ToStringInvariant();
// Case 1.2: Source is Dictionary, Target is not IDictionary (i.e. it is a complex type) // Handle basic types or enumerations if not
case Dictionary<string, object> sourceProperties: if(!this._targetType.TryParseBasicType(sourceStringValue, out target)) {
PopulateObject(sourceProperties); this.GetEnumValue(sourceStringValue, ref target);
break; }
// Case 2.1: Source is List, Target is Array break;
case List<object> sourceList when target is Array targetArray: }
PopulateArray(sourceList, targetArray); }
break;
private void PopulateIList(IList<Object> objects, IList list) {
// Case 2.2: Source is List, Target is IList Type parameterType = GetAddMethodParameterType(this._targetType);
case List<object> sourceList when target is IList targetList: if(parameterType == null) {
PopulateIList(sourceList, targetList); return;
break; }
// Case 3: Source is a simple type; Attempt conversion foreach(Object item in objects) {
default: try {
var sourceStringValue = source.ToStringInvariant(); _ = list.Add(FromJsonResult(
item,
// Handle basic types or enumerations if not parameterType,
if (!_targetType.TryParseBasicType(sourceStringValue, out target)) this._includeNonPublic));
GetEnumValue(sourceStringValue, ref target); } catch {
// ignored
break; }
} }
} }
private void PopulateIList(IList<object> objects, IList list) private void PopulateArray(IList<Object> objects, Array array) {
{ Type elementType = this._targetType.GetElementType();
var parameterType = GetAddMethodParameterType(_targetType);
if (parameterType == null) return; for(Int32 i = 0; i < objects.Count; i++) {
try {
foreach (var item in objects) Object targetItem = FromJsonResult(
{ objects[i],
try elementType,
{ this._includeNonPublic);
list.Add(FromJsonResult( array.SetValue(targetItem, i);
item, } catch {
parameterType, // ignored
_includeNonPublic)); }
} }
catch }
{
// ignored private void GetEnumValue(String sourceStringValue, ref Object target) {
} Type enumType = Nullable.GetUnderlyingType(this._targetType);
} if(enumType == null && this._targetType.GetTypeInfo().IsEnum) {
} enumType = this._targetType;
}
private void PopulateArray(IList<object> objects, Array array)
{ if(enumType == null) {
var elementType = _targetType.GetElementType(); return;
}
for (var i = 0; i < objects.Count; i++)
{ try {
try target = Enum.Parse(enumType, sourceStringValue);
{ } catch {
var targetItem = FromJsonResult( // ignored
objects[i], }
elementType, }
_includeNonPublic);
array.SetValue(targetItem, i); private void PopulateDictionary(IDictionary<String, Object> sourceProperties, IDictionary targetDictionary) {
} // find the add method of the target dictionary
catch MethodInfo addMethod = this._targetType.GetMethods()
{ .FirstOrDefault(
// ignored m => m.Name.Equals(AddMethodName) && m.IsPublic && m.GetParameters().Length == 2);
}
} // skip if we don't have a compatible add method
} if(addMethod == null) {
return;
private void GetEnumValue(string sourceStringValue, ref object target) }
{
var enumType = Nullable.GetUnderlyingType(_targetType); ParameterInfo[] addMethodParameters = addMethod.GetParameters();
if (enumType == null && _targetType.GetTypeInfo().IsEnum) enumType = _targetType; if(addMethodParameters[0].ParameterType != typeof(String)) {
if (enumType == null) return; return;
}
try
{ // Retrieve the target entry type
target = Enum.Parse(enumType, sourceStringValue); Type targetEntryType = addMethodParameters[1].ParameterType;
}
catch // Add the items to the target dictionary
{ foreach(KeyValuePair<String, Object> sourceProperty in sourceProperties) {
// ignored try {
} Object targetEntryValue = FromJsonResult(
} sourceProperty.Value,
targetEntryType,
private void PopulateDictionary(IDictionary<string, object> sourceProperties, IDictionary targetDictionary) this._includeNonPublic);
{ targetDictionary.Add(sourceProperty.Key, targetEntryValue);
// find the add method of the target dictionary } catch {
var addMethod = _targetType.GetMethods() // ignored
.FirstOrDefault( }
m => m.Name.Equals(AddMethodName) && m.IsPublic && m.GetParameters().Length == 2); }
}
// skip if we don't have a compatible add method
if (addMethod == null) return; private void PopulateObject(IDictionary<String, Object> sourceProperties) {
var addMethodParameters = addMethod.GetParameters(); if(this._targetType.IsValueType()) {
if (addMethodParameters[0].ParameterType != typeof(string)) return; this.PopulateFields(sourceProperties);
}
// Retrieve the target entry type
var targetEntryType = addMethodParameters[1].ParameterType; this.PopulateProperties(sourceProperties);
}
// Add the items to the target dictionary
foreach (var sourceProperty in sourceProperties) private void PopulateProperties(IDictionary<String, Object> sourceProperties) {
{ IEnumerable<PropertyInfo> properties = PropertyTypeCache.RetrieveFilteredProperties(this._targetType, false, p => p.CanWrite);
try
{ foreach(PropertyInfo property in properties) {
var targetEntryValue = FromJsonResult( Object sourcePropertyValue = GetSourcePropertyValue(sourceProperties, property);
sourceProperty.Value, if(sourcePropertyValue == null) {
targetEntryType, continue;
_includeNonPublic); }
targetDictionary.Add(sourceProperty.Key, targetEntryValue);
} try {
catch Object currentPropertyValue = !property.PropertyType.IsArray
{ ? property.GetCacheGetMethod(this._includeNonPublic)(this._target)
// ignored : null;
}
} Object targetPropertyValue = FromJsonResult(
} sourcePropertyValue,
property.PropertyType,
private void PopulateObject(IDictionary<string, object> sourceProperties) ref currentPropertyValue,
{ this._includeNonPublic);
if (_targetType.IsValueType())
{ property.GetCacheSetMethod(this._includeNonPublic)(this._target, new[] { targetPropertyValue });
PopulateFields(sourceProperties); } catch {
} // ignored
}
PopulateProperties(sourceProperties); }
} }
private void PopulateProperties(IDictionary<string, object> sourceProperties) private void PopulateFields(IDictionary<String, Object> sourceProperties) {
{ foreach(FieldInfo field in FieldTypeCache.RetrieveAllFields(this._targetType)) {
var properties = PropertyTypeCache.RetrieveFilteredProperties(_targetType, false, p => p.CanWrite); Object sourcePropertyValue = GetSourcePropertyValue(sourceProperties, field);
if(sourcePropertyValue == null) {
foreach (var property in properties) continue;
{ }
var sourcePropertyValue = GetSourcePropertyValue(sourceProperties, property);
if (sourcePropertyValue == null) continue; Object targetPropertyValue = FromJsonResult(
try
{
var currentPropertyValue = !property.PropertyType.IsArray
? property.GetCacheGetMethod(_includeNonPublic)(_target)
: null;
var targetPropertyValue = FromJsonResult(
sourcePropertyValue,
property.PropertyType,
ref currentPropertyValue,
_includeNonPublic);
property.GetCacheSetMethod(_includeNonPublic)(_target, new[] { targetPropertyValue });
}
catch
{
// ignored
}
}
}
private void PopulateFields(IDictionary<string, object> sourceProperties)
{
foreach (var field in FieldTypeCache.RetrieveAllFields(_targetType))
{
var sourcePropertyValue = GetSourcePropertyValue(sourceProperties, field);
if (sourcePropertyValue == null) continue;
var targetPropertyValue = FromJsonResult(
sourcePropertyValue, sourcePropertyValue,
field.FieldType, field.FieldType,
_includeNonPublic); this._includeNonPublic);
try try {
{ field.SetValue(this._target, targetPropertyValue);
field.SetValue(_target, targetPropertyValue); } catch {
} // ignored
catch }
{ }
// ignored }
} }
} }
}
}
}
} }
@@ -1,374 +1,366 @@
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>
/// A very simple, light-weight JSON library written by Mario
/// to teach Geo how things are done
///
/// This is an useful helper for small tasks but it doesn't represent a full-featured
/// serializer such as the beloved Json.NET.
/// </summary>
public partial class Json {
/// <summary> /// <summary>
/// A very simple, light-weight JSON library written by Mario /// A simple JSON Deserializer.
/// to teach Geo how things are done
///
/// This is an useful helper for small tasks but it doesn't represent a full-featured
/// serializer such as the beloved Json.NET.
/// </summary> /// </summary>
public partial class Json private class Deserializer {
{ #region State Variables
/// <summary>
/// A simple JSON Deserializer. private readonly Object _result;
/// </summary> private readonly Dictionary<String, Object> _resultObject;
private class Deserializer private readonly List<Object> _resultArray;
{
#region State Variables private readonly ReadState _state = ReadState.WaitingForRootOpen;
private readonly String _currentFieldName;
private readonly object _result; private readonly String _json;
private readonly Dictionary<string, object> _resultObject;
private readonly List<object> _resultArray; private Int32 _index;
private readonly ReadState _state = ReadState.WaitingForRootOpen; #endregion
private readonly string _currentFieldName;
private readonly string _json; private Deserializer(String json, Int32 startIndex) {
this._json = json;
private int _index;
for(this._index = startIndex; this._index < this._json.Length; this._index++) {
#endregion #region Wait for { or [
private Deserializer(string json, int startIndex) if(this._state == ReadState.WaitingForRootOpen) {
{ if(Char.IsWhiteSpace(this._json, this._index)) {
_json = json; continue;
}
for (_index = startIndex; _index < _json.Length; _index++)
{ if(this._json[this._index] == OpenObjectChar) {
#region Wait for { or [ this._resultObject = new Dictionary<String, Object>();
this._state = ReadState.WaitingForField;
if (_state == ReadState.WaitingForRootOpen) continue;
{ }
if (char.IsWhiteSpace(_json, _index)) continue;
if(this._json[this._index] == OpenArrayChar) {
if (_json[_index] == OpenObjectChar) this._resultArray = new List<Object>();
{ this._state = ReadState.WaitingForValue;
_resultObject = new Dictionary<string, object>(); continue;
_state = ReadState.WaitingForField; }
continue;
} throw this.CreateParserException($"'{OpenObjectChar}' or '{OpenArrayChar}'");
}
if (_json[_index] == OpenArrayChar)
{ #endregion
_resultArray = new List<object>();
_state = ReadState.WaitingForValue; #region Wait for opening field " (only applies for object results)
continue;
} if(this._state == ReadState.WaitingForField) {
if(Char.IsWhiteSpace(this._json, this._index)) {
throw CreateParserException($"'{OpenObjectChar}' or '{OpenArrayChar}'"); continue;
} }
#endregion // Handle empty arrays and empty objects
if(this._resultObject != null && this._json[this._index] == CloseObjectChar
#region Wait for opening field " (only applies for object results) || this._resultArray != null && this._json[this._index] == CloseArrayChar) {
this._result = this._resultObject ?? this._resultArray as Object;
if (_state == ReadState.WaitingForField) return;
{ }
if (char.IsWhiteSpace(_json, _index)) continue;
if(this._json[this._index] != StringQuotedChar) {
// Handle empty arrays and empty objects throw this.CreateParserException($"'{StringQuotedChar}'");
if ((_resultObject != null && _json[_index] == CloseObjectChar) }
|| (_resultArray != null && _json[_index] == CloseArrayChar))
{ Int32 charCount = this.GetFieldNameCount();
_result = _resultObject ?? _resultArray as object;
return; this._currentFieldName = Unescape(this._json.SliceLength(this._index + 1, charCount));
} this._index += charCount + 1;
this._state = ReadState.WaitingForColon;
if (_json[_index] != StringQuotedChar) continue;
throw CreateParserException($"'{StringQuotedChar}'"); }
var charCount = GetFieldNameCount(); #endregion
_currentFieldName = Unescape(_json.SliceLength(_index + 1, charCount)); #region Wait for field-value separator : (only applies for object results
_index += charCount + 1;
_state = ReadState.WaitingForColon; if(this._state == ReadState.WaitingForColon) {
continue; if(Char.IsWhiteSpace(this._json, this._index)) {
} continue;
}
#endregion
if(this._json[this._index] != ValueSeparatorChar) {
#region Wait for field-value separator : (only applies for object results throw this.CreateParserException($"'{ValueSeparatorChar}'");
}
if (_state == ReadState.WaitingForColon)
{ this._state = ReadState.WaitingForValue;
if (char.IsWhiteSpace(_json, _index)) continue; continue;
}
if (_json[_index] != ValueSeparatorChar)
throw CreateParserException($"'{ValueSeparatorChar}'"); #endregion
_state = ReadState.WaitingForValue; #region Wait for and Parse the value
continue;
} if(this._state == ReadState.WaitingForValue) {
if(Char.IsWhiteSpace(this._json, this._index)) {
#endregion continue;
}
#region Wait for and Parse the value
// Handle empty arrays and empty objects
if (_state == ReadState.WaitingForValue) if(this._resultObject != null && this._json[this._index] == CloseObjectChar
{ || this._resultArray != null && this._json[this._index] == CloseArrayChar) {
if (char.IsWhiteSpace(_json, _index)) continue; this._result = this._resultObject ?? this._resultArray as Object;
return;
// Handle empty arrays and empty objects }
if ((_resultObject != null && _json[_index] == CloseObjectChar)
|| (_resultArray != null && _json[_index] == CloseArrayChar)) // determine the value based on what it starts with
{ switch(this._json[this._index]) {
_result = _resultObject ?? _resultArray as object; case StringQuotedChar: // expect a string
return; this.ExtractStringQuoted();
} break;
// determine the value based on what it starts with case OpenObjectChar: // expect object
switch (_json[_index]) case OpenArrayChar: // expect array
{ this.ExtractObject();
case StringQuotedChar: // expect a string break;
ExtractStringQuoted();
break; case 't': // expect true
this.ExtractConstant(TrueLiteral, true);
case OpenObjectChar: // expect object break;
case OpenArrayChar: // expect array
ExtractObject(); case 'f': // expect false
break; this.ExtractConstant(FalseLiteral, false);
break;
case 't': // expect true
ExtractConstant(TrueLiteral, true); case 'n': // expect null
break; this.ExtractConstant(NullLiteral, null);
break;
case 'f': // expect false
ExtractConstant(FalseLiteral, false); default: // expect number
break; this.ExtractNumber();
break;
case 'n': // expect null }
ExtractConstant(NullLiteral, null);
break; this._currentFieldName = null;
this._state = ReadState.WaitingForNextOrRootClose;
default: // expect number continue;
ExtractNumber(); }
break;
} #endregion
_currentFieldName = null; #region Wait for closing ], } or an additional field or value ,
_state = ReadState.WaitingForNextOrRootClose;
continue; if(this._state != ReadState.WaitingForNextOrRootClose) {
} continue;
}
#endregion
if(Char.IsWhiteSpace(this._json, this._index)) {
#region Wait for closing ], } or an additional field or value , continue;
}
if (_state != ReadState.WaitingForNextOrRootClose) continue;
if(this._json[this._index] == FieldSeparatorChar) {
if (char.IsWhiteSpace(_json, _index)) continue; if(this._resultObject != null) {
this._state = ReadState.WaitingForField;
if (_json[_index] == FieldSeparatorChar) this._currentFieldName = null;
{ continue;
if (_resultObject != null) }
{
_state = ReadState.WaitingForField; this._state = ReadState.WaitingForValue;
_currentFieldName = null; continue;
continue; }
}
if(this._resultObject != null && this._json[this._index] == CloseObjectChar ||
_state = ReadState.WaitingForValue; this._resultArray != null && this._json[this._index] == CloseArrayChar) {
continue; this._result = this._resultObject ?? this._resultArray as Object;
} return;
}
if ((_resultObject != null && _json[_index] == CloseObjectChar) ||
(_resultArray != null && _json[_index] == CloseArrayChar)) throw this.CreateParserException($"'{FieldSeparatorChar}' '{CloseObjectChar}' or '{CloseArrayChar}'");
{
_result = _resultObject ?? _resultArray as object; #endregion
return; }
} }
throw CreateParserException($"'{FieldSeparatorChar}' '{CloseObjectChar}' or '{CloseArrayChar}'"); internal static Object DeserializeInternal(String json) => new Deserializer(json, 0)._result;
#endregion private static String Unescape(String str) {
} // check if we need to unescape at all
} if(str.IndexOf(StringEscapeChar) < 0) {
return str;
internal static object DeserializeInternal(string json) => new Deserializer(json, 0)._result; }
private static string Unescape(string str) StringBuilder builder = new StringBuilder(str.Length);
{ for(Int32 i = 0; i < str.Length; i++) {
// check if we need to unescape at all if(str[i] != StringEscapeChar) {
if (str.IndexOf(StringEscapeChar) < 0) _ = builder.Append(str[i]);
return str; continue;
}
var builder = new StringBuilder(str.Length);
for (var i = 0; i < str.Length; i++) if(i + 1 > str.Length - 1) {
{ break;
if (str[i] != StringEscapeChar) }
{
builder.Append(str[i]); // escape sequence begins here
continue; switch(str[i + 1]) {
} case 'u':
i = ExtractEscapeSequence(str, i, builder);
if (i + 1 > str.Length - 1) break;
break; case 'b':
_ = builder.Append('\b');
// escape sequence begins here i += 1;
switch (str[i + 1]) break;
{ case 't':
case 'u': _ = builder.Append('\t');
i = ExtractEscapeSequence(str, i, builder); i += 1;
break; break;
case 'b': case 'n':
builder.Append('\b'); _ = builder.Append('\n');
i += 1; i += 1;
break; break;
case 't': case 'f':
builder.Append('\t'); _ = builder.Append('\f');
i += 1; i += 1;
break; break;
case 'n': case 'r':
builder.Append('\n'); _ = builder.Append('\r');
i += 1; i += 1;
break; break;
case 'f': default:
builder.Append('\f'); _ = builder.Append(str[i + 1]);
i += 1; i += 1;
break; break;
case 'r': }
builder.Append('\r'); }
i += 1;
break; return builder.ToString();
default: }
builder.Append(str[i + 1]);
i += 1; private static Int32 ExtractEscapeSequence(String str, Int32 i, StringBuilder builder) {
break; Int32 startIndex = i + 2;
} Int32 endIndex = i + 5;
} if(endIndex > str.Length - 1) {
_ = builder.Append(str[i + 1]);
return builder.ToString(); i += 1;
} return i;
}
private static int ExtractEscapeSequence(string str, int i, StringBuilder builder)
{ Byte[] hexCode = str.Slice(startIndex, endIndex).ConvertHexadecimalToBytes();
var startIndex = i + 2; _ = builder.Append(Encoding.BigEndianUnicode.GetChars(hexCode));
var endIndex = i + 5; i += 5;
if (endIndex > str.Length - 1) return i;
{ }
builder.Append(str[i + 1]);
i += 1; private Int32 GetFieldNameCount() {
return i; Int32 charCount = 0;
} for(Int32 j = this._index + 1; j < this._json.Length; j++) {
if(this._json[j] == StringQuotedChar && this._json[j - 1] != StringEscapeChar) {
var hexCode = str.Slice(startIndex, endIndex).ConvertHexadecimalToBytes(); break;
builder.Append(Encoding.BigEndianUnicode.GetChars(hexCode)); }
i += 5;
return i; charCount++;
} }
private int GetFieldNameCount() return charCount;
{ }
var charCount = 0;
for (var j = _index + 1; j < _json.Length; j++) private void ExtractObject() {
{ // Extract and set the value
if (_json[j] == StringQuotedChar && _json[j - 1] != StringEscapeChar) Deserializer deserializer = new Deserializer(this._json, this._index);
break;
if(this._currentFieldName != null) {
charCount++; this._resultObject[this._currentFieldName] = deserializer._result;
} } else {
this._resultArray.Add(deserializer._result);
return charCount; }
}
this._index = deserializer._index;
private void ExtractObject() }
{
// Extract and set the value private void ExtractNumber() {
var deserializer = new Deserializer(_json, _index); Int32 charCount = 0;
for(Int32 j = this._index; j < this._json.Length; j++) {
if (_currentFieldName != null) if(Char.IsWhiteSpace(this._json[j]) || this._json[j] == FieldSeparatorChar
_resultObject[_currentFieldName] = deserializer._result; || this._resultObject != null && this._json[j] == CloseObjectChar
else || this._resultArray != null && this._json[j] == CloseArrayChar) {
_resultArray.Add(deserializer._result); break;
}
_index = deserializer._index;
} charCount++;
}
private void ExtractNumber()
{ // Extract and set the value
var charCount = 0; String stringValue = this._json.SliceLength(this._index, charCount);
for (var j = _index; j < _json.Length; j++)
{ if(Decimal.TryParse(stringValue, out Decimal value) == false) {
if (char.IsWhiteSpace(_json[j]) || _json[j] == FieldSeparatorChar throw this.CreateParserException("[number]");
|| (_resultObject != null && _json[j] == CloseObjectChar) }
|| (_resultArray != null && _json[j] == CloseArrayChar))
break; if(this._currentFieldName != null) {
this._resultObject[this._currentFieldName] = value;
charCount++; } else {
} this._resultArray.Add(value);
}
// Extract and set the value
var stringValue = _json.SliceLength(_index, charCount); this._index += charCount - 1;
}
if (decimal.TryParse(stringValue, out var value) == false)
throw CreateParserException("[number]"); private void ExtractConstant(String boolValue, Boolean? value) {
if(!this._json.SliceLength(this._index, boolValue.Length).Equals(boolValue)) {
if (_currentFieldName != null) throw this.CreateParserException($"'{ValueSeparatorChar}'");
_resultObject[_currentFieldName] = value; }
else
_resultArray.Add(value); // Extract and set the value
if(this._currentFieldName != null) {
_index += charCount - 1; this._resultObject[this._currentFieldName] = value;
} } else {
this._resultArray.Add(value);
private void ExtractConstant(string boolValue, bool? value) }
{
if (!_json.SliceLength(_index, boolValue.Length).Equals(boolValue)) this._index += boolValue.Length - 1;
throw CreateParserException($"'{ValueSeparatorChar}'"); }
// Extract and set the value private void ExtractStringQuoted() {
if (_currentFieldName != null) Int32 charCount = 0;
_resultObject[_currentFieldName] = value; Boolean escapeCharFound = false;
else for(Int32 j = this._index + 1; j < this._json.Length; j++) {
_resultArray.Add(value); if(this._json[j] == StringQuotedChar && !escapeCharFound) {
break;
_index += boolValue.Length - 1; }
}
escapeCharFound = this._json[j] == StringEscapeChar && !escapeCharFound;
private void ExtractStringQuoted() charCount++;
{ }
var charCount = 0;
var escapeCharFound = false; // Extract and set the value
for (var j = _index + 1; j < _json.Length; j++) String value = Unescape(this._json.SliceLength(this._index + 1, charCount));
{ if(this._currentFieldName != null) {
if (_json[j] == StringQuotedChar && !escapeCharFound) this._resultObject[this._currentFieldName] = value;
break; } else {
this._resultArray.Add(value);
escapeCharFound = _json[j] == StringEscapeChar && !escapeCharFound; }
charCount++;
} this._index += charCount + 1;
}
// Extract and set the value
var value = Unescape(_json.SliceLength(_index + 1, charCount)); private FormatException CreateParserException(String expected) {
if (_currentFieldName != null) Tuple<Int32, Int32> textPosition = this._json.TextPositionAt(this._index);
_resultObject[_currentFieldName] = value; return new FormatException(
else $"Parser error (Line {textPosition.Item1}, Col {textPosition.Item2}, State {this._state}): Expected {expected} but got '{this._json[this._index]}'.");
_resultArray.Add(value); }
_index += charCount + 1; /// <summary>
} /// Defines the different JSON read states.
/// </summary>
private FormatException CreateParserException(string expected) private enum ReadState {
{ WaitingForRootOpen,
var textPosition = _json.TextPositionAt(_index); WaitingForField,
return new FormatException( WaitingForColon,
$"Parser error (Line {textPosition.Item1}, Col {textPosition.Item2}, State {_state}): Expected {expected} but got '{_json[_index]}'."); WaitingForValue,
} WaitingForNextOrRootClose,
}
/// <summary> }
/// Defines the different JSON read states. }
/// </summary>
private enum ReadState
{
WaitingForRootOpen,
WaitingForField,
WaitingForColon,
WaitingForValue,
WaitingForNextOrRootClose,
}
}
}
} }
+341 -353
View File
@@ -1,359 +1,347 @@
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>
/// A very simple, light-weight JSON library written by Mario
/// to teach Geo how things are done
///
/// This is an useful helper for small tasks but it doesn't represent a full-featured
/// serializer such as the beloved Json.NET.
/// </summary>
public partial class Json {
/// <summary> /// <summary>
/// A very simple, light-weight JSON library written by Mario /// A simple JSON serializer.
/// to teach Geo how things are done
///
/// This is an useful helper for small tasks but it doesn't represent a full-featured
/// serializer such as the beloved Json.NET.
/// </summary> /// </summary>
public partial class Json private class Serializer {
{ #region Private Declarations
/// <summary>
/// A simple JSON serializer. private static readonly Dictionary<Int32, String> IndentStrings = new Dictionary<Int32, String>();
/// </summary>
private class Serializer private readonly SerializerOptions _options;
{ private readonly String _result;
#region Private Declarations private readonly StringBuilder _builder;
private readonly String _lastCommaSearch;
private static readonly Dictionary<int, string> IndentStrings = new Dictionary<int, string>();
#endregion
private readonly SerializerOptions _options;
private readonly string _result; #region Constructors
private readonly StringBuilder _builder;
private readonly string _lastCommaSearch; /// <summary>
/// Initializes a new instance of the <see cref="Serializer" /> class.
#endregion /// </summary>
/// <param name="obj">The object.</param>
#region Constructors /// <param name="depth">The depth.</param>
/// <param name="options">The options.</param>
/// <summary> private Serializer(Object obj, Int32 depth, SerializerOptions options) {
/// Initializes a new instance of the <see cref="Serializer" /> class. if(depth > 20) {
/// </summary> throw new InvalidOperationException(
/// <param name="obj">The object.</param> "The max depth (20) has been reached. Serializer can not continue.");
/// <param name="depth">The depth.</param> }
/// <param name="options">The options.</param>
private Serializer(object obj, int depth, SerializerOptions options) // Basic Type Handling (nulls, strings, number, date and bool)
{ this._result = ResolveBasicType(obj);
if (depth > 20)
{ if(String.IsNullOrWhiteSpace(this._result) == false) {
throw new InvalidOperationException( return;
"The max depth (20) has been reached. Serializer can not continue."); }
}
this._options = options;
// Basic Type Handling (nulls, strings, number, date and bool) this._lastCommaSearch = FieldSeparatorChar + (this._options.Format ? Environment.NewLine : String.Empty);
_result = ResolveBasicType(obj);
// Handle circular references correctly and avoid them
if (string.IsNullOrWhiteSpace(_result) == false) if(options.IsObjectPresent(obj)) {
return; this._result = $"{{ \"$circref\": \"{Escape(obj.GetHashCode().ToStringInvariant(), false)}\" }}";
return;
_options = options; }
_lastCommaSearch = FieldSeparatorChar + (_options.Format ? Environment.NewLine : string.Empty);
// At this point, we will need to construct the object with a StringBuilder.
// Handle circular references correctly and avoid them this._builder = new StringBuilder();
if (options.IsObjectPresent(obj))
{ switch(obj) {
_result = $"{{ \"$circref\": \"{Escape(obj.GetHashCode().ToStringInvariant(), false)}\" }}"; case IDictionary itemsZero when itemsZero.Count == 0:
return; this._result = EmptyObjectLiteral;
} break;
case IDictionary items:
// At this point, we will need to construct the object with a StringBuilder. this._result = this.ResolveDictionary(items, depth);
_builder = new StringBuilder(); break;
case IEnumerable enumerableZero when !enumerableZero.Cast<Object>().Any():
switch (obj) this._result = EmptyArrayLiteral;
{ break;
case IDictionary itemsZero when itemsZero.Count == 0: case IEnumerable enumerableBytes when enumerableBytes is Byte[] bytes:
_result = EmptyObjectLiteral; this._result = Serialize(bytes.ToBase64(), depth, this._options);
break; break;
case IDictionary items: case IEnumerable enumerable:
_result = ResolveDictionary(items, depth); this._result = this.ResolveEnumerable(enumerable, depth);
break; break;
case IEnumerable enumerableZero when !enumerableZero.Cast<object>().Any(): default:
_result = EmptyArrayLiteral; this._result = this.ResolveObject(obj, depth);
break; break;
case IEnumerable enumerableBytes when enumerableBytes is byte[] bytes: }
_result = Serialize(bytes.ToBase64(), depth, _options); }
break;
case IEnumerable enumerable: internal static String Serialize(Object obj, Int32 depth, SerializerOptions options) => new Serializer(obj, depth, options)._result;
_result = ResolveEnumerable(enumerable, depth);
break; #endregion
default:
_result = ResolveObject(obj, depth); #region Helper Methods
break;
} private static String ResolveBasicType(Object obj) {
} switch(obj) {
case null:
internal static string Serialize(object obj, int depth, SerializerOptions options) return NullLiteral;
{ case String s:
return new Serializer(obj, depth, options)._result; return Escape(s, true);
} case Boolean b:
return b ? TrueLiteral : FalseLiteral;
#endregion case Type _:
case Assembly _:
#region Helper Methods case MethodInfo _:
case PropertyInfo _:
private static string ResolveBasicType(object obj) case EventInfo _:
{ return Escape(obj.ToString(), true);
switch (obj) case DateTime d:
{ return $"{StringQuotedChar}{d:s}{StringQuotedChar}";
case null: default:
return NullLiteral; Type targetType = obj.GetType();
case string s:
return Escape(s, true); if(!Definitions.BasicTypesInfo.ContainsKey(targetType)) {
case bool b: return String.Empty;
return b ? TrueLiteral : FalseLiteral; }
case Type _:
case Assembly _: String escapedValue = Escape(Definitions.BasicTypesInfo[targetType].ToStringInvariant(obj), false);
case MethodInfo _:
case PropertyInfo _: return Decimal.TryParse(escapedValue, out _)
case EventInfo _: ? $"{escapedValue}"
return Escape(obj.ToString(), true); : $"{StringQuotedChar}{escapedValue}{StringQuotedChar}";
case DateTime d: }
return $"{StringQuotedChar}{d:s}{StringQuotedChar}"; }
default:
var targetType = obj.GetType(); private static Boolean IsNonEmptyJsonArrayOrObject(String serialized) {
if(serialized.Equals(EmptyObjectLiteral) || serialized.Equals(EmptyArrayLiteral)) {
if (!Definitions.BasicTypesInfo.ContainsKey(targetType)) return false;
return string.Empty; }
var escapedValue = Escape(Definitions.BasicTypesInfo[targetType].ToStringInvariant(obj), false); // find the first position the character is not a space
return serialized.Where(c => c != ' ').Select(c => c == OpenObjectChar || c == OpenArrayChar).FirstOrDefault();
return decimal.TryParse(escapedValue, out _) }
? $"{escapedValue}"
: $"{StringQuotedChar}{escapedValue}{StringQuotedChar}"; private static String Escape(String str, Boolean quoted) {
} if(str == null) {
} return String.Empty;
}
private static bool IsNonEmptyJsonArrayOrObject(string serialized)
{ StringBuilder builder = new StringBuilder(str.Length * 2);
if (serialized.Equals(EmptyObjectLiteral) || serialized.Equals(EmptyArrayLiteral)) return false; if(quoted) {
_ = builder.Append(StringQuotedChar);
// find the first position the character is not a space }
return serialized.Where(c => c != ' ').Select(c => c == OpenObjectChar || c == OpenArrayChar).FirstOrDefault();
} Escape(str, builder);
if(quoted) {
private static string Escape(string str, bool quoted) _ = builder.Append(StringQuotedChar);
{ }
if (str == null)
return string.Empty; return builder.ToString();
}
var builder = new StringBuilder(str.Length * 2);
if (quoted) builder.Append(StringQuotedChar); private static void Escape(String str, StringBuilder builder) {
Escape(str, builder); foreach(Char currentChar in str) {
if (quoted) builder.Append(StringQuotedChar); switch(currentChar) {
return builder.ToString(); case '\\':
} case '"':
case '/':
private static void Escape(string str, StringBuilder builder) _ = builder
{ .Append('\\')
foreach (var currentChar in str) .Append(currentChar);
{ break;
switch (currentChar) case '\b':
{ _ = builder.Append("\\b");
case '\\': break;
case '"': case '\t':
case '/': _ = builder.Append("\\t");
builder break;
.Append('\\') case '\n':
.Append(currentChar); _ = builder.Append("\\n");
break; break;
case '\b': case '\f':
builder.Append("\\b"); _ = builder.Append("\\f");
break; break;
case '\t': case '\r':
builder.Append("\\t"); _ = builder.Append("\\r");
break; break;
case '\n': default:
builder.Append("\\n"); if(currentChar < ' ') {
break; Byte[] escapeBytes = BitConverter.GetBytes((UInt16)currentChar);
case '\f': if(BitConverter.IsLittleEndian == false) {
builder.Append("\\f"); Array.Reverse(escapeBytes);
break; }
case '\r':
builder.Append("\\r"); _ = builder.Append("\\u")
break;
default:
if (currentChar < ' ')
{
var escapeBytes = BitConverter.GetBytes((ushort)currentChar);
if (BitConverter.IsLittleEndian == false)
Array.Reverse(escapeBytes);
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; }
} }
}
} private Dictionary<String, Object> CreateDictionary(
Dictionary<String, MemberInfo> fields,
private Dictionary<string, object> CreateDictionary( String targetType,
Dictionary<string, MemberInfo> fields, Object target) {
string targetType, // Create the dictionary and extract the properties
object target) Dictionary<String, Object> objectDictionary = new Dictionary<String, Object>();
{
// Create the dictionary and extract the properties if(String.IsNullOrWhiteSpace(this._options.TypeSpecifier) == false) {
var objectDictionary = new Dictionary<string, object>(); objectDictionary[this._options.TypeSpecifier] = targetType;
}
if (string.IsNullOrWhiteSpace(_options.TypeSpecifier) == false)
objectDictionary[_options.TypeSpecifier] = targetType; foreach(KeyValuePair<String, MemberInfo> field in fields) {
// Build the dictionary using property names and values
foreach (var field in fields) // Note: used to be: property.GetValue(target); but we would be reading private properties
{ try {
// Build the dictionary using property names and values objectDictionary[field.Key] = field.Value is PropertyInfo property
// Note: used to be: property.GetValue(target); but we would be reading private properties ? property.GetCacheGetMethod(this._options.IncludeNonPublic)(target)
try : (field.Value as FieldInfo)?.GetValue(target);
{ } catch {
objectDictionary[field.Key] = field.Value is PropertyInfo property /* ignored */
? property.GetCacheGetMethod(_options.IncludeNonPublic)(target) }
: (field.Value as FieldInfo)?.GetValue(target); }
}
catch return objectDictionary;
{ }
/* ignored */
} private String ResolveDictionary(IDictionary items, Int32 depth) {
} this.Append(OpenObjectChar, depth);
this.AppendLine();
return objectDictionary;
} // Iterate through the elements and output recursively
Int32 writeCount = 0;
private string ResolveDictionary(IDictionary items, int depth) foreach(Object key in items.Keys) {
{ // Serialize and append the key (first char indented)
Append(OpenObjectChar, depth); this.Append(StringQuotedChar, depth + 1);
AppendLine(); Escape(key.ToString(), this._builder);
_ = this._builder
// Iterate through the elements and output recursively
var writeCount = 0;
foreach (var key in items.Keys)
{
// Serialize and append the key (first char indented)
Append(StringQuotedChar, depth + 1);
Escape(key.ToString(), _builder);
_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();
}
// 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(serializedValue, 0);
AppendLine();
writeCount++; // Add a comma and start a new line -- We will remove the last one when we are done writing the elements
} this.Append(FieldSeparatorChar, 0);
this.AppendLine();
// Output the end of the object and set the result writeCount++;
RemoveLastComma(); }
Append(CloseObjectChar, writeCount > 0 ? depth : 0);
return _builder.ToString(); // Output the end of the object and set the result
} this.RemoveLastComma();
this.Append(CloseObjectChar, writeCount > 0 ? depth : 0);
private string ResolveObject(object target, int depth) return this._builder.ToString();
{ }
var targetType = target.GetType();
var fields = _options.GetProperties(targetType); private String ResolveObject(Object target, Int32 depth) {
Type targetType = target.GetType();
if (fields.Count == 0 && string.IsNullOrWhiteSpace(_options.TypeSpecifier)) Dictionary<String, MemberInfo> fields = this._options.GetProperties(targetType);
return EmptyObjectLiteral;
if(fields.Count == 0 && String.IsNullOrWhiteSpace(this._options.TypeSpecifier)) {
// If we arrive here, then we convert the object into a return EmptyObjectLiteral;
// dictionary of property names and values and call the serialization }
// function again
var objectDictionary = CreateDictionary(fields, targetType.ToString(), target); // If we arrive here, then we convert the object into a
// dictionary of property names and values and call the serialization
return Serialize(objectDictionary, depth, _options); // function again
} Dictionary<String, Object> objectDictionary = this.CreateDictionary(fields, targetType.ToString(), target);
private string ResolveEnumerable(IEnumerable target, int depth) return Serialize(objectDictionary, depth, this._options);
{ }
// Cast the items as a generic object array
var items = target.Cast<object>(); private String ResolveEnumerable(IEnumerable target, Int32 depth) {
// Cast the items as a generic object array
Append(OpenArrayChar, depth); IEnumerable<Object> items = target.Cast<Object>();
AppendLine();
this.Append(OpenArrayChar, depth);
// Iterate through the elements and output recursively this.AppendLine();
var writeCount = 0;
foreach (var entry in items) // Iterate through the elements and output recursively
{ Int32 writeCount = 0;
var serializedValue = Serialize(entry, depth + 1, _options); foreach(Object entry in items) {
String serializedValue = Serialize(entry, depth + 1, this._options);
if (IsNonEmptyJsonArrayOrObject(serializedValue))
Append(serializedValue, 0); if(IsNonEmptyJsonArrayOrObject(serializedValue)) {
else this.Append(serializedValue, 0);
Append(serializedValue, depth + 1); } else {
this.Append(serializedValue, depth + 1);
Append(FieldSeparatorChar, 0); }
AppendLine();
writeCount++; this.Append(FieldSeparatorChar, 0);
} this.AppendLine();
writeCount++;
// Output the end of the array and set the result }
RemoveLastComma();
Append(CloseArrayChar, writeCount > 0 ? depth : 0); // Output the end of the array and set the result
return _builder.ToString(); this.RemoveLastComma();
} this.Append(CloseArrayChar, writeCount > 0 ? depth : 0);
return this._builder.ToString();
private void SetIndent(int depth) }
{
if (_options.Format == false || depth <= 0) return; private void SetIndent(Int32 depth) {
if(this._options.Format == false || depth <= 0) {
_builder.Append(IndentStrings.GetOrAdd(depth, x => new string(' ', x * 4))); return;
} }
/// <summary> _ = this._builder.Append(IndentStrings.GetOrAdd(depth, x => new String(' ', x * 4)));
/// Removes the last comma in the current string builder. }
/// </summary>
private void RemoveLastComma() /// <summary>
{ /// Removes the last comma in the current string builder.
if (_builder.Length < _lastCommaSearch.Length) /// </summary>
return; private void RemoveLastComma() {
if(this._builder.Length < this._lastCommaSearch.Length) {
if (_lastCommaSearch.Where((t, i) => _builder[_builder.Length - _lastCommaSearch.Length + i] != t).Any()) return;
{ }
return;
} if(this._lastCommaSearch.Where((t, i) => this._builder[this._builder.Length - this._lastCommaSearch.Length + i] != t).Any()) {
return;
// If we got this far, we simply remove the comma character }
_builder.Remove(_builder.Length - _lastCommaSearch.Length, 1);
} // If we got this far, we simply remove the comma character
_ = this._builder.Remove(this._builder.Length - this._lastCommaSearch.Length, 1);
private void Append(string text, int depth) }
{
SetIndent(depth); private void Append(String text, Int32 depth) {
_builder.Append(text); this.SetIndent(depth);
} _ = this._builder.Append(text);
}
private void Append(char text, int depth)
{ private void Append(Char text, Int32 depth) {
SetIndent(depth); this.SetIndent(depth);
_builder.Append(text); _ = this._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,107 +1,107 @@
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 ///
/// /// 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 static readonly ConcurrentDictionary<Type, Dictionary<Tuple<String, String>, MemberInfo>>
private class SerializerOptions TypeCache = new ConcurrentDictionary<Type, Dictionary<Tuple<String, String>, MemberInfo>>();
{
private static readonly ConcurrentDictionary<Type, Dictionary<Tuple<string, string>, MemberInfo>> private readonly String[] _includeProperties;
TypeCache = new ConcurrentDictionary<Type, Dictionary<Tuple<string, string>, MemberInfo>>(); private readonly String[] _excludeProperties;
private readonly Dictionary<Int32, List<WeakReference>> _parentReferences = new Dictionary<Int32, List<WeakReference>>();
private readonly string[] _includeProperties;
private readonly string[] _excludeProperties; public SerializerOptions(
private readonly Dictionary<int, List<WeakReference>> _parentReferences = new Dictionary<int, List<WeakReference>>(); Boolean format,
String typeSpecifier,
public SerializerOptions( String[] includeProperties,
bool format, String[] excludeProperties = null,
string typeSpecifier, Boolean includeNonPublic = true,
string[] includeProperties, IReadOnlyCollection<WeakReference> parentReferences = null) {
string[] excludeProperties = null, this._includeProperties = includeProperties;
bool includeNonPublic = true, this._excludeProperties = excludeProperties;
IReadOnlyCollection<WeakReference> parentReferences = null)
{ this.IncludeNonPublic = includeNonPublic;
_includeProperties = includeProperties; this.Format = format;
_excludeProperties = excludeProperties; this.TypeSpecifier = typeSpecifier;
IncludeNonPublic = includeNonPublic; if(parentReferences == null) {
Format = format; return;
TypeSpecifier = typeSpecifier; }
if (parentReferences == null) foreach(WeakReference parentReference in parentReferences.Where(x => x.IsAlive)) {
return; _ = this.IsObjectPresent(parentReference.Target);
}
foreach (var parentReference in parentReferences.Where(x => x.IsAlive)) }
{
IsObjectPresent(parentReference.Target); public Boolean Format {
} get;
} }
public String TypeSpecifier {
public bool Format { get; } get;
public string TypeSpecifier { get; } }
public bool IncludeNonPublic { get; } public Boolean IncludeNonPublic {
get;
internal bool IsObjectPresent(object target) }
{
var hashCode = target.GetHashCode(); internal Boolean IsObjectPresent(Object target) {
Int32 hashCode = target.GetHashCode();
if (_parentReferences.ContainsKey(hashCode))
{ if(this._parentReferences.ContainsKey(hashCode)) {
if (_parentReferences[hashCode].Any(p => ReferenceEquals(p.Target, target))) if(this._parentReferences[hashCode].Any(p => ReferenceEquals(p.Target, target))) {
return true; return true;
}
_parentReferences[hashCode].Add(new WeakReference(target));
return false; this._parentReferences[hashCode].Add(new WeakReference(target));
} return false;
}
_parentReferences.Add(hashCode, new List<WeakReference> { new WeakReference(target) });
return false; this._parentReferences.Add(hashCode, new List<WeakReference> { new WeakReference(target) });
} return false;
}
internal Dictionary<string, MemberInfo> GetProperties(Type targetType)
=> GetPropertiesCache(targetType) internal Dictionary<String, MemberInfo> GetProperties(Type targetType)
.When(() => _includeProperties?.Length > 0, => GetPropertiesCache(targetType)
query => query.Where(p => _includeProperties.Contains(p.Key.Item1))) .When(() => this._includeProperties?.Length > 0,
.When(() => _excludeProperties?.Length > 0, query => query.Where(p => this._includeProperties.Contains(p.Key.Item1)))
query => query.Where(p => !_excludeProperties.Contains(p.Key.Item1))) .When(() => this._excludeProperties?.Length > 0,
.ToDictionary(x => x.Key.Item2, x => x.Value); query => query.Where(p => !this._excludeProperties.Contains(p.Key.Item1)))
.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 var current)) if(TypeCache.TryGetValue(targetType, out Dictionary<Tuple<String, String>, MemberInfo> current)) {
return current; return current;
}
var fields =
new List<MemberInfo>(PropertyTypeCache.RetrieveAllProperties(targetType).Where(p => p.CanRead)); List<MemberInfo> fields =
new List<MemberInfo>(PropertyTypeCache.RetrieveAllProperties(targetType).Where(p => p.CanRead));
// If the target is a struct (value type) navigate the fields.
if (targetType.IsValueType()) // If the target is a struct (value type) navigate the fields.
{ 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;
} }
} }
} }
} }
+314 -326
View File
@@ -1,331 +1,319 @@
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>
/// A very simple, light-weight JSON library written by Mario
/// to teach Geo how things are done
///
/// This is an useful helper for small tasks but it doesn't represent a full-featured
/// serializer such as the beloved Json.NET.
/// </summary>
public static partial class Json {
#region Constants
internal const String AddMethodName = "Add";
private const Char OpenObjectChar = '{';
private const Char CloseObjectChar = '}';
private const Char OpenArrayChar = '[';
private const Char CloseArrayChar = ']';
private const Char FieldSeparatorChar = ',';
private const Char ValueSeparatorChar = ':';
private const Char StringEscapeChar = '\\';
private const Char StringQuotedChar = '"';
private const String EmptyObjectLiteral = "{ }";
private const String EmptyArrayLiteral = "[ ]";
private const String TrueLiteral = "true";
private const String FalseLiteral = "false";
private const String NullLiteral = "null";
#endregion
private static readonly PropertyTypeCache PropertyTypeCache = new PropertyTypeCache();
private static readonly FieldTypeCache FieldTypeCache = new FieldTypeCache();
private static readonly CollectionCacheRepository<String> IgnoredPropertiesCache = new CollectionCacheRepository<String>();
#region Public API
/// <summary> /// <summary>
/// A very simple, light-weight JSON library written by Mario /// Serializes the specified object into a JSON string.
/// to teach Geo how things are done
///
/// This is an useful helper for small tasks but it doesn't represent a full-featured
/// serializer such as the beloved Json.NET.
/// </summary> /// </summary>
public static partial class Json /// <param name="obj">The object.</param>
{ /// <param name="format">if set to <c>true</c> it formats and indents the output.</param>
#region Constants /// <param name="typeSpecifier">The type specifier. Leave null or empty to avoid setting.</param>
/// <param name="includeNonPublic">if set to <c>true</c> non-public getters will be also read.</param>
internal const string AddMethodName = "Add"; /// <param name="includedNames">The included property names.</param>
/// <param name="excludedNames">The excluded property names.</param>
private const char OpenObjectChar = '{'; /// <returns>
private const char CloseObjectChar = '}'; /// A <see cref="System.String" /> that represents the current object.
/// </returns>
private const char OpenArrayChar = '['; /// <example>
private const char CloseArrayChar = ']'; /// The following example describes how to serialize a simple object.
/// <code>
private const char FieldSeparatorChar = ','; /// using Unosquare.Swan.Formatters;
private const char ValueSeparatorChar = ':'; ///
/// class Example
private const char StringEscapeChar = '\\'; /// {
private const char StringQuotedChar = '"'; /// static void Main()
/// {
private const string EmptyObjectLiteral = "{ }"; /// var obj = new { One = "One", Two = "Two" };
private const string EmptyArrayLiteral = "[ ]"; ///
private const string TrueLiteral = "true"; /// var serial = Json.Serialize(obj); // {"One": "One","Two": "Two"}
private const string FalseLiteral = "false"; /// }
private const string NullLiteral = "null"; /// }
/// </code>
#endregion /// The following example details how to serialize an object using the <see cref="JsonPropertyAttribute"/>.
/// <code>
private static readonly PropertyTypeCache PropertyTypeCache = new PropertyTypeCache(); /// using Unosquare.Swan.Attributes;
private static readonly FieldTypeCache FieldTypeCache = new FieldTypeCache(); /// using Unosquare.Swan.Formatters;
private static readonly CollectionCacheRepository<string> IgnoredPropertiesCache = new CollectionCacheRepository<string>(); ///
/// class Example
#region Public API /// {
/// class JsonPropertyExample
/// <summary> /// {
/// Serializes the specified object into a JSON string. /// [JsonProperty("data")]
/// </summary> /// public string Data { get; set; }
/// <param name="obj">The object.</param> ///
/// <param name="format">if set to <c>true</c> it formats and indents the output.</param> /// [JsonProperty("ignoredData", true)]
/// <param name="typeSpecifier">The type specifier. Leave null or empty to avoid setting.</param> /// public string IgnoredData { get; set; }
/// <param name="includeNonPublic">if set to <c>true</c> non-public getters will be also read.</param> /// }
/// <param name="includedNames">The included property names.</param> ///
/// <param name="excludedNames">The excluded property names.</param> /// static void Main()
/// <returns> /// {
/// A <see cref="System.String" /> that represents the current object. /// var obj = new JsonPropertyExample() { Data = "OK", IgnoredData = "OK" };
/// </returns> ///
/// <example> /// // {"data": "OK"}
/// The following example describes how to serialize a simple object. /// var serializedObj = Json.Serialize(obj);
/// <code> /// }
/// using Unosquare.Swan.Formatters; /// }
/// /// </code>
/// class Example /// </example>
/// { public static String Serialize(
/// static void Main() Object obj,
/// { Boolean format = false,
/// var obj = new { One = "One", Two = "Two" }; String typeSpecifier = null,
/// Boolean includeNonPublic = false,
/// var serial = Json.Serialize(obj); // {"One": "One","Two": "Two"} String[] includedNames = null,
/// } String[] excludedNames = null) => Serialize(obj, format, typeSpecifier, includeNonPublic, includedNames, excludedNames, null);
/// }
/// </code> /// <summary>
/// The following example details how to serialize an object using the <see cref="JsonPropertyAttribute"/>. /// Serializes the specified object into a JSON string.
/// <code> /// </summary>
/// using Unosquare.Swan.Attributes; /// <param name="obj">The object.</param>
/// using Unosquare.Swan.Formatters; /// <param name="format">if set to <c>true</c> it formats and indents the output.</param>
/// /// <param name="typeSpecifier">The type specifier. Leave null or empty to avoid setting.</param>
/// class Example /// <param name="includeNonPublic">if set to <c>true</c> non-public getters will be also read.</param>
/// { /// <param name="includedNames">The included property names.</param>
/// class JsonPropertyExample /// <param name="excludedNames">The excluded property names.</param>
/// { /// <param name="parentReferences">The parent references.</param>
/// [JsonProperty("data")] /// <returns>
/// public string Data { get; set; } /// A <see cref="System.String" /> that represents the current object.
/// /// </returns>
/// [JsonProperty("ignoredData", true)] public static String Serialize(
/// public string IgnoredData { get; set; } Object obj,
/// } Boolean format,
/// String typeSpecifier,
/// static void Main() Boolean includeNonPublic,
/// { String[] includedNames,
/// var obj = new JsonPropertyExample() { Data = "OK", IgnoredData = "OK" }; String[] excludedNames,
/// List<WeakReference> parentReferences) {
/// // {"data": "OK"} if(obj != null && (obj is String || Definitions.AllBasicValueTypes.Contains(obj.GetType()))) {
/// var serializedObj = Json.Serialize(obj); return SerializePrimitiveValue(obj);
/// } }
/// }
/// </code> SerializerOptions options = new SerializerOptions(
/// </example> format,
public static string Serialize( typeSpecifier,
object obj, includedNames,
bool format = false, GetExcludedNames(obj?.GetType(), excludedNames),
string typeSpecifier = null, includeNonPublic,
bool includeNonPublic = false, parentReferences);
string[] includedNames = null,
string[] excludedNames = null) return Serializer.Serialize(obj, 0, options);
{ }
return Serialize(obj, format, typeSpecifier, includeNonPublic, includedNames, excludedNames, null);
} /// <summary>
/// Serializes the specified object only including the specified property names.
/// <summary> /// </summary>
/// Serializes the specified object into a JSON string. /// <param name="obj">The object.</param>
/// </summary> /// <param name="format">if set to <c>true</c> it formats and indents the output.</param>
/// <param name="obj">The object.</param> /// <param name="includeNames">The include names.</param>
/// <param name="format">if set to <c>true</c> it formats and indents the output.</param> /// <returns>A <see cref="System.String" /> that represents the current object.</returns>
/// <param name="typeSpecifier">The type specifier. Leave null or empty to avoid setting.</param> /// <example>
/// <param name="includeNonPublic">if set to <c>true</c> non-public getters will be also read.</param> /// The following example shows how to serialize a simple object including the specified properties.
/// <param name="includedNames">The included property names.</param> /// <code>
/// <param name="excludedNames">The excluded property names.</param> /// using Unosquare.Swan.Formatters;
/// <param name="parentReferences">The parent references.</param> ///
/// <returns> /// class Example
/// A <see cref="System.String" /> that represents the current object. /// {
/// </returns> /// static void Main()
public static string Serialize( /// {
object obj, /// // object to serialize
bool format, /// var obj = new { One = "One", Two = "Two", Three = "Three" };
string typeSpecifier, ///
bool includeNonPublic, /// // the included names
string[] includedNames, /// var includedNames = new[] { "Two", "Three" };
string[] excludedNames, ///
List<WeakReference> parentReferences) /// // serialize only the included names
{ /// var data = Json.SerializeOnly(basicObject, true, includedNames);
if (obj != null && (obj is string || Definitions.AllBasicValueTypes.Contains(obj.GetType()))) /// // {"Two": "Two","Three": "Three" }
{ /// }
return SerializePrimitiveValue(obj); /// }
} /// </code>
/// </example>
var options = new SerializerOptions( public static String SerializeOnly(Object obj, Boolean format, params String[] includeNames) {
format, SerializerOptions options = new SerializerOptions(format, null, includeNames);
typeSpecifier,
includedNames, return Serializer.Serialize(obj, 0, options);
GetExcludedNames(obj?.GetType(), excludedNames), }
includeNonPublic,
parentReferences); /// <summary>
/// Serializes the specified object excluding the specified property names.
return Serializer.Serialize(obj, 0, options); /// </summary>
} /// <param name="obj">The object.</param>
/// <param name="format">if set to <c>true</c> it formats and indents the output.</param>
/// <summary> /// <param name="excludeNames">The exclude names.</param>
/// Serializes the specified object only including the specified property names. /// <returns>A <see cref="System.String" /> that represents the current object.</returns>
/// </summary> /// <example>
/// <param name="obj">The object.</param> /// The following code shows how to serialize a simple object exluding the specified properties.
/// <param name="format">if set to <c>true</c> it formats and indents the output.</param> /// <code>
/// <param name="includeNames">The include names.</param> /// using Unosquare.Swan.Formatters;
/// <returns>A <see cref="System.String" /> that represents the current object.</returns> ///
/// <example> /// class Example
/// The following example shows how to serialize a simple object including the specified properties. /// {
/// <code> /// static void Main()
/// using Unosquare.Swan.Formatters; /// {
/// /// // object to serialize
/// class Example /// var obj = new { One = "One", Two = "Two", Three = "Three" };
/// { ///
/// static void Main() /// // the excluded names
/// { /// var excludeNames = new[] { "Two", "Three" };
/// // object to serialize ///
/// var obj = new { One = "One", Two = "Two", Three = "Three" }; /// // serialize excluding
/// /// var data = Json.SerializeExcluding(basicObject, false, includedNames);
/// // the included names /// // {"One": "One"}
/// var includedNames = new[] { "Two", "Three" }; /// }
/// /// }
/// // serialize only the included names /// </code>
/// var data = Json.SerializeOnly(basicObject, true, includedNames); /// </example>
/// // {"Two": "Two","Three": "Three" } public static String SerializeExcluding(Object obj, Boolean format, params String[] excludeNames) {
/// } SerializerOptions options = new SerializerOptions(format, null, null, excludeNames);
/// }
/// </code> return Serializer.Serialize(obj, 0, options);
/// </example> }
public static string SerializeOnly(object obj, bool format, params string[] includeNames)
{ /// <summary>
var options = new SerializerOptions(format, null, includeNames); /// Deserializes the specified json string as either a Dictionary[string, object] or as a List[object]
/// depending on the syntax of the JSON string.
return Serializer.Serialize(obj, 0, options); /// </summary>
} /// <param name="json">The json.</param>
/// <returns>Type of the current deserializes.</returns>
/// <summary> /// <example>
/// Serializes the specified object excluding the specified property names. /// The following code shows how to deserialize a JSON string into a Dictionary.
/// </summary> /// <code>
/// <param name="obj">The object.</param> /// using Unosquare.Swan.Formatters;
/// <param name="format">if set to <c>true</c> it formats and indents the output.</param> ///
/// <param name="excludeNames">The exclude names.</param> /// class Example
/// <returns>A <see cref="System.String" /> that represents the current object.</returns> /// {
/// <example> /// static void Main()
/// The following code shows how to serialize a simple object exluding the specified properties. /// {
/// <code> /// // json to deserialize
/// using Unosquare.Swan.Formatters; /// var basicJson = "{\"One\":\"One\",\"Two\":\"Two\",\"Three\":\"Three\"}";
/// ///
/// class Example /// // deserializes the specified json into a Dictionary&lt;string, object&gt;.
/// { /// var data = Json.Deserialize(basicJson);
/// static void Main() /// }
/// { /// }
/// // object to serialize /// </code>
/// var obj = new { One = "One", Two = "Two", Three = "Three" }; /// </example>
/// public static Object Deserialize(String json) => Deserializer.DeserializeInternal(json);
/// // the excluded names
/// var excludeNames = new[] { "Two", "Three" }; /// <summary>
/// /// Deserializes the specified json string and converts it to the specified object type.
/// // serialize excluding /// Non-public constructors and property setters are ignored.
/// var data = Json.SerializeExcluding(basicObject, false, includedNames); /// </summary>
/// // {"One": "One"} /// <typeparam name="T">The type of object to deserialize.</typeparam>
/// } /// <param name="json">The json.</param>
/// } /// <returns>The deserialized specified type object.</returns>
/// </code> /// <example>
/// </example> /// The following code describes how to deserialize a JSON string into an object of type T.
public static string SerializeExcluding(object obj, bool format, params string[] excludeNames) /// <code>
{ /// using Unosquare.Swan.Formatters;
var options = new SerializerOptions(format, null, null, excludeNames); ///
/// class Example
return Serializer.Serialize(obj, 0, options); /// {
} /// static void Main()
/// {
/// <summary> /// // json type BasicJson to serialize
/// Deserializes the specified json string as either a Dictionary[string, object] or as a List[object] /// var basicJson = "{\"One\":\"One\",\"Two\":\"Two\",\"Three\":\"Three\"}";
/// depending on the syntax of the JSON string. ///
/// </summary> /// // deserializes the specified string in a new instance of the type BasicJson.
/// <param name="json">The json.</param> /// var data = Json.Deserialize&lt;BasicJson&gt;(basicJson);
/// <returns>Type of the current deserializes.</returns> /// }
/// <example> /// }
/// The following code shows how to deserialize a JSON string into a Dictionary. /// </code>
/// <code> /// </example>
/// using Unosquare.Swan.Formatters; public static T Deserialize<T>(String json) => (T)Deserialize(json, typeof(T));
///
/// class Example /// <summary>
/// { /// Deserializes the specified json string and converts it to the specified object type.
/// static void Main() /// </summary>
/// { /// <typeparam name="T">The type of object to deserialize.</typeparam>
/// // json to deserialize /// <param name="json">The json.</param>
/// var basicJson = "{\"One\":\"One\",\"Two\":\"Two\",\"Three\":\"Three\"}"; /// <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>
/// // deserializes the specified json into a Dictionary&lt;string, object&gt;. public static T Deserialize<T>(String json, Boolean includeNonPublic) => (T)Deserialize(json, typeof(T), includeNonPublic);
/// var data = Json.Deserialize(basicJson);
/// } /// <summary>
/// } /// Deserializes the specified json string and converts it to the specified object type.
/// </code> /// </summary>
/// </example> /// <param name="json">The json.</param>
public static object Deserialize(string json) => Deserializer.DeserializeInternal(json); /// <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>
/// <summary> /// <returns>Type of the current conversion from json result.</returns>
/// Deserializes the specified json string and converts it to the specified object type. public static Object Deserialize(String json, Type resultType, Boolean includeNonPublic = false)
/// Non-public constructors and property setters are ignored. => Converter.FromJsonResult(Deserializer.DeserializeInternal(json), resultType, includeNonPublic);
/// </summary>
/// <typeparam name="T">The type of object to deserialize.</typeparam> #endregion
/// <param name="json">The json.</param>
/// <returns>The deserialized specified type object.</returns> #region Private API
/// <example>
/// The following code describes how to deserialize a JSON string into an object of type T. private static String[] GetExcludedNames(Type type, String[] excludedNames) {
/// <code> if(type == null) {
/// using Unosquare.Swan.Formatters; return excludedNames;
/// }
/// class Example
/// { IEnumerable<String> excludedByAttr = IgnoredPropertiesCache.Retrieve(type, t => t.GetProperties()
/// static void Main()
/// {
/// // json type BasicJson to serialize
/// var basicJson = "{\"One\":\"One\",\"Two\":\"Two\",\"Three\":\"Three\"}";
///
/// // deserializes the specified string in a new instance of the type BasicJson.
/// var data = Json.Deserialize&lt;BasicJson&gt;(basicJson);
/// }
/// }
/// </code>
/// </example>
public static T Deserialize<T>(string json) => (T)Deserialize(json, typeof(T));
/// <summary>
/// Deserializes the specified json string and converts it to the specified object type.
/// </summary>
/// <typeparam name="T">The type of object to deserialize.</typeparam>
/// <param name="json">The json.</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>
public static T Deserialize<T>(string json, bool includeNonPublic) => (T)Deserialize(json, typeof(T), includeNonPublic);
/// <summary>
/// Deserializes the specified json string and converts it to the specified object type.
/// </summary>
/// <param name="json">The json.</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>
/// <returns>Type of the current conversion from json result.</returns>
public static object Deserialize(string json, Type resultType, bool includeNonPublic = false)
=> Converter.FromJsonResult(Deserializer.DeserializeInternal(json), resultType, includeNonPublic);
#endregion
#region Private API
private static string[] GetExcludedNames(Type type, string[] excludedNames)
{
if (type == null)
return excludedNames;
var 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:
{ return stringValue;
case string stringValue: case Boolean boolValue:
return stringValue; return boolValue ? TrueLiteral : FalseLiteral;
case bool boolValue: default:
return boolValue ? TrueLiteral : FalseLiteral; return obj.ToString();
default: }
return obj.ToString(); }
} #endregion
} }
#endregion
}
} }
+169 -171
View File
@@ -1,174 +1,172 @@
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>
/// A thread-safe cache of attributes belonging to a given key (MemberInfo or Type).
///
/// The Retrieve method is the most useful one in this class as it
/// calls the retrieval process if the type is not contained
/// in the cache.
/// </summary>
public class AttributeCache {
private readonly Lazy<ConcurrentDictionary<Tuple<Object, Type>, IEnumerable<Object>>> _data =
new Lazy<ConcurrentDictionary<Tuple<Object, Type>, IEnumerable<Object>>>(() =>
new ConcurrentDictionary<Tuple<Object, Type>, IEnumerable<Object>>(), true);
/// <summary> /// <summary>
/// A thread-safe cache of attributes belonging to a given key (MemberInfo or Type). /// Initializes a new instance of the <see cref="AttributeCache"/> class.
///
/// The Retrieve method is the most useful one in this class as it
/// calls the retrieval process if the type is not contained
/// in the cache.
/// </summary> /// </summary>
public class AttributeCache /// <param name="propertyCache">The property cache object.</param>
{ public AttributeCache(PropertyTypeCache propertyCache = null) => this.PropertyTypeCache = propertyCache ?? Runtime.PropertyTypeCache;
private readonly Lazy<ConcurrentDictionary<Tuple<object, Type>, IEnumerable<object>>> _data =
new Lazy<ConcurrentDictionary<Tuple<object, Type>, IEnumerable<object>>>(() => /// <summary>
new ConcurrentDictionary<Tuple<object, Type>, IEnumerable<object>>(), true); /// A PropertyTypeCache object for caching properties and their attributes.
/// </summary>
/// <summary> public PropertyTypeCache PropertyTypeCache {
/// Initializes a new instance of the <see cref="AttributeCache"/> class. get;
/// </summary> }
/// <param name="propertyCache">The property cache object.</param>
public AttributeCache(PropertyTypeCache propertyCache = null) /// <summary>
{ /// Determines whether [contains] [the specified member].
PropertyTypeCache = propertyCache ?? Runtime.PropertyTypeCache; /// </summary>
} /// <typeparam name="T">The type of the attribute to be retrieved.</typeparam>
/// <param name="member">The member.</param>
/// <summary> /// <returns>
/// A PropertyTypeCache object for caching properties and their attributes. /// <c>true</c> if [contains] [the specified member]; otherwise, <c>false</c>.
/// </summary> /// </returns>
public PropertyTypeCache PropertyTypeCache { get; } public Boolean Contains<T>(MemberInfo member) => this._data.Value.ContainsKey(new Tuple<Object, Type>(member, typeof(T)));
/// <summary> /// <summary>
/// Determines whether [contains] [the specified member]. /// Gets specific attributes from a member constrained to an attribute.
/// </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>
/// <param name="member">The member.</param> /// <param name="member">The member.</param>
/// <returns> /// <param name="inherit"><c>true</c> to inspect the ancestors of element; otherwise, <c>false</c>.</param>
/// <c>true</c> if [contains] [the specified member]; otherwise, <c>false</c>. /// <returns>An array of the attributes stored for the specified type.</returns>
/// </returns> public IEnumerable<Object> Retrieve<T>(MemberInfo member, Boolean inherit = false)
public bool Contains<T>(MemberInfo member) => _data.Value.ContainsKey(new Tuple<object, Type>(member, typeof(T))); where T : Attribute {
if(member == null) {
/// <summary> throw new ArgumentNullException(nameof(member));
/// Gets specific attributes from a member constrained to an attribute. }
/// </summary>
/// <typeparam name="T">The type of the attribute to be retrieved.</typeparam> return this.Retrieve(new Tuple<Object, Type>(member, typeof(T)), t => member.GetCustomAttributes<T>(inherit));
/// <param name="member">The member.</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> /// <summary>
public IEnumerable<object> Retrieve<T>(MemberInfo member, bool inherit = false) /// Gets all attributes of a specific type from a member.
where T : Attribute /// </summary>
{ /// <param name="member">The member.</param>
if (member == null) /// <param name="type">The attribute type.</param>
throw new ArgumentNullException(nameof(member)); /// <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>
return Retrieve(new Tuple<object, Type>(member, typeof(T)), t => member.GetCustomAttributes<T>(inherit)); public IEnumerable<Object> Retrieve(MemberInfo member, Type type, Boolean inherit = false) {
} if(member == null) {
throw new ArgumentNullException(nameof(member));
/// <summary> }
/// Gets all attributes of a specific type from a member.
/// </summary> if(type == null) {
/// <param name="member">The member.</param> throw new ArgumentNullException(nameof(type));
/// <param name="type">The attribute type.</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> return this.Retrieve(
public IEnumerable<object> Retrieve(MemberInfo member, Type type, bool inherit = false) new Tuple<Object, Type>(member, type),
{ t => member.GetCustomAttributes(type, inherit));
if (member == null) }
throw new ArgumentNullException(nameof(member));
/// <summary>
if (type == null) /// Gets one attribute of a specific type from a member.
throw new ArgumentNullException(nameof(type)); /// </summary>
/// <typeparam name="T">The attribute type.</typeparam>
return Retrieve( /// <param name="member">The member.</param>
new Tuple<object, Type>(member, type), /// <param name="inherit"><c>true</c> to inspect the ancestors of element; otherwise, <c>false</c>.</param>
t => member.GetCustomAttributes(type, inherit)); /// <returns>An attribute stored for the specified type.</returns>
} public T RetrieveOne<T>(MemberInfo member, Boolean inherit = false)
where T : Attribute {
/// <summary> if(member == null) {
/// Gets one attribute of a specific type from a member. return default;
/// </summary> }
/// <typeparam name="T">The attribute type.</typeparam>
/// <param name="member">The member.</param> IEnumerable<Object> attr = this.Retrieve(
/// <param name="inherit"><c>true</c> to inspect the ancestors of element; otherwise, <c>false</c>.</param> new Tuple<Object, Type>(member, typeof(T)),
/// <returns>An attribute stored for the specified type.</returns> t => member.GetCustomAttributes(typeof(T), inherit));
public T RetrieveOne<T>(MemberInfo member, bool inherit = false)
where T : Attribute return ConvertToAttribute<T>(attr);
{ }
if (member == null)
return default; /// <summary>
/// Gets one attribute of a specific type from a generic type.
var attr = Retrieve( /// </summary>
new Tuple<object, Type>(member, typeof(T)), /// <typeparam name="TAttribute">The type of the attribute.</typeparam>
t => member.GetCustomAttributes(typeof(T), inherit)); /// <typeparam name="T">The type to retrieve the attribute.</typeparam>
/// <param name="inherit">if set to <c>true</c> [inherit].</param>
return ConvertToAttribute<T>(attr); /// <returns>An attribute stored for the specified type.</returns>
} public TAttribute RetrieveOne<TAttribute, T>(Boolean inherit = false)
where TAttribute : Attribute {
/// <summary> IEnumerable<Object> attr = this.Retrieve(
/// Gets one attribute of a specific type from a generic type. new Tuple<Object, Type>(typeof(T), typeof(TAttribute)),
/// </summary> t => typeof(T).GetCustomAttributes(typeof(TAttribute), inherit));
/// <typeparam name="TAttribute">The type of the attribute.</typeparam>
/// <typeparam name="T">The type to retrieve the attribute.</typeparam> return ConvertToAttribute<TAttribute>(attr);
/// <param name="inherit">if set to <c>true</c> [inherit].</param> }
/// <returns>An attribute stored for the specified type.</returns>
public TAttribute RetrieveOne<TAttribute, T>(bool inherit = false) /// <summary>
where TAttribute : Attribute /// Gets all properties an their attributes of a given type constrained to only attributes.
{ /// </summary>
var attr = Retrieve( /// <typeparam name="T">The type of the attribute to retrieve.</typeparam>
new Tuple<object, Type>(typeof(T), typeof(TAttribute)), /// <param name="type">The type of the object.</param>
t => typeof(T).GetCustomAttributes(typeof(TAttribute), inherit)); /// <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>
return ConvertToAttribute<TAttribute>(attr); public Dictionary<PropertyInfo, IEnumerable<Object>> Retrieve<T>(Type type, Boolean inherit = false)
} where T : Attribute {
if(type == null) {
/// <summary> throw new ArgumentNullException(nameof(type));
/// Gets all properties an their attributes of a given type constrained to only attributes. }
/// </summary>
/// <typeparam name="T">The type of the attribute to retrieve.</typeparam> return this.PropertyTypeCache.RetrieveAllProperties(type, true)
/// <param name="type">The type of the object.</param> .ToDictionary(x => x, x => this.Retrieve<T>(x, inherit));
/// <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>
public Dictionary<PropertyInfo, IEnumerable<object>> Retrieve<T>(Type type, bool inherit = false) /// <summary>
where T : Attribute /// Gets all properties and their attributes of a given type.
{ /// </summary>
if (type == null) /// <typeparam name="T">The object type used to extract the properties from.</typeparam>
throw new ArgumentNullException(nameof(type)); /// <param name="attributeType">Type of the attribute.</param>
/// <param name="inherit"><c>true</c> to inspect the ancestors of element; otherwise, <c>false</c>.</param>
return PropertyTypeCache.RetrieveAllProperties(type, true) /// <returns>
.ToDictionary(x => x, x => Retrieve<T>(x, inherit)); /// A dictionary of the properties and their attributes stored for the specified type.
} /// </returns>
public Dictionary<PropertyInfo, IEnumerable<Object>> RetrieveFromType<T>(Type attributeType, Boolean inherit = false) {
/// <summary> if(attributeType == null) {
/// Gets all properties and their attributes of a given type. throw new ArgumentNullException(nameof(attributeType));
/// </summary> }
/// <typeparam name="T">The object type used to extract the properties from.</typeparam>
/// <param name="attributeType">Type of the attribute.</param> return this.PropertyTypeCache.RetrieveAllProperties<T>(true)
/// <param name="inherit"><c>true</c> to inspect the ancestors of element; otherwise, <c>false</c>.</param> .ToDictionary(x => x, x => this.Retrieve(x, attributeType, inherit));
/// <returns> }
/// A dictionary of the properties and their attributes stored for the specified type.
/// </returns> private static T ConvertToAttribute<T>(IEnumerable<Object> attr)
public Dictionary<PropertyInfo, IEnumerable<object>> RetrieveFromType<T>(Type attributeType, bool inherit = false) where T : Attribute {
{ if(attr?.Any() != true) {
if (attributeType == null) return default;
throw new ArgumentNullException(nameof(attributeType)); }
return PropertyTypeCache.RetrieveAllProperties<T>(true) if(attr.Count() == 1) {
.ToDictionary(x => x, x => Retrieve(x, attributeType, inherit)); return (T)Convert.ChangeType(attr.First(), typeof(T));
} }
private static T ConvertToAttribute<T>(IEnumerable<object> attr) throw new AmbiguousMatchException("Multiple custom attributes of the same type found.");
where T : Attribute }
{
if (attr?.Any() != true) private IEnumerable<Object> Retrieve(Tuple<Object, Type> key, Func<Tuple<Object, Type>, IEnumerable<Object>> factory) {
return default; if(factory == null) {
throw new ArgumentNullException(nameof(factory));
if (attr.Count() == 1) }
return (T) Convert.ChangeType(attr.First(), typeof(T));
return this._data.Value.GetOrAdd(key, k => factory.Invoke(k).Where(item => item != null));
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)
{
if (factory == null)
throw new ArgumentNullException(nameof(factory));
return _data.Value.GetOrAdd(key, k => factory.Invoke(k).Where(item => item != null));
}
}
} }
@@ -1,107 +1,114 @@
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>
/// Represents a Property object from a Object Reflection Property with extended values.
/// </summary>
public class ExtendedPropertyInfo {
/// <summary> /// <summary>
/// Represents a Property object from a Object Reflection Property with extended values. /// Initializes a new instance of the <see cref="ExtendedPropertyInfo"/> class.
/// </summary> /// </summary>
public class ExtendedPropertyInfo /// <param name="propertyInfo">The property information.</param>
{ public ExtendedPropertyInfo(PropertyInfo propertyInfo) {
/// <summary> if(propertyInfo == null) {
/// Initializes a new instance of the <see cref="ExtendedPropertyInfo"/> class. throw new ArgumentNullException(nameof(propertyInfo));
/// </summary> }
/// <param name="propertyInfo">The property information.</param>
public ExtendedPropertyInfo(PropertyInfo propertyInfo) this.Property = propertyInfo.Name;
{ this.DataType = propertyInfo.PropertyType.Name;
if (propertyInfo == null)
{ foreach(PropertyDisplayAttribute display in Runtime.AttributeCache.Retrieve<PropertyDisplayAttribute>(propertyInfo, true)) {
throw new ArgumentNullException(nameof(propertyInfo)); this.Name = display.Name;
} this.Description = display.Description;
this.GroupName = display.GroupName;
Property = propertyInfo.Name; this.DefaultValue = display.DefaultValue;
DataType = propertyInfo.PropertyType.Name; }
}
foreach (PropertyDisplayAttribute display in Runtime.AttributeCache.Retrieve<PropertyDisplayAttribute>(propertyInfo, true))
{
Name = display.Name;
Description = display.Description;
GroupName = display.GroupName;
DefaultValue = display.DefaultValue;
}
}
/// <summary>
/// Gets or sets the property.
/// </summary>
/// <value>
/// The property.
/// </value>
public string Property { get; }
/// <summary>
/// Gets or sets the type of the data.
/// </summary>
/// <value>
/// The type of the data.
/// </value>
public string DataType { get; }
/// <summary>
/// Gets or sets the value.
/// </summary>
/// <value>
/// The value.
/// </value>
public object Value { get; set; }
/// <summary>
/// Gets or sets the default value.
/// </summary>
/// <value>
/// The default value.
/// </value>
public object DefaultValue { get; }
/// <summary>
/// Gets or sets the name.
/// </summary>
/// <value>
/// The name.
/// </value>
public string Name { get; }
/// <summary>
/// Gets or sets the description.
/// </summary>
/// <value>
/// The description.
/// </value>
public string Description { get; }
/// <summary>
/// Gets or sets the name of the group.
/// </summary>
/// <value>
/// The name of the group.
/// </value>
public string GroupName { get; }
}
/// <summary> /// <summary>
/// Represents a Property object from a Object Reflection Property with extended values. /// Gets or sets the property.
/// </summary> /// </summary>
/// <typeparam name="T">The type of the object.</typeparam> /// <value>
public class ExtendedPropertyInfo<T> : ExtendedPropertyInfo /// The property.
{ /// </value>
/// <summary> public String Property {
/// Initializes a new instance of the <see cref="ExtendedPropertyInfo{T}"/> class. get;
/// </summary> }
/// <param name="property">The property.</param>
public ExtendedPropertyInfo(string property) /// <summary>
: base(typeof(T).GetProperty(property)) /// Gets or sets the type of the data.
{ /// </summary>
} /// <value>
} /// The type of the data.
/// </value>
public String DataType {
get;
}
/// <summary>
/// Gets or sets the value.
/// </summary>
/// <value>
/// The value.
/// </value>
public Object Value {
get; set;
}
/// <summary>
/// Gets or sets the default value.
/// </summary>
/// <value>
/// The default value.
/// </value>
public Object DefaultValue {
get;
}
/// <summary>
/// Gets or sets the name.
/// </summary>
/// <value>
/// The name.
/// </value>
public String Name {
get;
}
/// <summary>
/// Gets or sets the description.
/// </summary>
/// <value>
/// The description.
/// </value>
public String Description {
get;
}
/// <summary>
/// Gets or sets the name of the group.
/// </summary>
/// <value>
/// The name of the group.
/// </value>
public String GroupName {
get;
}
}
/// <summary>
/// Represents a Property object from a Object Reflection Property with extended values.
/// </summary>
/// <typeparam name="T">The type of the object.</typeparam>
public class ExtendedPropertyInfo<T> : ExtendedPropertyInfo {
/// <summary>
/// Initializes a new instance of the <see cref="ExtendedPropertyInfo{T}"/> class.
/// </summary>
/// <param name="property">The property.</param>
public ExtendedPropertyInfo(String property)
: base(typeof(T).GetProperty(property)) {
}
}
} }
+276 -285
View File
@@ -1,290 +1,281 @@
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
private const String TryParseMethodName = nameof(Byte.TryParse);
private const String ToStringMethodName = nameof(ToString);
private static readonly Type[] NumericTypes =
{ {
#region Static Declarations typeof(Byte),
typeof(SByte),
private const string TryParseMethodName = nameof(byte.TryParse); typeof(Decimal),
private const string ToStringMethodName = nameof(ToString); typeof(Double),
typeof(Single),
private static readonly Type[] NumericTypes = typeof(Int32),
{ typeof(UInt32),
typeof(byte), typeof(Int64),
typeof(sbyte), typeof(UInt64),
typeof(decimal), typeof(Int16),
typeof(double), typeof(UInt16),
typeof(float), };
typeof(int),
typeof(uint), #endregion
typeof(long),
typeof(ulong), #region State Management
typeof(short),
typeof(ushort), private readonly ParameterInfo[] _tryParseParameters;
}; private readonly Int32 _toStringArgumentLength;
#endregion #endregion
#region State Management #region Constructors
private readonly ParameterInfo[] _tryParseParameters; /// <summary>
private readonly int _toStringArgumentLength; /// Initializes a new instance of the <see cref="ExtendedTypeInfo"/> class.
/// </summary>
#endregion /// <param name="t">The t.</param>
public ExtendedTypeInfo(Type t) {
#region Constructors this.Type = t ?? throw new ArgumentNullException(nameof(t));
this.IsNullableValueType = this.Type.GetTypeInfo().IsGenericType
/// <summary> && this.Type.GetGenericTypeDefinition() == typeof(Nullable<>);
/// Initializes a new instance of the <see cref="ExtendedTypeInfo"/> class.
/// </summary> this.IsValueType = t.GetTypeInfo().IsValueType;
/// <param name="t">The t.</param>
public ExtendedTypeInfo(Type t) this.UnderlyingType = this.IsNullableValueType ?
{ new NullableConverter(this.Type).UnderlyingType :
Type = t ?? throw new ArgumentNullException(nameof(t)); this.Type;
IsNullableValueType = Type.GetTypeInfo().IsGenericType
&& Type.GetGenericTypeDefinition() == typeof(Nullable<>); this.IsNumeric = NumericTypes.Contains(this.UnderlyingType);
IsValueType = t.GetTypeInfo().IsValueType; // Extract the TryParse method info
try {
UnderlyingType = IsNullableValueType ? this.TryParseMethodInfo = this.UnderlyingType.GetMethod(TryParseMethodName,
new NullableConverter(Type).UnderlyingType : new[] { typeof(String), typeof(NumberStyles), typeof(IFormatProvider), this.UnderlyingType.MakeByRefType() }) ??
Type; this.UnderlyingType.GetMethod(TryParseMethodName,
new[] { typeof(String), this.UnderlyingType.MakeByRefType() });
IsNumeric = NumericTypes.Contains(UnderlyingType);
this._tryParseParameters = this.TryParseMethodInfo?.GetParameters();
// Extract the TryParse method info } catch {
try // ignored
{ }
TryParseMethodInfo = UnderlyingType.GetMethod(TryParseMethodName,
new[] { typeof(string), typeof(NumberStyles), typeof(IFormatProvider), UnderlyingType.MakeByRefType() }) ?? // Extract the ToString method Info
UnderlyingType.GetMethod(TryParseMethodName, try {
new[] { typeof(string), UnderlyingType.MakeByRefType() }); this.ToStringMethodInfo = this.UnderlyingType.GetMethod(ToStringMethodName,
_tryParseParameters = TryParseMethodInfo?.GetParameters();
}
catch
{
// ignored
}
// Extract the ToString method Info
try
{
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 }
}
} #endregion
#endregion #region Properties
#region Properties
/// <summary>
/// Gets the type this extended info class provides for.
/// </summary>
/// <value>
/// The type.
/// </value>
public Type Type { get; }
/// <summary>
/// Gets a value indicating whether the type is a nullable value type.
/// </summary>
/// <value>
/// <c>true</c> if this instance is nullable value type; otherwise, <c>false</c>.
/// </value>
public bool IsNullableValueType { get; }
/// <summary>
/// Gets a value indicating whether the type or underlying type is numeric.
/// </summary>
/// <value>
/// <c>true</c> if this instance is numeric; otherwise, <c>false</c>.
/// </value>
public bool IsNumeric { get; }
/// <summary>
/// Gets a value indicating whether the type is value type.
/// Nullable value types have this property set to False.
/// </summary>
public bool IsValueType { get; }
/// <summary>
/// When dealing with nullable value types, this property will
/// return the underlying value type of the nullable,
/// Otherwise it will return the same type as the Type property.
/// </summary>
/// <value>
/// The type of the underlying.
/// </value>
public Type UnderlyingType { get; }
/// <summary>
/// Gets the try parse method information. If the type does not contain
/// a suitable TryParse static method, it will return null.
/// </summary>
/// <value>
/// The try parse method information.
/// </value>
public MethodInfo TryParseMethodInfo { get; }
/// <summary>
/// Gets the ToString method info
/// It will prefer the overload containing the IFormatProvider argument.
/// </summary>
/// <value>
/// To string method information.
/// </value>
public MethodInfo ToStringMethodInfo { get; }
/// <summary>
/// Gets a value indicating whether the type contains a suitable TryParse method.
/// </summary>
/// <value>
/// <c>true</c> if this instance can parse natively; otherwise, <c>false</c>.
/// </value>
public bool CanParseNatively => TryParseMethodInfo != null;
#endregion
#region Methods
/// <summary>
/// Gets the default value of this type. For reference types it return null.
/// For value types it returns the default value.
/// </summary>
/// <returns>Default value of this type.</returns>
public object GetDefault() => Type.GetTypeInfo().IsValueType ? Activator.CreateInstance(Type) : null;
/// <summary>
/// Tries to parse the string into an object of the type this instance represents.
/// Returns false when no suitable TryParse methods exists for the type or when parsing fails
/// for any reason. When possible, this method uses CultureInfo.InvariantCulture and NumberStyles.Any.
/// </summary>
/// <param name="s">The s.</param>
/// <param name="result">The result.</param>
/// <returns><c>true</c> if parse was converted successfully; otherwise, <c>false</c>.</returns>
public bool TryParse(string s, out object result)
{
result = GetDefault();
try
{
if (Type == typeof(string))
{
result = Convert.ChangeType(s, Type);
return true;
}
if (IsNullableValueType && string.IsNullOrEmpty(s))
{
result = GetDefault();
return true;
}
if (CanParseNatively == false)
{
result = GetDefault();
return false;
}
// Build the arguments of the TryParse method
var dynamicArguments = new List<object> { s };
for (var pi = 1; pi < _tryParseParameters.Length - 1; pi++)
{
var argInfo = _tryParseParameters[pi];
if (argInfo.ParameterType == typeof(IFormatProvider))
dynamicArguments.Add(CultureInfo.InvariantCulture);
else if (argInfo.ParameterType == typeof(NumberStyles))
dynamicArguments.Add(NumberStyles.Any);
else
dynamicArguments.Add(null);
}
dynamicArguments.Add(null);
var parseArguments = dynamicArguments.ToArray();
if ((bool)TryParseMethodInfo.Invoke(null, parseArguments) == false)
{
result = GetDefault();
return false;
}
result = parseArguments[parseArguments.Length - 1];
return true;
}
catch
{
return false;
}
}
/// <summary>
/// Converts this instance to its string representation,
/// trying to use the CultureInfo.InvariantCulture
/// IFormat provider if the overload is available.
/// </summary>
/// <param name="instance">The instance.</param>
/// <returns>A <see cref="System.String" /> that represents the current object.</returns>
public string ToStringInvariant(object instance)
{
if (instance == null)
return string.Empty;
return _toStringArgumentLength != 1
? instance.ToString()
: ToStringMethodInfo.Invoke(instance, new object[] {CultureInfo.InvariantCulture}) as string;
}
#endregion
}
/// <summary> /// <summary>
/// Provides extended information about a type. /// Gets the type this extended info class provides for.
///
/// This class is mainly used to define sets of types within the Constants class
/// and it is not meant for other than querying the BasicTypesInfo dictionary.
/// </summary> /// </summary>
/// <typeparam name="T">The type of extended type information.</typeparam> /// <value>
public class ExtendedTypeInfo<T> : ExtendedTypeInfo /// The type.
{ /// </value>
/// <summary> public Type Type {
/// Initializes a new instance of the <see cref="ExtendedTypeInfo{T}"/> class. get;
/// </summary> }
public ExtendedTypeInfo()
: base(typeof(T)) /// <summary>
{ /// Gets a value indicating whether the type is a nullable value type.
// placeholder /// </summary>
} /// <value>
/// <c>true</c> if this instance is nullable value type; otherwise, <c>false</c>.
/// <summary> /// </value>
/// Converts this instance to its string representation, public Boolean IsNullableValueType {
/// trying to use the CultureInfo.InvariantCulture get;
/// IFormat provider if the overload is available. }
/// </summary>
/// <param name="instance">The instance.</param> /// <summary>
/// <returns>A <see cref="System.String" /> that represents the current object.</returns> /// Gets a value indicating whether the type or underlying type is numeric.
public string ToStringInvariant(T instance) => base.ToStringInvariant(instance); /// </summary>
} /// <value>
/// <c>true</c> if this instance is numeric; otherwise, <c>false</c>.
/// </value>
public Boolean IsNumeric {
get;
}
/// <summary>
/// Gets a value indicating whether the type is value type.
/// Nullable value types have this property set to False.
/// </summary>
public Boolean IsValueType {
get;
}
/// <summary>
/// When dealing with nullable value types, this property will
/// return the underlying value type of the nullable,
/// Otherwise it will return the same type as the Type property.
/// </summary>
/// <value>
/// The type of the underlying.
/// </value>
public Type UnderlyingType {
get;
}
/// <summary>
/// Gets the try parse method information. If the type does not contain
/// a suitable TryParse static method, it will return null.
/// </summary>
/// <value>
/// The try parse method information.
/// </value>
public MethodInfo TryParseMethodInfo {
get;
}
/// <summary>
/// Gets the ToString method info
/// It will prefer the overload containing the IFormatProvider argument.
/// </summary>
/// <value>
/// To string method information.
/// </value>
public MethodInfo ToStringMethodInfo {
get;
}
/// <summary>
/// Gets a value indicating whether the type contains a suitable TryParse method.
/// </summary>
/// <value>
/// <c>true</c> if this instance can parse natively; otherwise, <c>false</c>.
/// </value>
public Boolean CanParseNatively => this.TryParseMethodInfo != null;
#endregion
#region Methods
/// <summary>
/// Gets the default value of this type. For reference types it return null.
/// For value types it returns the default value.
/// </summary>
/// <returns>Default value of this type.</returns>
public Object GetDefault() => this.Type.GetTypeInfo().IsValueType ? Activator.CreateInstance(this.Type) : null;
/// <summary>
/// Tries to parse the string into an object of the type this instance represents.
/// Returns false when no suitable TryParse methods exists for the type or when parsing fails
/// for any reason. When possible, this method uses CultureInfo.InvariantCulture and NumberStyles.Any.
/// </summary>
/// <param name="s">The s.</param>
/// <param name="result">The result.</param>
/// <returns><c>true</c> if parse was converted successfully; otherwise, <c>false</c>.</returns>
public Boolean TryParse(String s, out Object result) {
result = this.GetDefault();
try {
if(this.Type == typeof(String)) {
result = Convert.ChangeType(s, this.Type);
return true;
}
if(this.IsNullableValueType && String.IsNullOrEmpty(s)) {
result = this.GetDefault();
return true;
}
if(this.CanParseNatively == false) {
result = this.GetDefault();
return false;
}
// Build the arguments of the TryParse method
List<Object> dynamicArguments = new List<Object> { s };
for(Int32 pi = 1; pi < this._tryParseParameters.Length - 1; pi++) {
ParameterInfo argInfo = this._tryParseParameters[pi];
if(argInfo.ParameterType == typeof(IFormatProvider)) {
dynamicArguments.Add(CultureInfo.InvariantCulture);
} else if(argInfo.ParameterType == typeof(NumberStyles)) {
dynamicArguments.Add(NumberStyles.Any);
} else {
dynamicArguments.Add(null);
}
}
dynamicArguments.Add(null);
Object[] parseArguments = dynamicArguments.ToArray();
if((Boolean)this.TryParseMethodInfo.Invoke(null, parseArguments) == false) {
result = this.GetDefault();
return false;
}
result = parseArguments[parseArguments.Length - 1];
return true;
} catch {
return false;
}
}
/// <summary>
/// Converts this instance to its string representation,
/// trying to use the CultureInfo.InvariantCulture
/// IFormat provider if the overload is available.
/// </summary>
/// <param name="instance">The instance.</param>
/// <returns>A <see cref="System.String" /> that represents the current object.</returns>
public String ToStringInvariant(Object instance) => instance == null
? String.Empty
: this._toStringArgumentLength != 1
? instance.ToString()
: this.ToStringMethodInfo.Invoke(instance, new Object[] { CultureInfo.InvariantCulture }) as String;
#endregion
}
/// <summary>
/// Provides extended information about a type.
///
/// This class is mainly used to define sets of types within the Constants class
/// and it is not meant for other than querying the BasicTypesInfo dictionary.
/// </summary>
/// <typeparam name="T">The type of extended type information.</typeparam>
public class ExtendedTypeInfo<T> : ExtendedTypeInfo {
/// <summary>
/// Initializes a new instance of the <see cref="ExtendedTypeInfo{T}"/> class.
/// </summary>
public ExtendedTypeInfo()
: base(typeof(T)) {
// placeholder
}
/// <summary>
/// Converts this instance to its string representation,
/// trying to use the CultureInfo.InvariantCulture
/// IFormat provider if the overload is available.
/// </summary>
/// <param name="instance">The instance.</param>
/// <returns>A <see cref="System.String" /> that represents the current object.</returns>
public String ToStringInvariant(T instance) => base.ToStringInvariant(instance);
}
} }
+114 -113
View File
@@ -1,118 +1,119 @@
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>
/// Represents a Method Info Cache.
/// </summary>
public class MethodInfoCache : ConcurrentDictionary<String, MethodInfo> {
/// <summary> /// <summary>
/// Represents a Method Info Cache. /// Retrieves the properties stored for the specified type.
/// If the properties are not available, it calls the factory method to retrieve them
/// and returns them as an array of PropertyInfo.
/// </summary> /// </summary>
public class MethodInfoCache : ConcurrentDictionary<string, MethodInfo> /// <typeparam name="T">The type of type.</typeparam>
{ /// <param name="name">The name.</param>
/// <summary> /// <param name="alias">The alias.</param>
/// Retrieves the properties stored for the specified type. /// <param name="types">The types.</param>
/// If the properties are not available, it calls the factory method to retrieve them /// <returns>
/// and returns them as an array of PropertyInfo. /// The cached MethodInfo.
/// </summary> /// </returns>
/// <typeparam name="T">The type of type.</typeparam> /// <exception cref="ArgumentNullException">name
/// <param name="name">The name.</param> /// or
/// <param name="alias">The alias.</param> /// factory.</exception>
/// <param name="types">The types.</param> /// <exception cref="System.ArgumentNullException">type.</exception>
/// <returns> public MethodInfo Retrieve<T>(String name, String alias, params Type[] types)
/// The cached MethodInfo. => this.Retrieve(typeof(T), name, alias, types);
/// </returns>
/// <exception cref="ArgumentNullException">name /// <summary>
/// or /// Retrieves the specified name.
/// factory.</exception> /// </summary>
/// <exception cref="System.ArgumentNullException">type.</exception> /// <typeparam name="T">The type of type.</typeparam>
public MethodInfo Retrieve<T>(string name, string alias, params Type[] types) /// <param name="name">The name.</param>
=> Retrieve(typeof(T), name, alias, types); /// <param name="types">The types.</param>
/// <returns>
/// <summary> /// The cached MethodInfo.
/// Retrieves the specified name. /// </returns>
/// </summary> public MethodInfo Retrieve<T>(String name, params Type[] types)
/// <typeparam name="T">The type of type.</typeparam> => this.Retrieve(typeof(T), name, name, types);
/// <param name="name">The name.</param>
/// <param name="types">The types.</param> /// <summary>
/// <returns> /// Retrieves the specified type.
/// The cached MethodInfo. /// </summary>
/// </returns> /// <param name="type">The type.</param>
public MethodInfo Retrieve<T>(string name, params Type[] types) /// <param name="name">The name.</param>
=> Retrieve(typeof(T), name, name, types); /// <param name="types">The types.</param>
/// <returns>
/// <summary> /// An array of the properties stored for the specified type.
/// Retrieves the specified type. /// </returns>
/// </summary> public MethodInfo Retrieve(Type type, String name, params Type[] types)
/// <param name="type">The type.</param> => this.Retrieve(type, name, name, types);
/// <param name="name">The name.</param>
/// <param name="types">The types.</param> /// <summary>
/// <returns> /// Retrieves the specified type.
/// An array of the properties stored for the specified type. /// </summary>
/// </returns> /// <param name="type">The type.</param>
public MethodInfo Retrieve(Type type, string name, params Type[] types) /// <param name="name">The name.</param>
=> Retrieve(type, name, name, types); /// <param name="alias">The alias.</param>
/// <param name="types">The types.</param>
/// <summary> /// <returns>
/// Retrieves the specified type. /// The cached MethodInfo.
/// </summary> /// </returns>
/// <param name="type">The type.</param> public MethodInfo Retrieve(Type type, String name, String alias, params Type[] types) {
/// <param name="name">The name.</param> if(type == null) {
/// <param name="alias">The alias.</param> throw new ArgumentNullException(nameof(type));
/// <param name="types">The types.</param> }
/// <returns>
/// The cached MethodInfo. if(alias == null) {
/// </returns> throw new ArgumentNullException(nameof(alias));
public MethodInfo Retrieve(Type type, string name, string alias, params Type[] types) }
{
if (type == null) if(name == null) {
throw new ArgumentNullException(nameof(type)); throw new ArgumentNullException(nameof(name));
}
if (alias == null)
throw new ArgumentNullException(nameof(alias)); return this.GetOrAdd(
if (name == null)
throw new ArgumentNullException(nameof(name));
return GetOrAdd(
alias, alias,
x => type.GetMethod(name, types ?? new Type[0])); x => type.GetMethod(name, types ?? new Type[0]));
} }
/// <summary> /// <summary>
/// Retrieves the specified name. /// Retrieves the specified name.
/// </summary> /// </summary>
/// <typeparam name="T">The type of type.</typeparam> /// <typeparam name="T">The type of type.</typeparam>
/// <param name="name">The name.</param> /// <param name="name">The name.</param>
/// <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.
/// </summary> /// </summary>
/// <param name="type">The type.</param> /// <param name="type">The type.</param>
/// <param name="name">The name.</param> /// <param name="name">The name.</param>
/// <returns> /// <returns>
/// The cached MethodInfo. /// The cached MethodInfo.
/// </returns> /// </returns>
/// <exception cref="ArgumentNullException"> /// <exception cref="ArgumentNullException">
/// type /// type
/// 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);
} }
} }
} }

Some files were not shown because too many files have changed in this diff Show More