-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Added a type converter for primitive value-objects. (#17)
- Loading branch information
Showing
3 changed files
with
240 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
126 changes: 126 additions & 0 deletions
126
src/Fluxera.ValueObject/PrimitiveValueObjectConverter.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,126 @@ | ||
namespace Fluxera.ValueObject | ||
{ | ||
using System; | ||
using System.Collections.Concurrent; | ||
using System.ComponentModel; | ||
using System.Globalization; | ||
using Fluxera.Guards; | ||
|
||
internal sealed class PrimitiveValueObjectConverter : TypeConverter | ||
{ | ||
private static readonly ConcurrentDictionary<Type, TypeConverter> ActualConverters = new ConcurrentDictionary<Type, TypeConverter>(); | ||
|
||
private readonly TypeConverter innerConverter; | ||
|
||
public PrimitiveValueObjectConverter(Type primitiveValueObjectType) | ||
{ | ||
this.innerConverter = ActualConverters.GetOrAdd(primitiveValueObjectType, CreateActualConverter); | ||
} | ||
|
||
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) | ||
{ | ||
return this.innerConverter.CanConvertFrom(context, sourceType); | ||
} | ||
|
||
public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType) | ||
{ | ||
return this.innerConverter.CanConvertTo(context, destinationType); | ||
} | ||
|
||
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) | ||
{ | ||
return this.innerConverter.ConvertFrom(context, culture, value); | ||
} | ||
|
||
public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType) | ||
{ | ||
return this.innerConverter.ConvertTo(context, culture, value, destinationType); | ||
} | ||
|
||
private static TypeConverter CreateActualConverter(Type primitiveValueObjectType) | ||
{ | ||
if(!primitiveValueObjectType.IsPrimitiveValueObject()) | ||
{ | ||
throw new InvalidOperationException($"The type '{primitiveValueObjectType}' is not a primitive value-object."); | ||
} | ||
|
||
Type valueType = primitiveValueObjectType.GetValueType(); | ||
Type actualConverterType = typeof(PrimitiveValueObjectConverter<,>).MakeGenericType(primitiveValueObjectType, valueType); | ||
return (TypeConverter)Activator.CreateInstance(actualConverterType); | ||
} | ||
} | ||
|
||
internal sealed class PrimitiveValueObjectConverter<TValueObject, TValue> : TypeConverter | ||
where TValueObject : PrimitiveValueObject<TValueObject, TValue> | ||
where TValue : IComparable | ||
{ | ||
private static TypeConverter ValueConverter { get; } = GetIdValueConverter(); | ||
|
||
private static TypeConverter GetIdValueConverter() | ||
{ | ||
TypeConverter converter = TypeDescriptor.GetConverter(typeof(TValue)); | ||
|
||
if(!converter.CanConvertFrom(typeof(string))) | ||
{ | ||
throw new InvalidOperationException( | ||
$"The type '{typeof(TValue)}' doesn't have a converter that can convert from string."); | ||
} | ||
|
||
return converter; | ||
} | ||
|
||
/// <inheritdoc /> | ||
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) | ||
{ | ||
return sourceType == typeof(string) | ||
|| sourceType == typeof(TValue) | ||
|| base.CanConvertFrom(context, sourceType); | ||
} | ||
|
||
/// <inheritdoc /> | ||
public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType) | ||
{ | ||
return destinationType == typeof(string) | ||
|| destinationType == typeof(TValue) | ||
|| base.CanConvertTo(context, destinationType); | ||
} | ||
|
||
/// <inheritdoc /> | ||
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) | ||
{ | ||
if(value is string str) | ||
{ | ||
value = ValueConverter.ConvertFrom(str); | ||
} | ||
|
||
if(value is TValue idValue) | ||
{ | ||
object instance = Activator.CreateInstance(typeof(TValueObject), new object[] { idValue }); | ||
return instance; | ||
} | ||
|
||
return base.ConvertFrom(context, culture, value); | ||
} | ||
|
||
/// <inheritdoc /> | ||
public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType) | ||
{ | ||
Guard.Against.Null(value); | ||
|
||
PrimitiveValueObject<TValueObject, TValue> primitiveValueObject = (PrimitiveValueObject<TValueObject, TValue>)value; | ||
|
||
TValue objectValue = primitiveValueObject.Value; | ||
if(destinationType == typeof(string)) | ||
{ | ||
return objectValue.ToString(); | ||
} | ||
|
||
if(destinationType == typeof(TValue)) | ||
{ | ||
return objectValue; | ||
} | ||
|
||
return base.ConvertTo(context, culture, value, destinationType); | ||
} | ||
} | ||
} |
112 changes: 112 additions & 0 deletions
112
tests/Fluxera.ValueObject.UnitTests/TypeConverterTests.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,112 @@ | ||
namespace Fluxera.ValueObject.UnitTests | ||
{ | ||
using System; | ||
using System.ComponentModel; | ||
using FluentAssertions; | ||
using Fluxera.ValueObject.UnitTests.Model; | ||
using NUnit.Framework; | ||
|
||
[TestFixture] | ||
public class TypeConverterTests | ||
{ | ||
[Test] | ||
public void ShouldConvertFromGuidStringValue() | ||
{ | ||
TypeConverter converter = TypeDescriptor.GetConverter(typeof(GuidValue)); | ||
|
||
GuidValue result = (GuidValue)converter.ConvertFromString("4c3668ce-3aaa-4adb-bece-05baa708e20f"); | ||
result.Should().NotBeNull(); | ||
result.Value.Should().NotBeEmpty().And.Be("4c3668ce-3aaa-4adb-bece-05baa708e20f"); | ||
} | ||
|
||
[Test] | ||
public void ShouldConvertFromGuidValue() | ||
{ | ||
TypeConverter converter = TypeDescriptor.GetConverter(typeof(GuidValue)); | ||
|
||
GuidValue result = (GuidValue)converter.ConvertFrom(Guid.Parse("4c3668ce-3aaa-4adb-bece-05baa708e20f")); | ||
result.Should().NotBeNull(); | ||
result.Value.Should().NotBeEmpty().And.Be(Guid.Parse("4c3668ce-3aaa-4adb-bece-05baa708e20f")); | ||
} | ||
|
||
[Test] | ||
public void ShouldConvertFromIntegerStringValue() | ||
{ | ||
TypeConverter converter = TypeDescriptor.GetConverter(typeof(IntValue)); | ||
|
||
IntValue result = (IntValue)converter.ConvertFromString("999"); | ||
result.Should().NotBeNull(); | ||
result.Value.Should().Be(999); | ||
} | ||
|
||
[Test] | ||
public void ShouldConvertFromIntegerValue() | ||
{ | ||
TypeConverter converter = TypeDescriptor.GetConverter(typeof(IntValue)); | ||
|
||
IntValue result = (IntValue)converter.ConvertFrom(999); | ||
result.Should().NotBeNull(); | ||
result.Value.Should().Be(999); | ||
} | ||
|
||
[Test] | ||
public void ShouldConvertFromStringValue() | ||
{ | ||
TypeConverter converter = TypeDescriptor.GetConverter(typeof(StringValue)); | ||
|
||
StringValue result = (StringValue)converter.ConvertFromString("12345"); | ||
result.Should().NotBeNull(); | ||
result.Value.Should().NotBeEmpty().And.Be("12345"); | ||
} | ||
|
||
[Test] | ||
public void ShouldConvertToGuidStringValue() | ||
{ | ||
TypeConverter converter = TypeDescriptor.GetConverter(typeof(GuidValue)); | ||
|
||
GuidValue id = new GuidValue(Guid.Parse("2ca3459d-794e-4d25-9594-bc3849972e1f")); | ||
string result = converter.ConvertToString(id); | ||
result.Should().Be("2ca3459d-794e-4d25-9594-bc3849972e1f"); | ||
} | ||
|
||
[Test] | ||
public void ShouldConvertToGuidValue() | ||
{ | ||
TypeConverter converter = TypeDescriptor.GetConverter(typeof(GuidValue)); | ||
|
||
GuidValue id = new GuidValue(Guid.Parse("2ca3459d-794e-4d25-9594-bc3849972e1f")); | ||
Guid result = (Guid)converter.ConvertTo(null, null, id, typeof(Guid)); | ||
result.Should().Be(Guid.Parse("2ca3459d-794e-4d25-9594-bc3849972e1f")); | ||
} | ||
|
||
[Test] | ||
public void ShouldConvertToIntegerStringValue() | ||
{ | ||
TypeConverter converter = TypeDescriptor.GetConverter(typeof(IntValue)); | ||
|
||
IntValue id = new IntValue(999); | ||
string result = converter.ConvertToString(id); | ||
result.Should().Be("999"); | ||
} | ||
|
||
[Test] | ||
public void ShouldConvertToIntegerValue() | ||
{ | ||
TypeConverter converter = TypeDescriptor.GetConverter(typeof(IntValue)); | ||
|
||
IntValue id = new IntValue(999); | ||
int result = (int)converter.ConvertTo(null, null, id, typeof(int)); | ||
result.Should().Be(999); | ||
} | ||
|
||
[Test] | ||
public void ShouldConvertToStringValue() | ||
{ | ||
TypeConverter converter = TypeDescriptor.GetConverter(typeof(StringValue)); | ||
|
||
StringValue id = new StringValue("12345"); | ||
string result = converter.ConvertToString(id); | ||
result.Should().Be("12345"); | ||
} | ||
} | ||
} |