From 415d9ed9232e8a837e535aba2a5a1deaee4d1487 Mon Sep 17 00:00:00 2001 From: Robert Wagner Date: Tue, 15 Sep 2020 10:17:15 +1000 Subject: [PATCH] Added support for nullable value types and enums --- .../Converters/EnumAttributeOclConverter.cs | 32 +++++++++++++++++ source/Ocl/OclAttribute.cs | 9 ++++- source/Ocl/OclConversionContext.cs | 1 + .../FromOclDocumentDefaultBehaviourFixture.cs | 35 ++++++++++++++++++- .../ToOclBodyDefaultBehaviourFixture.cs | 17 +++++++++ 5 files changed, 92 insertions(+), 2 deletions(-) create mode 100644 source/Ocl/Converters/EnumAttributeOclConverter.cs diff --git a/source/Ocl/Converters/EnumAttributeOclConverter.cs b/source/Ocl/Converters/EnumAttributeOclConverter.cs new file mode 100644 index 0000000..67d530c --- /dev/null +++ b/source/Ocl/Converters/EnumAttributeOclConverter.cs @@ -0,0 +1,32 @@ +using System; + +namespace Octopus.Ocl.Converters +{ + public class EnumAttributeOclConverter : OclConverter + { + public override bool CanConvert(Type type) + => type.IsEnum; + + public override object? FromElement(OclConversionContext context, Type type, IOclElement element, Func getCurrentValue) + { + if (element is OclAttribute attribute) + { + if (attribute.Value == null) + return null; + + if (attribute.Value is string str) + return Enum.Parse(type, str); + + if (attribute.Value is OclStringLiteral strLit) + return Enum.Parse(type, strLit.Value); + + throw new Exception("Enum values must be specified as a string"); + } + + throw new OclException("Can only convert attribute elements"); + } + + protected override IOclElement ConvertInternal(OclConversionContext context, string name, object obj) + => new OclAttribute(GetName(name, obj), obj.ToString()); + } +} \ No newline at end of file diff --git a/source/Ocl/OclAttribute.cs b/source/Ocl/OclAttribute.cs index 1dc9c44..f7bc2fc 100644 --- a/source/Ocl/OclAttribute.cs +++ b/source/Ocl/OclAttribute.cs @@ -50,11 +50,18 @@ public object? Value } public static bool IsSupportedValueType(Type type) - => type.IsPrimitive || + { + bool IsNullableSupportedValueType() + => type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>) && IsSupportedValueType(type.GetGenericArguments()[0]); + + return type.IsPrimitive || type == typeof(string) || type == typeof(decimal) || type == typeof(OclStringLiteral) || + IsNullableSupportedValueType() || IsSupportedValueCollectionType(type); + } + internal static bool IsSupportedValueCollectionType(Type type) => (type.IsArray && type.GetArrayRank() == 1 && IsSupportedValueType(type.GetElementType()!)) || diff --git a/source/Ocl/OclConversionContext.cs b/source/Ocl/OclConversionContext.cs index bc8a895..814a4f4 100644 --- a/source/Ocl/OclConversionContext.cs +++ b/source/Ocl/OclConversionContext.cs @@ -16,6 +16,7 @@ internal OclConversionContext(OclSerializerOptions options) .Concat( new IOclConverter[] { + new EnumAttributeOclConverter(), new DefaultAttributeOclConverter(), new DefaultCollectionOclConverter(), new DefaultBlockOclConverter(), diff --git a/source/Tests/FromOclDoc/FromOclDocumentDefaultBehaviourFixture.cs b/source/Tests/FromOclDoc/FromOclDocumentDefaultBehaviourFixture.cs index b13c584..487ca1d 100644 --- a/source/Tests/FromOclDoc/FromOclDocumentDefaultBehaviourFixture.cs +++ b/source/Tests/FromOclDoc/FromOclDocumentDefaultBehaviourFixture.cs @@ -29,6 +29,19 @@ public void IntAttribute() .BeEquivalentTo(new Car() { Doors = 4 }); } + [Test] + public void IntNullAttribute() + { + var document = new OclDocument() + { + new OclAttribute("Doors", null) + }; + + OclConvert.Deserialize(document, new OclSerializerOptions()) + .Should() + .BeEquivalentTo(new Car() { Doors = null }); + } + [Test] public void StringAttribute() { @@ -42,6 +55,19 @@ public void StringAttribute() .BeEquivalentTo(new Car() { Name = "Mystery Machine" }); } + [Test] + public void EnumAttribute() + { + var document = new OclDocument() + { + new OclAttribute("Type", "Suv") + }; + + OclConvert.Deserialize(document, new OclSerializerOptions()) + .Should() + .BeEquivalentTo(new Car() { Type = CarType.Suv }); + } + [Test] public void Block() { @@ -127,9 +153,16 @@ public void ExceptionIsThrownIfPropertyDoesNotExist() class Car { public string Name { get; set; } = ""; - public int Doors { get; set; } + public int? Doors { get; set; } public Person? Driver { get; set; } public List? Passengers { get; set; } + public CarType Type { get; set; } + } + + enum CarType + { + Hatchback, + Suv } class Person diff --git a/source/Tests/ToOclDoc/ToOclBodyDefaultBehaviourFixture.cs b/source/Tests/ToOclDoc/ToOclBodyDefaultBehaviourFixture.cs index 4904e86..b571d48 100644 --- a/source/Tests/ToOclDoc/ToOclBodyDefaultBehaviourFixture.cs +++ b/source/Tests/ToOclDoc/ToOclBodyDefaultBehaviourFixture.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Reflection; using NUnit.Framework; using Octopus.Ocl; @@ -47,6 +48,22 @@ public void ListOfComplexTypesProperty() ); } + [Test] + public void Enum() + { + var data = new + { + MyProp = BindingFlags.Static + }; + + var result = OclConvert.ToOclDocument(data, new OclSerializerOptions()); + + result.Should() + .HaveChildrenExactly( + new OclAttribute("MyProp", "Static") + ); + } + class Car { public int Doors { get; } = 2;