mirror of https://github.com/SixLabors/ImageSharp
Browse Source
Former-commit-id: 8a5d5ec6199e8f0b209993d5742fc6a27f33427b Former-commit-id: a0d212c778da02827b67958c8169d120a0fedb9e Former-commit-id: 0681f7bedb807673142b13a9b0ad7d594f067915af/merge-core
32 changed files with 1573 additions and 668 deletions
@ -0,0 +1,210 @@ |
|||
// --------------------------------------------------------------------------------------------------------------------
|
|||
// <copyright file="ExtendedColorTypeConverter.cs" company="James South">
|
|||
// Copyright (c) James South.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
// </copyright>
|
|||
// <summary>
|
|||
// The extended color type converter allows conversion of system and web colors.
|
|||
// </summary>
|
|||
// --------------------------------------------------------------------------------------------------------------------
|
|||
|
|||
namespace ImageProcessor.Web.Helpers |
|||
{ |
|||
using System; |
|||
using System.Collections; |
|||
using System.ComponentModel; |
|||
using System.Drawing; |
|||
using System.Globalization; |
|||
using System.Text; |
|||
using System.Text.RegularExpressions; |
|||
|
|||
/// <summary>
|
|||
/// The extended color type converter allows conversion of system and web colors.
|
|||
/// </summary>
|
|||
public class ExtendedColorTypeConverter : ColorConverter |
|||
{ |
|||
/// <summary>
|
|||
/// The web color regex.
|
|||
/// </summary>
|
|||
private static readonly Regex WebColorRegex = new Regex("([0-9a-fA-F]{3}){1,2}", RegexOptions.Compiled); |
|||
|
|||
/// <summary>
|
|||
/// The html system color table map.
|
|||
/// </summary>
|
|||
private static Hashtable htmlSystemColorTable; |
|||
|
|||
/// <summary>
|
|||
/// Converts the given object to the type of this converter, using the specified context and culture
|
|||
/// information.
|
|||
/// </summary>
|
|||
/// <returns>
|
|||
/// An <see cref="T:System.Object"/> that represents the converted value.
|
|||
/// </returns>
|
|||
/// <param name="context">
|
|||
/// An <see cref="T:System.ComponentModel.ITypeDescriptorContext"/> that provides a format context.
|
|||
/// </param>
|
|||
/// <param name="culture">
|
|||
/// The <see cref="T:System.Globalization.CultureInfo"/> to use as the current culture.
|
|||
/// </param>
|
|||
/// <param name="value">The <see cref="T:System.Object"/> to convert. </param>
|
|||
/// <exception cref="T:System.NotSupportedException">The conversion cannot be performed.</exception>
|
|||
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) |
|||
{ |
|||
string s = value as string; |
|||
if (s != null) |
|||
{ |
|||
string colorText = s.Trim(); |
|||
Color c = Color.Empty; |
|||
|
|||
// Empty color
|
|||
if (string.IsNullOrEmpty(colorText)) |
|||
{ |
|||
return c; |
|||
} |
|||
|
|||
// Special case. HTML requires LightGrey, but System.Drawing.KnownColor has LightGray
|
|||
if (colorText.Equals("LightGrey", StringComparison.OrdinalIgnoreCase)) |
|||
{ |
|||
return Color.LightGray; |
|||
} |
|||
|
|||
// Hex based color values.
|
|||
char hash = colorText[0]; |
|||
if (hash == '#' || WebColorRegex.IsMatch(colorText)) |
|||
{ |
|||
if (hash != '#') |
|||
{ |
|||
colorText = "#" + colorText; |
|||
} |
|||
|
|||
if (colorText.Length == 7) |
|||
{ |
|||
return Color.FromArgb( |
|||
Convert.ToInt32(colorText.Substring(1, 2), 16), |
|||
Convert.ToInt32(colorText.Substring(3, 2), 16), |
|||
Convert.ToInt32(colorText.Substring(5, 2), 16)); |
|||
} |
|||
|
|||
// Length is 4
|
|||
string r = char.ToString(colorText[1]); |
|||
string g = char.ToString(colorText[2]); |
|||
string b = char.ToString(colorText[3]); |
|||
|
|||
return Color.FromArgb( |
|||
Convert.ToInt32(r + r, 16), |
|||
Convert.ToInt32(g + g, 16), |
|||
Convert.ToInt32(b + b, 16)); |
|||
} |
|||
|
|||
// System color
|
|||
if (htmlSystemColorTable == null) |
|||
{ |
|||
InitializeHtmlSystemColorTable(); |
|||
} |
|||
|
|||
if (htmlSystemColorTable != null) |
|||
{ |
|||
object o = htmlSystemColorTable[colorText]; |
|||
if (o != null) |
|||
{ |
|||
return (Color)o; |
|||
} |
|||
} |
|||
} |
|||
|
|||
// ColorConverter handles all named and KnownColors
|
|||
return base.ConvertFrom(context, culture, value); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Converts the given value object to the specified type, using the specified context and culture
|
|||
/// information.
|
|||
/// </summary>
|
|||
/// <returns>
|
|||
/// An <see cref="T:System.Object"/> that represents the converted value.
|
|||
/// </returns>
|
|||
/// <param name="context">
|
|||
/// An <see cref="T:System.ComponentModel.ITypeDescriptorContext"/> that provides a format context.
|
|||
/// </param>
|
|||
/// <param name="culture">
|
|||
/// A <see cref="T:System.Globalization.CultureInfo"/>. If null is passed, the current culture is assumed.
|
|||
/// </param>
|
|||
/// <param name="value">The <see cref="T:System.Object"/> to convert. </param>
|
|||
/// <param name="destinationType">
|
|||
/// The <see cref="T:System.Type"/> to convert the <paramref name="value"/> parameter to.
|
|||
/// </param>
|
|||
/// <exception cref="T:System.ArgumentNullException">
|
|||
/// The <paramref name="destinationType"/> parameter is null.
|
|||
/// </exception>
|
|||
/// <exception cref="T:System.NotSupportedException">The conversion cannot be performed.
|
|||
/// </exception>
|
|||
public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType) |
|||
{ |
|||
if (destinationType == null) |
|||
{ |
|||
throw new ArgumentNullException("destinationType"); |
|||
} |
|||
|
|||
if (destinationType == typeof(string)) |
|||
{ |
|||
if (value != null) |
|||
{ |
|||
Color color = (Color)value; |
|||
|
|||
if (color == Color.Empty) |
|||
{ |
|||
return string.Empty; |
|||
} |
|||
|
|||
if (color.IsKnownColor == false) |
|||
{ |
|||
// In the Web scenario, colors should be formatted in #RRGGBB notation
|
|||
StringBuilder sb = new StringBuilder("#", 7); |
|||
sb.Append(color.R.ToString("X2", CultureInfo.InvariantCulture)); |
|||
sb.Append(color.G.ToString("X2", CultureInfo.InvariantCulture)); |
|||
sb.Append(color.B.ToString("X2", CultureInfo.InvariantCulture)); |
|||
return sb.ToString(); |
|||
} |
|||
} |
|||
} |
|||
|
|||
return base.ConvertTo(context, culture, value, destinationType); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Initializes color table mapping system colors to known colors.
|
|||
/// </summary>
|
|||
private static void InitializeHtmlSystemColorTable() |
|||
{ |
|||
Hashtable hashTable = new Hashtable(StringComparer.OrdinalIgnoreCase); |
|||
hashTable["activeborder"] = Color.FromKnownColor(KnownColor.ActiveBorder); |
|||
hashTable["activecaption"] = Color.FromKnownColor(KnownColor.ActiveCaption); |
|||
hashTable["appworkspace"] = Color.FromKnownColor(KnownColor.AppWorkspace); |
|||
hashTable["background"] = Color.FromKnownColor(KnownColor.Desktop); |
|||
hashTable["buttonface"] = Color.FromKnownColor(KnownColor.Control); |
|||
hashTable["buttonhighlight"] = Color.FromKnownColor(KnownColor.ControlLightLight); |
|||
hashTable["buttonshadow"] = Color.FromKnownColor(KnownColor.ControlDark); |
|||
hashTable["buttontext"] = Color.FromKnownColor(KnownColor.ControlText); |
|||
hashTable["captiontext"] = Color.FromKnownColor(KnownColor.ActiveCaptionText); |
|||
hashTable["graytext"] = Color.FromKnownColor(KnownColor.GrayText); |
|||
hashTable["highlight"] = Color.FromKnownColor(KnownColor.Highlight); |
|||
hashTable["highlighttext"] = Color.FromKnownColor(KnownColor.HighlightText); |
|||
hashTable["inactiveborder"] = Color.FromKnownColor(KnownColor.InactiveBorder); |
|||
hashTable["inactivecaption"] = Color.FromKnownColor(KnownColor.InactiveCaption); |
|||
hashTable["inactivecaptiontext"] = Color.FromKnownColor(KnownColor.InactiveCaptionText); |
|||
hashTable["infobackground"] = Color.FromKnownColor(KnownColor.Info); |
|||
hashTable["infotext"] = Color.FromKnownColor(KnownColor.InfoText); |
|||
hashTable["menu"] = Color.FromKnownColor(KnownColor.Menu); |
|||
hashTable["menutext"] = Color.FromKnownColor(KnownColor.MenuText); |
|||
hashTable["scrollbar"] = Color.FromKnownColor(KnownColor.ScrollBar); |
|||
hashTable["threeddarkshadow"] = Color.FromKnownColor(KnownColor.ControlDarkDark); |
|||
hashTable["threedface"] = Color.FromKnownColor(KnownColor.Control); |
|||
hashTable["threedhighlight"] = Color.FromKnownColor(KnownColor.ControlLight); |
|||
hashTable["threedlightshadow"] = Color.FromKnownColor(KnownColor.ControlLightLight); |
|||
hashTable["window"] = Color.FromKnownColor(KnownColor.Window); |
|||
hashTable["windowframe"] = Color.FromKnownColor(KnownColor.WindowFrame); |
|||
hashTable["windowtext"] = Color.FromKnownColor(KnownColor.WindowText); |
|||
htmlSystemColorTable = hashTable; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,48 @@ |
|||
// --------------------------------------------------------------------------------------------------------------------
|
|||
// <copyright file="GenericArrayTypeConverter.cs" company="James South">
|
|||
// Copyright (c) James South.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
// </copyright>
|
|||
// <summary>
|
|||
// Converts the value of an string to and from a Array{T}.
|
|||
// </summary>
|
|||
// --------------------------------------------------------------------------------------------------------------------
|
|||
|
|||
namespace ImageProcessor.Web.Helpers |
|||
{ |
|||
using System.Collections.Generic; |
|||
using System.ComponentModel; |
|||
using System.Globalization; |
|||
using System.Linq; |
|||
|
|||
/// <summary>
|
|||
/// Converts the value of an string to and from a Array{T}.
|
|||
/// </summary>
|
|||
/// <typeparam name="T">
|
|||
/// The type to convert from.
|
|||
/// </typeparam>
|
|||
public class GenericArrayTypeConverter<T> : GenericListTypeConverter<T> |
|||
{ |
|||
/// <summary>
|
|||
/// Converts the given object to the type of this converter, using the specified context and culture
|
|||
/// information.
|
|||
/// </summary>
|
|||
/// <returns>
|
|||
/// An <see cref="T:System.Object"/> that represents the converted value.
|
|||
/// </returns>
|
|||
/// <param name="context">
|
|||
/// An <see cref="T:System.ComponentModel.ITypeDescriptorContext"/> that provides a format context.
|
|||
/// </param>
|
|||
/// <param name="culture">
|
|||
/// The <see cref="T:System.Globalization.CultureInfo"/> to use as the current culture.
|
|||
/// </param>
|
|||
/// <param name="value">The <see cref="T:System.Object"/> to convert. </param>
|
|||
/// <exception cref="T:System.NotSupportedException">The conversion cannot be performed.</exception>
|
|||
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) |
|||
{ |
|||
object result = base.ConvertFrom(context, culture, value); |
|||
IList<T> list = result as IList<T>; |
|||
return list != null ? list.ToArray() : result; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,161 @@ |
|||
// --------------------------------------------------------------------------------------------------------------------
|
|||
// <copyright file="GenericListTypeConverter.cs" company="James South">
|
|||
// Copyright (c) James South.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
// </copyright>
|
|||
// <summary>
|
|||
// Converts the value of an string to and from a List{T}.
|
|||
// </summary>
|
|||
// --------------------------------------------------------------------------------------------------------------------
|
|||
|
|||
namespace ImageProcessor.Web.Helpers |
|||
{ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.ComponentModel; |
|||
using System.Globalization; |
|||
using System.Linq; |
|||
|
|||
/// <summary>
|
|||
/// Converts the value of an string to and from a List{T}.
|
|||
/// </summary>
|
|||
/// <typeparam name="T">
|
|||
/// The type to convert from.
|
|||
/// </typeparam>
|
|||
public class GenericListTypeConverter<T> : TypeConverter |
|||
{ |
|||
/// <summary>
|
|||
/// The type converter.
|
|||
/// </summary>
|
|||
private readonly TypeConverter typeConverter; |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="GenericListTypeConverter{T}"/> class.
|
|||
/// </summary>
|
|||
/// <exception cref="InvalidOperationException">
|
|||
/// Thrown if no converter exists for the given type.
|
|||
/// </exception>
|
|||
public GenericListTypeConverter() |
|||
{ |
|||
Type type = typeof(T); |
|||
this.typeConverter = TypeDescriptor.GetConverter(type); |
|||
if (this.typeConverter == null) |
|||
{ |
|||
throw new InvalidOperationException("No type converter exists for type " + type.FullName); |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Returns whether this converter can convert an object of the given type to the type of this converter,
|
|||
/// using the specified context.
|
|||
/// </summary>
|
|||
/// <returns>
|
|||
/// true if this converter can perform the conversion; otherwise, false.
|
|||
/// </returns>
|
|||
/// <param name="context">
|
|||
/// An <see cref="T:System.ComponentModel.ITypeDescriptorContext"/> that provides a
|
|||
/// format context. </param>
|
|||
/// <param name="sourceType">
|
|||
/// A <see cref="T:System.Type"/> that represents the type you want to convert from.
|
|||
/// </param>
|
|||
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) |
|||
{ |
|||
if (sourceType == typeof(string)) |
|||
{ |
|||
string[] items = this.GetStringArray(sourceType.ToString()); |
|||
return items.Any(); |
|||
} |
|||
|
|||
return base.CanConvertFrom(context, sourceType); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Converts the given object to the type of this converter, using the specified context and culture
|
|||
/// information.
|
|||
/// </summary>
|
|||
/// <returns>
|
|||
/// An <see cref="T:System.Object"/> that represents the converted value.
|
|||
/// </returns>
|
|||
/// <param name="context">
|
|||
/// An <see cref="T:System.ComponentModel.ITypeDescriptorContext"/> that provides a format context.
|
|||
/// </param>
|
|||
/// <param name="culture">
|
|||
/// The <see cref="T:System.Globalization.CultureInfo"/> to use as the current culture.
|
|||
/// </param>
|
|||
/// <param name="value">The <see cref="T:System.Object"/> to convert. </param>
|
|||
/// <exception cref="T:System.NotSupportedException">The conversion cannot be performed.</exception>
|
|||
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) |
|||
{ |
|||
string input = value as string; |
|||
if (input != null) |
|||
{ |
|||
string[] items = this.GetStringArray(input); |
|||
|
|||
List<T> result = new List<T>(); |
|||
|
|||
Array.ForEach( |
|||
items, |
|||
s => |
|||
{ |
|||
object item = this.typeConverter.ConvertFromInvariantString(s); |
|||
if (item != null) |
|||
{ |
|||
result.Add((T)item); |
|||
} |
|||
}); |
|||
|
|||
return result; |
|||
} |
|||
|
|||
return base.ConvertFrom(context, culture, value); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Converts the given value object to the specified type, using the specified context and culture
|
|||
/// information.
|
|||
/// </summary>
|
|||
/// <returns>
|
|||
/// An <see cref="T:System.Object"/> that represents the converted value.
|
|||
/// </returns>
|
|||
/// <param name="context">
|
|||
/// An <see cref="T:System.ComponentModel.ITypeDescriptorContext"/> that provides a format context.
|
|||
/// </param>
|
|||
/// <param name="culture">
|
|||
/// A <see cref="T:System.Globalization.CultureInfo"/>. If null is passed, the current culture is assumed.
|
|||
/// </param>
|
|||
/// <param name="value">The <see cref="T:System.Object"/> to convert. </param>
|
|||
/// <param name="destinationType">
|
|||
/// The <see cref="T:System.Type"/> to convert the <paramref name="value"/> parameter to.
|
|||
/// </param>
|
|||
/// <exception cref="T:System.ArgumentNullException">
|
|||
/// The <paramref name="destinationType"/> parameter is null.
|
|||
/// </exception>
|
|||
/// <exception cref="T:System.NotSupportedException">The conversion cannot be performed.
|
|||
/// </exception>
|
|||
public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType) |
|||
{ |
|||
if (destinationType == typeof(string)) |
|||
{ |
|||
return string.Join(",", (IList<T>)value); |
|||
} |
|||
|
|||
return base.ConvertTo(context, culture, value, destinationType); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Splits a string by comma to return an array of string values.
|
|||
/// </summary>
|
|||
/// <param name="input">
|
|||
/// The input string to split.
|
|||
/// </param>
|
|||
/// <returns>
|
|||
/// The <see cref="string"/> array from the comma separated values.
|
|||
/// </returns>
|
|||
protected string[] GetStringArray(string input) |
|||
{ |
|||
string[] result = input.Split(',').Select(s => s.Trim()).ToArray(); |
|||
|
|||
return result; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,220 @@ |
|||
// --------------------------------------------------------------------------------------------------------------------
|
|||
// <copyright file="QueryParamParser.cs" company="James South">
|
|||
// Copyright (c) James South.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
// </copyright>
|
|||
// <summary>
|
|||
// The query parameter parser that converts string values to different types.
|
|||
// </summary>
|
|||
// --------------------------------------------------------------------------------------------------------------------
|
|||
|
|||
namespace ImageProcessor.Web.Helpers |
|||
{ |
|||
using System; |
|||
using System.Collections.Concurrent; |
|||
using System.Collections.Generic; |
|||
using System.ComponentModel; |
|||
using System.Drawing; |
|||
using System.Globalization; |
|||
using System.Linq.Expressions; |
|||
|
|||
/// <summary>
|
|||
/// The query parameter parser that converts string values to different types.
|
|||
/// </summary>
|
|||
public class QueryParamParser |
|||
{ |
|||
/// <summary>
|
|||
/// A new instance of the <see cref="QueryParamParser"/> class.
|
|||
/// with lazy initialization.
|
|||
/// </summary>
|
|||
private static readonly Lazy<QueryParamParser> Lazy = new Lazy<QueryParamParser>(() => new QueryParamParser()); |
|||
|
|||
/// <summary>
|
|||
/// The cache for storing created default types.
|
|||
/// </summary>
|
|||
private static readonly ConcurrentDictionary<Type, object> TypeDefaultsCache = new ConcurrentDictionary<Type, object>(); |
|||
|
|||
/// <summary>
|
|||
/// Prevents a default instance of the <see cref="QueryParamParser"/> class from being created.
|
|||
/// </summary>
|
|||
private QueryParamParser() |
|||
{ |
|||
this.AddColorConverters(); |
|||
this.AddListConverters(); |
|||
this.AddArrayConverters(); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets the current <see cref="QueryParamParser"/> instance.
|
|||
/// </summary>
|
|||
public static QueryParamParser Instance |
|||
{ |
|||
get |
|||
{ |
|||
return Lazy.Value; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Parses the given string value converting it to the given type.
|
|||
/// </summary>
|
|||
/// <param name="value">
|
|||
/// The <see cref="String"/> value to parse.
|
|||
/// </param>
|
|||
/// <param name="culture">
|
|||
/// The <see cref="CultureInfo"/> to use as the current culture.
|
|||
/// <remarks>If not set will parse using <see cref="CultureInfo.InvariantCulture"/></remarks>
|
|||
/// </param>
|
|||
/// <typeparam name="T">
|
|||
/// The <see cref="Type"/> to convert the string to.
|
|||
/// </typeparam>
|
|||
/// <returns>
|
|||
/// The <see cref="T"/>.
|
|||
/// </returns>
|
|||
public T ParseValue<T>(string value, CultureInfo culture = null) |
|||
{ |
|||
return (T)this.ParseValue(typeof(T), value, culture); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Parses the given string value converting it to the given type.
|
|||
/// </summary>
|
|||
/// <param name="type">
|
|||
/// The <see cref="Type"/> to convert the string to.
|
|||
/// </param>
|
|||
/// <param name="value">
|
|||
/// The <see cref="String"/> value to parse.
|
|||
/// </param>
|
|||
/// <param name="culture">
|
|||
/// The <see cref="CultureInfo"/> to use as the current culture.
|
|||
/// <remarks>If not set will parse using <see cref="CultureInfo.InvariantCulture"/></remarks>
|
|||
/// </param>
|
|||
/// <returns>
|
|||
/// The <see cref="object"/>.
|
|||
/// </returns>
|
|||
public object ParseValue(Type type, string value, CultureInfo culture = null) |
|||
{ |
|||
if (culture == null) |
|||
{ |
|||
culture = CultureInfo.InvariantCulture; |
|||
} |
|||
|
|||
TypeConverter converter = TypeDescriptor.GetConverter(type); |
|||
try |
|||
{ |
|||
// ReSharper disable once AssignNullToNotNullAttribute
|
|||
return converter.ConvertFrom(null, culture, value); |
|||
} |
|||
catch |
|||
{ |
|||
// Return the default value
|
|||
return TypeDefaultsCache.GetOrAdd(type, t => this.GetDefaultValue(type)); |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Adds a type converter to the parser.
|
|||
/// </summary>
|
|||
/// <param name="type">
|
|||
/// The <see cref="Type"/> to add a converter for.
|
|||
/// </param>
|
|||
/// <param name="converterType">
|
|||
/// The type of <see cref="TypeConverter"/> to add.
|
|||
/// </param>
|
|||
/// <returns>
|
|||
/// The <see cref="TypeDescriptionProvider"/>.
|
|||
/// </returns>
|
|||
public TypeDescriptionProvider AddTypeConverter(Type type, Type converterType) |
|||
{ |
|||
return TypeDescriptor.AddAttributes(type, new TypeConverterAttribute(converterType)); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Adds color converters.
|
|||
/// </summary>
|
|||
private void AddColorConverters() |
|||
{ |
|||
this.AddTypeConverter(typeof(Color), typeof(ExtendedColorTypeConverter)); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Adds a selection of default list type converters.
|
|||
/// </summary>
|
|||
private void AddListConverters() |
|||
{ |
|||
this.AddTypeConverter(typeof(List<sbyte>), typeof(GenericListTypeConverter<sbyte>)); |
|||
this.AddTypeConverter(typeof(List<byte>), typeof(GenericListTypeConverter<byte>)); |
|||
|
|||
this.AddTypeConverter(typeof(List<short>), typeof(GenericListTypeConverter<short>)); |
|||
this.AddTypeConverter(typeof(List<ushort>), typeof(GenericListTypeConverter<ushort>)); |
|||
|
|||
this.AddTypeConverter(typeof(List<int>), typeof(GenericListTypeConverter<int>)); |
|||
this.AddTypeConverter(typeof(List<uint>), typeof(GenericListTypeConverter<uint>)); |
|||
|
|||
this.AddTypeConverter(typeof(List<long>), typeof(GenericListTypeConverter<long>)); |
|||
this.AddTypeConverter(typeof(List<ulong>), typeof(GenericListTypeConverter<ulong>)); |
|||
|
|||
this.AddTypeConverter(typeof(List<decimal>), typeof(GenericListTypeConverter<decimal>)); |
|||
this.AddTypeConverter(typeof(List<float>), typeof(GenericListTypeConverter<float>)); |
|||
this.AddTypeConverter(typeof(List<double>), typeof(GenericListTypeConverter<double>)); |
|||
|
|||
this.AddTypeConverter(typeof(List<string>), typeof(GenericListTypeConverter<string>)); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Adds a selection of default array type converters.
|
|||
/// </summary>
|
|||
private void AddArrayConverters() |
|||
{ |
|||
this.AddTypeConverter(typeof(sbyte[]), typeof(GenericArrayTypeConverter<sbyte>)); |
|||
this.AddTypeConverter(typeof(byte[]), typeof(GenericArrayTypeConverter<byte>)); |
|||
|
|||
this.AddTypeConverter(typeof(short[]), typeof(GenericArrayTypeConverter<short>)); |
|||
this.AddTypeConverter(typeof(ushort[]), typeof(GenericArrayTypeConverter<ushort>)); |
|||
|
|||
this.AddTypeConverter(typeof(int[]), typeof(GenericArrayTypeConverter<int>)); |
|||
this.AddTypeConverter(typeof(uint[]), typeof(GenericArrayTypeConverter<uint>)); |
|||
|
|||
this.AddTypeConverter(typeof(long[]), typeof(GenericArrayTypeConverter<long>)); |
|||
this.AddTypeConverter(typeof(ulong[]), typeof(GenericArrayTypeConverter<ulong>)); |
|||
|
|||
this.AddTypeConverter(typeof(decimal[]), typeof(GenericArrayTypeConverter<decimal>)); |
|||
this.AddTypeConverter(typeof(float[]), typeof(GenericArrayTypeConverter<float>)); |
|||
this.AddTypeConverter(typeof(double[]), typeof(GenericArrayTypeConverter<double>)); |
|||
|
|||
this.AddTypeConverter(typeof(string[]), typeof(GenericArrayTypeConverter<string>)); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Returns the default value for the given type.
|
|||
/// </summary>
|
|||
/// <param name="type">
|
|||
/// The <see cref="Type"/> to return.
|
|||
/// </param>
|
|||
/// <returns>
|
|||
/// The <see cref="object"/> representing the default value.
|
|||
/// </returns>
|
|||
/// <exception cref="ArgumentNullException">
|
|||
/// Thrown if the given <see cref="Type"/> is null.
|
|||
/// </exception>
|
|||
private object GetDefaultValue(Type type) |
|||
{ |
|||
// Validate parameters.
|
|||
if (type == null) |
|||
{ |
|||
throw new ArgumentNullException("type"); |
|||
} |
|||
|
|||
// We want an Func<object> which returns the default.
|
|||
// Create that expression here.
|
|||
// Have to convert to object.
|
|||
// The default value, always get what the *code* tells us.
|
|||
Expression<Func<object>> e = |
|||
Expression.Lambda<Func<object>>( |
|||
Expression.Convert(Expression.Default(type), typeof(object))); |
|||
|
|||
// Compile and return the value.
|
|||
return e.Compile()(); |
|||
} |
|||
} |
|||
} |
|||
@ -1,2 +1,3 @@ |
|||
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation"> |
|||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=configuration_005Cshared/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary> |
|||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=configuration_005Cshared/@EntryIndexedValue">True</s:Boolean> |
|||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=helpers_005Cquerystringparser/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary> |
|||
@ -0,0 +1,136 @@ |
|||
// --------------------------------------------------------------------------------------------------------------------
|
|||
// <copyright file="IOHelper.cs" company="James South">
|
|||
// Copyright (c) James South.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
// </copyright>
|
|||
// <summary>
|
|||
// Provides helper method for traversing the file system.
|
|||
// <remarks>
|
|||
// Adapted from identically named class within <see href="https://github.com/umbraco/Umbraco-CMS" />
|
|||
// </remarks>
|
|||
// </summary>
|
|||
// --------------------------------------------------------------------------------------------------------------------
|
|||
|
|||
namespace ImageProcessor.Common.Helpers |
|||
{ |
|||
using System; |
|||
using System.Globalization; |
|||
using System.IO; |
|||
using System.Reflection; |
|||
|
|||
using ImageProcessor.Common.Extensions; |
|||
|
|||
/// <summary>
|
|||
/// Provides helper method for traversing the file system.
|
|||
/// <remarks>
|
|||
/// Adapted from identically named class within <see href="https://github.com/umbraco/Umbraco-CMS"/>
|
|||
/// </remarks>
|
|||
/// </summary>
|
|||
internal class IOHelper |
|||
{ |
|||
/// <summary>
|
|||
/// The root directory.
|
|||
/// </summary>
|
|||
private static string rootDirectory; |
|||
|
|||
/// <summary>
|
|||
/// Maps a virtual path to a physical path.
|
|||
/// </summary>
|
|||
/// <param name="virtualPath">
|
|||
/// The virtual path to map.
|
|||
/// </param>
|
|||
/// <returns>
|
|||
/// The <see cref="string"/> representing the physical path.
|
|||
/// </returns>
|
|||
public static string MapPath(string virtualPath) |
|||
{ |
|||
// Check if the path is already mapped
|
|||
// UNC Paths start with "\\". If the site is running off a network drive mapped paths
|
|||
// will look like "\\Whatever\Boo\Bar"
|
|||
if ((virtualPath.Length >= 2 && virtualPath[1] == Path.VolumeSeparatorChar) |
|||
|| virtualPath.StartsWith(@"\\")) |
|||
{ |
|||
return virtualPath; |
|||
} |
|||
|
|||
char separator = Path.DirectorySeparatorChar; |
|||
string root = GetRootDirectorySafe(); |
|||
string newPath = virtualPath.TrimStart('~', '/').Replace('/', separator); |
|||
return root + separator.ToString(CultureInfo.InvariantCulture) + newPath; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets the root directory bin folder for the currently running application.
|
|||
/// </summary>
|
|||
/// <returns>
|
|||
/// The <see cref="string"/> representing the root directory bin folder.
|
|||
/// </returns>
|
|||
public static string GetRootDirectoryBinFolder() |
|||
{ |
|||
string binFolder = string.Empty; |
|||
if (string.IsNullOrEmpty(rootDirectory)) |
|||
{ |
|||
DirectoryInfo directoryInfo = Assembly.GetExecutingAssembly().GetAssemblyFile().Directory; |
|||
if (directoryInfo != null) |
|||
{ |
|||
binFolder = directoryInfo.FullName; |
|||
} |
|||
|
|||
return binFolder; |
|||
} |
|||
|
|||
binFolder = Path.Combine(GetRootDirectorySafe(), "bin"); |
|||
|
|||
#if DEBUG
|
|||
string debugFolder = Path.Combine(binFolder, "debug"); |
|||
if (Directory.Exists(debugFolder)) |
|||
{ |
|||
return debugFolder; |
|||
} |
|||
#endif
|
|||
string releaseFolder = Path.Combine(binFolder, "release"); |
|||
if (Directory.Exists(releaseFolder)) |
|||
{ |
|||
return releaseFolder; |
|||
} |
|||
|
|||
if (Directory.Exists(binFolder)) |
|||
{ |
|||
return binFolder; |
|||
} |
|||
|
|||
return rootDirectory; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Returns the path to the root of the application, by getting the path to where the assembly where this
|
|||
/// method is included is present, then traversing until it's past the /bin directory. I.e. this makes it work
|
|||
/// even if the assembly is in a /bin/debug or /bin/release folder
|
|||
/// </summary>
|
|||
/// <returns>
|
|||
/// The <see cref="string"/> representing the root path of the currently running application.</returns>
|
|||
internal static string GetRootDirectorySafe() |
|||
{ |
|||
if (string.IsNullOrEmpty(rootDirectory) == false) |
|||
{ |
|||
return rootDirectory; |
|||
} |
|||
|
|||
string codeBase = Assembly.GetExecutingAssembly().CodeBase; |
|||
Uri uri = new Uri(codeBase); |
|||
string path = uri.LocalPath; |
|||
string baseDirectory = Path.GetDirectoryName(path); |
|||
if (string.IsNullOrEmpty(baseDirectory)) |
|||
{ |
|||
throw new Exception( |
|||
"No root directory could be resolved. Please ensure that your solution is correctly configured."); |
|||
} |
|||
|
|||
rootDirectory = baseDirectory.Contains("bin") |
|||
? baseDirectory.Substring(0, baseDirectory.LastIndexOf("bin", StringComparison.OrdinalIgnoreCase) - 1) |
|||
: baseDirectory; |
|||
|
|||
return rootDirectory; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,288 @@ |
|||
// --------------------------------------------------------------------------------------------------------------------
|
|||
// <copyright file="TypeFinder.cs" company="James South">
|
|||
// Copyright (c) James South.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
// </copyright>
|
|||
// <summary>
|
|||
// A utility class to find all classes of a certain type by reflection in the current bin folder
|
|||
// of the web application.
|
|||
// </summary>
|
|||
// --------------------------------------------------------------------------------------------------------------------
|
|||
|
|||
namespace ImageProcessor.Common.Helpers |
|||
{ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Diagnostics; |
|||
using System.IO; |
|||
using System.Linq; |
|||
using System.Reflection; |
|||
using System.Security; |
|||
using System.Threading; |
|||
|
|||
using ImageProcessor.Common.Extensions; |
|||
|
|||
/// <summary>
|
|||
/// A utility class to find all classes of a certain type by reflection in the current bin folder
|
|||
/// of the web application.
|
|||
/// </summary>
|
|||
/// <remarks>
|
|||
/// Adapted from identically named class within <see href="https://github.com/umbraco/Umbraco-CMS"/>
|
|||
/// </remarks>
|
|||
internal static class TypeFinder |
|||
{ |
|||
/// <summary>
|
|||
/// The local filtered assembly cache.
|
|||
/// </summary>
|
|||
private static readonly HashSet<Assembly> LocalFilteredAssemblyCache = new HashSet<Assembly>(); |
|||
|
|||
/// <summary>
|
|||
/// The local filtered assembly cache locker.
|
|||
/// </summary>
|
|||
private static readonly ReaderWriterLockSlim LocalFilteredAssemblyCacheLocker = new ReaderWriterLockSlim(); |
|||
|
|||
/// <summary>
|
|||
/// The reader-writer lock implementation.
|
|||
/// </summary>
|
|||
private static readonly ReaderWriterLockSlim Locker = new ReaderWriterLockSlim(); |
|||
|
|||
/// <summary>
|
|||
/// An assembly filter collection to filter out known types that definitely don't contain types
|
|||
/// we'd like to find or plugins.
|
|||
/// Umbraco uses ImageProcessor in it's core so add common exclusion files from that.
|
|||
/// </summary>
|
|||
/// <remarks>
|
|||
/// NOTE the comma versus period... comma delimits the name in an Assembly FullName property so
|
|||
/// if it ends with comma then its an exact name match.
|
|||
/// </remarks>
|
|||
private static readonly string[] KnownAssemblyExclusionFilter = |
|||
{ |
|||
"mscorlib,", "System.", "Antlr3.", "Autofac.", |
|||
"Autofac,", "Castle.", "ClientDependency.", |
|||
"DataAnnotationsExtensions.", |
|||
"DataAnnotationsExtensions,", "Dynamic,", |
|||
"HtmlDiff,", "Iesi.Collections,", "log4net,", |
|||
"Microsoft.", "Newtonsoft.", "NHibernate.", |
|||
"NHibernate,", "NuGet.", "RouteDebugger,", |
|||
"SqlCE4Umbraco,", "umbraco.datalayer,", |
|||
"umbraco.interfaces,", |
|||
"umbraco.webservices", "Lucene.", "Examine,", |
|||
"Examine.", "ServiceStack.", "MySql.", |
|||
"HtmlAgilityPack.", "TidyNet.", |
|||
"ICSharpCode.", "CookComputing.", |
|||
"AutoMapper,", "AutoMapper.", |
|||
"AzureDirectory,", "itextsharp,", |
|||
"UrlRewritingNet.", "HtmlAgilityPack,", |
|||
"MiniProfiler,", "Moq,", "nunit.framework,", |
|||
"TidyNet,", "WebDriver," |
|||
}; |
|||
|
|||
/// <summary>
|
|||
/// A collection of all assemblies.
|
|||
/// </summary>
|
|||
private static HashSet<Assembly> allAssemblies; |
|||
|
|||
/// <summary>
|
|||
/// The bin folder assemblies.
|
|||
/// </summary>
|
|||
private static HashSet<Assembly> binFolderAssemblies; |
|||
|
|||
/// <summary>
|
|||
/// Lazily loads a reference to all assemblies and only local assemblies.
|
|||
/// This is a modified version of:
|
|||
/// <see href="http://www.dominicpettifer.co.uk/Blog/44/how-to-get-a-reference-to-all-assemblies-in-the--bin-folder"/>
|
|||
/// </summary>
|
|||
/// <remarks>
|
|||
/// We do this because we cannot use AppDomain.Current.GetAssemblies() as this will return only assemblies that have been
|
|||
/// loaded in the CLR, not all assemblies.
|
|||
/// See these threads:
|
|||
/// <see href="http://issues.umbraco.org/issue/U5-198"/>
|
|||
/// <see cref="http://stackoverflow.com/questions/3552223/asp-net-appdomain-currentdomain-getassemblies-assemblies-missing-after-app"/>
|
|||
/// <see cref="http://stackoverflow.com/questions/2477787/difference-between-appdomain-getassemblies-and-buildmanager-getreferencedassembl"/>
|
|||
/// </remarks>
|
|||
/// <returns>
|
|||
/// The <see cref="HashSet{Assembly}"/>.
|
|||
/// </returns>
|
|||
internal static HashSet<Assembly> GetAllAssemblies() |
|||
{ |
|||
using (UpgradeableReadLock locker = new UpgradeableReadLock(Locker)) |
|||
{ |
|||
if (allAssemblies == null) |
|||
{ |
|||
locker.UpgradeToWriteLock(); |
|||
|
|||
try |
|||
{ |
|||
// NOTE: we cannot use AppDomain.CurrentDomain.GetAssemblies() because this only returns assemblies that have
|
|||
// already been loaded in to the app domain, instead we will look directly into the bin folder and load each one.
|
|||
string binFolder = IOHelper.GetRootDirectoryBinFolder(); |
|||
List<string> binAssemblyFiles = Directory.GetFiles(binFolder, "*.dll", SearchOption.TopDirectoryOnly).ToList(); |
|||
HashSet<Assembly> assemblies = new HashSet<Assembly>(); |
|||
|
|||
foreach (string file in binAssemblyFiles) |
|||
{ |
|||
try |
|||
{ |
|||
AssemblyName assemblyName = AssemblyName.GetAssemblyName(file); |
|||
assemblies.Add(Assembly.Load(assemblyName)); |
|||
} |
|||
catch (Exception ex) |
|||
{ |
|||
if (ex is SecurityException || ex is BadImageFormatException) |
|||
{ |
|||
// Swallow exception but allow debugging.
|
|||
Debug.WriteLine(ex.Message); |
|||
} |
|||
else |
|||
{ |
|||
throw; |
|||
} |
|||
} |
|||
} |
|||
|
|||
// If for some reason they are still no assemblies, then use the AppDomain to load in already loaded assemblies.
|
|||
if (!assemblies.Any()) |
|||
{ |
|||
foreach (Assembly assembly in AppDomain.CurrentDomain.GetAssemblies()) |
|||
{ |
|||
assemblies.Add(assembly); |
|||
} |
|||
} |
|||
|
|||
// Here we are trying to get the App_Code assembly
|
|||
string[] fileExtensions = { ".cs", ".vb" }; |
|||
DirectoryInfo appCodeFolder = new DirectoryInfo(IOHelper.MapPath("~/App_code")); |
|||
|
|||
// Check if the folder exists and if there are any files in it with the supported file extensions
|
|||
if (appCodeFolder.Exists && fileExtensions.Any(x => appCodeFolder.GetFiles("*" + x).Any())) |
|||
{ |
|||
Assembly appCodeAssembly = Assembly.Load("App_Code"); |
|||
if (!assemblies.Contains(appCodeAssembly)) |
|||
{ |
|||
assemblies.Add(appCodeAssembly); |
|||
} |
|||
} |
|||
|
|||
// Now set the allAssemblies
|
|||
allAssemblies = new HashSet<Assembly>(assemblies); |
|||
} |
|||
catch (InvalidOperationException e) |
|||
{ |
|||
if (!(e.InnerException is SecurityException)) |
|||
{ |
|||
throw; |
|||
} |
|||
|
|||
binFolderAssemblies = allAssemblies; |
|||
} |
|||
} |
|||
|
|||
return allAssemblies; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Returns only assemblies found in the bin folder that have been loaded into the app domain.
|
|||
/// </summary>
|
|||
/// <returns>
|
|||
/// The collection of assemblies.
|
|||
/// </returns>
|
|||
internal static HashSet<Assembly> GetBinAssemblies() |
|||
{ |
|||
if (binFolderAssemblies == null) |
|||
{ |
|||
using (new WriteLock(Locker)) |
|||
{ |
|||
Assembly[] assemblies = GetAssembliesWithKnownExclusions().ToArray(); |
|||
DirectoryInfo binFolder = Assembly.GetExecutingAssembly().GetAssemblyFile().Directory; |
|||
// ReSharper disable once PossibleNullReferenceException
|
|||
List<string> binAssemblyFiles = Directory.GetFiles(binFolder.FullName, "*.dll", SearchOption.TopDirectoryOnly).ToList(); |
|||
IEnumerable<AssemblyName> domainAssemblyNames = binAssemblyFiles.Select(AssemblyName.GetAssemblyName); |
|||
HashSet<Assembly> safeDomainAssemblies = new HashSet<Assembly>(); |
|||
HashSet<Assembly> binFolderAssemblyList = new HashSet<Assembly>(); |
|||
|
|||
foreach (Assembly assembly in assemblies) |
|||
{ |
|||
safeDomainAssemblies.Add(assembly); |
|||
} |
|||
|
|||
foreach (AssemblyName assemblyName in domainAssemblyNames) |
|||
{ |
|||
Assembly foundAssembly = safeDomainAssemblies |
|||
.FirstOrDefault(a => a.GetAssemblyFile() == assemblyName.GetAssemblyFile()); |
|||
|
|||
if (foundAssembly != null) |
|||
{ |
|||
binFolderAssemblyList.Add(foundAssembly); |
|||
} |
|||
} |
|||
|
|||
binFolderAssemblies = new HashSet<Assembly>(binFolderAssemblyList); |
|||
} |
|||
} |
|||
|
|||
return binFolderAssemblies; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Return a list of found local Assemblies excluding the known assemblies we don't want to scan
|
|||
/// and excluding the ones passed in and excluding the exclusion list filter, the results of this are
|
|||
/// cached for performance reasons.
|
|||
/// </summary>
|
|||
/// <param name="excludeFromResults">
|
|||
/// An <see cref="IEnumerable{Assembly}"/> to exclude.
|
|||
/// </param>
|
|||
/// <returns>The collection of local assemblies.</returns>
|
|||
internal static HashSet<Assembly> GetAssembliesWithKnownExclusions( |
|||
IEnumerable<Assembly> excludeFromResults = null) |
|||
{ |
|||
using (UpgradeableReadLock locker = new UpgradeableReadLock(LocalFilteredAssemblyCacheLocker)) |
|||
{ |
|||
if (LocalFilteredAssemblyCache.Any()) |
|||
{ |
|||
return LocalFilteredAssemblyCache; |
|||
} |
|||
|
|||
locker.UpgradeToWriteLock(); |
|||
|
|||
IEnumerable<Assembly> assemblies = GetFilteredAssemblies(excludeFromResults, KnownAssemblyExclusionFilter); |
|||
foreach (Assembly assembly in assemblies) |
|||
{ |
|||
LocalFilteredAssemblyCache.Add(assembly); |
|||
} |
|||
|
|||
return LocalFilteredAssemblyCache; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Return a distinct list of found local Assemblies and excluding the ones passed in and excluding the exclusion list filter
|
|||
/// </summary>
|
|||
/// <param name="excludeFromResults">
|
|||
/// An <see cref="IEnumerable{Assembly}"/> to exclude.
|
|||
/// </param>
|
|||
/// <param name="exclusionFilter">
|
|||
/// An <see cref="string"/> array containing exclusion filters.
|
|||
/// </param>
|
|||
/// <returns>The collection of filtered local assemblies.</returns>
|
|||
private static IEnumerable<Assembly> GetFilteredAssemblies( |
|||
IEnumerable<Assembly> excludeFromResults = null, |
|||
string[] exclusionFilter = null) |
|||
{ |
|||
if (excludeFromResults == null) |
|||
{ |
|||
excludeFromResults = new HashSet<Assembly>(); |
|||
} |
|||
|
|||
if (exclusionFilter == null) |
|||
{ |
|||
exclusionFilter = new string[] { }; |
|||
} |
|||
|
|||
return GetAllAssemblies() |
|||
.Where(x => !excludeFromResults.Contains(x) |
|||
&& !x.GlobalAssemblyCache |
|||
&& !exclusionFilter.Any(f => x.FullName.StartsWith(f))); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,126 @@ |
|||
// --------------------------------------------------------------------------------------------------------------------
|
|||
// <copyright file="UpgradeableReadLock.cs" company="James South">
|
|||
// Copyright (c) James South.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
// </copyright>
|
|||
// <summary>
|
|||
// Provides a convenience methodology for implementing upgradeable locked access to resources.
|
|||
// </summary>
|
|||
// --------------------------------------------------------------------------------------------------------------------
|
|||
|
|||
namespace ImageProcessor.Common.Helpers |
|||
{ |
|||
using System; |
|||
using System.Threading; |
|||
|
|||
/// <summary>
|
|||
/// Provides a convenience methodology for implementing upgradeable locked access to resources.
|
|||
/// </summary>
|
|||
/// <remarks>
|
|||
/// Adapted from identically named class within <see href="https://github.com/umbraco/Umbraco-CMS"/>
|
|||
/// </remarks>
|
|||
internal sealed class UpgradeableReadLock : IDisposable |
|||
{ |
|||
/// <summary>
|
|||
/// The locker to lock against.
|
|||
/// </summary>
|
|||
private readonly ReaderWriterLockSlim locker; |
|||
|
|||
/// <summary>
|
|||
/// A value indicating whether the locker has been upgraded to a writeable lock.
|
|||
/// </summary>
|
|||
private bool upgraded; |
|||
|
|||
/// <summary>
|
|||
/// A value indicating whether this instance of the given entity has been disposed.
|
|||
/// </summary>
|
|||
/// <value><see langword="true"/> if this instance has been disposed; otherwise, <see langword="false"/>.</value>
|
|||
/// <remarks>
|
|||
/// If the entity is disposed, it must not be disposed a second
|
|||
/// time. The isDisposed field is set the first time the entity
|
|||
/// is disposed. If the isDisposed field is true, then the Dispose()
|
|||
/// method will not dispose again. This help not to prolong the entity's
|
|||
/// life in the Garbage Collector.
|
|||
/// </remarks>
|
|||
private bool isDisposed; |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="UpgradeableReadLock"/> class.
|
|||
/// </summary>
|
|||
/// <param name="locker">
|
|||
/// The locker.
|
|||
/// </param>
|
|||
public UpgradeableReadLock(ReaderWriterLockSlim locker) |
|||
{ |
|||
this.locker = locker; |
|||
this.locker.EnterUpgradeableReadLock(); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Finalizes an instance of the <see cref="UpgradeableReadLock"/> class.
|
|||
/// </summary>
|
|||
/// <remarks>
|
|||
/// Use C# destructor syntax for finalization code.
|
|||
/// This destructor will run only if the Dispose method
|
|||
/// does not get called.
|
|||
/// It gives your base class the opportunity to finalize.
|
|||
/// Do not provide destructors in types derived from this class.
|
|||
/// </remarks>
|
|||
~UpgradeableReadLock() |
|||
{ |
|||
// Do not re-create Dispose clean-up code here.
|
|||
// Calling Dispose(false) is optimal in terms of
|
|||
// readability and maintainability.
|
|||
this.Dispose(false); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Tries to enter the locker in write mode.
|
|||
/// </summary>
|
|||
public void UpgradeToWriteLock() |
|||
{ |
|||
this.locker.EnterWriteLock(); |
|||
this.upgraded = true; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Disposes the object and frees resources for the Garbage Collector.
|
|||
/// </summary>
|
|||
public void Dispose() |
|||
{ |
|||
this.Dispose(true); |
|||
|
|||
// This object will be cleaned up by the Dispose method.
|
|||
// Therefore, you should call GC.SuppressFinalize to
|
|||
// take this object off the finalization queue
|
|||
// and prevent finalization code for this object
|
|||
// from executing a second time.
|
|||
GC.SuppressFinalize(this); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Disposes the object and frees resources for the Garbage Collector.
|
|||
/// </summary>
|
|||
/// <param name="disposing">If true, the object gets disposed.</param>
|
|||
private void Dispose(bool disposing) |
|||
{ |
|||
if (this.isDisposed) |
|||
{ |
|||
return; |
|||
} |
|||
|
|||
if (disposing) |
|||
{ |
|||
if (this.upgraded) |
|||
{ |
|||
this.locker.ExitWriteLock(); |
|||
} |
|||
|
|||
this.locker.ExitUpgradeableReadLock(); |
|||
} |
|||
|
|||
// Note disposing is done.
|
|||
this.isDisposed = true; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,107 @@ |
|||
// --------------------------------------------------------------------------------------------------------------------
|
|||
// <copyright file="WriteLock.cs" company="James South">
|
|||
// Copyright (c) James South.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
// </copyright>
|
|||
// <summary>
|
|||
// Provides a convenience methodology for implementing writeable locked access to resources.
|
|||
// </summary>
|
|||
// --------------------------------------------------------------------------------------------------------------------
|
|||
|
|||
namespace ImageProcessor.Common.Helpers |
|||
{ |
|||
using System; |
|||
using System.Threading; |
|||
|
|||
/// <summary>
|
|||
/// Provides a convenience methodology for implementing writable locked access to resources.
|
|||
/// </summary>
|
|||
/// <remarks>
|
|||
/// Adapted from identically named class within <see href="https://github.com/umbraco/Umbraco-CMS"/>
|
|||
/// </remarks>
|
|||
internal sealed class WriteLock : IDisposable |
|||
{ |
|||
/// <summary>
|
|||
/// The locker to lock against.
|
|||
/// </summary>
|
|||
private readonly ReaderWriterLockSlim locker; |
|||
|
|||
/// <summary>
|
|||
/// A value indicating whether this instance of the given entity has been disposed.
|
|||
/// </summary>
|
|||
/// <value><see langword="true"/> if this instance has been disposed; otherwise, <see langword="false"/>.</value>
|
|||
/// <remarks>
|
|||
/// If the entity is disposed, it must not be disposed a second
|
|||
/// time. The isDisposed field is set the first time the entity
|
|||
/// is disposed. If the isDisposed field is true, then the Dispose()
|
|||
/// method will not dispose again. This help not to prolong the entity's
|
|||
/// life in the Garbage Collector.
|
|||
/// </remarks>
|
|||
private bool isDisposed; |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="WriteLock"/> class.
|
|||
/// </summary>
|
|||
/// <param name="locker">
|
|||
/// The locker.
|
|||
/// </param>
|
|||
public WriteLock(ReaderWriterLockSlim locker) |
|||
{ |
|||
this.locker = locker; |
|||
this.locker.EnterWriteLock(); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Finalizes an instance of the <see cref="WriteLock"/> class.
|
|||
/// </summary>
|
|||
/// <remarks>
|
|||
/// Use C# destructor syntax for finalization code.
|
|||
/// This destructor will run only if the Dispose method
|
|||
/// does not get called.
|
|||
/// It gives your base class the opportunity to finalize.
|
|||
/// Do not provide destructors in types derived from this class.
|
|||
/// </remarks>
|
|||
~WriteLock() |
|||
{ |
|||
// Do not re-create Dispose clean-up code here.
|
|||
// Calling Dispose(false) is optimal in terms of
|
|||
// readability and maintainability.
|
|||
this.Dispose(false); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Disposes the object and frees resources for the Garbage Collector.
|
|||
/// </summary>
|
|||
public void Dispose() |
|||
{ |
|||
this.Dispose(true); |
|||
|
|||
// This object will be cleaned up by the Dispose method.
|
|||
// Therefore, you should call GC.SuppressFinalize to
|
|||
// take this object off the finalization queue
|
|||
// and prevent finalization code for this object
|
|||
// from executing a second time.
|
|||
GC.SuppressFinalize(this); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Disposes the object and frees resources for the Garbage Collector.
|
|||
/// </summary>
|
|||
/// <param name="disposing">If true, the object gets disposed.</param>
|
|||
private void Dispose(bool disposing) |
|||
{ |
|||
if (this.isDisposed) |
|||
{ |
|||
return; |
|||
} |
|||
|
|||
if (disposing) |
|||
{ |
|||
this.locker.ExitWriteLock(); |
|||
} |
|||
|
|||
// Note disposing is done.
|
|||
this.isDisposed = true; |
|||
} |
|||
} |
|||
} |
|||
Loading…
Reference in new issue