Skip to content

Commit

Permalink
Get flag names (#98)
Browse files Browse the repository at this point in the history
* Add GetFlagNames method

* Add EnumCache class

* Add cache to EnumHelper

* Remove EnumHelper from Runtime and fix some issues

* Versioning
  • Loading branch information
geoperez authored Mar 12, 2018
1 parent 67617ff commit f600ee7
Show file tree
Hide file tree
Showing 6 changed files with 139 additions and 92 deletions.
184 changes: 110 additions & 74 deletions src/Unosquare.Swan.Lite/Components/EnumHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,29 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Abstractions;

/// <summary>
/// Provide Enumerations helpers with internal cache
/// </summary>
public static class EnumHelper
public class EnumHelper
: SingletonBase<CacheRepository<Type, Tuple<string, object>>>
{
private static readonly Dictionary<Type, Tuple<int, string>[]> ValueCache =
new Dictionary<Type, Tuple<int, string>[]>();

private static readonly Dictionary<Type, Tuple<int, string>[]> IndexCache =
new Dictionary<Type, Tuple<int, string>[]>();

private static readonly Dictionary<Type, Array> ArrayValueCache =
new Dictionary<Type, Array>();
/// <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 enumarator names and their value stored for the specified type</returns>
public static Tuple<string, object>[] Retrieve<T>()
where T : struct, IConvertible
{
return Instance.Retrieve(typeof(T), () =>
{
var values = Enum.GetValues(typeof(T)).Cast<object>();
private static readonly object LockObject = new object();
return values.Select(item => new Tuple<string, object>(Enum.GetName(typeof(T), item), item)).ToList();
});
}

/// <summary>
/// Gets the cached items with the enum item value.
Expand All @@ -30,68 +37,49 @@ public static class EnumHelper
/// that represents items with the enum item value
/// </returns>
public static Tuple<int, string>[] GetItemsWithValue<T>(bool humanize = true)
where T : struct, IConvertible
{
lock (LockObject)
{
var tupleName = typeof(T);

if (ValueCache.ContainsKey(tupleName) == false)
{
ValueCache.Add(tupleName, Enum.GetNames(tupleName)
.Select(x =>
new Tuple<int, string>((int) Enum.Parse(tupleName, x), humanize ? x.Humanize() : x))
.ToArray());
}

return ValueCache[tupleName];
}
return Retrieve<T>()
.Select(x => new Tuple<int, string>((int) x.Item2, humanize ? x.Item1.Humanize() : x.Item1))
.ToArray();
}

/// <summary>
/// Gets the cached items with the enum item index.
/// Gets the flag values.
/// </summary>
/// <typeparam name="T">The type of enumeration</typeparam>
/// <param name="humanize">if set to <c>true</c> [humanize].</param>
/// <typeparam name="TEnum">The type of the enum.</typeparam>
/// <param name="value">The value.</param>
/// <param name="ignoreZero">if set to <c>true</c> [ignore zero].</param>
/// <returns>
/// A collection of Type/Tuple pairs that represents items with the enum item value
/// A list of values in the flag
/// </returns>
public static Tuple<int, string>[] GetItemsWithIndex<T>(bool humanize = true)
public static List<int> GetFlagValues<TEnum>(int value, bool ignoreZero = false)
where TEnum : struct, IConvertible
{
lock (LockObject)
{
var tupleName = typeof(T);

if (IndexCache.ContainsKey(tupleName) == false)
{
var i = 0;

IndexCache.Add(tupleName, Enum.GetNames(tupleName)
.Select(x => new Tuple<int, string>(i++, humanize ? x.Humanize() : x))
.ToArray());
}

return IndexCache[tupleName];
}
return Retrieve<TEnum>()
.Select(x => (int) x.Item2)
.When(() => ignoreZero, q => q.Where(f => f != 0))
.Where(x => (x & value) == x)
.ToList();
}

/// <summary>
/// Gets the cached values array.
/// Gets the flag values.
/// </summary>
/// <typeparam name="TEnum">The type of the enum.</typeparam>
/// <returns>The array with values from the enumeration</returns>
public static Array GetValuesArray<TEnum>()
/// <param name="value">The value.</param>
/// <param name="ignoreZero">if set to <c>true</c> [ignore zero].</param>
/// <returns>
/// A list of values in the flag
/// </returns>
public static List<long> GetFlagValues<TEnum>(long value, bool ignoreZero = false)
where TEnum : struct, IConvertible
{
lock (LockObject)
{
var key = typeof(TEnum);

if (ArrayValueCache.ContainsKey(key) == false)
{
ArrayValueCache.Add(key, Enum.GetValues(typeof(TEnum)));
}

return ArrayValueCache[key];
}
return Retrieve<TEnum>()
.Select(x => (long) x.Item2)
.When(() => ignoreZero, q => q.Where(f => f != 0))
.Where(x => (x & value) == x)
.ToList();
}

/// <summary>
Expand All @@ -103,44 +91,92 @@ public static Array GetValuesArray<TEnum>()
/// <returns>
/// A list of values in the flag
/// </returns>
public static List<int> GetFlagValues<TEnum>(int value, bool ignoreZero = false)
public static List<byte> GetFlagValues<TEnum>(byte value, bool ignoreZero = false)
where TEnum : struct, IConvertible
{
return GetValuesArray<TEnum>()
.Cast<int>()
return Retrieve<TEnum>()
.Select(x => (byte) x.Item2)
.When(() => ignoreZero, q => q.Where(f => f != 0))
.Where(f => (f & value) == f)
.Where(x => (x & value) == x)
.ToList();
}

/// <summary>
/// Gets the flag values.
/// Gets the flag names
/// </summary>
/// <typeparam name="TEnum">The type of the enum</typeparam>
/// <param name="value">the value</param>
/// <param name="ignoreZero">if set to <c>true</c> [ignore zero].</param>
/// <param name="humanize">if set to <c>true</c> [humanize].</param>
/// <returns>
/// A list of flag names
/// </returns>
public static List<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)
.Select(x => humanize ? x.Item1.Humanize() : x.Item1)
.ToList();
}

/// <summary>
/// Gets the flag names.
/// </summary>
/// <typeparam name="TEnum">The type of the enum.</typeparam>
/// <param name="value">The value.</param>
/// <returns>A list of values in the flag</returns>
public static List<long> GetFlagValues<TEnum>(long value)
/// <param name="ignoreZero">if set to <c>true</c> [ignore zero].</param>
/// <param name="humanize">if set to <c>true</c> [humanize].</param>
/// <returns>
/// A list of flag names
/// </returns>
public static List<string> GetFlagNames<TEnum>(long value, bool ignoreZero = false, bool humanize = true)
where TEnum : struct, IConvertible
{
return GetValuesArray<TEnum>()
.Cast<long>()
.Where(f => (f & value) == f)
return Retrieve<TEnum>()
.When(() => ignoreZero, q => q.Where(f => (long) f.Item2 != 0))
.Where(x => ((long) x.Item2 & value) == (long) x.Item2)
.Select(x => humanize ? x.Item1.Humanize() : x.Item1)
.ToList();
}

/// <summary>
/// Gets the flag values.
/// Gets the flag names.
/// </summary>
/// <typeparam name="TEnum">The type of the enum.</typeparam>
/// <param name="value">The value.</param>
/// <returns>A list of values in the flag</returns>
public static List<byte> GetFlagValues<TEnum>(byte value)
/// <param name="ignoreZero">if set to <c>true</c> [ignore zero].</param>
/// <param name="humanize">if set to <c>true</c> [humanize].</param>
/// <returns>
/// A list of flag names
/// </returns>
public static List<string> GetFlagNames<TEnum>(byte value, bool ignoreZero = false, bool humanize = true)
where TEnum : struct, IConvertible
{
return GetValuesArray<TEnum>()
.Cast<byte>()
.Where(f => (f & value) == f)
return Retrieve<TEnum>()
.When(() => ignoreZero, q => q.Where(f => (byte) f.Item2 != 0))
.Where(x => ((byte) x.Item2 & value) == (byte) x.Item2)
.Select(x => humanize ? x.Item1.Humanize() : x.Item1)
.ToList();
}

/// <summary>
/// Gets the cached items with the enum item index.
/// </summary>
/// <typeparam name="T">The type of enumeration</typeparam>
/// <param name="humanize">if set to <c>true</c> [humanize].</param>
/// <returns>
/// A collection of Type/Tuple pairs that represents items with the enum item value
/// </returns>
public static Tuple<int, string>[] GetItemsWithIndex<T>(bool humanize = true)
where T : struct, IConvertible
{
var i = 0;

return Retrieve<T>()
.Select(x => new Tuple<int, string>(i++, humanize ? x.Item1.Humanize() : x.Item1))
.ToArray();
}
}
}
6 changes: 4 additions & 2 deletions src/Unosquare.Swan.Lite/Runtime.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,9 @@ public static class Runtime
private static readonly Lazy<PropertyTypeCache> _propertyTypeCache = new Lazy<PropertyTypeCache>(() => new PropertyTypeCache());

private static readonly Lazy<AttributeCache> _attributeCache = new Lazy<AttributeCache>(() => new AttributeCache());


private static readonly Lazy<EnumHelper> _enumHelper = new Lazy<EnumHelper>(() => new EnumHelper());

private static readonly Lazy<ObjectValidator> _objectValidator = new Lazy<ObjectValidator>(() => new ObjectValidator());

private static readonly Lazy<FieldTypeCache> _fieldTypeCache = new Lazy<FieldTypeCache>(() => new FieldTypeCache());
Expand Down Expand Up @@ -181,7 +183,7 @@ public static bool IsTheOnlyInstance
/// The attribute cache.
/// </value>
public static AttributeCache AttributeCache => _attributeCache.Value;

/// <summary>
/// Gets the object validator.
/// </summary>
Expand Down
2 changes: 1 addition & 1 deletion src/Unosquare.Swan.Lite/Unosquare.Swan.Lite.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
<PackageId>Unosquare.Swan.Lite</PackageId>
<CodeAnalysisRuleSet>..\..\StyleCop.Analyzers.ruleset</CodeAnalysisRuleSet>
<DebugType>Full</DebugType>
<Version>0.27.0</Version>
<Version>0.28.0</Version>
<Authors>Unosquare</Authors>
<PackageIconUrl>https://github.com/unosquare/swan/raw/master/swan-logo-32.png</PackageIconUrl>
<PackageProjectUrl>https://github.com/unosquare/swan</PackageProjectUrl>
Expand Down
2 changes: 1 addition & 1 deletion src/Unosquare.Swan/Unosquare.Swan.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
<PackageId>Unosquare.Swan</PackageId>
<CodeAnalysisRuleSet>..\..\StyleCop.Analyzers.ruleset</CodeAnalysisRuleSet>
<DebugType>Full</DebugType>
<Version>0.27.0</Version>
<Version>0.28.0</Version>
<Authors>Unosquare</Authors>
<PackageIconUrl>https://github.com/unosquare/swan/raw/master/swan-logo-32.png</PackageIconUrl>
<PackageProjectUrl>https://github.com/unosquare/swan</PackageProjectUrl>
Expand Down
35 changes: 22 additions & 13 deletions test/Unosquare.Swan.Test/EnumHelperTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,6 @@ public void WithValidIndexEnum_ReturnsTuple()
Assert.AreEqual("(1, Two)", items[1].ToString());
Assert.AreEqual("(2, Three)", items[2].ToString());
}

[Test]
public void WithInvalidType_ThrowsArgumentException()
{
Assert.Throws<ArgumentException>(() => EnumHelper.GetItemsWithIndex<string>());
}
}

[TestFixture]
Expand All @@ -37,18 +31,12 @@ public void WithValidValueEnum_ReturnsTuple()
Assert.AreEqual("(2, Two)", items[1].ToString());
Assert.AreEqual("(3, Three)", items[2].ToString());
}

[Test]
public void WithInvalidType_ThrowsArgumentException()
{
Assert.Throws<ArgumentException>(() => EnumHelper.GetItemsWithValue<string>());
}
}

[TestFixture]
public class GetFlagValues
{
[TestCase(MyFlag.None, false, new[] {0})]
[TestCase(MyFlag.NoneOrZero, false, new[] {0})]
[TestCase(MyFlag.One, false, new[] {0, 1})]
[TestCase(MyFlag.Two, false, new[] {0, 2})]
[TestCase(MyFlag.All, false, new[] {0, 1, 2, 3})]
Expand Down Expand Up @@ -80,4 +68,25 @@ public void WithInvalidType_ThrowsArgumentException()
Assert.Throws<ArgumentException>(() => EnumHelper.GetFlagValues<int>(0));
}
}

[TestFixture]
public class GetFlagNames
{
[TestCase(false, false, "NoneOrZero", "One")]
[TestCase(false, true, "None Or Zero", "One")]
[TestCase(true, false, "One", "Two")]
public void WithFlag_ReturnsListofStrings(bool ignoreZero, bool humanize, string zeroIndexValue, string oneIndexValue)
{
var names = EnumHelper.GetFlagNames<MyFlag>((int)MyFlag.All, ignoreZero, humanize);

Assert.AreEqual(zeroIndexValue, names[0]);
Assert.AreEqual(oneIndexValue, names[1]);
}

[Test]
public void WithInvalidType_ThrowsArgumentException()
{
Assert.Throws<ArgumentException>(() => EnumHelper.GetFlagNames<int>(0));
}
}
}
2 changes: 1 addition & 1 deletion test/Unosquare.Swan.Test/Mocks/JsonMock.cs
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ public enum MyEnum
[Flags]
public enum MyFlag
{
None = 0,
NoneOrZero = 0,
One = 1,
Two = 2,
All = One | Two,
Expand Down

0 comments on commit f600ee7

Please sign in to comment.