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
+39 -33
View File
@@ -1,23 +1,20 @@
namespace Unosquare.RaspberryIO.Camera
{
using Swan;
using System;
using System.Linq;
using Unosquare.Swan;
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
{
public class CameraColor {
/// <summary>
/// Initializes a new instance of the <see cref="CameraColor"/> class.
/// </summary>
/// <param name="r">The red.</param>
/// <param name="g">The green.</param>
/// <param name="b">The blue.</param>
public CameraColor(int r, int g, int b)
: this(r, g, b, string.Empty)
{
public CameraColor(Int32 r, Int32 g, Int32 b)
: this(r, g, b, String.Empty) {
}
/// <summary>
@@ -27,16 +24,15 @@
/// <param name="g">The green.</param>
/// <param name="b">The blue.</param>
/// <param name="name">The well-known color name.</param>
public CameraColor(int r, int g, int b, string name)
{
RGB = new[] { Convert.ToByte(r.Clamp(0, 255)), Convert.ToByte(g.Clamp(0, 255)), Convert.ToByte(b.Clamp(0, 255)) };
public CameraColor(Int32 r, Int32 g, Int32 b, String name) {
this.RGB = new[] { Convert.ToByte(r.Clamp(0, 255)), Convert.ToByte(g.Clamp(0, 255)), Convert.ToByte(b.Clamp(0, 255)) };
var y = (R * .299000f) + (G * .587000f) + (B * .114000f);
var u = (R * -.168736f) + (G * -.331264f) + (B * .500000f) + 128f;
var v = (R * .500000f) + (G * -.418688f) + (B * -.081312f) + 128f;
Single y = this.R * .299000f + this.G * .587000f + this.B * .114000f;
Single u = this.R * -.168736f + this.G * -.331264f + this.B * .500000f + 128f;
Single v = this.R * .500000f + this.G * -.418688f + this.B * -.081312f + 128f;
YUV = new byte[] { (byte)y.Clamp(0, 255), (byte)u.Clamp(0, 255), (byte)v.Clamp(0, 255) };
Name = name;
this.YUV = new Byte[] { (Byte)y.Clamp(0, 255), (Byte)u.Clamp(0, 255), (Byte)v.Clamp(0, 255) };
this.Name = name;
}
#region Static Definitions
@@ -71,32 +67,38 @@
/// <summary>
/// Gets the well-known color name.
/// </summary>
public string Name { get; }
public String Name {
get;
}
/// <summary>
/// Gets the red byte.
/// </summary>
public byte R => RGB[0];
public Byte R => this.RGB[0];
/// <summary>
/// Gets the green byte.
/// </summary>
public byte G => RGB[1];
public Byte G => this.RGB[1];
/// <summary>
/// Gets the blue byte.
/// </summary>
public byte B => RGB[2];
public Byte B => this.RGB[2];
/// <summary>
/// Gets the RGB byte array (3 bytes).
/// </summary>
public byte[] RGB { get; }
public Byte[] RGB {
get;
}
/// <summary>
/// Gets the YUV byte array (3 bytes).
/// </summary>
public byte[] YUV { get; }
public Byte[] YUV {
get;
}
/// <summary>
/// Returns a hexadecimal representation of the RGB byte array.
@@ -104,10 +106,12 @@
/// </summary>
/// <param name="reverse">if set to <c>true</c> [reverse].</param>
/// <returns>A string</returns>
public string ToRgbHex(bool reverse)
{
var data = RGB.ToArray();
if (reverse) Array.Reverse(data);
public String ToRgbHex(Boolean reverse) {
Byte[] data = this.RGB.ToArray();
if(reverse) {
Array.Reverse(data);
}
return ToHex(data);
}
@@ -117,10 +121,12 @@
/// </summary>
/// <param name="reverse">if set to <c>true</c> [reverse].</param>
/// <returns>A string</returns>
public string ToYuvHex(bool reverse)
{
var data = YUV.ToArray();
if (reverse) Array.Reverse(data);
public String ToYuvHex(Boolean reverse) {
Byte[] data = this.YUV.ToArray();
if(reverse) {
Array.Reverse(data);
}
return ToHex(data);
}
@@ -129,6 +135,6 @@
/// </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()}";
private static String ToHex(Byte[] data) => $"0x{BitConverter.ToString(data).Replace("-", String.Empty).ToLowerInvariant()}";
}
}
@@ -1,22 +1,20 @@
namespace Unosquare.RaspberryIO.Camera
{
using Swan.Abstractions;
using System;
using Swan.Components;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using Unosquare.Swan.Abstractions;
using System;
using Unosquare.Swan.Components;
using System.IO;
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>
{
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 readonly Object SyncRoot = new Object();
private static CancellationTokenSource _videoTokenSource = new CancellationTokenSource();
private static Task<Task> _videoStreamTask;
@@ -30,7 +28,7 @@
/// <value>
/// <c>true</c> if this instance is busy; otherwise, <c>false</c>.
/// </value>
public bool IsBusy => OperationDone.IsSet == false;
public Boolean IsBusy => OperationDone.IsSet == false;
#endregion
@@ -43,34 +41,30 @@
/// <param name="ct">The ct.</param>
/// <returns>The image bytes</returns>
/// <exception cref="InvalidOperationException">Cannot use camera module because it is currently busy.</exception>
public async Task<byte[]> CaptureImageAsync(CameraStillSettings settings, CancellationToken ct = default)
{
if (Instance.IsBusy)
[System.Diagnostics.CodeAnalysis.SuppressMessage("Codequalität", "IDE0067:Objekte verwerfen, bevor Bereich verloren geht", Justification = "<Ausstehend>")]
public async Task<Byte[]> CaptureImageAsync(CameraStillSettings settings, CancellationToken ct = default) {
if(Instance.IsBusy) {
throw new InvalidOperationException("Cannot use camera module because it is currently busy.");
}
if (settings.CaptureTimeoutMilliseconds <= 0)
if(settings.CaptureTimeoutMilliseconds <= 0) {
throw new ArgumentException($"{nameof(settings.CaptureTimeoutMilliseconds)} needs to be greater than 0");
}
try
{
try {
OperationDone.Reset();
var output = new MemoryStream();
var exitCode = await ProcessRunner.RunProcessAsync(
MemoryStream output = new MemoryStream();
Int32 exitCode = await ProcessRunner.RunProcessAsync(
settings.CommandName,
settings.CreateProcessArguments(),
(data, proc) =>
{
output.Write(data, 0, data.Length);
},
(data, proc) => output.Write(data, 0, data.Length),
null,
true,
ct);
return exitCode != 0 ? new byte[] { } : output.ToArray();
}
finally
{
return exitCode != 0 ? new Byte[] { } : output.ToArray();
} finally {
OperationDone.Set();
}
}
@@ -80,10 +74,7 @@
/// </summary>
/// <param name="settings">The settings.</param>
/// <returns>The image bytes</returns>
public byte[] CaptureImage(CameraStillSettings settings)
{
return CaptureImageAsync(settings).GetAwaiter().GetResult();
}
public Byte[] CaptureImage(CameraStillSettings settings) => this.CaptureImageAsync(settings).GetAwaiter().GetResult();
/// <summary>
/// Captures a JPEG encoded image asynchronously at 90% quality.
@@ -92,17 +83,15 @@
/// <param name="height">The height.</param>
/// <param name="ct">The ct.</param>
/// <returns>The image bytes</returns>
public Task<byte[]> CaptureImageJpegAsync(int width, int height, CancellationToken ct = default)
{
var settings = new CameraStillSettings
{
public Task<Byte[]> CaptureImageJpegAsync(Int32 width, Int32 height, CancellationToken ct = default) {
CameraStillSettings settings = new CameraStillSettings {
CaptureWidth = width,
CaptureHeight = height,
CaptureJpegQuality = 90,
CaptureTimeoutMilliseconds = 300,
};
return CaptureImageAsync(settings, ct);
return this.CaptureImageAsync(settings, ct);
}
/// <summary>
@@ -111,7 +100,7 @@
/// <param name="width">The width.</param>
/// <param name="height">The height.</param>
/// <returns>The image bytes</returns>
public byte[] CaptureImageJpeg(int width, int height) => CaptureImageJpegAsync(width, height).GetAwaiter().GetResult();
public Byte[] CaptureImageJpeg(Int32 width, Int32 height) => this.CaptureImageJpegAsync(width, height).GetAwaiter().GetResult();
#endregion
@@ -123,17 +112,15 @@
/// </summary>
/// <param name="onDataCallback">The on data callback.</param>
/// <param name="onExitCallback">The on exit callback.</param>
public void OpenVideoStream(Action<byte[]> onDataCallback, Action onExitCallback = null)
{
var settings = new CameraVideoSettings
{
public void OpenVideoStream(Action<Byte[]> onDataCallback, Action onExitCallback = null) {
CameraVideoSettings settings = new CameraVideoSettings {
CaptureTimeoutMilliseconds = 0,
CaptureDisplayPreview = false,
CaptureWidth = 1920,
CaptureHeight = 1080
};
OpenVideoStream(settings, onDataCallback, onExitCallback);
this.OpenVideoStream(settings, onDataCallback, onExitCallback);
}
/// <summary>
@@ -144,21 +131,19 @@
/// <param name="onExitCallback">The on exit callback.</param>
/// <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 (Instance.IsBusy)
public void OpenVideoStream(CameraVideoSettings settings, Action<Byte[]> onDataCallback, Action onExitCallback) {
if(Instance.IsBusy) {
throw new InvalidOperationException("Cannot use camera module because it is currently busy.");
}
if (settings.CaptureTimeoutMilliseconds < 0)
if(settings.CaptureTimeoutMilliseconds < 0) {
throw new ArgumentException($"{nameof(settings.CaptureTimeoutMilliseconds)} needs to be greater than or equal to 0");
}
try
{
try {
OperationDone.Reset();
_videoStreamTask = Task.Factory.StartNew(() => VideoWorkerDoWork(settings, onDataCallback, onExitCallback), _videoTokenSource.Token);
}
catch
{
} catch {
OperationDone.Set();
throw;
}
@@ -167,16 +152,14 @@
/// <summary>
/// Closes the video stream of a video stream is open.
/// </summary>
public void CloseVideoStream()
{
lock (SyncRoot)
{
if (IsBusy == false)
public void CloseVideoStream() {
lock(SyncRoot) {
if(this.IsBusy == false) {
return;
}
}
if (_videoTokenSource.IsCancellationRequested == false)
{
if(_videoTokenSource.IsCancellationRequested == false) {
_videoTokenSource.Cancel();
_videoStreamTask.Wait();
}
@@ -184,13 +167,8 @@
_videoTokenSource = new CancellationTokenSource();
}
private static async Task VideoWorkerDoWork(
CameraVideoSettings settings,
Action<byte[]> onDataCallback,
Action onExitCallback)
{
try
{
private static async Task VideoWorkerDoWork(CameraVideoSettings settings, Action<Byte[]> onDataCallback, Action onExitCallback) {
try {
await ProcessRunner.RunProcessAsync(
settings.CommandName,
settings.CreateProcessArguments(),
@@ -200,13 +178,9 @@
_videoTokenSource.Token);
onExitCallback?.Invoke();
}
catch
{
} catch {
// swallow
}
finally
{
} finally {
Instance.CloseVideoStream();
OperationDone.Set();
}
+29 -25
View File
@@ -1,13 +1,12 @@
namespace Unosquare.RaspberryIO.Camera
{
using Swan;
using System.Globalization;
using Unosquare.Swan;
using System;
using System.Globalization;
namespace Unosquare.RaspberryIO.Camera {
/// <summary>
/// Defines the Raspberry Pi camera's sensor ROI (Region of Interest)
/// </summary>
public struct CameraRect
{
public struct CameraRect {
/// <summary>
/// The default ROI which is the entire area.
/// </summary>
@@ -19,7 +18,9 @@
/// <value>
/// The x.
/// </value>
public decimal X { get; set; }
public Decimal X {
get; set;
}
/// <summary>
/// Gets or sets the y location in relative coordinates. (0.0 to 1.0)
@@ -27,7 +28,9 @@
/// <value>
/// The y.
/// </value>
public decimal Y { get; set; }
public Decimal Y {
get; set;
}
/// <summary>
/// Gets or sets the width in relative coordinates. (0.0 to 1.0)
@@ -35,7 +38,9 @@
/// <value>
/// The w.
/// </value>
public decimal W { get; set; }
public Decimal W {
get; set;
}
/// <summary>
/// Gets or sets the height in relative coordinates. (0.0 to 1.0)
@@ -43,7 +48,9 @@
/// <value>
/// The h.
/// </value>
public decimal H { get; set; }
public Decimal H {
get; set;
}
/// <summary>
/// Gets a value indicating whether this instance is equal to the default (The entire area).
@@ -51,32 +58,29 @@
/// <value>
/// <c>true</c> if this instance is default; otherwise, <c>false</c>.
/// </value>
public bool IsDefault
{
get
{
Clamp();
return X == Default.X && Y == Default.Y && W == Default.W && H == Default.H;
public Boolean IsDefault {
get {
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>
public void Clamp()
{
X = X.Clamp(0M, 1M);
Y = Y.Clamp(0M, 1M);
W = W.Clamp(0M, 1M - X);
H = H.Clamp(0M, 1M - Y);
public void Clamp() {
this.X = this.X.Clamp(0M, 1M);
this.Y = this.Y.Clamp(0M, 1M);
this.W = this.W.Clamp(0M, 1M - this.X);
this.H = this.H.Clamp(0M, 1M - this.Y);
}
/// <summary>
/// Returns a <see cref="string" /> that represents this instance.
/// Returns a <see cref="String" /> that represents this instance.
/// </summary>
/// <returns>
/// A <see cref="string" /> that represents this instance.
/// A <see cref="String" /> that represents this instance.
/// </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)}";
}
}
+134 -113
View File
@@ -1,16 +1,15 @@
namespace Unosquare.RaspberryIO.Camera
{
using Swan;
using System.Globalization;
using System.Text;
using Unosquare.Swan;
using System.Globalization;
using System.Text;
using System;
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
{
public abstract class CameraSettingsBase {
/// <summary>
/// The Invariant Culture shorthand
/// </summary>
@@ -23,27 +22,27 @@
/// Default value is 5000
/// Recommended value is at least 300 in order to let the light collectors open
/// </summary>
public int CaptureTimeoutMilliseconds { get; set; } = 5000;
public Int32 CaptureTimeoutMilliseconds { get; set; } = 5000;
/// <summary>
/// Gets or sets a value indicating whether or not to show a preview window on the screen
/// </summary>
public bool CaptureDisplayPreview { get; set; } = false;
public Boolean CaptureDisplayPreview { get; set; } = false;
/// <summary>
/// Gets or sets a value indicating whether a preview window is shown in full screen mode if enabled
/// </summary>
public bool CaptureDisplayPreviewInFullScreen { get; set; } = true;
public Boolean CaptureDisplayPreviewInFullScreen { get; set; } = true;
/// <summary>
/// Gets or sets a value indicating whether video stabilization should be enabled.
/// </summary>
public bool CaptureVideoStabilizationEnabled { get; set; } = false;
public Boolean CaptureVideoStabilizationEnabled { get; set; } = false;
/// <summary>
/// Gets or sets the display preview opacity only if the display preview property is enabled.
/// </summary>
public byte CaptureDisplayPreviewOpacity { get; set; } = 255;
public Byte CaptureDisplayPreviewOpacity { get; set; } = 255;
/// <summary>
/// Gets or sets the capture sensor region of interest in relative coordinates.
@@ -54,7 +53,7 @@
/// Gets or sets the capture shutter speed in microseconds.
/// Default -1, Range 0 to 6000000 (equivalent to 6 seconds)
/// </summary>
public int CaptureShutterSpeedMicroseconds { get; set; } = -1;
public Int32 CaptureShutterSpeedMicroseconds { get; set; } = -1;
/// <summary>
/// Gets or sets the exposure mode.
@@ -68,7 +67,7 @@
/// Exposure can be adjusted by changing either the lens f-number or the exposure time;
/// which one is changed usually depends on the camera's exposure mode.
/// </summary>
public int CaptureExposureCompensation { get; set; } = 0;
public Int32 CaptureExposureCompensation { get; set; } = 0;
/// <summary>
/// Gets or sets the capture metering mode.
@@ -85,20 +84,22 @@
/// Only takes effect if White balance control is set to off.
/// Default is 0
/// </summary>
public decimal CaptureWhiteBalanceGainBlue { get; set; } = 0M;
public Decimal CaptureWhiteBalanceGainBlue { get; set; } = 0M;
/// <summary>
/// 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.
/// Default is 0
/// </summary>
public decimal CaptureWhiteBalanceGainRed { get; set; } = 0M;
public Decimal CaptureWhiteBalanceGainRed { get; set; } = 0M;
/// <summary>
/// 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.
/// </summary>
public CameraDynamicRangeCompensation CaptureDynamicRangeCompensation { get; set; } =
public CameraDynamicRangeCompensation CaptureDynamicRangeCompensation {
get; set;
} =
CameraDynamicRangeCompensation.Off;
#endregion
@@ -109,39 +110,39 @@
/// 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>
public int CaptureWidth { get; set; } = 640;
public Int32 CaptureWidth { get; set; } = 640;
/// <summary>
/// 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>
public int CaptureHeight { get; set; } = 480;
public Int32 CaptureHeight { get; set; } = 480;
/// <summary>
/// Gets or sets the picture sharpness. Default is 0, Range form -100 to 100
/// </summary>
public int ImageSharpness { get; set; } = 0;
public Int32 ImageSharpness { get; set; } = 0;
/// <summary>
/// Gets or sets the picture contrast. Default is 0, Range form -100 to 100
/// </summary>
public int ImageContrast { get; set; } = 0;
public Int32 ImageContrast { get; set; } = 0;
/// <summary>
/// Gets or sets the picture brightness. Default is 50, Range form 0 to 100
/// </summary>
public int ImageBrightness { get; set; } = 50; // from 0 to 100
public Int32 ImageBrightness { get; set; } = 50; // from 0 to 100
/// <summary>
/// Gets or sets the picture saturation. Default is 0, Range form -100 to 100
/// </summary>
public int ImageSaturation { get; set; } = 0;
public Int32 ImageSaturation { get; set; } = 0;
/// <summary>
/// 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>
public int ImageIso { get; set; } = -1;
public Int32 ImageIso { get; set; } = -1;
/// <summary>
/// Gets or sets the image capture effect to be applied.
@@ -153,14 +154,14 @@
/// Default is -1, Range is 0 to 255
/// 128:128 should be effectively a monochrome image.
/// </summary>
public int ImageColorEffectU { get; set; } = -1; // 0 to 255
public Int32 ImageColorEffectU { get; set; } = -1; // 0 to 255
/// <summary>
/// Gets or sets the color effect V coordinates.
/// Default is -1, Range is 0 to 255
/// 128:128 should be effectively a monochrome image.
/// </summary>
public int ImageColorEffectV { get; set; } = -1; // 0 to 255
public Int32 ImageColorEffectV { get; set; } = -1; // 0 to 255
/// <summary>
/// Gets or sets the image rotation. Default is no rotation
@@ -170,12 +171,16 @@
/// <summary>
/// Gets or sets a value indicating whether the image should be flipped horizontally.
/// </summary>
public bool ImageFlipHorizontally { get; set; }
public Boolean ImageFlipHorizontally {
get; set;
}
/// <summary>
/// Gets or sets a value indicating whether the image should be flipped vertically.
/// </summary>
public bool ImageFlipVertically { get; set; }
public Boolean ImageFlipVertically {
get; set;
}
/// <summary>
/// Gets or sets the image annotations using a bitmask (or flags) notation.
@@ -188,13 +193,13 @@
/// Text may include date/time placeholders by using the '%' character, as used by strftime.
/// Example: ABC %Y-%m-%d %X will output ABC 2015-10-28 20:09:33
/// </summary>
public string ImageAnnotationsText { get; set; } = string.Empty;
public String ImageAnnotationsText { get; set; } = String.Empty;
/// <summary>
/// Gets or sets the font size of the text annotations
/// Default is -1, range is 6 to 160
/// </summary>
public int ImageAnnotationFontSize { get; set; } = -1;
public Int32 ImageAnnotationFontSize { get; set; } = -1;
/// <summary>
/// Gets or sets the color of the text annotations.
@@ -219,118 +224,134 @@
/// <summary>
/// Gets the command file executable.
/// </summary>
public abstract string CommandName { get; }
public abstract String CommandName {
get;
}
/// <summary>
/// Creates the process arguments.
/// </summary>
/// <returns>The string that represents the process arguments</returns>
public virtual string CreateProcessArguments()
{
var sb = new StringBuilder();
sb.Append("-o -"); // output to standard output as opposed to a file.
sb.Append($" -t {(CaptureTimeoutMilliseconds < 0 ? "0" : CaptureTimeoutMilliseconds.ToString(Ci))}");
public virtual String CreateProcessArguments() {
StringBuilder sb = new StringBuilder();
_ = sb.Append("-o -"); // output to standard output as opposed to a file.
_ = sb.Append($" -t {(this.CaptureTimeoutMilliseconds < 0 ? "0" : this.CaptureTimeoutMilliseconds.ToString(Ci))}");
// Basic Width and height
if (CaptureWidth > 0 && CaptureHeight > 0)
{
sb.Append($" -w {CaptureWidth.ToString(Ci)}");
sb.Append($" -h {CaptureHeight.ToString(Ci)}");
if(this.CaptureWidth > 0 && this.CaptureHeight > 0) {
_ = sb.Append($" -w {this.CaptureWidth.ToString(Ci)}");
_ = sb.Append($" -h {this.CaptureHeight.ToString(Ci)}");
}
// Display Preview
if (CaptureDisplayPreview)
{
if (CaptureDisplayPreviewInFullScreen)
sb.Append(" -f");
if (CaptureDisplayPreviewOpacity != byte.MaxValue)
sb.Append($" -op {CaptureDisplayPreviewOpacity.ToString(Ci)}");
if(this.CaptureDisplayPreview) {
if(this.CaptureDisplayPreviewInFullScreen) {
_ = sb.Append(" -f");
}
else
{
sb.Append(" -n"); // no preview
if(this.CaptureDisplayPreviewOpacity != Byte.MaxValue) {
_ = sb.Append($" -op {this.CaptureDisplayPreviewOpacity.ToString(Ci)}");
}
} else {
_ = sb.Append(" -n"); // no preview
}
// Picture Settings
if (ImageSharpness != 0)
sb.Append($" -sh {ImageSharpness.Clamp(-100, 100).ToString(Ci)}");
if (ImageContrast != 0)
sb.Append($" -co {ImageContrast.Clamp(-100, 100).ToString(Ci)}");
if (ImageBrightness != 50)
sb.Append($" -br {ImageBrightness.Clamp(0, 100).ToString(Ci)}");
if (ImageSaturation != 0)
sb.Append($" -sa {ImageSaturation.Clamp(-100, 100).ToString(Ci)}");
if (ImageIso >= 100)
sb.Append($" -ISO {ImageIso.Clamp(100, 800).ToString(Ci)}");
if (CaptureVideoStabilizationEnabled)
sb.Append(" -vs");
if (CaptureExposureCompensation != 0)
sb.Append($" -ev {CaptureExposureCompensation.Clamp(-10, 10).ToString(Ci)}");
if (CaptureExposure != CameraExposureMode.Auto)
sb.Append($" -ex {CaptureExposure.ToString().ToLowerInvariant()}");
if (CaptureWhiteBalanceControl != CameraWhiteBalanceMode.Auto)
sb.Append($" -awb {CaptureWhiteBalanceControl.ToString().ToLowerInvariant()}");
if (ImageEffect != CameraImageEffect.None)
sb.Append($" -ifx {ImageEffect.ToString().ToLowerInvariant()}");
if (ImageColorEffectU >= 0 && ImageColorEffectV >= 0)
{
sb.Append(
$" -cfx {ImageColorEffectU.Clamp(0, 255).ToString(Ci)}:{ImageColorEffectV.Clamp(0, 255).ToString(Ci)}");
if(this.ImageSharpness != 0) {
_ = sb.Append($" -sh {this.ImageSharpness.Clamp(-100, 100).ToString(Ci)}");
}
if (CaptureMeteringMode != CameraMeteringMode.Average)
sb.Append($" -mm {CaptureMeteringMode.ToString().ToLowerInvariant()}");
if(this.ImageContrast != 0) {
_ = sb.Append($" -co {this.ImageContrast.Clamp(-100, 100).ToString(Ci)}");
}
if (ImageRotation != CameraImageRotation.None)
sb.Append($" -rot {((int)ImageRotation).ToString(Ci)}");
if(this.ImageBrightness != 50) {
_ = sb.Append($" -br {this.ImageBrightness.Clamp(0, 100).ToString(Ci)}");
}
if (ImageFlipHorizontally)
sb.Append(" -hf");
if(this.ImageSaturation != 0) {
_ = sb.Append($" -sa {this.ImageSaturation.Clamp(-100, 100).ToString(Ci)}");
}
if (ImageFlipVertically)
sb.Append(" -vf");
if(this.ImageIso >= 100) {
_ = sb.Append($" -ISO {this.ImageIso.Clamp(100, 800).ToString(Ci)}");
}
if (CaptureSensorRoi.IsDefault == false)
sb.Append($" -roi {CaptureSensorRoi}");
if(this.CaptureVideoStabilizationEnabled) {
_ = sb.Append(" -vs");
}
if (CaptureShutterSpeedMicroseconds > 0)
sb.Append($" -ss {CaptureShutterSpeedMicroseconds.Clamp(0, 6000000).ToString(Ci)}");
if(this.CaptureExposureCompensation != 0) {
_ = sb.Append($" -ev {this.CaptureExposureCompensation.Clamp(-10, 10).ToString(Ci)}");
}
if (CaptureDynamicRangeCompensation != CameraDynamicRangeCompensation.Off)
sb.Append($" -drc {CaptureDynamicRangeCompensation.ToString().ToLowerInvariant()}");
if(this.CaptureExposure != CameraExposureMode.Auto) {
_ = sb.Append($" -ex {this.CaptureExposure.ToString().ToLowerInvariant()}");
}
if (CaptureWhiteBalanceControl == CameraWhiteBalanceMode.Off &&
(CaptureWhiteBalanceGainBlue != 0M || CaptureWhiteBalanceGainRed != 0M))
sb.Append($" -awbg {CaptureWhiteBalanceGainBlue.ToString(Ci)},{CaptureWhiteBalanceGainRed.ToString(Ci)}");
if(this.CaptureWhiteBalanceControl != CameraWhiteBalanceMode.Auto) {
_ = sb.Append($" -awb {this.CaptureWhiteBalanceControl.ToString().ToLowerInvariant()}");
}
if (ImageAnnotationFontSize > 0)
{
sb.Append($" -ae {ImageAnnotationFontSize.Clamp(6, 160).ToString(Ci)}");
sb.Append($",{(ImageAnnotationFontColor == null ? "0xff" : ImageAnnotationFontColor.ToYuvHex(true))}");
if(this.ImageEffect != CameraImageEffect.None) {
_ = sb.Append($" -ifx {this.ImageEffect.ToString().ToLowerInvariant()}");
}
if (ImageAnnotationBackground != null)
{
ImageAnnotations |= CameraAnnotation.SolidBackground;
sb.Append($",{ImageAnnotationBackground.ToYuvHex(true)}");
if(this.ImageColorEffectU >= 0 && this.ImageColorEffectV >= 0) {
_ = sb.Append(
$" -cfx {this.ImageColorEffectU.Clamp(0, 255).ToString(Ci)}:{this.ImageColorEffectV.Clamp(0, 255).ToString(Ci)}");
}
if(this.CaptureMeteringMode != CameraMeteringMode.Average) {
_ = sb.Append($" -mm {this.CaptureMeteringMode.ToString().ToLowerInvariant()}");
}
if(this.ImageRotation != CameraImageRotation.None) {
_ = sb.Append($" -rot {((Int32)this.ImageRotation).ToString(Ci)}");
}
if(this.ImageFlipHorizontally) {
_ = sb.Append(" -hf");
}
if(this.ImageFlipVertically) {
_ = sb.Append(" -vf");
}
if(this.CaptureSensorRoi.IsDefault == false) {
_ = sb.Append($" -roi {this.CaptureSensorRoi}");
}
if(this.CaptureShutterSpeedMicroseconds > 0) {
_ = sb.Append($" -ss {this.CaptureShutterSpeedMicroseconds.Clamp(0, 6000000).ToString(Ci)}");
}
if(this.CaptureDynamicRangeCompensation != CameraDynamicRangeCompensation.Off) {
_ = sb.Append($" -drc {this.CaptureDynamicRangeCompensation.ToString().ToLowerInvariant()}");
}
if(this.CaptureWhiteBalanceControl == CameraWhiteBalanceMode.Off &&
(this.CaptureWhiteBalanceGainBlue != 0M || this.CaptureWhiteBalanceGainRed != 0M)) {
_ = sb.Append($" -awbg {this.CaptureWhiteBalanceGainBlue.ToString(Ci)},{this.CaptureWhiteBalanceGainRed.ToString(Ci)}");
}
if(this.ImageAnnotationFontSize > 0) {
_ = sb.Append($" -ae {this.ImageAnnotationFontSize.Clamp(6, 160).ToString(Ci)}");
_ = sb.Append($",{(this.ImageAnnotationFontColor == null ? "0xff" : this.ImageAnnotationFontColor.ToYuvHex(true))}");
if(this.ImageAnnotationBackground != null) {
this.ImageAnnotations |= CameraAnnotation.SolidBackground;
_ = sb.Append($",{this.ImageAnnotationBackground.ToYuvHex(true)}");
}
}
if (ImageAnnotations != CameraAnnotation.None)
sb.Append($" -a {((int)ImageAnnotations).ToString(Ci)}");
if(this.ImageAnnotations != CameraAnnotation.None) {
_ = sb.Append($" -a {((Int32)this.ImageAnnotations).ToString(Ci)}");
}
if (string.IsNullOrWhiteSpace(ImageAnnotationsText) == false)
sb.Append($" -a \"{ImageAnnotationsText.Replace("\"", "'")}\"");
if(String.IsNullOrWhiteSpace(this.ImageAnnotationsText) == false) {
_ = sb.Append($" -a \"{this.ImageAnnotationsText.Replace("\"", "'")}\"");
}
return sb.ToString();
}
@@ -1,26 +1,24 @@
namespace Unosquare.RaspberryIO.Camera
{
using Swan;
using System;
using System.Collections.Generic;
using System.Text;
using Unosquare.Swan;
using System;
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 int _rotate;
public class CameraStillSettings : CameraSettingsBase {
private Int32 _rotate;
/// <inheritdoc />
public override string CommandName => "raspistill";
public override String CommandName => "raspistill";
/// <summary>
/// Gets or sets a value indicating whether the preview window (if enabled) uses native capture resolution
/// This may slow down preview FPS
/// </summary>
public bool CaptureDisplayPreviewAtResolution { get; set; } = false;
public Boolean CaptureDisplayPreviewAtResolution { get; set; } = false;
/// <summary>
/// Gets or sets the encoding format the hardware will use for the output.
@@ -31,18 +29,18 @@
/// Gets or sets the quality for JPEG only encoding mode.
/// Value ranges from 0 to 100
/// </summary>
public int CaptureJpegQuality { get; set; } = 90;
public Int32 CaptureJpegQuality { get; set; } = 90;
/// <summary>
/// Gets or sets a value indicating whether the JPEG encoder should add raw bayer metadata.
/// </summary>
public bool CaptureJpegIncludeRawBayerMetadata { get; set; } = false;
public Boolean CaptureJpegIncludeRawBayerMetadata { get; set; } = false;
/// <summary>
/// JPEG EXIF data
/// Keys and values must be already properly escaped. Otherwise the command will fail.
/// </summary>
public Dictionary<string, string> CaptureJpegExtendedInfo { get; } = new Dictionary<string, string>();
public Dictionary<String, String> CaptureJpegExtendedInfo { get; } = new Dictionary<String, String>();
/// <summary>
/// Gets or sets a value indicating whether [horizontal flip].
@@ -50,7 +48,7 @@
/// <value>
/// <c>true</c> if [horizontal flip]; otherwise, <c>false</c>.
/// </value>
public bool HorizontalFlip { get; set; } = false;
public Boolean HorizontalFlip { get; set; } = false;
/// <summary>
/// Gets or sets a value indicating whether [vertical flip].
@@ -58,61 +56,64 @@
/// <value>
/// <c>true</c> if [vertical flip]; otherwise, <c>false</c>.
/// </value>
public bool VerticalFlip { get; set; } = false;
public Boolean VerticalFlip { get; set; } = false;
/// <summary>
/// Gets or sets the rotation.
/// </summary>
/// <exception cref="ArgumentOutOfRangeException">Valid range 0-359</exception>
public int Rotation
{
get => _rotate;
set
{
if (value < 0 || value > 359)
{
public Int32 Rotation {
get => this._rotate;
set {
if(value < 0 || value > 359) {
throw new ArgumentOutOfRangeException(nameof(value), "Valid range 0-359");
}
_rotate = value;
this._rotate = value;
}
}
/// <inheritdoc />
public override string CreateProcessArguments()
{
var sb = new StringBuilder(base.CreateProcessArguments());
sb.Append($" -e {CaptureEncoding.ToString().ToLowerInvariant()}");
public override String CreateProcessArguments() {
StringBuilder sb = new StringBuilder(base.CreateProcessArguments());
_ = sb.Append($" -e {this.CaptureEncoding.ToString().ToLowerInvariant()}");
// JPEG Encoder specific arguments
if (CaptureEncoding == CameraImageEncodingFormat.Jpg)
{
sb.Append($" -q {CaptureJpegQuality.Clamp(0, 100).ToString(Ci)}");
if(this.CaptureEncoding == CameraImageEncodingFormat.Jpg) {
_ = sb.Append($" -q {this.CaptureJpegQuality.Clamp(0, 100).ToString(Ci)}");
if (CaptureJpegIncludeRawBayerMetadata)
sb.Append(" -r");
if(this.CaptureJpegIncludeRawBayerMetadata) {
_ = sb.Append(" -r");
}
// JPEG EXIF data
if (CaptureJpegExtendedInfo.Count > 0)
{
foreach (var kvp in CaptureJpegExtendedInfo)
{
if (string.IsNullOrWhiteSpace(kvp.Key) || string.IsNullOrWhiteSpace(kvp.Value))
if(this.CaptureJpegExtendedInfo.Count > 0) {
foreach(KeyValuePair<String, String> kvp in this.CaptureJpegExtendedInfo) {
if(String.IsNullOrWhiteSpace(kvp.Key) || String.IsNullOrWhiteSpace(kvp.Value)) {
continue;
}
sb.Append($" -x \"{kvp.Key.Replace("\"", "'")}={kvp.Value.Replace("\"", "'")}\"");
_ = sb.Append($" -x \"{kvp.Key.Replace("\"", "'")}={kvp.Value.Replace("\"", "'")}\"");
}
}
}
// Display preview settings
if (CaptureDisplayPreview && CaptureDisplayPreviewAtResolution) sb.Append(" -fp");
if(this.CaptureDisplayPreview && this.CaptureDisplayPreviewAtResolution) {
_ = sb.Append(" -fp");
}
if (Rotation != 0) sb.Append($" -rot {Rotation}");
if(this.Rotation != 0) {
_ = sb.Append($" -rot {this.Rotation}");
}
if (HorizontalFlip) sb.Append(" -hf");
if(this.HorizontalFlip) {
_ = sb.Append(" -hf");
}
if (VerticalFlip) sb.Append(" -vf");
if(this.VerticalFlip) {
_ = sb.Append(" -vf");
}
return sb.ToString();
}
@@ -1,43 +1,42 @@
namespace Unosquare.RaspberryIO.Camera
{
using Swan;
using System.Text;
using Unosquare.Swan;
using System;
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
{
public class CameraVideoSettings : CameraSettingsBase {
/// <inheritdoc />
public override string CommandName => "raspivid";
public override String CommandName => "raspivid";
/// <summary>
/// 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>
public int CaptureBitrate { get; set; } = -1;
public Int32 CaptureBitrate { get; set; } = -1;
/// <summary>
/// Gets or sets the framerate.
/// Default 25, range 2 to 30
/// </summary>
public int CaptureFramerate { get; set; } = 25;
public Int32 CaptureFramerate { get; set; } = 25;
/// <summary>
/// Sets the intra refresh period (GoP) rate for the recorded video. H264 video uses a complete frame (I-frame) every intra
/// 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>
public int CaptureKeyframeRate { get; set; } = 25;
public Int32 CaptureKeyframeRate { get; set; } = 25;
/// <summary>
/// Sets the initial quantisation parameter for the stream. Varies from approximately 10 to 40, and will greatly affect
/// 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>
public int CaptureQuantisation { get; set; } = 23;
public Int32 CaptureQuantisation { get; set; } = 23;
/// <summary>
/// Gets or sets the profile.
@@ -53,7 +52,7 @@
/// <value>
/// <c>true</c> if [interleave headers]; otherwise, <c>false</c>.
/// </value>
public bool CaptureInterleaveHeaders { get; set; } = true;
public Boolean CaptureInterleaveHeaders { get; set; } = true;
/// <summary>
/// Switch on an option to display the preview after compression. This will show any compression artefacts in the preview window. In normal operation,
@@ -62,31 +61,36 @@
/// <value>
/// <c>true</c> if [capture display preview encoded]; otherwise, <c>false</c>.
/// </value>
public bool CaptureDisplayPreviewEncoded { get; set; } = false;
public Boolean CaptureDisplayPreviewEncoded { get; set; } = false;
/// <inheritdoc />
public override string CreateProcessArguments()
{
var sb = new StringBuilder(base.CreateProcessArguments());
public override String CreateProcessArguments() {
StringBuilder sb = new StringBuilder(base.CreateProcessArguments());
sb.Append($" -pf {CaptureProfile.ToString().ToLowerInvariant()}");
if (CaptureBitrate < 0)
sb.Append($" -b {CaptureBitrate.Clamp(0, 25000000).ToString(Ci)}");
_ = sb.Append($" -pf {this.CaptureProfile.ToString().ToLowerInvariant()}");
if(this.CaptureBitrate < 0) {
_ = sb.Append($" -b {this.CaptureBitrate.Clamp(0, 25000000).ToString(Ci)}");
}
if (CaptureFramerate >= 2)
sb.Append($" -fps {CaptureFramerate.Clamp(2, 30).ToString(Ci)}");
if(this.CaptureFramerate >= 2) {
_ = sb.Append($" -fps {this.CaptureFramerate.Clamp(2, 30).ToString(Ci)}");
}
if (CaptureDisplayPreview && CaptureDisplayPreviewEncoded)
sb.Append(" -e");
if(this.CaptureDisplayPreview && this.CaptureDisplayPreviewEncoded) {
_ = sb.Append(" -e");
}
if (CaptureKeyframeRate > 0)
sb.Append($" -g {CaptureKeyframeRate.ToString(Ci)}");
if(this.CaptureKeyframeRate > 0) {
_ = sb.Append($" -g {this.CaptureKeyframeRate.ToString(Ci)}");
}
if (CaptureQuantisation >= 0)
sb.Append($" -qp {CaptureQuantisation.Clamp(0, 40).ToString(Ci)}");
if(this.CaptureQuantisation >= 0) {
_ = sb.Append($" -qp {this.CaptureQuantisation.Clamp(0, 40).ToString(Ci)}");
}
if (CaptureInterleaveHeaders)
sb.Append(" -ih");
if(this.CaptureInterleaveHeaders) {
_ = sb.Append(" -ih");
}
return sb.ToString();
}
+11 -21
View File
@@ -1,12 +1,10 @@
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
{
public enum CameraImageEncodingFormat {
/// <summary>
/// The JPG
/// </summary>
@@ -31,8 +29,7 @@
/// <summary>
/// Defines the different exposure modes for the Raspberry Pi's camera module
/// </summary>
public enum CameraExposureMode
{
public enum CameraExposureMode {
/// <summary>
/// The automatic
/// </summary>
@@ -97,8 +94,7 @@
/// <summary>
/// Defines the different AWB (Auto White Balance) modes for the Raspberry Pi's camera module
/// </summary>
public enum CameraWhiteBalanceMode
{
public enum CameraWhiteBalanceMode {
/// <summary>
/// No white balance
/// </summary>
@@ -153,8 +149,7 @@
/// <summary>
/// Defines the available image effects for the Raspberry Pi's camera module
/// </summary>
public enum CameraImageEffect
{
public enum CameraImageEffect {
/// <summary>
/// No effect
/// </summary>
@@ -264,8 +259,7 @@
/// <summary>
/// Defines the different metering modes for the Raspberry Pi's camera module
/// </summary>
public enum CameraMeteringMode
{
public enum CameraMeteringMode {
/// <summary>
/// The average
/// </summary>
@@ -290,8 +284,7 @@
/// <summary>
/// Defines the different image rotation modes for the Raspberry Pi's camera module
/// </summary>
public enum CameraImageRotation
{
public enum CameraImageRotation {
/// <summary>
/// No rerotation
/// </summary>
@@ -317,8 +310,7 @@
/// Defines the different DRC (Dynamic Range Compensation) modes for the Raspberry Pi's camera module
/// Helpful for low light photos
/// </summary>
public enum CameraDynamicRangeCompensation
{
public enum CameraDynamicRangeCompensation {
/// <summary>
/// The off setting
/// </summary>
@@ -344,8 +336,7 @@
/// Defines the bit-wise mask flags for the available annotation elements for the Raspberry Pi's camera module
/// </summary>
[Flags]
public enum CameraAnnotation
{
public enum CameraAnnotation {
/// <summary>
/// The none
/// </summary>
@@ -400,8 +391,7 @@
/// <summary>
/// Defines the different H.264 encoding profiles to be used when capturing video.
/// </summary>
public enum CameraH264Profile
{
public enum CameraH264Profile {
/// <summary>
/// BP: Primarily for lower-cost applications with limited computing resources,
/// this profile is used widely in videoconferencing and mobile applications.
+21 -35
View File
@@ -1,24 +1,22 @@
namespace Unosquare.RaspberryIO.Computer
{
using Swan.Abstractions;
using System.Globalization;
using System.IO;
using Unosquare.Swan.Abstractions;
using System.Globalization;
using System.IO;
using System;
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";
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>
/// Prevents a default instance of the <see cref="DsiDisplay"/> class from being created.
/// </summary>
private DsiDisplay()
{
private DsiDisplay() {
// placeholder
}
@@ -28,7 +26,7 @@
/// <value>
/// <c>true</c> if this instance is present; otherwise, <c>false</c>.
/// </value>
public bool IsPresent => File.Exists(BrightnessFilename);
public Boolean IsPresent => File.Exists(BrightnessFilename);
/// <summary>
/// Gets or sets the brightness of the DSI display via filesystem.
@@ -36,17 +34,13 @@
/// <value>
/// The brightness.
/// </value>
public byte Brightness
{
get
{
if (IsPresent == false) return 0;
return byte.TryParse(File.ReadAllText(BrightnessFilename).Trim(), out var brightness) ? brightness : (byte)0;
public Byte Brightness {
get => this.IsPresent == false ? (Byte)0 : Byte.TryParse(File.ReadAllText(BrightnessFilename).Trim(), out Byte brightness) ? brightness : (Byte)0;
set {
if(this.IsPresent == false) {
return;
}
set
{
if (IsPresent == false) return;
File.WriteAllText(BrightnessFilename, value.ToString(CultureInfo.InvariantCulture));
}
}
@@ -58,20 +52,12 @@
/// <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;
public Boolean IsBacklightOn {
get => this.IsPresent == false ? false : Int32.TryParse(File.ReadAllText(BacklightFilename).Trim(), out Int32 backlight) ? backlight == 0 : false;
set {
if(this.IsPresent == false) {
return;
}
set
{
if (IsPresent == false) return;
File.WriteAllText(BacklightFilename, value ? "0" : "1");
}
@@ -1,40 +1,51 @@
namespace Unosquare.RaspberryIO.Computer
{
using System.Net;
using System;
using System.Net;
namespace Unosquare.RaspberryIO.Computer {
/// <summary>
/// Represents a Network Adapter
/// </summary>
public class NetworkAdapterInfo
{
public class NetworkAdapterInfo {
/// <summary>
/// Gets the name.
/// </summary>
public string Name { get; internal set; }
public String Name {
get; internal set;
}
/// <summary>
/// Gets the IP V4 address.
/// </summary>
public IPAddress IPv4 { get; internal set; }
public IPAddress IPv4 {
get; internal set;
}
/// <summary>
/// Gets the IP V6 address.
/// </summary>
public IPAddress IPv6 { get; internal set; }
public IPAddress IPv6 {
get; internal set;
}
/// <summary>
/// Gets the name of the access point.
/// </summary>
public string AccessPointName { get; internal set; }
public String AccessPointName {
get; internal set;
}
/// <summary>
/// Gets the MAC (Physical) address.
/// </summary>
public string MacAddress { get; internal set; }
public String MacAddress {
get; internal set;
}
/// <summary>
/// Gets a value indicating whether this instance is wireless.
/// </summary>
public bool IsWireless { get; internal set; }
public Boolean IsWireless {
get; internal set;
}
}
}
+91 -105
View File
@@ -1,95 +1,92 @@
namespace Unosquare.RaspberryIO.Computer
{
using Swan;
using Swan.Abstractions;
using Swan.Components;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using Unosquare.Swan;
using Unosquare.Swan.Abstractions;
using Unosquare.Swan.Components;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
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:";
public class NetworkSettings : SingletonBase<NetworkSettings> {
private const String EssidTag = "ESSID:";
/// <summary>
/// Gets the local machine Host Name.
/// </summary>
public string HostName => Network.HostName;
public String HostName => Network.HostName;
/// <summary>
/// Retrieves the wireless networks.
/// </summary>
/// <param name="adapter">The adapter.</param>
/// <returns>A list of WiFi networks</returns>
public List<WirelessNetworkInfo> RetrieveWirelessNetworks(string adapter) => RetrieveWirelessNetworks(new[] { adapter });
public List<WirelessNetworkInfo> RetrieveWirelessNetworks(String adapter) => this.RetrieveWirelessNetworks(new[] { adapter });
/// <summary>
/// Retrieves the wireless networks.
/// </summary>
/// <param name="adapters">The adapters.</param>
/// <returns>A list of WiFi networks</returns>
public List<WirelessNetworkInfo> RetrieveWirelessNetworks(string[] adapters = null)
{
var result = new List<WirelessNetworkInfo>();
public List<WirelessNetworkInfo> RetrieveWirelessNetworks(String[] adapters = null) {
List<WirelessNetworkInfo> result = new List<WirelessNetworkInfo>();
foreach (var networkAdapter in adapters ?? RetrieveAdapters().Where(x => x.IsWireless).Select(x => x.Name))
{
var wirelessOutput = ProcessRunner.GetProcessOutputAsync("iwlist", $"{networkAdapter} scanning").Result;
var outputLines =
foreach(String networkAdapter in adapters ?? this.RetrieveAdapters().Where(x => x.IsWireless).Select(x => x.Name)) {
String wirelessOutput = ProcessRunner.GetProcessOutputAsync("iwlist", $"{networkAdapter} scanning").Result;
String[] outputLines =
wirelessOutput.Split('\n')
.Select(x => x.Trim())
.Where(x => string.IsNullOrWhiteSpace(x) == false)
.Where(x => String.IsNullOrWhiteSpace(x) == false)
.ToArray();
for (var i = 0; i < outputLines.Length; i++)
{
var line = outputLines[i];
for(Int32 i = 0; i < outputLines.Length; i++) {
String line = outputLines[i];
if (line.StartsWith(EssidTag) == false) continue;
if(line.StartsWith(EssidTag) == false) {
continue;
}
var network = new WirelessNetworkInfo()
{
Name = line.Replace(EssidTag, string.Empty).Replace("\"", string.Empty)
WirelessNetworkInfo network = new WirelessNetworkInfo() {
Name = line.Replace(EssidTag, String.Empty).Replace("\"", String.Empty)
};
while (true)
{
if (i + 1 >= outputLines.Length) break;
while(true) {
if(i + 1 >= outputLines.Length) {
break;
}
// should look for two lines before the ESSID acording to the scan
line = outputLines[i - 2];
if (line.StartsWith("Quality="))
{
network.Quality = line.Replace("Quality=", string.Empty);
if(line.StartsWith("Quality=")) {
network.Quality = line.Replace("Quality=", String.Empty);
break;
}
}
while (true)
{
if (i + 1 >= outputLines.Length) break;
while(true) {
if(i + 1 >= outputLines.Length) {
break;
}
// should look for a line before the ESSID acording to the scan
line = outputLines[i - 1];
if (line.StartsWith("Encryption key:"))
{
network.IsEncrypted = line.Replace("Encryption key:", string.Empty).Trim() == "on";
if(line.StartsWith("Encryption key:")) {
network.IsEncrypted = line.Replace("Encryption key:", String.Empty).Trim() == "on";
break;
}
}
if (result.Any(x => x.Name == network.Name) == false)
if(result.Any(x => x.Name == network.Name) == false) {
result.Add(network);
}
}
}
return result.OrderBy(x => x.Name).ToList();
}
@@ -102,22 +99,18 @@
/// <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>
public bool SetupWirelessNetwork(string adapterName, string networkSsid, string password = null, string countryCode = "US")
{
public Boolean SetupWirelessNetwork(String adapterName, String networkSsid, String password = null, String countryCode = "US") {
// TODO: Get the country where the device is located to set 'country' param in payload var
var payload = $"country={countryCode}\nctrl_interface=DIR=/var/run/wpa_supplicant GROUP=netdev\nupdate_config=1\n";
payload += string.IsNullOrEmpty(password)
String payload = $"country={countryCode}\nctrl_interface=DIR=/var/run/wpa_supplicant GROUP=netdev\nupdate_config=1\n";
payload += String.IsNullOrEmpty(password)
? $"network={{\n\tssid=\"{networkSsid}\"\n\t}}\n"
: $"network={{\n\tssid=\"{networkSsid}\"\n\tpsk=\"{password}\"\n\t}}\n";
try
{
try {
File.WriteAllText("/etc/wpa_supplicant/wpa_supplicant.conf", payload);
ProcessRunner.GetProcessOutputAsync("pkill", "-f wpa_supplicant").Wait();
ProcessRunner.GetProcessOutputAsync("ifdown", adapterName).Wait();
ProcessRunner.GetProcessOutputAsync("ifup", adapterName).Wait();
}
catch (Exception ex)
{
} catch(Exception ex) {
ex.Log(nameof(NetworkSettings));
return false;
}
@@ -129,99 +122,92 @@
/// Retrieves the network adapters.
/// </summary>
/// <returns>A list of network adapters.</returns>
public List<NetworkAdapterInfo> RetrieveAdapters()
{
const string hWaddr = "HWaddr ";
const string ether = "ether ";
public List<NetworkAdapterInfo> RetrieveAdapters() {
const String hWaddr = "HWaddr ";
const String ether = "ether ";
var result = new List<NetworkAdapterInfo>();
var interfacesOutput = ProcessRunner.GetProcessOutputAsync("ifconfig").Result;
var wlanOutput = ProcessRunner.GetProcessOutputAsync("iwconfig")
List<NetworkAdapterInfo> result = new List<NetworkAdapterInfo>();
String interfacesOutput = ProcessRunner.GetProcessOutputAsync("ifconfig").Result;
String[] wlanOutput = ProcessRunner.GetProcessOutputAsync("iwconfig")
.Result.Split('\n')
.Where(x => x.Contains("no wireless extensions.") == false)
.ToArray();
var outputLines = interfacesOutput.Split('\n').Where(x => string.IsNullOrWhiteSpace(x) == false).ToArray();
String[] outputLines = interfacesOutput.Split('\n').Where(x => String.IsNullOrWhiteSpace(x) == false).ToArray();
for (var i = 0; i < outputLines.Length; i++)
{
for(Int32 i = 0; i < outputLines.Length; i++) {
// grab the current line
var line = outputLines[i];
String line = outputLines[i];
// skip if the line is indented
if (char.IsLetterOrDigit(line[0]) == false)
if(Char.IsLetterOrDigit(line[0]) == false) {
continue;
}
// Read the line as an adatper
var adapter = new NetworkAdapterInfo
{
NetworkAdapterInfo adapter = new NetworkAdapterInfo {
Name = line.Substring(0, line.IndexOf(' ')).TrimEnd(':')
};
// Parse the MAC address in old version of ifconfig; it comes in the first line
if (line.IndexOf(hWaddr) >= 0)
{
var startIndexHwd = line.IndexOf(hWaddr) + hWaddr.Length;
if(line.IndexOf(hWaddr) >= 0) {
Int32 startIndexHwd = line.IndexOf(hWaddr) + hWaddr.Length;
adapter.MacAddress = line.Substring(startIndexHwd, 17).Trim();
}
// Parse the info in lines other than the first
for (var j = i + 1; j < outputLines.Length; j++)
{
for(Int32 j = i + 1; j < outputLines.Length; j++) {
// Get the contents of the indented line
var indentedLine = outputLines[j];
String indentedLine = outputLines[j];
// We have hit the next adapter info
if (char.IsLetterOrDigit(indentedLine[0]))
{
if(Char.IsLetterOrDigit(indentedLine[0])) {
i = j - 1;
break;
}
// Parse the MAC address in new versions of ifconfig; it no longer comes in the first line
if (indentedLine.IndexOf(ether) >= 0 && string.IsNullOrWhiteSpace(adapter.MacAddress))
{
var startIndexHwd = indentedLine.IndexOf(ether) + ether.Length;
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 IPv4 Address
{
var addressText = ParseOutputTagFromLine(indentedLine, "inet addr:") ?? ParseOutputTagFromLine(indentedLine, "inet ");
String addressText = ParseOutputTagFromLine(indentedLine, "inet addr:") ?? ParseOutputTagFromLine(indentedLine, "inet ");
if (addressText != null)
{
if (IPAddress.TryParse(addressText, out var outValue))
if(addressText != null) {
if(IPAddress.TryParse(addressText, out IPAddress outValue)) {
adapter.IPv4 = outValue;
}
}
}
// Parse the IPv6 Address
{
var addressText = ParseOutputTagFromLine(indentedLine, "inet6 addr:") ?? ParseOutputTagFromLine(indentedLine, "inet6 ");
String addressText = ParseOutputTagFromLine(indentedLine, "inet6 addr:") ?? ParseOutputTagFromLine(indentedLine, "inet6 ");
if (addressText != null)
{
if (IPAddress.TryParse(addressText, out var outValue))
if(addressText != null) {
if(IPAddress.TryParse(addressText, out IPAddress outValue)) {
adapter.IPv6 = outValue;
}
}
}
// we have hit the end of the output in an indented line
if (j >= outputLines.Length - 1)
if(j >= outputLines.Length - 1) {
i = outputLines.Length;
}
}
// Retrieve the wireless LAN info
var wlanInfo = wlanOutput.FirstOrDefault(x => x.StartsWith(adapter.Name));
String wlanInfo = wlanOutput.FirstOrDefault(x => x.StartsWith(adapter.Name));
if (wlanInfo != null)
{
if(wlanInfo != null) {
adapter.IsWireless = true;
var essidParts = wlanInfo.Split(new[] { EssidTag }, StringSplitOptions.RemoveEmptyEntries);
if (essidParts.Length >= 2)
{
adapter.AccessPointName = essidParts[1].Replace("\"", string.Empty).Trim();
String[] essidParts = wlanInfo.Split(new[] { EssidTag }, StringSplitOptions.RemoveEmptyEntries);
if(essidParts.Length >= 2) {
adapter.AccessPointName = essidParts[1].Replace("\"", String.Empty).Trim();
}
}
@@ -236,7 +222,7 @@
/// Retrieves current wireless connected network name.
/// </summary>
/// <returns>The connected network name.</returns>
public string GetWirelessNetworkName() => ProcessRunner.GetProcessOutputAsync("iwgetid", "-r").Result;
public String GetWirelessNetworkName() => ProcessRunner.GetProcessOutputAsync("iwgetid", "-r").Result;
/// <summary>
/// Parses the output tag from the given line.
@@ -244,20 +230,20 @@
/// <param name="indentedLine">The indented line.</param>
/// <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)
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))
Int32 startIndex = indentedLine.IndexOf(tagName) + tagName.Length;
StringBuilder builder = new StringBuilder(1024);
for(Int32 c = startIndex; c < indentedLine.Length; c++) {
Char currentChar = indentedLine[c];
if(!Char.IsPunctuation(currentChar) && !Char.IsLetterOrDigit(currentChar)) {
break;
}
builder.Append(currentChar);
_ = builder.Append(currentChar);
}
return builder.ToString();
+25 -13
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
{
public class OsInfo {
/// <summary>
/// System name
/// </summary>
public string SysName { get; set; }
public String SysName {
get; set;
}
/// <summary>
/// Node name
/// </summary>
public string NodeName { get; set; }
public String NodeName {
get; set;
}
/// <summary>
/// Release level
/// </summary>
public string Release { get; set; }
public String Release {
get; set;
}
/// <summary>
/// Version level
/// </summary>
public string Version { get; set; }
public String Version {
get; set;
}
/// <summary>
/// Hardware level
/// </summary>
public string Machine { get; set; }
public String Machine {
get; set;
}
/// <summary>
/// Domain name
/// </summary>
public string DomainName { get; set; }
public String DomainName {
get; set;
}
/// <summary>
/// Returns a <see cref="string" /> that represents this instance.
/// Returns a <see cref="String" /> that represents this instance.
/// </summary>
/// <returns>
/// A <see cref="string" /> that represents this instance.
/// A <see cref="String" /> that represents this instance.
/// </returns>
public override string ToString() => $"{SysName} {Release} {Version}";
public override String ToString() => $"{this.SysName} {this.Release} {this.Version}";
}
}
+2 -4
View File
@@ -1,11 +1,9 @@
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
{
public enum PiVersion {
/// <summary>
/// The unknown version
/// </summary>
+147 -148
View File
@@ -1,76 +1,70 @@
namespace Unosquare.RaspberryIO.Computer
{
using Native;
using Swan.Abstractions;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using Unosquare.RaspberryIO.Native;
using Unosquare.Swan.Abstractions;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
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";
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;
private static readonly object SyncRoot = new object();
[System.Diagnostics.CodeAnalysis.SuppressMessage("Codequalität", "IDE0052:Ungelesene private Member entfernen", Justification = "<Ausstehend>")]
private static readonly Object SyncRoot = new Object();
/// <summary>
/// Prevents a default instance of the <see cref="SystemInfo"/> class from being created.
/// </summary>
/// <exception cref="NotSupportedException">Could not initialize the GPIO controller</exception>
private SystemInfo()
{
private SystemInfo() {
#region Obtain and format a property dictionary
var properties =
PropertyInfo[] properties =
typeof(SystemInfo).GetTypeInfo()
.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
.Where(
p =>
p.CanWrite && p.CanRead &&
(p.PropertyType == typeof(string) || p.PropertyType == typeof(string[])))
(p.PropertyType == typeof(String) || p.PropertyType == typeof(String[])))
.ToArray();
var propDictionary = new Dictionary<string, PropertyInfo>(StringComparer);
Dictionary<String, PropertyInfo> propDictionary = new Dictionary<String, PropertyInfo>(StringComparer);
foreach (var prop in properties)
{
propDictionary[prop.Name.Replace(" ", string.Empty).ToLowerInvariant().Trim()] = prop;
foreach(PropertyInfo prop in properties) {
propDictionary[prop.Name.Replace(" ", String.Empty).ToLowerInvariant().Trim()] = prop;
}
#endregion
#region Extract CPU information
if (File.Exists(CpuInfoFilePath))
{
var cpuInfoLines = File.ReadAllLines(CpuInfoFilePath);
if(File.Exists(CpuInfoFilePath)) {
String[] cpuInfoLines = File.ReadAllLines(CpuInfoFilePath);
foreach (var line in cpuInfoLines)
{
var lineParts = line.Split(new[] { ':' }, 2);
if (lineParts.Length != 2)
foreach(String line in cpuInfoLines) {
String[] lineParts = line.Split(new[] { ':' }, 2);
if(lineParts.Length != 2) {
continue;
var propertyKey = lineParts[0].Trim().Replace(" ", string.Empty);
var propertyStringValue = lineParts[1].Trim();
if (!propDictionary.ContainsKey(propertyKey)) continue;
var property = propDictionary[propertyKey];
if (property.PropertyType == typeof(string))
{
property.SetValue(this, propertyStringValue);
}
else if (property.PropertyType == typeof(string[]))
{
var propertyArrayAvalue = propertyStringValue.Split(' ');
String propertyKey = lineParts[0].Trim().Replace(" ", String.Empty);
String propertyStringValue = lineParts[1].Trim();
if(!propDictionary.ContainsKey(propertyKey)) {
continue;
}
PropertyInfo property = propDictionary[propertyKey];
if(property.PropertyType == typeof(String)) {
property.SetValue(this, propertyStringValue);
} else if(property.PropertyType == typeof(String[])) {
String[] propertyArrayAvalue = propertyStringValue.Split(' ');
property.SetValue(this, propertyArrayAvalue);
}
}
@@ -80,23 +74,22 @@
#region Extract Memory Information
if (File.Exists(MemInfoFilePath))
{
var memInfoLines = File.ReadAllLines(MemInfoFilePath);
foreach (var line in memInfoLines)
{
var lineParts = line.Split(new[] { ':' }, 2);
if (lineParts.Length != 2)
if(File.Exists(MemInfoFilePath)) {
String[] memInfoLines = File.ReadAllLines(MemInfoFilePath);
foreach(String line in memInfoLines) {
String[] lineParts = line.Split(new[] { ':' }, 2);
if(lineParts.Length != 2) {
continue;
}
if (lineParts[0].ToLowerInvariant().Trim().Equals("memtotal") == false)
if(lineParts[0].ToLowerInvariant().Trim().Equals("memtotal") == false) {
continue;
}
var memKb = lineParts[1].ToLowerInvariant().Trim().Replace("kb", string.Empty).Trim();
String memKb = lineParts[1].ToLowerInvariant().Trim().Replace("kb", String.Empty).Trim();
if (int.TryParse(memKb, out var parsedMem))
{
InstalledRam = parsedMem * 1024;
if(Int32.TryParse(memKb, out Int32 parsedMem)) {
this.InstalledRam = parsedMem * 1024;
break;
}
}
@@ -106,26 +99,21 @@
#region Board Version and Form Factor
try
{
if (string.IsNullOrWhiteSpace(Revision) == false &&
int.TryParse(
Revision.ToUpperInvariant(),
try {
if(String.IsNullOrWhiteSpace(this.Revision) == false &&
Int32.TryParse(
this.Revision.ToUpperInvariant(),
NumberStyles.HexNumber,
CultureInfo.InvariantCulture,
out var boardVersion))
{
RaspberryPiVersion = PiVersion.Unknown;
if (Enum.GetValues(typeof(PiVersion)).Cast<int>().Contains(boardVersion))
{
RaspberryPiVersion = (PiVersion)boardVersion;
out Int32 boardVersion)) {
this.RaspberryPiVersion = PiVersion.Unknown;
if(Enum.GetValues(typeof(PiVersion)).Cast<Int32>().Contains(boardVersion)) {
this.RaspberryPiVersion = (PiVersion)boardVersion;
}
}
WiringPiBoardRevision = WiringPi.PiBoardRev();
}
catch
{
this.WiringPiBoardRevision = WiringPi.PiBoardRev();
} catch {
/* Ignore */
}
@@ -134,22 +122,20 @@
#region Version Information
{
var libParts = WiringPi.WiringPiLibrary.Split('.');
var major = int.Parse(libParts[libParts.Length - 2]);
var minor = int.Parse(libParts[libParts.Length - 1]);
var version = new Version(major, minor);
WiringPiVersion = version;
String[] libParts = WiringPi.WiringPiLibrary.Split('.');
Int32 major = Int32.Parse(libParts[libParts.Length - 2]);
Int32 minor = Int32.Parse(libParts[libParts.Length - 1]);
Version version = new Version(major, minor);
this.WiringPiVersion = version;
}
#endregion
#region Extract OS Info
try
{
Standard.Uname(out var unameInfo);
OperatingSystem = new OsInfo
{
try {
_ = Standard.Uname(out SystemName unameInfo);
this.OperatingSystem = new OsInfo {
DomainName = unameInfo.DomainName,
Machine = unameInfo.Machine,
NodeName = unameInfo.NodeName,
@@ -157,10 +143,8 @@
SysName = unameInfo.SysName,
Version = unameInfo.Version
};
}
catch
{
OperatingSystem = new OsInfo();
} catch {
this.OperatingSystem = new OsInfo();
}
#endregion
@@ -169,7 +153,9 @@
/// <summary>
/// Gets the wiring pi library version.
/// </summary>
public Version WiringPiVersion { get; }
public Version WiringPiVersion {
get;
}
/// <summary>
/// Gets the OS information.
@@ -177,12 +163,16 @@
/// <value>
/// The os information.
/// </value>
public OsInfo OperatingSystem { get; }
public OsInfo OperatingSystem {
get;
}
/// <summary>
/// Gets the Raspberry Pi version.
/// </summary>
public PiVersion RaspberryPiVersion { get; }
public PiVersion RaspberryPiVersion {
get;
}
/// <summary>
/// Gets the Wiring Pi board revision (1 or 2).
@@ -190,100 +180,112 @@
/// <value>
/// The wiring pi board revision.
/// </value>
public int WiringPiBoardRevision { get; }
public Int32 WiringPiBoardRevision {
get;
}
/// <summary>
/// Gets the number of processor cores.
/// </summary>
public int ProcessorCount
{
get
{
if (int.TryParse(Processor, out var outIndex))
{
return outIndex + 1;
}
return 0;
}
}
public Int32 ProcessorCount => Int32.TryParse(this.Processor, out Int32 outIndex) ? outIndex + 1 : 0;
/// <summary>
/// Gets the installed ram in bytes.
/// </summary>
public int InstalledRam { get; }
public Int32 InstalledRam {
get;
}
/// <summary>
/// Gets a value indicating whether this CPU is little endian.
/// </summary>
public bool IsLittleEndian => BitConverter.IsLittleEndian;
public Boolean IsLittleEndian => BitConverter.IsLittleEndian;
/// <summary>
/// Gets the CPU model name.
/// </summary>
public string ModelName { get; private set; }
public String ModelName {
get; private set;
}
/// <summary>
/// Gets a list of supported CPU features.
/// </summary>
public string[] Features { get; private set; }
public String[] Features {
get; private set;
}
/// <summary>
/// Gets the CPU implementer hex code.
/// </summary>
public string CpuImplementer { get; private set; }
public String CpuImplementer {
get; private set;
}
/// <summary>
/// Gets the CPU architecture code.
/// </summary>
public string CpuArchitecture { get; private set; }
public String CpuArchitecture {
get; private set;
}
/// <summary>
/// Gets the CPU variant code.
/// </summary>
public string CpuVariant { get; private set; }
public String CpuVariant {
get; private set;
}
/// <summary>
/// Gets the CPU part code.
/// </summary>
public string CpuPart { get; private set; }
public String CpuPart {
get; private set;
}
/// <summary>
/// Gets the CPU revision code.
/// </summary>
public string CpuRevision { get; private set; }
public String CpuRevision {
get; private set;
}
/// <summary>
/// Gets the hardware model number.
/// </summary>
public string Hardware { get; private set; }
public String Hardware {
get; private set;
}
/// <summary>
/// Gets the hardware revision number.
/// </summary>
public string Revision { get; private set; }
public String Revision {
get; private set;
}
/// <summary>
/// Gets the serial number.
/// </summary>
public string Serial { get; private set; }
public String Serial {
get; private set;
}
/// <summary>
/// Gets the system uptime (in seconds).
/// </summary>
public double Uptime
{
get
{
try
{
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))
public Double Uptime {
get {
try {
if(File.Exists(UptimeFilePath) == false) {
return 0;
}
String[] parts = File.ReadAllText(UptimeFilePath).Trim().Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
if(parts.Length >= 1 && Single.TryParse(parts[0], out Single result)) {
return result;
}
catch
{
} catch {
/* Ignore */
}
@@ -294,51 +296,48 @@
/// <summary>
/// Gets the uptime in TimeSpan.
/// </summary>
public TimeSpan UptimeTimeSpan => TimeSpan.FromSeconds(Uptime);
public TimeSpan UptimeTimeSpan => TimeSpan.FromSeconds(this.Uptime);
/// <summary>
/// Placeholder for processor index
/// </summary>
private string Processor { get; set; }
private String Processor {
get; set;
}
/// <summary>
/// Returns a <see cref="string" /> that represents this instance.
/// Returns a <see cref="String" /> that represents this instance.
/// </summary>
/// <returns>
/// A <see cref="string" /> that represents this instance.
/// A <see cref="String" /> that represents this instance.
/// </returns>
public override string ToString()
{
var properties = typeof(SystemInfo).GetTypeInfo().GetProperties(BindingFlags.Instance | BindingFlags.Public)
public override String ToString() {
PropertyInfo[] properties = typeof(SystemInfo).GetTypeInfo().GetProperties(BindingFlags.Instance | BindingFlags.Public)
.Where(p => p.CanRead && (
p.PropertyType == typeof(string) ||
p.PropertyType == typeof(string[]) ||
p.PropertyType == typeof(int) ||
p.PropertyType == typeof(bool) ||
p.PropertyType == typeof(String) ||
p.PropertyType == typeof(String[]) ||
p.PropertyType == typeof(Int32) ||
p.PropertyType == typeof(Boolean) ||
p.PropertyType == typeof(TimeSpan)))
.ToArray();
var properyValues = new List<string>
List<String> properyValues = new List<String>
{
"System Information",
$"\t{nameof(WiringPiVersion),-22}: {WiringPiVersion}",
$"\t{nameof(RaspberryPiVersion),-22}: {RaspberryPiVersion}"
$"\t{nameof(this.WiringPiVersion),-22}: {this.WiringPiVersion}",
$"\t{nameof(this.RaspberryPiVersion),-22}: {this.RaspberryPiVersion}"
};
foreach (var property in properties)
{
if (property.PropertyType != typeof(string[]))
{
foreach(PropertyInfo property in properties) {
if(property.PropertyType != typeof(String[])) {
properyValues.Add($"\t{property.Name,-22}: {property.GetValue(this)}");
}
else if (property.GetValue(this) is string[] allValues)
{
var concatValues = string.Join(" ", allValues);
} else if(property.GetValue(this) is String[] allValues) {
String 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
{
public class WirelessNetworkInfo {
/// <summary>
/// Gets the ESSID of the Wireless network.
/// </summary>
public string Name { get; internal set; }
public String Name {
get; internal set;
}
/// <summary>
/// Gets the network quality.
/// </summary>
public string Quality { get; internal set; }
public String Quality {
get; internal set;
}
/// <summary>
/// Gets a value indicating whether this instance is encrypted.
/// </summary>
public bool IsEncrypted { get; internal set; }
public Boolean IsEncrypted {
get; internal set;
}
}
}
+13 -26
View File
@@ -1,10 +1,8 @@
namespace Unosquare.RaspberryIO.Gpio
{
namespace Unosquare.RaspberryIO.Gpio {
/// <summary>
/// Defines the different drive modes of a GPIO pin
/// </summary>
public enum GpioPinDriveMode
{
public enum GpioPinDriveMode {
/// <summary>
/// Input drive mode (perform reads)
/// </summary>
@@ -30,8 +28,7 @@
/// The GPIO pin resistor mode. This is used on input pins so that their
/// lines are not floating
/// </summary>
public enum GpioPinResistorPullMode
{
public enum GpioPinResistorPullMode {
/// <summary>
/// Pull resistor not active. Line floating
/// </summary>
@@ -51,8 +48,7 @@
/// <summary>
/// The PWM mode.
/// </summary>
public enum PwmMode
{
public enum PwmMode {
/// <summary>
/// PWM pulses are sent using mark-sign patterns (old school)
/// </summary>
@@ -67,8 +63,7 @@
/// <summary>
/// Defines the different edge detection modes for pin interrupts
/// </summary>
public enum EdgeDetection
{
public enum EdgeDetection {
/// <summary>
/// Assumes edge detection was already setup externally
/// </summary>
@@ -93,8 +88,7 @@
/// <summary>
/// Defines the GPIO Pin values 0 for low, 1 for High
/// </summary>
public enum GpioPinValue
{
public enum GpioPinValue {
/// <summary>
/// Digital high
/// </summary>
@@ -109,8 +103,7 @@
/// <summary>
/// Defines the Header connectors available
/// </summary>
public enum GpioHeader
{
public enum GpioHeader {
/// <summary>
/// Not defined
/// </summary>
@@ -130,8 +123,7 @@
/// <summary>
/// Defines all the available Wiring Pi Pin Numbers
/// </summary>
public enum WiringPiPin
{
public enum WiringPiPin {
/// <summary>
/// The unknown
/// </summary>
@@ -303,8 +295,7 @@
/// as commonly referenced by Raspberry Pi Documentation.
/// Enumeration values correspond to the physical pin number.
/// </summary>
public enum P1
{
public enum P1 {
/// <summary>
/// Header P1, GPIO Pin 02
/// </summary>
@@ -441,8 +432,7 @@
/// as commonly referenced by Raspberry Pi documentation.
/// Enumeration values correspond to the physical pin number.
/// </summary>
public enum P5
{
public enum P5 {
/// <summary>
/// Header P5, GPIO Pin 28
/// </summary>
@@ -467,8 +457,7 @@
/// <summary>
/// Defines the different pin capabilities
/// </summary>
public enum PinCapability
{
public enum PinCapability {
/// <summary>
/// General Purpose capability: Digital and Analog Read/Write
/// </summary>
@@ -533,8 +522,7 @@
/// <summary>
/// Defines the SPI channel numbers
/// </summary>
internal enum SpiChannelNumber
{
internal enum SpiChannelNumber {
/// <summary>
/// The channel 0
/// </summary>
@@ -549,8 +537,7 @@
/// <summary>
/// Defines GPIO controller initialization modes
/// </summary>
internal enum ControllerMode
{
internal enum ControllerMode {
/// <summary>
/// The not initialized
/// </summary>
+120 -142
View File
@@ -1,30 +1,25 @@
namespace Unosquare.RaspberryIO.Gpio
{
using Native;
using Swan;
using Swan.Abstractions;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Threading.Tasks;
using Unosquare.RaspberryIO.Native;
using Unosquare.Swan;
using Unosquare.Swan.Abstractions;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Threading.Tasks;
namespace Unosquare.RaspberryIO.Gpio {
/// <summary>
/// Represents a singleton of the Raspberry Pi GPIO controller
/// as an IReadOnlyCollection of GpioPins
/// Low level operations are accomplished by using the Wiring Pi library.
/// Use the Instance property to access the singleton's instance
/// </summary>
public sealed class GpioController : SingletonBase<GpioController>, IReadOnlyCollection<GpioPin>
{
public sealed class GpioController : SingletonBase<GpioController>, IReadOnlyCollection<GpioPin> {
#region Private Declarations
private const string WiringPiCodesEnvironmentVariable = "WIRINGPI_CODES";
private static readonly object SyncRoot = new object();
private readonly ReadOnlyCollection<GpioPin> _pinCollection;
private readonly ReadOnlyDictionary<int, GpioPin> _headerP1Pins;
private readonly ReadOnlyDictionary<int, GpioPin> _headerP5Pins;
private const String WiringPiCodesEnvironmentVariable = "WIRINGPI_CODES";
private static readonly Object SyncRoot = new Object();
private readonly Dictionary<WiringPiPin, GpioPin> _pinsByWiringPiPinNumber = new Dictionary<WiringPiPin, GpioPin>();
#endregion
@@ -36,66 +31,65 @@
/// It in turn initializes the controller and registers the pin -- in that order.
/// </summary>
/// <exception cref="Exception">Unable to initialize the GPIO controller.</exception>
private GpioController()
{
if (_pinCollection != null)
private GpioController() {
if(this.Pins != null) {
return;
}
if (IsInitialized == false)
{
var initResult = Initialize(ControllerMode.DirectWithWiringPiPins);
if (initResult == false)
if(IsInitialized == false) {
Boolean initResult = this.Initialize(ControllerMode.DirectWithWiringPiPins);
if(initResult == false) {
throw new Exception("Unable to initialize the GPIO controller.");
}
}
#region Pin Registration (32 WiringPi Pins)
RegisterPin(GpioPin.Pin00.Value);
RegisterPin(GpioPin.Pin01.Value);
RegisterPin(GpioPin.Pin02.Value);
RegisterPin(GpioPin.Pin03.Value);
RegisterPin(GpioPin.Pin04.Value);
RegisterPin(GpioPin.Pin05.Value);
RegisterPin(GpioPin.Pin06.Value);
RegisterPin(GpioPin.Pin07.Value);
RegisterPin(GpioPin.Pin08.Value);
RegisterPin(GpioPin.Pin09.Value);
RegisterPin(GpioPin.Pin10.Value);
RegisterPin(GpioPin.Pin11.Value);
RegisterPin(GpioPin.Pin12.Value);
RegisterPin(GpioPin.Pin13.Value);
RegisterPin(GpioPin.Pin14.Value);
RegisterPin(GpioPin.Pin15.Value);
RegisterPin(GpioPin.Pin16.Value);
RegisterPin(GpioPin.Pin17.Value);
RegisterPin(GpioPin.Pin18.Value);
RegisterPin(GpioPin.Pin19.Value);
RegisterPin(GpioPin.Pin20.Value);
RegisterPin(GpioPin.Pin21.Value);
RegisterPin(GpioPin.Pin22.Value);
RegisterPin(GpioPin.Pin23.Value);
RegisterPin(GpioPin.Pin24.Value);
RegisterPin(GpioPin.Pin25.Value);
RegisterPin(GpioPin.Pin26.Value);
RegisterPin(GpioPin.Pin27.Value);
RegisterPin(GpioPin.Pin28.Value);
RegisterPin(GpioPin.Pin29.Value);
RegisterPin(GpioPin.Pin30.Value);
RegisterPin(GpioPin.Pin31.Value);
this.RegisterPin(GpioPin.Pin00.Value);
this.RegisterPin(GpioPin.Pin01.Value);
this.RegisterPin(GpioPin.Pin02.Value);
this.RegisterPin(GpioPin.Pin03.Value);
this.RegisterPin(GpioPin.Pin04.Value);
this.RegisterPin(GpioPin.Pin05.Value);
this.RegisterPin(GpioPin.Pin06.Value);
this.RegisterPin(GpioPin.Pin07.Value);
this.RegisterPin(GpioPin.Pin08.Value);
this.RegisterPin(GpioPin.Pin09.Value);
this.RegisterPin(GpioPin.Pin10.Value);
this.RegisterPin(GpioPin.Pin11.Value);
this.RegisterPin(GpioPin.Pin12.Value);
this.RegisterPin(GpioPin.Pin13.Value);
this.RegisterPin(GpioPin.Pin14.Value);
this.RegisterPin(GpioPin.Pin15.Value);
this.RegisterPin(GpioPin.Pin16.Value);
this.RegisterPin(GpioPin.Pin17.Value);
this.RegisterPin(GpioPin.Pin18.Value);
this.RegisterPin(GpioPin.Pin19.Value);
this.RegisterPin(GpioPin.Pin20.Value);
this.RegisterPin(GpioPin.Pin21.Value);
this.RegisterPin(GpioPin.Pin22.Value);
this.RegisterPin(GpioPin.Pin23.Value);
this.RegisterPin(GpioPin.Pin24.Value);
this.RegisterPin(GpioPin.Pin25.Value);
this.RegisterPin(GpioPin.Pin26.Value);
this.RegisterPin(GpioPin.Pin27.Value);
this.RegisterPin(GpioPin.Pin28.Value);
this.RegisterPin(GpioPin.Pin29.Value);
this.RegisterPin(GpioPin.Pin30.Value);
this.RegisterPin(GpioPin.Pin31.Value);
#endregion
_pinCollection = new ReadOnlyCollection<GpioPin>(_pinsByWiringPiPinNumber.Values.ToArray());
var headerP1 = new Dictionary<int, GpioPin>(_pinCollection.Count);
var headerP5 = new Dictionary<int, GpioPin>(_pinCollection.Count);
foreach (var pin in _pinCollection)
{
var target = pin.Header == GpioHeader.P1 ? headerP1 : headerP5;
this.Pins = new ReadOnlyCollection<GpioPin>(this._pinsByWiringPiPinNumber.Values.ToArray());
Dictionary<Int32, GpioPin> headerP1 = new Dictionary<Int32, GpioPin>(this.Pins.Count);
Dictionary<Int32, GpioPin> headerP5 = new Dictionary<Int32, GpioPin>(this.Pins.Count);
foreach(GpioPin pin in this.Pins) {
Dictionary<Int32, GpioPin> target = pin.Header == GpioHeader.P1 ? headerP1 : headerP5;
target[pin.HeaderPinNumber] = pin;
}
_headerP1Pins = new ReadOnlyDictionary<int, GpioPin>(headerP1);
_headerP5Pins = new ReadOnlyDictionary<int, GpioPin>(headerP5);
this.HeaderP1 = new ReadOnlyDictionary<Int32, GpioPin>(headerP1);
this.HeaderP5 = new ReadOnlyDictionary<Int32, GpioPin>(headerP5);
}
/// <summary>
@@ -104,12 +98,9 @@
/// <value>
/// <c>true</c> if the controller is properly initialized; otherwise, <c>false</c>.
/// </value>
public static bool IsInitialized
{
get
{
lock (SyncRoot)
{
public static Boolean IsInitialized {
get {
lock(SyncRoot) {
return Mode != ControllerMode.NotInitialized;
}
}
@@ -118,7 +109,7 @@
/// <summary>
/// Gets the number of registered pins in the controller.
/// </summary>
public int Count => _pinCollection.Count;
public Int32 Count => this.Pins.Count;
#endregion
@@ -127,24 +118,30 @@
/// <summary>
/// Gets the PWM base frequency (in Hz).
/// </summary>
public int PwmBaseFrequency => 19200000;
public Int32 PwmBaseFrequency => 19200000;
/// <summary>
/// Gets a red-only collection of all registered pins.
/// </summary>
public ReadOnlyCollection<GpioPin> Pins => _pinCollection;
public ReadOnlyCollection<GpioPin> Pins {
get;
}
/// <summary>
/// Provides all the pins on Header P1 of the Pi as a lookup by physical header pin number.
/// This header is the main header and it is the one commonly used.
/// </summary>
public ReadOnlyDictionary<int, GpioPin> HeaderP1 => _headerP1Pins;
public ReadOnlyDictionary<Int32, GpioPin> HeaderP1 {
get;
}
/// <summary>
/// Provides all the pins on Header P5 of the Pi as a lookup by physical header pin number.
/// This header is the secondary header and it is rarely used.
/// </summary>
public ReadOnlyDictionary<int, GpioPin> HeaderP5 => _headerP5Pins;
public ReadOnlyDictionary<Int32, GpioPin> HeaderP5 {
get;
}
#endregion
@@ -327,7 +324,7 @@
/// </value>
/// <param name="pinNumber">The pin number.</param>
/// <returns>A reference to the GPIO pin</returns>
public GpioPin this[WiringPiPin pinNumber] => _pinsByWiringPiPinNumber[pinNumber];
public GpioPin this[WiringPiPin pinNumber] => this._pinsByWiringPiPinNumber[pinNumber];
/// <summary>
/// Gets the <see cref="GpioPin"/> with the specified pin number.
@@ -337,7 +334,7 @@
/// </value>
/// <param name="pinNumber">The pin number.</param>
/// <returns>A reference to the GPIO pin</returns>
public GpioPin this[P1 pinNumber] => HeaderP1[(int)pinNumber];
public GpioPin this[P1 pinNumber] => this.HeaderP1[(Int32)pinNumber];
/// <summary>
/// Gets the <see cref="GpioPin"/> with the specified pin number.
@@ -347,7 +344,7 @@
/// </value>
/// <param name="pinNumber">The pin number.</param>
/// <returns>A reference to the GPIO pin</returns>
public GpioPin this[P5 pinNumber] => HeaderP5[(int)pinNumber];
public GpioPin this[P5 pinNumber] => this.HeaderP5[(Int32)pinNumber];
/// <summary>
/// Gets the <see cref="GpioPin"/> with the specified Wiring Pi pin number.
@@ -359,14 +356,13 @@
/// <param name="wiringPiPinNumber">The pin number as defined by Wiring Pi. This is not the header pin number as pin number in headers are obvoisly repeating.</param>
/// <returns>A reference to the GPIO pin</returns>
/// <exception cref="IndexOutOfRangeException">When the pin index is not found</exception>
public GpioPin this[int wiringPiPinNumber]
{
get
{
if (Enum.IsDefined(typeof(WiringPiPin), wiringPiPinNumber) == false)
public GpioPin this[Int32 wiringPiPinNumber] {
get {
if(Enum.IsDefined(typeof(WiringPiPin), wiringPiPinNumber) == false) {
throw new IndexOutOfRangeException($"Pin {wiringPiPinNumber} is not registered in the GPIO controller.");
}
return _pinsByWiringPiPinNumber[(WiringPiPin)wiringPiPinNumber];
return this._pinsByWiringPiPinNumber[(WiringPiPin)wiringPiPinNumber];
}
}
@@ -381,11 +377,9 @@
/// </summary>
/// <param name="group">The group.</param>
/// <param name="value">The value.</param>
public void SetPadDrive(int group, int value)
{
lock (SyncRoot)
{
WiringPi.SetPadDrive(group, value);
public void SetPadDrive(Int32 group, Int32 value) {
lock(SyncRoot) {
_ = WiringPi.SetPadDrive(group, value);
}
}
@@ -397,7 +391,7 @@
/// <param name="group">The group.</param>
/// <param name="value">The value.</param>
/// <returns>The awaitable task</returns>
public Task SetPadDriveAsync(int group, int value) => Task.Run(() => { SetPadDrive(group, value); });
public Task SetPadDriveAsync(Int32 group, Int32 value) => Task.Run(() => this.SetPadDrive(group, value));
/// <summary>
/// This writes the 8-bit byte supplied to the first 8 GPIO pins.
@@ -406,12 +400,9 @@
/// </summary>
/// <param name="value">The value.</param>
/// <exception cref="InvalidOperationException">PinMode</exception>
public void WriteByte(byte value)
{
lock (SyncRoot)
{
if (this.Skip(0).Take(8).Any(p => p.PinMode != GpioPinDriveMode.Output))
{
public void WriteByte(Byte value) {
lock(SyncRoot) {
if(this.Skip(0).Take(8).Any(p => p.PinMode != GpioPinDriveMode.Output)) {
throw new InvalidOperationException(
$"All firts 8 pins (0 to 7) need their {nameof(GpioPin.PinMode)} to be set to {GpioPinDriveMode.Output}");
}
@@ -427,7 +418,7 @@
/// </summary>
/// <param name="value">The value.</param>
/// <returns>The awaitable task</returns>
public Task WriteByteAsync(byte value) => Task.Run(() => { WriteByte(value); });
public Task WriteByteAsync(Byte value) => Task.Run(() => this.WriteByte(value));
/// <summary>
/// This reads the 8-bit byte supplied to the first 8 GPIO pins.
@@ -436,18 +427,15 @@
/// </summary>
/// <returns>A byte from the GPIO</returns>
/// <exception cref="InvalidOperationException">PinMode</exception>
public byte ReadByte()
{
lock (SyncRoot)
{
if (this.Skip(0).Take(8).Any(p =>
p.PinMode != GpioPinDriveMode.Input && p.PinMode != GpioPinDriveMode.Output))
{
public Byte ReadByte() {
lock(SyncRoot) {
if(this.Skip(0).Take(8).Any(p =>
p.PinMode != GpioPinDriveMode.Input && p.PinMode != GpioPinDriveMode.Output)) {
throw new InvalidOperationException(
$"All firts 8 pins (0 to 7) need their {nameof(GpioPin.PinMode)} to be set to {GpioPinDriveMode.Input} or {GpioPinDriveMode.Output}");
}
return (byte)WiringPi.DigitalReadByte();
return (Byte)WiringPi.DigitalReadByte();
}
}
@@ -457,7 +445,7 @@
/// Please note this function is undocumented and unsopported
/// </summary>
/// <returns>A byte from the GPIO</returns>
public Task<byte> ReadByteAsync() => Task.Run(() => ReadByte());
public Task<Byte> ReadByteAsync() => Task.Run(() => this.ReadByte());
#endregion
@@ -469,7 +457,7 @@
/// <returns>
/// A <see cref="T:System.Collections.Generic.IEnumerator`1" /> that can be used to iterate through the collection.
/// </returns>
public IEnumerator<GpioPin> GetEnumerator() => _pinCollection.GetEnumerator();
public IEnumerator<GpioPin> GetEnumerator() => this.Pins.GetEnumerator();
/// <summary>
/// Returns an enumerator that iterates through the collection.
@@ -477,7 +465,7 @@
/// <returns>
/// An <see cref="T:System.Collections.IEnumerator" /> object that can be used to iterate through the collection.
/// </returns>
IEnumerator IEnumerable.GetEnumerator() => _pinCollection.GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => this.Pins.GetEnumerator();
#endregion
@@ -488,17 +476,15 @@
/// </summary>
/// <param name="bcmPinNumber">The BCM pin number.</param>
/// <returns>The GPIO pin</returns>
public GpioPin GetGpioPinByBcmPinNumber(int bcmPinNumber) => this.First(pin => pin.BcmPinNumber == bcmPinNumber);
public GpioPin GetGpioPinByBcmPinNumber(Int32 bcmPinNumber) => this.First(pin => pin.BcmPinNumber == bcmPinNumber);
/// <summary>
/// Converts the Wirings Pi pin number to the BCM pin number.
/// </summary>
/// <param name="wiringPiPinNumber">The wiring pi pin number.</param>
/// <returns>The converted pin</returns>
internal static int WiringPiToBcmPinNumber(int wiringPiPinNumber)
{
lock (SyncRoot)
{
internal static Int32 WiringPiToBcmPinNumber(Int32 wiringPiPinNumber) {
lock(SyncRoot) {
return WiringPi.WpiPinToGpio(wiringPiPinNumber);
}
}
@@ -508,10 +494,8 @@
/// </summary>
/// <param name="headerPinNumber">The header pin number.</param>
/// <returns>The converted pin</returns>
internal static int HaderToBcmPinNumber(int headerPinNumber)
{
lock (SyncRoot)
{
internal static Int32 HaderToBcmPinNumber(Int32 headerPinNumber) {
lock(SyncRoot) {
return WiringPi.PhysPinToGpio(headerPinNumber);
}
}
@@ -520,13 +504,13 @@
/// Short-hand method of registering pins
/// </summary>
/// <param name="pin">The pin.</param>
private void RegisterPin(GpioPin pin)
{
if (_pinsByWiringPiPinNumber.ContainsKey(pin.WiringPiPinNumber) == false)
_pinsByWiringPiPinNumber[pin.WiringPiPinNumber] = pin;
else
private void RegisterPin(GpioPin pin) {
if(this._pinsByWiringPiPinNumber.ContainsKey(pin.WiringPiPinNumber) == false) {
this._pinsByWiringPiPinNumber[pin.WiringPiPinNumber] = pin;
} else {
throw new InvalidOperationException($"Pin {pin.WiringPiPinNumber} has been registered");
}
}
/// <summary>
/// Initializes the controller given the initialization mode and pin numbering scheme
@@ -538,47 +522,41 @@
/// </exception>
/// <exception cref="InvalidOperationException">Library was already Initialized</exception>
/// <exception cref="ArgumentException">The init mode is invalid</exception>
private bool Initialize(ControllerMode mode)
{
if (Runtime.OS != Swan.OperatingSystem.Unix)
private Boolean Initialize(ControllerMode mode) {
if(Runtime.OS != Swan.OperatingSystem.Unix) {
throw new PlatformNotSupportedException("This library does not support the platform");
}
lock (SyncRoot)
{
if (IsInitialized)
lock(SyncRoot) {
if(IsInitialized) {
throw new InvalidOperationException($"Cannot call {nameof(Initialize)} more than once.");
}
Environment.SetEnvironmentVariable(WiringPiCodesEnvironmentVariable, "1", EnvironmentVariableTarget.Process);
int setpuResult;
Int32 setpuResult;
switch (mode)
{
case ControllerMode.DirectWithWiringPiPins:
{
switch(mode) {
case ControllerMode.DirectWithWiringPiPins: {
setpuResult = WiringPi.WiringPiSetup();
break;
}
case ControllerMode.DirectWithBcmPins:
{
case ControllerMode.DirectWithBcmPins: {
setpuResult = WiringPi.WiringPiSetupGpio();
break;
}
case ControllerMode.DirectWithHeaderPins:
{
case ControllerMode.DirectWithHeaderPins: {
setpuResult = WiringPi.WiringPiSetupPhys();
break;
}
case ControllerMode.FileStreamWithHardwarePins:
{
case ControllerMode.FileStreamWithHardwarePins: {
setpuResult = WiringPi.WiringPiSetupSys();
break;
}
default:
{
default: {
throw new ArgumentException($"'{mode}' is not a valid initialization mode.");
}
}
+35 -69
View File
@@ -1,197 +1,163 @@
namespace Unosquare.RaspberryIO.Gpio
{
using System;
using System;
public partial class GpioPin
{
namespace Unosquare.RaspberryIO.Gpio {
public partial class GpioPin {
#region Static Pin Definitions
internal static readonly Lazy<GpioPin> Pin08 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin08, 3)
{
internal static readonly Lazy<GpioPin> Pin08 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin08, 3) {
Capabilities = new[] { PinCapability.GP, PinCapability.I2CSDA },
Name = "BCM 2 (SDA)"
});
internal static readonly Lazy<GpioPin> Pin09 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin09, 5)
{
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> Pin07 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin07, 7)
{
internal static readonly Lazy<GpioPin> Pin07 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin07, 7) {
Capabilities = new[] { PinCapability.GP, PinCapability.GPCLK },
Name = "BCM 4 (GPCLK0)"
});
internal static readonly Lazy<GpioPin> Pin00 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin00, 11)
{
internal static readonly Lazy<GpioPin> Pin00 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin00, 11) {
Capabilities = new[] { PinCapability.GP, PinCapability.UARTRTS },
Name = "BCM 17"
});
internal static readonly Lazy<GpioPin> Pin02 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin02, 13)
{
internal static readonly Lazy<GpioPin> Pin02 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin02, 13) {
Capabilities = new[] { PinCapability.GP },
Name = "BCM 27"
});
internal static readonly Lazy<GpioPin> Pin03 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin03, 15)
{
internal static readonly Lazy<GpioPin> Pin03 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin03, 15) {
Capabilities = new[] { PinCapability.GP },
Name = "BCM 22"
});
internal static readonly Lazy<GpioPin> Pin12 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin12, 19)
{
internal static readonly Lazy<GpioPin> Pin12 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin12, 19) {
Capabilities = new[] { PinCapability.GP, PinCapability.SPIMOSI },
Name = "BCM 10 (MOSI)"
});
internal static readonly Lazy<GpioPin> Pin13 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin13, 21)
{
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> Pin14 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin14, 23)
{
internal static readonly Lazy<GpioPin> Pin14 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin14, 23) {
Capabilities = new[] { PinCapability.GP, PinCapability.SPICLK },
Name = "BCM 11 (SCLCK)"
});
internal static readonly Lazy<GpioPin> Pin30 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin30, 27)
{
internal static readonly Lazy<GpioPin> Pin30 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin30, 27) {
Capabilities = new[] { PinCapability.I2CSDA },
Name = "BCM 0 (ID_SD)"
});
internal static readonly Lazy<GpioPin> Pin31 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin31, 28)
{
internal static readonly Lazy<GpioPin> Pin31 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin31, 28) {
Capabilities = new[] { PinCapability.I2CSCL },
Name = "BCM 1 (ID_SC)"
});
internal static readonly Lazy<GpioPin> Pin11 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin11, 26)
{
internal static readonly Lazy<GpioPin> Pin11 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin11, 26) {
Capabilities = new[] { PinCapability.GP, PinCapability.SPICS },
Name = "BCM 7 (CE1)"
});
internal static readonly Lazy<GpioPin> Pin10 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin10, 24)
{
internal static readonly Lazy<GpioPin> Pin10 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin10, 24) {
Capabilities = new[] { PinCapability.GP, PinCapability.SPICS },
Name = "BCM 8 (CE0)"
});
internal static readonly Lazy<GpioPin> Pin06 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin06, 22)
{
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> Pin05 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin05, 18)
{
internal static readonly Lazy<GpioPin> Pin05 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin05, 18) {
Capabilities = new[] { PinCapability.GP },
Name = "BCM 24"
});
internal static readonly Lazy<GpioPin> Pin04 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin04, 16)
{
internal static readonly Lazy<GpioPin> Pin04 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin04, 16) {
Capabilities = new[] { PinCapability.GP },
Name = "BCM 23"
});
internal static readonly Lazy<GpioPin> Pin01 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin01, 12)
{
internal static readonly Lazy<GpioPin> Pin01 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin01, 12) {
Capabilities = new[] { PinCapability.GP, PinCapability.PWM },
Name = "BCM 18 (PWM0)"
});
internal static readonly Lazy<GpioPin> Pin16 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin16, 10)
{
internal static readonly Lazy<GpioPin> Pin16 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin16, 10) {
Capabilities = new[] { PinCapability.UARTRXD },
Name = "BCM 15 (RXD)"
});
internal static readonly Lazy<GpioPin> Pin15 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin15, 8)
{
internal static readonly Lazy<GpioPin> Pin15 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin15, 8) {
Capabilities = new[] { PinCapability.UARTTXD },
Name = "BCM 14 (TXD)"
});
internal static readonly Lazy<GpioPin> Pin21 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin21, 29)
{
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> Pin22 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin22, 31)
{
internal static readonly Lazy<GpioPin> Pin22 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin22, 31) {
Capabilities = new[] { PinCapability.GP },
Name = "BCM 6"
});
internal static readonly Lazy<GpioPin> Pin23 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin23, 33)
{
internal static readonly Lazy<GpioPin> Pin23 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin23, 33) {
Capabilities = new[] { PinCapability.GP, PinCapability.PWM },
Name = "BCM 13 (PWM1)"
});
internal static readonly Lazy<GpioPin> Pin24 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin24, 35)
{
internal static readonly Lazy<GpioPin> Pin24 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin24, 35) {
Capabilities = new[] { PinCapability.GP, PinCapability.SPIMISO },
Name = "BCM 19 (MISO)"
});
internal static readonly Lazy<GpioPin> Pin25 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin25, 37)
{
internal static readonly Lazy<GpioPin> Pin25 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin25, 37) {
Capabilities = new[] { PinCapability.GP },
Name = "BCM 26"
});
internal static readonly Lazy<GpioPin> Pin29 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin29, 40)
{
internal static readonly Lazy<GpioPin> Pin29 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin29, 40) {
Capabilities = new[] { PinCapability.GP, PinCapability.SPICLK },
Name = "BCM 21 (SCLK)"
});
internal static readonly Lazy<GpioPin> Pin28 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin28, 38)
{
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> Pin27 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin27, 36)
{
internal static readonly Lazy<GpioPin> Pin27 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin27, 36) {
Capabilities = new[] { PinCapability.GP },
Name = "BCM 16"
});
internal static readonly Lazy<GpioPin> Pin26 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin26, 32)
{
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)
{
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)
{
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)
{
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)
{
internal static readonly Lazy<GpioPin> Pin20 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin20, 6) {
Capabilities = new[] { PinCapability.GP },
Name = "BCM 31"
});
+199 -242
View File
@@ -1,29 +1,27 @@
namespace Unosquare.RaspberryIO.Gpio
{
using Native;
using Swan;
using System;
using System.Linq;
using System.Threading.Tasks;
using Unosquare.RaspberryIO.Native;
using Unosquare.Swan;
using System;
using System.Linq;
using System.Threading.Tasks;
namespace Unosquare.RaspberryIO.Gpio {
/// <summary>
/// Represents a GPIO Pin, its location and its capabilities.
/// Full pin reference available here:
/// http://pinout.xyz/pinout/pin31_gpio6 and http://wiringpi.com/pins/
/// </summary>
public sealed partial class GpioPin
{
public sealed partial class GpioPin {
#region Property Backing
private readonly object _syncLock = new object();
private readonly Object _syncLock = new Object();
private GpioPinDriveMode m_PinMode;
private GpioPinResistorPullMode m_ResistorPullMode;
private int m_PwmRegister;
private Int32 m_PwmRegister;
private PwmMode m_PwmMode = PwmMode.Balanced;
private uint m_PwmRange = 1024;
private int m_PwmClockDivisor = 1;
private int m_SoftPwmValue = -1;
private int m_SoftToneFrequency = -1;
private UInt32 m_PwmRange = 1024;
private Int32 m_PwmClockDivisor = 1;
private Int32 m_SoftPwmValue = -1;
private Int32 m_SoftToneFrequency = -1;
#endregion
@@ -34,13 +32,12 @@
/// </summary>
/// <param name="wiringPiPinNumber">The wiring pi pin number.</param>
/// <param name="headerPinNumber">The header pin number.</param>
private GpioPin(WiringPiPin wiringPiPinNumber, int headerPinNumber)
{
PinNumber = (int)wiringPiPinNumber;
WiringPiPinNumber = wiringPiPinNumber;
BcmPinNumber = GpioController.WiringPiToBcmPinNumber((int)wiringPiPinNumber);
HeaderPinNumber = headerPinNumber;
Header = (PinNumber >= 17 && PinNumber <= 20) ? GpioHeader.P5 : GpioHeader.P1;
private GpioPin(WiringPiPin wiringPiPinNumber, Int32 headerPinNumber) {
this.PinNumber = (Int32)wiringPiPinNumber;
this.WiringPiPinNumber = wiringPiPinNumber;
this.BcmPinNumber = GpioController.WiringPiToBcmPinNumber((Int32)wiringPiPinNumber);
this.HeaderPinNumber = headerPinNumber;
this.Header = (this.PinNumber >= 17 && this.PinNumber <= 20) ? GpioHeader.P5 : GpioHeader.P1;
}
#endregion
@@ -50,37 +47,51 @@
/// <summary>
/// Gets or sets the Wiring Pi pin number as an integer.
/// </summary>
public int PinNumber { get; }
public Int32 PinNumber {
get;
}
/// <summary>
/// Gets the WiringPi Pin number
/// </summary>
public WiringPiPin WiringPiPinNumber { get; }
public WiringPiPin WiringPiPinNumber {
get;
}
/// <summary>
/// Gets the BCM chip (hardware) pin number.
/// </summary>
public int BcmPinNumber { get; }
public Int32 BcmPinNumber {
get;
}
/// <summary>
/// Gets or the physical header (physical board) pin number.
/// </summary>
public int HeaderPinNumber { get; }
public Int32 HeaderPinNumber {
get;
}
/// <summary>
/// Gets the pin's header (physical board) location.
/// </summary>
public GpioHeader Header { get; }
public GpioHeader Header {
get;
}
/// <summary>
/// Gets the friendly name of the pin.
/// </summary>
public string Name { get; private set; }
public String Name {
get; private set;
}
/// <summary>
/// Gets the hardware mode capabilities of this pin.
/// </summary>
public PinCapability[] Capabilities { get; private set; }
public PinCapability[] Capabilities {
get; private set;
}
#endregion
@@ -93,26 +104,22 @@
/// The pin mode.
/// </value>
/// <exception cref="NotSupportedException">Thrown when a pin does not support the given operation mode.</exception>
public GpioPinDriveMode PinMode
{
get => m_PinMode;
public GpioPinDriveMode PinMode {
get => this.m_PinMode;
set
{
lock (_syncLock)
{
var mode = value;
if ((mode == GpioPinDriveMode.GpioClock && Capabilities.Contains(PinCapability.GPCLK) == false) ||
(mode == GpioPinDriveMode.PwmOutput && Capabilities.Contains(PinCapability.PWM) == false) ||
(mode == GpioPinDriveMode.Input && Capabilities.Contains(PinCapability.GP) == false) ||
(mode == GpioPinDriveMode.Output && Capabilities.Contains(PinCapability.GP) == false))
{
set {
lock(this._syncLock) {
GpioPinDriveMode mode = value;
if(mode == GpioPinDriveMode.GpioClock && this.Capabilities.Contains(PinCapability.GPCLK) == false ||
mode == GpioPinDriveMode.PwmOutput && this.Capabilities.Contains(PinCapability.PWM) == false ||
mode == GpioPinDriveMode.Input && this.Capabilities.Contains(PinCapability.GP) == false ||
mode == GpioPinDriveMode.Output && this.Capabilities.Contains(PinCapability.GP) == false) {
throw new NotSupportedException(
$"Pin {WiringPiPinNumber} '{Name}' does not support mode '{mode}'. Pin capabilities are limited to: {string.Join(", ", Capabilities)}");
$"Pin {this.WiringPiPinNumber} '{this.Name}' does not support mode '{mode}'. Pin capabilities are limited to: {String.Join(", ", this.Capabilities)}");
}
WiringPi.PinMode(PinNumber, (int)mode);
m_PinMode = mode;
WiringPi.PinMode(this.PinNumber, (Int32)mode);
this.m_PinMode = mode;
}
}
}
@@ -121,7 +128,9 @@
/// Gets the interrupt callback. Returns null if no interrupt
/// has been registered.
/// </summary>
public InterruptServiceRoutineCallback InterruptCallback { get; private set; }
public InterruptServiceRoutineCallback InterruptCallback {
get; private set;
}
/// <summary>
/// Gets the interrupt edge detection mode.
@@ -138,24 +147,20 @@
/// 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 have a value of approximately 50KΩ on the Raspberry Pi.
/// </summary>
public GpioPinResistorPullMode InputPullMode
{
get => PinMode == GpioPinDriveMode.Input ? m_ResistorPullMode : GpioPinResistorPullMode.Off;
public GpioPinResistorPullMode InputPullMode {
get => this.PinMode == GpioPinDriveMode.Input ? this.m_ResistorPullMode : GpioPinResistorPullMode.Off;
set
{
lock (_syncLock)
{
if (PinMode != GpioPinDriveMode.Input)
{
m_ResistorPullMode = GpioPinResistorPullMode.Off;
set {
lock(this._syncLock) {
if(this.PinMode != GpioPinDriveMode.Input) {
this.m_ResistorPullMode = GpioPinResistorPullMode.Off;
throw new InvalidOperationException(
$"Unable to set the {nameof(InputPullMode)} for pin {PinNumber} because operating mode is {PinMode}."
+ $" Setting the {nameof(InputPullMode)} is only allowed if {nameof(PinMode)} is set to {GpioPinDriveMode.Input}");
$"Unable to set the {nameof(this.InputPullMode)} for pin {this.PinNumber} because operating mode is {this.PinMode}."
+ $" Setting the {nameof(this.InputPullMode)} is only allowed if {nameof(this.PinMode)} is set to {GpioPinDriveMode.Input}");
}
WiringPi.PullUpDnControl(PinNumber, (int)value);
m_ResistorPullMode = value;
WiringPi.PullUpDnControl(this.PinNumber, (Int32)value);
this.m_ResistorPullMode = value;
}
}
}
@@ -166,27 +171,23 @@
/// <value>
/// The PWM register.
/// </value>
public int PwmRegister
{
get => m_PwmRegister;
public Int32 PwmRegister {
get => this.m_PwmRegister;
set
{
lock (_syncLock)
{
if (PinMode != GpioPinDriveMode.PwmOutput)
{
m_PwmRegister = 0;
set {
lock(this._syncLock) {
if(this.PinMode != GpioPinDriveMode.PwmOutput) {
this.m_PwmRegister = 0;
throw new InvalidOperationException(
$"Unable to write PWM register for pin {PinNumber} because operating mode is {PinMode}."
+ $" Writing the PWM register is only allowed if {nameof(PinMode)} is set to {GpioPinDriveMode.PwmOutput}");
$"Unable to write PWM register for pin {this.PinNumber} because operating mode is {this.PinMode}."
+ $" Writing the PWM register is only allowed if {nameof(this.PinMode)} is set to {GpioPinDriveMode.PwmOutput}");
}
var val = value.Clamp(0, 1024);
Int32 val = value.Clamp(0, 1024);
WiringPi.PwmWrite(PinNumber, val);
m_PwmRegister = val;
WiringPi.PwmWrite(this.PinNumber, val);
this.m_PwmRegister = val;
}
}
}
@@ -199,25 +200,21 @@
/// The PWM mode.
/// </value>
/// <exception cref="InvalidOperationException">When pin mode is not set a Pwn output</exception>
public PwmMode PwmMode
{
get => PinMode == GpioPinDriveMode.PwmOutput ? m_PwmMode : PwmMode.Balanced;
public PwmMode PwmMode {
get => this.PinMode == GpioPinDriveMode.PwmOutput ? this.m_PwmMode : PwmMode.Balanced;
set
{
lock (_syncLock)
{
if (PinMode != GpioPinDriveMode.PwmOutput)
{
m_PwmMode = PwmMode.Balanced;
set {
lock(this._syncLock) {
if(this.PinMode != GpioPinDriveMode.PwmOutput) {
this.m_PwmMode = PwmMode.Balanced;
throw new InvalidOperationException(
$"Unable to set PWM mode for pin {PinNumber} because operating mode is {PinMode}."
+ $" Setting the PWM mode is only allowed if {nameof(PinMode)} is set to {GpioPinDriveMode.PwmOutput}");
$"Unable to set PWM mode for pin {this.PinNumber} because operating mode is {this.PinMode}."
+ $" Setting the PWM mode is only allowed if {nameof(this.PinMode)} is set to {GpioPinDriveMode.PwmOutput}");
}
WiringPi.PwmSetMode((int)value);
m_PwmMode = value;
WiringPi.PwmSetMode((Int32)value);
this.m_PwmMode = value;
}
}
}
@@ -229,25 +226,21 @@
/// The PWM range.
/// </value>
/// <exception cref="InvalidOperationException">When pin mode is not set to PWM output</exception>
public uint PwmRange
{
get => PinMode == GpioPinDriveMode.PwmOutput ? m_PwmRange : 0;
public UInt32 PwmRange {
get => this.PinMode == GpioPinDriveMode.PwmOutput ? this.m_PwmRange : 0;
set
{
lock (_syncLock)
{
if (PinMode != GpioPinDriveMode.PwmOutput)
{
m_PwmRange = 1024;
set {
lock(this._syncLock) {
if(this.PinMode != GpioPinDriveMode.PwmOutput) {
this.m_PwmRange = 1024;
throw new InvalidOperationException(
$"Unable to set PWM range for pin {PinNumber} because operating mode is {PinMode}."
+ $" Setting the PWM range is only allowed if {nameof(PinMode)} is set to {GpioPinDriveMode.PwmOutput}");
$"Unable to set PWM range for pin {this.PinNumber} because operating mode is {this.PinMode}."
+ $" Setting the PWM range is only allowed if {nameof(this.PinMode)} is set to {GpioPinDriveMode.PwmOutput}");
}
WiringPi.PwmSetRange(value);
m_PwmRange = value;
this.m_PwmRange = value;
}
}
}
@@ -259,25 +252,21 @@
/// The PWM clock divisor.
/// </value>
/// <exception cref="InvalidOperationException">When pin mode is not set to PWM output</exception>
public int PwmClockDivisor
{
get => PinMode == GpioPinDriveMode.PwmOutput ? m_PwmClockDivisor : 0;
public Int32 PwmClockDivisor {
get => this.PinMode == GpioPinDriveMode.PwmOutput ? this.m_PwmClockDivisor : 0;
set
{
lock (_syncLock)
{
if (PinMode != GpioPinDriveMode.PwmOutput)
{
m_PwmClockDivisor = 1;
set {
lock(this._syncLock) {
if(this.PinMode != GpioPinDriveMode.PwmOutput) {
this.m_PwmClockDivisor = 1;
throw new InvalidOperationException(
$"Unable to set PWM range for pin {PinNumber} because operating mode is {PinMode}."
+ $" Setting the PWM range is only allowed if {nameof(PinMode)} is set to {GpioPinDriveMode.PwmOutput}");
$"Unable to set PWM range for pin {this.PinNumber} because operating mode is {this.PinMode}."
+ $" Setting the PWM range is only allowed if {nameof(this.PinMode)} is set to {GpioPinDriveMode.PwmOutput}");
}
WiringPi.PwmSetClock(value);
m_PwmClockDivisor = value;
this.m_PwmClockDivisor = value;
}
}
}
@@ -292,7 +281,7 @@
/// <value>
/// <c>true</c> if this instance is in soft tone mode; otherwise, <c>false</c>.
/// </value>
public bool IsInSoftToneMode => m_SoftToneFrequency >= 0;
public Boolean IsInSoftToneMode => this.m_SoftToneFrequency >= 0;
/// <summary>
/// Gets or sets the soft tone frequency. 0 to 5000 Hz is typical
@@ -301,26 +290,21 @@
/// The soft tone frequency.
/// </value>
/// <exception cref="InvalidOperationException">When soft tones cannot be initialized on the pin</exception>
public int SoftToneFrequency
{
get => m_SoftToneFrequency;
public Int32 SoftToneFrequency {
get => this.m_SoftToneFrequency;
set
{
lock (_syncLock)
{
if (IsInSoftToneMode == false)
{
var setupResult = WiringPi.SoftToneCreate(PinNumber);
if (setupResult != 0)
{
set {
lock(this._syncLock) {
if(this.IsInSoftToneMode == false) {
Int32 setupResult = WiringPi.SoftToneCreate(this.PinNumber);
if(setupResult != 0) {
throw new InvalidOperationException(
$"Unable to initialize soft tone on pin {PinNumber}. Error Code: {setupResult}");
$"Unable to initialize soft tone on pin {this.PinNumber}. Error Code: {setupResult}");
}
}
WiringPi.SoftToneWrite(PinNumber, value);
m_SoftToneFrequency = value;
WiringPi.SoftToneWrite(this.PinNumber, value);
this.m_SoftToneFrequency = value;
}
}
}
@@ -335,7 +319,7 @@
/// <value>
/// <c>true</c> if this instance is in soft PWM mode; otherwise, <c>false</c>.
/// </value>
public bool IsInSoftPwmMode => m_SoftPwmValue >= 0;
public Boolean IsInSoftPwmMode => this.m_SoftPwmValue >= 0;
/// <summary>
/// Gets or sets the software PWM value on the pin.
@@ -344,21 +328,15 @@
/// The soft PWM value.
/// </value>
/// <exception cref="InvalidOperationException">StartSoftPwm</exception>
public int SoftPwmValue
{
get => m_SoftPwmValue;
public Int32 SoftPwmValue {
get => this.m_SoftPwmValue;
set
{
lock (_syncLock)
{
if (IsInSoftPwmMode && value >= 0)
{
WiringPi.SoftPwmWrite(PinNumber, value);
m_SoftPwmValue = value;
}
else
{
set {
lock(this._syncLock) {
if(this.IsInSoftPwmMode && value >= 0) {
WiringPi.SoftPwmWrite(this.PinNumber, value);
this.m_SoftPwmValue = value;
} else {
throw new InvalidOperationException($"Software PWM requires a call to {nameof(StartSoftPwm)}.");
}
}
@@ -368,7 +346,7 @@
/// <summary>
/// Gets the software PWM range used upon starting the PWM.
/// </summary>
public int SoftPwmRange { get; private set; } = -1;
public Int32 SoftPwmRange { get; private set; } = -1;
/// <summary>
/// Starts the software based PWM on this pin.
@@ -378,27 +356,24 @@
/// <exception cref="NotSupportedException">When the pin does not suppoert PWM</exception>
/// <exception cref="InvalidOperationException">StartSoftPwm
/// or</exception>
public void StartSoftPwm(int value, int range)
{
lock (_syncLock)
{
if (Capabilities.Contains(PinCapability.GP) == false)
throw new NotSupportedException($"Pin {PinNumber} does not support software PWM");
if (IsInSoftPwmMode)
throw new InvalidOperationException($"{nameof(StartSoftPwm)} has already been called.");
var startResult = WiringPi.SoftPwmCreate(PinNumber, value, range);
if (startResult == 0)
{
m_SoftPwmValue = value;
SoftPwmRange = range;
public void StartSoftPwm(Int32 value, Int32 range) {
lock(this._syncLock) {
if(this.Capabilities.Contains(PinCapability.GP) == false) {
throw new NotSupportedException($"Pin {this.PinNumber} does not support software PWM");
}
else
{
if(this.IsInSoftPwmMode) {
throw new InvalidOperationException($"{nameof(StartSoftPwm)} has already been called.");
}
Int32 startResult = WiringPi.SoftPwmCreate(this.PinNumber, value, range);
if(startResult == 0) {
this.m_SoftPwmValue = value;
this.SoftPwmRange = range;
} else {
throw new InvalidOperationException(
$"Could not start software based PWM on pin {PinNumber}. Error code: {startResult}");
$"Could not start software based PWM on pin {this.PinNumber}. Error code: {startResult}");
}
}
}
@@ -412,18 +387,15 @@
/// This method performs a digital write
/// </summary>
/// <param name="value">The value.</param>
public void Write(GpioPinValue value)
{
lock (_syncLock)
{
if (PinMode != GpioPinDriveMode.Output)
{
public void Write(GpioPinValue value) {
lock(this._syncLock) {
if(this.PinMode != GpioPinDriveMode.Output) {
throw new InvalidOperationException(
$"Unable to write to pin {PinNumber} because operating mode is {PinMode}."
+ $" Writes are only allowed if {nameof(PinMode)} is set to {GpioPinDriveMode.Output}");
$"Unable to write to pin {this.PinNumber} because operating mode is {this.PinMode}."
+ $" Writes are only allowed if {nameof(this.PinMode)} is set to {GpioPinDriveMode.Output}");
}
WiringPi.DigitalWrite(PinNumber, (int)value);
WiringPi.DigitalWrite(this.PinNumber, (Int32)value);
}
}
@@ -432,15 +404,15 @@
/// </summary>
/// <param name="value">The value.</param>
/// <returns>The awaitable task</returns>
public Task WriteAsync(GpioPinValue value) => Task.Run(() => { Write(value); });
public Task WriteAsync(GpioPinValue value) => Task.Run(() => this.Write(value));
/// <summary>
/// Writes the specified bit value.
/// This method performs a digital write
/// </summary>
/// <param name="value">if set to <c>true</c> [value].</param>
public void Write(bool value)
=> Write(value ? GpioPinValue.High : GpioPinValue.Low);
public void Write(Boolean value)
=> this.Write(value ? GpioPinValue.High : GpioPinValue.Low);
/// <summary>
/// Writes the specified bit value.
@@ -450,14 +422,14 @@
/// <returns>
/// The awaitable task
/// </returns>
public Task WriteAsync(bool value) => Task.Run(() => { Write(value); });
public Task WriteAsync(Boolean value) => Task.Run(() => this.Write(value));
/// <summary>
/// Writes the specified value. 0 for low, any other value for high
/// This method performs a digital write
/// </summary>
/// <param name="value">The value.</param>
public void Write(int value) => Write(value != 0 ? GpioPinValue.High : GpioPinValue.Low);
public void Write(Int32 value) => this.Write(value != 0 ? GpioPinValue.High : GpioPinValue.Low);
/// <summary>
/// Writes the specified value. 0 for low, any other value for high
@@ -465,25 +437,22 @@
/// </summary>
/// <param name="value">The value.</param>
/// <returns>The awaitable task</returns>
public Task WriteAsync(int value) => Task.Run(() => { Write(value); });
public Task WriteAsync(Int32 value) => Task.Run(() => this.Write(value));
/// <summary>
/// Writes the specified value as an analog level.
/// You will need to register additional analog modules to enable this function for devices such as the Gertboard.
/// </summary>
/// <param name="value">The value.</param>
public void WriteLevel(int value)
{
lock (_syncLock)
{
if (PinMode != GpioPinDriveMode.Output)
{
public void WriteLevel(Int32 value) {
lock(this._syncLock) {
if(this.PinMode != GpioPinDriveMode.Output) {
throw new InvalidOperationException(
$"Unable to write to pin {PinNumber} because operating mode is {PinMode}."
+ $" Writes are only allowed if {nameof(PinMode)} is set to {GpioPinDriveMode.Output}");
$"Unable to write to pin {this.PinNumber} because operating mode is {this.PinMode}."
+ $" Writes are only allowed if {nameof(this.PinMode)} is set to {GpioPinDriveMode.Output}");
}
WiringPi.AnalogWrite(PinNumber, value);
WiringPi.AnalogWrite(this.PinNumber, value);
}
}
@@ -493,7 +462,7 @@
/// </summary>
/// <param name="value">The value.</param>
/// <returns>The awaitable task</returns>
public Task WriteLevelAsync(int value) => Task.Run(() => { WriteLevel(value); });
public Task WriteLevelAsync(Int32 value) => Task.Run(() => this.WriteLevel(value));
#endregion
@@ -505,25 +474,23 @@
/// <param name="status">status to check</param>
/// <param name="timeOutMillisecond">timeout to reach status</param>
/// <returns>true/false</returns>
public bool WaitForValue(GpioPinValue status, int timeOutMillisecond)
{
if (PinMode != GpioPinDriveMode.Input)
{
public Boolean WaitForValue(GpioPinValue status, Int32 timeOutMillisecond) {
if(this.PinMode != GpioPinDriveMode.Input) {
throw new InvalidOperationException(
$"Unable to read from pin {PinNumber} because operating mode is {PinMode}."
+ $" Reads are only allowed if {nameof(PinMode)} is set to {GpioPinDriveMode.Input}");
$"Unable to read from pin {this.PinNumber} because operating mode is {this.PinMode}."
+ $" Reads are only allowed if {nameof(this.PinMode)} is set to {GpioPinDriveMode.Input}");
}
var hrt = new HighResolutionTimer();
HighResolutionTimer hrt = new HighResolutionTimer();
hrt.Start();
do
{
if (ReadValue() == status)
do {
if(this.ReadValue() == status) {
return true;
}
Pi.Timing.SleepMicroseconds(101); // 101 uses nanosleep as opposed to a loop.
}
while (hrt.ElapsedMilliseconds <= timeOutMillisecond);
while(hrt.ElapsedMilliseconds <= timeOutMillisecond);
return false;
}
@@ -532,18 +499,15 @@
/// Reads the digital value on the pin as a boolean value.
/// </summary>
/// <returns>The state of the pin</returns>
public bool Read()
{
lock (_syncLock)
{
if (PinMode != GpioPinDriveMode.Input && PinMode != GpioPinDriveMode.Output)
{
public Boolean Read() {
lock(this._syncLock) {
if(this.PinMode != GpioPinDriveMode.Input && this.PinMode != GpioPinDriveMode.Output) {
throw new InvalidOperationException(
$"Unable to read from pin {PinNumber} because operating mode is {PinMode}."
+ $" Reads are only allowed if {nameof(PinMode)} is set to {GpioPinDriveMode.Input} or {GpioPinDriveMode.Output}");
$"Unable to read from pin {this.PinNumber} because operating mode is {this.PinMode}."
+ $" Reads are only allowed if {nameof(this.PinMode)} is set to {GpioPinDriveMode.Input} or {GpioPinDriveMode.Output}");
}
return WiringPi.DigitalRead(PinNumber) != 0;
return WiringPi.DigitalRead(this.PinNumber) != 0;
}
}
@@ -551,20 +515,20 @@
/// Reads the digital value on the pin as a boolean value.
/// </summary>
/// <returns>The state of the pin</returns>
public Task<bool> ReadAsync() => Task.Run(() => Read());
public Task<Boolean> ReadAsync() => Task.Run(() => this.Read());
/// <summary>
/// Reads the digital value on the pin as a High or Low value.
/// </summary>
/// <returns>The state of the pin</returns>
public GpioPinValue ReadValue()
=> Read() ? GpioPinValue.High : GpioPinValue.Low;
=> this.Read() ? GpioPinValue.High : GpioPinValue.Low;
/// <summary>
/// Reads the digital value on the pin as a High or Low value.
/// </summary>
/// <returns>The state of the pin</returns>
public Task<GpioPinValue> ReadValueAsync() => Task.Run(() => ReadValue());
public Task<GpioPinValue> ReadValueAsync() => Task.Run(() => this.ReadValue());
/// <summary>
/// Reads the analog value on the pin.
@@ -574,18 +538,15 @@
/// </summary>
/// <returns>The analog level</returns>
/// <exception cref="InvalidOperationException">When the pin mode is not configured as an input.</exception>
public int ReadLevel()
{
lock (_syncLock)
{
if (PinMode != GpioPinDriveMode.Input)
{
public Int32 ReadLevel() {
lock(this._syncLock) {
if(this.PinMode != GpioPinDriveMode.Input) {
throw new InvalidOperationException(
$"Unable to read from pin {PinNumber} because operating mode is {PinMode}."
+ $" Reads are only allowed if {nameof(PinMode)} is set to {GpioPinDriveMode.Input}");
$"Unable to read from pin {this.PinNumber} because operating mode is {this.PinMode}."
+ $" Reads are only allowed if {nameof(this.PinMode)} is set to {GpioPinDriveMode.Input}");
}
return WiringPi.AnalogRead(PinNumber);
return WiringPi.AnalogRead(this.PinNumber);
}
}
@@ -596,7 +557,7 @@
/// quick2Wire analog board, etc.
/// </summary>
/// <returns>The analog level</returns>
public Task<int> ReadLevelAsync() => Task.Run(() => ReadLevel());
public Task<Int32> ReadLevelAsync() => Task.Run(() => this.ReadLevel());
#endregion
@@ -613,31 +574,27 @@
/// or
/// RegisterInterruptCallback
/// </exception>
public void RegisterInterruptCallback(EdgeDetection edgeDetection, InterruptServiceRoutineCallback callback)
{
if (callback == null)
public void RegisterInterruptCallback(EdgeDetection edgeDetection, InterruptServiceRoutineCallback callback) {
if(callback == null) {
throw new ArgumentException($"{nameof(callback)} cannot be null");
}
if (InterruptCallback != null)
if(this.InterruptCallback != null) {
throw new InvalidOperationException("An interrupt callback was already registered.");
}
if (PinMode != GpioPinDriveMode.Input)
{
if(this.PinMode != GpioPinDriveMode.Input) {
throw new InvalidOperationException(
$"Unable to {nameof(RegisterInterruptCallback)} for pin {PinNumber} because operating mode is {PinMode}."
+ $" Calling {nameof(RegisterInterruptCallback)} is only allowed if {nameof(PinMode)} is set to {GpioPinDriveMode.Input}");
$"Unable to {nameof(RegisterInterruptCallback)} for pin {this.PinNumber} because operating mode is {this.PinMode}."
+ $" Calling {nameof(RegisterInterruptCallback)} is only allowed if {nameof(this.PinMode)} is set to {GpioPinDriveMode.Input}");
}
lock (_syncLock)
{
var registerResult = WiringPi.WiringPiISR(PinNumber, (int)edgeDetection, callback);
if (registerResult == 0)
{
InterruptEdgeDetection = edgeDetection;
InterruptCallback = callback;
}
else
{
lock(this._syncLock) {
Int32 registerResult = WiringPi.WiringPiISR(this.PinNumber, (Int32)edgeDetection, callback);
if(registerResult == 0) {
this.InterruptEdgeDetection = edgeDetection;
this.InterruptCallback = callback;
} else {
HardwareException.Throw(nameof(GpioPin), nameof(RegisterInterruptCallback));
}
}
+28 -34
View File
@@ -1,32 +1,30 @@
namespace Unosquare.RaspberryIO.Gpio
{
using Native;
using Swan.Abstractions;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using Unosquare.RaspberryIO.Native;
using Unosquare.Swan.Abstractions;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System;
namespace Unosquare.RaspberryIO.Gpio {
/// <summary>
/// A simple wrapper for the I2c bus on the Raspberry Pi
/// </summary>
public class I2CBus : SingletonBase<I2CBus>
{
public class I2CBus : SingletonBase<I2CBus> {
// TODO: It would be nice to integrate i2c device detection.
private static readonly object SyncRoot = new object();
private readonly Dictionary<int, I2CDevice> _devices = new Dictionary<int, I2CDevice>();
private static readonly Object SyncRoot = new Object();
private readonly Dictionary<Int32, I2CDevice> _devices = new Dictionary<Int32, I2CDevice>();
/// <summary>
/// Prevents a default instance of the <see cref="I2CBus"/> class from being created.
/// </summary>
private I2CBus()
{
private I2CBus() {
// placeholder
}
/// <summary>
/// Gets the registered devices as a read only collection.
/// </summary>
public ReadOnlyCollection<I2CDevice> Devices => new ReadOnlyCollection<I2CDevice>(_devices.Values.ToArray());
public ReadOnlyCollection<I2CDevice> Devices => new ReadOnlyCollection<I2CDevice>(this._devices.Values.ToArray());
/// <summary>
/// Gets the <see cref="I2CDevice"/> with the specified device identifier.
@@ -36,18 +34,16 @@
/// </value>
/// <param name="deviceId">The device identifier.</param>
/// <returns>A reference to an I2C device</returns>
public I2CDevice this[int deviceId] => GetDeviceById(deviceId);
public I2CDevice this[Int32 deviceId] => this.GetDeviceById(deviceId);
/// <summary>
/// Gets the device by identifier.
/// </summary>
/// <param name="deviceId">The device identifier.</param>
/// <returns>The device reference</returns>
public I2CDevice GetDeviceById(int deviceId)
{
lock (SyncRoot)
{
return _devices[deviceId];
public I2CDevice GetDeviceById(Int32 deviceId) {
lock(SyncRoot) {
return this._devices[deviceId];
}
}
@@ -57,19 +53,19 @@
/// <param name="deviceId">The device identifier.</param>
/// <returns>The device reference</returns>
/// <exception cref="KeyNotFoundException">When the device file descriptor is not found</exception>
public I2CDevice AddDevice(int deviceId)
{
lock (SyncRoot)
{
if (_devices.ContainsKey(deviceId))
return _devices[deviceId];
public I2CDevice AddDevice(Int32 deviceId) {
lock(SyncRoot) {
if(this._devices.ContainsKey(deviceId)) {
return this._devices[deviceId];
}
var fileDescriptor = SetupFileDescriptor(deviceId);
if (fileDescriptor < 0)
Int32 fileDescriptor = SetupFileDescriptor(deviceId);
if(fileDescriptor < 0) {
throw new KeyNotFoundException($"Device with id {deviceId} could not be registered with the I2C bus. Error Code: {fileDescriptor}.");
}
var device = new I2CDevice(deviceId, fileDescriptor);
_devices[deviceId] = device;
I2CDevice device = new I2CDevice(deviceId, fileDescriptor);
this._devices[deviceId] = device;
return device;
}
}
@@ -82,10 +78,8 @@
/// </summary>
/// <param name="deviceId">The device identifier.</param>
/// <returns>The Linux file handle</returns>
private static int SetupFileDescriptor(int deviceId)
{
lock (SyncRoot)
{
private static Int32 SetupFileDescriptor(Int32 deviceId) {
lock(SyncRoot) {
return WiringPi.WiringPiI2CSetup(deviceId);
}
}
+75 -77
View File
@@ -1,25 +1,22 @@
namespace Unosquare.RaspberryIO.Gpio
{
using System;
using System.Threading.Tasks;
using Native;
using System;
using System.Threading.Tasks;
using Unosquare.RaspberryIO.Native;
namespace Unosquare.RaspberryIO.Gpio {
/// <summary>
/// Represents a device on the I2C Bus
/// </summary>
public class I2CDevice
{
private readonly object _syncLock = new object();
public class I2CDevice {
private readonly Object _syncLock = new Object();
/// <summary>
/// Initializes a new instance of the <see cref="I2CDevice"/> class.
/// </summary>
/// <param name="deviceId">The device identifier.</param>
/// <param name="fileDescriptor">The file descriptor.</param>
internal I2CDevice(int deviceId, int fileDescriptor)
{
DeviceId = deviceId;
FileDescriptor = fileDescriptor;
internal I2CDevice(Int32 deviceId, Int32 fileDescriptor) {
this.DeviceId = deviceId;
this.FileDescriptor = fileDescriptor;
}
/// <summary>
@@ -28,7 +25,9 @@
/// <value>
/// The device identifier.
/// </value>
public int DeviceId { get; }
public Int32 DeviceId {
get;
}
/// <summary>
/// Gets the standard POSIX file descriptor.
@@ -36,19 +35,22 @@
/// <value>
/// The file descriptor.
/// </value>
public int FileDescriptor { get; }
public Int32 FileDescriptor {
get;
}
/// <summary>
/// Reads a byte from the specified file descriptor
/// </summary>
/// <returns>The byte from device</returns>
public byte Read()
{
lock (_syncLock)
{
var result = WiringPi.WiringPiI2CRead(FileDescriptor);
if (result < 0) HardwareException.Throw(nameof(I2CDevice), nameof(Read));
return (byte)result;
public Byte Read() {
lock(this._syncLock) {
Int32 result = WiringPi.WiringPiI2CRead(this.FileDescriptor);
if(result < 0) {
HardwareException.Throw(nameof(I2CDevice), nameof(Read));
}
return (Byte)result;
}
}
@@ -56,23 +58,23 @@
/// Reads a byte from the specified file descriptor
/// </summary>
/// <returns>The byte from device</returns>
public Task<byte> ReadAsync() => Task.Run(() => Read());
public Task<Byte> ReadAsync() => Task.Run(() => this.Read());
/// <summary>
/// Reads a buffer of the specified length, one byte at a time
/// </summary>
/// <param name="length">The length.</param>
/// <returns>The byte array from device</returns>
public byte[] Read(int length)
{
lock (_syncLock)
{
var buffer = new byte[length];
for (var i = 0; i < length; i++)
{
var result = WiringPi.WiringPiI2CRead(FileDescriptor);
if (result < 0) HardwareException.Throw(nameof(I2CDevice), nameof(Read));
buffer[i] = (byte)result;
public Byte[] Read(Int32 length) {
lock(this._syncLock) {
Byte[] buffer = new Byte[length];
for(Int32 i = 0; i < length; i++) {
Int32 result = WiringPi.WiringPiI2CRead(this.FileDescriptor);
if(result < 0) {
HardwareException.Throw(nameof(I2CDevice), nameof(Read));
}
buffer[i] = (Byte)result;
}
return buffer;
@@ -84,18 +86,18 @@
/// </summary>
/// <param name="length">The length.</param>
/// <returns>The byte array from device</returns>
public Task<byte[]> ReadAsync(int length) => Task.Run(() => Read(length));
public Task<Byte[]> ReadAsync(Int32 length) => Task.Run(() => this.Read(length));
/// <summary>
/// Writes a byte of data the specified file descriptor.
/// </summary>
/// <param name="data">The data.</param>
public void Write(byte data)
{
lock (_syncLock)
{
var result = WiringPi.WiringPiI2CWrite(FileDescriptor, data);
if (result < 0) HardwareException.Throw(nameof(I2CDevice), nameof(Write));
public void Write(Byte data) {
lock(this._syncLock) {
Int32 result = WiringPi.WiringPiI2CWrite(this.FileDescriptor, data);
if(result < 0) {
HardwareException.Throw(nameof(I2CDevice), nameof(Write));
}
}
}
@@ -104,20 +106,19 @@
/// </summary>
/// <param name="data">The data.</param>
/// <returns>The awaitable task</returns>
public Task WriteAsync(byte data) => Task.Run(() => { Write(data); });
public Task WriteAsync(Byte data) => Task.Run(() => this.Write(data));
/// <summary>
/// Writes a set of bytes to the specified file descriptor.
/// </summary>
/// <param name="data">The data.</param>
public void Write(byte[] data)
{
lock (_syncLock)
{
foreach (var b in data)
{
var result = WiringPi.WiringPiI2CWrite(FileDescriptor, b);
if (result < 0) HardwareException.Throw(nameof(I2CDevice), nameof(Write));
public void Write(Byte[] data) {
lock(this._syncLock) {
foreach(Byte b in data) {
Int32 result = WiringPi.WiringPiI2CWrite(this.FileDescriptor, b);
if(result < 0) {
HardwareException.Throw(nameof(I2CDevice), nameof(Write));
}
}
}
}
@@ -127,22 +128,19 @@
/// </summary>
/// <param name="data">The data.</param>
/// <returns>The awaitable task</returns>
public Task WriteAsync(byte[] data)
{
return Task.Run(() => { Write(data); });
}
public Task WriteAsync(Byte[] data) => Task.Run(() => this.Write(data));
/// <summary>
/// These write an 8 or 16-bit data value into the device register indicated.
/// </summary>
/// <param name="address">The register.</param>
/// <param name="data">The data.</param>
public void WriteAddressByte(int address, byte data)
{
lock (_syncLock)
{
var result = WiringPi.WiringPiI2CWriteReg8(FileDescriptor, address, data);
if (result < 0) HardwareException.Throw(nameof(I2CDevice), nameof(WriteAddressByte));
public void WriteAddressByte(Int32 address, Byte data) {
lock(this._syncLock) {
Int32 result = WiringPi.WiringPiI2CWriteReg8(this.FileDescriptor, address, data);
if(result < 0) {
HardwareException.Throw(nameof(I2CDevice), nameof(WriteAddressByte));
}
}
}
@@ -151,12 +149,12 @@
/// </summary>
/// <param name="address">The register.</param>
/// <param name="data">The data.</param>
public void WriteAddressWord(int address, ushort data)
{
lock (_syncLock)
{
var result = WiringPi.WiringPiI2CWriteReg16(FileDescriptor, address, data);
if (result < 0) HardwareException.Throw(nameof(I2CDevice), nameof(WriteAddressWord));
public void WriteAddressWord(Int32 address, UInt16 data) {
lock(this._syncLock) {
Int32 result = WiringPi.WiringPiI2CWriteReg16(this.FileDescriptor, address, data);
if(result < 0) {
HardwareException.Throw(nameof(I2CDevice), nameof(WriteAddressWord));
}
}
}
@@ -165,14 +163,14 @@
/// </summary>
/// <param name="address">The register.</param>
/// <returns>The address byte from device</returns>
public byte ReadAddressByte(int address)
{
lock (_syncLock)
{
var result = WiringPi.WiringPiI2CReadReg8(FileDescriptor, address);
if (result < 0) HardwareException.Throw(nameof(I2CDevice), nameof(ReadAddressByte));
public Byte ReadAddressByte(Int32 address) {
lock(this._syncLock) {
Int32 result = WiringPi.WiringPiI2CReadReg8(this.FileDescriptor, address);
if(result < 0) {
HardwareException.Throw(nameof(I2CDevice), nameof(ReadAddressByte));
}
return (byte)result;
return (Byte)result;
}
}
@@ -181,12 +179,12 @@
/// </summary>
/// <param name="address">The register.</param>
/// <returns>The address word from device</returns>
public ushort ReadAddressWord(int address)
{
lock (_syncLock)
{
var result = WiringPi.WiringPiI2CReadReg16(FileDescriptor, address);
if (result < 0) HardwareException.Throw(nameof(I2CDevice), nameof(ReadAddressWord));
public UInt16 ReadAddressWord(Int32 address) {
lock(this._syncLock) {
Int32 result = WiringPi.WiringPiI2CReadReg16(this.FileDescriptor, address);
if(result < 0) {
HardwareException.Throw(nameof(I2CDevice), nameof(ReadAddressWord));
}
return Convert.ToUInt16(result);
}
+23 -23
View File
@@ -1,17 +1,15 @@
namespace Unosquare.RaspberryIO.Gpio
{
using Swan.Abstractions;
using System;
using Unosquare.Swan.Abstractions;
namespace Unosquare.RaspberryIO.Gpio {
/// <summary>
/// The SPI Bus containing the 2 SPI channels
/// </summary>
public class SpiBus : SingletonBase<SpiBus>
{
public class SpiBus : SingletonBase<SpiBus> {
/// <summary>
/// Prevents a default instance of the <see cref="SpiBus"/> class from being created.
/// </summary>
private SpiBus()
{
private SpiBus() {
// placeholder
}
@@ -23,7 +21,9 @@
/// <value>
/// The channel0 frequency.
/// </value>
public int Channel0Frequency { get; set; }
public Int32 Channel0Frequency {
get; set;
}
/// <summary>
/// Gets the SPI bus on channel 1.
@@ -31,14 +31,13 @@
/// <value>
/// The channel0.
/// </value>
public SpiChannel Channel0
{
get
{
if (Channel0Frequency == 0)
Channel0Frequency = SpiChannel.DefaultFrequency;
public SpiChannel Channel0 {
get {
if(this.Channel0Frequency == 0) {
this.Channel0Frequency = SpiChannel.DefaultFrequency;
}
return SpiChannel.Retrieve(SpiChannelNumber.Channel0, Channel0Frequency);
return SpiChannel.Retrieve(SpiChannelNumber.Channel0, this.Channel0Frequency);
}
}
@@ -48,7 +47,9 @@
/// <value>
/// The channel1 frequency.
/// </value>
public int Channel1Frequency { get; set; }
public Int32 Channel1Frequency {
get; set;
}
/// <summary>
/// Gets the SPI bus on channel 1.
@@ -56,14 +57,13 @@
/// <value>
/// The channel1.
/// </value>
public SpiChannel Channel1
{
get
{
if (Channel1Frequency == 0)
Channel1Frequency = SpiChannel.DefaultFrequency;
public SpiChannel Channel1 {
get {
if(this.Channel1Frequency == 0) {
this.Channel1Frequency = SpiChannel.DefaultFrequency;
}
return SpiChannel.Retrieve(SpiChannelNumber.Channel1, Channel1Frequency);
return SpiChannel.Retrieve(SpiChannelNumber.Channel1, this.Channel1Frequency);
}
}
+48 -48
View File
@@ -1,53 +1,48 @@
namespace Unosquare.RaspberryIO.Gpio
{
using Native;
using Swan;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Unosquare.RaspberryIO.Native;
using Unosquare.Swan;
using System;
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
{
public sealed class SpiChannel {
/// <summary>
/// The minimum frequency of an SPI Channel
/// </summary>
public const int MinFrequency = 500000;
public const Int32 MinFrequency = 500000;
/// <summary>
/// The maximum frequency of an SPI channel
/// </summary>
public const int MaxFrequency = 32000000;
public const Int32 MaxFrequency = 32000000;
/// <summary>
/// The default frequency of SPI channels
/// This is set to 8 Mhz wich is typical in modern hardware.
/// </summary>
public const int DefaultFrequency = 8000000;
public const Int32 DefaultFrequency = 8000000;
private static readonly object SyncRoot = new object();
private static readonly Object SyncRoot = new Object();
private static readonly Dictionary<SpiChannelNumber, SpiChannel> Buses = new Dictionary<SpiChannelNumber, SpiChannel>();
private readonly object _syncLock = new object();
private readonly Object _syncLock = new Object();
/// <summary>
/// Initializes a new instance of the <see cref="SpiChannel"/> class.
/// </summary>
/// <param name="channel">The channel.</param>
/// <param name="frequency">The frequency.</param>
private SpiChannel(SpiChannelNumber channel, int frequency)
{
lock (SyncRoot)
{
Frequency = frequency.Clamp(MinFrequency, MaxFrequency);
Channel = (int)channel;
FileDescriptor = WiringPi.WiringPiSPISetup((int)channel, Frequency);
private SpiChannel(SpiChannelNumber channel, Int32 frequency) {
lock(SyncRoot) {
this.Frequency = frequency.Clamp(MinFrequency, MaxFrequency);
this.Channel = (Int32)channel;
this.FileDescriptor = WiringPi.WiringPiSPISetup((Int32)channel, this.Frequency);
if (FileDescriptor < 0)
{
if(this.FileDescriptor < 0) {
HardwareException.Throw(nameof(SpiChannel), channel.ToString());
}
}
@@ -60,35 +55,42 @@
/// <value>
/// The file descriptor.
/// </value>
public int FileDescriptor { get; }
public Int32 FileDescriptor {
get;
}
/// <summary>
/// Gets the channel.
/// </summary>
public int Channel { get; }
public Int32 Channel {
get;
}
/// <summary>
/// Gets the frequency.
/// </summary>
public int Frequency { get; }
public Int32 Frequency {
get;
}
/// <summary>
/// Sends data and simultaneously receives the data in the return buffer
/// </summary>
/// <param name="buffer">The buffer.</param>
/// <returns>The read bytes from the ring-style bus</returns>
public byte[] SendReceive(byte[] buffer)
{
if (buffer == null || buffer.Length == 0)
public Byte[] SendReceive(Byte[] buffer) {
if(buffer == null || buffer.Length == 0) {
return null;
}
lock (_syncLock)
{
var spiBuffer = new byte[buffer.Length];
lock(this._syncLock) {
Byte[] spiBuffer = new Byte[buffer.Length];
Array.Copy(buffer, spiBuffer, buffer.Length);
var result = WiringPi.WiringPiSPIDataRW(Channel, spiBuffer, spiBuffer.Length);
if (result < 0) HardwareException.Throw(nameof(SpiChannel), nameof(SendReceive));
Int32 result = WiringPi.WiringPiSPIDataRW(this.Channel, spiBuffer, spiBuffer.Length);
if(result < 0) {
HardwareException.Throw(nameof(SpiChannel), nameof(SendReceive));
}
return spiBuffer;
}
@@ -101,7 +103,7 @@
/// <returns>
/// The read bytes from the ring-style bus
/// </returns>
public Task<byte[]> SendReceiveAsync(byte[] buffer) => Task.Run(() => SendReceive(buffer));
public Task<Byte[]> SendReceiveAsync(Byte[] buffer) => Task.Run(() => this.SendReceive(buffer));
/// <summary>
/// Writes the specified buffer the the underlying FileDescriptor.
@@ -110,16 +112,15 @@
/// like sending data over to those long RGB LED strips
/// </summary>
/// <param name="buffer">The buffer.</param>
public void Write(byte[] buffer)
{
lock (_syncLock)
{
var result = Standard.Write(FileDescriptor, buffer, buffer.Length);
public void Write(Byte[] buffer) {
lock(this._syncLock) {
Int32 result = Standard.Write(this.FileDescriptor, buffer, buffer.Length);
if (result < 0)
if(result < 0) {
HardwareException.Throw(nameof(SpiChannel), nameof(Write));
}
}
}
/// <summary>
/// Writes the specified buffer the the underlying FileDescriptor.
@@ -129,7 +130,7 @@
/// </summary>
/// <param name="buffer">The buffer.</param>
/// <returns>The awaitable task</returns>
public Task WriteAsync(byte[] buffer) => Task.Run(() => { Write(buffer); });
public Task WriteAsync(Byte[] buffer) => Task.Run(() => this.Write(buffer));
/// <summary>
/// Retrieves the spi bus. If the bus channel is not registered it sets it up automatically.
@@ -138,14 +139,13 @@
/// <param name="channel">The channel.</param>
/// <param name="frequency">The frequency.</param>
/// <returns>The usable SPI channel</returns>
internal static SpiChannel Retrieve(SpiChannelNumber channel, int frequency)
{
lock (SyncRoot)
{
if (Buses.ContainsKey(channel))
internal static SpiChannel Retrieve(SpiChannelNumber channel, Int32 frequency) {
lock(SyncRoot) {
if(Buses.ContainsKey(channel)) {
return Buses[channel];
}
var newBus = new SpiChannel(channel, frequency);
SpiChannel newBus = new SpiChannel(channel, frequency);
Buses[channel] = newBus;
return newBus;
}
+1 -2
View File
@@ -1,5 +1,4 @@
namespace Unosquare.RaspberryIO.Native
{
namespace Unosquare.RaspberryIO.Native {
/// <summary>
/// A delegate defining a callback for an Interrupt Service Routine
/// </summary>
@@ -1,38 +1,32 @@
namespace Unosquare.RaspberryIO.Native
{
using Swan;
using System;
using System.Runtime.InteropServices;
using Unosquare.Swan;
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
{
public class HardwareException : Exception {
/// <summary>
/// Initializes a new instance of the <see cref="HardwareException" /> class.
/// </summary>
/// <param name="errorCode">The error code.</param>
/// <param name="component">The component.</param>
public HardwareException(int errorCode, string component)
: base($"A hardware exception occurred. Error Code: {errorCode}")
{
ExtendedMessage = null;
public HardwareException(Int32 errorCode, String component)
: base($"A hardware exception occurred. Error Code: {errorCode}") {
this.ExtendedMessage = null;
try
{
ExtendedMessage = Standard.Strerror(errorCode);
}
catch
{
try {
this.ExtendedMessage = Standard.Strerror(errorCode);
} catch {
// TODO: strerror not working great...
$"Could not retrieve native error description using {nameof(Standard.Strerror)}".Error(Pi.LoggerSource);
}
ErrorCode = errorCode;
Component = component;
this.ErrorCode = errorCode;
this.Component = component;
}
/// <summary>
@@ -41,7 +35,9 @@
/// <value>
/// The error code.
/// </value>
public int ErrorCode { get; }
public Int32 ErrorCode {
get;
}
/// <summary>
/// Gets the component.
@@ -49,7 +45,9 @@
/// <value>
/// The component.
/// </value>
public string Component { get; }
public String Component {
get;
}
/// <summary>
/// Gets the extended message (could be null).
@@ -57,7 +55,9 @@
/// <value>
/// The extended message.
/// </value>
public string ExtendedMessage { get; }
public String ExtendedMessage {
get;
}
/// <summary>
/// Throws a new instance of a hardware error by retrieving the last error number (errno).
@@ -65,9 +65,9 @@
/// <param name="className">Name of the class.</param>
/// <param name="methodName">Name of the method.</param>
/// <exception cref="HardwareException">When an error thrown by an API call occurs</exception>
public static void Throw(string className, string methodName) => throw new HardwareException(Marshal.GetLastWin32Error(), $"{className}.{methodName}");
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}";
public override String ToString() => $"{this.GetType()}{(String.IsNullOrWhiteSpace(this.Component) ? String.Empty : $" on {this.Component}")}: ({this.ErrorCode}) - {this.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
{
public class HighResolutionTimer : Stopwatch {
/// <summary>
/// Initializes a new instance of the <see cref="HighResolutionTimer"/> class.
/// </summary>
/// <exception cref="NotSupportedException">High-resolution timer not available</exception>
public HighResolutionTimer()
{
if (!IsHighResolution)
public HighResolutionTimer() {
if(!IsHighResolution) {
throw new NotSupportedException("High-resolution timer not available");
}
}
/// <summary>
/// Gets the numer of microseconds per timer tick.
/// </summary>
public static double MicrosecondsPerTick { get; } = 1000000d / Frequency;
public static Double MicrosecondsPerTick { get; } = 1000000d / Frequency;
/// <summary>
/// Gets the elapsed microseconds.
/// </summary>
public long ElapsedMicroseconds => (long)(ElapsedTicks * MicrosecondsPerTick);
public Int64 ElapsedMicroseconds => (Int64)(this.ElapsedTicks * MicrosecondsPerTick);
}
}
+22 -26
View File
@@ -1,16 +1,14 @@
namespace Unosquare.RaspberryIO.Native
{
using Swan;
using System;
using System.Runtime.InteropServices;
using System.Text;
using Unosquare.Swan;
using System;
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";
internal static class Standard {
internal const String LibCLibrary = "libc";
#region LibC Calls
@@ -19,18 +17,16 @@
/// </summary>
/// <param name="error">The error.</param>
/// <returns></returns>
public static string Strerror(int error)
{
if (!Runtime.IsUsingMonoRuntime) return StrError(error);
try
{
var buffer = new StringBuilder(256);
var result = Strerror(error, buffer, (ulong)buffer.Capacity);
return (result != -1) ? buffer.ToString() : null;
public static String Strerror(Int32 error) {
if(!Runtime.IsUsingMonoRuntime) {
return StrError(error);
}
catch (EntryPointNotFoundException)
{
try {
StringBuilder buffer = new StringBuilder(256);
Int32 result = Strerror(error, buffer, (UInt64)buffer.Capacity);
return (result != -1) ? buffer.ToString() : null;
} catch(EntryPointNotFoundException) {
return null;
}
}
@@ -42,7 +38,7 @@
/// <param name="mode">The mode.</param>
/// <returns>The result</returns>
[DllImport(LibCLibrary, EntryPoint = "chmod", SetLastError = true)]
public static extern int Chmod(string filename, uint mode);
public static extern Int32 Chmod(String filename, UInt32 mode);
/// <summary>
/// Converts a string to a 32 bit integer. Use endpointer as IntPtr.Zero
@@ -52,7 +48,7 @@
/// <param name="numberBase">The number base.</param>
/// <returns>The result</returns>
[DllImport(LibCLibrary, EntryPoint = "strtol", SetLastError = true)]
public static extern int StringToInteger(string numberString, IntPtr endPointer, int numberBase);
public static extern Int32 StringToInteger(String numberString, IntPtr endPointer, Int32 numberBase);
/// <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.
@@ -63,7 +59,7 @@
/// <param name="count">The count.</param>
/// <returns>The result</returns>
[DllImport(LibCLibrary, EntryPoint = "write", SetLastError = true)]
public static extern int Write(int fd, byte[] buffer, int count);
public static extern Int32 Write(Int32 fd, Byte[] buffer, Int32 count);
/// <summary>
/// Fills in the structure with information about the system.
@@ -71,13 +67,13 @@
/// <param name="name">The name.</param>
/// <returns>The result</returns>
[DllImport(LibCLibrary, EntryPoint = "uname", SetLastError = true)]
public static extern int Uname(out SystemName name);
public static extern Int32 Uname(out SystemName name);
[DllImport(LibCLibrary, EntryPoint = "strerror", SetLastError = true)]
private static extern string StrError(int errnum);
private static extern String StrError(Int32 errnum);
[DllImport("MonoPosixHelper", EntryPoint = "Mono_Posix_Syscall_strerror_r", SetLastError = true)]
private static extern int Strerror(int error, [Out] StringBuilder buffer, ulong length);
private static extern Int32 Strerror(Int32 error, [Out] StringBuilder buffer, UInt64 length);
#endregion
}
+10 -11
View File
@@ -1,47 +1,46 @@
namespace Unosquare.RaspberryIO.Native
{
using System.Runtime.InteropServices;
using System;
using System.Runtime.InteropServices;
namespace Unosquare.RaspberryIO.Native {
/// <summary>
/// OS uname structure
/// </summary>
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
internal struct SystemName
{
internal struct SystemName {
/// <summary>
/// System name
/// </summary>
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 65)]
public string SysName;
public String SysName;
/// <summary>
/// Node name
/// </summary>
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 65)]
public string NodeName;
public String NodeName;
/// <summary>
/// Release level
/// </summary>
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 65)]
public string Release;
public String Release;
/// <summary>
/// Version level
/// </summary>
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 65)]
public string Version;
public String Version;
/// <summary>
/// Hardware level
/// </summary>
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 65)]
public string Machine;
public String Machine;
/// <summary>
/// Domain name
/// </summary>
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 65)]
public string DomainName;
public String DomainName;
}
}
@@ -1,10 +1,8 @@
namespace Unosquare.RaspberryIO.Native
{
namespace Unosquare.RaspberryIO.Native {
/// <summary>
/// Defines the different threading locking keys
/// </summary>
public enum ThreadLockKey
{
public enum ThreadLockKey {
/// <summary>
/// The lock 0
/// </summary>
+24 -24
View File
@@ -1,20 +1,17 @@
namespace Unosquare.RaspberryIO.Native
{
using Swan;
using Swan.Abstractions;
using System;
using Unosquare.Swan;
using Unosquare.Swan.Abstractions;
using System;
namespace Unosquare.RaspberryIO.Native {
/// <summary>
/// Provides access to timing and threading properties and methods
/// </summary>
public class Timing : SingletonBase<Timing>
{
public class Timing : SingletonBase<Timing> {
/// <summary>
/// Prevents a default instance of the <see cref="Timing"/> class from being created.
/// </summary>
/// <exception cref="NotSupportedException">Could not initialize the GPIO controller</exception>
private Timing()
{
private Timing() {
// placeholder
}
@@ -26,7 +23,7 @@
/// <value>
/// The milliseconds since setup.
/// </value>
public uint MillisecondsSinceSetup => WiringPi.Millis();
public UInt32 MillisecondsSinceSetup => WiringPi.Millis();
/// <summary>
/// This returns a number representing the number of microseconds since your
@@ -36,7 +33,7 @@
/// <value>
/// The microseconds since setup.
/// </value>
public uint MicrosecondsSinceSetup => WiringPi.Micros();
public UInt32 MicrosecondsSinceSetup => WiringPi.Micros();
/// <summary>
/// This causes program execution to pause for at least howLong milliseconds.
@@ -44,7 +41,7 @@
/// Note that the maximum delay is an unsigned 32-bit integer or approximately 49 days.
/// </summary>
/// <param name="value">The value.</param>
public static void SleepMilliseconds(uint value) => WiringPi.Delay(value);
public static void SleepMilliseconds(UInt32 value) => WiringPi.Delay(value);
/// <summary>
/// This causes program execution to pause for at least howLong microseconds.
@@ -56,7 +53,7 @@
/// especially if using threads.
/// </summary>
/// <param name="value">The value.</param>
public void SleepMicroseconds(uint value) => WiringPi.DelayMicroseconds(value);
public void SleepMicroseconds(UInt32 value) => WiringPi.DelayMicroseconds(value);
/// <summary>
/// This attempts to shift your program (or thread in a multi-threaded program) to a higher priority and
@@ -67,11 +64,12 @@
/// (as long as no other programs are running with elevated priorities)
/// </summary>
/// <param name="priority">The priority.</param>
public void SetThreadPriority(int priority)
{
public void SetThreadPriority(Int32 priority) {
priority = priority.Clamp(0, 99);
var result = WiringPi.PiHiPri(priority);
if (result < 0) HardwareException.Throw(nameof(Timing), nameof(SetThreadPriority));
Int32 result = WiringPi.PiHiPri(priority);
if(result < 0) {
HardwareException.Throw(nameof(Timing), nameof(SetThreadPriority));
}
}
/// <summary>
@@ -80,13 +78,15 @@
/// </summary>
/// <param name="worker">The worker.</param>
/// <exception cref="ArgumentNullException">worker</exception>
public void CreateThread(ThreadWorker worker)
{
if (worker == null)
public void CreateThread(ThreadWorker worker) {
if(worker == null) {
throw new ArgumentNullException(nameof(worker));
}
var result = WiringPi.PiThreadCreate(worker);
if (result != 0) HardwareException.Throw(nameof(Timing), nameof(CreateThread));
Int32 result = WiringPi.PiThreadCreate(worker);
if(result != 0) {
HardwareException.Throw(nameof(Timing), nameof(CreateThread));
}
}
/// <summary>
@@ -95,7 +95,7 @@
/// it will be stalled until the first process has unlocked the same key.
/// </summary>
/// <param name="key">The key.</param>
public void Lock(ThreadLockKey key) => WiringPi.PiLock((int)key);
public void Lock(ThreadLockKey key) => WiringPi.PiLock((Int32)key);
/// <summary>
/// These allow you to synchronize variable updates from your main program to any threads running in your program.
@@ -103,6 +103,6 @@
/// it will be stalled until the first process has unlocked the same key.
/// </summary>
/// <param name="key">The key.</param>
public void Unlock(ThreadLockKey key) => WiringPi.PiUnlock((int)key);
public void Unlock(ThreadLockKey key) => WiringPi.PiUnlock((Int32)key);
}
}
+11 -12
View File
@@ -1,9 +1,8 @@
namespace Unosquare.RaspberryIO.Native
{
using System.Runtime.InteropServices;
using System;
using System.Runtime.InteropServices;
public partial class WiringPi
{
namespace Unosquare.RaspberryIO.Native {
public partial class WiringPi {
#region WiringPi - I2C Library Calls
/// <summary>
@@ -12,7 +11,7 @@
/// <param name="fd">The fd.</param>
/// <returns>The result</returns>
[DllImport(WiringPiLibrary, EntryPoint = "wiringPiI2CRead", SetLastError = true)]
public static extern int WiringPiI2CRead(int fd);
public static extern Int32 WiringPiI2CRead(Int32 fd);
/// <summary>
/// These read an 8-bit value from the device register indicated.
@@ -21,7 +20,7 @@
/// <param name="reg">The reg.</param>
/// <returns>The result</returns>
[DllImport(WiringPiLibrary, EntryPoint = "wiringPiI2CReadReg8", SetLastError = true)]
public static extern int WiringPiI2CReadReg8(int fd, int reg);
public static extern Int32 WiringPiI2CReadReg8(Int32 fd, Int32 reg);
/// <summary>
/// These read a 16-bit value from the device register indicated.
@@ -30,7 +29,7 @@
/// <param name="reg">The reg.</param>
/// <returns>The result</returns>
[DllImport(WiringPiLibrary, EntryPoint = "wiringPiI2CReadReg16", SetLastError = true)]
public static extern int WiringPiI2CReadReg16(int fd, int reg);
public static extern Int32 WiringPiI2CReadReg16(Int32 fd, Int32 reg);
/// <summary>
/// Simple device write. Some devices accept data this way without needing to access any internal registers.
@@ -39,7 +38,7 @@
/// <param name="data">The data.</param>
/// <returns>The result</returns>
[DllImport(WiringPiLibrary, EntryPoint = "wiringPiI2CWrite", SetLastError = true)]
public static extern int WiringPiI2CWrite(int fd, int data);
public static extern Int32 WiringPiI2CWrite(Int32 fd, Int32 data);
/// <summary>
/// These write an 8-bit data value into the device register indicated.
@@ -49,7 +48,7 @@
/// <param name="data">The data.</param>
/// <returns>The result</returns>
[DllImport(WiringPiLibrary, EntryPoint = "wiringPiI2CWriteReg8", SetLastError = true)]
public static extern int WiringPiI2CWriteReg8(int fd, int reg, int data);
public static extern Int32 WiringPiI2CWriteReg8(Int32 fd, Int32 reg, Int32 data);
/// <summary>
/// These write a 16-bit data value into the device register indicated.
@@ -59,7 +58,7 @@
/// <param name="data">The data.</param>
/// <returns>The result</returns>
[DllImport(WiringPiLibrary, EntryPoint = "wiringPiI2CWriteReg16", SetLastError = true)]
public static extern int WiringPiI2CWriteReg16(int fd, int reg, int data);
public static extern Int32 WiringPiI2CWriteReg16(Int32 fd, Int32 reg, Int32 data);
/// <summary>
/// This initialises the I2C system with your given device identifier.
@@ -71,7 +70,7 @@
/// <param name="devId">The dev identifier.</param>
/// <returns>The result</returns>
[DllImport(WiringPiLibrary, EntryPoint = "wiringPiI2CSetup", SetLastError = true)]
public static extern int WiringPiI2CSetup(int devId);
public static extern Int32 WiringPiI2CSetup(Int32 devId);
#endregion
@@ -1,9 +1,8 @@
namespace Unosquare.RaspberryIO.Native
{
using System.Runtime.InteropServices;
using System;
using System.Runtime.InteropServices;
public partial class WiringPi
{
namespace Unosquare.RaspberryIO.Native {
public partial class WiringPi {
#region WiringPi - Serial Port
/// <summary>
@@ -17,7 +16,7 @@
/// <param name="baud">The baud.</param>
/// <returns>The result</returns>
[DllImport(WiringPiLibrary, EntryPoint = "serialOpen", SetLastError = true)]
public static extern int SerialOpen(string device, int baud);
public static extern Int32 SerialOpen(String device, Int32 baud);
/// <summary>
/// Closes the device identified by the file descriptor given.
@@ -25,7 +24,7 @@
/// <param name="fd">The fd.</param>
/// <returns>The result</returns>
[DllImport(WiringPiLibrary, EntryPoint = "serialClose", SetLastError = true)]
public static extern int SerialClose(int fd);
public static extern Int32 SerialClose(Int32 fd);
/// <summary>
/// Sends the single byte to the serial device identified by the given file descriptor.
@@ -33,7 +32,7 @@
/// <param name="fd">The fd.</param>
/// <param name="c">The c.</param>
[DllImport(WiringPiLibrary, EntryPoint = "serialPutchar", SetLastError = true)]
public static extern void SerialPutchar(int fd, byte c);
public static extern void SerialPutchar(Int32 fd, Byte c);
/// <summary>
/// Sends the nul-terminated string to the serial device identified by the given file descriptor.
@@ -41,7 +40,7 @@
/// <param name="fd">The fd.</param>
/// <param name="s">The s.</param>
[DllImport(WiringPiLibrary, EntryPoint = "serialPuts", SetLastError = true)]
public static extern void SerialPuts(int fd, string s);
public static extern void SerialPuts(Int32 fd, String s);
/// <summary>
/// Returns the number of characters available for reading, or -1 for any error condition,
@@ -50,7 +49,7 @@
/// <param name="fd">The fd.</param>
/// <returns>The result</returns>
[DllImport(WiringPiLibrary, EntryPoint = "serialDataAvail", SetLastError = true)]
public static extern int SerialDataAvail(int fd);
public static extern Int32 SerialDataAvail(Int32 fd);
/// <summary>
/// Returns the next character available on the serial device.
@@ -59,14 +58,14 @@
/// <param name="fd">The fd.</param>
/// <returns>The result</returns>
[DllImport(WiringPiLibrary, EntryPoint = "serialGetchar", SetLastError = true)]
public static extern int SerialGetchar(int fd);
public static extern Int32 SerialGetchar(Int32 fd);
/// <summary>
/// This discards all data received, or waiting to be send down the given device.
/// </summary>
/// <param name="fd">The fd.</param>
[DllImport(WiringPiLibrary, EntryPoint = "serialFlush", SetLastError = true)]
public static extern void SerialFlush(int fd);
public static extern void SerialFlush(Int32 fd);
#endregion
}
@@ -1,9 +1,8 @@
namespace Unosquare.RaspberryIO.Native
{
using System.Runtime.InteropServices;
using System;
using System.Runtime.InteropServices;
public partial class WiringPi
{
namespace Unosquare.RaspberryIO.Native {
public partial class WiringPi {
#region WiringPi - Shift Library
/// <summary>
@@ -16,7 +15,7 @@
/// <param name="order">The order.</param>
/// <returns>The result</returns>
[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>
/// 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.
@@ -28,7 +27,7 @@
/// <param name="order">The order.</param>
/// <param name="val">The value.</param>
[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
@@ -1,9 +1,8 @@
namespace Unosquare.RaspberryIO.Native
{
using System.Runtime.InteropServices;
using System;
using System.Runtime.InteropServices;
public partial class WiringPi
{
namespace Unosquare.RaspberryIO.Native {
public partial class WiringPi {
#region WiringPi - Soft PWM (https://github.com/WiringPi/WiringPi/blob/master/wiringPi/softPwm.h)
/// <summary>
@@ -16,7 +15,7 @@
/// <param name="pwmRange">The PWM range.</param>
/// <returns>The result</returns>
[DllImport(WiringPiLibrary, EntryPoint = "softPwmCreate", SetLastError = true)]
public static extern int SoftPwmCreate(int pin, int initialValue, int pwmRange);
public static extern Int32 SoftPwmCreate(Int32 pin, Int32 initialValue, Int32 pwmRange);
/// <summary>
/// This updates the PWM value on the given pin. The value is checked to be in-range and pins that haven’t previously
@@ -25,14 +24,14 @@
/// <param name="pin">The pin.</param>
/// <param name="value">The value.</param>
[DllImport(WiringPiLibrary, EntryPoint = "softPwmWrite", SetLastError = true)]
public static extern void SoftPwmWrite(int pin, int value);
public static extern void SoftPwmWrite(Int32 pin, Int32 value);
/// <summary>
/// This function is undocumented
/// </summary>
/// <param name="pin">The pin.</param>
[DllImport(WiringPiLibrary, EntryPoint = "softPwmStop", SetLastError = true)]
public static extern void SoftPwmStop(int pin);
public static extern void SoftPwmStop(Int32 pin);
/// <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.
@@ -41,14 +40,14 @@
/// <param name="pin">The pin.</param>
/// <returns>The result</returns>
[DllImport(WiringPiLibrary, EntryPoint = "softToneCreate", SetLastError = true)]
public static extern int SoftToneCreate(int pin);
public static extern Int32 SoftToneCreate(Int32 pin);
/// <summary>
/// This function is undocumented
/// </summary>
/// <param name="pin">The pin.</param>
[DllImport(WiringPiLibrary, EntryPoint = "softToneStop", SetLastError = true)]
public static extern void SoftToneStop(int pin);
public static extern void SoftToneStop(Int32 pin);
/// <summary>
/// This updates the tone frequency value on the given pin. The tone will be played until you set the frequency to 0.
@@ -56,7 +55,7 @@
/// <param name="pin">The pin.</param>
/// <param name="freq">The freq.</param>
[DllImport(WiringPiLibrary, EntryPoint = "softToneWrite", SetLastError = true)]
public static extern void SoftToneWrite(int pin, int freq);
public static extern void SoftToneWrite(Int32 pin, Int32 freq);
#endregion
+8 -9
View File
@@ -1,9 +1,8 @@
namespace Unosquare.RaspberryIO.Native
{
using System.Runtime.InteropServices;
using System;
using System.Runtime.InteropServices;
public partial class WiringPi
{
namespace Unosquare.RaspberryIO.Native {
public partial class WiringPi {
#region WiringPi - SPI Library Calls
/// <summary>
@@ -12,7 +11,7 @@
/// <param name="channel">The channel.</param>
/// <returns>Unknown</returns>
[DllImport(WiringPiLibrary, EntryPoint = "wiringPiSPIGetFd", SetLastError = true)]
public static extern int WiringPiSPIGetFd(int channel);
public static extern Int32 WiringPiSPIGetFd(Int32 channel);
/// <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.
@@ -25,7 +24,7 @@
/// <param name="len">The length.</param>
/// <returns>The result</returns>
[DllImport(WiringPiLibrary, EntryPoint = "wiringPiSPIDataRW", SetLastError = true)]
public static extern int WiringPiSPIDataRW(int channel, byte[] data, int len);
public static extern Int32 WiringPiSPIDataRW(Int32 channel, Byte[] data, Int32 len);
/// <summary>
/// This function is undocumented
@@ -35,7 +34,7 @@
/// <param name="mode">The mode.</param>
/// <returns>Unkown</returns>
[DllImport(WiringPiLibrary, EntryPoint = "wiringPiSPISetupMode", SetLastError = true)]
public static extern int WiringPiSPISetupMode(int channel, int speed, int mode);
public static extern Int32 WiringPiSPISetupMode(Int32 channel, Int32 speed, Int32 mode);
/// <summary>
/// This is the way to initialize a channel (The Pi has 2 channels; 0 and 1). The speed parameter is an integer
@@ -46,7 +45,7 @@
/// <param name="speed">The speed.</param>
/// <returns>The Linux file descriptor for the device or -1 for error</returns>
[DllImport(WiringPiLibrary, EntryPoint = "wiringPiSPISetup", SetLastError = true)]
public static extern int WiringPiSPISetup(int channel, int speed);
public static extern Int32 WiringPiSPISetup(Int32 channel, Int32 speed);
#endregion
}
+42 -44
View File
@@ -1,16 +1,14 @@
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";
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)
@@ -22,7 +20,7 @@
/// </summary>
/// <returns>The result code</returns>
[DllImport(WiringPiLibrary, EntryPoint = "wiringPiSetup", SetLastError = true)]
public static extern int WiringPiSetup();
public static extern Int32 WiringPiSetup();
/// <summary>
/// This initialises wiringPi but uses the /sys/class/gpio interface rather than accessing the hardware directly.
@@ -37,7 +35,7 @@
/// </summary>
/// <returns>The result code</returns>
[DllImport(WiringPiLibrary, EntryPoint = "wiringPiSetupSys", SetLastError = true)]
public static extern int WiringPiSetupSys();
public static extern Int32 WiringPiSetupSys();
/// <summary>
/// This is identical to wiringPiSetup, however it allows the calling programs to use the Broadcom GPIO
@@ -47,7 +45,7 @@
/// </summary>
/// <returns>The result code</returns>
[DllImport(WiringPiLibrary, EntryPoint = "wiringPiSetupGpio", SetLastError = true)]
public static extern int WiringPiSetupGpio();
public static extern Int32 WiringPiSetupGpio();
/// <summary>
/// Identical to wiringPiSetup, however it allows the calling programs to use the physical pin numbers on the P1 connector only.
@@ -55,7 +53,7 @@
/// </summary>
/// <returns>The result code</returns>
[DllImport(WiringPiLibrary, EntryPoint = "wiringPiSetupPhys", SetLastError = true)]
public static extern int WiringPiSetupPhys();
public static extern Int32 WiringPiSetupPhys();
/// <summary>
/// This function is undocumented
@@ -63,7 +61,7 @@
/// <param name="pin">The pin.</param>
/// <param name="mode">The mode.</param>
[DllImport(WiringPiLibrary, EntryPoint = "pinModeAlt", SetLastError = true)]
public static extern void PinModeAlt(int pin, int mode);
public static extern void PinModeAlt(Int32 pin, Int32 mode);
/// <summary>
/// This sets the mode of a pin to either INPUT, OUTPUT, PWM_OUTPUT or GPIO_CLOCK.
@@ -76,7 +74,7 @@
/// <param name="pin">The pin.</param>
/// <param name="mode">The mode.</param>
[DllImport(WiringPiLibrary, EntryPoint = "pinMode", SetLastError = true)]
public static extern void PinMode(int pin, int mode);
public static extern void PinMode(Int32 pin, Int32 mode);
/// <summary>
/// This sets the pull-up or pull-down resistor mode on the given pin, which should be set as an input.
@@ -90,7 +88,7 @@
/// <param name="pin">The pin.</param>
/// <param name="pud">The pud.</param>
[DllImport(WiringPiLibrary, EntryPoint = "pullUpDnControl", SetLastError = true)]
public static extern void PullUpDnControl(int pin, int pud);
public static extern void PullUpDnControl(Int32 pin, Int32 pud);
/// <summary>
/// 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.
@@ -98,7 +96,7 @@
/// <param name="pin">The pin.</param>
/// <returns>The result code</returns>
[DllImport(WiringPiLibrary, EntryPoint = "digitalRead", SetLastError = true)]
public static extern int DigitalRead(int pin);
public static extern Int32 DigitalRead(Int32 pin);
/// <summary>
/// Writes the value HIGH or LOW (1 or 0) to the given pin which must have been previously set as an output.
@@ -107,7 +105,7 @@
/// <param name="pin">The pin.</param>
/// <param name="value">The value.</param>
[DllImport(WiringPiLibrary, EntryPoint = "digitalWrite", SetLastError = true)]
public static extern void DigitalWrite(int pin, int value);
public static extern void DigitalWrite(Int32 pin, Int32 value);
/// <summary>
/// Writes the value to the PWM register for the given pin. The Raspberry Pi has one
@@ -118,7 +116,7 @@
/// <param name="pin">The pin.</param>
/// <param name="value">The value.</param>
[DllImport(WiringPiLibrary, EntryPoint = "pwmWrite", SetLastError = true)]
public static extern void PwmWrite(int pin, int value);
public static extern void PwmWrite(Int32 pin, Int32 value);
/// <summary>
/// This returns the value read on the supplied analog input pin. You will need to
@@ -127,7 +125,7 @@
/// <param name="pin">The pin.</param>
/// <returns>The result code</returns>
[DllImport(WiringPiLibrary, EntryPoint = "analogRead", SetLastError = true)]
public static extern int AnalogRead(int pin);
public static extern Int32 AnalogRead(Int32 pin);
/// <summary>
/// This writes the given value to the supplied analog pin. You will need to register additional
@@ -136,7 +134,7 @@
/// <param name="pin">The pin.</param>
/// <param name="value">The value.</param>
[DllImport(WiringPiLibrary, EntryPoint = "analogWrite", SetLastError = true)]
public static extern void AnalogWrite(int pin, int value);
public static extern void AnalogWrite(Int32 pin, Int32 value);
/// <summary>
/// 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
@@ -144,7 +142,7 @@
/// </summary>
/// <returns>The result code</returns>
[DllImport(WiringPiLibrary, EntryPoint = "piBoardRev", SetLastError = true)]
public static extern int PiBoardRev();
public static extern Int32 PiBoardRev();
/// <summary>
/// This function is undocumented
@@ -155,7 +153,7 @@
/// <param name="overVolted">The over volted.</param>
/// <returns>The result code</returns>
[DllImport(WiringPiLibrary, EntryPoint = "piBoardId", SetLastError = true)]
public static extern int PiBoardId(ref int model, ref int mem, ref int maker, ref int overVolted);
public static extern Int32 PiBoardId(ref Int32 model, ref Int32 mem, ref Int32 maker, ref Int32 overVolted);
/// <summary>
/// This returns the BCM_GPIO pin number of the supplied wiringPi pin. It takes the board revision into account.
@@ -163,7 +161,7 @@
/// <param name="wPiPin">The w pi pin.</param>
/// <returns>The result code</returns>
[DllImport(WiringPiLibrary, EntryPoint = "wpiPinToGpio", SetLastError = true)]
public static extern int WpiPinToGpio(int wPiPin);
public static extern Int32 WpiPinToGpio(Int32 wPiPin);
/// <summary>
/// This returns the BCM_GPIO pin number of the supplied physical pin on the P1 connector.
@@ -171,7 +169,7 @@
/// <param name="physPin">The physical pin.</param>
/// <returns>The result code</returns>
[DllImport(WiringPiLibrary, EntryPoint = "physPinToGpio", SetLastError = true)]
public static extern int PhysPinToGpio(int physPin);
public static extern Int32 PhysPinToGpio(Int32 physPin);
/// <summary>
/// This sets the “strength” of the pad drivers for a particular group of pins.
@@ -181,7 +179,7 @@
/// <param name="value">The value.</param>
/// <returns>The result code</returns>
[DllImport(WiringPiLibrary, EntryPoint = "setPadDrive", SetLastError = true)]
public static extern int SetPadDrive(int group, int value);
public static extern Int32 SetPadDrive(Int32 group, Int32 value);
/// <summary>
/// Undocumented function
@@ -189,7 +187,7 @@
/// <param name="pin">The pin.</param>
/// <returns>The result code</returns>
[DllImport(WiringPiLibrary, EntryPoint = "getAlt", SetLastError = true)]
public static extern int GetAlt(int pin);
public static extern Int32 GetAlt(Int32 pin);
/// <summary>
/// Undocumented function
@@ -198,7 +196,7 @@
/// <param name="freq">The freq.</param>
/// <returns>The result code</returns>
[DllImport(WiringPiLibrary, EntryPoint = "pwmToneWrite", SetLastError = true)]
public static extern int PwmToneWrite(int pin, int freq);
public static extern Int32 PwmToneWrite(Int32 pin, Int32 freq);
/// <summary>
/// This writes the 8-bit byte supplied to the first 8 GPIO pins.
@@ -206,7 +204,7 @@
/// </summary>
/// <param name="value">The value.</param>
[DllImport(WiringPiLibrary, EntryPoint = "digitalWriteByte", SetLastError = true)]
public static extern void DigitalWriteByte(int value);
public static extern void DigitalWriteByte(Int32 value);
/// <summary>
/// This writes the 8-bit byte supplied to the first 8 GPIO pins.
@@ -214,7 +212,7 @@
/// </summary>
/// <param name="value">The value.</param>
[DllImport(WiringPiLibrary, EntryPoint = "digitalWriteByte2", SetLastError = true)]
public static extern void DigitalWriteByte2(int value);
public static extern void DigitalWriteByte2(Int32 value);
/// <summary>
/// Undocumented function
@@ -223,7 +221,7 @@
/// </summary>
/// <returns>The result code</returns>
[DllImport(WiringPiLibrary, EntryPoint = "digitalReadByte", SetLastError = true)]
public static extern uint DigitalReadByte();
public static extern UInt32 DigitalReadByte();
/// <summary>
/// Undocumented function
@@ -232,7 +230,7 @@
/// </summary>
/// <returns>The result code</returns>
[DllImport(WiringPiLibrary, EntryPoint = "digitalReadByte2", SetLastError = true)]
public static extern uint DigitalReadByte2();
public static extern UInt32 DigitalReadByte2();
/// <summary>
/// The PWM generator can run in 2 modes – “balanced” and “mark:space”. The mark:space mode is traditional,
@@ -240,14 +238,14 @@
/// </summary>
/// <param name="mode">The mode.</param>
[DllImport(WiringPiLibrary, EntryPoint = "pwmSetMode", SetLastError = true)]
public static extern void PwmSetMode(int mode);
public static extern void PwmSetMode(Int32 mode);
/// <summary>
/// This sets the range register in the PWM generator. The default is 1024.
/// </summary>
/// <param name="range">The range.</param>
[DllImport(WiringPiLibrary, EntryPoint = "pwmSetRange", SetLastError = true)]
public static extern void PwmSetRange(uint range);
public static extern void PwmSetRange(UInt32 range);
/// <summary>
/// This sets the divisor for the PWM clock.
@@ -256,7 +254,7 @@
/// </summary>
/// <param name="divisor">The divisor.</param>
[DllImport(WiringPiLibrary, EntryPoint = "pwmSetClock", SetLastError = true)]
public static extern void PwmSetClock(int divisor);
public static extern void PwmSetClock(Int32 divisor);
/// <summary>
/// Undocumented function
@@ -264,7 +262,7 @@
/// <param name="pin">The pin.</param>
/// <param name="freq">The freq.</param>
[DllImport(WiringPiLibrary, EntryPoint = "gpioClockSet", SetLastError = true)]
public static extern void GpioClockSet(int pin, int freq);
public static extern void GpioClockSet(Int32 pin, Int32 freq);
/// <summary>
/// Note: Jan 2013: The waitForInterrupt() function is deprecated – you should use the newer and easier to use wiringPiISR() function below.
@@ -281,7 +279,7 @@
/// <returns>The result code</returns>
[Obsolete]
[DllImport(WiringPiLibrary, EntryPoint = "waitForInterrupt", SetLastError = true)]
public static extern int WaitForInterrupt(int pin, int timeout);
public static extern Int32 WaitForInterrupt(Int32 pin, Int32 timeout);
/// <summary>
/// This function registers a function to received interrupts on the specified pin.
@@ -302,7 +300,7 @@
/// <param name="method">The method.</param>
/// <returns>The result code</returns>
[DllImport(WiringPiLibrary, EntryPoint = "wiringPiISR", SetLastError = true)]
public static extern int WiringPiISR(int pin, int mode, InterruptServiceRoutineCallback method);
public static extern Int32 WiringPiISR(Int32 pin, Int32 mode, InterruptServiceRoutineCallback method);
/// <summary>
/// This function creates a thread which is another function in your program previously declared using the PI_THREAD declaration.
@@ -313,7 +311,7 @@
/// <param name="method">The method.</param>
/// <returns>The result code</returns>
[DllImport(WiringPiLibrary, EntryPoint = "piThreadCreate", SetLastError = true)]
public static extern int PiThreadCreate(ThreadWorker method);
public static extern Int32 PiThreadCreate(ThreadWorker method);
/// <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.
@@ -324,7 +322,7 @@
/// </summary>
/// <param name="key">The key.</param>
[DllImport(WiringPiLibrary, EntryPoint = "piLock", SetLastError = true)]
public static extern void PiLock(int key);
public static extern void PiLock(Int32 key);
/// <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.
@@ -335,7 +333,7 @@
/// </summary>
/// <param name="key">The key.</param>
[DllImport(WiringPiLibrary, EntryPoint = "piUnlock", SetLastError = true)]
public static extern void PiUnlock(int key);
public static extern void PiUnlock(Int32 key);
/// <summary>
/// This attempts to shift your program (or thread in a multi-threaded program) to a higher priority
@@ -350,7 +348,7 @@
/// <param name="priority">The priority.</param>
/// <returns>The result code</returns>
[DllImport(WiringPiLibrary, EntryPoint = "piHiPri", SetLastError = true)]
public static extern int PiHiPri(int priority);
public static extern Int32 PiHiPri(Int32 priority);
/// <summary>
/// This causes program execution to pause for at least howLong milliseconds.
@@ -359,7 +357,7 @@
/// </summary>
/// <param name="howLong">The how long.</param>
[DllImport(WiringPiLibrary, EntryPoint = "delay", SetLastError = true)]
public static extern void Delay(uint howLong);
public static extern void Delay(UInt32 howLong);
/// <summary>
/// This causes program execution to pause for at least howLong microseconds.
@@ -371,7 +369,7 @@
/// </summary>
/// <param name="howLong">The how long.</param>
[DllImport(WiringPiLibrary, EntryPoint = "delayMicroseconds", SetLastError = true)]
public static extern void DelayMicroseconds(uint howLong);
public static extern void DelayMicroseconds(UInt32 howLong);
/// <summary>
/// This returns a number representing the number of milliseconds since your program called one of the wiringPiSetup functions.
@@ -379,7 +377,7 @@
/// </summary>
/// <returns>The result code</returns>
[DllImport(WiringPiLibrary, EntryPoint = "millis", SetLastError = true)]
public static extern uint Millis();
public static extern UInt32 Millis();
/// <summary>
/// This returns a number representing the number of microseconds since your program called one of
@@ -387,7 +385,7 @@
/// </summary>
/// <returns>The result code</returns>
[DllImport(WiringPiLibrary, EntryPoint = "micros", SetLastError = true)]
public static extern uint Micros();
public static extern UInt32 Micros();
#endregion
}
+34 -23
View File
@@ -1,26 +1,23 @@
namespace Unosquare.RaspberryIO
{
using Camera;
using Computer;
using Gpio;
using Native;
using System.Threading.Tasks;
using Swan.Components;
using Unosquare.RaspberryIO.Camera;
using Unosquare.RaspberryIO.Computer;
using Unosquare.RaspberryIO.Gpio;
using Unosquare.RaspberryIO.Native;
using System.Threading.Tasks;
using Unosquare.Swan.Components;
using System;
namespace Unosquare.RaspberryIO {
/// <summary>
/// Our main character. Provides access to the Raspberry Pi's GPIO, system and board information and Camera
/// </summary>
public static class Pi
{
private static readonly object SyncLock = new object();
public static class Pi {
private static readonly Object SyncLock = new Object();
/// <summary>
/// Initializes static members of the <see cref="Pi" /> class.
/// </summary>
static Pi()
{
lock (SyncLock)
{
static Pi() {
lock(SyncLock) {
// Extraction of embedded resources
Resources.EmbeddedResources.ExtractAll();
@@ -40,42 +37,56 @@
/// <summary>
/// Provides access to the Raspberry Pi's GPIO as a collection of GPIO Pins.
/// </summary>
public static GpioController Gpio { get; }
public static GpioController Gpio {
get;
}
/// <summary>
/// Provides information on this Raspberry Pi's CPU and form factor.
/// </summary>
public static SystemInfo Info { get; }
public static SystemInfo Info {
get;
}
/// <summary>
/// Provides access to The PI's Timing and threading API
/// </summary>
public static Timing Timing { get; }
public static Timing Timing {
get;
}
/// <summary>
/// Provides access to the 2-channel SPI bus
/// </summary>
public static SpiBus Spi { get; }
public static SpiBus Spi {
get;
}
/// <summary>
/// Provides access to the functionality of the i2c bus.
/// </summary>
public static I2CBus I2C { get; }
public static I2CBus I2C {
get;
}
/// <summary>
/// Provides access to the official Raspberry Pi Camera
/// </summary>
public static CameraController Camera { get; }
public static CameraController Camera {
get;
}
/// <summary>
/// Provides access to the official Raspberry Pi 7-inch DSI Display
/// </summary>
public static DsiDisplay PiDisplay { get; }
public static DsiDisplay PiDisplay {
get;
}
/// <summary>
/// Gets the logger source name.
/// </summary>
internal static string LoggerSource => typeof(Pi).Namespace;
internal static String LoggerSource => typeof(Pi).Namespace;
#endregion
@@ -1,24 +1,18 @@
namespace Unosquare.RaspberryIO.Resources
{
using Native;
using Swan;
using System;
using System.Collections.ObjectModel;
using System.IO;
using Unosquare.RaspberryIO.Native;
using Unosquare.Swan;
using System;
using System.Collections.ObjectModel;
using System.IO;
namespace Unosquare.RaspberryIO.Resources {
/// <summary>
/// Provides access to embedded assembly files
/// </summary>
internal static class EmbeddedResources
{
internal static class EmbeddedResources {
/// <summary>
/// Initializes static members of the <see cref="EmbeddedResources"/> class.
/// </summary>
static EmbeddedResources()
{
ResourceNames =
new ReadOnlyCollection<string>(typeof(EmbeddedResources).Assembly().GetManifestResourceNames());
}
static EmbeddedResources() => ResourceNames = new ReadOnlyCollection<String>(typeof(EmbeddedResources).Assembly().GetManifestResourceNames());
/// <summary>
/// Gets the resource names.
@@ -26,36 +20,32 @@
/// <value>
/// The resource names.
/// </value>
public static ReadOnlyCollection<string> ResourceNames { get; }
public static ReadOnlyCollection<String> ResourceNames {
get;
}
/// <summary>
/// Extracts all the file resources to the specified base path.
/// </summary>
public static void ExtractAll()
{
var basePath = Runtime.EntryAssemblyDirectory;
var executablePermissions = Standard.StringToInteger("0777", IntPtr.Zero, 8);
public static void ExtractAll() {
String basePath = Runtime.EntryAssemblyDirectory;
Int32 executablePermissions = Standard.StringToInteger("0777", IntPtr.Zero, 8);
foreach (var resourceName in ResourceNames)
{
var filename = resourceName.Substring($"{typeof(EmbeddedResources).Namespace}.".Length);
var targetPath = Path.Combine(basePath, filename);
if (File.Exists(targetPath)) return;
foreach(String resourceName in ResourceNames) {
String filename = resourceName.Substring($"{typeof(EmbeddedResources).Namespace}.".Length);
String targetPath = Path.Combine(basePath, filename);
if(File.Exists(targetPath)) {
return;
}
using (var stream = typeof(EmbeddedResources).Assembly()
.GetManifestResourceStream($"{typeof(EmbeddedResources).Namespace}.{filename}"))
{
using (var outputStream = File.OpenWrite(targetPath))
{
using(Stream stream = typeof(EmbeddedResources).Assembly().GetManifestResourceStream($"{typeof(EmbeddedResources).Namespace}.{filename}")) {
using(FileStream outputStream = File.OpenWrite(targetPath)) {
stream?.CopyTo(outputStream);
}
try
{
Standard.Chmod(targetPath, (uint)executablePermissions);
}
catch
{
try {
_ = Standard.Chmod(targetPath, (UInt32)executablePermissions);
} catch {
/* Ignore */
}
}
@@ -1,8 +1,7 @@
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
///
@@ -11,35 +10,29 @@
/// </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 long _backingValue;
where T : struct, IComparable, IComparable<T>, IEquatable<T> {
private Int64 _backingValue;
/// <summary>
/// Initializes a new instance of the <see cref="AtomicTypeBase{T}"/> class.
/// </summary>
/// <param name="initialValue">The initial value.</param>
protected AtomicTypeBase(long initialValue)
{
BackingValue = initialValue;
}
protected AtomicTypeBase(Int64 initialValue) => this.BackingValue = initialValue;
/// <summary>
/// Gets or sets the value.
/// </summary>
public T Value
{
get => FromLong(BackingValue);
set => BackingValue = ToLong(value);
public T Value {
get => this.FromLong(this.BackingValue);
set => this.BackingValue = this.ToLong(value);
}
/// <summary>
/// Gets or sets the backing value.
/// </summary>
protected long BackingValue
{
get => Interlocked.Read(ref _backingValue);
set => Interlocked.Exchange(ref _backingValue, value);
protected Int64 BackingValue {
get => Interlocked.Read(ref this._backingValue);
set => Interlocked.Exchange(ref this._backingValue, value);
}
/// <summary>
@@ -50,7 +43,7 @@
/// <returns>
/// The result of the operator.
/// </returns>
public static bool operator ==(AtomicTypeBase<T> a, T b) => a?.Equals(b) == true;
public static Boolean operator ==(AtomicTypeBase<T> a, T b) => a?.Equals(b) == true;
/// <summary>
/// Implements the operator !=.
@@ -60,7 +53,7 @@
/// <returns>
/// The result of the operator.
/// </returns>
public static bool operator !=(AtomicTypeBase<T> a, T b) => a?.Equals(b) == false;
public static Boolean operator !=(AtomicTypeBase<T> a, T b) => a?.Equals(b) == false;
/// <summary>
/// Implements the operator &gt;.
@@ -70,7 +63,7 @@
/// <returns>
/// The result of the operator.
/// </returns>
public static bool operator >(AtomicTypeBase<T> a, T b) => a.CompareTo(b) > 0;
public static Boolean operator >(AtomicTypeBase<T> a, T b) => a.CompareTo(b) > 0;
/// <summary>
/// Implements the operator &lt;.
@@ -80,7 +73,7 @@
/// <returns>
/// The result of the operator.
/// </returns>
public static bool operator <(AtomicTypeBase<T> a, T b) => a.CompareTo(b) < 0;
public static Boolean operator <(AtomicTypeBase<T> a, T b) => a.CompareTo(b) < 0;
/// <summary>
/// Implements the operator &gt;=.
@@ -90,7 +83,7 @@
/// <returns>
/// The result of the operator.
/// </returns>
public static bool operator >=(AtomicTypeBase<T> a, T b) => a.CompareTo(b) >= 0;
public static Boolean operator >=(AtomicTypeBase<T> a, T b) => a.CompareTo(b) >= 0;
/// <summary>
/// Implements the operator &lt;=.
@@ -100,7 +93,7 @@
/// <returns>
/// The result of the operator.
/// </returns>
public static bool operator <=(AtomicTypeBase<T> a, T b) => a.CompareTo(b) <= 0;
public static Boolean operator <=(AtomicTypeBase<T> a, T b) => a.CompareTo(b) <= 0;
/// <summary>
/// Implements the operator ++.
@@ -109,9 +102,8 @@
/// <returns>
/// The result of the operator.
/// </returns>
public static AtomicTypeBase<T> operator ++(AtomicTypeBase<T> instance)
{
Interlocked.Increment(ref instance._backingValue);
public static AtomicTypeBase<T> operator ++(AtomicTypeBase<T> instance) {
_ = Interlocked.Increment(ref instance._backingValue);
return instance;
}
@@ -122,9 +114,8 @@
/// <returns>
/// The result of the operator.
/// </returns>
public static AtomicTypeBase<T> operator --(AtomicTypeBase<T> instance)
{
Interlocked.Decrement(ref instance._backingValue);
public static AtomicTypeBase<T> operator --(AtomicTypeBase<T> instance) {
_ = Interlocked.Decrement(ref instance._backingValue);
return instance;
}
@@ -136,9 +127,8 @@
/// <returns>
/// The result of the operator.
/// </returns>
public static AtomicTypeBase<T> operator +(AtomicTypeBase<T> instance, long operand)
{
instance.BackingValue = instance.BackingValue + operand;
public static AtomicTypeBase<T> operator +(AtomicTypeBase<T> instance, Int64 operand) {
instance.BackingValue += operand;
return instance;
}
@@ -150,9 +140,8 @@
/// <returns>
/// The result of the operator.
/// </returns>
public static AtomicTypeBase<T> operator -(AtomicTypeBase<T> instance, long operand)
{
instance.BackingValue = instance.BackingValue - operand;
public static AtomicTypeBase<T> operator -(AtomicTypeBase<T> instance, Int64 operand) {
instance.BackingValue -= operand;
return instance;
}
@@ -162,16 +151,14 @@
/// <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>
/// <exception cref="ArgumentException">When types are incompatible.</exception>
public int CompareTo(object other)
{
switch (other)
{
public Int32 CompareTo(Object other) {
switch(other) {
case null:
return 1;
case AtomicTypeBase<T> atomic:
return BackingValue.CompareTo(atomic.BackingValue);
return this.BackingValue.CompareTo(atomic.BackingValue);
case T variable:
return Value.CompareTo(variable);
return this.Value.CompareTo(variable);
}
throw new ArgumentException("Incompatible comparison types");
@@ -182,30 +169,28 @@
/// </summary>
/// <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(T other) => Value.CompareTo(other);
public Int32 CompareTo(T other) => this.Value.CompareTo(other);
/// <summary>
/// Compares the value to the other instance.
/// </summary>
/// <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);
public Int32 CompareTo(AtomicTypeBase<T> other) => this.BackingValue.CompareTo(other?.BackingValue ?? default);
/// <summary>
/// Determines whether the specified <see cref="object" />, is equal to this instance.
/// Determines whether the specified <see cref="Object" />, is equal to this instance.
/// </summary>
/// <param name="other">The <see cref="object" /> to compare with this instance.</param>
/// <param name="other">The <see cref="Object" /> to compare with this instance.</param>
/// <returns>
/// <c>true</c> if the specified <see cref="object" /> is equal to this instance; otherwise, <c>false</c>.
/// <c>true</c> if the specified <see cref="Object" /> is equal to this instance; otherwise, <c>false</c>.
/// </returns>
public override bool Equals(object other)
{
switch (other)
{
public override Boolean Equals(Object other) {
switch(other) {
case AtomicTypeBase<T> atomic:
return Equals(atomic);
return this.Equals(atomic);
case T variable:
return Equals(variable);
return this.Equals(variable);
}
return false;
@@ -217,27 +202,27 @@
/// <returns>
/// A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table.
/// </returns>
public override int GetHashCode() => BackingValue.GetHashCode();
public override Int32 GetHashCode() => this.BackingValue.GetHashCode();
/// <inheritdoc />
public bool Equals(AtomicTypeBase<T> other) =>
BackingValue == (other?.BackingValue ?? default);
public Boolean Equals(AtomicTypeBase<T> other) =>
this.BackingValue == (other?.BackingValue ?? default);
/// <inheritdoc />
public bool Equals(T other) => Equals(Value, other);
public Boolean Equals(T other) => Equals(this.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);
protected abstract T FromLong(Int64 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);
protected abstract Int64 ToLong(T value);
}
}
@@ -1,22 +1,20 @@
namespace Unosquare.Swan.Abstractions
{
using System;
using System.Threading;
using System;
using System.Threading;
namespace Unosquare.Swan.Abstractions {
/// <summary>
/// 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();
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 int _period;
private Int32 _period;
/// <summary>
/// Initializes a new instance of the <see cref="ExclusiveTimer"/> class.
@@ -25,11 +23,10 @@
/// <param name="state">The state.</param>
/// <param name="dueTime">The due time.</param>
/// <param name="period">The period.</param>
public ExclusiveTimer(TimerCallback timerCallback, object state, int dueTime, int period)
{
_period = period;
_userCallback = timerCallback;
_backingTimer = new Timer(InternalCallback, state ?? this, dueTime, Timeout.Infinite);
public ExclusiveTimer(TimerCallback timerCallback, Object state, Int32 dueTime, Int32 period) {
this._period = period;
this._userCallback = timerCallback;
this._backingTimer = new Timer(this.InternalCallback, state ?? this, dueTime, Timeout.Infinite);
}
/// <summary>
@@ -39,9 +36,8 @@
/// <param name="state">The state.</param>
/// <param name="dueTime">The due time.</param>
/// <param name="period">The period.</param>
public ExclusiveTimer(TimerCallback timerCallback, object state, TimeSpan dueTime, TimeSpan period)
: this(timerCallback, state, Convert.ToInt32(dueTime.TotalMilliseconds), Convert.ToInt32(period.TotalMilliseconds))
{
public ExclusiveTimer(TimerCallback timerCallback, Object state, TimeSpan dueTime, TimeSpan period)
: this(timerCallback, state, Convert.ToInt32(dueTime.TotalMilliseconds), Convert.ToInt32(period.TotalMilliseconds)) {
// placeholder
}
@@ -50,8 +46,7 @@
/// </summary>
/// <param name="timerCallback">The timer callback.</param>
public ExclusiveTimer(TimerCallback timerCallback)
: this(timerCallback, null, Timeout.Infinite, Timeout.Infinite)
{
: this(timerCallback, null, Timeout.Infinite, Timeout.Infinite) {
// placholder
}
@@ -61,9 +56,8 @@
/// <param name="timerCallback">The timer callback.</param>
/// <param name="dueTime">The due time.</param>
/// <param name="period">The period.</param>
public ExclusiveTimer(Action timerCallback, int dueTime, int period)
: this(s => { timerCallback?.Invoke(); }, null, dueTime, period)
{
public ExclusiveTimer(Action timerCallback, Int32 dueTime, Int32 period)
: this(s => timerCallback?.Invoke(), null, dueTime, period) {
// placeholder
}
@@ -74,8 +68,7 @@
/// <param name="dueTime">The due time.</param>
/// <param name="period">The period.</param>
public ExclusiveTimer(Action timerCallback, TimeSpan dueTime, TimeSpan period)
: this(s => { timerCallback?.Invoke(); }, null, dueTime, period)
{
: this(s => timerCallback?.Invoke(), null, dueTime, period) {
// placeholder
}
@@ -84,8 +77,7 @@
/// </summary>
/// <param name="timerCallback">The timer callback.</param>
public ExclusiveTimer(Action timerCallback)
: this(timerCallback, Timeout.Infinite, Timeout.Infinite)
{
: this(timerCallback, Timeout.Infinite, Timeout.Infinite) {
// placeholder
}
@@ -95,7 +87,7 @@
/// <value>
/// <c>true</c> if this instance is disposing; otherwise, <c>false</c>.
/// </value>
public bool IsDisposing => _isDisposing.Value;
public Boolean IsDisposing => this._isDisposing.Value;
/// <summary>
/// Gets a value indicating whether this instance is disposed.
@@ -103,18 +95,17 @@
/// <value>
/// <c>true</c> if this instance is disposed; otherwise, <c>false</c>.
/// </value>
public bool IsDisposed => _isDisposed.Value;
public Boolean IsDisposed => this._isDisposed.Value;
/// <summary>
/// Changes the start time and the interval between method invocations for the internal timer.
/// </summary>
/// <param name="dueTime">The due time.</param>
/// <param name="period">The period.</param>
public void Change(int dueTime, int period)
{
_period = period;
public void Change(Int32 dueTime, Int32 period) {
this._period = period;
_backingTimer.Change(dueTime, Timeout.Infinite);
_ = this._backingTimer.Change(dueTime, Timeout.Infinite);
}
/// <summary>
@@ -123,46 +114,42 @@
/// <param name="dueTime">The due time.</param>
/// <param name="period">The period.</param>
public void Change(TimeSpan dueTime, TimeSpan period)
=> Change(Convert.ToInt32(dueTime.TotalMilliseconds), Convert.ToInt32(period.TotalMilliseconds));
=> this.Change(Convert.ToInt32(dueTime.TotalMilliseconds), Convert.ToInt32(period.TotalMilliseconds));
/// <summary>
/// Changes the interval between method invocations for the internal timer.
/// </summary>
/// <param name="period">The period.</param>
public void Resume(int period) => Change(0, period);
public void Resume(Int32 period) => this.Change(0, period);
/// <summary>
/// Changes the interval between method invocations for the internal timer.
/// </summary>
/// <param name="period">The period.</param>
public void Resume(TimeSpan period) => Change(TimeSpan.Zero, period);
public void Resume(TimeSpan period) => this.Change(TimeSpan.Zero, period);
/// <summary>
/// Pauses this instance.
/// </summary>
public void Pause() => Change(Timeout.Infinite, Timeout.Infinite);
public void Pause() => this.Change(Timeout.Infinite, Timeout.Infinite);
/// <inheritdoc />
public void Dispose()
{
lock (_syncLock)
{
if (_isDisposed == true || _isDisposing == true)
public void Dispose() {
lock(this._syncLock) {
if(this._isDisposed == true || this._isDisposing == true) {
return;
_isDisposing.Value = true;
}
try
{
_backingTimer.Dispose();
_cycleDoneEvent.Wait();
_cycleDoneEvent.Dispose();
this._isDisposing.Value = true;
}
finally
{
_isDisposed.Value = true;
_isDisposing.Value = false;
try {
this._backingTimer.Dispose();
this._cycleDoneEvent.Wait();
this._cycleDoneEvent.Dispose();
} finally {
this._isDisposed.Value = true;
this._isDisposing.Value = false;
}
}
@@ -170,27 +157,24 @@
/// Logic that runs every time the timer hits the due time.
/// </summary>
/// <param name="state">The state.</param>
private void InternalCallback(object state)
{
lock (_syncLock)
{
if (IsDisposed || IsDisposing)
private void InternalCallback(Object state) {
lock(this._syncLock) {
if(this.IsDisposed || this.IsDisposing) {
return;
}
}
if(this._cycleDoneEvent.IsSet == false) {
return;
}
if (_cycleDoneEvent.IsSet == false)
return;
this._cycleDoneEvent.Reset();
_cycleDoneEvent.Reset();
try
{
_userCallback(state);
}
finally
{
_cycleDoneEvent?.Set();
_backingTimer?.Change(_period, Timeout.Infinite);
try {
this._userCallback(state);
} finally {
this._cycleDoneEvent?.Set();
_ = this._backingTimer?.Change(this._period, Timeout.Infinite);
}
}
}
@@ -1,24 +1,21 @@
namespace Unosquare.Swan.Abstractions
{
using System;
using System.Linq;
using System.Collections.Generic;
using System.Linq.Expressions;
using System;
using System.Linq;
using System.Collections.Generic;
using System.Linq.Expressions;
namespace Unosquare.Swan.Abstractions {
/// <summary>
/// Represents a generic expression parser.
/// </summary>
public abstract class ExpressionParser
{
public abstract class ExpressionParser {
/// <summary>
/// Resolves the expression.
/// </summary>
/// <typeparam name="T">The type of expression result.</typeparam>
/// <param name="tokens">The tokens.</param>
/// <returns>The representation of the expression parsed.</returns>
public virtual T ResolveExpression<T>(IEnumerable<Token> tokens)
{
var conversion = Expression.Convert(Parse(tokens), typeof(T));
public virtual T ResolveExpression<T>(IEnumerable<Token> tokens) {
UnaryExpression conversion = Expression.Convert(this.Parse(tokens), typeof(T));
return Expression.Lambda<Func<T>>(conversion).Compile()();
}
@@ -27,17 +24,15 @@
/// </summary>
/// <param name="tokens">The tokens.</param>
/// <returns>The final expression.</returns>
public virtual Expression Parse(IEnumerable<Token> tokens)
{
var expressionStack = new List<Stack<Expression>>();
public virtual Expression Parse(IEnumerable<Token> tokens) {
List<Stack<Expression>> expressionStack = new List<Stack<Expression>>();
foreach (var token in tokens)
{
if (expressionStack.Any() == false)
foreach(Token token in tokens) {
if(expressionStack.Any() == false) {
expressionStack.Add(new Stack<Expression>());
}
switch (token.Type)
{
switch(token.Type) {
case TokenType.Wall:
expressionStack.Add(new Stack<Expression>());
break;
@@ -45,21 +40,20 @@
expressionStack.Last().Push(Expression.Constant(Convert.ToDecimal(token.Value)));
break;
case TokenType.Variable:
ResolveVariable(token.Value, expressionStack.Last());
this.ResolveVariable(token.Value, expressionStack.Last());
break;
case TokenType.String:
expressionStack.Last().Push(Expression.Constant(token.Value));
break;
case TokenType.Operator:
ResolveOperator(token.Value, expressionStack.Last());
this.ResolveOperator(token.Value, expressionStack.Last());
break;
case TokenType.Function:
ResolveFunction(token.Value, expressionStack.Last());
this.ResolveFunction(token.Value, expressionStack.Last());
if (expressionStack.Count > 1 && expressionStack.Last().Count == 1)
{
var lastValue = expressionStack.Last().Pop();
expressionStack.Remove(expressionStack.Last());
if(expressionStack.Count > 1 && expressionStack.Last().Count == 1) {
Expression lastValue = expressionStack.Last().Pop();
_ = expressionStack.Remove(expressionStack.Last());
expressionStack.Last().Push(lastValue);
}
@@ -75,20 +69,20 @@
/// </summary>
/// <param name="value">The value.</param>
/// <param name="expressionStack">The expression stack.</param>
public abstract void ResolveVariable(string value, Stack<Expression> expressionStack);
public abstract void ResolveVariable(String value, Stack<Expression> expressionStack);
/// <summary>
/// Resolves the operator.
/// </summary>
/// <param name="value">The value.</param>
/// <param name="expressionStack">The expression stack.</param>
public abstract void ResolveOperator(string value, Stack<Expression> expressionStack);
public abstract void ResolveOperator(String value, Stack<Expression> expressionStack);
/// <summary>
/// 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);
public abstract void ResolveFunction(String value, Stack<Expression> expressionStack);
}
}
+14 -10
View File
@@ -1,27 +1,31 @@
namespace Unosquare.Swan.Abstractions
{
using System;
using System.Collections.Generic;
using System.Reflection;
using System;
using System.Collections.Generic;
using System.Reflection;
namespace Unosquare.Swan.Abstractions {
/// <summary>
/// Interface object map.
/// </summary>
public interface IObjectMap
{
public interface IObjectMap {
/// <summary>
/// Gets or sets the map.
/// </summary>
Dictionary<PropertyInfo, List<PropertyInfo>> Map { get; }
Dictionary<PropertyInfo, List<PropertyInfo>> Map {
get;
}
/// <summary>
/// Gets or sets the type of the source.
/// </summary>
Type SourceType { get; }
Type SourceType {
get;
}
/// <summary>
/// Gets or sets the type of the destination.
/// </summary>
Type DestinationType { get; }
Type DestinationType {
get;
}
}
}
@@ -1,12 +1,10 @@
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
{
public interface ISyncLocker : IDisposable {
/// <summary>
/// Acquires a writer lock.
/// The lock is released when the returned locking object is disposed.
@@ -1,14 +1,16 @@
namespace Unosquare.Swan.Abstractions
{
using System;
namespace Unosquare.Swan.Abstractions {
/// <summary>
/// A simple Validator interface.
/// </summary>
public interface IValidator
{
public interface IValidator {
/// <summary>
/// The error message.
/// </summary>
string ErrorMessage { get; }
String ErrorMessage {
get;
}
/// <summary>
/// Checks if a value is valid.
@@ -16,6 +18,6 @@
/// <typeparam name="T">The type.</typeparam>
/// <param name="value"> The value.</param>
/// <returns>True if it is valid.False if it is not.</returns>
bool IsValid<T>(T value);
Boolean IsValid<T>(T value);
}
}
+16 -10
View File
@@ -1,33 +1,39 @@
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
{
public interface IWaitEvent : IDisposable {
/// <summary>
/// Gets a value indicating whether the event is in the completed state.
/// </summary>
bool IsCompleted { get; }
Boolean IsCompleted {
get;
}
/// <summary>
/// Gets a value indicating whether the Begin method has been called.
/// It returns false after the Complete method is called.
/// </summary>
bool IsInProgress { get; }
Boolean IsInProgress {
get;
}
/// <summary>
/// Returns true if the underlying handle is not closed and it is still valid.
/// </summary>
bool IsValid { get; }
Boolean IsValid {
get;
}
/// <summary>
/// Gets a value indicating whether this instance is disposed.
/// </summary>
bool IsDisposed { get; }
Boolean IsDisposed {
get;
}
/// <summary>
/// Enters the state in which waiters need to wait.
@@ -52,6 +58,6 @@
/// </summary>
/// <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>
bool Wait(TimeSpan timeout);
Boolean Wait(TimeSpan timeout);
}
}
+2 -4
View File
@@ -1,10 +1,8 @@
namespace Unosquare.Swan.Abstractions
{
namespace Unosquare.Swan.Abstractions {
/// <summary>
/// A simple interface for application workers.
/// </summary>
public interface IWorker
{
public interface IWorker {
/// <summary>
/// Should start the task immediately and asynchronously.
/// </summary>
+64 -78
View File
@@ -1,16 +1,12 @@
#if !NETSTANDARD1_3
namespace Unosquare.Swan.Abstractions
{
using System;
using System.Collections.Generic;
using System.Threading;
using Swan;
using System;
using System.Collections.Generic;
using System.Threading;
namespace Unosquare.Swan.Abstractions {
/// <summary>
/// Represents an background worker abstraction with a life cycle and running at a independent thread.
/// </summary>
public abstract class RunnerBase
{
public abstract class RunnerBase {
private Thread _worker;
private CancellationTokenSource _cancelTokenSource;
private ManualResetEvent _workFinished;
@@ -19,10 +15,9 @@ namespace Unosquare.Swan.Abstractions
/// Initializes a new instance of the <see cref="RunnerBase"/> class.
/// </summary>
/// <param name="isEnabled">if set to <c>true</c> [is enabled].</param>
protected RunnerBase(bool isEnabled)
{
Name = GetType().Name;
IsEnabled = isEnabled;
protected RunnerBase(Boolean isEnabled) {
this.Name = this.GetType().Name;
this.IsEnabled = isEnabled;
}
/// <summary>
@@ -31,7 +26,7 @@ namespace Unosquare.Swan.Abstractions
/// <value>
/// The error messages.
/// </value>
public List<string> ErrorMessages { get; } = new List<string>();
public List<String> ErrorMessages { get; } = new List<String>();
/// <summary>
/// Gets the name.
@@ -39,7 +34,9 @@ namespace Unosquare.Swan.Abstractions
/// <value>
/// The name.
/// </value>
public string Name { get; }
public String Name {
get;
}
/// <summary>
/// Gets a value indicating whether this instance is running.
@@ -47,7 +44,9 @@ namespace Unosquare.Swan.Abstractions
/// <value>
/// <c>true</c> if this instance is running; otherwise, <c>false</c>.
/// </value>
public bool IsRunning { get; private set; }
public Boolean IsRunning {
get; private set;
}
/// <summary>
/// Gets a value indicating whether this instance is enabled.
@@ -55,68 +54,59 @@ namespace Unosquare.Swan.Abstractions
/// <value>
/// <c>true</c> if this instance is enabled; otherwise, <c>false</c>.
/// </value>
public bool IsEnabled { get; }
public Boolean IsEnabled {
get;
}
/// <summary>
/// Starts this instance.
/// </summary>
public virtual void Start()
{
if (IsEnabled == false)
public virtual void Start() {
if(this.IsEnabled == false) {
return;
}
$"Start Requested".Debug(Name);
_cancelTokenSource = new CancellationTokenSource();
_workFinished = new ManualResetEvent(false);
$"Start Requested".Debug(this.Name);
this._cancelTokenSource = new CancellationTokenSource();
this._workFinished = new ManualResetEvent(false);
_worker = new Thread(() =>
{
_workFinished.Reset();
IsRunning = true;
try
{
Setup();
DoBackgroundWork(_cancelTokenSource.Token);
this._worker = new Thread(() => {
_ = this._workFinished.Reset();
this.IsRunning = true;
try {
this.Setup();
this.DoBackgroundWork(this._cancelTokenSource.Token);
} catch(ThreadAbortException) {
$"{nameof(ThreadAbortException)} caught.".Warn(this.Name);
} catch(Exception ex) {
$"{ex.GetType()}: {ex.Message}\r\n{ex.StackTrace}".Error(this.Name);
} finally {
this.Cleanup();
_ = this._workFinished?.Set();
this.IsRunning = false;
"Stopped Completely".Debug(this.Name);
}
catch (ThreadAbortException)
{
$"{nameof(ThreadAbortException)} caught.".Warn(Name);
}
catch (Exception ex)
{
$"{ex.GetType()}: {ex.Message}\r\n{ex.StackTrace}".Error(Name);
}
finally
{
Cleanup();
_workFinished?.Set();
IsRunning = false;
"Stopped Completely".Debug(Name);
}
})
{
}) {
IsBackground = true,
Name = $"{Name}Thread",
Name = $"{this.Name}Thread",
};
_worker.Start();
this._worker.Start();
}
/// <summary>
/// Stops this instance.
/// </summary>
public virtual void Stop()
{
if (IsEnabled == false || IsRunning == false)
public virtual void Stop() {
if(this.IsEnabled == false || this.IsRunning == false) {
return;
}
$"Stop Requested".Debug(Name);
_cancelTokenSource.Cancel();
var waitRetries = 5;
while (waitRetries >= 1)
{
if (_workFinished.WaitOne(250))
{
$"Stop Requested".Debug(this.Name);
this._cancelTokenSource.Cancel();
Int32 waitRetries = 5;
while(waitRetries >= 1) {
if(this._workFinished.WaitOne(250)) {
waitRetries = -1;
break;
}
@@ -124,38 +114,34 @@ namespace Unosquare.Swan.Abstractions
waitRetries--;
}
if (waitRetries < 0)
{
"Workbench stopped gracefully".Debug(Name);
}
else
{
"Did not respond to stop request. Aborting thread and waiting . . .".Warn(Name);
_worker.Abort();
if(waitRetries < 0) {
"Workbench stopped gracefully".Debug(this.Name);
} else {
"Did not respond to stop request. Aborting thread and waiting . . .".Warn(this.Name);
this._worker.Abort();
if (_workFinished.WaitOne(5000) == false)
"Waited and no response. Worker might have been left in an inconsistent state.".Error(Name);
else
"Waited for worker and it finally responded (OK).".Debug(Name);
if(this._workFinished.WaitOne(5000) == false) {
"Waited and no response. Worker might have been left in an inconsistent state.".Error(this.Name);
} else {
"Waited for worker and it finally responded (OK).".Debug(this.Name);
}
}
_workFinished.Dispose();
_workFinished = null;
this._workFinished.Dispose();
this._workFinished = null;
}
/// <summary>
/// Setups this instance.
/// </summary>
protected virtual void Setup()
{
protected virtual void Setup() {
// empty
}
/// <summary>
/// Cleanups this instance.
/// </summary>
protected virtual void Cleanup()
{
protected virtual void Cleanup() {
// empty
}
@@ -1,14 +1,13 @@
namespace Unosquare.Swan.Abstractions
{
using Formatters;
using Reflection;
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using Unosquare.Swan.Formatters;
using Unosquare.Swan.Reflection;
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
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>
@@ -42,9 +41,8 @@
/// </example>
/// <typeparam name="T">The type of settings model.</typeparam>
public sealed class SettingsProvider<T>
: SingletonBase<SettingsProvider<T>>
{
private readonly object _syncRoot = new object();
: SingletonBase<SettingsProvider<T>> {
private readonly Object _syncRoot = new Object();
private T _global;
@@ -55,7 +53,9 @@
/// <value>
/// The configuration file path.
/// </value>
public string ConfigurationFilePath { get; set; } =
public String ConfigurationFilePath {
get; set;
} =
#if NETSTANDARD1_3
Path.Combine(Runtime.LocalStoragePath, "appsettings.json");
#else
@@ -68,16 +68,14 @@
/// <value>
/// The global settings object.
/// </value>
public T Global
{
get
{
lock (_syncRoot)
{
if (Equals(_global, default(T)))
ReloadGlobalSettings();
public T Global {
get {
lock(this._syncRoot) {
if(Equals(this._global, default(T))) {
this.ReloadGlobalSettings();
}
return _global;
return this._global;
}
}
}
@@ -85,22 +83,21 @@
/// <summary>
/// Reloads the global settings.
/// </summary>
public void ReloadGlobalSettings()
{
if (File.Exists(ConfigurationFilePath) == false || File.ReadAllText(ConfigurationFilePath).Length == 0)
{
ResetGlobalSettings();
public void ReloadGlobalSettings() {
if(File.Exists(this.ConfigurationFilePath) == false || File.ReadAllText(this.ConfigurationFilePath).Length == 0) {
this.ResetGlobalSettings();
return;
}
lock (_syncRoot)
_global = Json.Deserialize<T>(File.ReadAllText(ConfigurationFilePath));
lock(this._syncRoot) {
this._global = Json.Deserialize<T>(File.ReadAllText(this.ConfigurationFilePath));
}
}
/// <summary>
/// Persists the global settings.
/// </summary>
public void PersistGlobalSettings() => File.WriteAllText(ConfigurationFilePath, Json.Serialize(Global, true));
public void PersistGlobalSettings() => File.WriteAllText(this.ConfigurationFilePath, Json.Serialize(this.Global, true));
/// <summary>
/// Updates settings from list.
@@ -110,29 +107,32 @@
/// A list of settings of type ref="ExtendedPropertyInfo".
/// </returns>
/// <exception cref="ArgumentNullException">propertyList.</exception>
public List<string> RefreshFromList(List<ExtendedPropertyInfo<T>> propertyList)
{
if (propertyList == null)
public List<String> RefreshFromList(List<ExtendedPropertyInfo<T>> propertyList) {
if(propertyList == null) {
throw new ArgumentNullException(nameof(propertyList));
}
var changedSettings = new List<string>();
var globalProps = Runtime.PropertyTypeCache.RetrieveAllProperties<T>();
List<String> changedSettings = new List<String>();
IEnumerable<PropertyInfo> globalProps = Runtime.PropertyTypeCache.RetrieveAllProperties<T>();
foreach (var property in propertyList)
{
var propertyInfo = globalProps.FirstOrDefault(x => x.Name == property.Property);
foreach(ExtendedPropertyInfo<T> property in propertyList) {
PropertyInfo propertyInfo = globalProps.FirstOrDefault(x => x.Name == property.Property);
if (propertyInfo == null) continue;
if(propertyInfo == null) {
continue;
}
var originalValue = propertyInfo.GetValue(Global);
var isChanged = propertyInfo.PropertyType.IsArray
? property.Value is IEnumerable enumerable && propertyInfo.TrySetArray(enumerable.Cast<object>(), Global)
: SetValue(property.Value, originalValue, propertyInfo);
Object originalValue = propertyInfo.GetValue(this.Global);
Boolean isChanged = propertyInfo.PropertyType.IsArray
? property.Value is IEnumerable enumerable && propertyInfo.TrySetArray(enumerable.Cast<Object>(), this.Global)
: this.SetValue(property.Value, originalValue, propertyInfo);
if (!isChanged) continue;
if(!isChanged) {
continue;
}
changedSettings.Add(property.Property);
PersistGlobalSettings();
this.PersistGlobalSettings();
}
return changedSettings;
@@ -142,9 +142,8 @@
/// Gets the list.
/// </summary>
/// <returns>A List of ExtendedPropertyInfo of the type T.</returns>
public List<ExtendedPropertyInfo<T>> GetList()
{
var jsonData = Json.Deserialize(Json.Serialize(Global)) as Dictionary<string, object>;
public List<ExtendedPropertyInfo<T>> GetList() {
Dictionary<String, Object> jsonData = Json.Deserialize(Json.Serialize(this.Global)) as Dictionary<String, Object>;
return jsonData?.Keys
.Select(p => new ExtendedPropertyInfo<T>(p) { Value = jsonData[p] })
@@ -154,28 +153,25 @@
/// <summary>
/// Resets the global settings.
/// </summary>
public void ResetGlobalSettings()
{
lock (_syncRoot)
_global = Activator.CreateInstance<T>();
PersistGlobalSettings();
public void ResetGlobalSettings() {
lock(this._syncRoot) {
this._global = Activator.CreateInstance<T>();
}
private bool SetValue(object property, object originalValue, PropertyInfo propertyInfo)
{
switch (property)
{
this.PersistGlobalSettings();
}
private Boolean SetValue(Object property, Object originalValue, PropertyInfo propertyInfo) {
switch(property) {
case null when originalValue == null:
break;
case null:
propertyInfo.SetValue(Global, null);
propertyInfo.SetValue(this.Global, null);
return true;
default:
if (propertyInfo.PropertyType.TryParseBasicType(property, out var propertyValue) &&
!propertyValue.Equals(originalValue))
{
propertyInfo.SetValue(Global, propertyValue);
if(propertyInfo.PropertyType.TryParseBasicType(property, out Object propertyValue) &&
!propertyValue.Equals(originalValue)) {
propertyInfo.SetValue(this.Global, propertyValue);
return true;
}
@@ -1,14 +1,12 @@
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
{
where T : class {
/// <summary>
/// The static, singleton instance reference.
/// </summary>
@@ -16,7 +14,7 @@
valueFactory: () => Activator.CreateInstance(typeof(T), true) as T,
isThreadSafe: true);
private bool _isDisposing; // To detect redundant calls
private Boolean _isDisposing; // To detect redundant calls
/// <summary>
/// Gets the instance that this singleton represents.
@@ -28,7 +26,7 @@
public static T Instance => LazyInstance.Value;
/// <inheritdoc />
public void Dispose() => Dispose(true);
public void Dispose() => this.Dispose(true);
/// <summary>
/// Releases unmanaged and - optionally - managed resources.
@@ -36,22 +34,22 @@
/// a non-default class finalizer (destructor).
/// </summary>
/// <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)
{
if (_isDisposing) return;
protected virtual void Dispose(Boolean disposeManaged) {
if(this._isDisposing) {
return;
}
_isDisposing = true;
this._isDisposing = true;
// free managed resources
if (LazyInstance == null) return;
try
{
var disposableInstance = LazyInstance.Value as IDisposable;
disposableInstance?.Dispose();
if(LazyInstance == null) {
return;
}
catch
{
try {
IDisposable disposableInstance = LazyInstance.Value as IDisposable;
disposableInstance?.Dispose();
} catch {
// swallow
}
}
+117 -126
View File
@@ -1,22 +1,20 @@
namespace Unosquare.Swan.Abstractions
{
using System;
using System.Collections.Generic;
using System.Linq;
using System;
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 = '-';
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 const String OpenFuncStr = "(";
private readonly List<Operator> _operators = new List<Operator>();
@@ -84,10 +82,9 @@
/// </list>
/// </summary>
/// <param name="input">The input.</param>
protected Tokenizer(string input)
{
_operators.AddRange(GetDefaultOperators());
Tokenize(input);
protected Tokenizer(String input) {
this._operators.AddRange(this.GetDefaultOperators());
this.Tokenize(input);
}
/// <summary>
@@ -95,10 +92,9 @@
/// </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);
protected Tokenizer(String input, IEnumerable<Operator> operators) {
this._operators.AddRange(operators);
this.Tokenize(input);
}
/// <summary>
@@ -115,14 +111,14 @@
/// <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);
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);
public abstract TokenType ResolveFunctionOrMemberType(String input);
/// <summary>
/// Evaluates the function or member.
@@ -130,7 +126,7 @@
/// <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;
public virtual Boolean EvaluateFunctionOrMember(String input, Int32 position) => false;
/// <summary>
/// Gets the default operators.
@@ -165,14 +161,11 @@
/// or
/// Mismatched parenthesis.
/// </exception>
public virtual IEnumerable<Token> ShuntingYard(bool includeFunctionStopper = true)
{
var stack = new Stack<Token>();
public virtual IEnumerable<Token> ShuntingYard(Boolean includeFunctionStopper = true) {
Stack<Token> stack = new Stack<Token>();
foreach (var tok in Tokens)
{
switch (tok.Type)
{
foreach(Token tok in this.Tokens) {
switch(tok.Type) {
case TokenType.Number:
case TokenType.Variable:
case TokenType.String:
@@ -182,38 +175,37 @@
stack.Push(tok);
break;
case TokenType.Operator:
while (stack.Any() && stack.Peek().Type == TokenType.Operator &&
CompareOperators(tok.Value, stack.Peek().Value))
while(stack.Any() && stack.Peek().Type == TokenType.Operator &&
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))
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)
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)
} else {
while(stack.Peek().Value != OpenFuncStr) {
yield return stack.Pop();
}
stack.Pop();
_ = stack.Pop();
if (stack.Any() && stack.Peek().Type == TokenType.Function)
{
if(stack.Any() && stack.Peek().Type == TokenType.Function) {
yield return stack.Pop();
}
}
@@ -224,142 +216,136 @@
}
}
while (stack.Any())
{
var tok = stack.Pop();
if (tok.Type == TokenType.Parenthesis)
while(stack.Any()) {
Token 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
private static Boolean 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))
{
private void Tokenize(String input) {
if(!this.ValidateInput(input, out Int32 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] })));
for(Int32 i = startIndex; i < input.Length; i++) {
if(Char.IsWhiteSpace(input, i)) {
continue;
}
if (input[i] == StringQuotedChar)
{
i = ExtractString(input, i);
if(input[i] == CommaChar) {
this.Tokens.Add(new Token(TokenType.Comma, new String(new[] { input[i] })));
continue;
}
if (char.IsLetter(input, i) || EvaluateFunctionOrMember(input, i))
{
i = ExtractFunctionOrMember(input, i);
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) || (
if(Char.IsNumber(input, i) ||
input[i] == NegativeChar &&
((Tokens.Any() && Tokens.Last().Type != TokenType.Number) || !Tokens.Any())))
{
i = ExtractNumber(input, i);
(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)
{
Tokens.Add(new Token(TokenType.Parenthesis, new string(new[] { input[i] })));
if(input[i] == OpenFuncChar ||
input[i] == CloseFuncChar) {
this.Tokens.Add(new Token(TokenType.Parenthesis, new String(new[] { input[i] })));
continue;
}
i = ExtractOperator(input, i);
i = this.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]))
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
var value = input.SliceLength(i + right, charCount);
Tokens.Add(new Token(tokenTypeEvaluation(value), value));
String value = input.SliceLength(i + right, charCount);
this.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 ||
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));
Char.IsWhiteSpace(x) ||
Char.IsNumber(x));
private int ExtractFunctionOrMember(string input, int i) =>
ExtractData(input, i, ResolveFunctionOrMemberType, x => x == OpenFuncChar ||
private Int32 ExtractFunctionOrMember(String input, Int32 i) =>
this.ExtractData(input, i, this.ResolveFunctionOrMemberType, x => x == OpenFuncChar ||
x == CloseFuncChar ||
x == CommaChar ||
char.IsWhiteSpace(x));
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 Int32 ExtractNumber(String input, Int32 i) =>
this.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);
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)
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 Boolean CompareOperators(String op1, String op2)
=> CompareOperators(this.GetOperatorOrDefault(op1), this.GetOperatorOrDefault(op2));
private Operator GetOperatorOrDefault(string op)
=> _operators.FirstOrDefault(x => x.Name == op) ?? new Operator { Name = op, Precedence = 0 };
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
{
public class Operator {
/// <summary>
/// Gets or sets the name.
/// </summary>
/// <value>
/// The name.
/// </value>
public string Name { get; set; }
public String Name {
get; set;
}
/// <summary>
/// Gets or sets the precedence.
@@ -367,7 +353,9 @@
/// <value>
/// The precedence.
/// </value>
public int Precedence { get; set; }
public Int32 Precedence {
get; set;
}
/// <summary>
/// Gets or sets a value indicating whether [right associative].
@@ -375,23 +363,23 @@
/// <value>
/// <c>true</c> if [right associative]; otherwise, <c>false</c>.
/// </value>
public bool RightAssociative { get; set; }
public Boolean RightAssociative {
get; set;
}
}
/// <summary>
/// Represents a Token structure.
/// </summary>
public struct Token
{
public struct Token {
/// <summary>
/// Initializes a new instance of the <see cref="Token"/> struct.
/// </summary>
/// <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;
public Token(TokenType type, String value) {
this.Type = type;
this.Value = type == TokenType.Function || type == TokenType.Operator ? value.ToLowerInvariant() : value;
}
/// <summary>
@@ -400,7 +388,9 @@
/// <value>
/// The type.
/// </value>
public TokenType Type { get; set; }
public TokenType Type {
get; set;
}
/// <summary>
/// Gets the value.
@@ -408,14 +398,15 @@
/// <value>
/// The value.
/// </value>
public string Value { get; }
public String Value {
get;
}
}
/// <summary>
/// Enums the token types.
/// </summary>
public enum TokenType
{
public enum TokenType {
/// <summary>
/// The number
/// </summary>
@@ -1,37 +1,32 @@
namespace Unosquare.Swan.Lite.Abstractions
{
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Runtime.CompilerServices;
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, bool> QueuedNotifications = new ConcurrentDictionary<string, bool>();
private readonly bool UseDeferredNotifications;
public abstract class ViewModelBase : INotifyPropertyChanged {
private readonly ConcurrentDictionary<String, Boolean> QueuedNotifications = new ConcurrentDictionary<String, Boolean>();
private readonly Boolean UseDeferredNotifications;
/// <summary>
/// Initializes a new instance of the <see cref="ViewModelBase"/> class.
/// </summary>
/// <param name="useDeferredNotifications">Set to <c>true</c> to use deferred notifications in the background.</param>
protected ViewModelBase(bool useDeferredNotifications)
{
UseDeferredNotifications = useDeferredNotifications;
}
protected ViewModelBase(Boolean useDeferredNotifications) => this.UseDeferredNotifications = useDeferredNotifications;
/// <summary>
/// Initializes a new instance of the <see cref="ViewModelBase"/> class.
/// </summary>
protected ViewModelBase()
: this(false)
{
: this(false) {
// placeholder
}
@@ -52,13 +47,13 @@
/// <param name="notifyAlso">An rray of property names to notify in addition to notifying the changes on the current property name.</param>
/// <returns>True if the value was changed, false if the existing value matched the
/// desired value.</returns>
protected bool SetProperty<T>(ref T storage, T value, [CallerMemberName] string propertyName = "", string[] notifyAlso = null)
{
if (EqualityComparer<T>.Default.Equals(storage, value))
protected Boolean SetProperty<T>(ref T storage, T value, [CallerMemberName] String propertyName = "", String[] notifyAlso = null) {
if(EqualityComparer<T>.Default.Equals(storage, value)) {
return false;
}
storage = value;
NotifyPropertyChanged(propertyName, notifyAlso);
this.NotifyPropertyChanged(propertyName, notifyAlso);
return true;
}
@@ -66,54 +61,55 @@
/// Notifies one or more properties changed.
/// </summary>
/// <param name="propertyNames">The property names.</param>
protected void NotifyPropertyChanged(params string[] propertyNames) => NotifyPropertyChanged(null, propertyNames);
protected void NotifyPropertyChanged(params String[] propertyNames) => this.NotifyPropertyChanged(null, propertyNames);
/// <summary>
/// Notifies one or more properties changed.
/// </summary>
/// <param name="mainProperty">The main property.</param>
/// <param name="auxiliaryProperties">The auxiliary properties.</param>
private void NotifyPropertyChanged(string mainProperty, string[] auxiliaryProperties)
{
private void NotifyPropertyChanged(String mainProperty, String[] auxiliaryProperties) {
// Queue property notification
if (string.IsNullOrWhiteSpace(mainProperty) == false)
QueuedNotifications[mainProperty] = true;
if(String.IsNullOrWhiteSpace(mainProperty) == false) {
this.QueuedNotifications[mainProperty] = true;
}
// Set the state for notification properties
if (auxiliaryProperties != null)
{
foreach (var property in auxiliaryProperties)
{
if (string.IsNullOrWhiteSpace(property) == false)
QueuedNotifications[property] = true;
if(auxiliaryProperties != null) {
foreach(String property in auxiliaryProperties) {
if(String.IsNullOrWhiteSpace(property) == false) {
this.QueuedNotifications[property] = true;
}
}
}
// Depending on operation mode, either fire the notifications in the background
// or fire them immediately
if (UseDeferredNotifications)
Task.Run(() => NotifyQueuedProperties());
else
NotifyQueuedProperties();
if(this.UseDeferredNotifications) {
_ = Task.Run(() => this.NotifyQueuedProperties());
} else {
this.NotifyQueuedProperties();
}
}
/// <summary>
/// Notifies the queued properties and resets the property name to a non-queued stated.
/// </summary>
private void NotifyQueuedProperties()
{
private void NotifyQueuedProperties() {
// get a snapshot of property names.
var propertyNames = QueuedNotifications.Keys.ToArray();
String[] propertyNames = this.QueuedNotifications.Keys.ToArray();
// Iterate through the properties
foreach (var property in propertyNames)
{
foreach(String property in propertyNames) {
// don't notify if we don't have a change
if (!QueuedNotifications[property]) continue;
if(!this.QueuedNotifications[property]) {
continue;
}
// notify and reset queued state to false
try { OnPropertyChanged(property); }
finally { QueuedNotifications[property] = false; }
try {
this.OnPropertyChanged(property);
} finally { this.QueuedNotifications[property] = false; }
}
}
@@ -121,7 +117,7 @@
/// 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));
private void OnPropertyChanged(String propertyName) =>
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName ?? String.Empty));
}
}
+8 -10
View File
@@ -1,26 +1,24 @@
namespace Unosquare.Swan
{
using Abstractions;
using System;
using Unosquare.Swan.Abstractions;
namespace Unosquare.Swan {
/// <summary>
/// Fast, atomic boolean combining interlocked to write value and volatile to read values.
/// </summary>
public sealed class AtomicBoolean : AtomicTypeBase<bool>
{
public sealed class AtomicBoolean : AtomicTypeBase<Boolean> {
/// <summary>
/// Initializes a new instance of the <see cref="AtomicBoolean"/> class.
/// </summary>
/// <param name="initialValue">if set to <c>true</c> [initial value].</param>
public AtomicBoolean(bool initialValue = default)
: base(initialValue ? 1 : 0)
{
public AtomicBoolean(Boolean initialValue = default)
: base(initialValue ? 1 : 0) {
// placeholder
}
/// <inheritdoc/>
protected override bool FromLong(long backingValue) => backingValue != 0;
protected override Boolean FromLong(Int64 backingValue) => backingValue != 0;
/// <inheritdoc/>
protected override long ToLong(bool value) => value ? 1 : 0;
protected override Int64 ToLong(Boolean value) => value ? 1 : 0;
}
}
+8 -11
View File
@@ -1,29 +1,26 @@
namespace Unosquare.Swan
{
using System;
using Abstractions;
using System;
using Unosquare.Swan.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>
{
public sealed class AtomicDouble : AtomicTypeBase<Double> {
/// <summary>
/// Initializes a new instance of the <see cref="AtomicDouble"/> class.
/// </summary>
/// <param name="initialValue">if set to <c>true</c> [initial value].</param>
public AtomicDouble(double initialValue = default)
: base(BitConverter.DoubleToInt64Bits(initialValue))
{
public AtomicDouble(Double initialValue = default)
: base(BitConverter.DoubleToInt64Bits(initialValue)) {
// placeholder
}
/// <inheritdoc/>
protected override double FromLong(long backingValue) =>
protected override Double FromLong(Int64 backingValue) =>
BitConverter.Int64BitsToDouble(backingValue);
/// <inheritdoc/>
protected override long ToLong(double value) =>
protected override Int64 ToLong(Double value) =>
BitConverter.DoubleToInt64Bits(value);
}
}
+8 -11
View File
@@ -1,29 +1,26 @@
namespace Unosquare.Swan
{
using System;
using Abstractions;
using System;
using Unosquare.Swan.Abstractions;
namespace Unosquare.Swan {
/// <summary>
/// Represents an atomically readable or writable integer.
/// </summary>
public class AtomicInteger : AtomicTypeBase<int>
{
public class AtomicInteger : AtomicTypeBase<Int32> {
/// <summary>
/// Initializes a new instance of the <see cref="AtomicInteger"/> class.
/// </summary>
/// <param name="initialValue">if set to <c>true</c> [initial value].</param>
public AtomicInteger(int initialValue = default)
: base(Convert.ToInt64(initialValue))
{
public AtomicInteger(Int32 initialValue = default)
: base(Convert.ToInt64(initialValue)) {
// placeholder
}
/// <inheritdoc/>
protected override int FromLong(long backingValue) =>
protected override Int32 FromLong(Int64 backingValue) =>
Convert.ToInt32(backingValue);
/// <inheritdoc/>
protected override long ToLong(int value) =>
protected override Int64 ToLong(Int32 value) =>
Convert.ToInt64(value);
}
}
+8 -10
View File
@@ -1,26 +1,24 @@
namespace Unosquare.Swan
{
using Abstractions;
using System;
using Unosquare.Swan.Abstractions;
namespace Unosquare.Swan {
/// <summary>
/// Fast, atomioc long combining interlocked to write value and volatile to read values.
/// </summary>
public sealed class AtomicLong : AtomicTypeBase<long>
{
public sealed class AtomicLong : AtomicTypeBase<Int64> {
/// <summary>
/// Initializes a new instance of the <see cref="AtomicLong"/> class.
/// </summary>
/// <param name="initialValue">if set to <c>true</c> [initial value].</param>
public AtomicLong(long initialValue = default)
: base(initialValue)
{
public AtomicLong(Int64 initialValue = default)
: base(initialValue) {
// placeholder
}
/// <inheritdoc />
protected override long FromLong(long backingValue) => backingValue;
protected override Int64 FromLong(Int64 backingValue) => backingValue;
/// <inheritdoc />
protected override long ToLong(long value) => value;
protected override Int64 ToLong(Int64 value) => value;
}
}
@@ -1,31 +1,27 @@
namespace Unosquare.Swan.Attributes
{
using System;
using System;
namespace Unosquare.Swan.Attributes {
/// <summary>
/// Models an option specification.
/// Based on CommandLine (Copyright 2005-2015 Giacomo Stelluti Scala and Contributors.).
/// </summary>
[AttributeUsage(AttributeTargets.Property)]
public sealed class ArgumentOptionAttribute
: Attribute
{
: Attribute {
/// <summary>
/// Initializes a new instance of the <see cref="ArgumentOptionAttribute"/> class.
/// The default long name will be inferred from target property.
/// </summary>
public ArgumentOptionAttribute()
: this(string.Empty, string.Empty)
{
: this(String.Empty, String.Empty) {
}
/// <summary>
/// Initializes a new instance of the <see cref="ArgumentOptionAttribute"/> class.
/// </summary>
/// <param name="longName">The long name of the option.</param>
public ArgumentOptionAttribute(string longName)
: this(string.Empty, longName)
{
public ArgumentOptionAttribute(String longName)
: this(String.Empty, longName) {
}
/// <summary>
@@ -33,24 +29,21 @@
/// </summary>
/// <param name="shortName">The short name of the option.</param>
/// <param name="longName">The long name of the option or null if not used.</param>
public ArgumentOptionAttribute(char shortName, string longName)
: this(new string(shortName, 1), longName)
{
public ArgumentOptionAttribute(Char shortName, String longName)
: this(new String(shortName, 1), longName) {
}
/// <summary>
/// Initializes a new instance of the <see cref="ArgumentOptionAttribute"/> class.
/// </summary>
/// <param name="shortName">The short name of the option..</param>
public ArgumentOptionAttribute(char shortName)
: this(new string(shortName, 1), string.Empty)
{
public ArgumentOptionAttribute(Char shortName)
: this(new String(shortName, 1), String.Empty) {
}
private ArgumentOptionAttribute(string shortName, string longName)
{
ShortName = shortName ?? throw new ArgumentNullException(nameof(shortName));
LongName = longName ?? throw new ArgumentNullException(nameof(longName));
private ArgumentOptionAttribute(String shortName, String longName) {
this.ShortName = shortName ?? throw new ArgumentNullException(nameof(shortName));
this.LongName = longName ?? throw new ArgumentNullException(nameof(longName));
}
/// <summary>
@@ -59,7 +52,9 @@
/// <value>
/// The long name.
/// </value>
public string LongName { get; }
public String LongName {
get;
}
/// <summary>
/// Gets a short name of this command line option, made of one character.
@@ -67,13 +62,15 @@
/// <value>
/// The short name.
/// </value>
public string ShortName { get; }
public String ShortName {
get;
}
/// <summary>
/// When applying attribute to <see cref="System.Collections.Generic.IEnumerable{T}"/> target properties,
/// it allows you to split an argument and consume its content as a sequence.
/// </summary>
public char Separator { get; set; } = '\0';
public Char Separator { get; set; } = '\0';
/// <summary>
/// Gets or sets mapped property default value.
@@ -81,7 +78,9 @@
/// <value>
/// The default value.
/// </value>
public object DefaultValue { get; set; }
public Object DefaultValue {
get; set;
}
/// <summary>
/// Gets or sets a value indicating whether a command line option is required.
@@ -89,7 +88,9 @@
/// <value>
/// <c>true</c> if required; otherwise, <c>false</c>.
/// </value>
public bool Required { get; set; }
public Boolean Required {
get; set;
}
/// <summary>
/// Gets or sets a short description of this command line option. Usually a sentence summary.
@@ -97,6 +98,8 @@
/// <value>
/// The help text.
/// </value>
public string HelpText { get; set; }
public String HelpText {
get; set;
}
}
}
@@ -1,13 +1,11 @@
namespace Unosquare.Swan.Attributes
{
using System;
using System;
namespace Unosquare.Swan.Attributes {
/// <summary>
/// Represents an attribute to select which properties are copyable between objects.
/// </summary>
/// <seealso cref="Attribute" />
[AttributeUsage(AttributeTargets.Property)]
public class CopyableAttribute : Attribute
{
public class CopyableAttribute : Attribute {
}
}
@@ -1,23 +1,20 @@
namespace Unosquare.Swan.Attributes
{
using System;
using System;
namespace Unosquare.Swan.Attributes {
/// <summary>
/// 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
{
public sealed class JsonPropertyAttribute : Attribute {
/// <summary>
/// Initializes a new instance of the <see cref="JsonPropertyAttribute" /> class.
/// </summary>
/// <param name="propertyName">Name of the property.</param>
/// <param name="ignored">if set to <c>true</c> [ignored].</param>
public JsonPropertyAttribute(string propertyName, bool ignored = false)
{
PropertyName = propertyName ?? throw new ArgumentNullException(nameof(propertyName));
Ignored = ignored;
public JsonPropertyAttribute(String propertyName, Boolean ignored = false) {
this.PropertyName = propertyName ?? throw new ArgumentNullException(nameof(propertyName));
this.Ignored = ignored;
}
/// <summary>
@@ -26,7 +23,9 @@
/// <value>
/// The name of the property.
/// </value>
public string PropertyName { get; }
public String PropertyName {
get;
}
/// <summary>
/// Gets or sets a value indicating whether this <see cref="JsonPropertyAttribute" /> is ignored.
@@ -34,6 +33,8 @@
/// <value>
/// <c>true</c> if ignored; otherwise, <c>false</c>.
/// </value>
public bool Ignored { get; }
public Boolean Ignored {
get;
}
}
}
@@ -1,7 +1,6 @@
namespace Unosquare.Swan.Attributes
{
using System;
using System;
namespace Unosquare.Swan.Attributes {
/// <summary>
/// An attribute used to include additional information to a Property for serialization.
///
@@ -9,15 +8,16 @@
/// </summary>
/// <seealso cref="System.Attribute" />
[AttributeUsage(AttributeTargets.Property)]
public sealed class PropertyDisplayAttribute : Attribute
{
public sealed class PropertyDisplayAttribute : Attribute {
/// <summary>
/// Gets or sets the name.
/// </summary>
/// <value>
/// The name.
/// </value>
public string Name { get; set; }
public String Name {
get; set;
}
/// <summary>
/// Gets or sets the description.
@@ -25,7 +25,9 @@
/// <value>
/// The description.
/// </value>
public string Description { get; set; }
public String Description {
get; set;
}
/// <summary>
/// Gets or sets the name of the group.
@@ -33,7 +35,9 @@
/// <value>
/// The name of the group.
/// </value>
public string GroupName { get; set; }
public String GroupName {
get; set;
}
/// <summary>
/// Gets or sets the default value.
@@ -41,7 +45,9 @@
/// <value>
/// The default value.
/// </value>
public object DefaultValue { get; set; }
public Object DefaultValue {
get; set;
}
/// <summary>
/// Gets or sets the format string to call with method <c>ToString</c>.
@@ -49,6 +55,8 @@
/// <value>
/// The format.
/// </value>
public string Format { get; set; }
public String Format {
get; set;
}
}
}
@@ -1,23 +1,18 @@
namespace Unosquare.Swan.Attributes
{
using System;
using System;
namespace Unosquare.Swan.Attributes {
/// <summary>
/// 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
{
public class StructEndiannessAttribute : Attribute {
/// <summary>
/// Initializes a new instance of the <see cref="StructEndiannessAttribute"/> class.
/// </summary>
/// <param name="endianness">The endianness.</param>
public StructEndiannessAttribute(Endianness endianness)
{
Endianness = endianness;
}
public StructEndiannessAttribute(Endianness endianness) => this.Endianness = endianness;
/// <summary>
/// Gets the endianness.
@@ -25,6 +20,8 @@
/// <value>
/// The endianness.
/// </value>
public Endianness Endianness { get; }
public Endianness Endianness {
get;
}
}
}
+49 -52
View File
@@ -1,54 +1,50 @@
namespace Unosquare.Swan.Attributes
{
using System;
using System.Text.RegularExpressions;
using Abstractions;
using System;
using System.Text.RegularExpressions;
using Unosquare.Swan.Abstractions;
namespace Unosquare.Swan.Attributes {
/// <summary>
/// Regex validator.
/// </summary>
[AttributeUsage(AttributeTargets.Property)]
public class MatchAttribute : Attribute, IValidator
{
public class MatchAttribute : Attribute, IValidator {
/// <summary>
/// Initializes a new instance of the <see cref="MatchAttribute" /> class.
/// </summary>
/// <param name="regex">A regex string.</param>
/// <param name="errorMessage">The error message.</param>
/// <exception cref="ArgumentNullException">Expression.</exception>
public MatchAttribute(string regex, string errorMessage = null)
{
Expression = regex ?? throw new ArgumentNullException(nameof(Expression));
ErrorMessage = errorMessage ?? "String does not match the specified regular expression";
public MatchAttribute(String regex, String errorMessage = null) {
this.Expression = regex ?? throw new ArgumentNullException(nameof(this.Expression));
this.ErrorMessage = errorMessage ?? "String does not match the specified regular expression";
}
/// <summary>
/// The string regex used to find a match.
/// </summary>
public string Expression { get; }
/// <inheritdoc/>
public string ErrorMessage { get; internal set; }
/// <inheritdoc/>
public bool IsValid<T>(T value)
{
if (Equals(value, default(T)))
return false;
return !(value is string)
? throw new ArgumentException("Property is not a string")
: Regex.IsMatch(value.ToString(), Expression);
public String Expression {
get;
}
/// <inheritdoc/>
public String ErrorMessage {
get; internal set;
}
/// <inheritdoc/>
public Boolean IsValid<T>(T value) => Equals(value, default(T))
? false
: !(value is String)
? throw new ArgumentException("Property is not a string")
: Regex.IsMatch(value.ToString(), this.Expression);
}
/// <summary>
/// Email validator.
/// </summary>
[AttributeUsage(AttributeTargets.Property)]
public class EmailAttribute : MatchAttribute
{
private const string EmailRegExp =
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]))$";
@@ -56,9 +52,8 @@
/// Initializes a new instance of the <see cref="EmailAttribute"/> class.
/// </summary>
/// <param name="errorMessage">The error message.</param>
public EmailAttribute(string errorMessage = null)
: base(EmailRegExp, errorMessage ?? "String is not an email")
{
public EmailAttribute(String errorMessage = null)
: base(EmailRegExp, errorMessage ?? "String is not an email") {
}
}
@@ -66,34 +61,32 @@
/// A not null validator.
/// </summary>
[AttributeUsage(AttributeTargets.Property)]
public class NotNullAttribute : Attribute, IValidator
{
public class NotNullAttribute : Attribute, IValidator {
/// <inheritdoc/>
public string ErrorMessage => "Value is null";
public String ErrorMessage => "Value is null";
/// <inheritdoc/>
public bool IsValid<T>(T value) => !Equals(default(T), value);
public Boolean IsValid<T>(T value) => !Equals(default(T), value);
}
/// <summary>
/// A range constraint validator.
/// </summary>
[AttributeUsage(AttributeTargets.Property)]
public class RangeAttribute : Attribute, IValidator
{
public class RangeAttribute : Attribute, IValidator {
/// <summary>
/// Initializes a new instance of the <see cref="RangeAttribute"/> class.
/// Constructor that takes integer minimum and maximum values.
/// </summary>
/// <param name="min">The minimum value.</param>
/// <param name="max">The maximum value.</param>
public RangeAttribute(int min, int max)
{
if (min >= max)
public RangeAttribute(Int32 min, Int32 max) {
if(min >= max) {
throw new InvalidOperationException("Maximum value must be greater than minimum");
}
Maximum = max;
Minimum = min;
this.Maximum = max;
this.Minimum = min;
}
/// <summary>
@@ -102,32 +95,36 @@
/// </summary>
/// <param name="min">The minimum value.</param>
/// <param name="max">The maximum value.</param>
public RangeAttribute(double min, double max)
{
if (min >= max)
public RangeAttribute(Double min, Double max) {
if(min >= max) {
throw new InvalidOperationException("Maximum value must be greater than minimum");
}
Maximum = max;
Minimum = min;
this.Maximum = max;
this.Minimum = min;
}
/// <inheritdoc/>
public string ErrorMessage => "Value is not within the specified range";
public String ErrorMessage => "Value is not within the specified range";
/// <summary>
/// Maximum value for the range.
/// </summary>
public IComparable Maximum { get; }
public IComparable Maximum {
get;
}
/// <summary>
/// Minimum value for the range.
/// </summary>
public IComparable Minimum { get; }
public IComparable Minimum {
get;
}
/// <inheritdoc/>
public bool IsValid<T>(T value)
public Boolean IsValid<T>(T value)
=> value is IComparable comparable
? comparable.CompareTo(Minimum) >= 0 && comparable.CompareTo(Maximum) <= 0
? comparable.CompareTo(this.Minimum) >= 0 && comparable.CompareTo(this.Maximum) <= 0
: throw new ArgumentException(nameof(value));
}
}
@@ -1,22 +1,17 @@
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
{
public sealed class VerbOptionAttribute : Attribute {
/// <summary>
/// Initializes a new instance of the <see cref="VerbOptionAttribute" /> class.
/// </summary>
/// <param name="name">The name.</param>
/// <exception cref="ArgumentNullException">name.</exception>
public VerbOptionAttribute(string name)
{
Name = name ?? throw new ArgumentNullException(nameof(name));
}
public VerbOptionAttribute(String name) => this.Name = name ?? throw new ArgumentNullException(nameof(name));
/// <summary>
/// Gets the name of the verb option.
@@ -24,7 +19,9 @@
/// <value>
/// Name.
/// </value>
public string Name { get; }
public String Name {
get;
}
/// <summary>
/// Gets or sets a short description of this command line verb. Usually a sentence summary.
@@ -32,9 +29,11 @@
/// <value>
/// The help text.
/// </value>
public string HelpText { get; set; }
public String HelpText {
get; set;
}
/// <inheritdoc />
public override string ToString() => $" {Name}\t\t{HelpText}";
public override String ToString() => $" {this.Name}\t\t{this.HelpText}";
}
}
@@ -1,21 +1,18 @@
namespace Unosquare.Swan.Components
{
using System.Linq;
using System.Reflection;
using Attributes;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Unosquare.Swan.Attributes;
using System;
using System.Collections.Generic;
namespace Unosquare.Swan.Components {
/// <summary>
/// Provides methods to parse command line arguments.
/// Based on CommandLine (Copyright 2005-2015 Giacomo Stelluti Scala and Contributors.).
/// </summary>
public partial class ArgumentParser
{
private sealed class Validator
{
private readonly object _instance;
private readonly IEnumerable<string> _args;
public partial class ArgumentParser {
private sealed class Validator {
private readonly Object _instance;
private readonly IEnumerable<String> _args;
private readonly List<PropertyInfo> _updatedList = new List<PropertyInfo>();
private readonly ArgumentParserSettings _settings;
@@ -23,122 +20,112 @@
public Validator(
PropertyInfo[] properties,
IEnumerable<string> args,
object instance,
ArgumentParserSettings settings)
{
_args = args;
_instance = instance;
_settings = settings;
_properties = properties;
IEnumerable<String> args,
Object instance,
ArgumentParserSettings settings) {
this._args = args;
this._instance = instance;
this._settings = settings;
this._properties = properties;
PopulateInstance();
SetDefaultValues();
GetRequiredList();
this.PopulateInstance();
this.SetDefaultValues();
this.GetRequiredList();
}
public List<string> UnknownList { get; } = new List<string>();
public List<string> RequiredList { get; } = new List<string>();
public List<String> UnknownList { get; } = new List<String>();
public List<String> RequiredList { get; } = new List<String>();
public bool IsValid() => (_settings.IgnoreUnknownArguments || !UnknownList.Any()) && !RequiredList.Any();
public Boolean IsValid() => (this._settings.IgnoreUnknownArguments || !this.UnknownList.Any()) && !this.RequiredList.Any();
public IEnumerable<ArgumentOptionAttribute> GetPropertiesOptions()
=> _properties.Select(p => Runtime.AttributeCache.RetrieveOne<ArgumentOptionAttribute>(p))
=> this._properties.Select(p => Runtime.AttributeCache.RetrieveOne<ArgumentOptionAttribute>(p))
.Where(x => x != null);
private void GetRequiredList()
{
foreach (var targetProperty in _properties)
{
var optionAttr = Runtime.AttributeCache.RetrieveOne<ArgumentOptionAttribute>(targetProperty);
private void GetRequiredList() {
foreach(PropertyInfo targetProperty in this._properties) {
ArgumentOptionAttribute optionAttr = Runtime.AttributeCache.RetrieveOne<ArgumentOptionAttribute>(targetProperty);
if (optionAttr == null || optionAttr.Required == false)
if(optionAttr == null || optionAttr.Required == false) {
continue;
}
if (targetProperty.GetValue(_instance) == null)
{
RequiredList.Add(optionAttr.LongName ?? optionAttr.ShortName);
if(targetProperty.GetValue(this._instance) == null) {
this.RequiredList.Add(optionAttr.LongName ?? optionAttr.ShortName);
}
}
}
private void SetDefaultValues()
{
foreach (var targetProperty in _properties.Except(_updatedList))
{
var optionAttr = Runtime.AttributeCache.RetrieveOne<ArgumentOptionAttribute>(targetProperty);
private void SetDefaultValues() {
foreach(PropertyInfo targetProperty in this._properties.Except(this._updatedList)) {
ArgumentOptionAttribute optionAttr = Runtime.AttributeCache.RetrieveOne<ArgumentOptionAttribute>(targetProperty);
var defaultValue = optionAttr?.DefaultValue;
Object defaultValue = optionAttr?.DefaultValue;
if (defaultValue == null)
if(defaultValue == null) {
continue;
}
if (SetPropertyValue(targetProperty, defaultValue.ToString(), _instance, optionAttr))
_updatedList.Add(targetProperty);
if(this.SetPropertyValue(targetProperty, defaultValue.ToString(), this._instance, optionAttr)) {
this._updatedList.Add(targetProperty);
}
}
}
private void PopulateInstance()
{
const char dash = '-';
var propertyName = string.Empty;
private void PopulateInstance() {
const Char dash = '-';
String propertyName = String.Empty;
foreach (var arg in _args)
{
var ignoreSetValue = string.IsNullOrWhiteSpace(propertyName);
foreach(String arg in this._args) {
Boolean ignoreSetValue = String.IsNullOrWhiteSpace(propertyName);
if (ignoreSetValue)
{
if (string.IsNullOrWhiteSpace(arg) || arg[0] != dash) continue;
if(ignoreSetValue) {
if(String.IsNullOrWhiteSpace(arg) || arg[0] != dash) {
continue;
}
propertyName = arg.Substring(1);
if (!string.IsNullOrWhiteSpace(propertyName) && propertyName[0] == dash)
if(!String.IsNullOrWhiteSpace(propertyName) && propertyName[0] == dash) {
propertyName = propertyName.Substring(1);
}
}
var targetProperty = TryGetProperty(propertyName);
PropertyInfo targetProperty = this.TryGetProperty(propertyName);
if (targetProperty == null)
{
if(targetProperty == null) {
// Skip if the property is not found
UnknownList.Add(propertyName);
this.UnknownList.Add(propertyName);
continue;
}
if (!ignoreSetValue && SetPropertyValue(targetProperty, arg, _instance))
{
_updatedList.Add(targetProperty);
propertyName = string.Empty;
}
else if (targetProperty.PropertyType == typeof(bool))
{
if(!ignoreSetValue && this.SetPropertyValue(targetProperty, arg, this._instance)) {
this._updatedList.Add(targetProperty);
propertyName = String.Empty;
} else if(targetProperty.PropertyType == typeof(Boolean)) {
// If the arg is a boolean property set it to true.
targetProperty.SetValue(_instance, true);
targetProperty.SetValue(this._instance, true);
_updatedList.Add(targetProperty);
propertyName = string.Empty;
this._updatedList.Add(targetProperty);
propertyName = String.Empty;
}
}
if (!string.IsNullOrEmpty(propertyName))
{
UnknownList.Add(propertyName);
if(!String.IsNullOrEmpty(propertyName)) {
this.UnknownList.Add(propertyName);
}
}
private bool SetPropertyValue(
private Boolean SetPropertyValue(
PropertyInfo targetProperty,
string propertyValueString,
object result,
ArgumentOptionAttribute optionAttr = null)
{
if (targetProperty.PropertyType.GetTypeInfo().IsEnum)
{
var parsedValue = Enum.Parse(
String propertyValueString,
Object result,
ArgumentOptionAttribute optionAttr = null) {
if(targetProperty.PropertyType.GetTypeInfo().IsEnum) {
Object parsedValue = Enum.Parse(
targetProperty.PropertyType,
propertyValueString,
_settings.CaseInsensitiveEnumValues);
this._settings.CaseInsensitiveEnumValues);
targetProperty.SetValue(result, Enum.ToObject(targetProperty.PropertyType, parsedValue));
@@ -150,10 +137,10 @@
: 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));
private PropertyInfo TryGetProperty(String propertyName)
=> this._properties.FirstOrDefault(p =>
String.Equals(Runtime.AttributeCache.RetrieveOne<ArgumentOptionAttribute>(p)?.LongName, propertyName, this._settings.NameComparer) ||
String.Equals(Runtime.AttributeCache.RetrieveOne<ArgumentOptionAttribute>(p)?.ShortName, propertyName, this._settings.NameComparer));
}
}
}
@@ -1,53 +1,48 @@
namespace Unosquare.Swan.Components
{
using System.Linq;
using System.Reflection;
using Attributes;
using System;
using System.Linq;
using System.Reflection;
using Unosquare.Swan.Attributes;
using System;
namespace Unosquare.Swan.Components {
/// <summary>
/// Provides methods to parse command line arguments.
/// </summary>
public partial class ArgumentParser
{
private sealed class TypeResolver<T>
{
private readonly string _selectedVerb;
public partial class ArgumentParser {
private sealed class TypeResolver<T> {
private readonly String _selectedVerb;
private PropertyInfo[] _properties;
public TypeResolver(string selectedVerb)
{
_selectedVerb = selectedVerb;
public TypeResolver(String selectedVerb) => this._selectedVerb = selectedVerb;
public PropertyInfo[] GetProperties() => this._properties?.Any() == true ? this._properties : null;
public Object GetOptionsObject(T instance) {
this._properties = Runtime.PropertyTypeCache.RetrieveAllProperties<T>(true).ToArray();
if(!this._properties.Any(x => x.GetCustomAttributes(typeof(VerbOptionAttribute), false).Any())) {
return instance;
}
public PropertyInfo[] GetProperties() => _properties?.Any() == true ? _properties : null;
public object GetOptionsObject(T instance)
{
_properties = Runtime.PropertyTypeCache.RetrieveAllProperties<T>(true).ToArray();
if (!_properties.Any(x => x.GetCustomAttributes(typeof(VerbOptionAttribute), false).Any()))
return instance;
var selectedVerb = string.IsNullOrWhiteSpace(_selectedVerb)
PropertyInfo selectedVerb = String.IsNullOrWhiteSpace(this._selectedVerb)
? null
: _properties.FirstOrDefault(x =>
Runtime.AttributeCache.RetrieveOne<VerbOptionAttribute>(x).Name.Equals(_selectedVerb));
: this._properties.FirstOrDefault(x =>
Runtime.AttributeCache.RetrieveOne<VerbOptionAttribute>(x).Name.Equals(this._selectedVerb));
if (selectedVerb == null) return null;
if(selectedVerb == null) {
return null;
}
var type = instance.GetType();
Type type = instance.GetType();
var verbProperty = type.GetProperty(selectedVerb.Name);
PropertyInfo verbProperty = type.GetProperty(selectedVerb.Name);
if (verbProperty?.GetValue(instance) == null)
{
var propertyInstance = Activator.CreateInstance(selectedVerb.PropertyType);
if(verbProperty?.GetValue(instance) == null) {
Object propertyInstance = Activator.CreateInstance(selectedVerb.PropertyType);
verbProperty?.SetValue(instance, propertyInstance);
}
_properties = Runtime.PropertyTypeCache.RetrieveAllProperties(selectedVerb.PropertyType, true)
this._properties = Runtime.PropertyTypeCache.RetrieveAllProperties(selectedVerb.PropertyType, true)
.ToArray();
return verbProperty?.GetValue(instance);
@@ -1,10 +1,9 @@
namespace Unosquare.Swan.Components
{
using System;
using System.Collections.Generic;
using Attributes;
using System.Linq;
using System;
using System.Collections.Generic;
using Unosquare.Swan.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.).
@@ -106,14 +105,12 @@ namespace Unosquare.Swan.Components
/// }
/// </code>
/// </example>
public partial class ArgumentParser
{
public partial class ArgumentParser {
/// <summary>
/// Initializes a new instance of the <see cref="ArgumentParser"/> class.
/// </summary>
public ArgumentParser()
: this(new ArgumentParserSettings())
{
: this(new ArgumentParserSettings()) {
}
/// <summary>
@@ -121,10 +118,7 @@ namespace Unosquare.Swan.Components
/// 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));
}
public ArgumentParser(ArgumentParserSettings parseSettings) => this.Settings = parseSettings ?? throw new ArgumentNullException(nameof(parseSettings));
/// <summary>
/// Gets the instance that implements <see cref="ArgumentParserSettings" /> in use.
@@ -132,7 +126,9 @@ namespace Unosquare.Swan.Components
/// <value>
/// The settings.
/// </value>
public ArgumentParserSettings Settings { get; }
public ArgumentParserSettings Settings {
get;
}
/// <summary>
/// Parses a string array of command line arguments constructing values in an instance of type <typeparamref name="T" />.
@@ -150,39 +146,40 @@ namespace Unosquare.Swan.Components
/// <exception cref="InvalidOperationException">
/// 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)
public Boolean ParseArguments<T>(IEnumerable<String> args, T instance) {
if(args == null) {
throw new ArgumentNullException(nameof(args));
}
if (Equals(instance, default(T)))
if(Equals(instance, default(T))) {
throw new ArgumentNullException(nameof(instance));
}
var typeResolver = new TypeResolver<T>(args.FirstOrDefault());
var options = typeResolver.GetOptionsObject(instance);
TypeResolver<T> typeResolver = new TypeResolver<T>(args.FirstOrDefault());
Object options = typeResolver.GetOptionsObject(instance);
if (options == null)
{
if(options == null) {
ReportUnknownVerb<T>();
return false;
}
var properties = typeResolver.GetProperties();
System.Reflection.PropertyInfo[] properties = typeResolver.GetProperties();
if (properties == null)
if(properties == null) {
throw new InvalidOperationException($"Type {typeof(T).Name} is not valid");
}
var validator = new Validator(properties, args, options, Settings);
Validator validator = new Validator(properties, args, options, this.Settings);
if (validator.IsValid())
if(validator.IsValid()) {
return true;
}
ReportIssues(validator);
this.ReportIssues(validator);
return false;
}
private static void ReportUnknownVerb<T>()
{
private static void ReportUnknownVerb<T>() {
"No verb was specified".WriteLine(ConsoleColor.Red);
"Valid verbs:".WriteLine(ConsoleColor.Cyan);
@@ -193,38 +190,39 @@ namespace Unosquare.Swan.Components
.ForEach(x => x.ToString().WriteLine(ConsoleColor.Cyan));
}
private void ReportIssues(Validator validator)
{
private void ReportIssues(Validator validator) {
#if !NETSTANDARD1_3
if (Settings.WriteBanner)
if(this.Settings.WriteBanner) {
Runtime.WriteWelcomeBanner();
}
#endif
var options = validator.GetPropertiesOptions();
IEnumerable<ArgumentOptionAttribute> options = validator.GetPropertiesOptions();
foreach (var option in options)
{
string.Empty.WriteLine();
foreach(ArgumentOptionAttribute option in options) {
String.Empty.WriteLine();
// TODO: If Enum list values
var shortName = string.IsNullOrWhiteSpace(option.ShortName) ? string.Empty : $"-{option.ShortName}";
var longName = string.IsNullOrWhiteSpace(option.LongName) ? string.Empty : $"--{option.LongName}";
var comma = string.IsNullOrWhiteSpace(shortName) || string.IsNullOrWhiteSpace(longName)
? string.Empty
String shortName = String.IsNullOrWhiteSpace(option.ShortName) ? String.Empty : $"-{option.ShortName}";
String longName = String.IsNullOrWhiteSpace(option.LongName) ? String.Empty : $"--{option.LongName}";
String comma = String.IsNullOrWhiteSpace(shortName) || String.IsNullOrWhiteSpace(longName)
? String.Empty
: ", ";
var defaultValue = option.DefaultValue == null ? string.Empty : $"(Default: {option.DefaultValue}) ";
String defaultValue = option.DefaultValue == null ? String.Empty : $"(Default: {option.DefaultValue}) ";
$" {shortName}{comma}{longName}\t\t{defaultValue}{option.HelpText}".WriteLine(ConsoleColor.Cyan);
}
string.Empty.WriteLine();
String.Empty.WriteLine();
" --help\t\tDisplay this help screen.".WriteLine(ConsoleColor.Cyan);
if (validator.UnknownList.Any())
$"Unknown arguments: {string.Join(", ", validator.UnknownList)}".WriteLine(ConsoleColor.Red);
if(validator.UnknownList.Any()) {
$"Unknown arguments: {String.Join(", ", validator.UnknownList)}".WriteLine(ConsoleColor.Red);
}
if (validator.RequiredList.Any())
$"Required arguments: {string.Join(", ", validator.RequiredList)}".WriteLine(ConsoleColor.Red);
if(validator.RequiredList.Any()) {
$"Required arguments: {String.Join(", ", validator.RequiredList)}".WriteLine(ConsoleColor.Red);
}
}
}
}
@@ -1,20 +1,18 @@
namespace Unosquare.Swan.Components
{
using System;
using System;
namespace Unosquare.Swan.Components {
/// <summary>
/// Provides settings for <see cref="ArgumentParser"/>.
/// Based on CommandLine (Copyright 2005-2015 Giacomo Stelluti Scala and Contributors.).
/// </summary>
public class ArgumentParserSettings
{
public class ArgumentParserSettings {
/// <summary>
/// Gets or sets a value indicating whether [write banner].
/// </summary>
/// <value>
/// <c>true</c> if [write banner]; otherwise, <c>false</c>.
/// </value>
public bool WriteBanner { get; set; } = true;
public Boolean WriteBanner { get; set; } = true;
/// <summary>
/// Gets or sets a value indicating whether perform case sensitive comparisons.
@@ -24,7 +22,7 @@
/// <value>
/// <c>true</c> if [case sensitive]; otherwise, <c>false</c>.
/// </value>
public bool CaseSensitive { get; set; } = false;
public Boolean CaseSensitive { get; set; } = false;
/// <summary>
/// Gets or sets a value indicating whether perform case sensitive comparisons of <i>values</i>.
@@ -33,7 +31,7 @@
/// <value>
/// <c>true</c> if [case insensitive enum values]; otherwise, <c>false</c>.
/// </value>
public bool CaseInsensitiveEnumValues { get; set; } = true;
public Boolean CaseInsensitiveEnumValues { get; set; } = true;
/// <summary>
/// Gets or sets a value indicating whether the parser shall move on to the next argument and ignore the given argument if it
@@ -46,8 +44,8 @@
/// This allows fragmented version class parsing, useful for project with add-on where add-ons also requires command line arguments but
/// when these are unknown by the main program at build time.
/// </remarks>
public bool IgnoreUnknownArguments { get; set; } = true;
public Boolean IgnoreUnknownArguments { get; set; } = true;
internal StringComparison NameComparer => CaseSensitive ? StringComparison.Ordinal : StringComparison.OrdinalIgnoreCase;
internal StringComparison NameComparer => this.CaseSensitive ? StringComparison.Ordinal : StringComparison.OrdinalIgnoreCase;
}
}
+36 -44
View File
@@ -1,11 +1,10 @@
namespace Unosquare.Swan.Components
{
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
namespace Unosquare.Swan.Components {
/// <summary>
/// A simple benchmarking class.
/// </summary>
@@ -33,31 +32,27 @@
/// }
/// </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>>();
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>
/// Starts measuring with the given identifier.
/// </summary>
/// <param name="identifier">The identifier.</param>
/// <returns>A disposable object that when disposed, adds a benchmark result.</returns>
public static IDisposable Start(string identifier) => new BenchmarkUnit(identifier);
public static IDisposable Start(String identifier) => new BenchmarkUnit(identifier);
/// <summary>
/// Outputs the benchmark statistics.
/// </summary>
/// <returns>A string containing human-readable statistics.</returns>
public static string Dump()
{
var builder = new StringBuilder();
public static String Dump() {
StringBuilder builder = new StringBuilder();
lock (SyncLock)
{
foreach (var kvp in Measures)
{
builder.Append($"BID: {kvp.Key,-30} | ")
lock(SyncLock) {
foreach(KeyValuePair<String, List<TimeSpan>> kvp in Measures) {
_ = builder.Append($"BID: {kvp.Key,-30} | ")
.Append($"CNT: {kvp.Value.Count,6} | ")
.Append($"AVG: {kvp.Value.Average(t => t.TotalMilliseconds),8:0.000} ms. | ")
.Append($"MAX: {kvp.Value.Max(t => t.TotalMilliseconds),8:0.000} ms. | ")
@@ -74,12 +69,11 @@
/// </summary>
/// <param name="identifier">The identifier.</param>
/// <param name="elapsed">The elapsed.</param>
private static void Add(string identifier, TimeSpan elapsed)
{
lock (SyncLock)
{
if (Measures.ContainsKey(identifier) == false)
private static void Add(String identifier, TimeSpan elapsed) {
lock(SyncLock) {
if(Measures.ContainsKey(identifier) == false) {
Measures[identifier] = new List<TimeSpan>(1024 * 1024);
}
Measures[identifier].Add(elapsed);
}
@@ -89,41 +83,39 @@
/// 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 sealed class BenchmarkUnit : IDisposable {
private readonly String _identifier;
private Boolean _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();
public BenchmarkUnit(String identifier) {
this._identifier = identifier;
this._stopwatch.Start();
}
/// <inheritdoc />
public void Dispose() => Dispose(true);
public void Dispose() => this.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();
private void Dispose(Boolean alsoManaged) {
if(this._isDisposed) {
return;
}
_stopwatch = null;
_isDisposed = true;
if(alsoManaged) {
Add(this._identifier, this._stopwatch.Elapsed);
this._stopwatch?.Stop();
}
this._stopwatch = null;
this._isDisposed = true;
}
}
}
@@ -1,16 +1,14 @@
namespace Unosquare.Swan.Components
{
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System;
using System.Collections.Concurrent;
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>
{
public class CollectionCacheRepository<TValue> {
private readonly Lazy<ConcurrentDictionary<Type, IEnumerable<TValue>>> _data =
new Lazy<ConcurrentDictionary<Type, IEnumerable<TValue>>>(() =>
new ConcurrentDictionary<Type, IEnumerable<TValue>>(), true);
@@ -20,7 +18,7 @@
/// </summary>
/// <param name="key">The key.</param>
/// <returns><c>true</c> if the cache contains the key, otherwise <c>false</c>.</returns>
public bool ContainsKey(Type key) => _data.Value.ContainsKey(key);
public Boolean ContainsKey(Type key) => this._data.Value.ContainsKey(key);
/// <summary>
/// Retrieves the properties stored for the specified type.
@@ -33,12 +31,12 @@
/// 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)
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));
return this._data.Value.GetOrAdd(key, k => factory.Invoke(k).Where(item => item != null));
}
}
}
+36 -63
View File
@@ -1,28 +1,23 @@
namespace Unosquare.Swan.Components
{
using System;
using System.Collections.Generic;
using System.Linq;
using Abstractions;
using System;
using System.Collections.Generic;
using System.Linq;
using Unosquare.Swan.Abstractions;
namespace Unosquare.Swan.Components {
/// <summary>
/// Provide Enumerations helpers with internal cache.
/// </summary>
public class EnumHelper
: SingletonBase<CollectionCacheRepository<Tuple<string, object>>>
{
: SingletonBase<CollectionCacheRepository<Tuple<String, Object>>> {
/// <summary>
/// Gets all the names and enumerators from a specific Enum type.
/// </summary>
/// <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>
public static IEnumerable<Tuple<string, object>> Retrieve<T>()
where T : struct, IConvertible
{
return Instance.Retrieve(typeof(T), t => Enum.GetValues(t)
.Cast<object>()
public static IEnumerable<Tuple<String, Object>> Retrieve<T>()
where T : struct, IConvertible => Instance.Retrieve(typeof(T), t => Enum.GetValues(t)
.Cast<Object>()
.Select(item => Tuple.Create(Enum.GetName(t, item), item)));
}
/// <summary>
/// Gets the cached items with the enum item value.
@@ -33,12 +28,9 @@
/// A collection of Type/Tuple pairs
/// that represents items with the enum item value.
/// </returns>
public static IEnumerable<Tuple<int, string>> GetItemsWithValue<T>(bool humanize = true)
where T : struct, IConvertible
{
return Retrieve<T>()
.Select(x => Tuple.Create((int) x.Item2, humanize ? x.Item1.Humanize() : x.Item1));
}
public static IEnumerable<Tuple<Int32, String>> GetItemsWithValue<T>(Boolean humanize = true)
where T : struct, IConvertible => Retrieve<T>()
.Select(x => Tuple.Create((Int32)x.Item2, humanize ? x.Item1.Humanize() : x.Item1));
/// <summary>
/// Gets the flag values.
@@ -49,14 +41,11 @@
/// <returns>
/// A list of values in the flag.
/// </returns>
public static IEnumerable<int> GetFlagValues<TEnum>(int value, bool ignoreZero = false)
where TEnum : struct, IConvertible
{
return Retrieve<TEnum>()
.Select(x => (int) x.Item2)
public static IEnumerable<Int32> GetFlagValues<TEnum>(Int32 value, Boolean ignoreZero = false)
where TEnum : struct, IConvertible => Retrieve<TEnum>()
.Select(x => (Int32)x.Item2)
.When(() => ignoreZero, q => q.Where(f => f != 0))
.Where(x => (x & value) == x);
}
/// <summary>
/// Gets the flag values.
@@ -67,14 +56,11 @@
/// <returns>
/// A list of values in the flag.
/// </returns>
public static IEnumerable<long> GetFlagValues<TEnum>(long value, bool ignoreZero = false)
where TEnum : struct, IConvertible
{
return Retrieve<TEnum>()
.Select(x => (long) x.Item2)
public static IEnumerable<Int64> GetFlagValues<TEnum>(Int64 value, Boolean ignoreZero = false)
where TEnum : struct, IConvertible => Retrieve<TEnum>()
.Select(x => (Int64)x.Item2)
.When(() => ignoreZero, q => q.Where(f => f != 0))
.Where(x => (x & value) == x);
}
/// <summary>
/// Gets the flag values.
@@ -85,14 +71,11 @@
/// <returns>
/// A list of values in the flag.
/// </returns>
public static IEnumerable<byte> GetFlagValues<TEnum>(byte value, bool ignoreZero = false)
where TEnum : struct, IConvertible
{
return Retrieve<TEnum>()
.Select(x => (byte) x.Item2)
public static IEnumerable<Byte> GetFlagValues<TEnum>(Byte value, Boolean ignoreZero = false)
where TEnum : struct, IConvertible => Retrieve<TEnum>()
.Select(x => (Byte)x.Item2)
.When(() => ignoreZero, q => q.Where(f => f != 0))
.Where(x => (x & value) == x);
}
/// <summary>
/// Gets the flag names.
@@ -104,14 +87,11 @@
/// <returns>
/// A list of flag names.
/// </returns>
public static IEnumerable<string> GetFlagNames<TEnum>(int value, bool ignoreZero = false, bool humanize = true)
where TEnum : struct, IConvertible
{
return Retrieve<TEnum>()
.When(() => ignoreZero, q => q.Where(f => (int) f.Item2 != 0))
.Where(x => ((int) x.Item2 & value) == (int) x.Item2)
public static IEnumerable<String> GetFlagNames<TEnum>(Int32 value, Boolean ignoreZero = false, Boolean humanize = true)
where TEnum : struct, IConvertible => Retrieve<TEnum>()
.When(() => ignoreZero, q => q.Where(f => (Int32)f.Item2 != 0))
.Where(x => ((Int32)x.Item2 & value) == (Int32)x.Item2)
.Select(x => humanize ? x.Item1.Humanize() : x.Item1);
}
/// <summary>
/// Gets the flag names.
@@ -123,14 +103,11 @@
/// <returns>
/// A list of flag names.
/// </returns>
public static IEnumerable<string> GetFlagNames<TEnum>(long value, bool ignoreZero = false, bool humanize = true)
where TEnum : struct, IConvertible
{
return Retrieve<TEnum>()
.When(() => ignoreZero, q => q.Where(f => (long) f.Item2 != 0))
.Where(x => ((long) x.Item2 & value) == (long) x.Item2)
public static IEnumerable<String> GetFlagNames<TEnum>(Int64 value, Boolean ignoreZero = false, Boolean humanize = true)
where TEnum : struct, IConvertible => Retrieve<TEnum>()
.When(() => ignoreZero, q => q.Where(f => (Int64)f.Item2 != 0))
.Where(x => ((Int64)x.Item2 & value) == (Int64)x.Item2)
.Select(x => humanize ? x.Item1.Humanize() : x.Item1);
}
/// <summary>
/// Gets the flag names.
@@ -142,14 +119,11 @@
/// <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)
public static IEnumerable<String> GetFlagNames<TEnum>(Byte value, Boolean ignoreZero = false, Boolean humanize = true)
where TEnum : struct, IConvertible => 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.
@@ -159,10 +133,9 @@
/// <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;
public static IEnumerable<Tuple<Int32, String>> GetItemsWithIndex<T>(Boolean humanize = true)
where T : struct, IConvertible {
Int32 i = 0;
return Retrieve<T>()
.Select(x => Tuple.Create(i++, humanize ? x.Item1.Humanize() : x.Item1));
@@ -1,17 +1,15 @@
namespace Unosquare.Swan.Components
{
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System;
using System.Collections;
using System.Collections.Generic;
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
{
public static class ObjectComparer {
/// <summary>
/// Compare if two variables of the same type are equal.
/// </summary>
@@ -19,7 +17,7 @@
/// <param name="left">The left.</param>
/// <param name="right">The right.</param>
/// <returns><c>true</c> if the variables are equal; otherwise, <c>false</c>.</returns>
public static bool AreEqual<T>(T left, T right) => AreEqual(left, right, typeof(T));
public static Boolean AreEqual<T>(T left, T right) => AreEqual(left, right, typeof(T));
/// <summary>
/// Compare if two variables of the same type are equal.
@@ -31,18 +29,16 @@
/// <c>true</c> if the variables are equal; otherwise, <c>false</c>.
/// </returns>
/// <exception cref="ArgumentNullException">targetType.</exception>
public static bool AreEqual(object left, object right, Type targetType)
{
if (targetType == null)
public static Boolean AreEqual(Object left, Object right, Type targetType) {
if(targetType == null) {
throw new ArgumentNullException(nameof(targetType));
}
if (Definitions.BasicTypesInfo.ContainsKey(targetType))
return Equals(left, right);
if (targetType.IsValueType() || targetType.IsArray)
return AreStructsEqual(left, right, targetType);
return AreObjectsEqual(left, right, targetType);
return Definitions.BasicTypesInfo.ContainsKey(targetType)
? Equals(left, right)
: targetType.IsValueType() || targetType.IsArray
? AreStructsEqual(left, right, targetType)
: AreObjectsEqual(left, right, targetType);
}
/// <summary>
@@ -52,11 +48,8 @@
/// <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>
public static bool AreObjectsEqual<T>(T left, T right)
where T : class
{
return AreObjectsEqual(left, right, typeof(T));
}
public static Boolean AreObjectsEqual<T>(T left, T right)
where T : class => AreObjectsEqual(left, right, typeof(T));
/// <summary>
/// Compare if two objects of the same type are equal.
@@ -66,31 +59,29 @@
/// <param name="targetType">Type of the target.</param>
/// <returns><c>true</c> if the objects are equal; otherwise, <c>false</c>.</returns>
/// <exception cref="ArgumentNullException">targetType.</exception>
public static bool AreObjectsEqual(object left, object right, Type targetType)
{
if (targetType == null)
public static Boolean AreObjectsEqual(Object left, Object right, Type targetType) {
if(targetType == null) {
throw new ArgumentNullException(nameof(targetType));
}
var properties = Runtime.PropertyTypeCache.RetrieveAllProperties(targetType).ToArray();
PropertyInfo[] properties = Runtime.PropertyTypeCache.RetrieveAllProperties(targetType).ToArray();
foreach (var propertyTarget in properties)
{
var targetPropertyGetMethod = propertyTarget.GetCacheGetMethod();
foreach(PropertyInfo propertyTarget in properties) {
Func<Object, Object> targetPropertyGetMethod = propertyTarget.GetCacheGetMethod();
if (propertyTarget.PropertyType.IsArray)
{
var leftObj = targetPropertyGetMethod(left) as IEnumerable;
var rightObj = targetPropertyGetMethod(right) as IEnumerable;
if(propertyTarget.PropertyType.IsArray) {
IEnumerable leftObj = targetPropertyGetMethod(left) as IEnumerable;
IEnumerable rightObj = targetPropertyGetMethod(right) as IEnumerable;
if (!AreEnumerationsEquals(leftObj, rightObj))
if(!AreEnumerationsEquals(leftObj, rightObj)) {
return false;
}
else
{
if (!Equals(targetPropertyGetMethod(left), targetPropertyGetMethod(right)))
} else {
if(!Equals(targetPropertyGetMethod(left), targetPropertyGetMethod(right))) {
return false;
}
}
}
return true;
}
@@ -102,11 +93,8 @@
/// <param name="left">The left.</param>
/// <param name="right">The right.</param>
/// <returns><c>true</c> if the structs are equal; otherwise, <c>false</c>.</returns>
public static bool AreStructsEqual<T>(T left, T right)
where T : struct
{
return AreStructsEqual(left, right, typeof(T));
}
public static Boolean AreStructsEqual<T>(T left, T right)
where T : struct => AreStructsEqual(left, right, typeof(T));
/// <summary>
/// Compare if two structures of the same type are equal.
@@ -118,28 +106,30 @@
/// <c>true</c> if the structs are equal; otherwise, <c>false</c>.
/// </returns>
/// <exception cref="ArgumentNullException">targetType.</exception>
public static bool AreStructsEqual(object left, object right, Type targetType)
{
if (targetType == null)
public static Boolean AreStructsEqual(Object left, Object right, Type targetType) {
if(targetType == null) {
throw new ArgumentNullException(nameof(targetType));
}
var fields = new List<MemberInfo>(Runtime.FieldTypeCache.RetrieveAllFields(targetType))
IEnumerable<MemberInfo> fields = new List<MemberInfo>(Runtime.FieldTypeCache.RetrieveAllFields(targetType))
.Union(Runtime.PropertyTypeCache.RetrieveAllProperties(targetType));
foreach (var targetMember in fields)
{
switch (targetMember)
{
foreach(MemberInfo targetMember in fields) {
switch(targetMember) {
case FieldInfo field:
if (Equals(field.GetValue(left), field.GetValue(right)) == false)
if(Equals(field.GetValue(left), field.GetValue(right)) == false) {
return false;
}
break;
case PropertyInfo property:
var targetPropertyGetMethod = property.GetCacheGetMethod();
Func<Object, Object> targetPropertyGetMethod = property.GetCacheGetMethod();
if (targetPropertyGetMethod != null &&
!Equals(targetPropertyGetMethod(left), targetPropertyGetMethod(right)))
if(targetPropertyGetMethod != null &&
!Equals(targetPropertyGetMethod(left), targetPropertyGetMethod(right))) {
return false;
}
break;
}
}
@@ -161,28 +151,28 @@
/// or
/// right.
/// </exception>
public static bool AreEnumerationsEquals<T>(T left, T right)
where T : IEnumerable
{
if (Equals(left, default(T)))
public static Boolean AreEnumerationsEquals<T>(T left, T right)
where T : IEnumerable {
if(Equals(left, default(T))) {
throw new ArgumentNullException(nameof(left));
}
if (Equals(right, default(T)))
if(Equals(right, default(T))) {
throw new ArgumentNullException(nameof(right));
}
var leftEnumerable = left.Cast<object>().ToArray();
var rightEnumerable = right.Cast<object>().ToArray();
Object[] leftEnumerable = left.Cast<Object>().ToArray();
Object[] rightEnumerable = right.Cast<Object>().ToArray();
if (leftEnumerable.Length != rightEnumerable.Length)
if(leftEnumerable.Length != rightEnumerable.Length) {
return false;
}
for (var i = 0; i < leftEnumerable.Length; i++)
{
var leftEl = leftEnumerable[i];
var rightEl = rightEnumerable[i];
for(Int32 i = 0; i < leftEnumerable.Length; i++) {
Object leftEl = leftEnumerable[i];
Object rightEl = rightEnumerable[i];
if (!AreEqual(leftEl, rightEl, leftEl.GetType()))
{
if(!AreEqual(leftEl, rightEl, leftEl.GetType())) {
return false;
}
}
+44 -44
View File
@@ -1,37 +1,40 @@
namespace Unosquare.Swan.Components
{
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using Abstractions;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using Unosquare.Swan.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)
{
SourceType = typeof(TSource);
DestinationType = typeof(TDestination);
Map = intersect.ToDictionary(
property => DestinationType.GetProperty(property.Name),
property => new List<PropertyInfo> {SourceType.GetProperty(property.Name)});
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; }
public Dictionary<PropertyInfo, List<PropertyInfo>> Map {
get;
}
/// <inheritdoc/>
public Type SourceType { get; }
public Type SourceType {
get;
}
/// <inheritdoc/>
public Type DestinationType { get; }
public Type DestinationType {
get;
}
/// <summary>
/// Maps the property.
@@ -47,25 +50,22 @@
public ObjectMap<TSource, TDestination> MapProperty
<TDestinationProperty, TSourceProperty>(
Expression<Func<TDestination, TDestinationProperty>> destinationProperty,
Expression<Func<TSource, TSourceProperty>> sourceProperty)
{
var propertyDestinationInfo = (destinationProperty.Body as MemberExpression)?.Member as PropertyInfo;
Expression<Func<TSource, TSourceProperty>> sourceProperty) {
PropertyInfo propertyDestinationInfo = (destinationProperty.Body as MemberExpression)?.Member as PropertyInfo;
if (propertyDestinationInfo == null)
{
if(propertyDestinationInfo == null) {
throw new ArgumentException("Invalid destination expression", nameof(destinationProperty));
}
var sourceMembers = GetSourceMembers(sourceProperty);
List<PropertyInfo> sourceMembers = GetSourceMembers(sourceProperty);
if (sourceMembers.Any() == false)
{
if(sourceMembers.Any() == false) {
throw new ArgumentException("Invalid source expression", nameof(sourceProperty));
}
// reverse order
sourceMembers.Reverse();
Map[propertyDestinationInfo] = sourceMembers;
this.Map[propertyDestinationInfo] = sourceMembers;
return this;
}
@@ -81,31 +81,31 @@
/// </returns>
/// <exception cref="System.Exception">Invalid destination expression.</exception>
public ObjectMap<TSource, TDestination> RemoveMapProperty<TDestinationProperty>(
Expression<Func<TDestination, TDestinationProperty>> destinationProperty)
{
var propertyDestinationInfo = (destinationProperty.Body as MemberExpression)?.Member as PropertyInfo;
Expression<Func<TDestination, TDestinationProperty>> destinationProperty) {
PropertyInfo propertyDestinationInfo = (destinationProperty.Body as MemberExpression)?.Member as PropertyInfo;
if (propertyDestinationInfo == null)
if(propertyDestinationInfo == null) {
throw new ArgumentException("Invalid destination expression", nameof(destinationProperty));
}
if (Map.ContainsKey(propertyDestinationInfo))
{
Map.Remove(propertyDestinationInfo);
if(this.Map.ContainsKey(propertyDestinationInfo)) {
_ = this.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;
private static List<PropertyInfo> GetSourceMembers<TSourceProperty>(Expression<Func<TSource, TSourceProperty>> sourceProperty) {
List<PropertyInfo> sourceMembers = new List<PropertyInfo>();
MemberExpression initialExpression = sourceProperty.Body as MemberExpression;
while (true)
{
var propertySourceInfo = initialExpression?.Member as PropertyInfo;
while(true) {
PropertyInfo propertySourceInfo = initialExpression?.Member as PropertyInfo;
if(propertySourceInfo == null) {
break;
}
if (propertySourceInfo == null) break;
sourceMembers.Add(propertySourceInfo);
initialExpression = initialExpression.Expression as MemberExpression;
}
+100 -126
View File
@@ -1,12 +1,11 @@
namespace Unosquare.Swan.Components
{
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Abstractions;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Unosquare.Swan.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
@@ -77,8 +76,7 @@
/// }
/// </code>
/// </example>
public class ObjectMapper
{
public class ObjectMapper {
private readonly List<IObjectMap> _maps = new List<IObjectMap>();
/// <summary>
@@ -96,17 +94,18 @@
/// or
/// target.
/// </exception>
public static int Copy(
object source,
object target,
string[] propertiesToCopy = null,
string[] ignoreProperties = null)
{
if (source == null)
public static Int32 Copy(
Object source,
Object target,
String[] propertiesToCopy = null,
String[] ignoreProperties = null) {
if(source == null) {
throw new ArgumentNullException(nameof(source));
}
if (target == null)
if(target == null) {
throw new ArgumentNullException(nameof(target));
}
return Copy(
target,
@@ -130,17 +129,18 @@
/// or
/// target.
/// </exception>
public static int Copy(
IDictionary<string, object> source,
object target,
string[] propertiesToCopy = null,
string[] ignoreProperties = null)
{
if (source == null)
public static Int32 Copy(
IDictionary<String, Object> source,
Object target,
String[] propertiesToCopy = null,
String[] ignoreProperties = null) {
if(source == null) {
throw new ArgumentNullException(nameof(source));
}
if (target == null)
if(target == null) {
throw new ArgumentNullException(nameof(target));
}
return Copy(
target,
@@ -148,7 +148,7 @@
ignoreProperties,
source.ToDictionary(
x => x.Key.ToLowerInvariant(),
x => new TypeValuePair(typeof(object), x.Value)));
x => new TypeValuePair(typeof(Object), x.Value)));
}
/// <summary>
@@ -165,26 +165,23 @@
/// or
/// Types doesn't match.
/// </exception>
public ObjectMap<TSource, TDestination> CreateMap<TSource, TDestination>()
{
if (_maps.Any(x => x.SourceType == typeof(TSource) && x.DestinationType == typeof(TDestination)))
{
public ObjectMap<TSource, TDestination> CreateMap<TSource, TDestination>() {
if(this._maps.Any(x => x.SourceType == typeof(TSource) && x.DestinationType == typeof(TDestination))) {
throw new InvalidOperationException("You can't create an existing map");
}
var sourceType = Runtime.PropertyTypeCache.RetrieveAllProperties<TSource>(true);
var destinationType = Runtime.PropertyTypeCache.RetrieveAllProperties<TDestination>(true);
IEnumerable<PropertyInfo> sourceType = Runtime.PropertyTypeCache.RetrieveAllProperties<TSource>(true);
IEnumerable<PropertyInfo> destinationType = Runtime.PropertyTypeCache.RetrieveAllProperties<TDestination>(true);
var intersect = sourceType.Intersect(destinationType, new PropertyInfoComparer()).ToArray();
PropertyInfo[] intersect = sourceType.Intersect(destinationType, new PropertyInfoComparer()).ToArray();
if (intersect.Any() == false)
{
if(intersect.Any() == false) {
throw new InvalidOperationException("Types doesn't match");
}
var map = new ObjectMap<TSource, TDestination>(intersect);
ObjectMap<TSource, TDestination> map = new ObjectMap<TSource, TDestination>(intersect);
_maps.Add(map);
this._maps.Add(map);
return map;
}
@@ -200,58 +197,50 @@
/// </returns>
/// <exception cref="ArgumentNullException">source.</exception>
/// <exception cref="InvalidOperationException">You can't map from type {source.GetType().Name} to {typeof(TDestination).Name}.</exception>
public TDestination Map<TDestination>(object source, bool autoResolve = true)
{
if (source == null)
{
public TDestination Map<TDestination>(Object source, Boolean autoResolve = true) {
if(source == null) {
throw new ArgumentNullException(nameof(source));
}
var destination = Activator.CreateInstance<TDestination>();
var map = _maps
TDestination destination = Activator.CreateInstance<TDestination>();
IObjectMap map = this._maps
.FirstOrDefault(x => x.SourceType == source.GetType() && x.DestinationType == typeof(TDestination));
if (map != null)
{
foreach (var property in map.Map)
{
var finalSource = property.Value.Aggregate(source,
if(map != null) {
foreach(KeyValuePair<PropertyInfo, List<PropertyInfo>> property in map.Map) {
Object finalSource = property.Value.Aggregate(source,
(current, sourceProperty) => sourceProperty.GetValue(current));
property.Key.SetValue(destination, finalSource);
}
}
else
{
if (!autoResolve)
{
} else {
if(!autoResolve) {
throw new InvalidOperationException(
$"You can't map from type {source.GetType().Name} to {typeof(TDestination).Name}");
}
// Missing mapping, try to use default behavior
Copy(source, destination);
_ = Copy(source, destination);
}
return destination;
}
private static int Copy(
object target,
IEnumerable<string> propertiesToCopy,
IEnumerable<string> ignoreProperties,
Dictionary<string, TypeValuePair> sourceProperties)
{
private static Int32 Copy(
Object target,
IEnumerable<String> propertiesToCopy,
IEnumerable<String> ignoreProperties,
Dictionary<String, TypeValuePair> sourceProperties) {
// Filter properties
var requiredProperties = propertiesToCopy?
.Where(p => !string.IsNullOrWhiteSpace(p))
IEnumerable<String> requiredProperties = propertiesToCopy?
.Where(p => !String.IsNullOrWhiteSpace(p))
.Select(p => p.ToLowerInvariant());
var ignoredProperties = ignoreProperties?
.Where(p => !string.IsNullOrWhiteSpace(p))
IEnumerable<String> ignoredProperties = ignoreProperties?
.Where(p => !String.IsNullOrWhiteSpace(p))
.Select(p => p.ToLowerInvariant());
var properties = Runtime.PropertyTypeCache
IEnumerable<PropertyInfo> properties = Runtime.PropertyTypeCache
.RetrieveFilteredProperties(target.GetType(), true, x => x.CanWrite);
return properties
@@ -265,115 +254,99 @@
.Sum(x => TrySetValue(x, target) ? 1 : 0);
}
private static bool TrySetValue(KeyValuePair<PropertyInfo, TypeValuePair> property, object target)
{
try
{
private static Boolean TrySetValue(KeyValuePair<PropertyInfo, TypeValuePair> property, Object target) {
try {
SetValue(property, target);
return true;
}
catch
{
} catch {
// swallow
}
return false;
}
private static void SetValue(KeyValuePair<PropertyInfo, TypeValuePair> property, object target)
{
if (property.Value.Type.GetTypeInfo().IsEnum)
{
private static void SetValue(KeyValuePair<PropertyInfo, TypeValuePair> property, Object target) {
if(property.Value.Type.GetTypeInfo().IsEnum) {
property.Key.SetValue(target,
Enum.ToObject(property.Key.PropertyType, property.Value.Value));
return;
}
if (!property.Value.Type.IsValueType() && property.Key.PropertyType == property.Value.Type)
{
if(!property.Value.Type.IsValueType() && property.Key.PropertyType == property.Value.Type) {
property.Key.SetValue(target, GetValue(property.Value.Value, property.Key.PropertyType));
return;
}
if (property.Key.PropertyType == typeof(bool))
{
if(property.Key.PropertyType == typeof(Boolean)) {
property.Key.SetValue(target,
Convert.ToBoolean(property.Value.Value));
return;
}
property.Key.TrySetBasicType(property.Value.Value, target);
_ = property.Key.TrySetBasicType(property.Value.Value, target);
}
private static object GetValue(object source, Type targetType)
{
if (source == null)
private static Object GetValue(Object source, Type targetType) {
if(source == null) {
return null;
}
object target = null;
Object target = null;
source.CreateTarget(targetType, false, ref target);
switch (source)
{
case string _:
switch(source) {
case String _:
target = source;
break;
case IList sourceList when target is Array targetArray:
for (var i = 0; i < sourceList.Count; i++)
{
try
{
for(Int32 i = 0; i < sourceList.Count; i++) {
try {
targetArray.SetValue(
sourceList[i].GetType().IsValueType()
? sourceList[i]
: sourceList[i].CopyPropertiesToNew<object>(), i);
}
catch
{
: sourceList[i].CopyPropertiesToNew<Object>(), i);
} catch {
// ignored
}
}
break;
case IList sourceList when target is IList targetList:
var addMethod = targetType.GetMethods()
MethodInfo addMethod = targetType.GetMethods()
.FirstOrDefault(
m => m.Name.Equals(Formatters.Json.AddMethodName) && m.IsPublic &&
m.GetParameters().Length == 1);
if (addMethod == null) return target;
foreach (var item in sourceList)
{
try
{
targetList.Add(item.GetType().IsValueType()
? item
: item.CopyPropertiesToNew<object>());
if(addMethod == null) {
return target;
}
catch
{
foreach(Object item in sourceList) {
try {
_ = targetList.Add(item.GetType().IsValueType()
? item
: item.CopyPropertiesToNew<Object>());
} catch {
// ignored
}
}
break;
default:
source.CopyPropertiesTo(target);
_ = source.CopyPropertiesTo(target);
break;
}
return target;
}
private static Dictionary<string, TypeValuePair> GetSourceMap(object source)
{
private static Dictionary<String, TypeValuePair> GetSourceMap(Object source) {
// select distinct properties because they can be duplicated by inheritance
var sourceProperties = Runtime.PropertyTypeCache
PropertyInfo[] sourceProperties = Runtime.PropertyTypeCache
.RetrieveFilteredProperties(source.GetType(), true, x => x.CanRead)
.ToArray();
@@ -386,25 +359,26 @@
sourceProperties.First(y => y.Name == x).GetValue(source)));
}
internal class TypeValuePair
{
public TypeValuePair(Type type, object value)
{
Type = type;
Value = value;
internal class TypeValuePair {
public TypeValuePair(Type type, Object value) {
this.Type = type;
this.Value = value;
}
public Type Type { get; }
public object Value { get; }
public Type Type {
get;
}
internal class PropertyInfoComparer : IEqualityComparer<PropertyInfo>
{
public bool Equals(PropertyInfo x, PropertyInfo y)
public Object Value {
get;
}
}
internal class PropertyInfoComparer : IEqualityComparer<PropertyInfo> {
public Boolean Equals(PropertyInfo x, PropertyInfo y)
=> x != null && y != null && x.Name == y.Name && x.PropertyType == y.PropertyType;
public int GetHashCode(PropertyInfo obj)
public Int32 GetHashCode(PropertyInfo obj)
=> obj.Name.GetHashCode() + obj.PropertyType.Name.GetHashCode();
}
}
@@ -1,11 +1,10 @@
namespace Unosquare.Swan.Components
{
using System;
using System.Linq;
using System.Collections.Concurrent;
using System.Collections.Generic;
using Abstractions;
using System;
using System.Linq;
using System.Collections.Concurrent;
using System.Collections.Generic;
using Unosquare.Swan.Abstractions;
namespace Unosquare.Swan.Components {
/// <summary>
/// Represents an object validator.
/// </summary>
@@ -68,10 +67,9 @@
/// }
/// </code>
/// </example>
public class ObjectValidator
{
private readonly ConcurrentDictionary<Type, List<Tuple<Delegate, string>>> _predicates =
new ConcurrentDictionary<Type, List<Tuple<Delegate, string>>>();
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.
@@ -79,10 +77,9 @@
/// <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);
public ObjectValidationResult Validate<T>(T obj) {
ObjectValidationResult errorList = new ObjectValidationResult();
_ = this.ValidateObject(obj, false, errorList.Add);
return errorList;
}
@@ -96,7 +93,7 @@
/// <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);
public Boolean IsValid<T>(T obj) => this.ValidateObject(obj);
/// <summary>
/// Adds a validator to a specific class.
@@ -109,52 +106,56 @@
/// or
/// message.
/// </exception>
public void AddValidator<T>(Predicate<T> predicate, string message)
where T : class
{
if (predicate == null)
public void AddValidator<T>(Predicate<T> predicate, String message)
where T : class {
if(predicate == null) {
throw new ArgumentNullException(nameof(predicate));
}
if (string.IsNullOrEmpty(message))
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));
if(!this._predicates.TryGetValue(typeof(T), out List<Tuple<Delegate, String>> existing)) {
existing = new List<Tuple<Delegate, String>>();
this._predicates[typeof(T)] = existing;
}
private bool ValidateObject<T>(T obj, bool returnOnError = true, Action<string, string> action = null)
{
if (Equals(obj, null))
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 (_predicates.ContainsKey(typeof(T)))
{
foreach (var validation in _predicates[typeof(T)])
{
if ((bool) validation.Item1.DynamicInvoke(obj)) continue;
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;
action?.Invoke(String.Empty, validation.Item2);
if(returnOnError) {
return false;
}
}
}
var properties = Runtime.AttributeCache.RetrieveFromType<T>(typeof(IValidator));
Dictionary<System.Reflection.PropertyInfo, IEnumerable<Object>> properties = Runtime.AttributeCache.RetrieveFromType<T>(typeof(IValidator));
foreach (var prop in properties)
{
foreach (var attribute in prop.Value)
{
var val = (IValidator) attribute;
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;
if(val.IsValid(prop.Key.GetValue(obj, null))) {
continue;
}
action?.Invoke(prop.Key.Name, val.ErrorMessage);
if (returnOnError) return false;
if(returnOnError) {
return false;
}
}
}
@@ -165,8 +166,7 @@
/// <summary>
/// Defines a validation result containing all validation errors and their properties.
/// </summary>
public class ObjectValidationResult
{
public class ObjectValidationResult {
/// <summary>
/// A list of errors.
/// </summary>
@@ -175,30 +175,33 @@
/// <summary>
/// <c>true</c> if there are no errors; otherwise, <c>false</c>.
/// </summary>
public bool IsValid => !Errors.Any();
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) =>
Errors.Add(new ValidationError {ErrorMessage = errorMessage, PropertyName = propertyName});
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
{
public class ValidationError {
/// <summary>
/// The property name.
/// </summary>
public string PropertyName { get; set; }
public String PropertyName {
get; set;
}
/// <summary>
/// The message error.
/// </summary>
public string ErrorMessage { get; set; }
public String ErrorMessage {
get; set;
}
}
}
}
@@ -1,22 +1,19 @@
namespace Unosquare.Swan.Components
{
using System;
using System.Threading;
using Abstractions;
using System;
using System.Threading;
using Unosquare.Swan.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
{
public static class SyncLockerFactory {
#region Enums and Interfaces
/// <summary>
/// Enumerates the locking operations.
/// </summary>
private enum LockHolderType
{
private enum LockHolderType {
Read,
Write,
}
@@ -24,8 +21,7 @@
/// <summary>
/// Defines methods for releasing locks.
/// </summary>
private interface ISyncReleasable
{
private interface ISyncReleasable {
/// <summary>
/// Releases the writer lock.
/// </summary>
@@ -67,7 +63,7 @@
/// </summary>
/// <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>
public static ISyncLocker Create(bool useSlim) => useSlim ? CreateSlim() : Create();
public static ISyncLocker Create(Boolean useSlim) => useSlim ? CreateSlim() : Create();
#endregion
@@ -77,34 +73,35 @@
/// The lock releaser. Calling the dispose method releases the lock entered by the parent SyncLocker.
/// </summary>
/// <seealso cref="System.IDisposable" />
private sealed class SyncLockReleaser : IDisposable
{
private sealed class SyncLockReleaser : IDisposable {
private readonly ISyncReleasable _parent;
private readonly LockHolderType _operation;
private bool _isDisposed;
private Boolean _isDisposed;
/// <summary>
/// Initializes a new instance of the <see cref="SyncLockReleaser"/> class.
/// </summary>
/// <param name="parent">The parent.</param>
/// <param name="operation">The operation.</param>
public SyncLockReleaser(ISyncReleasable parent, LockHolderType operation)
{
_parent = parent;
_operation = operation;
public SyncLockReleaser(ISyncReleasable parent, LockHolderType operation) {
this._parent = parent;
this._operation = operation;
}
/// <inheritdoc />
public void Dispose()
{
if (_isDisposed) return;
_isDisposed = true;
public void Dispose() {
if(this._isDisposed) {
return;
}
if (_operation == LockHolderType.Read)
_parent.ReleaseReaderLock();
else
_parent.ReleaseWriterLock();
this._isDisposed = true;
if(this._operation == LockHolderType.Read) {
this._parent.ReleaseReaderLock();
} else {
this._parent.ReleaseWriterLock();
}
}
}
@@ -114,38 +111,37 @@
/// </summary>
/// <seealso cref="ISyncLocker" />
/// <seealso cref="ISyncReleasable" />
private sealed class SyncLocker : ISyncLocker, ISyncReleasable
{
private bool _isDisposed;
private sealed class SyncLocker : ISyncLocker, ISyncReleasable {
private Boolean _isDisposed;
private ReaderWriterLock _locker = new ReaderWriterLock();
/// <inheritdoc />
public IDisposable AcquireReaderLock()
{
_locker?.AcquireReaderLock(Timeout.Infinite);
public IDisposable AcquireReaderLock() {
this._locker?.AcquireReaderLock(Timeout.Infinite);
return new SyncLockReleaser(this, LockHolderType.Read);
}
/// <inheritdoc />
public IDisposable AcquireWriterLock()
{
_locker?.AcquireWriterLock(Timeout.Infinite);
public IDisposable AcquireWriterLock() {
this._locker?.AcquireWriterLock(Timeout.Infinite);
return new SyncLockReleaser(this, LockHolderType.Write);
}
/// <inheritdoc />
public void ReleaseWriterLock() => _locker?.ReleaseWriterLock();
public void ReleaseWriterLock() => this._locker?.ReleaseWriterLock();
/// <inheritdoc />
public void ReleaseReaderLock() => _locker?.ReleaseReaderLock();
public void ReleaseReaderLock() => this._locker?.ReleaseReaderLock();
/// <inheritdoc />
public void Dispose()
{
if (_isDisposed) return;
_isDisposed = true;
_locker?.ReleaseLock();
_locker = null;
public void Dispose() {
if(this._isDisposed) {
return;
}
this._isDisposed = true;
_ = this._locker?.ReleaseLock();
this._locker = null;
}
}
#endif
@@ -155,40 +151,39 @@
/// </summary>
/// <seealso cref="ISyncLocker" />
/// <seealso cref="ISyncReleasable" />
private sealed class SyncLockerSlim : ISyncLocker, ISyncReleasable
{
private bool _isDisposed;
private sealed class SyncLockerSlim : ISyncLocker, ISyncReleasable {
private Boolean _isDisposed;
private ReaderWriterLockSlim _locker
= new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion);
/// <inheritdoc />
public IDisposable AcquireReaderLock()
{
_locker?.EnterReadLock();
public IDisposable AcquireReaderLock() {
this._locker?.EnterReadLock();
return new SyncLockReleaser(this, LockHolderType.Read);
}
/// <inheritdoc />
public IDisposable AcquireWriterLock()
{
_locker?.EnterWriteLock();
public IDisposable AcquireWriterLock() {
this._locker?.EnterWriteLock();
return new SyncLockReleaser(this, LockHolderType.Write);
}
/// <inheritdoc />
public void ReleaseWriterLock() => _locker?.ExitWriteLock();
public void ReleaseWriterLock() => this._locker?.ExitWriteLock();
/// <inheritdoc />
public void ReleaseReaderLock() => _locker?.ExitReadLock();
public void ReleaseReaderLock() => this._locker?.ExitReadLock();
/// <inheritdoc />
public void Dispose()
{
if (_isDisposed) return;
_isDisposed = true;
_locker?.Dispose();
_locker = null;
public void Dispose() {
if(this._isDisposed) {
return;
}
this._isDisposed = true;
this._locker?.Dispose();
this._locker = null;
}
}
+17 -25
View File
@@ -1,10 +1,9 @@
#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.
///
@@ -12,43 +11,36 @@ namespace Unosquare.Swan.Components
/// the time condition is satisfied.
/// </summary>
/// <seealso cref="SingletonBase{TimerControl}" />
public class TimerControl : SingletonBase<TimerControl>
{
public class TimerControl : SingletonBase<TimerControl> {
[System.Diagnostics.CodeAnalysis.SuppressMessage("Codequalität", "IDE0052:Ungelesene private Member entfernen", Justification = "<Ausstehend>")]
private readonly Timer _innerTimer;
private readonly IWaitEvent _delayLock = WaitEventFactory.Create(true);
/// <summary>
/// Initializes a new instance of the <see cref="TimerControl"/> class.
/// </summary>
protected TimerControl()
{
_innerTimer = new Timer(
x =>
{
try
{
_delayLock.Complete();
_delayLock.Begin();
}
catch
{
protected TimerControl() => this._innerTimer = new Timer(
x => {
try {
this._delayLock.Complete();
this._delayLock.Begin();
} catch {
// ignore
}
},
null,
0,
15);
}
/// <summary>
/// Waits until the time is elapsed.
/// </summary>
/// <param name="untilDate">The until date.</param>
/// <param name="ct">The cancellation token.</param>
public void WaitUntil(DateTime untilDate, CancellationToken ct = default)
{
while (!ct.IsCancellationRequested && DateTime.UtcNow < untilDate)
_delayLock.Wait();
public void WaitUntil(DateTime untilDate, CancellationToken ct = default) {
while(!ct.IsCancellationRequested && DateTime.UtcNow < untilDate) {
this._delayLock.Wait();
}
}
/// <summary>
@@ -57,7 +49,7 @@ namespace Unosquare.Swan.Components
/// <param name="waitTime">The wait time.</param>
/// <param name="ct">The cancellation token.</param>
public void Wait(TimeSpan waitTime, CancellationToken ct = default) =>
WaitUntil(DateTime.UtcNow.Add(waitTime), ct);
this.WaitUntil(DateTime.UtcNow.Add(waitTime), ct);
}
}
#endif
@@ -1,10 +1,10 @@
#if !NETSTANDARD1_3
namespace Unosquare.Swan.Components
{
using System;
using System.Threading;
using Abstractions;

#if !NETSTANDARD1_3
using System;
using System.Threading;
using Unosquare.Swan.Abstractions;
namespace Unosquare.Swan.Components {
/// <summary>
/// Provides a Manual Reset Event factory with a unified API.
/// </summary>
@@ -53,8 +53,7 @@ namespace Unosquare.Swan.Components
/// }
/// </code>
/// </example>
public static class WaitEventFactory
{
public static class WaitEventFactory {
#region Factory Methods
/// <summary>
@@ -62,14 +61,14 @@ namespace Unosquare.Swan.Components
/// </summary>
/// <param name="isCompleted">if initially set to completed. Generally true.</param>
/// <returns>The Wait Event.</returns>
public static IWaitEvent Create(bool isCompleted) => new WaitEvent(isCompleted);
public static IWaitEvent Create(Boolean isCompleted) => new WaitEvent(isCompleted);
/// <summary>
/// Creates a Wait Event backed by a ManualResetEventSlim.
/// </summary>
/// <param name="isCompleted">if initially set to completed. Generally true.</param>
/// <returns>The Wait Event.</returns>
public static IWaitEvent CreateSlim(bool isCompleted) => new WaitEventSlim(isCompleted);
public static IWaitEvent CreateSlim(Boolean isCompleted) => new WaitEventSlim(isCompleted);
/// <summary>
/// Creates a Wait Event backed by a ManualResetEventSlim.
@@ -77,7 +76,7 @@ namespace Unosquare.Swan.Components
/// <param name="isCompleted">if initially set to completed. Generally true.</param>
/// <param name="useSlim">if set to <c>true</c> creates a slim version of the wait event.</param>
/// <returns>The Wait Event.</returns>
public static IWaitEvent Create(bool isCompleted, bool useSlim) => useSlim ? CreateSlim(isCompleted) : Create(isCompleted);
public static IWaitEvent Create(Boolean isCompleted, Boolean useSlim) => useSlim ? CreateSlim(isCompleted) : Create(isCompleted);
#endregion
@@ -86,134 +85,109 @@ namespace Unosquare.Swan.Components
/// <summary>
/// Defines a WaitEvent backed by a ManualResetEvent.
/// </summary>
private class WaitEvent : IWaitEvent
{
private class WaitEvent : IWaitEvent {
private ManualResetEvent _event;
/// <summary>
/// Initializes a new instance of the <see cref="WaitEvent"/> class.
/// </summary>
/// <param name="isCompleted">if set to <c>true</c> [is completed].</param>
public WaitEvent(bool isCompleted)
{
_event = new ManualResetEvent(isCompleted);
public WaitEvent(Boolean isCompleted) => this._event = new ManualResetEvent(isCompleted);
/// <inheritdoc />
public Boolean IsDisposed {
get; private set;
}
/// <inheritdoc />
public bool IsDisposed { get; private set; }
public Boolean IsValid => this.IsDisposed || this._event == null
? false
: this._event?.SafeWaitHandle?.IsClosed ?? true ? false : !(this._event?.SafeWaitHandle?.IsInvalid ?? true);
/// <inheritdoc />
public bool IsValid
{
get
{
if (IsDisposed || _event == null)
return false;
public Boolean IsCompleted => this.IsValid == false ? true : this._event?.WaitOne(0) ?? true;
if (_event?.SafeWaitHandle?.IsClosed ?? true)
return false;
/// <inheritdoc />
public Boolean IsInProgress => !this.IsCompleted;
return !(_event?.SafeWaitHandle?.IsInvalid ?? true);
/// <inheritdoc />
public void Begin() => this._event?.Reset();
/// <inheritdoc />
public void Complete() => this._event?.Set();
/// <inheritdoc />
void IDisposable.Dispose() {
if(this.IsDisposed) {
return;
}
this.IsDisposed = true;
_ = this._event?.Set();
this._event?.Dispose();
this._event = null;
}
/// <inheritdoc />
public bool IsCompleted
{
get
{
if (IsValid == false)
return true;
return _event?.WaitOne(0) ?? true;
}
}
public void Wait() => this._event?.WaitOne();
/// <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?.WaitOne();
/// <inheritdoc />
public bool Wait(TimeSpan timeout) => _event?.WaitOne(timeout) ?? true;
public Boolean Wait(TimeSpan timeout) => this._event?.WaitOne(timeout) ?? true;
}
/// <summary>
/// Defines a WaitEvent backed by a ManualResetEventSlim.
/// </summary>
private class WaitEventSlim : IWaitEvent
{
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);
public WaitEventSlim(Boolean isCompleted) => this._event = new ManualResetEventSlim(isCompleted);
/// <inheritdoc />
public Boolean IsDisposed {
get; private set;
}
/// <inheritdoc />
public bool IsDisposed { get; private set; }
public Boolean IsValid => this.IsDisposed || this._event?.WaitHandle?.SafeWaitHandle == null
? false
: !this._event.WaitHandle.SafeWaitHandle.IsClosed && !this._event.WaitHandle.SafeWaitHandle.IsInvalid;
/// <inheritdoc />
public bool IsValid
{
get
{
if (IsDisposed || _event?.WaitHandle?.SafeWaitHandle == null) return false;
public Boolean IsCompleted => this.IsValid == false || this._event.IsSet;
return !_event.WaitHandle.SafeWaitHandle.IsClosed && !_event.WaitHandle.SafeWaitHandle.IsInvalid;
/// <inheritdoc />
public Boolean IsInProgress => !this.IsCompleted;
/// <inheritdoc />
public void Begin() => this._event?.Reset();
/// <inheritdoc />
public void Complete() => this._event?.Set();
/// <inheritdoc />
void IDisposable.Dispose() {
if(this.IsDisposed) {
return;
}
this.IsDisposed = true;
this._event?.Set();
this._event?.Dispose();
this._event = null;
}
/// <inheritdoc />
public bool IsCompleted => IsValid == false || _event.IsSet;
public void Wait() => this._event?.Wait();
/// <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;
public Boolean Wait(TimeSpan timeout) => this._event?.Wait(timeout) ?? true;
}
#endregion
+54 -56
View File
@@ -1,15 +1,13 @@
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
{
public struct DateTimeSpan {
/// <summary>
/// Initializes a new instance of the <see cref="DateTimeSpan"/> struct.
/// </summary>
@@ -20,15 +18,14 @@
/// <param name="minutes">The minutes.</param>
/// <param name="seconds">The seconds.</param>
/// <param name="milliseconds">The milliseconds.</param>
public DateTimeSpan(int years, int months, int days, int hours, int minutes, int seconds, int milliseconds)
{
Years = years;
Months = months;
Days = days;
Hours = hours;
Minutes = minutes;
Seconds = seconds;
Milliseconds = milliseconds;
public DateTimeSpan(Int32 years, Int32 months, Int32 days, Int32 hours, Int32 minutes, Int32 seconds, Int32 milliseconds) {
this.Years = years;
this.Months = months;
this.Days = days;
this.Hours = hours;
this.Minutes = minutes;
this.Seconds = seconds;
this.Milliseconds = milliseconds;
}
/// <summary>
@@ -37,7 +34,9 @@
/// <value>
/// The years.
/// </value>
public int Years { get; }
public Int32 Years {
get;
}
/// <summary>
/// Gets the months.
@@ -45,7 +44,9 @@
/// <value>
/// The months.
/// </value>
public int Months { get; }
public Int32 Months {
get;
}
/// <summary>
/// Gets the days.
@@ -53,7 +54,9 @@
/// <value>
/// The days.
/// </value>
public int Days { get; }
public Int32 Days {
get;
}
/// <summary>
/// Gets the hours.
@@ -61,7 +64,9 @@
/// <value>
/// The hours.
/// </value>
public int Hours { get; }
public Int32 Hours {
get;
}
/// <summary>
/// Gets the minutes.
@@ -69,7 +74,9 @@
/// <value>
/// The minutes.
/// </value>
public int Minutes { get; }
public Int32 Minutes {
get;
}
/// <summary>
/// Gets the seconds.
@@ -77,7 +84,9 @@
/// <value>
/// The seconds.
/// </value>
public int Seconds { get; }
public Int32 Seconds {
get;
}
/// <summary>
/// Gets the milliseconds.
@@ -85,62 +94,54 @@
/// <value>
/// The milliseconds.
/// </value>
public int Milliseconds { get; }
public Int32 Milliseconds {
get;
}
internal static DateTimeSpan CompareDates(DateTime date1, DateTime date2)
{
if (date2 < date1)
{
var sub = date1;
internal static DateTimeSpan CompareDates(DateTime date1, DateTime date2) {
if(date2 < date1) {
DateTime sub = date1;
date1 = date2;
date2 = sub;
}
var current = date1;
var years = 0;
var months = 0;
var days = 0;
DateTime current = date1;
Int32 years = 0;
Int32 months = 0;
Int32 days = 0;
var phase = Phase.Years;
var span = new DateTimeSpan();
var officialDay = current.Day;
Phase phase = Phase.Years;
DateTimeSpan span = new DateTimeSpan();
Int32 officialDay = current.Day;
while (phase != Phase.Done)
{
switch (phase)
{
while(phase != Phase.Done) {
switch(phase) {
case Phase.Years:
if (current.AddYears(years + 1) > date2)
{
if(current.AddYears(years + 1) > date2) {
phase = Phase.Months;
current = current.AddYears(years);
}
else
{
} else {
years++;
}
break;
case Phase.Months:
if (current.AddMonths(months + 1) > date2)
{
if(current.AddMonths(months + 1) > date2) {
phase = Phase.Days;
current = current.AddMonths(months);
if (current.Day < officialDay &&
officialDay <= DateTime.DaysInMonth(current.Year, current.Month))
if(current.Day < officialDay &&
officialDay <= DateTime.DaysInMonth(current.Year, current.Month)) {
current = current.AddDays(officialDay - current.Day);
}
else
{
} else {
months++;
}
break;
case Phase.Days:
if (current.AddDays(days + 1) > date2)
{
if(current.AddDays(days + 1) > date2) {
current = current.AddDays(days);
var timespan = date2 - current;
TimeSpan timespan = date2 - current;
span = new DateTimeSpan(
years,
months,
@@ -150,9 +151,7 @@
timespan.Seconds,
timespan.Milliseconds);
phase = Phase.Done;
}
else
{
} else {
days++;
}
@@ -163,8 +162,7 @@
return span;
}
private enum Phase
{
private enum Phase {
Years,
Months,
Days,
+50 -42
View File
@@ -1,16 +1,14 @@
namespace Unosquare.Swan
{
using Reflection;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using Unosquare.Swan.Reflection;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
namespace Unosquare.Swan {
/// <summary>
/// Contains useful constants and definitions.
/// </summary>
public static partial class Definitions
{
public static partial class Definitions {
#region Main Dictionary Definition
/// <summary>
@@ -21,39 +19,39 @@
{
// Non-Nullables
{typeof(DateTime), new ExtendedTypeInfo<DateTime>()},
{typeof(byte), new ExtendedTypeInfo<byte>()},
{typeof(sbyte), new ExtendedTypeInfo<sbyte>()},
{typeof(int), new ExtendedTypeInfo<int>()},
{typeof(uint), new ExtendedTypeInfo<uint>()},
{typeof(short), new ExtendedTypeInfo<short>()},
{typeof(ushort), new ExtendedTypeInfo<ushort>()},
{typeof(long), new ExtendedTypeInfo<long>()},
{typeof(ulong), new ExtendedTypeInfo<ulong>()},
{typeof(float), new ExtendedTypeInfo<float>()},
{typeof(double), new ExtendedTypeInfo<double>()},
{typeof(char), new ExtendedTypeInfo<char>()},
{typeof(bool), new ExtendedTypeInfo<bool>()},
{typeof(decimal), new ExtendedTypeInfo<decimal>()},
{typeof(Byte), new ExtendedTypeInfo<Byte>()},
{typeof(SByte), new ExtendedTypeInfo<SByte>()},
{typeof(Int32), new ExtendedTypeInfo<Int32>()},
{typeof(UInt32), new ExtendedTypeInfo<UInt32>()},
{typeof(Int16), new ExtendedTypeInfo<Int16>()},
{typeof(UInt16), new ExtendedTypeInfo<UInt16>()},
{typeof(Int64), new ExtendedTypeInfo<Int64>()},
{typeof(UInt64), new ExtendedTypeInfo<UInt64>()},
{typeof(Single), new ExtendedTypeInfo<Single>()},
{typeof(Double), new ExtendedTypeInfo<Double>()},
{typeof(Char), new ExtendedTypeInfo<Char>()},
{typeof(Boolean), new ExtendedTypeInfo<Boolean>()},
{typeof(Decimal), new ExtendedTypeInfo<Decimal>()},
{typeof(Guid), new ExtendedTypeInfo<Guid>()},
// 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
{typeof(DateTime?), new ExtendedTypeInfo<DateTime?>()},
{typeof(byte?), new ExtendedTypeInfo<byte?>()},
{typeof(sbyte?), new ExtendedTypeInfo<sbyte?>()},
{typeof(int?), new ExtendedTypeInfo<int?>()},
{typeof(uint?), new ExtendedTypeInfo<uint?>()},
{typeof(short?), new ExtendedTypeInfo<short?>()},
{typeof(ushort?), new ExtendedTypeInfo<ushort?>()},
{typeof(long?), new ExtendedTypeInfo<long?>()},
{typeof(ulong?), new ExtendedTypeInfo<ulong?>()},
{typeof(float?), new ExtendedTypeInfo<float?>()},
{typeof(double?), new ExtendedTypeInfo<double?>()},
{typeof(char?), new ExtendedTypeInfo<char?>()},
{typeof(bool?), new ExtendedTypeInfo<bool?>()},
{typeof(decimal?), new ExtendedTypeInfo<decimal?>()},
{typeof(Byte?), new ExtendedTypeInfo<Byte?>()},
{typeof(SByte?), new ExtendedTypeInfo<SByte?>()},
{typeof(Int32?), new ExtendedTypeInfo<Int32?>()},
{typeof(UInt32?), new ExtendedTypeInfo<UInt32?>()},
{typeof(Int16?), new ExtendedTypeInfo<Int16?>()},
{typeof(UInt16?), new ExtendedTypeInfo<UInt16?>()},
{typeof(Int64?), new ExtendedTypeInfo<Int64?>()},
{typeof(UInt64?), new ExtendedTypeInfo<UInt64?>()},
{typeof(Single?), new ExtendedTypeInfo<Single?>()},
{typeof(Double?), new ExtendedTypeInfo<Double?>()},
{typeof(Char?), new ExtendedTypeInfo<Char?>()},
{typeof(Boolean?), new ExtendedTypeInfo<Boolean?>()},
{typeof(Decimal?), new ExtendedTypeInfo<Decimal?>()},
{typeof(Guid?), new ExtendedTypeInfo<Guid?>()},
// Additional Types
@@ -79,7 +77,9 @@
/// <value>
/// All numeric types.
/// </value>
public static List<Type> AllNumericTypes { get; } = new List<Type>(
public static List<Type> AllNumericTypes {
get;
} = new List<Type>(
BasicTypesInfo
.Where(kvp => kvp.Value.IsNumeric)
.Select(kvp => kvp.Key).ToArray());
@@ -91,7 +91,9 @@
/// <value>
/// All numeric value types.
/// </value>
public static List<Type> AllNumericValueTypes { get; } = new List<Type>(
public static List<Type> AllNumericValueTypes {
get;
} = new List<Type>(
BasicTypesInfo
.Where(kvp => kvp.Value.IsNumeric && kvp.Value.IsNullableValueType == false)
.Select(kvp => kvp.Key).ToArray());
@@ -102,7 +104,9 @@
/// <value>
/// All basic value types.
/// </value>
public static List<Type> AllBasicValueTypes { get; } = new List<Type>(
public static List<Type> AllBasicValueTypes {
get;
} = new List<Type>(
BasicTypesInfo
.Where(kvp => kvp.Value.IsValueType)
.Select(kvp => kvp.Key).ToArray());
@@ -113,9 +117,11 @@
/// <value>
/// All basic value and string types.
/// </value>
public static List<Type> AllBasicValueAndStringTypes { get; } = new List<Type>(
public static List<Type> AllBasicValueAndStringTypes {
get;
} = new List<Type>(
BasicTypesInfo
.Where(kvp => kvp.Value.IsValueType || kvp.Key == typeof(string))
.Where(kvp => kvp.Value.IsValueType || kvp.Key == typeof(String))
.Select(kvp => kvp.Key).ToArray());
/// <summary>
@@ -124,7 +130,9 @@
/// <value>
/// All basic nullable value types.
/// </value>
public static List<Type> AllBasicNullableValueTypes { get; } = new List<Type>(
public static List<Type> AllBasicNullableValueTypes {
get;
} = new List<Type>(
BasicTypesInfo
.Where(kvp => kvp.Value.IsNullableValueType)
.Select(kvp => kvp.Key).ToArray());
+8 -13
View File
@@ -1,12 +1,11 @@
namespace Unosquare.Swan
{
using System.Text;
using System;
using System.Text;
namespace Unosquare.Swan {
/// <summary>
/// Contains useful constants and definitions.
/// </summary>
public static partial class Definitions
{
public static partial class Definitions {
/// <summary>
/// The MS Windows codepage 1252 encoding used in some legacy scenarios
/// such as default CSV text encoding from Excel.
@@ -22,15 +21,11 @@
/// <summary>
/// Initializes the <see cref="Definitions"/> class.
/// </summary>
static Definitions()
{
CurrentAnsiEncoding = Encoding.GetEncoding(default(int));
try
{
static Definitions() {
CurrentAnsiEncoding = Encoding.GetEncoding(default(Int32));
try {
Windows1252Encoding = Encoding.GetEncoding(1252);
}
catch
{
} catch {
// ignore, the codepage is not available use default
Windows1252Encoding = CurrentAnsiEncoding;
}
+4 -8
View File
@@ -1,10 +1,8 @@
namespace Unosquare.Swan
{
namespace Unosquare.Swan {
/// <summary>
/// Enumeration of Operating Systems.
/// </summary>
public enum OperatingSystem
{
public enum OperatingSystem {
/// <summary>
/// Unknown OS
/// </summary>
@@ -29,8 +27,7 @@
/// <summary>
/// Enumerates the different Application Worker States.
/// </summary>
public enum AppWorkerState
{
public enum AppWorkerState {
/// <summary>
/// The stopped
/// </summary>
@@ -45,8 +42,7 @@
/// <summary>
/// Defines Endianness, big or little.
/// </summary>
public enum Endianness
{
public enum Endianness {
/// <summary>
/// In big endian, you store the most significant byte in the smallest address.
/// </summary>
+52 -39
View File
@@ -1,15 +1,13 @@
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
{
public class LogMessageReceivedEventArgs : EventArgs {
/// <summary>
/// Initializes a new instance of the <see cref="LogMessageReceivedEventArgs" /> class.
/// </summary>
@@ -23,25 +21,24 @@
/// <param name="callerFilePath">The caller file path.</param>
/// <param name="callerLineNumber">The caller line number.</param>
public LogMessageReceivedEventArgs(
ulong sequence,
UInt64 sequence,
LogMessageType messageType,
DateTime utcDate,
string source,
string message,
object extendedData,
string callerMemberName,
string callerFilePath,
int callerLineNumber)
{
Sequence = sequence;
MessageType = messageType;
UtcDate = utcDate;
Source = source;
Message = message;
CallerMemberName = callerMemberName;
CallerFilePath = callerFilePath;
CallerLineNumber = callerLineNumber;
ExtendedData = extendedData;
String source,
String message,
Object extendedData,
String callerMemberName,
String callerFilePath,
Int32 callerLineNumber) {
this.Sequence = sequence;
this.MessageType = messageType;
this.UtcDate = utcDate;
this.Source = source;
this.Message = message;
this.CallerMemberName = callerMemberName;
this.CallerFilePath = callerFilePath;
this.CallerLineNumber = callerLineNumber;
this.ExtendedData = extendedData;
}
/// <summary>
@@ -50,7 +47,9 @@
/// <value>
/// The sequence.
/// </value>
public ulong Sequence { get; }
public UInt64 Sequence {
get;
}
/// <summary>
/// Gets the type of the message.
@@ -59,7 +58,9 @@
/// <value>
/// The type of the message.
/// </value>
public LogMessageType MessageType { get; }
public LogMessageType MessageType {
get;
}
/// <summary>
/// Gets the UTC date at which the event at which the message was logged.
@@ -67,7 +68,9 @@
/// <value>
/// The UTC date.
/// </value>
public DateTime UtcDate { get; }
public DateTime UtcDate {
get;
}
/// <summary>
/// Gets the name of the source where the logging message
@@ -76,7 +79,9 @@
/// <value>
/// The source.
/// </value>
public string Source { get; }
public String Source {
get;
}
/// <summary>
/// Gets the body of the message.
@@ -84,7 +89,9 @@
/// <value>
/// The message.
/// </value>
public string Message { get; }
public String Message {
get;
}
/// <summary>
/// Gets the name of the caller member.
@@ -92,7 +99,9 @@
/// <value>
/// The name of the caller member.
/// </value>
public string CallerMemberName { get; }
public String CallerMemberName {
get;
}
/// <summary>
/// Gets the caller file path.
@@ -100,7 +109,9 @@
/// <value>
/// The caller file path.
/// </value>
public string CallerFilePath { get; }
public String CallerFilePath {
get;
}
/// <summary>
/// Gets the caller line number.
@@ -108,7 +119,9 @@
/// <value>
/// The caller line number.
/// </value>
public int CallerLineNumber { get; }
public Int32 CallerLineNumber {
get;
}
/// <summary>
/// Gets an object representing extended data.
@@ -117,7 +130,9 @@
/// <value>
/// The extended data.
/// </value>
public object ExtendedData { get; }
public Object ExtendedData {
get;
}
/// <summary>
/// Gets the Extended Data properties cast as an Exception (if possible)
@@ -126,7 +141,7 @@
/// <value>
/// The exception.
/// </value>
public Exception Exception => ExtendedData as Exception;
public Exception Exception => this.ExtendedData as Exception;
}
/// <summary>
@@ -135,8 +150,7 @@
/// event handler to prevent the terminal from displaying the message.
/// </summary>
/// <seealso cref="LogMessageReceivedEventArgs" />
public class LogMessageDisplayingEventArgs : LogMessageReceivedEventArgs
{
public class LogMessageDisplayingEventArgs : LogMessageReceivedEventArgs {
/// <summary>
/// Initializes a new instance of the <see cref="LogMessageDisplayingEventArgs"/> class.
/// </summary>
@@ -151,10 +165,7 @@
data.ExtendedData,
data.CallerMemberName,
data.CallerFilePath,
data.CallerLineNumber)
{
CancelOutput = false;
}
data.CallerLineNumber) => this.CancelOutput = false;
/// <summary>
/// Gets or sets a value indicating whether the displaying of the
@@ -163,6 +174,8 @@
/// <value>
/// <c>true</c> if [cancel output]; otherwise, <c>false</c>.
/// </value>
public bool CancelOutput { get; set; }
public Boolean CancelOutput {
get; set;
}
}
}
+186 -176
View File
@@ -1,19 +1,17 @@
namespace Unosquare.Swan
{
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace Unosquare.Swan {
/// <summary>
/// Provides various extension methods for byte arrays and streams.
/// </summary>
public static class ByteArrayExtensions
{
public static class ByteArrayExtensions {
/// <summary>
/// Converts an array of bytes to its lower-case, hexadecimal representation.
/// </summary>
@@ -23,7 +21,7 @@
/// The specified string instance; no actual conversion is performed.
/// </returns>
/// <exception cref="ArgumentNullException">bytes.</exception>
public static string ToLowerHex(this byte[] bytes, bool addPrefix = false)
public static String ToLowerHex(this Byte[] bytes, Boolean addPrefix = false)
=> ToHex(bytes, addPrefix, "x2");
/// <summary>
@@ -35,7 +33,7 @@
/// The specified string instance; no actual conversion is performed.
/// </returns>
/// <exception cref="ArgumentNullException">bytes.</exception>
public static string ToUpperHex(this byte[] bytes, bool addPrefix = false)
public static String ToUpperHex(this Byte[] bytes, Boolean addPrefix = false)
=> ToHex(bytes, addPrefix, "X2");
/// <summary>
@@ -47,14 +45,14 @@
/// A string of hexadecimal pairs separated by hyphens, where each pair represents
/// the corresponding element in value; for example, "7F-2C-4A-00".
/// </returns>
public static string ToDashedHex(this byte[] bytes) => BitConverter.ToString(bytes);
public static String ToDashedHex(this Byte[] bytes) => BitConverter.ToString(bytes);
/// <summary>
/// Converts an array of bytes to a base-64 encoded string.
/// </summary>
/// <param name="bytes">The bytes.</param>
/// <returns>A <see cref="System.String" /> converted from an array of bytes.</returns>
public static string ToBase64(this byte[] bytes) => Convert.ToBase64String(bytes);
public static String ToBase64(this Byte[] bytes) => Convert.ToBase64String(bytes);
/// <summary>
/// Converts a set of hexadecimal characters (uppercase or lowercase)
@@ -66,10 +64,10 @@
/// A byte array containing the results of encoding the specified set of characters.
/// </returns>
/// <exception cref="ArgumentNullException">hex.</exception>
public static byte[] ConvertHexadecimalToBytes(this string hex)
{
if (string.IsNullOrWhiteSpace(hex))
public static Byte[] ConvertHexadecimalToBytes(this String hex) {
if(String.IsNullOrWhiteSpace(hex)) {
throw new ArgumentNullException(nameof(hex));
}
return Enumerable
.Range(0, hex.Length / 2)
@@ -86,7 +84,7 @@
/// <returns>
/// Bit value at the given offset.
/// </returns>
public static byte GetBitValueAt(this byte b, byte offset, byte length = 1) => (byte)((b >> offset) & ~(0xff << length));
public static Byte GetBitValueAt(this Byte b, Byte offset, Byte length = 1) => (Byte)((b >> offset) & ~(0xff << length));
/// <summary>
/// Sets the bit value at the given offset.
@@ -96,12 +94,11 @@
/// <param name="length">The length.</param>
/// <param name="value">The value.</param>
/// <returns>Bit value at the given offset.</returns>
public static byte SetBitValueAt(this byte b, byte offset, byte length, byte value)
{
var mask = ~(0xff << length);
var valueAt = (byte)(value & mask);
public static Byte SetBitValueAt(this Byte b, Byte offset, Byte length, Byte value) {
Int32 mask = ~(0xff << length);
Byte valueAt = (Byte)(value & mask);
return (byte)((valueAt << offset) | (b & ~(mask << offset)));
return (Byte)((valueAt << offset) | (b & ~(mask << offset)));
}
/// <summary>
@@ -111,7 +108,7 @@
/// <param name="offset">The offset.</param>
/// <param name="value">The value.</param>
/// <returns>Bit value at the given offset.</returns>
public static byte SetBitValueAt(this byte b, byte offset, byte value) => b.SetBitValueAt(offset, 1, value);
public static Byte SetBitValueAt(this Byte b, Byte offset, Byte value) => b.SetBitValueAt(offset, 1, value);
/// <summary>
/// Splits a byte array delimited by the specified sequence of bytes.
@@ -130,32 +127,29 @@
/// or
/// sequence.
/// </exception>
public static List<byte[]> Split(this byte[] buffer, int offset, params byte[] sequence)
{
if (buffer == null)
public static List<Byte[]> Split(this Byte[] buffer, Int32 offset, params Byte[] sequence) {
if(buffer == null) {
throw new ArgumentNullException(nameof(buffer));
}
if (sequence == null)
if(sequence == null) {
throw new ArgumentNullException(nameof(sequence));
}
var seqOffset = offset.Clamp(0, buffer.Length - 1);
Int32 seqOffset = offset.Clamp(0, buffer.Length - 1);
var result = new List<byte[]>();
List<Byte[]> result = new List<Byte[]>();
while (seqOffset < buffer.Length)
{
var separatorStartIndex = buffer.GetIndexOf(sequence, seqOffset);
while(seqOffset < buffer.Length) {
Int32 separatorStartIndex = buffer.GetIndexOf(sequence, seqOffset);
if (separatorStartIndex >= 0)
{
var item = new byte[separatorStartIndex - seqOffset + sequence.Length];
if(separatorStartIndex >= 0) {
Byte[] item = new Byte[separatorStartIndex - seqOffset + sequence.Length];
Array.Copy(buffer, seqOffset, item, 0, item.Length);
result.Add(item);
seqOffset += item.Length;
}
else
{
var item = new byte[buffer.Length - seqOffset];
} else {
Byte[] item = new Byte[buffer.Length - seqOffset];
Array.Copy(buffer, seqOffset, item, 0, item.Length);
result.Add(item);
break;
@@ -170,12 +164,12 @@
/// </summary>
/// <param name="buffer">The buffer.</param>
/// <returns>A byte array containing the results of encoding the specified set of characters.</returns>
public static byte[] DeepClone(this byte[] buffer)
{
if (buffer == null)
public static Byte[] DeepClone(this Byte[] buffer) {
if(buffer == null) {
return null;
}
var result = new byte[buffer.Length];
Byte[] result = new Byte[buffer.Length];
Array.Copy(buffer, result, buffer.Length);
return result;
}
@@ -189,15 +183,16 @@
/// A new trimmed byte array.
/// </returns>
/// <exception cref="ArgumentNullException">buffer.</exception>
public static byte[] TrimStart(this byte[] buffer, params byte[] sequence)
{
if (buffer == null)
public static Byte[] TrimStart(this Byte[] buffer, params Byte[] sequence) {
if(buffer == null) {
throw new ArgumentNullException(nameof(buffer));
}
if (buffer.StartsWith(sequence) == false)
if(buffer.StartsWith(sequence) == false) {
return buffer.DeepClone();
}
var result = new byte[buffer.Length - sequence.Length];
Byte[] result = new Byte[buffer.Length - sequence.Length];
Array.Copy(buffer, sequence.Length, result, 0, result.Length);
return result;
}
@@ -211,15 +206,16 @@
/// A byte array containing the results of encoding the specified set of characters.
/// </returns>
/// <exception cref="ArgumentNullException">buffer.</exception>
public static byte[] TrimEnd(this byte[] buffer, params byte[] sequence)
{
if (buffer == null)
public static Byte[] TrimEnd(this Byte[] buffer, params Byte[] sequence) {
if(buffer == null) {
throw new ArgumentNullException(nameof(buffer));
}
if (buffer.EndsWith(sequence) == false)
if(buffer.EndsWith(sequence) == false) {
return buffer.DeepClone();
}
var result = new byte[buffer.Length - sequence.Length];
Byte[] result = new Byte[buffer.Length - sequence.Length];
Array.Copy(buffer, 0, result, 0, result.Length);
return result;
}
@@ -231,9 +227,8 @@
/// <param name="buffer">The buffer.</param>
/// <param name="sequence">The sequence.</param>
/// <returns>A byte array containing the results of encoding the specified set of characters.</returns>
public static byte[] Trim(this byte[] buffer, params byte[] sequence)
{
var trimStart = buffer.TrimStart(sequence);
public static Byte[] Trim(this Byte[] buffer, params Byte[] sequence) {
Byte[] trimStart = buffer.TrimStart(sequence);
return trimStart.TrimEnd(sequence);
}
@@ -246,12 +241,12 @@
/// True if the specified buffer is ends; otherwise, false.
/// </returns>
/// <exception cref="ArgumentNullException">buffer.</exception>
public static bool EndsWith(this byte[] buffer, params byte[] sequence)
{
if (buffer == null)
public static Boolean EndsWith(this Byte[] buffer, params Byte[] sequence) {
if(buffer == null) {
throw new ArgumentNullException(nameof(buffer));
}
var startIndex = buffer.Length - sequence.Length;
Int32 startIndex = buffer.Length - sequence.Length;
return buffer.GetIndexOf(sequence, startIndex) == startIndex;
}
@@ -261,7 +256,7 @@
/// <param name="buffer">The buffer.</param>
/// <param name="sequence">The sequence.</param>
/// <returns><c>true</c> if the specified buffer starts; otherwise, <c>false</c>.</returns>
public static bool StartsWith(this byte[] buffer, params byte[] sequence) => buffer.GetIndexOf(sequence) == 0;
public static Boolean StartsWith(this Byte[] buffer, params Byte[] sequence) => buffer.GetIndexOf(sequence) == 0;
/// <summary>
/// Determines whether the buffer contains the specified sequence.
@@ -271,7 +266,7 @@
/// <returns>
/// <c>true</c> if [contains] [the specified sequence]; otherwise, <c>false</c>.
/// </returns>
public static bool Contains(this byte[] buffer, params byte[] sequence) => buffer.GetIndexOf(sequence) >= 0;
public static Boolean Contains(this Byte[] buffer, params Byte[] sequence) => buffer.GetIndexOf(sequence) >= 0;
/// <summary>
/// Determines whether the buffer exactly matches, byte by byte the specified sequence.
@@ -282,13 +277,14 @@
/// <c>true</c> if [is equal to] [the specified sequence]; otherwise, <c>false</c>.
/// </returns>
/// <exception cref="ArgumentNullException">buffer.</exception>
public static bool IsEqualTo(this byte[] buffer, params byte[] sequence)
{
if (ReferenceEquals(buffer, sequence))
public static Boolean IsEqualTo(this Byte[] buffer, params Byte[] sequence) {
if(ReferenceEquals(buffer, sequence)) {
return true;
}
if (buffer == null)
if(buffer == null) {
throw new ArgumentNullException(nameof(buffer));
}
return buffer.Length == sequence.Length && buffer.GetIndexOf(sequence) == 0;
}
@@ -306,30 +302,37 @@
/// or
/// sequence.
/// </exception>
public static int GetIndexOf(this byte[] buffer, byte[] sequence, int offset = 0)
{
if (buffer == null)
public static Int32 GetIndexOf(this Byte[] buffer, Byte[] sequence, Int32 offset = 0) {
if(buffer == null) {
throw new ArgumentNullException(nameof(buffer));
if (sequence == null)
}
if(sequence == null) {
throw new ArgumentNullException(nameof(sequence));
if (sequence.Length == 0)
return -1;
if (sequence.Length > buffer.Length)
return -1;
}
var seqOffset = offset < 0 ? 0 : offset;
if(sequence.Length == 0) {
return -1;
}
var matchedCount = 0;
for (var i = seqOffset; i < buffer.Length; i++)
{
if (buffer[i] == sequence[matchedCount])
if(sequence.Length > buffer.Length) {
return -1;
}
Int32 seqOffset = offset < 0 ? 0 : offset;
Int32 matchedCount = 0;
for(Int32 i = seqOffset; i < buffer.Length; i++) {
if(buffer[i] == sequence[matchedCount]) {
matchedCount++;
else
} else {
matchedCount = 0;
}
if (matchedCount == sequence.Length)
if(matchedCount == sequence.Length) {
return i - (matchedCount - 1);
}
}
return -1;
}
@@ -347,13 +350,14 @@
/// or
/// buffer.
/// </exception>
public static MemoryStream Append(this MemoryStream stream, byte[] buffer)
{
if (stream == null)
public static MemoryStream Append(this MemoryStream stream, Byte[] buffer) {
if(stream == null) {
throw new ArgumentNullException(nameof(stream));
}
if (buffer == null)
if(buffer == null) {
throw new ArgumentNullException(nameof(buffer));
}
stream.Write(buffer, 0, buffer.Length);
return stream;
@@ -368,7 +372,7 @@
/// Block of bytes to the current stream using data read from a buffer.
/// </returns>
/// <exception cref="ArgumentNullException">buffer.</exception>
public static MemoryStream Append(this MemoryStream stream, IEnumerable<byte> buffer) => Append(stream, buffer?.ToArray());
public static MemoryStream Append(this MemoryStream stream, IEnumerable<Byte> buffer) => Append(stream, buffer?.ToArray());
/// <summary>
/// Appends the Memory Stream with the specified set of buffers.
@@ -379,13 +383,14 @@
/// Block of bytes to the current stream using data read from a buffer.
/// </returns>
/// <exception cref="ArgumentNullException">buffers.</exception>
public static MemoryStream Append(this MemoryStream stream, IEnumerable<byte[]> buffers)
{
if (buffers == null)
public static MemoryStream Append(this MemoryStream stream, IEnumerable<Byte[]> buffers) {
if(buffers == null) {
throw new ArgumentNullException(nameof(buffers));
}
foreach (var buffer in buffers)
Append(stream, buffer);
foreach(Byte[] buffer in buffers) {
_ = Append(stream, buffer);
}
return stream;
}
@@ -396,14 +401,14 @@
/// <param name="buffer">The buffer.</param>
/// <param name="encoding">The encoding.</param>
/// <returns>A <see cref="System.String" /> that contains the results of decoding the specified sequence of bytes.</returns>
public static string ToText(this IEnumerable<byte> buffer, Encoding encoding) => encoding.GetString(buffer.ToArray());
public static String ToText(this IEnumerable<Byte> buffer, Encoding encoding) => encoding.GetString(buffer.ToArray());
/// <summary>
/// Converts an array of bytes into text with UTF8 encoding.
/// </summary>
/// <param name="buffer">The buffer.</param>
/// <returns>A <see cref="System.String" /> that contains the results of decoding the specified sequence of bytes.</returns>
public static string ToText(this IEnumerable<byte> buffer) => buffer.ToText(Encoding.UTF8);
public static String ToText(this IEnumerable<Byte> buffer) => buffer.ToText(Encoding.UTF8);
/// <summary>
/// Retrieves a sub-array from the specified <paramref name="array"/>. A sub-array starts at
@@ -417,28 +422,30 @@
/// An array of T from which to retrieve a sub-array.
/// </param>
/// <param name="startIndex">
/// An <see cref="int"/> that represents the zero-based starting position of
/// An <see cref="Int32"/> that represents the zero-based starting position of
/// a sub-array in <paramref name="array"/>.
/// </param>
/// <param name="length">
/// An <see cref="int"/> that represents the number of elements to retrieve.
/// An <see cref="Int32"/> that represents the number of elements to retrieve.
/// </param>
/// <typeparam name="T">
/// The type of elements in <paramref name="array"/>.
/// </typeparam>
public static T[] SubArray<T>(this T[] array, int startIndex, int length)
{
int len;
if (array == null || (len = array.Length) == 0)
public static T[] SubArray<T>(this T[] array, Int32 startIndex, Int32 length) {
Int32 len;
if(array == null || (len = array.Length) == 0) {
return new T[0];
}
if (startIndex < 0 || length <= 0 || startIndex + length > len)
if(startIndex < 0 || length <= 0 || startIndex + length > len) {
return new T[0];
}
if (startIndex == 0 && length == len)
if(startIndex == 0 && length == len) {
return array;
}
var subArray = new T[length];
T[] subArray = new T[length];
Array.Copy(array, startIndex, subArray, 0, length);
return subArray;
@@ -456,16 +463,16 @@
/// An array of T from which to retrieve a sub-array.
/// </param>
/// <param name="startIndex">
/// A <see cref="long"/> that represents the zero-based starting position of
/// A <see cref="Int64"/> that represents the zero-based starting position of
/// a sub-array in <paramref name="array"/>.
/// </param>
/// <param name="length">
/// A <see cref="long"/> that represents the number of elements to retrieve.
/// A <see cref="Int64"/> that represents the number of elements to retrieve.
/// </param>
/// <typeparam name="T">
/// The type of elements in <paramref name="array"/>.
/// </typeparam>
public static T[] SubArray<T>(this T[] array, long startIndex, long length) => array.SubArray((int)startIndex, (int)length);
public static T[] SubArray<T>(this T[] array, Int64 startIndex, Int64 length) => array.SubArray((Int32)startIndex, (Int32)length);
/// <summary>
/// Reads the bytes asynchronous.
@@ -478,31 +485,28 @@
/// A byte array containing the results of encoding the specified set of characters.
/// </returns>
/// <exception cref="ArgumentNullException">stream.</exception>
public static async Task<byte[]> ReadBytesAsync(this Stream stream, long length, int bufferLength, CancellationToken ct = default)
{
if (stream == null)
public static async Task<Byte[]> ReadBytesAsync(this Stream stream, Int64 length, Int32 bufferLength, CancellationToken ct = default) {
if(stream == null) {
throw new ArgumentNullException(nameof(stream));
}
using (var dest = new MemoryStream())
{
try
{
var buff = new byte[bufferLength];
while (length > 0)
{
if (length < bufferLength)
bufferLength = (int)length;
using(MemoryStream dest = new MemoryStream()) {
try {
Byte[] buff = new Byte[bufferLength];
while(length > 0) {
if(length < bufferLength) {
bufferLength = (Int32)length;
}
var nread = await stream.ReadAsync(buff, 0, bufferLength, ct).ConfigureAwait(false);
if (nread == 0)
Int32 nread = await stream.ReadAsync(buff, 0, bufferLength, ct).ConfigureAwait(false);
if(nread == 0) {
break;
}
dest.Write(buff, 0, nread);
length -= nread;
}
}
catch
{
} catch {
// ignored
}
@@ -520,27 +524,24 @@
/// A byte array containing the results of encoding the specified set of characters.
/// </returns>
/// <exception cref="ArgumentNullException">stream.</exception>
public static async Task<byte[]> ReadBytesAsync(this Stream stream, int length, CancellationToken ct = default)
{
if (stream == null)
public static async Task<Byte[]> ReadBytesAsync(this Stream stream, Int32 length, CancellationToken ct = default) {
if(stream == null) {
throw new ArgumentNullException(nameof(stream));
}
var buff = new byte[length];
var offset = 0;
try
{
while (length > 0)
{
var nread = await stream.ReadAsync(buff, offset, length, ct).ConfigureAwait(false);
if (nread == 0)
Byte[] buff = new Byte[length];
Int32 offset = 0;
try {
while(length > 0) {
Int32 nread = await stream.ReadAsync(buff, offset, length, ct).ConfigureAwait(false);
if(nread == 0) {
break;
}
offset += nread;
length -= nread;
}
}
catch
{
} catch {
// ignored
}
@@ -555,14 +556,15 @@
/// The byte array from conversion.
/// </returns>
/// <exception cref="ArgumentNullException">sbyteArray.</exception>
public static byte[] ToByteArray(this sbyte[] sbyteArray)
{
if (sbyteArray == null)
public static Byte[] ToByteArray(this SByte[] sbyteArray) {
if(sbyteArray == null) {
throw new ArgumentNullException(nameof(sbyteArray));
}
var byteArray = new byte[sbyteArray.Length];
for (var index = 0; index < sbyteArray.Length; index++)
byteArray[index] = (byte)sbyteArray[index];
Byte[] byteArray = new Byte[sbyteArray.Length];
for(Int32 index = 0; index < sbyteArray.Length; index++) {
byteArray[index] = (Byte)sbyteArray[index];
}
return byteArray;
}
@@ -575,14 +577,16 @@
/// The sbyte array from conversion.
/// </returns>
/// <exception cref="ArgumentNullException">byteArray.</exception>
public static sbyte[] ToSByteArray(this byte[] byteArray)
{
if (byteArray == null)
public static SByte[] ToSByteArray(this Byte[] byteArray) {
if(byteArray == null) {
throw new ArgumentNullException(nameof(byteArray));
}
SByte[] sbyteArray = new SByte[byteArray.Length];
for(Int32 index = 0; index < byteArray.Length; index++) {
sbyteArray[index] = (SByte)byteArray[index];
}
var sbyteArray = new sbyte[byteArray.Length];
for (var index = 0; index < byteArray.Length; index++)
sbyteArray[index] = (sbyte)byteArray[index];
return sbyteArray;
}
@@ -592,7 +596,7 @@
/// <param name="encoding">The encoding.</param>
/// <param name="s">The s.</param>
/// <returns>The sbyte array from string.</returns>
public static sbyte[] GetSBytes(this Encoding encoding, string s)
public static SByte[] GetSBytes(this Encoding encoding, String s)
=> encoding.GetBytes(s).ToSByteArray();
/// <summary>
@@ -601,7 +605,7 @@
/// <param name="encoding">The encoding.</param>
/// <param name="data">The data.</param>
/// <returns>The string.</returns>
public static string GetString(this Encoding encoding, sbyte[] data)
public static String GetString(this Encoding encoding, SByte[] data)
=> encoding.GetString(data.ToByteArray());
/// <summary>
@@ -620,53 +624,59 @@
/// or
/// target.
/// </exception>
public static int ReadInput(this Stream sourceStream, ref sbyte[] target, int start, int count)
{
if (sourceStream == null)
public static Int32 ReadInput(this Stream sourceStream, ref SByte[] target, Int32 start, Int32 count) {
if(sourceStream == null) {
throw new ArgumentNullException(nameof(sourceStream));
}
if (target == null)
if(target == null) {
throw new ArgumentNullException(nameof(target));
}
// Returns 0 bytes if not enough space in target
if (target.Length == 0)
if(target.Length == 0) {
return 0;
}
var receiver = new byte[target.Length];
var bytesRead = 0;
var startIndex = start;
var bytesToRead = count;
while (bytesToRead > 0)
{
var n = sourceStream.Read(receiver, startIndex, bytesToRead);
if (n == 0)
Byte[] receiver = new Byte[target.Length];
Int32 bytesRead = 0;
Int32 startIndex = start;
Int32 bytesToRead = count;
while(bytesToRead > 0) {
Int32 n = sourceStream.Read(receiver, startIndex, bytesToRead);
if(n == 0) {
break;
}
bytesRead += n;
startIndex += n;
bytesToRead -= n;
}
// Returns -1 if EOF
if (bytesRead == 0)
if(bytesRead == 0) {
return -1;
}
for (var i = start; i < start + bytesRead; i++)
target[i] = (sbyte)receiver[i];
for(Int32 i = start; i < start + bytesRead; i++) {
target[i] = (SByte)receiver[i];
}
return bytesRead;
}
private static string ToHex(byte[] bytes, bool addPrefix, string format)
{
if (bytes == null)
private static String ToHex(Byte[] bytes, Boolean addPrefix, String format) {
if(bytes == null) {
throw new ArgumentNullException(nameof(bytes));
}
var sb = new StringBuilder(bytes.Length * 2);
StringBuilder sb = new StringBuilder(bytes.Length * 2);
foreach (var item in bytes)
sb.Append(item.ToString(format, CultureInfo.InvariantCulture));
foreach(Byte item in bytes) {
_ = sb.Append(item.ToString(format, CultureInfo.InvariantCulture));
}
return $"{(addPrefix ? "0x" : string.Empty)}{sb}";
return $"{(addPrefix ? "0x" : String.Empty)}{sb}";
}
}
}
+72 -71
View File
@@ -1,15 +1,13 @@
namespace Unosquare.Swan
{
using System;
using System.Collections.Generic;
using System.Linq;
using System;
using System.Collections.Generic;
using System.Linq;
namespace Unosquare.Swan {
/// <summary>
/// Provides various extension methods for dates.
/// </summary>
public static class DateExtensions
{
private static readonly Dictionary<string, int> DateRanges = new Dictionary<string, int>()
public static class DateExtensions {
private static readonly Dictionary<String, Int32> DateRanges = new Dictionary<String, Int32>()
{
{ "minute", 59},
{ "hour", 23},
@@ -23,7 +21,7 @@
/// </summary>
/// <param name="date">The date.</param>
/// <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}";
/// <summary>
@@ -31,7 +29,7 @@
/// </summary>
/// <param name="date">The date.</param>
/// <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}";
/// <summary>
@@ -49,43 +47,44 @@
/// <exception cref="ArgumentException">
/// Unable to parse sortable date and time. - sortableDate.
/// </exception>
public static DateTime ToDateTime(this string sortableDate)
{
if (string.IsNullOrWhiteSpace(sortableDate))
public static DateTime ToDateTime(this String sortableDate) {
if(String.IsNullOrWhiteSpace(sortableDate)) {
throw new ArgumentNullException(nameof(sortableDate));
}
var hour = 0;
var minute = 0;
var second = 0;
Int32 hour = 0;
Int32 minute = 0;
Int32 second = 0;
var dateTimeParts = sortableDate.Split(' ');
String[] dateTimeParts = sortableDate.Split(' ');
try
{
if (dateTimeParts.Length != 1 && dateTimeParts.Length != 2)
try {
if(dateTimeParts.Length != 1 && dateTimeParts.Length != 2) {
throw new Exception();
}
var dateParts = dateTimeParts[0].Split('-');
if (dateParts.Length != 3) throw new Exception();
String[] dateParts = dateTimeParts[0].Split('-');
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]);
Int32 day = Int32.Parse(dateParts[2]);
if (dateTimeParts.Length > 1)
{
var timeParts = dateTimeParts[1].Split(':');
if (timeParts.Length != 3) throw new Exception();
if(dateTimeParts.Length > 1) {
String[] timeParts = dateTimeParts[1].Split(':');
if(timeParts.Length != 3) {
throw new Exception();
}
hour = int.Parse(timeParts[0]);
minute = int.Parse(timeParts[1]);
second = int.Parse(timeParts[2]);
hour = Int32.Parse(timeParts[0]);
minute = Int32.Parse(timeParts[1]);
second = Int32.Parse(timeParts[2]);
}
return new DateTime(year, month, day, hour, minute, second);
}
catch (Exception)
{
} catch(Exception) {
throw new ArgumentException("Unable to parse sortable date and time.", nameof(sortableDate));
}
}
@@ -110,19 +109,18 @@
/// A new instance of the DateTime structure to the specified datetime and timespan ticks.
/// </returns>
public static DateTime RoundUp(this DateTime date, TimeSpan timeSpan)
=> new DateTime(((date.Ticks + timeSpan.Ticks - 1) / timeSpan.Ticks) * timeSpan.Ticks);
=> new DateTime((date.Ticks + timeSpan.Ticks - 1) / timeSpan.Ticks * timeSpan.Ticks);
/// <summary>
/// Get this datetime as a Unix epoch timestamp (seconds since Jan 1, 1970, midnight UTC).
/// </summary>
/// <param name="date">The date to convert.</param>
/// <returns>Seconds since Unix epoch.</returns>
public static long ToUnixEpochDate(this DateTime date)
{
public static Int64 ToUnixEpochDate(this DateTime date) {
#if NETSTANDARD2_0
return new DateTimeOffset(date).ToUniversalTime().ToUnixTimeSeconds();
#else
var epochTicks = new DateTime(1970, 1, 1).Ticks;
Int64 epochTicks = new DateTime(1970, 1, 1).Ticks;
return (date.Ticks - epochTicks) / TimeSpan.TicksPerSecond;
#endif
@@ -147,15 +145,14 @@
/// <param name="month">The month. (1-12).</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>
public static bool AsCronCanRun(this DateTime date, int? minute = null, int? hour = null, int? dayOfMonth = null, int? month = null, int? dayOfWeek = null)
{
var results = new List<bool?>
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?>
{
GetElementParts(minute, date.Minute),
GetElementParts(hour, date.Hour),
GetElementParts(dayOfMonth, date.Day),
GetElementParts(month, date.Month),
GetElementParts(dayOfWeek, (int) date.DayOfWeek),
GetElementParts(dayOfWeek, (Int32) date.DayOfWeek),
};
return results.Any(x => x != false);
@@ -171,60 +168,64 @@
/// <param name="month">The month. (1-12).</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>
public static bool AsCronCanRun(this DateTime date, string minute = "*", string hour = "*", string dayOfMonth = "*", string month = "*", string dayOfWeek = "*")
{
var results = new List<bool?>
public static Boolean AsCronCanRun(this DateTime date, String minute = "*", String hour = "*", String dayOfMonth = "*", String month = "*", String dayOfWeek = "*") {
List<Boolean?> results = new List<Boolean?>
{
GetElementParts(minute, nameof(minute), date.Minute),
GetElementParts(hour, nameof(hour), date.Hour),
GetElementParts(dayOfMonth, nameof(dayOfMonth), date.Day),
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);
}
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)
{
if (string.IsNullOrWhiteSpace(parts) || parts == "*") return null;
if (parts.Contains(","))
{
return parts.Split(',').Select(int.Parse).Contains(value);
private static Boolean? GetElementParts(String parts, String type, Int32 value) {
if(String.IsNullOrWhiteSpace(parts) || parts == "*") {
return null;
}
var stop = DateRanges[type];
if(parts.Contains(",")) {
return parts.Split(',').Select(Int32.Parse).Contains(value);
}
if (parts.Contains("/"))
{
var multiple = int.Parse(parts.Split('/').Last());
var start = type == "dayOfMonth" || type == "month" ? 1 : 0;
Int32 stop = DateRanges[type];
for (var i = start; i <= stop; i += multiple)
if (i == value) return true;
if(parts.Contains("/")) {
Int32 multiple = Int32.Parse(parts.Split('/').Last());
Int32 start = type == "dayOfMonth" || type == "month" ? 1 : 0;
for(Int32 i = start; i <= stop; i += multiple) {
if(i == value) {
return true;
}
}
return false;
}
if (parts.Contains("-"))
{
var range = parts.Split('-');
var start = int.Parse(range.First());
stop = Math.Max(stop, int.Parse(range.Last()));
if(parts.Contains("-")) {
String[] range = parts.Split('-');
Int32 start = Int32.Parse(range.First());
stop = Math.Max(stop, Int32.Parse(range.Last()));
if ((type == "dayOfMonth" || type == "month") && start == 0)
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++) {
if(i == value) {
return true;
}
}
return false;
}
return int.Parse(parts) == value;
return Int32.Parse(parts) == value;
}
}
}
+20 -21
View File
@@ -1,13 +1,11 @@
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
{
public static partial class Extensions {
/// <summary>
/// Gets the value if exists or default.
/// </summary>
@@ -20,10 +18,10 @@
/// The value of the provided key or default.
/// </returns>
/// <exception cref="ArgumentNullException">dict.</exception>
public static TValue GetValueOrDefault<TKey, TValue>(this IDictionary<TKey, TValue> dict, TKey key, TValue defaultValue = default)
{
if (dict == null)
public static TValue GetValueOrDefault<TKey, TValue>(this IDictionary<TKey, TValue> dict, TKey key, TValue defaultValue = default) {
if(dict == null) {
throw new ArgumentNullException(nameof(dict));
}
return dict.ContainsKey(key) ? dict[key] : defaultValue;
}
@@ -40,15 +38,17 @@
/// <param name="key">The key.</param>
/// <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 == null)
public static TValue GetOrAdd<TKey, TValue>(this IDictionary<TKey, TValue> dict, TKey key, Func<TKey, TValue> valueFactory) {
if(dict == null) {
throw new ArgumentNullException(nameof(dict));
}
if(!dict.ContainsKey(key)) {
TValue value = valueFactory(key);
if(Equals(value, default)) {
return default;
}
if (!dict.ContainsKey(key))
{
var value = valueFactory(key);
if (Equals(value, default)) return default;
dict[key] = value;
}
@@ -63,13 +63,12 @@
/// <param name="dict">The dictionary.</param>
/// <param name="itemAction">The item action.</param>
/// <exception cref="ArgumentNullException">dict.</exception>
public static void ForEach<TKey, TValue>(this IDictionary<TKey, TValue> dict, Action<TKey, TValue> itemAction)
{
if (dict == null)
public static void ForEach<TKey, TValue>(this IDictionary<TKey, TValue> dict, Action<TKey, TValue> itemAction) {
if(dict == null) {
throw new ArgumentNullException(nameof(dict));
}
foreach (var kvp in dict)
{
foreach(KeyValuePair<TKey, TValue> kvp in dict) {
itemAction(kvp.Key, kvp.Value);
}
}
+47 -38
View File
@@ -1,14 +1,12 @@
namespace Unosquare.Swan
{
using System;
using System.Collections.Generic;
using System.Linq;
using System;
using System.Collections.Generic;
using System.Linq;
namespace Unosquare.Swan {
/// <summary>
/// Functional programming extension methods.
/// </summary>
public static class FunctionalExtensions
{
public static class FunctionalExtensions {
/// <summary>
/// Whens the specified condition.
/// </summary>
@@ -28,17 +26,19 @@
/// </exception>
public static IQueryable<T> When<T>(
this IQueryable<T> list,
Func<bool> condition,
Func<IQueryable<T>, IQueryable<T>> fn)
{
if (list == null)
Func<Boolean> condition,
Func<IQueryable<T>, IQueryable<T>> fn) {
if(list == null) {
throw new ArgumentNullException(nameof(list));
}
if (condition == null)
if(condition == null) {
throw new ArgumentNullException(nameof(condition));
}
if (fn == null)
if(fn == null) {
throw new ArgumentNullException(nameof(fn));
}
return condition() ? fn(list) : list;
}
@@ -62,17 +62,19 @@
/// </exception>
public static IEnumerable<T> When<T>(
this IEnumerable<T> list,
Func<bool> condition,
Func<IEnumerable<T>, IEnumerable<T>> fn)
{
if (list == null)
Func<Boolean> condition,
Func<IEnumerable<T>, IEnumerable<T>> fn) {
if(list == null) {
throw new ArgumentNullException(nameof(list));
}
if (condition == null)
if(condition == null) {
throw new ArgumentNullException(nameof(condition));
}
if (fn == null)
if(fn == null) {
throw new ArgumentNullException(nameof(fn));
}
return condition() ? fn(list) : list;
}
@@ -96,20 +98,23 @@
/// </exception>
public static IList<T> AddWhen<T>(
this IList<T> list,
Func<bool> condition,
Func<T> value)
{
if (list == null)
Func<Boolean> condition,
Func<T> value) {
if(list == null) {
throw new ArgumentNullException(nameof(list));
}
if (condition == null)
if(condition == null) {
throw new ArgumentNullException(nameof(condition));
}
if (value == null)
if(value == null) {
throw new ArgumentNullException(nameof(value));
}
if (condition())
if(condition()) {
list.Add(value());
}
return list;
}
@@ -127,14 +132,15 @@
/// <exception cref="ArgumentNullException">list.</exception>
public static IList<T> AddWhen<T>(
this IList<T> list,
bool condition,
T value)
{
if (list == null)
Boolean condition,
T value) {
if(list == null) {
throw new ArgumentNullException(nameof(list));
}
if (condition)
if(condition) {
list.Add(value);
}
return list;
}
@@ -158,20 +164,23 @@
/// </exception>
public static List<T> AddRangeWhen<T>(
this List<T> list,
Func<bool> condition,
Func<IEnumerable<T>> value)
{
if (list == null)
Func<Boolean> condition,
Func<IEnumerable<T>> value) {
if(list == null) {
throw new ArgumentNullException(nameof(list));
}
if (condition == null)
if(condition == null) {
throw new ArgumentNullException(nameof(condition));
}
if (value == null)
if(value == null) {
throw new ArgumentNullException(nameof(value));
}
if (condition())
if(condition()) {
list.AddRange(value());
}
return list;
}
+100 -122
View File
@@ -1,23 +1,21 @@
namespace Unosquare.Swan
{
using System;
using System.Collections.Concurrent;
using System.Collections;
using System.Linq;
using System.Reflection;
using System.Collections.Generic;
using Attributes;
using System;
using System.Collections.Concurrent;
using System.Collections;
using System.Linq;
using System.Reflection;
using System.Collections.Generic;
using Unosquare.Swan.Attributes;
namespace Unosquare.Swan {
/// <summary>
/// Provides various extension methods for Reflection and Types.
/// </summary>
public static class ReflectionExtensions
{
private static readonly Lazy<ConcurrentDictionary<Tuple<bool, PropertyInfo>, Func<object, object>>> CacheGetMethods =
new Lazy<ConcurrentDictionary<Tuple<bool, PropertyInfo>, Func<object, object>>>(() => new ConcurrentDictionary<Tuple<bool, PropertyInfo>, Func<object, object>>(), true);
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<bool, PropertyInfo>, Action<object, object[]>>> CacheSetMethods =
new Lazy<ConcurrentDictionary<Tuple<bool, PropertyInfo>, Action<object, object[]>>>(() => new ConcurrentDictionary<Tuple<bool, PropertyInfo>, Action<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
@@ -29,17 +27,14 @@
/// Array of Type objects representing the types specified by an assembly.
/// </returns>
/// <exception cref="ArgumentNullException">assembly.</exception>
public static IEnumerable<Type> GetAllTypes(this Assembly assembly)
{
if (assembly == null)
public static IEnumerable<Type> GetAllTypes(this Assembly assembly) {
if(assembly == null) {
throw new ArgumentNullException(nameof(assembly));
try
{
return assembly.GetTypes();
}
catch (ReflectionTypeLoadException e)
{
try {
return assembly.GetTypes();
} catch(ReflectionTypeLoadException e) {
return e.Types.Where(t => t != null);
}
}
@@ -56,10 +51,10 @@
/// Default value of this type.
/// </returns>
/// <exception cref="ArgumentNullException">type.</exception>
public static object GetDefault(this Type type)
{
if (type == null)
public static Object GetDefault(this Type type) {
if(type == null) {
throw new ArgumentNullException(nameof(type));
}
return type.IsValueType() ? Activator.CreateInstance(type) : null;
}
@@ -72,12 +67,12 @@
/// <c>true</c> if the specified source type is collection; otherwise, <c>false</c>.
/// </returns>
/// <exception cref="ArgumentNullException">sourceType.</exception>
public static bool IsCollection(this Type sourceType)
{
if (sourceType == null)
public static Boolean IsCollection(this Type sourceType) {
if(sourceType == null) {
throw new ArgumentNullException(nameof(sourceType));
}
return sourceType != typeof(string) &&
return sourceType != typeof(String) &&
typeof(IEnumerable).IsAssignableFrom(sourceType);
}
@@ -99,25 +94,28 @@
public static MethodInfo GetMethod(
this Type type,
BindingFlags bindingFlags,
string methodName,
String methodName,
Type[] genericTypes,
Type[] parameterTypes)
{
if (type == null)
Type[] parameterTypes) {
if(type == null) {
throw new ArgumentNullException(nameof(type));
}
if (methodName == null)
if(methodName == null) {
throw new ArgumentNullException(nameof(methodName));
}
if (genericTypes == null)
if(genericTypes == null) {
throw new ArgumentNullException(nameof(genericTypes));
}
if (parameterTypes == null)
if(parameterTypes == null) {
throw new ArgumentNullException(nameof(parameterTypes));
}
var methods = type
List<MethodInfo> methods = type
.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.GetGenericArguments().Length == genericTypes.Length)
.Where(mi => mi.GetParameters().Length == parameterTypes.Length)
@@ -135,7 +133,7 @@
/// <returns>
/// <c>true</c> if the specified type is class; otherwise, <c>false</c>.
/// </returns>
public static bool IsClass(this Type type) => type.GetTypeInfo().IsClass;
public static Boolean IsClass(this Type type) => type.GetTypeInfo().IsClass;
/// <summary>
/// Determines whether this instance is abstract.
@@ -144,7 +142,7 @@
/// <returns>
/// <c>true</c> if the specified type is abstract; otherwise, <c>false</c>.
/// </returns>
public static bool IsAbstract(this Type type) => type.GetTypeInfo().IsAbstract;
public static Boolean IsAbstract(this Type type) => type.GetTypeInfo().IsAbstract;
/// <summary>
/// Determines whether this instance is interface.
@@ -153,7 +151,7 @@
/// <returns>
/// <c>true</c> if the specified type is interface; otherwise, <c>false</c>.
/// </returns>
public static bool IsInterface(this Type type) => type.GetTypeInfo().IsInterface;
public static Boolean IsInterface(this Type type) => type.GetTypeInfo().IsInterface;
/// <summary>
/// Determines whether this instance is primitive.
@@ -162,7 +160,7 @@
/// <returns>
/// <c>true</c> if the specified type is primitive; otherwise, <c>false</c>.
/// </returns>
public static bool IsPrimitive(this Type type) => type.GetTypeInfo().IsPrimitive;
public static Boolean IsPrimitive(this Type type) => type.GetTypeInfo().IsPrimitive;
/// <summary>
/// Determines whether [is value type].
@@ -171,7 +169,7 @@
/// <returns>
/// <c>true</c> if [is value type] [the specified type]; otherwise, <c>false</c>.
/// </returns>
public static bool IsValueType(this Type type) => type.GetTypeInfo().IsValueType;
public static Boolean IsValueType(this Type type) => type.GetTypeInfo().IsValueType;
/// <summary>
/// Determines whether [is generic type].
@@ -180,7 +178,7 @@
/// <returns>
/// <c>true</c> if [is generic type] [the specified type]; otherwise, <c>false</c>.
/// </returns>
public static bool IsGenericType(this Type type) => type.GetTypeInfo().IsGenericType;
public static Boolean IsGenericType(this Type type) => type.GetTypeInfo().IsGenericType;
/// <summary>
/// Determines whether [is generic parameter].
@@ -189,7 +187,7 @@
/// <returns>
/// <c>true</c> if [is generic parameter] [the specified type]; otherwise, <c>false</c>.
/// </returns>
public static bool IsGenericParameter(this Type type) => type.IsGenericParameter;
public static Boolean IsGenericParameter(this Type type) => type.IsGenericParameter;
/// <summary>
/// Determines whether the specified attribute type is defined.
@@ -200,7 +198,7 @@
/// <returns>
/// <c>true</c> if the specified attribute type is defined; otherwise, <c>false</c>.
/// </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);
/// <summary>
@@ -212,7 +210,7 @@
/// <returns>
/// Attributes associated with the property represented by this PropertyInfo object.
/// </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();
/// <summary>
@@ -222,7 +220,7 @@
/// <returns>
/// <c>true</c> if [is generic type definition] [the specified type]; otherwise, <c>false</c>.
/// </returns>
public static bool IsGenericTypeDefinition(this Type type) => type.GetTypeInfo().IsGenericTypeDefinition;
public static Boolean IsGenericTypeDefinition(this Type type) => type.GetTypeInfo().IsGenericTypeDefinition;
/// <summary>
/// Bases the type.
@@ -246,7 +244,7 @@
/// <c>true</c> if [is i enumerable request] [the specified type]; otherwise, <c>false</c>.
/// </returns>
/// <exception cref="ArgumentNullException">type.</exception>
public static bool IsIEnumerable(this Type type)
public static Boolean IsIEnumerable(this Type type)
=> type == null
? throw new ArgumentNullException(nameof(type))
: type.IsGenericType() && type.GetGenericTypeDefinition() == typeof(IEnumerable<>);
@@ -262,7 +260,7 @@
/// <returns>
/// <c>true</c> if parsing was successful; otherwise, <c>false</c>.
/// </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);
/// <summary>
@@ -274,8 +272,7 @@
/// <returns>
/// <c>true</c> if parsing was successful; otherwise, <c>false</c>.
/// </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;
return Definitions.BasicTypesInfo.ContainsKey(type) && Definitions.BasicTypesInfo[type].TryParse(value, out result);
@@ -290,18 +287,13 @@
/// <returns>
/// <c>true</c> if parsing was successful; otherwise, <c>false</c>.
/// </returns>
public static bool TrySetBasicType(this PropertyInfo property, object value, object obj)
{
try
{
if (property.PropertyType.TryParseBasicType(value, out var propertyValue))
{
public static Boolean TrySetBasicType(this PropertyInfo property, Object value, Object obj) {
try {
if(property.PropertyType.TryParseBasicType(value, out Object propertyValue)) {
property.SetValue(obj, propertyValue);
return true;
}
}
catch
{
} catch {
// swallow
}
@@ -318,30 +310,23 @@
/// <returns>
/// <c>true</c> if parsing was successful; otherwise, <c>false</c>.
/// </returns>
public static bool TrySetArrayBasicType(this Type type, object value, Array array, int index)
{
try
{
if (value == null)
{
public static Boolean TrySetArrayBasicType(this Type type, Object value, Array array, Int32 index) {
try {
if(value == null) {
array.SetValue(null, index);
return true;
}
if (type.TryParseBasicType(value, out var propertyValue))
{
if(type.TryParseBasicType(value, out Object propertyValue)) {
array.SetValue(propertyValue, index);
return true;
}
if (type.GetTypeInfo().IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>))
{
if(type.GetTypeInfo().IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>)) {
array.SetValue(null, index);
return true;
}
}
catch
{
} catch {
// swallow
}
@@ -357,22 +342,23 @@
/// <returns>
/// <c>true</c> if parsing was successful; otherwise, <c>false</c>.
/// </returns>
public static bool TrySetArray(this PropertyInfo propertyInfo, IEnumerable<object> value, object obj)
{
var elementType = propertyInfo.PropertyType.GetElementType();
public static Boolean TrySetArray(this PropertyInfo propertyInfo, IEnumerable<Object> value, Object obj) {
Type elementType = propertyInfo.PropertyType.GetElementType();
if (elementType == null)
if(elementType == null) {
return false;
}
var targetArray = Array.CreateInstance(elementType, value.Count());
Array targetArray = Array.CreateInstance(elementType, value.Count());
var i = 0;
Int32 i = 0;
foreach (var sourceElement in value)
{
var result = elementType.TrySetArrayBasicType(sourceElement, targetArray, i++);
foreach(Object sourceElement in value) {
Boolean result = elementType.TrySetArrayBasicType(sourceElement, targetArray, i++);
if (!result) return false;
if(!result) {
return false;
}
}
propertyInfo.SetValue(obj, targetArray);
@@ -391,23 +377,21 @@
/// <param name="propertyInfo">The property information.</param>
/// <param name="obj">The object.</param>
/// <returns>The property value or null.</returns>
public static string ToFormattedString(this PropertyInfo propertyInfo, object obj)
{
try
{
var value = propertyInfo.GetValue(obj);
var attr = Runtime.AttributeCache.RetrieveOne<PropertyDisplayAttribute>(propertyInfo);
public static String ToFormattedString(this PropertyInfo propertyInfo, Object obj) {
try {
Object value = propertyInfo.GetValue(obj);
PropertyDisplayAttribute attr = Runtime.AttributeCache.RetrieveOne<PropertyDisplayAttribute>(propertyInfo);
if (attr == null) return value?.ToString() ?? string.Empty;
var valueToFormat = value ?? attr.DefaultValue;
return string.IsNullOrEmpty(attr.Format)
? (valueToFormat?.ToString() ?? string.Empty)
: ConvertObjectAndFormat(propertyInfo.PropertyType, valueToFormat, attr.Format);
if(attr == null) {
return value?.ToString() ?? String.Empty;
}
catch
{
Object valueToFormat = value ?? attr.DefaultValue;
return String.IsNullOrEmpty(attr.Format)
? (valueToFormat?.ToString() ?? String.Empty)
: ConvertObjectAndFormat(propertyInfo.PropertyType, valueToFormat, attr.Format);
} catch {
return null;
}
}
@@ -420,9 +404,8 @@
/// <returns>
/// The cached MethodInfo.
/// </returns>
public static Func<object, object> GetCacheGetMethod(this PropertyInfo propertyInfo, bool nonPublic = false)
{
var key = Tuple.Create(!nonPublic, propertyInfo);
public static Func<Object, Object> GetCacheGetMethod(this PropertyInfo propertyInfo, Boolean nonPublic = false) {
Tuple<Boolean, PropertyInfo> key = Tuple.Create(!nonPublic, propertyInfo);
return !nonPublic && !CacheGetMethods.Value.ContainsKey(key) && !propertyInfo.GetGetMethod(true).IsPublic
? null
@@ -439,9 +422,8 @@
/// <returns>
/// The cached MethodInfo.
/// </returns>
public static Action<object, object[]> GetCacheSetMethod(this PropertyInfo propertyInfo, bool nonPublic = false)
{
var key = Tuple.Create(!nonPublic, propertyInfo);
public static Action<Object, Object[]> GetCacheSetMethod(this PropertyInfo propertyInfo, Boolean nonPublic = false) {
Tuple<Boolean, PropertyInfo> key = Tuple.Create(!nonPublic, propertyInfo);
return !nonPublic && !CacheSetMethods.Value.ContainsKey(key) && !propertyInfo.GetSetMethod(true).IsPublic
? null
@@ -450,20 +432,16 @@
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;
}
private static String ConvertObjectAndFormat(Type propertyType, Object value, String format) => propertyType == typeof(DateTime) || propertyType == typeof(DateTime?)
? Convert.ToDateTime(value).ToString(format)
: propertyType == typeof(Int32) || propertyType == typeof(Int32?)
? Convert.ToInt32(value).ToString(format)
: propertyType == typeof(Decimal) || propertyType == typeof(Decimal?)
? Convert.ToDecimal(value).ToString(format)
: propertyType == typeof(Double) || propertyType == typeof(Double?)
? Convert.ToDouble(value).ToString(format)
: propertyType == typeof(Byte) || propertyType == typeof(Byte?)
? Convert.ToByte(value).ToString(format)
: value?.ToString() ?? String.Empty;
}
}
+138 -157
View File
@@ -1,24 +1,22 @@
namespace Unosquare.Swan
{
using Formatters;
using System;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Text.RegularExpressions;
using Unosquare.Swan.Formatters;
using System;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Text.RegularExpressions;
namespace Unosquare.Swan {
/// <summary>
/// String related extension methods.
/// </summary>
public static class StringExtensions
{
public static class StringExtensions {
#region Private Declarations
private const RegexOptions StandardRegexOptions =
RegexOptions.Multiline | RegexOptions.Compiled | RegexOptions.CultureInvariant;
private static readonly string[] ByteSuffixes = {"B", "KB", "MB", "GB", "TB"};
private static readonly String[] ByteSuffixes = { "B", "KB", "MB", "GB", "TB" };
private static readonly Lazy<MD5> Md5Hasher = new Lazy<MD5>(MD5.Create, true);
private static readonly Lazy<SHA1> SHA1Hasher = new Lazy<SHA1>(SHA1.Create, true);
@@ -39,17 +37,13 @@
new Regex(@"[a-z][A-Z]",
StandardRegexOptions));
private static readonly Lazy<MatchEvaluator> SplitCamelCaseString = new Lazy<MatchEvaluator>(() =>
{
return m =>
{
var x = m.ToString();
private static readonly Lazy<MatchEvaluator> SplitCamelCaseString = new Lazy<MatchEvaluator>(() => m => {
String x = m.ToString();
return x[0] + " " + x.Substring(1, x.Length - 1);
};
});
private static readonly Lazy<string[]> InvalidFilenameChars =
new Lazy<string[]>(() => Path.GetInvalidFileNameChars().Select(c => c.ToString()).ToArray());
private static readonly Lazy<String[]> InvalidFilenameChars =
new Lazy<String[]>(() => Path.GetInvalidFileNameChars().Select(c => c.ToString()).ToArray());
#endregion
@@ -63,32 +57,33 @@
/// The computed hash code.
/// </returns>
/// <exception cref="ArgumentNullException">stream.</exception>
public static byte[] ComputeMD5(this Stream stream, bool createHasher = false)
{
if (stream == null)
[System.Diagnostics.CodeAnalysis.SuppressMessage("Codequalität", "IDE0067:Objekte verwerfen, bevor Bereich verloren geht", Justification = "<Ausstehend>")]
public static Byte[] ComputeMD5(this Stream stream, Boolean createHasher = false) {
if(stream == null) {
throw new ArgumentNullException(nameof(stream));
}
#if NET452
var md5 = MD5.Create();
const int bufferSize = 4096;
MD5 md5 = MD5.Create();
const Int32 bufferSize = 4096;
var readAheadBuffer = new byte[bufferSize];
var readAheadBytesRead = stream.Read(readAheadBuffer, 0, readAheadBuffer.Length);
Byte[] readAheadBuffer = new Byte[bufferSize];
Int32 readAheadBytesRead = stream.Read(readAheadBuffer, 0, readAheadBuffer.Length);
do
{
var bytesRead = readAheadBytesRead;
var buffer = readAheadBuffer;
do {
Int32 bytesRead = readAheadBytesRead;
Byte[] buffer = readAheadBuffer;
readAheadBuffer = new byte[bufferSize];
readAheadBuffer = new Byte[bufferSize];
readAheadBytesRead = stream.Read(readAheadBuffer, 0, readAheadBuffer.Length);
if (readAheadBytesRead == 0)
if(readAheadBytesRead == 0) {
md5.TransformFinalBlock(buffer, 0, bytesRead);
else
} else {
md5.TransformBlock(buffer, 0, bytesRead, buffer, 0);
}
while (readAheadBytesRead != 0);
}
while(readAheadBytesRead != 0);
return md5.Hash;
#else
@@ -108,18 +103,21 @@
/// <param name="value">The input string.</param>
/// <param name="createHasher">if set to <c>true</c> [create hasher].</param>
/// <returns>The computed hash code.</returns>
public static byte[] ComputeMD5(this string value, bool createHasher = false) =>
public static Byte[] ComputeMD5(this String value, Boolean createHasher = false) =>
Encoding.UTF8.GetBytes(value).ComputeMD5(createHasher);
/// <summary>
/// Computes the MD5 hash of the given byte array.
/// </summary>
/// <param name="data">The data.</param>
/// <param name="createHasher">if set to <c>true</c> [create hasher].</param>
/// <returns>The computed hash code.</returns>
public static byte[] ComputeMD5(this byte[] data, bool createHasher = false) =>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Codequalität", "IDE0067:Objekte verwerfen, bevor Bereich verloren geht", Justification = "<Ausstehend>")]
public static Byte[] ComputeMD5(this Byte[] data, Boolean createHasher = false) =>
(createHasher ? MD5.Create() : Md5Hasher.Value).ComputeHash(data);
/// <summary>
/// Computes the SHA-1 hash of the given string using UTF8 byte encoding.
/// </summary>
@@ -129,12 +127,13 @@
/// The computes a Hash-based Message Authentication Code (HMAC)
/// using the SHA1 hash function.
/// </returns>
public static byte[] ComputeSha1(this string value, bool createHasher = false)
{
var inputBytes = Encoding.UTF8.GetBytes(value);
[System.Diagnostics.CodeAnalysis.SuppressMessage("Codequalität", "IDE0067:Objekte verwerfen, bevor Bereich verloren geht", Justification = "<Ausstehend>")]
public static Byte[] ComputeSha1(this String value, Boolean createHasher = false) {
Byte[] inputBytes = Encoding.UTF8.GetBytes(value);
return (createHasher ? SHA1.Create() : SHA1Hasher.Value).ComputeHash(inputBytes);
}
/// <summary>
/// Computes the SHA-256 hash of the given string using UTF8 byte encoding.
/// </summary>
@@ -144,12 +143,13 @@
/// The computes a Hash-based Message Authentication Code (HMAC)
/// by using the SHA256 hash function.
/// </returns>
public static byte[] ComputeSha256(this string value, bool createHasher = false)
{
var inputBytes = Encoding.UTF8.GetBytes(value);
[System.Diagnostics.CodeAnalysis.SuppressMessage("Codequalität", "IDE0067:Objekte verwerfen, bevor Bereich verloren geht", Justification = "<Ausstehend>")]
public static Byte[] ComputeSha256(this String value, Boolean createHasher = false) {
Byte[] inputBytes = Encoding.UTF8.GetBytes(value);
return (createHasher ? SHA256.Create() : SHA256Hasher.Value).ComputeHash(inputBytes);
}
/// <summary>
/// Computes the SHA-512 hash of the given string using UTF8 byte encoding.
/// </summary>
@@ -159,9 +159,9 @@
/// The computes a Hash-based Message Authentication Code (HMAC)
/// using the SHA512 hash function.
/// </returns>
public static byte[] ComputeSha512(this string value, bool createHasher = false)
{
var inputBytes = Encoding.UTF8.GetBytes(value);
[System.Diagnostics.CodeAnalysis.SuppressMessage("Codequalität", "IDE0067:Objekte verwerfen, bevor Bereich verloren geht", Justification = "<Ausstehend>")]
public static Byte[] ComputeSha512(this String value, Boolean createHasher = false) {
Byte[] inputBytes = Encoding.UTF8.GetBytes(value);
return (createHasher ? SHA512.Create() : SHA512Hasher.Value).ComputeHash(inputBytes);
}
@@ -172,17 +172,16 @@
/// </summary>
/// <param name="obj">The item.</param>
/// <returns>A <see cref="System.String" /> that represents the current object.</returns>
public static string ToStringInvariant(this object obj)
{
if (obj == null)
return string.Empty;
public static String ToStringInvariant(this Object obj) {
if(obj == null) {
return String.Empty;
}
var itemType = obj.GetType();
Type itemType = obj.GetType();
if (itemType == typeof(string))
return obj as string;
return Definitions.BasicTypesInfo.ContainsKey(itemType)
return itemType == typeof(String)
? obj as String
: Definitions.BasicTypesInfo.ContainsKey(itemType)
? Definitions.BasicTypesInfo[itemType].ToStringInvariant(obj)
: obj.ToString();
}
@@ -195,13 +194,7 @@
/// <typeparam name="T">The type to get the string.</typeparam>
/// <param name="item">The item.</param>
/// <returns>A <see cref="System.String" /> that represents the current object.</returns>
public static string ToStringInvariant<T>(this T item)
{
if (typeof(string) == typeof(T))
return Equals(item, default(T)) ? string.Empty : item as string;
return ToStringInvariant(item as object);
}
public static String ToStringInvariant<T>(this T item) => typeof(String) == typeof(T) ? Equals(item, default(T)) ? String.Empty : item as String : ToStringInvariant(item as Object);
/// <summary>
/// Removes the control characters from a string except for those specified.
@@ -212,16 +205,17 @@
/// A string that represents the current object.
/// </returns>
/// <exception cref="ArgumentNullException">input.</exception>
public static string RemoveControlCharsExcept(this string value, params char[] excludeChars)
{
if (value == null)
public static String RemoveControlCharsExcept(this String value, params Char[] excludeChars) {
if(value == null) {
throw new ArgumentNullException(nameof(value));
}
if (excludeChars == null)
excludeChars = new char[] { };
if(excludeChars == null) {
excludeChars = new Char[] { };
}
return new string(value
.Where(c => char.IsControl(c) == false || excludeChars.Contains(c))
return new String(value
.Where(c => Char.IsControl(c) == false || excludeChars.Contains(c))
.ToArray());
}
@@ -231,7 +225,7 @@
/// <param name="value">The input.</param>
/// <returns>A <see cref="System.String" /> that represents the current object.</returns>
/// <exception cref="ArgumentNullException">input.</exception>
public static string RemoveControlChars(this string value) => value.RemoveControlCharsExcept(null);
public static String RemoveControlChars(this String value) => value.RemoveControlCharsExcept(null);
/// <summary>
/// Outputs JSON string representing this object.
@@ -239,8 +233,8 @@
/// <param name="obj">The object.</param>
/// <param name="format">if set to <c>true</c> format the output.</param>
/// <returns>A <see cref="System.String" /> that represents the current object.</returns>
public static string ToJson(this object obj, bool format = true) =>
obj == null ? string.Empty : Json.Serialize(obj, format);
public static String ToJson(this Object obj, Boolean format = true) =>
obj == null ? String.Empty : Json.Serialize(obj, format);
/// <summary>
/// Returns text representing the properties of the specified object in a human-readable format.
@@ -249,20 +243,17 @@
/// </summary>
/// <param name="obj">The object.</param>
/// <returns>A <see cref="System.String" /> that represents the current object.</returns>
public static string Stringify(this object obj)
{
if (obj == null)
public static String Stringify(this Object obj) {
if(obj == null) {
return "(null)";
}
try
{
var jsonText = Json.Serialize(obj, false, "$type");
var jsonData = Json.Deserialize(jsonText);
try {
String jsonText = Json.Serialize(obj, false, "$type");
Object jsonData = Json.Deserialize(jsonText);
return new HumanizeJson(jsonData, 0).GetResult();
}
catch
{
} catch {
return obj.ToStringInvariant();
}
}
@@ -276,14 +267,14 @@
/// <param name="startIndex">The start index.</param>
/// <param name="endIndex">The end index.</param>
/// <returns>Retrieves a substring from this instance.</returns>
public static string Slice(this string value, int startIndex, int endIndex)
{
if (value == null)
return string.Empty;
public static String Slice(this String value, Int32 startIndex, Int32 endIndex) {
if(value == null) {
return String.Empty;
}
var end = endIndex.Clamp(startIndex, value.Length - 1);
Int32 end = endIndex.Clamp(startIndex, value.Length - 1);
return startIndex >= end ? string.Empty : value.Substring(startIndex, (end - startIndex) + 1);
return startIndex >= end ? String.Empty : value.Substring(startIndex, end - startIndex + 1);
}
/// <summary>
@@ -295,15 +286,15 @@
/// <param name="startIndex">The start index.</param>
/// <param name="length">The length.</param>
/// <returns>Retrieves a substring from this instance.</returns>
public static string SliceLength(this string str, int startIndex, int length)
{
if (str == null)
return string.Empty;
public static String SliceLength(this String str, Int32 startIndex, Int32 length) {
if(str == null) {
return String.Empty;
}
var start = startIndex.Clamp(0, str.Length - 1);
var len = length.Clamp(0, str.Length - start);
Int32 start = startIndex.Clamp(0, str.Length - 1);
Int32 len = length.Clamp(0, str.Length - start);
return len == 0 ? string.Empty : str.Substring(start, len);
return len == 0 ? String.Empty : str.Substring(start, len);
}
/// <summary>
@@ -314,8 +305,8 @@
/// An array whose elements contain the substrings from this instance
/// that are delimited by one or more characters in separator.
/// </returns>
public static string[] ToLines(this string value) =>
value == null ? new string[] { } : SplitLinesRegex.Value.Split(value);
public static String[] ToLines(this String value) =>
value == null ? new String[] { } : SplitLinesRegex.Value.Split(value);
/// <summary>
/// Humanizes (make more human-readable) an identifier-style string
@@ -324,12 +315,12 @@
/// </summary>
/// <param name="value">The identifier-style string.</param>
/// <returns>A <see cref="System.String" /> that represents the current object.</returns>
public static string Humanize(this string value)
{
if (value == null)
return string.Empty;
public static String Humanize(this String value) {
if(value == null) {
return String.Empty;
}
var returnValue = UnderscoreRegex.Value.Replace(value, " ");
String returnValue = UnderscoreRegex.Value.Replace(value, " ");
returnValue = CamelCaseRegEx.Value.Replace(returnValue, SplitCamelCaseString.Value);
return returnValue;
}
@@ -341,18 +332,21 @@
/// <param name="value">The text.</param>
/// <param name="spaces">The spaces.</param>
/// <returns>A <see cref="System.String" /> that represents the current object.</returns>
public static string Indent(this string value, int spaces = 4)
{
if (value == null) value = string.Empty;
if (spaces <= 0) return value;
public static String Indent(this String value, Int32 spaces = 4) {
if(value == null) {
value = String.Empty;
}
var lines = value.ToLines();
var builder = new StringBuilder();
var indentStr = new string(' ', spaces);
if(spaces <= 0) {
return value;
}
foreach (var line in lines)
{
builder.AppendLine($"{indentStr}{line}");
String[] lines = value.ToLines();
StringBuilder builder = new StringBuilder();
String indentStr = new String(' ', spaces);
foreach(String line in lines) {
_ = builder.AppendLine($"{indentStr}{line}");
}
return builder.ToString().TrimEnd();
@@ -368,28 +362,27 @@
/// <param name="value">The string.</param>
/// <param name="charIndex">Index of the character.</param>
/// <returns>A 2-tuple whose value is (item1, item2).</returns>
public static Tuple<int, int> TextPositionAt(this string value, int charIndex)
{
if (value == null)
public static Tuple<Int32, Int32> TextPositionAt(this String value, Int32 charIndex) {
if(value == null) {
return Tuple.Create(0, 0);
}
var index = charIndex.Clamp(0, value.Length - 1);
Int32 index = charIndex.Clamp(0, value.Length - 1);
var lineIndex = 0;
var colNumber = 0;
Int32 lineIndex = 0;
Int32 colNumber = 0;
for (var i = 0; i <= index; i++)
{
if (value[i] == '\n')
{
for(Int32 i = 0; i <= index; i++) {
if(value[i] == '\n') {
lineIndex++;
colNumber = 0;
continue;
}
if (value[i] != '\r')
if(value[i] != '\r') {
colNumber++;
}
}
return Tuple.Create(lineIndex + 1, colNumber);
}
@@ -402,14 +395,11 @@
/// A string with a safe file name.
/// </returns>
/// <exception cref="ArgumentNullException">s.</exception>
public static string ToSafeFilename(this string value)
{
return value == null
public static String ToSafeFilename(this String value) => value == null
? throw new ArgumentNullException(nameof(value))
: InvalidFilenameChars.Value
.Aggregate(value, (current, c) => current.Replace(c, string.Empty))
.Aggregate(value, (current, c) => current.Replace(c, String.Empty))
.Slice(0, 220);
}
/// <summary>
/// Formats a long into the closest bytes string.
@@ -418,7 +408,7 @@
/// <returns>
/// The string representation of the current Byte object, formatted as specified by the format parameter.
/// </returns>
public static string FormatBytes(this long bytes) => ((ulong) bytes).FormatBytes();
public static String FormatBytes(this Int64 bytes) => ((UInt64)bytes).FormatBytes();
/// <summary>
/// Formats a long into the closest bytes string.
@@ -428,13 +418,11 @@
/// A copy of format in which the format items have been replaced by the string
/// representations of the corresponding arguments.
/// </returns>
public static string FormatBytes(this ulong bytes)
{
int i;
double dblSByte = bytes;
public static String FormatBytes(this UInt64 bytes) {
Int32 i;
Double dblSByte = bytes;
for (i = 0; i < ByteSuffixes.Length && bytes >= 1024; i++, bytes /= 1024)
{
for(i = 0; i < ByteSuffixes.Length && bytes >= 1024; i++, bytes /= 1024) {
dblSByte = bytes / 1024.0;
}
@@ -450,8 +438,8 @@
/// Retrieves a substring from this instance.
/// The substring starts at a specified character position and has a specified length.
/// </returns>
public static string Truncate(this string value, int maximumLength) =>
Truncate(value, maximumLength, string.Empty);
public static String Truncate(this String value, Int32 maximumLength) =>
Truncate(value, maximumLength, String.Empty);
/// <summary>
/// Truncates the specified value and append the omission last.
@@ -463,32 +451,28 @@
/// Retrieves a substring from this instance.
/// The substring starts at a specified character position and has a specified length.
/// </returns>
public static string Truncate(this string value, int maximumLength, string omission)
{
if (value == null)
return null;
return value.Length > maximumLength
? value.Substring(0, maximumLength) + (omission ?? string.Empty)
public static String Truncate(this String value, Int32 maximumLength, String omission) => value == null
? null
: value.Length > maximumLength
? value.Substring(0, maximumLength) + (omission ?? String.Empty)
: value;
}
/// <summary>
/// Determines whether the specified <see cref="string"/> contains any of characters in
/// the specified array of <see cref="char"/>.
/// Determines whether the specified <see cref="String"/> contains any of characters in
/// the specified array of <see cref="Char"/>.
/// </summary>
/// <returns>
/// <c>true</c> if <paramref name="value"/> contains any of <paramref name="chars"/>;
/// otherwise, <c>false</c>.
/// </returns>
/// <param name="value">
/// A <see cref="string"/> to test.
/// A <see cref="String"/> to test.
/// </param>
/// <param name="chars">
/// An array of <see cref="char"/> that contains characters to find.
/// An array of <see cref="Char"/> that contains characters to find.
/// </param>
public static bool Contains(this string value, params char[] chars) =>
chars?.Length == 0 || (!string.IsNullOrEmpty(value) && value.IndexOfAny(chars) > -1);
public static Boolean Contains(this String value, params Char[] chars) =>
chars?.Length == 0 || !String.IsNullOrEmpty(value) && value.IndexOfAny(chars) > -1;
/// <summary>
/// Replaces all chars in a string.
@@ -497,8 +481,8 @@
/// <param name="replaceValue">The replace value.</param>
/// <param name="chars">The chars.</param>
/// <returns>The string with the characters replaced.</returns>
public static string ReplaceAll(this string value, string replaceValue, params char[] chars) =>
chars.Aggregate(value, (current, c) => current.Replace(new string(new[] {c}), replaceValue));
public static String ReplaceAll(this String value, String replaceValue, params Char[] chars) =>
chars.Aggregate(value, (current, c) => current.Replace(new String(new[] { c }), replaceValue));
/// <summary>
/// Convert hex character to an integer. Return -1 if char is something
@@ -506,9 +490,7 @@
/// </summary>
/// <param name="value">The c.</param>
/// <returns>Converted integer.</returns>
public static int Hex2Int(this char value)
{
return value >= '0' && value <= '9'
public static Int32 Hex2Int(this Char value) => value >= '0' && value <= '9'
? value - '0'
: value >= 'A' && value <= 'F'
? value - 'A' + 10
@@ -516,5 +498,4 @@
? value - 'a' + 10
: -1;
}
}
}
+43 -64
View File
@@ -1,15 +1,13 @@
namespace Unosquare.Swan
{
using System;
using System.Reflection;
using System.Runtime.InteropServices;
using Attributes;
using System;
using System.Reflection;
using System.Runtime.InteropServices;
using Unosquare.Swan.Attributes;
namespace Unosquare.Swan {
/// <summary>
/// Provides various extension methods for value types and structs.
/// </summary>
public static class ValueTypeExtensions
{
public static class ValueTypeExtensions {
/// <summary>
/// Clamps the specified value between the minimum and the maximum.
/// </summary>
@@ -19,12 +17,7 @@
/// <param name="max">The maximum.</param>
/// <returns>A value that indicates the relative order of the objects being compared.</returns>
public static T Clamp<T>(this T value, T min, T max)
where T : struct, IComparable
{
if (value.CompareTo(min) < 0) return min;
return value.CompareTo(max) > 0 ? max : value;
}
where T : struct, IComparable => value.CompareTo(min) < 0 ? min : value.CompareTo(max) > 0 ? max : value;
/// <summary>
/// Clamps the specified value between the minimum and the maximum.
@@ -33,7 +26,7 @@
/// <param name="min">The minimum.</param>
/// <param name="max">The maximum.</param>
/// <returns>A value that indicates the relative order of the objects being compared.</returns>
public static int Clamp(this int value, int min, int max)
public static Int32 Clamp(this Int32 value, Int32 min, Int32 max)
=> value < min ? min : (value > max ? max : value);
/// <summary>
@@ -46,11 +39,8 @@
/// <returns>
/// <c>true</c> if the specified minimum is between; otherwise, <c>false</c>.
/// </returns>
public static bool IsBetween<T>(this T value, T min, T max)
where T : struct, IComparable
{
return value.CompareTo(min) >= 0 && value.CompareTo(max) <= 0;
}
public static Boolean IsBetween<T>(this T value, T min, T max)
where T : struct, IComparable => value.CompareTo(min) >= 0 && value.CompareTo(max) <= 0;
/// <summary>
/// Converts an array of bytes into the given struct type.
@@ -58,11 +48,8 @@
/// <typeparam name="T">The type of structure to convert.</typeparam>
/// <param name="data">The data.</param>
/// <returns>a struct type derived from convert an array of bytes ref=ToStruct".</returns>
public static T ToStruct<T>(this byte[] data)
where T : struct
{
return ToStruct<T>(data, 0, data.Length);
}
public static T ToStruct<T>(this Byte[] data)
where T : struct => ToStruct<T>(data, 0, data.Length);
/// <summary>
/// Converts an array of bytes into the given struct type.
@@ -75,22 +62,19 @@
/// A managed object containing the data pointed to by the ptr parameter.
/// </returns>
/// <exception cref="ArgumentNullException">data.</exception>
public static T ToStruct<T>(this byte[] data, int offset, int length)
where T : struct
{
if (data == null)
public static T ToStruct<T>(this Byte[] data, Int32 offset, Int32 length)
where T : struct {
if(data == null) {
throw new ArgumentNullException(nameof(data));
var buffer = new byte[length];
Array.Copy(data, offset, buffer, 0, buffer.Length);
var handle = GCHandle.Alloc(GetStructBytes<T>(buffer), GCHandleType.Pinned);
try
{
return Marshal.PtrToStructure<T>(handle.AddrOfPinnedObject());
}
finally
{
Byte[] buffer = new Byte[length];
Array.Copy(data, offset, buffer, 0, buffer.Length);
GCHandle handle = GCHandle.Alloc(GetStructBytes<T>(buffer), GCHandleType.Pinned);
try {
return Marshal.PtrToStructure<T>(handle.AddrOfPinnedObject());
} finally {
handle.Free();
}
}
@@ -101,19 +85,15 @@
/// <typeparam name="T">The type of structure to convert.</typeparam>
/// <param name="obj">The object.</param>
/// <returns>A byte array containing the results of encoding the specified set of characters.</returns>
public static byte[] ToBytes<T>(this T obj)
where T : struct
{
var data = new byte[Marshal.SizeOf(obj)];
var handle = GCHandle.Alloc(data, GCHandleType.Pinned);
public static Byte[] ToBytes<T>(this T obj)
where T : struct {
Byte[] data = new Byte[Marshal.SizeOf(obj)];
GCHandle handle = GCHandle.Alloc(data, GCHandleType.Pinned);
try
{
try {
Marshal.StructureToPtr(obj, handle.AddrOfPinnedObject(), false);
return GetStructBytes<T>(data);
}
finally
{
} finally {
handle.Free();
}
}
@@ -126,38 +106,37 @@
/// A 32-bit unsigned integer equivalent to the ulong
/// contained in longBytes.
/// </returns>
public static uint SwapEndianness(this ulong longBytes)
=> (uint) (((longBytes & 0x000000ff) << 24) +
public static UInt32 SwapEndianness(this UInt64 longBytes)
=> (UInt32)(((longBytes & 0x000000ff) << 24) +
((longBytes & 0x0000ff00) << 8) +
((longBytes & 0x00ff0000) >> 8) +
((longBytes & 0xff000000) >> 24));
private static byte[] GetStructBytes<T>(byte[] data)
{
if (data == null)
private static Byte[] GetStructBytes<T>(Byte[] data) {
if(data == null) {
throw new ArgumentNullException(nameof(data));
}
#if !NETSTANDARD1_3
var fields = typeof(T).GetTypeInfo()
FieldInfo[] fields = typeof(T).GetTypeInfo()
.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
#else
var fields = typeof(T).GetTypeInfo().DeclaredFields;
#endif
var endian = Runtime.AttributeCache.RetrieveOne<StructEndiannessAttribute, T>();
StructEndiannessAttribute endian = Runtime.AttributeCache.RetrieveOne<StructEndiannessAttribute, T>();
foreach (var field in fields)
{
if (endian == null && !field.IsDefined(typeof(StructEndiannessAttribute), false))
foreach(FieldInfo field in fields) {
if(endian == null && !field.IsDefined(typeof(StructEndiannessAttribute), false)) {
continue;
}
var offset = Marshal.OffsetOf<T>(field.Name).ToInt32();
var length = Marshal.SizeOf(field.FieldType);
Int32 offset = Marshal.OffsetOf<T>(field.Name).ToInt32();
Int32 length = Marshal.SizeOf(field.FieldType);
endian = endian ?? Runtime.AttributeCache.RetrieveOne<StructEndiannessAttribute>(field);
if (endian != null && (endian.Endianness == Endianness.Big && BitConverter.IsLittleEndian ||
endian.Endianness == Endianness.Little && !BitConverter.IsLittleEndian))
{
if(endian != null && (endian.Endianness == Endianness.Big && BitConverter.IsLittleEndian ||
endian.Endianness == Endianness.Little && !BitConverter.IsLittleEndian)) {
Array.Reverse(data, offset, length);
}
}
+79 -105
View File
@@ -1,19 +1,17 @@
namespace Unosquare.Swan
{
using Attributes;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
using Unosquare.Swan.Attributes;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
namespace Unosquare.Swan {
/// <summary>
/// Extension methods.
/// </summary>
public static partial class Extensions
{
public static partial class Extensions {
/// <summary>
/// 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.
@@ -22,10 +20,9 @@
/// <param name="source">The source.</param>
/// <param name="target">The target.</param>
/// <returns>Number of properties that was copied successful.</returns>
public static int CopyPropertiesTo<T>(this T source, object target)
where T : class
{
var copyable = GetCopyableProperties(target);
public static Int32 CopyPropertiesTo<T>(this T source, Object target)
where T : class {
IEnumerable<String> copyable = GetCopyableProperties(target);
return copyable.Any()
? CopyOnlyPropertiesTo(source, target, copyable.ToArray())
: CopyPropertiesTo(source, target, null);
@@ -41,7 +38,7 @@
/// <returns>
/// Number of properties that were successfully copied.
/// </returns>
public static int CopyPropertiesTo(this object source, object target, string[] ignoreProperties = null)
public static Int32 CopyPropertiesTo(this Object source, Object target, String[] ignoreProperties = null)
=> Components.ObjectMapper.Copy(source, target, null, ignoreProperties);
/// <summary>
@@ -52,11 +49,8 @@
/// <param name="source">The source.</param>
/// <param name="target">The target.</param>
/// <returns>Number of properties that was copied successful.</returns>
public static int CopyOnlyPropertiesTo<T>(this T source, object target)
where T : class
{
return CopyOnlyPropertiesTo(source, target, null);
}
public static Int32 CopyOnlyPropertiesTo<T>(this T source, Object target)
where T : class => CopyOnlyPropertiesTo(source, target, null);
/// <summary>
/// Iterates over the public, instance, readable properties of the source and
@@ -68,10 +62,7 @@
/// <returns>
/// Number of properties that were successfully copied.
/// </returns>
public static int CopyOnlyPropertiesTo(this object source, object target, string[] propertiesToCopy)
{
return Components.ObjectMapper.Copy(source, target, propertiesToCopy);
}
public static Int32 CopyOnlyPropertiesTo(this Object source, Object target, String[] propertiesToCopy) => Components.ObjectMapper.Copy(source, target, propertiesToCopy);
/// <summary>
/// Copies the properties to new instance of T.
@@ -83,11 +74,8 @@
/// The specified type with properties copied.
/// </returns>
/// <exception cref="ArgumentNullException">source.</exception>
public static T DeepClone<T>(this T source, string[] ignoreProperties = null)
where T : class
{
return source.CopyPropertiesToNew<T>(ignoreProperties);
}
public static T DeepClone<T>(this T source, String[] ignoreProperties = null)
where T : class => source.CopyPropertiesToNew<T>(ignoreProperties);
/// <summary>
/// Copies the properties to new instance of T.
@@ -99,19 +87,16 @@
/// The specified type with properties copied.
/// </returns>
/// <exception cref="ArgumentNullException">source.</exception>
public static T CopyPropertiesToNew<T>(this object source, string[] ignoreProperties = null)
where T : class
{
if (source == null)
public static T CopyPropertiesToNew<T>(this Object source, String[] ignoreProperties = null)
where T : class {
if(source == null) {
throw new ArgumentNullException(nameof(source));
}
var target = Activator.CreateInstance<T>();
var copyable = target.GetCopyableProperties();
T target = Activator.CreateInstance<T>();
IEnumerable<String> copyable = target.GetCopyableProperties();
if (copyable.Any())
source.CopyOnlyPropertiesTo(target, copyable.ToArray());
else
source.CopyPropertiesTo(target, ignoreProperties);
_ = copyable.Any() ? source.CopyOnlyPropertiesTo(target, copyable.ToArray()) : source.CopyPropertiesTo(target, ignoreProperties);
return target;
}
@@ -126,14 +111,14 @@
/// The specified type with properties copied.
/// </returns>
/// <exception cref="ArgumentNullException">source.</exception>
public static T CopyOnlyPropertiesToNew<T>(this object source, string[] propertiesToCopy)
where T : class
{
if (source == null)
public static T CopyOnlyPropertiesToNew<T>(this Object source, String[] propertiesToCopy)
where T : class {
if(source == null) {
throw new ArgumentNullException(nameof(source));
}
var target = Activator.CreateInstance<T>();
source.CopyOnlyPropertiesTo(target, propertiesToCopy);
T target = Activator.CreateInstance<T>();
_ = source.CopyOnlyPropertiesTo(target, propertiesToCopy);
return target;
}
@@ -145,13 +130,10 @@
/// <param name="target">The target.</param>
/// <param name="ignoreKeys">The ignore keys.</param>
/// <returns>Number of properties that was copied successful.</returns>
public static int CopyKeyValuePairTo(
this IDictionary<string, object> source,
object target,
string[] ignoreKeys = null)
{
return Components.ObjectMapper.Copy(source, target, null, ignoreKeys);
}
public static Int32 CopyKeyValuePairTo(
this IDictionary<String, Object> source,
Object target,
String[] ignoreKeys = null) => Components.ObjectMapper.Copy(source, target, null, ignoreKeys);
/// <summary>
/// Measures the elapsed time of the given action as a TimeSpan
@@ -162,24 +144,19 @@
/// A time interval that represents a specified time, where the specification is in units of ticks.
/// </returns>
/// <exception cref="ArgumentNullException">target.</exception>
public static TimeSpan Benchmark(this Action target)
{
if (target == null)
public static TimeSpan Benchmark(this Action target) {
if(target == null) {
throw new ArgumentNullException(nameof(target));
}
var sw = new Stopwatch();
Stopwatch sw = new Stopwatch();
try
{
try {
sw.Start();
target.Invoke();
}
catch
{
} catch {
// swallow
}
finally
{
} finally {
sw.Stop();
}
@@ -195,13 +172,12 @@
public static void Retry(
this Action action,
TimeSpan retryInterval = default,
int retryCount = 3)
{
if (action == null)
Int32 retryCount = 3) {
if(action == null) {
throw new ArgumentNullException(nameof(action));
}
Retry<object>(() =>
{
_ = Retry<Object>(() => {
action();
return null;
},
@@ -224,27 +200,25 @@
public static T Retry<T>(
this Func<T> action,
TimeSpan retryInterval = default,
int retryCount = 3)
{
if (action == null)
Int32 retryCount = 3) {
if(action == null) {
throw new ArgumentNullException(nameof(action));
}
if (retryInterval == default)
if(retryInterval == default) {
retryInterval = TimeSpan.FromSeconds(1);
}
var exceptions = new List<Exception>();
List<Exception> exceptions = new List<Exception>();
for (var retry = 0; retry < retryCount; retry++)
{
try
{
if (retry > 0)
for(Int32 retry = 0; retry < retryCount; retry++) {
try {
if(retry > 0) {
Task.Delay(retryInterval).Wait();
}
return action();
}
catch (Exception ex)
{
} catch(Exception ex) {
exceptions.Add(ex);
}
}
@@ -258,19 +232,19 @@
/// <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)
public static String ExceptionMessage(this Exception ex, String priorMessage = "") {
while(true) {
if(ex == null) {
throw new ArgumentNullException(nameof(ex));
}
var fullMessage = string.IsNullOrWhiteSpace(priorMessage)
String fullMessage = String.IsNullOrWhiteSpace(priorMessage)
? ex.Message
: priorMessage + "\r\n" + ex.Message;
if (string.IsNullOrWhiteSpace(ex.InnerException?.Message))
if(String.IsNullOrWhiteSpace(ex.InnerException?.Message)) {
return fullMessage;
}
ex = ex.InnerException;
priorMessage = fullMessage;
@@ -285,14 +259,14 @@
/// Array of properties.
/// </returns>
/// <exception cref="ArgumentNullException">model.</exception>
public static IEnumerable<string> GetCopyableProperties(this object obj)
{
if (obj == null)
public static IEnumerable<String> GetCopyableProperties(this Object obj) {
if(obj == null) {
throw new ArgumentNullException(nameof(obj));
}
return Runtime.PropertyTypeCache
.RetrieveAllProperties(obj.GetType(), true)
.Select(x => new { x.Name, HasAttribute = Runtime.AttributeCache.RetrieveOne<CopyableAttribute>(x) != null})
.Select(x => new { x.Name, HasAttribute = Runtime.AttributeCache.RetrieveOne<CopyableAttribute>(x) != null })
.Where(x => x.HasAttribute)
.Select(x => x.Name);
}
@@ -304,23 +278,23 @@
/// <returns>
/// <c>true</c> if the specified model is valid; otherwise, <c>false</c>.
/// </returns>
public static bool IsValid(this object obj) => Runtime.ObjectValidator.IsValid(obj);
public static Boolean IsValid(this Object obj) => Runtime.ObjectValidator.IsValid(obj);
internal static void CreateTarget(
this object source,
this Object source,
Type targetType,
bool includeNonPublic,
ref object target)
{
switch (source)
{
case string _:
Boolean includeNonPublic,
ref Object target) {
switch(source) {
case String _:
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
var elementType = targetType.GetElementType();
Type elementType = targetType.GetElementType();
if (elementType != null)
if(elementType != null) {
target = Array.CreateInstance(elementType, sourceObjectList.Count);
}
break;
default:
target = Activator.CreateInstance(targetType, includeNonPublic);
+154 -204
View File
@@ -1,13 +1,12 @@
namespace Unosquare.Swan.Formatters
{
using Reflection;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using Unosquare.Swan.Reflection;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
namespace Unosquare.Swan.Formatters {
/// <summary>
/// Represents a reader designed for CSV text.
/// It is capable of deserializing objects from individual lines of CSV text,
@@ -66,19 +65,18 @@
/// }
/// </code>
/// </example>
public class CsvReader : IDisposable
{
public class CsvReader : IDisposable {
private static readonly PropertyTypeCache TypeCache = new PropertyTypeCache();
private readonly object _syncLock = new object();
private readonly Object _syncLock = new Object();
private ulong _count;
private char _escapeCharacter = '"';
private char _separatorCharacter = ',';
private UInt64 _count;
private Char _escapeCharacter = '"';
private Char _separatorCharacter = ',';
private bool _hasDisposed; // To detect redundant calls
private string[] _headings;
private Dictionary<string, string> _defaultMap;
private Boolean _hasDisposed; // To detect redundant calls
private String[] _headings;
private Dictionary<String, String> _defaultMap;
private StreamReader _reader;
#region Constructors
@@ -89,15 +87,16 @@
/// <param name="inputStream">The stream.</param>
/// <param name="leaveOpen">if set to <c>true</c> leaves the input stream open.</param>
/// <param name="textEncoding">The text encoding.</param>
public CsvReader(Stream inputStream, bool leaveOpen, Encoding textEncoding)
{
if (inputStream == null)
public CsvReader(Stream inputStream, Boolean leaveOpen, Encoding textEncoding) {
if(inputStream == null) {
throw new ArgumentNullException(nameof(inputStream));
}
if (textEncoding == null)
if(textEncoding == null) {
throw new ArgumentNullException(nameof(textEncoding));
}
_reader = new StreamReader(inputStream, textEncoding, true, 2048, leaveOpen);
this._reader = new StreamReader(inputStream, textEncoding, true, 2048, leaveOpen);
}
/// <summary>
@@ -107,8 +106,7 @@
/// <param name="stream">The stream.</param>
/// <param name="textEncoding">The text encoding.</param>
public CsvReader(Stream stream, Encoding textEncoding)
: this(stream, false, textEncoding)
{
: this(stream, false, textEncoding) {
// placeholder
}
@@ -119,8 +117,7 @@
/// </summary>
/// <param name="stream">The stream.</param>
public CsvReader(Stream stream)
: this(stream, false, Definitions.Windows1252Encoding)
{
: this(stream, false, Definitions.Windows1252Encoding) {
}
/// <summary>
@@ -129,9 +126,8 @@
/// when this reader is disposed of.
/// </summary>
/// <param name="filename">The filename.</param>
public CsvReader(string filename)
: this(File.OpenRead(filename), false, Definitions.Windows1252Encoding)
{
public CsvReader(String filename)
: this(File.OpenRead(filename), false, Definitions.Windows1252Encoding) {
// placeholder
}
@@ -141,9 +137,8 @@
/// </summary>
/// <param name="filename">The filename.</param>
/// <param name="encoding">The encoding.</param>
public CsvReader(string filename, Encoding encoding)
: this(File.OpenRead(filename), false, encoding)
{
public CsvReader(String filename, Encoding encoding)
: this(File.OpenRead(filename), false, encoding) {
// placeholder
}
@@ -157,13 +152,10 @@
/// <value>
/// The count.
/// </value>
public ulong Count
{
get
{
lock (_syncLock)
{
return _count;
public UInt64 Count {
get {
lock(this._syncLock) {
return this._count;
}
}
}
@@ -175,14 +167,11 @@
/// <value>
/// The escape character.
/// </value>
public char EscapeCharacter
{
get => _escapeCharacter;
set
{
lock (_syncLock)
{
_escapeCharacter = value;
public Char EscapeCharacter {
get => this._escapeCharacter;
set {
lock(this._syncLock) {
this._escapeCharacter = value;
}
}
}
@@ -194,14 +183,11 @@
/// <value>
/// The separator character.
/// </value>
public char SeparatorCharacter
{
get => _separatorCharacter;
set
{
lock (_syncLock)
{
_separatorCharacter = value;
public Char SeparatorCharacter {
get => this._separatorCharacter;
set {
lock(this._syncLock) {
this._separatorCharacter = value;
}
}
}
@@ -213,13 +199,10 @@
/// <value>
/// <c>true</c> if [end of stream]; otherwise, <c>false</c>.
/// </value>
public bool EndOfStream
{
get
{
lock (_syncLock)
{
return _reader.EndOfStream;
public Boolean EndOfStream {
get {
lock(this._syncLock) {
return this._reader.EndOfStream;
}
}
}
@@ -233,15 +216,14 @@
/// </summary>
/// <returns>An array of the specified element type containing copies of the elements of the ArrayList.</returns>
/// <exception cref="System.IO.EndOfStreamException">Cannot read past the end of the stream.</exception>
public string[] ReadLine()
{
lock (_syncLock)
{
if (_reader.EndOfStream)
public String[] ReadLine() {
lock(this._syncLock) {
if(this._reader.EndOfStream) {
throw new EndOfStreamException("Cannot read past the end of the stream");
}
var values = ParseRecord(_reader, _escapeCharacter, _separatorCharacter);
_count++;
String[] values = ParseRecord(this._reader, this._escapeCharacter, this._separatorCharacter);
this._count++;
return values;
}
}
@@ -257,14 +239,13 @@
/// as the first read operation (i.e. while count is still 0).
/// </summary>
/// <exception cref="System.IO.EndOfStreamException">Cannot read past the end of the stream.</exception>
public void SkipRecord()
{
lock (_syncLock)
{
if (_reader.EndOfStream)
public void SkipRecord() {
lock(this._syncLock) {
if(this._reader.EndOfStream) {
throw new EndOfStreamException("Cannot read past the end of the stream");
}
ParseRecord(_reader, _escapeCharacter, _separatorCharacter);
_ = ParseRecord(this._reader, this._escapeCharacter, this._separatorCharacter);
}
}
@@ -279,20 +260,20 @@
/// ReadHeadings.
/// </exception>
/// <exception cref="System.IO.EndOfStreamException">Cannot read past the end of the stream.</exception>
public string[] ReadHeadings()
{
lock (_syncLock)
{
if (_headings != null)
public String[] ReadHeadings() {
lock(this._syncLock) {
if(this._headings != null) {
throw new InvalidOperationException($"The {nameof(ReadHeadings)} method had already been called.");
}
if (_count != 0)
if(this._count != 0) {
throw new InvalidOperationException("Reading headings is only supported as the first read operation.");
}
_headings = ReadLine();
_defaultMap = _headings.ToDictionary(x => x, x => x);
this._headings = this.ReadLine();
this._defaultMap = this._headings.ToDictionary(x => x, x => x);
return _headings.ToArray();
return this._headings.ToArray();
}
}
@@ -304,25 +285,25 @@
/// <exception cref="System.InvalidOperationException">ReadHeadings.</exception>
/// <exception cref="System.IO.EndOfStreamException">Cannot read past the end of the stream.</exception>
/// <exception cref="System.ArgumentNullException">map.</exception>
public IDictionary<string, object> ReadObject(IDictionary<string, string> map)
{
lock (_syncLock)
{
if (_headings == null)
public IDictionary<String, Object> ReadObject(IDictionary<String, String> map) {
lock(this._syncLock) {
if(this._headings == null) {
throw new InvalidOperationException($"Call the {nameof(ReadHeadings)} method before reading as an object.");
}
if (map == null)
if(map == null) {
throw new ArgumentNullException(nameof(map));
}
var result = new Dictionary<string, object>();
var values = ReadLine();
Dictionary<String, Object> result = new Dictionary<String, Object>();
String[] values = this.ReadLine();
for (var i = 0; i < _headings.Length; i++)
{
if (i > values.Length - 1)
for(Int32 i = 0; i < this._headings.Length; i++) {
if(i > values.Length - 1) {
break;
}
result[_headings[i]] = values[i];
result[this._headings[i]] = values[i];
}
return result;
@@ -334,7 +315,7 @@
/// The property names correspond to the names of the CSV headings.
/// </summary>
/// <returns>Object of the type of the elements in the collection of key/value pairs.</returns>
public IDictionary<string, object> ReadObject() => ReadObject(_defaultMap);
public IDictionary<String, Object> ReadObject() => this.ReadObject(this._defaultMap);
/// <summary>
/// Reads a line of CSV text converting it into an object of the given type, using a map (or Dictionary)
@@ -349,49 +330,52 @@
/// result.</exception>
/// <exception cref="System.InvalidOperationException">ReadHeadings.</exception>
/// <exception cref="System.IO.EndOfStreamException">Cannot read past the end of the stream.</exception>
public void ReadObject<T>(IDictionary<string, string> map, ref T result)
{
lock (_syncLock)
{
public void ReadObject<T>(IDictionary<String, String> map, ref T result) {
lock(this._syncLock) {
// Check arguments
{
if (map == null)
if(map == null) {
throw new ArgumentNullException(nameof(map));
}
if (_reader.EndOfStream)
if(this._reader.EndOfStream) {
throw new EndOfStreamException("Cannot read past the end of the stream");
}
if (_headings == null)
if(this._headings == null) {
throw new InvalidOperationException($"Call the {nameof(ReadHeadings)} method before reading as an object.");
}
if (Equals(result, default(T)))
if(Equals(result, default(T))) {
throw new ArgumentNullException(nameof(result));
}
}
// Read line and extract values
var values = ReadLine();
String[] values = this.ReadLine();
// Extract properties from cache
var properties = TypeCache
IEnumerable<PropertyInfo> properties = TypeCache
.RetrieveFilteredProperties(typeof(T), true, x => x.CanWrite && Definitions.BasicTypesInfo.ContainsKey(x.PropertyType));
// Assign property values for each heading
for (var i = 0; i < _headings.Length; i++)
{
for(Int32 i = 0; i < this._headings.Length; i++) {
// break if no more headings are matched
if (i > values.Length - 1)
if(i > values.Length - 1) {
break;
}
// skip if no heading is available or the heading is empty
if (map.ContainsKey(_headings[i]) == false &&
string.IsNullOrWhiteSpace(map[_headings[i]]) == false)
if(map.ContainsKey(this._headings[i]) == false &&
String.IsNullOrWhiteSpace(map[this._headings[i]]) == false) {
continue;
}
// Prepare the target property
var propertyName = map[_headings[i]];
String propertyName = map[this._headings[i]];
// Parse and assign the basic type value to the property if exists
properties
_ = properties
.FirstOrDefault(p => p.Name.Equals(propertyName))?
.TrySetBasicType(values[i], result);
}
@@ -409,11 +393,10 @@
/// <exception cref="System.ArgumentNullException">map.</exception>
/// <exception cref="System.InvalidOperationException">ReadHeadings.</exception>
/// <exception cref="System.IO.EndOfStreamException">Cannot read past the end of the stream.</exception>
public T ReadObject<T>(IDictionary<string, string> map)
where T : new()
{
var result = Activator.CreateInstance<T>();
ReadObject(map, ref result);
public T ReadObject<T>(IDictionary<String, String> map)
where T : new() {
T result = Activator.CreateInstance<T>();
this.ReadObject(map, ref result);
return result;
}
@@ -424,10 +407,7 @@
/// <typeparam name="T">The type of object.</typeparam>
/// <returns>The conversion of specific type of object.</returns>
public T ReadObject<T>()
where T : new()
{
return ReadObject<T>(_defaultMap);
}
where T : new() => this.ReadObject<T>(this._defaultMap);
#endregion
@@ -441,89 +421,76 @@
/// <param name="escapeCharacter">The escape character.</param>
/// <param name="separatorCharacter">The separator character.</param>
/// <returns>An array of the specified element type containing copies of the elements of the ArrayList.</returns>
private static string[] ParseRecord(StreamReader reader, char escapeCharacter = '"', char separatorCharacter = ',')
{
var values = new List<string>();
var currentValue = new StringBuilder(1024);
var currentState = ReadState.WaitingForNewField;
string line;
private static String[] ParseRecord(StreamReader reader, Char escapeCharacter = '"', Char separatorCharacter = ',') {
List<String> values = new List<String>();
StringBuilder currentValue = new StringBuilder(1024);
ReadState currentState = ReadState.WaitingForNewField;
String line;
while ((line = reader.ReadLine()) != null)
{
for (var charIndex = 0; charIndex < line.Length; charIndex++)
{
while((line = reader.ReadLine()) != null) {
for(Int32 charIndex = 0; charIndex < line.Length; charIndex++) {
// Get the current and next character
var currentChar = line[charIndex];
var nextChar = charIndex < line.Length - 1 ? line[charIndex + 1] : new char?();
Char currentChar = line[charIndex];
Char? nextChar = charIndex < line.Length - 1 ? line[charIndex + 1] : new global::System.Char?();
// Perform logic based on state and decide on next state
switch (currentState)
{
case ReadState.WaitingForNewField:
{
currentValue.Clear();
switch(currentState) {
case ReadState.WaitingForNewField: {
_ = currentValue.Clear();
if (currentChar == escapeCharacter)
{
if(currentChar == escapeCharacter) {
currentState = ReadState.PushingQuoted;
continue;
}
if (currentChar == separatorCharacter)
{
if(currentChar == separatorCharacter) {
values.Add(currentValue.ToString());
currentState = ReadState.WaitingForNewField;
continue;
}
currentValue.Append(currentChar);
_ = currentValue.Append(currentChar);
currentState = ReadState.PushingNormal;
continue;
}
case ReadState.PushingNormal:
{
case ReadState.PushingNormal: {
// Handle field content delimiter by comma
if (currentChar == separatorCharacter)
{
if(currentChar == separatorCharacter) {
currentState = ReadState.WaitingForNewField;
values.Add(currentValue.ToString());
currentValue.Clear();
_ = currentValue.Clear();
continue;
}
// Handle double quote escaping
if (currentChar == escapeCharacter && nextChar.HasValue && nextChar == escapeCharacter)
{
if(currentChar == escapeCharacter && nextChar.HasValue && nextChar == escapeCharacter) {
// advance 1 character now. The loop will advance one more.
currentValue.Append(currentChar);
_ = currentValue.Append(currentChar);
charIndex++;
continue;
}
currentValue.Append(currentChar);
_ = currentValue.Append(currentChar);
break;
}
case ReadState.PushingQuoted:
{
case ReadState.PushingQuoted: {
// Handle field content delimiter by ending double quotes
if (currentChar == escapeCharacter && (nextChar.HasValue == false || nextChar != escapeCharacter))
{
if(currentChar == escapeCharacter && (nextChar.HasValue == false || nextChar != escapeCharacter)) {
currentState = ReadState.PushingNormal;
continue;
}
// Handle double quote escaping
if (currentChar == escapeCharacter && nextChar.HasValue && nextChar == escapeCharacter)
{
if(currentChar == escapeCharacter && nextChar.HasValue && nextChar == escapeCharacter) {
// advance 1 character now. The loop will advance one more.
currentValue.Append(currentChar);
_ = currentValue.Append(currentChar);
charIndex++;
continue;
}
currentValue.Append(currentChar);
_ = currentValue.Append(currentChar);
break;
}
}
@@ -531,17 +498,14 @@
// determine if we need to continue reading a new line if it is part of the quoted
// field value
if (currentState == ReadState.PushingQuoted)
{
if(currentState == ReadState.PushingQuoted) {
// we need to add the new line sequence to the output of the field
// because we were pushing a quoted value
currentValue.Append(Environment.NewLine);
}
else
{
_ = currentValue.Append(Environment.NewLine);
} else {
// push anything that has not been pushed (flush) into a last value
values.Add(currentValue.ToString());
currentValue.Clear();
_ = currentValue.Clear();
// stop reading more lines we have reached the end of the CSV record
break;
@@ -550,8 +514,7 @@
// If we ended up pushing quoted and no closing quotes we might
// have additional text in yt
if (currentValue.Length > 0)
{
if(currentValue.Length > 0) {
values.Add(currentValue.ToString());
}
@@ -570,15 +533,12 @@
/// <param name="stream">The stream.</param>
/// <returns>A generic collection of objects that can be individually accessed by index.</returns>
public static IList<T> LoadRecords<T>(Stream stream)
where T : new()
{
var result = new List<T>();
where T : new() {
List<T> result = new List<T>();
using (var reader = new CsvReader(stream))
{
reader.ReadHeadings();
while (!reader.EndOfStream)
{
using(CsvReader reader = new CsvReader(stream)) {
_ = reader.ReadHeadings();
while(!reader.EndOfStream) {
result.Add(reader.ReadObject<T>());
}
}
@@ -593,11 +553,8 @@
/// <typeparam name="T">The type of IList items to load.</typeparam>
/// <param name="filePath">The file path.</param>
/// <returns>A generic collection of objects that can be individually accessed by index.</returns>
public static IList<T> LoadRecords<T>(string filePath)
where T : new()
{
return LoadRecords<T>(File.OpenRead(filePath));
}
public static IList<T> LoadRecords<T>(String filePath)
where T : new() => LoadRecords<T>(File.OpenRead(filePath));
#endregion
@@ -607,32 +564,26 @@
/// Releases unmanaged and - optionally - managed resources.
/// </summary>
/// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
protected virtual void Dispose(bool disposing)
{
if (_hasDisposed) return;
if (disposing)
{
try
{
_reader.Dispose();
protected virtual void Dispose(Boolean disposing) {
if(this._hasDisposed) {
return;
}
finally
{
_reader = null;
if(disposing) {
try {
this._reader.Dispose();
} finally {
this._reader = null;
}
}
_hasDisposed = true;
this._hasDisposed = true;
}
/// <summary>
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
/// </summary>
public void Dispose()
{
Dispose(true);
}
public void Dispose() => this.Dispose(true);
#endregion
@@ -640,8 +591,7 @@
/// Defines the 3 different read states
/// for the parsing state machine.
/// </summary>
private enum ReadState
{
private enum ReadState {
WaitingForNewField,
PushingNormal,
PushingQuoted,
+111 -126
View File
@@ -1,14 +1,13 @@
namespace Unosquare.Swan.Formatters
{
using Reflection;
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using Unosquare.Swan.Reflection;
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
namespace Unosquare.Swan.Formatters {
/// <summary>
/// A CSV writer useful for exporting a set of objects.
/// </summary>
@@ -46,16 +45,15 @@
/// }
/// </code>
/// </example>
public class CsvWriter : IDisposable
{
public class CsvWriter : IDisposable {
private static readonly PropertyTypeCache TypeCache = new PropertyTypeCache();
private readonly object _syncLock = new object();
private readonly Object _syncLock = new Object();
private readonly Stream _outputStream;
private readonly Encoding _encoding;
private readonly bool _leaveStreamOpen;
private bool _isDisposing;
private ulong _mCount;
private readonly Boolean _leaveStreamOpen;
private Boolean _isDisposing;
private UInt64 _mCount;
#region Constructors
@@ -65,11 +63,10 @@
/// <param name="outputStream">The output stream.</param>
/// <param name="leaveOpen">if set to <c>true</c> [leave open].</param>
/// <param name="encoding">The encoding.</param>
public CsvWriter(Stream outputStream, bool leaveOpen, Encoding encoding)
{
_outputStream = outputStream;
_encoding = encoding;
_leaveStreamOpen = leaveOpen;
public CsvWriter(Stream outputStream, Boolean leaveOpen, Encoding encoding) {
this._outputStream = outputStream;
this._encoding = encoding;
this._leaveStreamOpen = leaveOpen;
}
/// <summary>
@@ -79,8 +76,7 @@
/// <param name="outputStream">The output stream.</param>
/// <param name="encoding">The encoding.</param>
public CsvWriter(Stream outputStream, Encoding encoding)
: this(outputStream, false, encoding)
{
: this(outputStream, false, encoding) {
// placeholder
}
@@ -91,8 +87,7 @@
/// </summary>
/// <param name="outputStream">The output stream.</param>
public CsvWriter(Stream outputStream)
: this(outputStream, false, Definitions.Windows1252Encoding)
{
: this(outputStream, false, Definitions.Windows1252Encoding) {
// placeholder
}
@@ -102,9 +97,8 @@
/// disposing of this writer, and uses the Windows 1252 encoding.
/// </summary>
/// <param name="filename">The filename.</param>
public CsvWriter(string filename)
: this(File.OpenWrite(filename), false, Definitions.Windows1252Encoding)
{
public CsvWriter(String filename)
: this(File.OpenWrite(filename), false, Definitions.Windows1252Encoding) {
// placeholder
}
@@ -115,9 +109,8 @@
/// </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)
{
public CsvWriter(String filename, Encoding encoding)
: this(File.OpenWrite(filename), false, encoding) {
// placeholder
}
@@ -131,7 +124,7 @@
/// <value>
/// The separator character.
/// </value>
public char SeparatorCharacter { get; set; } = ',';
public Char SeparatorCharacter { get; set; } = ',';
/// <summary>
/// Gets or sets the escape character to use to escape field values.
@@ -139,7 +132,7 @@
/// <value>
/// The escape character.
/// </value>
public char EscapeCharacter { get; set; } = '"';
public Char EscapeCharacter { get; set; } = '"';
/// <summary>
/// Gets or sets the new line character sequence to use when writing a line.
@@ -147,7 +140,7 @@
/// <value>
/// The new line sequence.
/// </value>
public string NewLineSequence { get; set; } = Environment.NewLine;
public String NewLineSequence { get; set; } = Environment.NewLine;
/// <summary>
/// Defines a list of properties to ignore when outputting CSV lines.
@@ -155,7 +148,7 @@
/// <value>
/// The ignore property names.
/// </value>
public List<string> IgnorePropertyNames { get; } = new List<string>();
public List<String> IgnorePropertyNames { get; } = new List<String>();
/// <summary>
/// Gets number of lines that have been written, including the headings line.
@@ -163,13 +156,10 @@
/// <value>
/// The count.
/// </value>
public ulong Count
{
get
{
lock (_syncLock)
{
return _mCount;
public UInt64 Count {
get {
lock(this._syncLock) {
return this._mCount;
}
}
}
@@ -187,17 +177,16 @@
/// <param name="stream">The stream.</param>
/// <param name="truncateData"><c>true</c> if stream is truncated, default <c>false</c>.</param>
/// <returns>Number of item saved.</returns>
public static int SaveRecords<T>(IEnumerable<T> items, Stream stream, bool truncateData = false)
{
public static Int32 SaveRecords<T>(IEnumerable<T> items, Stream stream, Boolean truncateData = false) {
// truncate the file if it had data
if (truncateData && stream.Length > 0)
if(truncateData && stream.Length > 0) {
stream.SetLength(0);
}
using (var writer = new CsvWriter(stream))
{
using(CsvWriter writer = new CsvWriter(stream)) {
writer.WriteHeadings<T>();
writer.WriteObjects(items);
return (int)writer.Count;
return (Int32)writer.Count;
}
}
@@ -210,7 +199,7 @@
/// <param name="items">The items.</param>
/// <param name="filePath">The file path.</param>
/// <returns>Number of item saved.</returns>
public static int SaveRecords<T>(IEnumerable<T> items, string filePath) => SaveRecords(items, File.OpenWrite(filePath), true);
public static Int32 SaveRecords<T>(IEnumerable<T> items, String filePath) => SaveRecords(items, File.OpenWrite(filePath), true);
#endregion
@@ -222,8 +211,8 @@
/// If items are not string, the ToStringInvariant() method is called on them.
/// </summary>
/// <param name="items">The items.</param>
public void WriteLine(params object[] items)
=> WriteLine(items.Select(x => x == null ? string.Empty : x.ToStringInvariant()));
public void WriteLine(params Object[] items)
=> this.WriteLine(items.Select(x => x == null ? String.Empty : x.ToStringInvariant()));
/// <summary>
/// Writes a line of CSV text. Items are converted to strings.
@@ -231,67 +220,66 @@
/// If items are not string, the ToStringInvariant() method is called on them.
/// </summary>
/// <param name="items">The items.</param>
public void WriteLine(IEnumerable<object> items)
=> WriteLine(items.Select(x => x == null ? string.Empty : x.ToStringInvariant()));
public void WriteLine(IEnumerable<Object> items)
=> this.WriteLine(items.Select(x => x == null ? String.Empty : x.ToStringInvariant()));
/// <summary>
/// Writes a line of CSV text.
/// If items are found to be null, empty strings are written out.
/// </summary>
/// <param name="items">The items.</param>
public void WriteLine(params string[] items) => WriteLine((IEnumerable<string>) items);
public void WriteLine(params String[] items) => this.WriteLine((IEnumerable<String>)items);
/// <summary>
/// Writes a line of CSV text.
/// If items are found to be null, empty strings are written out.
/// </summary>
/// <param name="items">The items.</param>
public void WriteLine(IEnumerable<string> items)
{
lock (_syncLock)
{
var length = items.Count();
var separatorBytes = _encoding.GetBytes(new[] { SeparatorCharacter });
var endOfLineBytes = _encoding.GetBytes(NewLineSequence);
public void WriteLine(IEnumerable<String> items) {
lock(this._syncLock) {
Int32 length = items.Count();
Byte[] separatorBytes = this._encoding.GetBytes(new[] { this.SeparatorCharacter });
Byte[] endOfLineBytes = this._encoding.GetBytes(this.NewLineSequence);
// Declare state variables here to avoid recreation, allocation and
// reassignment in every loop
bool needsEnclosing;
string textValue;
byte[] output;
Boolean needsEnclosing;
String textValue;
Byte[] output;
for (var i = 0; i < length; i++)
{
for(Int32 i = 0; i < length; i++) {
textValue = items.ElementAt(i);
// 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
needsEnclosing = textValue.IndexOf(this.SeparatorCharacter) >= 0
|| textValue.IndexOf(this.EscapeCharacter) >= 0
|| textValue.IndexOf('\r') >= 0
|| textValue.IndexOf('\n') >= 0;
// Escape the escape characters by repeating them twice for every instance
textValue = textValue.Replace($"{EscapeCharacter}",
$"{EscapeCharacter}{EscapeCharacter}");
textValue = textValue.Replace($"{this.EscapeCharacter}",
$"{this.EscapeCharacter}{this.EscapeCharacter}");
// Enclose the text value if we need to
if (needsEnclosing)
textValue = string.Format($"{EscapeCharacter}{textValue}{EscapeCharacter}", textValue);
if(needsEnclosing) {
textValue = String.Format($"{this.EscapeCharacter}{textValue}{this.EscapeCharacter}", textValue);
}
// Get the bytes to write to the stream and write them
output = _encoding.GetBytes(textValue);
_outputStream.Write(output, 0, output.Length);
output = this._encoding.GetBytes(textValue);
this._outputStream.Write(output, 0, output.Length);
// only write a separator if we are moving in between values.
// the last value should not be written.
if (i < length - 1)
_outputStream.Write(separatorBytes, 0, separatorBytes.Length);
if(i < length - 1) {
this._outputStream.Write(separatorBytes, 0, separatorBytes.Length);
}
}
// output the newline sequence
_outputStream.Write(endOfLineBytes, 0, endOfLineBytes.Length);
_mCount += 1;
this._outputStream.Write(endOfLineBytes, 0, endOfLineBytes.Length);
this._mCount += 1;
}
}
@@ -307,23 +295,21 @@
/// </summary>
/// <param name="item">The item.</param>
/// <exception cref="System.ArgumentNullException">item.</exception>
public void WriteObject(object item)
{
if (item == null)
public void WriteObject(Object item) {
if(item == null) {
throw new ArgumentNullException(nameof(item));
}
lock (_syncLock)
{
switch (item)
{
lock(this._syncLock) {
switch(item) {
case IDictionary typedItem:
WriteLine(GetFilteredDictionary(typedItem));
this.WriteLine(this.GetFilteredDictionary(typedItem));
return;
case ICollection typedItem:
WriteLine(typedItem.Cast<object>());
this.WriteLine(typedItem.Cast<Object>());
return;
default:
WriteLine(GetFilteredTypeProperties(item.GetType())
this.WriteLine(this.GetFilteredTypeProperties(item.GetType())
.Select(x => x.ToFormattedString(item)));
break;
}
@@ -338,7 +324,7 @@
/// </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);
public void WriteObject<T>(T item) => this.WriteObject(item as Object);
/// <summary>
/// Writes a set of items, one per line and atomically by repeatedly calling the
@@ -347,12 +333,11 @@
/// </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);
public void WriteObjects<T>(IEnumerable<T> items) {
lock(this._syncLock) {
foreach(T item in items) {
this.WriteObject(item);
}
}
}
@@ -365,32 +350,32 @@
/// </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)
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);
IEnumerable<Object> properties = this.GetFilteredTypeProperties(type).Select(p => p.Name).Cast<Object>();
this.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));
public void WriteHeadings<T>() => this.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)
public void WriteHeadings(IDictionary dictionary) {
if(dictionary == null) {
throw new ArgumentNullException(nameof(dictionary));
}
WriteLine(GetFilteredDictionary(dictionary, true));
this.WriteLine(this.GetFilteredDictionary(dictionary, true));
}
#if NET452
@@ -400,15 +385,16 @@
/// <param name="item">The object to extract headings.</param>
/// <exception cref="ArgumentNullException">item</exception>
/// <exception cref="ArgumentException">Unable to cast dynamic object to a suitable dictionary - item</exception>
public void WriteHeadings(dynamic item)
{
if (item == null)
public void WriteHeadings(dynamic item) {
if(item == null) {
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));
}
WriteHeadings(dictionary);
this.WriteHeadings(dictionary);
}
#else
/// <summary>
@@ -430,47 +416,46 @@
#region IDisposable Support
/// <inheritdoc />
public void Dispose() => Dispose(true);
public void Dispose() => this.Dispose(true);
/// <summary>
/// Releases unmanaged and - optionally - managed resources.
/// </summary>
/// <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)
{
if (_isDisposing) return;
protected virtual void Dispose(Boolean disposeAlsoManaged) {
if(this._isDisposing) {
return;
}
if (disposeAlsoManaged)
{
if (_leaveStreamOpen == false)
{
_outputStream.Dispose();
if(disposeAlsoManaged) {
if(this._leaveStreamOpen == false) {
this._outputStream.Dispose();
}
}
_isDisposing = true;
this._isDisposing = true;
}
#endregion
#region Support Methods
private IEnumerable<string> GetFilteredDictionary(IDictionary dictionary, bool filterKeys = false)
private IEnumerable<String> GetFilteredDictionary(IDictionary dictionary, Boolean filterKeys = false)
=> dictionary
.Keys
.Cast<object>()
.Select(key => key == null ? string.Empty : key.ToStringInvariant())
.Where(stringKey => !IgnorePropertyNames.Contains(stringKey))
.Cast<Object>()
.Select(key => key == null ? String.Empty : key.ToStringInvariant())
.Where(stringKey => !this.IgnorePropertyNames.Contains(stringKey))
.Select(stringKey =>
filterKeys
? stringKey
: dictionary[stringKey] == null ? string.Empty : dictionary[stringKey].ToStringInvariant());
: dictionary[stringKey] == null ? String.Empty : dictionary[stringKey].ToStringInvariant());
private IEnumerable<PropertyInfo> GetFilteredTypeProperties(Type type)
=> TypeCache.Retrieve(type, t =>
t.GetProperties(BindingFlags.Public | BindingFlags.Instance)
.Where(p => p.CanRead))
.Where(p => !IgnorePropertyNames.Contains(p.Name));
.Where(p => !this.IgnorePropertyNames.Contains(p.Name));
#endregion
+69 -85
View File
@@ -1,149 +1,133 @@
namespace Unosquare.Swan.Formatters
{
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
internal class HumanizeJson
{
namespace Unosquare.Swan.Formatters {
internal class HumanizeJson {
private readonly StringBuilder _builder = new StringBuilder();
private readonly int _indent;
private readonly string _indentStr;
private readonly object _obj;
private readonly Int32 _indent;
private readonly String _indentStr;
private readonly Object _obj;
public HumanizeJson(object obj, int indent)
{
if (obj == null)
{
public HumanizeJson(Object obj, Int32 indent) {
if(obj == null) {
return;
}
_indent = indent;
_indentStr = new string(' ', indent * 4);
_obj = obj;
this._indent = indent;
this._indentStr = new String(' ', indent * 4);
this._obj = obj;
ParseObject();
this.ParseObject();
}
public string GetResult() => _builder == null ? string.Empty : _builder.ToString().TrimEnd();
public String GetResult() => this._builder == null ? String.Empty : this._builder.ToString().TrimEnd();
private void ParseObject()
{
switch (_obj)
{
case Dictionary<string, object> dictionary:
AppendDictionary(dictionary);
private void ParseObject() {
switch(this._obj) {
case Dictionary<String, Object> dictionary:
this.AppendDictionary(dictionary);
break;
case List<object> list:
AppendList(list);
case List<Object> list:
this.AppendList(list);
break;
default:
AppendString();
this.AppendString();
break;
}
}
private void AppendDictionary(Dictionary<string, object> objects)
{
foreach (var kvp in objects)
{
if (kvp.Value == null) continue;
private void AppendDictionary(Dictionary<String, Object> objects) {
foreach(KeyValuePair<String, Object> kvp in objects) {
if(kvp.Value == null) {
continue;
}
var writeOutput = false;
Boolean writeOutput = false;
switch (kvp.Value)
{
case Dictionary<string, object> valueDictionary:
if (valueDictionary.Count > 0)
{
switch(kvp.Value) {
case Dictionary<String, Object> valueDictionary:
if(valueDictionary.Count > 0) {
writeOutput = true;
_builder
.Append($"{_indentStr}{kvp.Key,-16}: object")
_ = this._builder
.Append($"{this._indentStr}{kvp.Key,-16}: object")
.AppendLine();
}
break;
case List<object> valueList:
if (valueList.Count > 0)
{
case List<Object> valueList:
if(valueList.Count > 0) {
writeOutput = true;
_builder
.Append($"{_indentStr}{kvp.Key,-16}: array[{valueList.Count}]")
_ = this._builder
.Append($"{this._indentStr}{kvp.Key,-16}: array[{valueList.Count}]")
.AppendLine();
}
break;
default:
writeOutput = true;
_builder.Append($"{_indentStr}{kvp.Key,-16}: ");
_ = this._builder.Append($"{this._indentStr}{kvp.Key,-16}: ");
break;
}
if (writeOutput)
_builder.AppendLine(new HumanizeJson(kvp.Value, _indent + 1).GetResult());
if(writeOutput) {
_ = this._builder.AppendLine(new HumanizeJson(kvp.Value, this._indent + 1).GetResult());
}
}
}
private void AppendList(List<object> objects)
{
var index = 0;
foreach (var value in objects)
{
var writeOutput = false;
private void AppendList(List<Object> objects) {
Int32 index = 0;
foreach(Object value in objects) {
Boolean writeOutput = false;
switch (value)
{
case Dictionary<string, object> valueDictionary:
if (valueDictionary.Count > 0)
{
switch(value) {
case Dictionary<String, Object> valueDictionary:
if(valueDictionary.Count > 0) {
writeOutput = true;
_builder
.Append($"{_indentStr}[{index}]: object")
_ = this._builder
.Append($"{this._indentStr}[{index}]: object")
.AppendLine();
}
break;
case List<object> valueList:
if (valueList.Count > 0)
{
case List<Object> valueList:
if(valueList.Count > 0) {
writeOutput = true;
_builder
.Append($"{_indentStr}[{index}]: array[{valueList.Count}]")
_ = this._builder
.Append($"{this._indentStr}[{index}]: array[{valueList.Count}]")
.AppendLine();
}
break;
default:
writeOutput = true;
_builder.Append($"{_indentStr}[{index}]: ");
_ = this._builder.Append($"{this._indentStr}[{index}]: ");
break;
}
index++;
if (writeOutput)
_builder.AppendLine(new HumanizeJson(value, _indent + 1).GetResult());
if(writeOutput) {
_ = this._builder.AppendLine(new HumanizeJson(value, this._indent + 1).GetResult());
}
}
}
private void AppendString()
{
var stringValue = _obj.ToString();
private void AppendString() {
String stringValue = this._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());
if(stringValue.Length + this._indentStr.Length > 96 || stringValue.IndexOf('\r') >= 0 ||
stringValue.IndexOf('\n') >= 0) {
_ = this._builder.AppendLine();
IEnumerable<String> stringLines = stringValue.ToLines().Select(l => l.Trim());
foreach (var line in stringLines)
{
_builder.AppendLine($"{_indentStr}{line}");
foreach(String line in stringLines) {
_ = this._builder.AppendLine($"{this._indentStr}{line}");
}
}
else
{
_builder.Append($"{stringValue}");
} else {
_ = this._builder.Append($"{stringValue}");
}
}
}
+138 -171
View File
@@ -1,14 +1,13 @@
namespace Unosquare.Swan.Formatters
{
using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using Attributes;
using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using Unosquare.Swan.Attributes;
namespace Unosquare.Swan.Formatters {
/// <summary>
/// A very simple, light-weight JSON library written by Mario
/// to teach Geo how things are done
@@ -16,46 +15,45 @@
/// This is an useful helper for small tasks but it doesn't represent a full-featured
/// serializer such as the beloved Json.NET.
/// </summary>
public static partial class Json
{
private class Converter
{
private static readonly ConcurrentDictionary<MemberInfo, string> MemberInfoNameCache =
new ConcurrentDictionary<MemberInfo, string>();
public static partial class Json {
private class Converter {
private static readonly ConcurrentDictionary<MemberInfo, String> MemberInfoNameCache =
new ConcurrentDictionary<MemberInfo, String>();
private static readonly ConcurrentDictionary<Type, Type> ListAddMethodCache = new ConcurrentDictionary<Type, Type>();
private readonly object _target;
private readonly Object _target;
private readonly Type _targetType;
private readonly bool _includeNonPublic;
private readonly Boolean _includeNonPublic;
private Converter(
object source,
Object source,
Type targetType,
ref object targetInstance,
bool includeNonPublic)
{
_targetType = targetInstance != null ? targetInstance.GetType() : targetType;
_includeNonPublic = includeNonPublic;
ref Object targetInstance,
Boolean includeNonPublic) {
this._targetType = targetInstance != null ? targetInstance.GetType() : targetType;
this._includeNonPublic = includeNonPublic;
if (source == null)
{
if(source == null) {
return;
}
var sourceType = source.GetType();
Type sourceType = source.GetType();
if (_targetType == null || _targetType == typeof(object)) _targetType = sourceType;
if (sourceType == _targetType)
{
_target = source;
if(this._targetType == null || this._targetType == typeof(Object)) {
this._targetType = sourceType;
}
if(sourceType == this._targetType) {
this._target = source;
return;
}
if (!TrySetInstance(targetInstance, source, ref _target))
if(!this.TrySetInstance(targetInstance, source, ref this._target)) {
return;
}
ResolveObject(source, ref _target);
this.ResolveObject(source, ref this._target);
}
/// <summary>
@@ -65,21 +63,17 @@
/// <param name="targetType">Type of the target.</param>
/// <param name="includeNonPublic">if set to <c>true</c> [include non public].</param>
/// <returns>The target object.</returns>
internal static object FromJsonResult(object source,
internal static Object FromJsonResult(Object source,
Type targetType,
bool includeNonPublic)
{
object nullRef = null;
Boolean includeNonPublic) {
Object nullRef = null;
return new Converter(source, targetType, ref nullRef, includeNonPublic).GetResult();
}
private static object FromJsonResult(object source,
private static Object FromJsonResult(Object source,
Type targetType,
ref object targetInstance,
bool includeNonPublic)
{
return new Converter(source, targetType, ref targetInstance, includeNonPublic).GetResult();
}
ref Object targetInstance,
Boolean includeNonPublic) => new Converter(source, targetType, ref targetInstance, includeNonPublic).GetResult();
private static Type GetAddMethodParameterType(Type targetType)
=> ListAddMethodCache.GetOrAdd(targetType,
@@ -89,243 +83,216 @@
.GetParameters()[0]
.ParameterType);
private static void GetByteArray(string sourceString, ref object target)
{
try
{
private static void GetByteArray(String sourceString, ref Object target) {
try {
target = Convert.FromBase64String(sourceString);
} // Try conversion from Base 64
catch
{
catch {
target = Encoding.UTF8.GetBytes(sourceString);
} // Get the string bytes in UTF8
}
private static object GetSourcePropertyValue(IDictionary<string, object> sourceProperties,
MemberInfo targetProperty)
{
var targetPropertyName = MemberInfoNameCache.GetOrAdd(
private static Object GetSourcePropertyValue(IDictionary<String, Object> sourceProperties,
MemberInfo targetProperty) {
String targetPropertyName = MemberInfoNameCache.GetOrAdd(
targetProperty,
x => Runtime.AttributeCache.RetrieveOne<JsonPropertyAttribute>(x)?.PropertyName ?? x.Name);
return sourceProperties.GetValueOrDefault(targetPropertyName);
}
private bool TrySetInstance(object targetInstance, object source, ref object target)
{
if (targetInstance == null)
{
private Boolean TrySetInstance(Object targetInstance, Object source, ref Object target) {
if(targetInstance == null) {
// Try to create a default instance
try
{
source.CreateTarget(_targetType, _includeNonPublic, ref target);
}
catch
{
try {
source.CreateTarget(this._targetType, this._includeNonPublic, ref target);
} catch {
return false;
}
}
else
{
} else {
target = targetInstance;
}
return true;
}
private object GetResult() => _target ?? _targetType.GetDefault();
private Object GetResult() => this._target ?? this._targetType.GetDefault();
private void ResolveObject(object source, ref object target)
{
switch (source)
{
private void ResolveObject(Object source, ref Object target) {
switch(source) {
// Case 0: Special Cases Handling (Source and Target are of specific convertible types)
// Case 0.1: Source is string, Target is byte[]
case string sourceString when _targetType == typeof(byte[]):
case String sourceString when this._targetType == typeof(Byte[]):
GetByteArray(sourceString, ref target);
break;
// Case 1.1: Source is Dictionary, Target is IDictionary
case Dictionary<string, object> sourceProperties when target is IDictionary targetDictionary:
PopulateDictionary(sourceProperties, targetDictionary);
case Dictionary<String, Object> sourceProperties when target is IDictionary targetDictionary:
this.PopulateDictionary(sourceProperties, targetDictionary);
break;
// Case 1.2: Source is Dictionary, Target is not IDictionary (i.e. it is a complex type)
case Dictionary<string, object> sourceProperties:
PopulateObject(sourceProperties);
case Dictionary<String, Object> sourceProperties:
this.PopulateObject(sourceProperties);
break;
// Case 2.1: Source is List, Target is Array
case List<object> sourceList when target is Array targetArray:
PopulateArray(sourceList, targetArray);
case List<Object> sourceList when target is Array targetArray:
this.PopulateArray(sourceList, targetArray);
break;
// Case 2.2: Source is List, Target is IList
case List<object> sourceList when target is IList targetList:
PopulateIList(sourceList, targetList);
case List<Object> sourceList when target is IList targetList:
this.PopulateIList(sourceList, targetList);
break;
// Case 3: Source is a simple type; Attempt conversion
default:
var sourceStringValue = source.ToStringInvariant();
String sourceStringValue = source.ToStringInvariant();
// Handle basic types or enumerations if not
if (!_targetType.TryParseBasicType(sourceStringValue, out target))
GetEnumValue(sourceStringValue, ref target);
if(!this._targetType.TryParseBasicType(sourceStringValue, out target)) {
this.GetEnumValue(sourceStringValue, ref target);
}
break;
}
}
private void PopulateIList(IList<object> objects, IList list)
{
var parameterType = GetAddMethodParameterType(_targetType);
if (parameterType == null) return;
private void PopulateIList(IList<Object> objects, IList list) {
Type parameterType = GetAddMethodParameterType(this._targetType);
if(parameterType == null) {
return;
}
foreach (var item in objects)
{
try
{
list.Add(FromJsonResult(
foreach(Object item in objects) {
try {
_ = list.Add(FromJsonResult(
item,
parameterType,
_includeNonPublic));
}
catch
{
this._includeNonPublic));
} catch {
// ignored
}
}
}
private void PopulateArray(IList<object> objects, Array array)
{
var elementType = _targetType.GetElementType();
private void PopulateArray(IList<Object> objects, Array array) {
Type elementType = this._targetType.GetElementType();
for (var i = 0; i < objects.Count; i++)
{
try
{
var targetItem = FromJsonResult(
for(Int32 i = 0; i < objects.Count; i++) {
try {
Object targetItem = FromJsonResult(
objects[i],
elementType,
_includeNonPublic);
this._includeNonPublic);
array.SetValue(targetItem, i);
}
catch
{
} catch {
// ignored
}
}
}
private void GetEnumValue(string sourceStringValue, ref object target)
{
var enumType = Nullable.GetUnderlyingType(_targetType);
if (enumType == null && _targetType.GetTypeInfo().IsEnum) enumType = _targetType;
if (enumType == null) return;
private void GetEnumValue(String sourceStringValue, ref Object target) {
Type enumType = Nullable.GetUnderlyingType(this._targetType);
if(enumType == null && this._targetType.GetTypeInfo().IsEnum) {
enumType = this._targetType;
}
try
{
if(enumType == null) {
return;
}
try {
target = Enum.Parse(enumType, sourceStringValue);
}
catch
{
} catch {
// ignored
}
}
private void PopulateDictionary(IDictionary<string, object> sourceProperties, IDictionary targetDictionary)
{
private void PopulateDictionary(IDictionary<String, Object> sourceProperties, IDictionary targetDictionary) {
// find the add method of the target dictionary
var addMethod = _targetType.GetMethods()
MethodInfo addMethod = this._targetType.GetMethods()
.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;
var addMethodParameters = addMethod.GetParameters();
if (addMethodParameters[0].ParameterType != typeof(string)) return;
if(addMethod == null) {
return;
}
ParameterInfo[] addMethodParameters = addMethod.GetParameters();
if(addMethodParameters[0].ParameterType != typeof(String)) {
return;
}
// Retrieve the target entry type
var targetEntryType = addMethodParameters[1].ParameterType;
Type targetEntryType = addMethodParameters[1].ParameterType;
// Add the items to the target dictionary
foreach (var sourceProperty in sourceProperties)
{
try
{
var targetEntryValue = FromJsonResult(
foreach(KeyValuePair<String, Object> sourceProperty in sourceProperties) {
try {
Object targetEntryValue = FromJsonResult(
sourceProperty.Value,
targetEntryType,
_includeNonPublic);
this._includeNonPublic);
targetDictionary.Add(sourceProperty.Key, targetEntryValue);
}
catch
{
} catch {
// ignored
}
}
}
private void PopulateObject(IDictionary<string, object> sourceProperties)
{
if (_targetType.IsValueType())
{
PopulateFields(sourceProperties);
private void PopulateObject(IDictionary<String, Object> sourceProperties) {
if(this._targetType.IsValueType()) {
this.PopulateFields(sourceProperties);
}
PopulateProperties(sourceProperties);
this.PopulateProperties(sourceProperties);
}
private void PopulateProperties(IDictionary<string, object> sourceProperties)
{
var properties = PropertyTypeCache.RetrieveFilteredProperties(_targetType, false, p => p.CanWrite);
private void PopulateProperties(IDictionary<String, Object> sourceProperties) {
IEnumerable<PropertyInfo> properties = PropertyTypeCache.RetrieveFilteredProperties(this._targetType, false, p => p.CanWrite);
foreach (var property in properties)
{
var sourcePropertyValue = GetSourcePropertyValue(sourceProperties, property);
if (sourcePropertyValue == null) continue;
foreach(PropertyInfo property in properties) {
Object sourcePropertyValue = GetSourcePropertyValue(sourceProperties, property);
if(sourcePropertyValue == null) {
continue;
}
try
{
var currentPropertyValue = !property.PropertyType.IsArray
? property.GetCacheGetMethod(_includeNonPublic)(_target)
try {
Object currentPropertyValue = !property.PropertyType.IsArray
? property.GetCacheGetMethod(this._includeNonPublic)(this._target)
: null;
var targetPropertyValue = FromJsonResult(
Object targetPropertyValue = FromJsonResult(
sourcePropertyValue,
property.PropertyType,
ref currentPropertyValue,
_includeNonPublic);
this._includeNonPublic);
property.GetCacheSetMethod(_includeNonPublic)(_target, new[] { targetPropertyValue });
}
catch
{
property.GetCacheSetMethod(this._includeNonPublic)(this._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;
private void PopulateFields(IDictionary<String, Object> sourceProperties) {
foreach(FieldInfo field in FieldTypeCache.RetrieveAllFields(this._targetType)) {
Object sourcePropertyValue = GetSourcePropertyValue(sourceProperties, field);
if(sourcePropertyValue == null) {
continue;
}
var targetPropertyValue = FromJsonResult(
Object targetPropertyValue = FromJsonResult(
sourcePropertyValue,
field.FieldType,
_includeNonPublic);
this._includeNonPublic);
try
{
field.SetValue(_target, targetPropertyValue);
}
catch
{
try {
field.SetValue(this._target, targetPropertyValue);
} catch {
// ignored
}
}
@@ -1,9 +1,8 @@
namespace Unosquare.Swan.Formatters
{
using System;
using System.Collections.Generic;
using System.Text;
using System;
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
@@ -11,80 +10,76 @@
/// 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
{
public partial class Json {
/// <summary>
/// A simple JSON Deserializer.
/// </summary>
private class Deserializer
{
private class Deserializer {
#region State Variables
private readonly object _result;
private readonly Dictionary<string, object> _resultObject;
private readonly List<object> _resultArray;
private readonly Object _result;
private readonly Dictionary<String, Object> _resultObject;
private readonly List<Object> _resultArray;
private readonly ReadState _state = ReadState.WaitingForRootOpen;
private readonly string _currentFieldName;
private readonly string _json;
private readonly String _currentFieldName;
private readonly String _json;
private int _index;
private Int32 _index;
#endregion
private Deserializer(string json, int startIndex)
{
_json = json;
private Deserializer(String json, Int32 startIndex) {
this._json = json;
for (_index = startIndex; _index < _json.Length; _index++)
{
for(this._index = startIndex; this._index < this._json.Length; this._index++) {
#region Wait for { or [
if (_state == ReadState.WaitingForRootOpen)
{
if (char.IsWhiteSpace(_json, _index)) continue;
if (_json[_index] == OpenObjectChar)
{
_resultObject = new Dictionary<string, object>();
_state = ReadState.WaitingForField;
if(this._state == ReadState.WaitingForRootOpen) {
if(Char.IsWhiteSpace(this._json, this._index)) {
continue;
}
if (_json[_index] == OpenArrayChar)
{
_resultArray = new List<object>();
_state = ReadState.WaitingForValue;
if(this._json[this._index] == OpenObjectChar) {
this._resultObject = new Dictionary<String, Object>();
this._state = ReadState.WaitingForField;
continue;
}
throw CreateParserException($"'{OpenObjectChar}' or '{OpenArrayChar}'");
if(this._json[this._index] == OpenArrayChar) {
this._resultArray = new List<Object>();
this._state = ReadState.WaitingForValue;
continue;
}
throw this.CreateParserException($"'{OpenObjectChar}' or '{OpenArrayChar}'");
}
#endregion
#region Wait for opening field " (only applies for object results)
if (_state == ReadState.WaitingForField)
{
if (char.IsWhiteSpace(_json, _index)) continue;
if(this._state == ReadState.WaitingForField) {
if(Char.IsWhiteSpace(this._json, this._index)) {
continue;
}
// Handle empty arrays and empty objects
if ((_resultObject != null && _json[_index] == CloseObjectChar)
|| (_resultArray != null && _json[_index] == CloseArrayChar))
{
_result = _resultObject ?? _resultArray as object;
if(this._resultObject != null && this._json[this._index] == CloseObjectChar
|| this._resultArray != null && this._json[this._index] == CloseArrayChar) {
this._result = this._resultObject ?? this._resultArray as Object;
return;
}
if (_json[_index] != StringQuotedChar)
throw CreateParserException($"'{StringQuotedChar}'");
if(this._json[this._index] != StringQuotedChar) {
throw this.CreateParserException($"'{StringQuotedChar}'");
}
var charCount = GetFieldNameCount();
Int32 charCount = this.GetFieldNameCount();
_currentFieldName = Unescape(_json.SliceLength(_index + 1, charCount));
_index += charCount + 1;
_state = ReadState.WaitingForColon;
this._currentFieldName = Unescape(this._json.SliceLength(this._index + 1, charCount));
this._index += charCount + 1;
this._state = ReadState.WaitingForColon;
continue;
}
@@ -92,14 +87,16 @@
#region Wait for field-value separator : (only applies for object results
if (_state == ReadState.WaitingForColon)
{
if (char.IsWhiteSpace(_json, _index)) continue;
if(this._state == ReadState.WaitingForColon) {
if(Char.IsWhiteSpace(this._json, this._index)) {
continue;
}
if (_json[_index] != ValueSeparatorChar)
throw CreateParserException($"'{ValueSeparatorChar}'");
if(this._json[this._index] != ValueSeparatorChar) {
throw this.CreateParserException($"'{ValueSeparatorChar}'");
}
_state = ReadState.WaitingForValue;
this._state = ReadState.WaitingForValue;
continue;
}
@@ -107,49 +104,48 @@
#region Wait for and Parse the value
if (_state == ReadState.WaitingForValue)
{
if (char.IsWhiteSpace(_json, _index)) continue;
if(this._state == ReadState.WaitingForValue) {
if(Char.IsWhiteSpace(this._json, this._index)) {
continue;
}
// Handle empty arrays and empty objects
if ((_resultObject != null && _json[_index] == CloseObjectChar)
|| (_resultArray != null && _json[_index] == CloseArrayChar))
{
_result = _resultObject ?? _resultArray as object;
if(this._resultObject != null && this._json[this._index] == CloseObjectChar
|| this._resultArray != null && this._json[this._index] == CloseArrayChar) {
this._result = this._resultObject ?? this._resultArray as Object;
return;
}
// determine the value based on what it starts with
switch (_json[_index])
{
switch(this._json[this._index]) {
case StringQuotedChar: // expect a string
ExtractStringQuoted();
this.ExtractStringQuoted();
break;
case OpenObjectChar: // expect object
case OpenArrayChar: // expect array
ExtractObject();
this.ExtractObject();
break;
case 't': // expect true
ExtractConstant(TrueLiteral, true);
this.ExtractConstant(TrueLiteral, true);
break;
case 'f': // expect false
ExtractConstant(FalseLiteral, false);
this.ExtractConstant(FalseLiteral, false);
break;
case 'n': // expect null
ExtractConstant(NullLiteral, null);
this.ExtractConstant(NullLiteral, null);
break;
default: // expect number
ExtractNumber();
this.ExtractNumber();
break;
}
_currentFieldName = null;
_state = ReadState.WaitingForNextOrRootClose;
this._currentFieldName = null;
this._state = ReadState.WaitingForNextOrRootClose;
continue;
}
@@ -157,84 +153,83 @@
#region Wait for closing ], } or an additional field or value ,
if (_state != ReadState.WaitingForNextOrRootClose) continue;
if (char.IsWhiteSpace(_json, _index)) continue;
if (_json[_index] == FieldSeparatorChar)
{
if (_resultObject != null)
{
_state = ReadState.WaitingForField;
_currentFieldName = null;
if(this._state != ReadState.WaitingForNextOrRootClose) {
continue;
}
_state = ReadState.WaitingForValue;
if(Char.IsWhiteSpace(this._json, this._index)) {
continue;
}
if ((_resultObject != null && _json[_index] == CloseObjectChar) ||
(_resultArray != null && _json[_index] == CloseArrayChar))
{
_result = _resultObject ?? _resultArray as object;
if(this._json[this._index] == FieldSeparatorChar) {
if(this._resultObject != null) {
this._state = ReadState.WaitingForField;
this._currentFieldName = null;
continue;
}
this._state = ReadState.WaitingForValue;
continue;
}
if(this._resultObject != null && this._json[this._index] == CloseObjectChar ||
this._resultArray != null && this._json[this._index] == CloseArrayChar) {
this._result = this._resultObject ?? this._resultArray as Object;
return;
}
throw CreateParserException($"'{FieldSeparatorChar}' '{CloseObjectChar}' or '{CloseArrayChar}'");
throw this.CreateParserException($"'{FieldSeparatorChar}' '{CloseObjectChar}' or '{CloseArrayChar}'");
#endregion
}
}
internal static object DeserializeInternal(string json) => new Deserializer(json, 0)._result;
internal static Object DeserializeInternal(String json) => new Deserializer(json, 0)._result;
private static string Unescape(string str)
{
private static String Unescape(String str) {
// check if we need to unescape at all
if (str.IndexOf(StringEscapeChar) < 0)
if(str.IndexOf(StringEscapeChar) < 0) {
return str;
}
var builder = new StringBuilder(str.Length);
for (var i = 0; i < str.Length; i++)
{
if (str[i] != StringEscapeChar)
{
builder.Append(str[i]);
StringBuilder builder = new StringBuilder(str.Length);
for(Int32 i = 0; i < str.Length; i++) {
if(str[i] != StringEscapeChar) {
_ = builder.Append(str[i]);
continue;
}
if (i + 1 > str.Length - 1)
if(i + 1 > str.Length - 1) {
break;
}
// escape sequence begins here
switch (str[i + 1])
{
switch(str[i + 1]) {
case 'u':
i = ExtractEscapeSequence(str, i, builder);
break;
case 'b':
builder.Append('\b');
_ = builder.Append('\b');
i += 1;
break;
case 't':
builder.Append('\t');
_ = builder.Append('\t');
i += 1;
break;
case 'n':
builder.Append('\n');
_ = builder.Append('\n');
i += 1;
break;
case 'f':
builder.Append('\f');
_ = builder.Append('\f');
i += 1;
break;
case 'r':
builder.Append('\r');
_ = builder.Append('\r');
i += 1;
break;
default:
builder.Append(str[i + 1]);
_ = builder.Append(str[i + 1]);
i += 1;
break;
}
@@ -243,30 +238,27 @@
return builder.ToString();
}
private static int ExtractEscapeSequence(string str, int i, StringBuilder builder)
{
var startIndex = i + 2;
var endIndex = i + 5;
if (endIndex > str.Length - 1)
{
builder.Append(str[i + 1]);
private static Int32 ExtractEscapeSequence(String str, Int32 i, StringBuilder builder) {
Int32 startIndex = i + 2;
Int32 endIndex = i + 5;
if(endIndex > str.Length - 1) {
_ = builder.Append(str[i + 1]);
i += 1;
return i;
}
var hexCode = str.Slice(startIndex, endIndex).ConvertHexadecimalToBytes();
builder.Append(Encoding.BigEndianUnicode.GetChars(hexCode));
Byte[] hexCode = str.Slice(startIndex, endIndex).ConvertHexadecimalToBytes();
_ = builder.Append(Encoding.BigEndianUnicode.GetChars(hexCode));
i += 5;
return i;
}
private int GetFieldNameCount()
{
var charCount = 0;
for (var j = _index + 1; j < _json.Length; j++)
{
if (_json[j] == StringQuotedChar && _json[j - 1] != StringEscapeChar)
private Int32 GetFieldNameCount() {
Int32 charCount = 0;
for(Int32 j = this._index + 1; j < this._json.Length; j++) {
if(this._json[j] == StringQuotedChar && this._json[j - 1] != StringEscapeChar) {
break;
}
charCount++;
}
@@ -274,95 +266,95 @@
return charCount;
}
private void ExtractObject()
{
private void ExtractObject() {
// Extract and set the value
var deserializer = new Deserializer(_json, _index);
Deserializer deserializer = new Deserializer(this._json, this._index);
if (_currentFieldName != null)
_resultObject[_currentFieldName] = deserializer._result;
else
_resultArray.Add(deserializer._result);
_index = deserializer._index;
if(this._currentFieldName != null) {
this._resultObject[this._currentFieldName] = deserializer._result;
} else {
this._resultArray.Add(deserializer._result);
}
private void ExtractNumber()
{
var charCount = 0;
for (var j = _index; j < _json.Length; j++)
{
if (char.IsWhiteSpace(_json[j]) || _json[j] == FieldSeparatorChar
|| (_resultObject != null && _json[j] == CloseObjectChar)
|| (_resultArray != null && _json[j] == CloseArrayChar))
this._index = deserializer._index;
}
private void ExtractNumber() {
Int32 charCount = 0;
for(Int32 j = this._index; j < this._json.Length; j++) {
if(Char.IsWhiteSpace(this._json[j]) || this._json[j] == FieldSeparatorChar
|| this._resultObject != null && this._json[j] == CloseObjectChar
|| this._resultArray != null && this._json[j] == CloseArrayChar) {
break;
}
charCount++;
}
// Extract and set the value
var stringValue = _json.SliceLength(_index, charCount);
String stringValue = this._json.SliceLength(this._index, charCount);
if (decimal.TryParse(stringValue, out var value) == false)
throw CreateParserException("[number]");
if (_currentFieldName != null)
_resultObject[_currentFieldName] = value;
else
_resultArray.Add(value);
_index += charCount - 1;
if(Decimal.TryParse(stringValue, out Decimal value) == false) {
throw this.CreateParserException("[number]");
}
private void ExtractConstant(string boolValue, bool? value)
{
if (!_json.SliceLength(_index, boolValue.Length).Equals(boolValue))
throw CreateParserException($"'{ValueSeparatorChar}'");
if(this._currentFieldName != null) {
this._resultObject[this._currentFieldName] = value;
} else {
this._resultArray.Add(value);
}
this._index += charCount - 1;
}
private void ExtractConstant(String boolValue, Boolean? value) {
if(!this._json.SliceLength(this._index, boolValue.Length).Equals(boolValue)) {
throw this.CreateParserException($"'{ValueSeparatorChar}'");
}
// Extract and set the value
if (_currentFieldName != null)
_resultObject[_currentFieldName] = value;
else
_resultArray.Add(value);
_index += boolValue.Length - 1;
if(this._currentFieldName != null) {
this._resultObject[this._currentFieldName] = value;
} else {
this._resultArray.Add(value);
}
private void ExtractStringQuoted()
{
var charCount = 0;
var escapeCharFound = false;
for (var j = _index + 1; j < _json.Length; j++)
{
if (_json[j] == StringQuotedChar && !escapeCharFound)
break;
this._index += boolValue.Length - 1;
}
escapeCharFound = _json[j] == StringEscapeChar && !escapeCharFound;
private void ExtractStringQuoted() {
Int32 charCount = 0;
Boolean escapeCharFound = false;
for(Int32 j = this._index + 1; j < this._json.Length; j++) {
if(this._json[j] == StringQuotedChar && !escapeCharFound) {
break;
}
escapeCharFound = this._json[j] == StringEscapeChar && !escapeCharFound;
charCount++;
}
// Extract and set the value
var value = Unescape(_json.SliceLength(_index + 1, charCount));
if (_currentFieldName != null)
_resultObject[_currentFieldName] = value;
else
_resultArray.Add(value);
_index += charCount + 1;
String value = Unescape(this._json.SliceLength(this._index + 1, charCount));
if(this._currentFieldName != null) {
this._resultObject[this._currentFieldName] = value;
} else {
this._resultArray.Add(value);
}
private FormatException CreateParserException(string expected)
{
var textPosition = _json.TextPositionAt(_index);
this._index += charCount + 1;
}
private FormatException CreateParserException(String expected) {
Tuple<Int32, Int32> textPosition = this._json.TextPositionAt(this._index);
return new FormatException(
$"Parser error (Line {textPosition.Item1}, Col {textPosition.Item2}, State {_state}): Expected {expected} but got '{_json[_index]}'.");
$"Parser error (Line {textPosition.Item1}, Col {textPosition.Item2}, State {this._state}): Expected {expected} but got '{this._json[this._index]}'.");
}
/// <summary>
/// Defines the different JSON read states.
/// </summary>
private enum ReadState
{
private enum ReadState {
WaitingForRootOpen,
WaitingForField,
WaitingForColon,
+152 -164
View File
@@ -1,12 +1,11 @@
namespace Unosquare.Swan.Formatters
{
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
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
@@ -14,21 +13,19 @@
/// 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
{
public partial class Json {
/// <summary>
/// A simple JSON serializer.
/// </summary>
private class Serializer
{
private class Serializer {
#region Private Declarations
private static readonly Dictionary<int, string> IndentStrings = new Dictionary<int, string>();
private static readonly Dictionary<Int32, String> IndentStrings = new Dictionary<Int32, String>();
private readonly SerializerOptions _options;
private readonly string _result;
private readonly String _result;
private readonly StringBuilder _builder;
private readonly string _lastCommaSearch;
private readonly String _lastCommaSearch;
#endregion
@@ -40,74 +37,66 @@
/// <param name="obj">The object.</param>
/// <param name="depth">The depth.</param>
/// <param name="options">The options.</param>
private Serializer(object obj, int depth, SerializerOptions options)
{
if (depth > 20)
{
private Serializer(Object obj, Int32 depth, SerializerOptions options) {
if(depth > 20) {
throw new InvalidOperationException(
"The max depth (20) has been reached. Serializer can not continue.");
}
// Basic Type Handling (nulls, strings, number, date and bool)
_result = ResolveBasicType(obj);
this._result = ResolveBasicType(obj);
if (string.IsNullOrWhiteSpace(_result) == false)
if(String.IsNullOrWhiteSpace(this._result) == false) {
return;
}
_options = options;
_lastCommaSearch = FieldSeparatorChar + (_options.Format ? Environment.NewLine : string.Empty);
this._options = options;
this._lastCommaSearch = FieldSeparatorChar + (this._options.Format ? Environment.NewLine : String.Empty);
// Handle circular references correctly and avoid them
if (options.IsObjectPresent(obj))
{
_result = $"{{ \"$circref\": \"{Escape(obj.GetHashCode().ToStringInvariant(), false)}\" }}";
if(options.IsObjectPresent(obj)) {
this._result = $"{{ \"$circref\": \"{Escape(obj.GetHashCode().ToStringInvariant(), false)}\" }}";
return;
}
// At this point, we will need to construct the object with a StringBuilder.
_builder = new StringBuilder();
this._builder = new StringBuilder();
switch (obj)
{
switch(obj) {
case IDictionary itemsZero when itemsZero.Count == 0:
_result = EmptyObjectLiteral;
this._result = EmptyObjectLiteral;
break;
case IDictionary items:
_result = ResolveDictionary(items, depth);
this._result = this.ResolveDictionary(items, depth);
break;
case IEnumerable enumerableZero when !enumerableZero.Cast<object>().Any():
_result = EmptyArrayLiteral;
case IEnumerable enumerableZero when !enumerableZero.Cast<Object>().Any():
this._result = EmptyArrayLiteral;
break;
case IEnumerable enumerableBytes when enumerableBytes is byte[] bytes:
_result = Serialize(bytes.ToBase64(), depth, _options);
case IEnumerable enumerableBytes when enumerableBytes is Byte[] bytes:
this._result = Serialize(bytes.ToBase64(), depth, this._options);
break;
case IEnumerable enumerable:
_result = ResolveEnumerable(enumerable, depth);
this._result = this.ResolveEnumerable(enumerable, depth);
break;
default:
_result = ResolveObject(obj, depth);
this._result = this.ResolveObject(obj, depth);
break;
}
}
internal static string Serialize(object obj, int depth, SerializerOptions options)
{
return new Serializer(obj, depth, options)._result;
}
internal static String Serialize(Object obj, Int32 depth, SerializerOptions options) => new Serializer(obj, depth, options)._result;
#endregion
#region Helper Methods
private static string ResolveBasicType(object obj)
{
switch (obj)
{
private static String ResolveBasicType(Object obj) {
switch(obj) {
case null:
return NullLiteral;
case string s:
case String s:
return Escape(s, true);
case bool b:
case Boolean b:
return b ? TrueLiteral : FalseLiteral;
case Type _:
case Assembly _:
@@ -118,81 +107,84 @@
case DateTime d:
return $"{StringQuotedChar}{d:s}{StringQuotedChar}";
default:
var targetType = obj.GetType();
Type targetType = obj.GetType();
if (!Definitions.BasicTypesInfo.ContainsKey(targetType))
return string.Empty;
if(!Definitions.BasicTypesInfo.ContainsKey(targetType)) {
return String.Empty;
}
var escapedValue = Escape(Definitions.BasicTypesInfo[targetType].ToStringInvariant(obj), false);
String escapedValue = Escape(Definitions.BasicTypesInfo[targetType].ToStringInvariant(obj), false);
return decimal.TryParse(escapedValue, out _)
return Decimal.TryParse(escapedValue, out _)
? $"{escapedValue}"
: $"{StringQuotedChar}{escapedValue}{StringQuotedChar}";
}
}
private static bool IsNonEmptyJsonArrayOrObject(string serialized)
{
if (serialized.Equals(EmptyObjectLiteral) || serialized.Equals(EmptyArrayLiteral)) return false;
private static Boolean IsNonEmptyJsonArrayOrObject(String serialized) {
if(serialized.Equals(EmptyObjectLiteral) || serialized.Equals(EmptyArrayLiteral)) {
return false;
}
// find the first position the character is not a space
return serialized.Where(c => c != ' ').Select(c => c == OpenObjectChar || c == OpenArrayChar).FirstOrDefault();
}
private static string Escape(string str, bool quoted)
{
if (str == null)
return string.Empty;
private static String Escape(String str, Boolean quoted) {
if(str == null) {
return String.Empty;
}
StringBuilder builder = new StringBuilder(str.Length * 2);
if(quoted) {
_ = builder.Append(StringQuotedChar);
}
var builder = new StringBuilder(str.Length * 2);
if (quoted) builder.Append(StringQuotedChar);
Escape(str, builder);
if (quoted) builder.Append(StringQuotedChar);
if(quoted) {
_ = builder.Append(StringQuotedChar);
}
return builder.ToString();
}
private static void Escape(string str, StringBuilder builder)
{
foreach (var currentChar in str)
{
switch (currentChar)
{
private static void Escape(String str, StringBuilder builder) {
foreach(Char currentChar in str) {
switch(currentChar) {
case '\\':
case '"':
case '/':
builder
_ = builder
.Append('\\')
.Append(currentChar);
break;
case '\b':
builder.Append("\\b");
_ = builder.Append("\\b");
break;
case '\t':
builder.Append("\\t");
_ = builder.Append("\\t");
break;
case '\n':
builder.Append("\\n");
_ = builder.Append("\\n");
break;
case '\f':
builder.Append("\\f");
_ = builder.Append("\\f");
break;
case '\r':
builder.Append("\\r");
_ = builder.Append("\\r");
break;
default:
if (currentChar < ' ')
{
var escapeBytes = BitConverter.GetBytes((ushort)currentChar);
if (BitConverter.IsLittleEndian == false)
if(currentChar < ' ') {
Byte[] escapeBytes = BitConverter.GetBytes((UInt16)currentChar);
if(BitConverter.IsLittleEndian == false) {
Array.Reverse(escapeBytes);
}
builder.Append("\\u")
_ = builder.Append("\\u")
.Append(escapeBytes[1].ToString("X").PadLeft(2, '0'))
.Append(escapeBytes[0].ToString("X").PadLeft(2, '0'));
}
else
{
builder.Append(currentChar);
} else {
_ = builder.Append(currentChar);
}
break;
@@ -200,29 +192,25 @@
}
}
private Dictionary<string, object> CreateDictionary(
Dictionary<string, MemberInfo> fields,
string targetType,
object target)
{
private Dictionary<String, Object> CreateDictionary(
Dictionary<String, MemberInfo> fields,
String targetType,
Object target) {
// Create the dictionary and extract the properties
var objectDictionary = new Dictionary<string, object>();
Dictionary<String, Object> objectDictionary = new Dictionary<String, Object>();
if (string.IsNullOrWhiteSpace(_options.TypeSpecifier) == false)
objectDictionary[_options.TypeSpecifier] = targetType;
if(String.IsNullOrWhiteSpace(this._options.TypeSpecifier) == false) {
objectDictionary[this._options.TypeSpecifier] = targetType;
}
foreach (var field in fields)
{
foreach(KeyValuePair<String, MemberInfo> field in fields) {
// Build the dictionary using property names and values
// Note: used to be: property.GetValue(target); but we would be reading private properties
try
{
try {
objectDictionary[field.Key] = field.Value is PropertyInfo property
? property.GetCacheGetMethod(_options.IncludeNonPublic)(target)
? property.GetCacheGetMethod(this._options.IncludeNonPublic)(target)
: (field.Value as FieldInfo)?.GetValue(target);
}
catch
{
} catch {
/* ignored */
}
}
@@ -230,127 +218,127 @@
return objectDictionary;
}
private string ResolveDictionary(IDictionary items, int depth)
{
Append(OpenObjectChar, depth);
AppendLine();
private String ResolveDictionary(IDictionary items, Int32 depth) {
this.Append(OpenObjectChar, depth);
this.AppendLine();
// Iterate through the elements and output recursively
var writeCount = 0;
foreach (var key in items.Keys)
{
Int32 writeCount = 0;
foreach(Object key in items.Keys) {
// Serialize and append the key (first char indented)
Append(StringQuotedChar, depth + 1);
Escape(key.ToString(), _builder);
_builder
this.Append(StringQuotedChar, depth + 1);
Escape(key.ToString(), this._builder);
_ = this._builder
.Append(StringQuotedChar)
.Append(ValueSeparatorChar)
.Append(" ");
// 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();
Append(serializedValue, 0);
if(IsNonEmptyJsonArrayOrObject(serializedValue)) {
this.AppendLine();
}
this.Append(serializedValue, 0);
// Add a comma and start a new line -- We will remove the last one when we are done writing the elements
Append(FieldSeparatorChar, 0);
AppendLine();
this.Append(FieldSeparatorChar, 0);
this.AppendLine();
writeCount++;
}
// Output the end of the object and set the result
RemoveLastComma();
Append(CloseObjectChar, writeCount > 0 ? depth : 0);
return _builder.ToString();
this.RemoveLastComma();
this.Append(CloseObjectChar, writeCount > 0 ? depth : 0);
return this._builder.ToString();
}
private string ResolveObject(object target, int depth)
{
var targetType = target.GetType();
var fields = _options.GetProperties(targetType);
private String ResolveObject(Object target, Int32 depth) {
Type targetType = target.GetType();
Dictionary<String, MemberInfo> fields = this._options.GetProperties(targetType);
if (fields.Count == 0 && string.IsNullOrWhiteSpace(_options.TypeSpecifier))
if(fields.Count == 0 && String.IsNullOrWhiteSpace(this._options.TypeSpecifier)) {
return EmptyObjectLiteral;
}
// If we arrive here, then we convert the object into a
// dictionary of property names and values and call the serialization
// function again
var objectDictionary = CreateDictionary(fields, targetType.ToString(), target);
Dictionary<String, Object> objectDictionary = this.CreateDictionary(fields, targetType.ToString(), target);
return Serialize(objectDictionary, depth, _options);
return Serialize(objectDictionary, depth, this._options);
}
private string ResolveEnumerable(IEnumerable target, int depth)
{
private String ResolveEnumerable(IEnumerable target, Int32 depth) {
// Cast the items as a generic object array
var items = target.Cast<object>();
IEnumerable<Object> items = target.Cast<Object>();
Append(OpenArrayChar, depth);
AppendLine();
this.Append(OpenArrayChar, depth);
this.AppendLine();
// Iterate through the elements and output recursively
var writeCount = 0;
foreach (var entry in items)
{
var serializedValue = Serialize(entry, depth + 1, _options);
Int32 writeCount = 0;
foreach(Object entry in items) {
String serializedValue = Serialize(entry, depth + 1, this._options);
if (IsNonEmptyJsonArrayOrObject(serializedValue))
Append(serializedValue, 0);
else
Append(serializedValue, depth + 1);
if(IsNonEmptyJsonArrayOrObject(serializedValue)) {
this.Append(serializedValue, 0);
} else {
this.Append(serializedValue, depth + 1);
}
Append(FieldSeparatorChar, 0);
AppendLine();
this.Append(FieldSeparatorChar, 0);
this.AppendLine();
writeCount++;
}
// Output the end of the array and set the result
RemoveLastComma();
Append(CloseArrayChar, writeCount > 0 ? depth : 0);
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) {
return;
}
_builder.Append(IndentStrings.GetOrAdd(depth, x => new string(' ', x * 4)));
_ = this._builder.Append(IndentStrings.GetOrAdd(depth, x => new String(' ', x * 4)));
}
/// <summary>
/// Removes the last comma in the current string builder.
/// </summary>
private void RemoveLastComma()
{
if (_builder.Length < _lastCommaSearch.Length)
private void RemoveLastComma() {
if(this._builder.Length < this._lastCommaSearch.Length) {
return;
}
if (_lastCommaSearch.Where((t, i) => _builder[_builder.Length - _lastCommaSearch.Length + i] != t).Any())
{
if(this._lastCommaSearch.Where((t, i) => this._builder[this._builder.Length - this._lastCommaSearch.Length + i] != t).Any()) {
return;
}
// If we got this far, we simply remove the comma character
_builder.Remove(_builder.Length - _lastCommaSearch.Length, 1);
_ = this._builder.Remove(this._builder.Length - this._lastCommaSearch.Length, 1);
}
private void Append(string text, int depth)
{
SetIndent(depth);
_builder.Append(text);
private void Append(String text, Int32 depth) {
this.SetIndent(depth);
_ = this._builder.Append(text);
}
private void Append(char text, int depth)
{
SetIndent(depth);
_builder.Append(text);
private void Append(Char text, Int32 depth) {
this.SetIndent(depth);
_ = this._builder.Append(text);
}
private void AppendLine()
{
if (_options.Format == false) return;
_builder.Append(Environment.NewLine);
private void AppendLine() {
if(this._options.Format == false) {
return;
}
_ = this._builder.Append(Environment.NewLine);
}
#endregion
@@ -1,12 +1,11 @@
namespace Unosquare.Swan.Formatters
{
using System;
using System.Collections.Generic;
using System.Collections.Concurrent;
using System.Linq;
using System.Reflection;
using Attributes;
using System;
using System.Collections.Generic;
using System.Collections.Concurrent;
using System.Linq;
using System.Reflection;
using Unosquare.Swan.Attributes;
namespace Unosquare.Swan.Formatters {
/// <summary>
/// A very simple, light-weight JSON library written by Mario
/// to teach Geo how things are done
@@ -14,91 +13,92 @@
/// This is an useful helper for small tasks but it doesn't represent a full-featured
/// serializer such as the beloved Json.NET.
/// </summary>
public partial class Json
{
private class SerializerOptions
{
private static readonly ConcurrentDictionary<Type, Dictionary<Tuple<string, string>, MemberInfo>>
TypeCache = new ConcurrentDictionary<Type, Dictionary<Tuple<string, string>, MemberInfo>>();
public partial class Json {
private class SerializerOptions {
private static readonly ConcurrentDictionary<Type, Dictionary<Tuple<String, String>, MemberInfo>>
TypeCache = new ConcurrentDictionary<Type, Dictionary<Tuple<String, String>, MemberInfo>>();
private readonly string[] _includeProperties;
private readonly string[] _excludeProperties;
private readonly Dictionary<int, List<WeakReference>> _parentReferences = new Dictionary<int, List<WeakReference>>();
private readonly String[] _includeProperties;
private readonly String[] _excludeProperties;
private readonly Dictionary<Int32, List<WeakReference>> _parentReferences = new Dictionary<Int32, List<WeakReference>>();
public SerializerOptions(
bool format,
string typeSpecifier,
string[] includeProperties,
string[] excludeProperties = null,
bool includeNonPublic = true,
IReadOnlyCollection<WeakReference> parentReferences = null)
{
_includeProperties = includeProperties;
_excludeProperties = excludeProperties;
Boolean format,
String typeSpecifier,
String[] includeProperties,
String[] excludeProperties = null,
Boolean includeNonPublic = true,
IReadOnlyCollection<WeakReference> parentReferences = null) {
this._includeProperties = includeProperties;
this._excludeProperties = excludeProperties;
IncludeNonPublic = includeNonPublic;
Format = format;
TypeSpecifier = typeSpecifier;
this.IncludeNonPublic = includeNonPublic;
this.Format = format;
this.TypeSpecifier = typeSpecifier;
if (parentReferences == null)
if(parentReferences == null) {
return;
}
foreach (var parentReference in parentReferences.Where(x => x.IsAlive))
{
IsObjectPresent(parentReference.Target);
foreach(WeakReference parentReference in parentReferences.Where(x => x.IsAlive)) {
_ = this.IsObjectPresent(parentReference.Target);
}
}
public bool Format { get; }
public string TypeSpecifier { get; }
public bool IncludeNonPublic { get; }
public Boolean Format {
get;
}
public String TypeSpecifier {
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 (_parentReferences[hashCode].Any(p => ReferenceEquals(p.Target, target)))
if(this._parentReferences.ContainsKey(hashCode)) {
if(this._parentReferences[hashCode].Any(p => ReferenceEquals(p.Target, target))) {
return true;
}
_parentReferences[hashCode].Add(new WeakReference(target));
this._parentReferences[hashCode].Add(new WeakReference(target));
return false;
}
_parentReferences.Add(hashCode, new List<WeakReference> { new WeakReference(target) });
this._parentReferences.Add(hashCode, new List<WeakReference> { new WeakReference(target) });
return false;
}
internal Dictionary<string, MemberInfo> GetProperties(Type targetType)
internal Dictionary<String, MemberInfo> GetProperties(Type targetType)
=> GetPropertiesCache(targetType)
.When(() => _includeProperties?.Length > 0,
query => query.Where(p => _includeProperties.Contains(p.Key.Item1)))
.When(() => _excludeProperties?.Length > 0,
query => query.Where(p => !_excludeProperties.Contains(p.Key.Item1)))
.When(() => this._includeProperties?.Length > 0,
query => query.Where(p => this._includeProperties.Contains(p.Key.Item1)))
.When(() => this._excludeProperties?.Length > 0,
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)
{
if (TypeCache.TryGetValue(targetType, out var current))
private static Dictionary<Tuple<String, String>, MemberInfo> GetPropertiesCache(Type targetType) {
if(TypeCache.TryGetValue(targetType, out Dictionary<Tuple<String, String>, MemberInfo> current)) {
return current;
}
var fields =
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(targetType.IsValueType()) {
fields.AddRange(FieldTypeCache.RetrieveAllFields(targetType));
}
var value = fields
Dictionary<Tuple<String, String>, MemberInfo> value = fields
.ToDictionary(
x => Tuple.Create(x.Name,
x.GetCustomAttribute<JsonPropertyAttribute>()?.PropertyName ?? x.Name),
x => x);
TypeCache.TryAdd(targetType, value);
_ = TypeCache.TryAdd(targetType, value);
return value;
}
+60 -72
View File
@@ -1,13 +1,12 @@
namespace Unosquare.Swan.Formatters
{
using Reflection;
using System;
using Components;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Attributes;
using Unosquare.Swan.Reflection;
using System;
using Unosquare.Swan.Components;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Unosquare.Swan.Attributes;
namespace Unosquare.Swan.Formatters {
/// <summary>
/// A very simple, light-weight JSON library written by Mario
/// to teach Geo how things are done
@@ -15,35 +14,34 @@
/// This is an useful helper for small tasks but it doesn't represent a full-featured
/// serializer such as the beloved Json.NET.
/// </summary>
public static partial class Json
{
public static partial class Json {
#region Constants
internal const string AddMethodName = "Add";
internal const String AddMethodName = "Add";
private const char OpenObjectChar = '{';
private const char CloseObjectChar = '}';
private const Char OpenObjectChar = '{';
private const Char CloseObjectChar = '}';
private const char OpenArrayChar = '[';
private const char CloseArrayChar = ']';
private const Char OpenArrayChar = '[';
private const Char CloseArrayChar = ']';
private const char FieldSeparatorChar = ',';
private const char ValueSeparatorChar = ':';
private const Char FieldSeparatorChar = ',';
private const Char ValueSeparatorChar = ':';
private const char StringEscapeChar = '\\';
private const char StringQuotedChar = '"';
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";
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>();
private static readonly CollectionCacheRepository<String> IgnoredPropertiesCache = new CollectionCacheRepository<String>();
#region Public API
@@ -100,16 +98,13 @@
/// }
/// </code>
/// </example>
public static string Serialize(
object obj,
bool format = false,
string typeSpecifier = null,
bool includeNonPublic = false,
string[] includedNames = null,
string[] excludedNames = null)
{
return Serialize(obj, format, typeSpecifier, includeNonPublic, includedNames, excludedNames, null);
}
public static String Serialize(
Object obj,
Boolean format = false,
String typeSpecifier = null,
Boolean includeNonPublic = false,
String[] includedNames = null,
String[] excludedNames = null) => Serialize(obj, format, typeSpecifier, includeNonPublic, includedNames, excludedNames, null);
/// <summary>
/// Serializes the specified object into a JSON string.
@@ -124,21 +119,19 @@
/// <returns>
/// A <see cref="System.String" /> that represents the current object.
/// </returns>
public static string Serialize(
object obj,
bool format,
string typeSpecifier,
bool includeNonPublic,
string[] includedNames,
string[] excludedNames,
List<WeakReference> parentReferences)
{
if (obj != null && (obj is string || Definitions.AllBasicValueTypes.Contains(obj.GetType())))
{
public static String Serialize(
Object obj,
Boolean format,
String typeSpecifier,
Boolean includeNonPublic,
String[] includedNames,
String[] excludedNames,
List<WeakReference> parentReferences) {
if(obj != null && (obj is String || Definitions.AllBasicValueTypes.Contains(obj.GetType()))) {
return SerializePrimitiveValue(obj);
}
var options = new SerializerOptions(
SerializerOptions options = new SerializerOptions(
format,
typeSpecifier,
includedNames,
@@ -178,9 +171,8 @@
/// }
/// </code>
/// </example>
public static string SerializeOnly(object obj, bool format, params string[] includeNames)
{
var options = new SerializerOptions(format, null, includeNames);
public static String SerializeOnly(Object obj, Boolean format, params String[] includeNames) {
SerializerOptions options = new SerializerOptions(format, null, includeNames);
return Serializer.Serialize(obj, 0, options);
}
@@ -214,9 +206,8 @@
/// }
/// </code>
/// </example>
public static string SerializeExcluding(object obj, bool format, params string[] excludeNames)
{
var options = new SerializerOptions(format, null, null, excludeNames);
public static String SerializeExcluding(Object obj, Boolean format, params String[] excludeNames) {
SerializerOptions options = new SerializerOptions(format, null, null, excludeNames);
return Serializer.Serialize(obj, 0, options);
}
@@ -245,7 +236,7 @@
/// }
/// </code>
/// </example>
public static object Deserialize(string json) => Deserializer.DeserializeInternal(json);
public static Object Deserialize(String json) => Deserializer.DeserializeInternal(json);
/// <summary>
/// Deserializes the specified json string and converts it to the specified object type.
@@ -272,7 +263,7 @@
/// }
/// </code>
/// </example>
public static T Deserialize<T>(string json) => (T)Deserialize(json, typeof(T));
public static T Deserialize<T>(String json) => (T)Deserialize(json, typeof(T));
/// <summary>
/// Deserializes the specified json string and converts it to the specified object type.
@@ -281,7 +272,7 @@
/// <param name="json">The json.</param>
/// <param name="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);
public static T Deserialize<T>(String json, Boolean includeNonPublic) => (T)Deserialize(json, typeof(T), includeNonPublic);
/// <summary>
/// Deserializes the specified json string and converts it to the specified object type.
@@ -290,37 +281,34 @@
/// <param name="resultType">Type of the result.</param>
/// <param name="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)
public static Object Deserialize(String json, Type resultType, Boolean includeNonPublic = false)
=> Converter.FromJsonResult(Deserializer.DeserializeInternal(json), resultType, includeNonPublic);
#endregion
#region Private API
private static string[] GetExcludedNames(Type type, string[] excludedNames)
{
if (type == null)
private static String[] GetExcludedNames(Type type, String[] excludedNames) {
if(type == null) {
return excludedNames;
}
var excludedByAttr = IgnoredPropertiesCache.Retrieve(type, t => t.GetProperties()
IEnumerable<String> excludedByAttr = IgnoredPropertiesCache.Retrieve(type, t => t.GetProperties()
.Where(x => Runtime.AttributeCache.RetrieveOne<JsonPropertyAttribute>(x)?.Ignored == true)
.Select(x => x.Name));
if (excludedByAttr?.Any() != true)
return excludedNames;
return excludedNames == null
return excludedByAttr?.Any() != true
? excludedNames
: excludedNames == null
? excludedByAttr.ToArray()
: excludedByAttr.Intersect(excludedNames).ToArray();
}
private static string SerializePrimitiveValue(object obj)
{
switch (obj)
{
case string stringValue:
private static String SerializePrimitiveValue(Object obj) {
switch(obj) {
case String stringValue:
return stringValue;
case bool boolValue:
case Boolean boolValue:
return boolValue ? TrueLiteral : FalseLiteral;
default:
return obj.ToString();
@@ -1,11 +1,10 @@
namespace Unosquare.Swan.Reflection
{
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Collections.Concurrent;
using System.Linq;
using System;
using System.Collections.Generic;
using System.Reflection;
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).
///
@@ -13,25 +12,23 @@
/// 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);
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>
/// Initializes a new instance of the <see cref="AttributeCache"/> class.
/// </summary>
/// <param name="propertyCache">The property cache object.</param>
public AttributeCache(PropertyTypeCache propertyCache = null)
{
PropertyTypeCache = propertyCache ?? Runtime.PropertyTypeCache;
}
public AttributeCache(PropertyTypeCache propertyCache = null) => this.PropertyTypeCache = propertyCache ?? Runtime.PropertyTypeCache;
/// <summary>
/// A PropertyTypeCache object for caching properties and their attributes.
/// </summary>
public PropertyTypeCache PropertyTypeCache { get; }
public PropertyTypeCache PropertyTypeCache {
get;
}
/// <summary>
/// Determines whether [contains] [the specified member].
@@ -41,7 +38,7 @@
/// <returns>
/// <c>true</c> if [contains] [the specified member]; otherwise, <c>false</c>.
/// </returns>
public bool Contains<T>(MemberInfo member) => _data.Value.ContainsKey(new Tuple<object, Type>(member, typeof(T)));
public Boolean Contains<T>(MemberInfo member) => this._data.Value.ContainsKey(new Tuple<Object, Type>(member, typeof(T)));
/// <summary>
/// Gets specific attributes from a member constrained to an attribute.
@@ -50,13 +47,13 @@
/// <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>
public IEnumerable<object> Retrieve<T>(MemberInfo member, bool inherit = false)
where T : Attribute
{
if (member == null)
public IEnumerable<Object> Retrieve<T>(MemberInfo member, Boolean inherit = false)
where T : Attribute {
if(member == null) {
throw new ArgumentNullException(nameof(member));
}
return Retrieve(new Tuple<object, Type>(member, typeof(T)), t => member.GetCustomAttributes<T>(inherit));
return this.Retrieve(new Tuple<Object, Type>(member, typeof(T)), t => member.GetCustomAttributes<T>(inherit));
}
/// <summary>
@@ -66,16 +63,17 @@
/// <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>
public IEnumerable<object> Retrieve(MemberInfo member, Type type, bool inherit = false)
{
if (member == null)
public IEnumerable<Object> Retrieve(MemberInfo member, Type type, Boolean inherit = false) {
if(member == null) {
throw new ArgumentNullException(nameof(member));
}
if (type == null)
if(type == null) {
throw new ArgumentNullException(nameof(type));
}
return Retrieve(
new Tuple<object, Type>(member, type),
return this.Retrieve(
new Tuple<Object, Type>(member, type),
t => member.GetCustomAttributes(type, inherit));
}
@@ -86,14 +84,14 @@
/// <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 attribute stored for the specified type.</returns>
public T RetrieveOne<T>(MemberInfo member, bool inherit = false)
where T : Attribute
{
if (member == null)
public T RetrieveOne<T>(MemberInfo member, Boolean inherit = false)
where T : Attribute {
if(member == null) {
return default;
}
var attr = Retrieve(
new Tuple<object, Type>(member, typeof(T)),
IEnumerable<Object> attr = this.Retrieve(
new Tuple<Object, Type>(member, typeof(T)),
t => member.GetCustomAttributes(typeof(T), inherit));
return ConvertToAttribute<T>(attr);
@@ -106,11 +104,10 @@
/// <typeparam name="T">The type to retrieve the attribute.</typeparam>
/// <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)
where TAttribute : Attribute
{
var attr = Retrieve(
new Tuple<object, Type>(typeof(T), typeof(TAttribute)),
public TAttribute RetrieveOne<TAttribute, T>(Boolean inherit = false)
where TAttribute : Attribute {
IEnumerable<Object> attr = this.Retrieve(
new Tuple<Object, Type>(typeof(T), typeof(TAttribute)),
t => typeof(T).GetCustomAttributes(typeof(TAttribute), inherit));
return ConvertToAttribute<TAttribute>(attr);
@@ -123,14 +120,14 @@
/// <param name="type">The type of the object.</param>
/// <param name="inherit"><c>true</c> to inspect the ancestors of element; otherwise, <c>false</c>.</param>
/// <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)
where T : Attribute
{
if (type == null)
public Dictionary<PropertyInfo, IEnumerable<Object>> Retrieve<T>(Type type, Boolean inherit = false)
where T : Attribute {
if(type == null) {
throw new ArgumentNullException(nameof(type));
}
return PropertyTypeCache.RetrieveAllProperties(type, true)
.ToDictionary(x => x, x => Retrieve<T>(x, inherit));
return this.PropertyTypeCache.RetrieveAllProperties(type, true)
.ToDictionary(x => x, x => this.Retrieve<T>(x, inherit));
}
/// <summary>
@@ -142,33 +139,34 @@
/// <returns>
/// A dictionary of the properties and their attributes stored for the specified type.
/// </returns>
public Dictionary<PropertyInfo, IEnumerable<object>> RetrieveFromType<T>(Type attributeType, bool inherit = false)
{
if (attributeType == null)
public Dictionary<PropertyInfo, IEnumerable<Object>> RetrieveFromType<T>(Type attributeType, Boolean inherit = false) {
if(attributeType == null) {
throw new ArgumentNullException(nameof(attributeType));
return PropertyTypeCache.RetrieveAllProperties<T>(true)
.ToDictionary(x => x, x => Retrieve(x, attributeType, inherit));
}
private static T ConvertToAttribute<T>(IEnumerable<object> attr)
where T : Attribute
{
if (attr?.Any() != true)
return default;
return this.PropertyTypeCache.RetrieveAllProperties<T>(true)
.ToDictionary(x => x, x => this.Retrieve(x, attributeType, inherit));
}
if (attr.Count() == 1)
return (T) Convert.ChangeType(attr.First(), typeof(T));
private static T ConvertToAttribute<T>(IEnumerable<Object> attr)
where T : Attribute {
if(attr?.Any() != true) {
return default;
}
if(attr.Count() == 1) {
return (T)Convert.ChangeType(attr.First(), typeof(T));
}
throw new AmbiguousMatchException("Multiple custom attributes of the same type found.");
}
private IEnumerable<object> Retrieve(Tuple<object, Type> key, Func<Tuple<object, Type>, IEnumerable<object>> factory)
{
if (factory == null)
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));
return this._data.Value.GetOrAdd(key, k => factory.Invoke(k).Where(item => item != null));
}
}
}
@@ -1,34 +1,29 @@
namespace Unosquare.Swan.Reflection
{
using System;
using System.Reflection;
using Attributes;
using System;
using System.Reflection;
using Unosquare.Swan.Attributes;
namespace Unosquare.Swan.Reflection {
/// <summary>
/// Represents a Property object from a Object Reflection Property with extended values.
/// </summary>
public class ExtendedPropertyInfo
{
public class ExtendedPropertyInfo {
/// <summary>
/// Initializes a new instance of the <see cref="ExtendedPropertyInfo"/> class.
/// </summary>
/// <param name="propertyInfo">The property information.</param>
public ExtendedPropertyInfo(PropertyInfo propertyInfo)
{
if (propertyInfo == null)
{
public ExtendedPropertyInfo(PropertyInfo propertyInfo) {
if(propertyInfo == null) {
throw new ArgumentNullException(nameof(propertyInfo));
}
Property = propertyInfo.Name;
DataType = propertyInfo.PropertyType.Name;
this.Property = propertyInfo.Name;
this.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;
foreach(PropertyDisplayAttribute display in Runtime.AttributeCache.Retrieve<PropertyDisplayAttribute>(propertyInfo, true)) {
this.Name = display.Name;
this.Description = display.Description;
this.GroupName = display.GroupName;
this.DefaultValue = display.DefaultValue;
}
}
@@ -38,7 +33,9 @@
/// <value>
/// The property.
/// </value>
public string Property { get; }
public String Property {
get;
}
/// <summary>
/// Gets or sets the type of the data.
@@ -46,7 +43,9 @@
/// <value>
/// The type of the data.
/// </value>
public string DataType { get; }
public String DataType {
get;
}
/// <summary>
/// Gets or sets the value.
@@ -54,7 +53,9 @@
/// <value>
/// The value.
/// </value>
public object Value { get; set; }
public Object Value {
get; set;
}
/// <summary>
/// Gets or sets the default value.
@@ -62,7 +63,9 @@
/// <value>
/// The default value.
/// </value>
public object DefaultValue { get; }
public Object DefaultValue {
get;
}
/// <summary>
/// Gets or sets the name.
@@ -70,7 +73,9 @@
/// <value>
/// The name.
/// </value>
public string Name { get; }
public String Name {
get;
}
/// <summary>
/// Gets or sets the description.
@@ -78,7 +83,9 @@
/// <value>
/// The description.
/// </value>
public string Description { get; }
public String Description {
get;
}
/// <summary>
/// Gets or sets the name of the group.
@@ -86,22 +93,22 @@
/// <value>
/// The name of the group.
/// </value>
public string GroupName { get; }
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
{
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))
{
public ExtendedPropertyInfo(String property)
: base(typeof(T).GetProperty(property)) {
}
}
}
@@ -1,38 +1,36 @@
namespace Unosquare.Swan.Reflection
{
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Globalization;
using System.Linq;
using System.Reflection;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Globalization;
using System.Linq;
using System.Reflection;
namespace Unosquare.Swan.Reflection {
/// <summary>
/// Provides extended information about a type.
///
/// 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.
/// </summary>
public class ExtendedTypeInfo
{
public class ExtendedTypeInfo {
#region Static Declarations
private const string TryParseMethodName = nameof(byte.TryParse);
private const string ToStringMethodName = nameof(ToString);
private const String TryParseMethodName = nameof(Byte.TryParse);
private const String ToStringMethodName = nameof(ToString);
private static readonly Type[] NumericTypes =
{
typeof(byte),
typeof(sbyte),
typeof(decimal),
typeof(double),
typeof(float),
typeof(int),
typeof(uint),
typeof(long),
typeof(ulong),
typeof(short),
typeof(ushort),
typeof(Byte),
typeof(SByte),
typeof(Decimal),
typeof(Double),
typeof(Single),
typeof(Int32),
typeof(UInt32),
typeof(Int64),
typeof(UInt64),
typeof(Int16),
typeof(UInt16),
};
#endregion
@@ -40,7 +38,7 @@
#region State Management
private readonly ParameterInfo[] _tryParseParameters;
private readonly int _toStringArgumentLength;
private readonly Int32 _toStringArgumentLength;
#endregion
@@ -50,47 +48,40 @@
/// Initializes a new instance of the <see cref="ExtendedTypeInfo"/> class.
/// </summary>
/// <param name="t">The t.</param>
public ExtendedTypeInfo(Type t)
{
Type = t ?? throw new ArgumentNullException(nameof(t));
IsNullableValueType = Type.GetTypeInfo().IsGenericType
&& Type.GetGenericTypeDefinition() == typeof(Nullable<>);
public ExtendedTypeInfo(Type t) {
this.Type = t ?? throw new ArgumentNullException(nameof(t));
this.IsNullableValueType = this.Type.GetTypeInfo().IsGenericType
&& this.Type.GetGenericTypeDefinition() == typeof(Nullable<>);
IsValueType = t.GetTypeInfo().IsValueType;
this.IsValueType = t.GetTypeInfo().IsValueType;
UnderlyingType = IsNullableValueType ?
new NullableConverter(Type).UnderlyingType :
Type;
this.UnderlyingType = this.IsNullableValueType ?
new NullableConverter(this.Type).UnderlyingType :
this.Type;
IsNumeric = NumericTypes.Contains(UnderlyingType);
this.IsNumeric = NumericTypes.Contains(this.UnderlyingType);
// Extract the TryParse method info
try
{
TryParseMethodInfo = UnderlyingType.GetMethod(TryParseMethodName,
new[] { typeof(string), typeof(NumberStyles), typeof(IFormatProvider), UnderlyingType.MakeByRefType() }) ??
UnderlyingType.GetMethod(TryParseMethodName,
new[] { typeof(string), UnderlyingType.MakeByRefType() });
try {
this.TryParseMethodInfo = this.UnderlyingType.GetMethod(TryParseMethodName,
new[] { typeof(String), typeof(NumberStyles), typeof(IFormatProvider), this.UnderlyingType.MakeByRefType() }) ??
this.UnderlyingType.GetMethod(TryParseMethodName,
new[] { typeof(String), this.UnderlyingType.MakeByRefType() });
_tryParseParameters = TryParseMethodInfo?.GetParameters();
}
catch
{
this._tryParseParameters = this.TryParseMethodInfo?.GetParameters();
} catch {
// ignored
}
// Extract the ToString method Info
try
{
ToStringMethodInfo = UnderlyingType.GetMethod(ToStringMethodName,
try {
this.ToStringMethodInfo = this.UnderlyingType.GetMethod(ToStringMethodName,
new[] { typeof(IFormatProvider) }) ??
UnderlyingType.GetMethod(ToStringMethodName,
this.UnderlyingType.GetMethod(ToStringMethodName,
new Type[] { });
_toStringArgumentLength = ToStringMethodInfo?.GetParameters().Length ?? 0;
}
catch
{
this._toStringArgumentLength = this.ToStringMethodInfo?.GetParameters().Length ?? 0;
} catch {
// ignored
}
}
@@ -105,7 +96,9 @@
/// <value>
/// The type.
/// </value>
public Type Type { get; }
public Type Type {
get;
}
/// <summary>
/// Gets a value indicating whether the type is a nullable value type.
@@ -113,7 +106,9 @@
/// <value>
/// <c>true</c> if this instance is nullable value type; otherwise, <c>false</c>.
/// </value>
public bool IsNullableValueType { get; }
public Boolean IsNullableValueType {
get;
}
/// <summary>
/// Gets a value indicating whether the type or underlying type is numeric.
@@ -121,13 +116,17 @@
/// <value>
/// <c>true</c> if this instance is numeric; otherwise, <c>false</c>.
/// </value>
public bool IsNumeric { get; }
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 bool IsValueType { get; }
public Boolean IsValueType {
get;
}
/// <summary>
/// When dealing with nullable value types, this property will
@@ -137,7 +136,9 @@
/// <value>
/// The type of the underlying.
/// </value>
public Type UnderlyingType { get; }
public Type UnderlyingType {
get;
}
/// <summary>
/// Gets the try parse method information. If the type does not contain
@@ -146,7 +147,9 @@
/// <value>
/// The try parse method information.
/// </value>
public MethodInfo TryParseMethodInfo { get; }
public MethodInfo TryParseMethodInfo {
get;
}
/// <summary>
/// Gets the ToString method info
@@ -155,7 +158,9 @@
/// <value>
/// To string method information.
/// </value>
public MethodInfo ToStringMethodInfo { get; }
public MethodInfo ToStringMethodInfo {
get;
}
/// <summary>
/// Gets a value indicating whether the type contains a suitable TryParse method.
@@ -163,7 +168,7 @@
/// <value>
/// <c>true</c> if this instance can parse natively; otherwise, <c>false</c>.
/// </value>
public bool CanParseNatively => TryParseMethodInfo != null;
public Boolean CanParseNatively => this.TryParseMethodInfo != null;
#endregion
@@ -174,7 +179,7 @@
/// 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;
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.
@@ -184,58 +189,50 @@
/// <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();
public Boolean TryParse(String s, out Object result) {
result = this.GetDefault();
try
{
if (Type == typeof(string))
{
result = Convert.ChangeType(s, Type);
try {
if(this.Type == typeof(String)) {
result = Convert.ChangeType(s, this.Type);
return true;
}
if (IsNullableValueType && string.IsNullOrEmpty(s))
{
result = GetDefault();
if(this.IsNullableValueType && String.IsNullOrEmpty(s)) {
result = this.GetDefault();
return true;
}
if (CanParseNatively == false)
{
result = GetDefault();
if(this.CanParseNatively == false) {
result = this.GetDefault();
return false;
}
// Build the arguments of the TryParse method
var dynamicArguments = new List<object> { s };
List<Object> dynamicArguments = new List<Object> { s };
for (var pi = 1; pi < _tryParseParameters.Length - 1; pi++)
{
var argInfo = _tryParseParameters[pi];
if (argInfo.ParameterType == typeof(IFormatProvider))
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))
} else if(argInfo.ParameterType == typeof(NumberStyles)) {
dynamicArguments.Add(NumberStyles.Any);
else
} else {
dynamicArguments.Add(null);
}
}
dynamicArguments.Add(null);
var parseArguments = dynamicArguments.ToArray();
Object[] parseArguments = dynamicArguments.ToArray();
if ((bool)TryParseMethodInfo.Invoke(null, parseArguments) == false)
{
result = GetDefault();
if((Boolean)this.TryParseMethodInfo.Invoke(null, parseArguments) == false) {
result = this.GetDefault();
return false;
}
result = parseArguments[parseArguments.Length - 1];
return true;
}
catch
{
} catch {
return false;
}
}
@@ -247,15 +244,11 @@
/// </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
public String ToStringInvariant(Object instance) => instance == null
? String.Empty
: this._toStringArgumentLength != 1
? instance.ToString()
: ToStringMethodInfo.Invoke(instance, new object[] {CultureInfo.InvariantCulture}) as string;
}
: this.ToStringMethodInfo.Invoke(instance, new Object[] { CultureInfo.InvariantCulture }) as String;
#endregion
}
@@ -267,14 +260,12 @@
/// 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
{
public class ExtendedTypeInfo<T> : ExtendedTypeInfo {
/// <summary>
/// Initializes a new instance of the <see cref="ExtendedTypeInfo{T}"/> class.
/// </summary>
public ExtendedTypeInfo()
: base(typeof(T))
{
: base(typeof(T)) {
// placeholder
}
@@ -285,6 +276,6 @@
/// </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);
public String ToStringInvariant(T instance) => base.ToStringInvariant(instance);
}
}
@@ -1,14 +1,12 @@
namespace Unosquare.Swan.Reflection
{
using System;
using System.Reflection;
using System.Collections.Concurrent;
using System;
using System.Reflection;
using System.Collections.Concurrent;
namespace Unosquare.Swan.Reflection {
/// <summary>
/// Represents a Method Info Cache.
/// </summary>
public class MethodInfoCache : ConcurrentDictionary<string, MethodInfo>
{
public class MethodInfoCache : ConcurrentDictionary<String, MethodInfo> {
/// <summary>
/// Retrieves the properties stored for the specified type.
/// If the properties are not available, it calls the factory method to retrieve them
@@ -25,8 +23,8 @@
/// or
/// factory.</exception>
/// <exception cref="System.ArgumentNullException">type.</exception>
public MethodInfo Retrieve<T>(string name, string alias, params Type[] types)
=> Retrieve(typeof(T), name, alias, types);
public MethodInfo Retrieve<T>(String name, String alias, params Type[] types)
=> this.Retrieve(typeof(T), name, alias, types);
/// <summary>
/// Retrieves the specified name.
@@ -37,8 +35,8 @@
/// <returns>
/// The cached MethodInfo.
/// </returns>
public MethodInfo Retrieve<T>(string name, params Type[] types)
=> Retrieve(typeof(T), name, name, types);
public MethodInfo Retrieve<T>(String name, params Type[] types)
=> this.Retrieve(typeof(T), name, name, types);
/// <summary>
/// Retrieves the specified type.
@@ -49,8 +47,8 @@
/// <returns>
/// An array of the properties stored for the specified type.
/// </returns>
public MethodInfo Retrieve(Type type, string name, params Type[] types)
=> Retrieve(type, name, name, types);
public MethodInfo Retrieve(Type type, String name, params Type[] types)
=> this.Retrieve(type, name, name, types);
/// <summary>
/// Retrieves the specified type.
@@ -62,18 +60,20 @@
/// <returns>
/// The cached MethodInfo.
/// </returns>
public MethodInfo Retrieve(Type type, string name, string alias, params Type[] types)
{
if (type == null)
public MethodInfo Retrieve(Type type, String name, String alias, params Type[] types) {
if(type == null) {
throw new ArgumentNullException(nameof(type));
}
if (alias == null)
if(alias == null) {
throw new ArgumentNullException(nameof(alias));
}
if (name == null)
if(name == null) {
throw new ArgumentNullException(nameof(name));
}
return GetOrAdd(
return this.GetOrAdd(
alias,
x => type.GetMethod(name, types ?? new Type[0]));
}
@@ -86,8 +86,8 @@
/// <returns>
/// The cached MethodInfo.
/// </returns>
public MethodInfo Retrieve<T>(string name)
=> Retrieve(typeof(T), name);
public MethodInfo Retrieve<T>(String name)
=> this.Retrieve(typeof(T), name);
/// <summary>
/// Retrieves the specified type.
@@ -102,15 +102,16 @@
/// or
/// name.
/// </exception>
public MethodInfo Retrieve(Type type, string name)
{
if (type == null)
public MethodInfo Retrieve(Type type, String name) {
if(type == null) {
throw new ArgumentNullException(nameof(type));
}
if (name == null)
if(name == null) {
throw new ArgumentNullException(nameof(name));
}
return GetOrAdd(
return this.GetOrAdd(
name,
type.GetMethod);
}

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