Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

AsString() Enums support added #1307

Merged
merged 7 commits into from
May 20, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
14 changes: 12 additions & 2 deletions src/Framework/Framework/Binding/ValueOrBindingExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -74,11 +74,21 @@ public static ValueOrBinding<IList<T>> GetItems<T>(this ValueOrBinding<IBaseGrid
public static ValueOrBinding<string> AsString<T>(this ValueOrBinding<T> v)
{
if (v.BindingOrDefault is IBinding binding)
return new ValueOrBinding<string>(
return new(
binding.GetProperty<ExpectedAsStringBindingExpression>().Binding
);
else if (v.ValueOrDefault is null)
{
return new("");
}
else if (typeof(T).IsValueType && typeof(T).UnwrapNullableType().IsEnum)
{
return new(ReflectionUtils.ToEnumString(typeof(T), v.ValueOrDefault.ToString() ?? ""));
}
else
return new ValueOrBinding<string>("" + v.ValueOrDefault);
{
return new(v.ValueOrDefault.ToString() ?? "");
}
}
/// <summary> Returns ValueOrBinding with the value of `a is object`. The resulting binding is cached, so it's safe to use this method at runtime. </summary>
public static ValueOrBinding<bool> NotNull<T>(this ValueOrBinding<T> v) =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,10 @@ public static bool IsStringConversionAllowed(Type fromType)

public static Expression? ToStringConversion(Expression src)
{
if (src.Type.UnwrapNullableType().IsEnum)
{
return Expression.Call(typeof(ReflectionUtils), "ToEnumString", new [] { src.Type.UnwrapNullableType() }, src);
}
var toStringMethod = src.Type.GetMethod("ToString", Type.EmptyTypes);
if (toStringMethod?.DeclaringType == typeof(object))
toStringMethod = null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,9 @@ private void AddDefaultToStringTranslations()
{
AddMethodTranslator(typeof(object), "ToString", new PrimitiveToStringTranslator(), 0);
AddMethodTranslator(typeof(Convert), "ToString", new PrimitiveToStringTranslator(), 1, true);
AddMethodTranslator(typeof(ReflectionUtils), "ToEnumString", parameterCount: 1, translator: new GenericMethodCompiler(
args => args[1]
));

AddMethodTranslator(typeof(DateTime).GetMethod("ToString", Type.EmptyTypes), new GenericMethodCompiler(
args => new JsIdentifierExpression("dotvvm").Member("globalize").Member("bindingDateToString")
Expand Down
2 changes: 1 addition & 1 deletion src/Framework/Framework/Controls/HtmlGenericControl.cs
Original file line number Diff line number Diff line change
Expand Up @@ -381,7 +381,7 @@ private void AddHtmlAttribute(IHtmlWriter writer, string name, object? value)
value switch {
null => "",
string str => str,
Enum enumValue => enumValue.ToEnumString(),
Enum enumValue => ReflectionUtils.ToEnumString(enumValue.GetType(), enumValue.ToString()),
Guid guid => guid.ToString(),
_ when ReflectionUtils.IsNumericType(value.GetType()) => Convert.ToString(value, CultureInfo.InvariantCulture) ?? "",
System.Collections.IEnumerable =>
Expand Down
25 changes: 23 additions & 2 deletions src/Framework/Framework/Utils/ReflectionUtils.cs
Original file line number Diff line number Diff line change
Expand Up @@ -472,10 +472,14 @@ public static string GetTypeHash(this Type type)
member is TypeInfo type ? type.AsType() :
throw new NotImplementedException($"Could not get return type of member {member.GetType().FullName}");

public static string ToEnumString<T>(this T instance) where T : Enum
public static string ToEnumString<T>(this T instance) where T : struct, Enum
{
return ToEnumString(instance.GetType(), instance.ToString());
var name = instance.ToString();
if (!EnumInfo<T>.HasEnumMemberField)
return name;
return ToEnumString(typeof(T), name);
}

public static string ToEnumString(Type enumType, string name)
{
var field = enumType.GetField(name);
Expand All @@ -490,6 +494,23 @@ public static string ToEnumString(Type enumType, string name)
return name;
}

internal static class EnumInfo<T> where T: struct, Enum
{
internal static readonly bool HasEnumMemberField;

static EnumInfo()
{
foreach (var field in typeof(T).GetFields(BindingFlags.Public | BindingFlags.Static))
{
if (field.IsDefined(typeof(EnumMemberAttribute), false))
{
HasEnumMemberField = true;
break;
}
}
}
}

public static Type GetDelegateType(MethodInfo methodInfo)
{
return Expression.GetDelegateType(methodInfo.GetParameters().Select(a => a.ParameterType).Append(methodInfo.ReturnType).ToArray());
Expand Down
17 changes: 16 additions & 1 deletion src/Tests/Binding/BindingCompilationTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
using DotVVM.Framework.ViewModel;
using DotVVM.Framework.Testing;
using System.Collections.ObjectModel;
using System.Runtime.Serialization;

namespace DotVVM.Framework.Tests.Binding
{
Expand Down Expand Up @@ -525,6 +526,18 @@ public void BindingCompiler_Valid_EnumMemberAccess()
Assert.AreEqual(ExecuteBinding("StringProp == 'abc' ? TestEnum.A : TestEnum.Underscore_hhh", viewModel), TestEnum.A);
Assert.AreEqual(ExecuteBinding("StringProp == 'abcd' ? TestEnum.A : TestEnum.Underscore_hhh", viewModel), TestEnum.Underscore_hhh);
}
[TestMethod]
public void BindingCompiler_EnumToStringConversion()
{
var result1 = ExecuteBinding("DotVVM.Framework.Tests.Binding.TestEnum.Underscore_hhh", new object[0], null, expectedType: typeof(string));
Assert.AreEqual("Underscore_hhh", result1);
var result2 = ExecuteBinding("DotVVM.Framework.Tests.Binding.TestEnum.SpecialField", new object[0], null, expectedType: typeof(string));
Assert.AreEqual("xxx", result2);
var result3 = ExecuteBinding("EnumProperty", new object[] { new TestViewModel { EnumProperty = TestEnum.A } }, null, expectedType: typeof(string));
Assert.AreEqual("A", result3);
var result4 = ExecuteBinding("EnumProperty", new object[] { new TestViewModel { EnumProperty = TestEnum.SpecialField } }, null, expectedType: typeof(string));
Assert.AreEqual("xxx", result4);
}

[TestMethod]
public void BindingCompiler_Invalid_EnumStringComparison()
Expand Down Expand Up @@ -1169,7 +1182,9 @@ enum TestEnum
B,
C,
D,
Underscore_hhh
Underscore_hhh,
[EnumMember(Value = "xxx")]
SpecialField
}


Expand Down