Skip to content

Commit

Permalink
Move TryGetDictionaryValue(...) to class RelectionUtils
Browse files Browse the repository at this point in the history
  • Loading branch information
axunonb committed Sep 10, 2023
1 parent 7c8be3b commit abc2707
Show file tree
Hide file tree
Showing 3 changed files with 91 additions and 36 deletions.
36 changes: 32 additions & 4 deletions src/SmartFormat.Tests/Extensions/DictionarySourceTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using SmartFormat.Core.Settings;
using SmartFormat.Extensions;
using SmartFormat.Tests.TestUtils;
using SmartFormat.Utilities;

namespace SmartFormat.Tests.Extensions;

Expand Down Expand Up @@ -158,12 +159,26 @@ public void Dictionary_Dot_Notation_Nullable()
Assert.That(result, Is.EqualTo(expected));
}

[Test]
public void Generic_Dictionary_String_String()
{
var dict = new Dictionary<string, string> { { "Name", "Joe" } };
var smart = new SmartFormatter()
.AddExtensions(new DefaultSource(), new DictionarySource())
.AddExtensions(new DefaultFormatter());
var result = smart.Format("{Name}", dict);

Assert.That(result, Is.EqualTo("Joe"));
}

[Test]
public void IReadOnlyDictionary_With_IConvertible_Key()
{
var roDict = new CustomReadOnlyDictionary<IConvertible, object?>(new Dictionary<IConvertible, object?> { { 1, 1 }, { "Two", 2 }, { "Three", "three" }, });
var formatter = Smart.CreateDefaultSmartFormat();
var result = formatter.Format("{1}{Two}{Three}", roDict);
var smart = new SmartFormatter()
.AddExtensions(new DefaultSource(), new DictionarySource())
.AddExtensions(new DefaultFormatter());
var result = smart.Format("{1}{Two}{Three}", roDict);

Assert.That(result, Is.EqualTo("12three"));
}
Expand All @@ -172,12 +187,25 @@ public void IReadOnlyDictionary_With_IConvertible_Key()
public void IReadOnlyDictionary_With_String_Key()
{
var roDict = new CustomReadOnlyDictionary<string, object?>(new Dictionary<string, object?> { { "One", 1 }, { "Two", 2 }, { "Three", "three" }, });
var formatter = Smart.CreateDefaultSmartFormat();
var result = formatter.Format("{One}{Two}{Three}", roDict);
var smart = new SmartFormatter()
.AddExtensions(new DefaultSource(), new DictionarySource())
.AddExtensions(new DefaultFormatter());
var result = smart.Format("{One}{Two}{Three}", roDict);

Assert.That(result, Is.EqualTo("12three"));
}

[TestCase("key", "value")]
[TestCase("nokey", null)]
public void Get_GenericDictionary_Value(string key, string? expected)
{
var dict = new Dictionary<string, object> { { "key", "value" } };
var success = ReflectionUtils.TryGetDictionaryValue(dict.GetType(), dict, key, StringComparison.Ordinal, out var value);

Assert.That(success, Is.EqualTo(expected != null));
Assert.That(value, Is.EqualTo(expected));
}

public class CustomReadOnlyDictionary<TKey, TValue> : IReadOnlyDictionary<TKey, TValue?>
{
private readonly IDictionary<TKey, TValue?> _dictionary;
Expand Down
38 changes: 6 additions & 32 deletions src/SmartFormat/Extensions/DictionarySource.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using SmartFormat.Utilities;
using SmartFormat.Core.Extensions;

namespace SmartFormat.Extensions;
Expand Down Expand Up @@ -53,40 +53,14 @@ public override bool TryEvaluateSelector(ISelectorInfo selectorInfo)
return true;
}

// This is for IReadOnlyDictionary<,>
var currentType = current.GetType();
if (IsDictionary(currentType))
// This is for IReadOnlyDictionary<,> using Reflection
if (ReflectionUtils.TryGetDictionaryValue(current.GetType(), current, selector,
selectorInfo.FormatDetails.Settings.GetCaseSensitivityComparison(), out var value))
{
if (currentType.GetProperty("Keys")?.GetValue(current) is not IEnumerable keys)
return false;

foreach (var key in keys)
{
if (!key.ToString()
.Equals(selector, selectorInfo.FormatDetails.Settings.GetCaseSensitivityComparison()))
continue;

selectorInfo.Result = currentType.GetProperty("Item")?.GetValue(current, new [] { key });
return true;
}
selectorInfo.Result = value;
return true;
}

return false;
}

private static readonly List<Type> DictionaryInterfaces = new() {
typeof(IDictionary<,>), // 1
typeof(IDictionary), // 2
typeof(IReadOnlyDictionary<,>) // 3
};
private static bool IsDictionary(Type type)
{
return DictionaryInterfaces
.Exists(dictInterface =>
dictInterface == type || // 1
(type.IsGenericType && dictInterface == type.GetGenericTypeDefinition()) || // 2
type.GetInterfaces().ToList().Exists(typeInterface => // 3
typeInterface == dictInterface ||
(typeInterface.IsGenericType && dictInterface == typeInterface.GetGenericTypeDefinition())));
}
}
53 changes: 53 additions & 0 deletions src/SmartFormat/Utilities/ReflectionUtils.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
//
// Copyright SmartFormat Project maintainers and contributors.
// Licensed under the MIT license.
//

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;

namespace SmartFormat.Utilities;
internal static class ReflectionUtils
{
#region *** Dictionary ***

public static readonly List<Type> DictionaryInterfaces = new() {
typeof(IDictionary<,>), // 1
typeof(IDictionary), // 2
typeof(IReadOnlyDictionary<,>) // 3
};

public static bool IsDictionary(Type type)
{
return DictionaryInterfaces
.Exists(dictInterface =>
dictInterface == type || // 1
(type.IsGenericType && dictInterface == type.GetGenericTypeDefinition()) || // 2
type.GetInterfaces().ToList().Exists(typeInterface => // 3
typeInterface == dictInterface ||
(typeInterface.IsGenericType && dictInterface == typeInterface.GetGenericTypeDefinition())));
}

public static bool TryGetDictionaryValue(Type type, object obj, string theKey, StringComparison comparison, out object? value)
{
value = null;
if (!IsDictionary(type)) return false;

var keys = (IEnumerable) type.GetProperty(nameof(IDictionary.Keys))!.GetValue(obj);

foreach (var key in keys)
{
if (!key.ToString().Equals(theKey, comparison))
continue;

value = type.GetProperty("Item")?.GetValue(obj, new [] { key });
return true;
}

return false;
}

#endregion
}

0 comments on commit abc2707

Please sign in to comment.