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

Support more collection types + Able to set null value to nullable property + Value types will always be primitive #12

Merged
merged 7 commits into from
Jan 14, 2016
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions src/Mapster.Tests/WhenMappingCollections.cs
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,8 @@ public class PersonDTO
public Guid Id { get; set; }
public string Name { get; set; }
public Projects Project { get; set; }
public List<int> X { get; set; }

public HashSet<int> X { get; set; }
public int[] Y { get; set; }
public ICollection<Guid> Z { get; set; }
public ArrayList Ids { get; set; }
Expand Down Expand Up @@ -107,7 +108,7 @@ public void MapCollectionProperty()
dto.Project == person.Project);

Assert.IsNotNull(dto.X);
Assert.IsTrue(dto.X.Count == 4 && dto.X[0] == 1 && dto.X[1] == 2 && dto.X[2] == 3 && dto.X[3] == 4);
Assert.IsTrue(dto.X.Count == 4 && dto.X.Contains(1) && dto.X.Contains(2) && dto.X.Contains(3) && dto.X.Contains(4));

Assert.IsNotNull(dto.Y);
Assert.IsTrue(dto.Y.Length == 3 && dto.Y[0] == 5 && dto.Y[1] == 6 && dto.Y[2] == 7);
Expand Down
18 changes: 18 additions & 0 deletions src/Mapster.Tests/WhenMappingNullablePrimitives.cs
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,24 @@ public void Can_Map_From_Nullable_Source_To_Nullable_Target()
dto.Amount.ShouldBeNull();
}

[Test]
public void Can_Map_From_Nullable_Source_To_Nullable_Existing_Target()
{
var poco = new NullablePrimitivesPoco { Id = Guid.NewGuid(), Name = "TestName" };

NullablePrimitivesPoco2 dto = new NullablePrimitivesPoco2
{
IsImport = true,
Amount = 1,
};

TypeAdapter.Adapt(poco, dto);

dto.Id.ShouldEqual(poco.Id);
dto.Name.ShouldEqual(poco.Name);
dto.IsImport.ShouldBeNull();
dto.Amount.ShouldBeNull();
}

[Test]
public void Can_Map_From_Nullable_Source_With_Values_To_Non_Nullable_Target()
Expand Down
23 changes: 23 additions & 0 deletions src/Mapster.Tests/WhenMappingPrimitives.cs
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,21 @@ public void Byte_Array_In_Test_Class_Is_Mapped_Correctly()
testString.ShouldEqual(resultString);
}

[Test]
public void ValueType_String_Object_Is_Always_Primitive()
{
var sourceDto = new PrimitivePoco
{
Id = "test",
Time = TimeSpan.FromHours(7),
Obj = new object(),
};
var targetDto = TypeAdapter.Adapt<PrimitivePoco, PrimitivePoco>(sourceDto);

targetDto.Id.ShouldEqual(sourceDto.Id);
targetDto.Time.ShouldEqual(sourceDto.Time);
targetDto.Obj.ShouldBeSameAs(sourceDto.Obj);
}

public class TestA
{
Expand All @@ -55,5 +70,13 @@ public class TestB
{
public Byte[] Bytes { get; set; }
}

public class PrimitivePoco
{
public string Id { get; set; }
public TimeSpan Time { get; set; }
public object Obj { get; set; }
}

}
}
26 changes: 21 additions & 5 deletions src/Mapster/Adapters/ClassAdapter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ private static TDestination Adapt(TSource source, TDestination destination, bool
{
case 1: //Primitive
object primitiveValue = propertyModel.Getter.Invoke(source);
if (primitiveValue == null)
if (primitiveValue == null && (ignoreNullValues || propertyModel.DestinationPropertyType.IsNonNullable()))
{
continue;
}
Expand All @@ -141,6 +141,11 @@ private static TDestination Adapt(TSource source, TDestination destination, bool
break;
case 2: //Flattening Get Method
destinationValue = propertyModel.AdaptInvoker(source, null);
if (destinationValue == null &&
(ignoreNullValues || propertyModel.DestinationPropertyType.IsNonNullable()))
{
continue;
}
break;
case 3: //Flattening Deep Property
var flatInvokers = propertyModel.FlatteningInvokers;
Expand All @@ -152,15 +157,15 @@ private static TDestination Adapt(TSource source, TDestination destination, bool
break;
}

if (value == null && ignoreNullValues)
if (value == null && (ignoreNullValues || propertyModel.DestinationPropertyType.IsNonNullable()))
{
continue;
}
destinationValue = value;
break;
case 4: // Adapter
object sourceValue = propertyModel.Getter.Invoke(source);
if (sourceValue == null && ignoreNullValues)
if (sourceValue == null && (ignoreNullValues || propertyModel.DestinationPropertyType.IsNonNullable()))
{
continue;
}
Expand All @@ -177,6 +182,11 @@ private static TDestination Adapt(TSource source, TDestination destination, bool
if (propertyModel.Condition == null || propertyModel.Condition(source))
{
destinationValue = propertyModel.CustomResolver(source);
if (destinationValue == null &&
(ignoreNullValues || propertyModel.DestinationPropertyType.IsNonNullable()))
{
continue;
}
break;
}
continue;
Expand Down Expand Up @@ -303,6 +313,7 @@ public static void Reset()
var propertyModel = propertyModelFactory();
propertyModel.Getter = getter;
propertyModel.Setter = setter;
propertyModel.DestinationPropertyType = destinationPropertyType;
propertyModel.SetterPropertyName = ExtractPropertyName(setter, "Set");
if (destinationTransforms.ContainsKey(destinationPropertyType))
propertyModel.DestinationTransform = destinationTransforms[destinationPropertyType];
Expand Down Expand Up @@ -411,12 +422,14 @@ public static void Reset()
ReflectionUtils.GetDeepFlattening(sourceType, destinationMember.Name, delegates);
if (delegates.Count > 0)
{
var setter = PropertyCaller<TDestination>.CreateSetMethod((PropertyInfo)destinationMember);
var destinationProperty = (PropertyInfo) destinationMember;
var setter = PropertyCaller<TDestination>.CreateSetMethod(destinationProperty);
if (setter != null)
{
var propertyModel = (PropertyModel<TSource, TDestination>)propertyModelFactory();
propertyModel.ConvertType = 3;
propertyModel.Setter = setter;
propertyModel.DestinationPropertyType = destinationProperty.PropertyType;
propertyModel.SetterPropertyName = ExtractPropertyName(setter, "Set");
var destinationPropertyType = typeof(TDestination);
if (destinationTransforms.ContainsKey(destinationPropertyType))
Expand All @@ -438,13 +451,15 @@ public static void Reset()
var getMethod = sourceType.GetMethod(String.Concat("Get", destinationMember.Name));
if (getMethod != null)
{
var setter = PropertyCaller<TDestination>.CreateSetMethod((PropertyInfo)destinationMember);
var destinationProperty = (PropertyInfo) destinationMember;
var setter = PropertyCaller<TDestination>.CreateSetMethod(destinationProperty);
if (setter == null)
return true;

var propertyModel = (PropertyModel<TSource, TDestination>)propertyModelFactory();
propertyModel.ConvertType = 2;
propertyModel.Setter = setter;
propertyModel.DestinationPropertyType = destinationProperty.PropertyType;
propertyModel.SetterPropertyName = ExtractPropertyName(setter, "Set");
var destinationPropertyType = typeof(TDestination);
if (destinationTransforms.ContainsKey(destinationPropertyType))
Expand Down Expand Up @@ -481,6 +496,7 @@ public static void Reset()
var propertyModel = (PropertyModel<TSource, TDestination>)propertyModelFactory();
propertyModel.ConvertType = 5;
propertyModel.Setter = setter;
propertyModel.DestinationPropertyType = destinationProperty.PropertyType;
propertyModel.SetterPropertyName = ExtractPropertyName(setter, "Set");
propertyModel.CustomResolver = resolver.Invoker;
propertyModel.Condition = resolver.Condition;
Expand Down
28 changes: 20 additions & 8 deletions src/Mapster/Adapters/CollectionAdapter.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Collections;
using System;
using System.Collections;
using System.Collections.Generic;
using Mapster.Models;
using Mapster.Utils;
Expand Down Expand Up @@ -61,7 +62,7 @@ public static object Adapt(TSource source, object destination, Dictionary<long,
{
#region CopyToArray

byte i = 0;
int i = 0;
var adapterInvoker = _collectionAdapterModel.AdaptInvoker;
var array = destination == null ? new TDestinationElement[((ICollection)source).Count] : (TDestinationElement[])destination;
if (_collectionAdapterModel.IsPrimitive)
Expand Down Expand Up @@ -91,21 +92,32 @@ public static object Adapt(TSource source, object destination, Dictionary<long,

#endregion
}

if (destinationType.IsGenericType)

var canInstantiate = !destinationType.IsInterface && typeof (ICollection<TDestinationElement>).IsAssignableFrom(destinationType);
if (canInstantiate || destinationType.IsAssignableFrom(typeof(List<TDestinationElement>)))
{
#region CopyToList

var adapterInvoker = _collectionAdapterModel.AdaptInvoker;
var list = destination == null ? new List<TDestinationElement>() : (List<TDestinationElement>)destination;
ICollection<TDestinationElement> list;
if (destination == null)
{
list = canInstantiate
? (ICollection<TDestinationElement>) ActivatorExtensions.CreateInstance(destinationType)
: new List<TDestinationElement>();
}
else
{
list = (ICollection<TDestinationElement>)destination;
}
if (_collectionAdapterModel.IsPrimitive)
{
bool hasInvoker = adapterInvoker != null;
foreach (var item in source)
{
if (item == null)
list.Add(default(TDestinationElement));
else if(hasInvoker)
else if (hasInvoker)
list.Add((TDestinationElement)adapterInvoker(null, new[] { item }));
else
list.Add((TDestinationElement)item);
Expand All @@ -124,12 +136,12 @@ public static object Adapt(TSource source, object destination, Dictionary<long,
#endregion
}

if (destinationType == typeof(ArrayList))
if (!destinationType.IsInterface && typeof(IList).IsAssignableFrom(destinationType))
{
#region CopyToArrayList

var adapterInvoker = _collectionAdapterModel.AdaptInvoker;
var array = destination == null ? new ArrayList() : (ArrayList)destination;
var array = destination == null ? (IList)ActivatorExtensions.CreateInstance(destinationType) : (IList)destination;
if (_collectionAdapterModel.IsPrimitive)
{
bool hasInvoker = adapterInvoker != null;
Expand Down
2 changes: 1 addition & 1 deletion src/Mapster/Models/PropertyModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,6 @@ public class PropertyModel<TSource, TDestination>

public string SetterPropertyName;


public Type DestinationPropertyType;
}
}
2 changes: 1 addition & 1 deletion src/Mapster/Utils/Activator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ public static Delegate GetConstructorDelegate(this Type type, Type delegateType)
type.Name));
}

var dynamicMethod = new DynamicMethod("DM$_" + type.Name, type, argTypes, type);
var dynamicMethod = new DynamicMethod("DM$_" + type.Name, type, argTypes);
ILGenerator ilGen = dynamicMethod.GetILGenerator();
for (int i = 0; i < argTypes.Length; i++)
{
Expand Down
2 changes: 1 addition & 1 deletion src/Mapster/Utils/FastObjectFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ public static Func<T> CreateObjectFactory<T>(Func<T> factory = null)
}
else
{
var dynMethod = new DynamicMethod("DM$OBJ_FACTORY_" + type.Name, type, null, type);
var dynMethod = new DynamicMethod("DM$OBJ_FACTORY_" + type.Name, type, null);
ILGenerator ilGen = dynMethod.GetILGenerator();

ilGen.Emit(OpCodes.Newobj, type.GetConstructor(Type.EmptyTypes));
Expand Down
35 changes: 18 additions & 17 deletions src/Mapster/Utils/ReflectionUtils.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,17 @@ internal static class ReflectionUtils
private static readonly Type _nullableType = typeof (Nullable<>);

private static readonly Type _iEnumerableType = typeof(IEnumerable);
private static readonly Type _arrayListType = typeof(ArrayList);

public static bool IsNullable(this Type type)
{
return type.IsGenericType && type.GetGenericTypeDefinition() == _nullableType;
}

public static bool IsNonNullable(this Type type)
{
return type.IsValueType && !type.IsNullable();
}

public static List<MemberInfo> GetPublicFieldsAndProperties(this Type type, bool allowNonPublicSetter = true, bool allowNoSetter = true)
{
var results = new List<MemberInfo>();
Expand Down Expand Up @@ -87,13 +91,8 @@ public static bool IsCollection(this Type type)
public static bool IsPrimitiveRoot(this Type type)
{
return
type.IsPrimitive
type.IsValueType
|| type == typeof(string)
|| type == typeof(decimal)
|| type == typeof(DateTime)
|| type == typeof(Guid)
|| type.IsEnum
|| (IsNullable(type) && IsPrimitiveRoot(Nullable.GetUnderlyingType(type)))
|| TypeAdapterConfig.GlobalSettings.PrimitiveTypes.Contains(type)
|| type == typeof(object)
;
Expand All @@ -111,21 +110,23 @@ public static bool IsEnum(this Type type)

public static Type ExtractCollectionType(this Type collectionType)
{
if (collectionType.IsArray)
if (collectionType.IsGenericEnumerableType())
{
return collectionType.GetElementType();
return collectionType.GetGenericArguments()[0];
}
if (collectionType == _arrayListType)
var enumerableType = collectionType.GetInterfaces().FirstOrDefault(IsGenericEnumerableType);
if (enumerableType != null)
{
return typeof(object);
return enumerableType.GetGenericArguments()[0];
}
if (collectionType.IsGenericType)
{
return collectionType.GetGenericArguments()[0];
}
return collectionType;
return typeof (object);
}


public static bool IsGenericEnumerableType(this Type type)
{
return type.IsGenericType && type.GetGenericTypeDefinition() == typeof (IEnumerable<>);
}

public static FastInvokeHandler CreatePrimitiveConverter(this Type sourceType, Type destinationType)
{
Type srcType;
Expand Down