#nullable enable
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Swan.Reflection;
namespace Swan.Mappers {
///
/// Represents an AutoMapper-like object to map from one object type
/// to another using defined properties map or using the default behaviour
/// to copy same named properties from one object to another.
///
/// The extension methods like CopyPropertiesTo use the default behaviour.
///
///
/// The following code explains how to map an object's properties into an instance of type T.
///
/// using Swan.Mappers;
///
/// class Example
/// {
/// class Person
/// {
/// public string Name { get; set; }
/// public int Age { get; set; }
/// }
///
/// static void Main()
/// {
/// var obj = new { Name = "John", Age = 42 };
///
/// var person = Runtime.ObjectMapper.Map<Person>(obj);
/// }
/// }
///
///
/// The following code explains how to explicitly map certain properties.
///
/// using Swan.Mappers;
///
/// class Example
/// {
/// class User
/// {
/// public string Name { get; set; }
/// public Role Role { get; set; }
/// }
///
/// public class Role
/// {
/// public string Name { get; set; }
/// }
///
/// class UserDto
/// {
/// public string Name { get; set; }
/// public string Role { get; set; }
/// }
///
/// static void Main()
/// {
/// // create a User object
/// var person =
/// new User { Name = "Phillip", Role = new Role { Name = "Admin" } };
///
/// // create an Object Mapper
/// var mapper = new ObjectMapper();
///
/// // map the User's Role.Name to UserDto's Role
/// mapper.CreateMap<User, UserDto>()
/// .MapProperty(d => d.Role, x => x.Role.Name);
///
/// // apply the previous map and retrieve a UserDto object
/// var destination = mapper.Map<UserDto>(person);
/// }
/// }
///
///
public partial class ObjectMapper {
///
/// Copies the specified source.
///
/// The source.
/// The target.
/// The properties to copy.
/// The ignore properties.
///
/// Copied properties count.
///
///
/// source
/// or
/// target.
///
public static Int32 Copy(Object source, Object? target, IEnumerable? propertiesToCopy = null, params String[]? ignoreProperties) {
if(source == null) {
throw new ArgumentNullException(nameof(source));
}
if(target == null) {
throw new ArgumentNullException(nameof(target));
}
return CopyInternal(target, GetSourceMap(source), propertiesToCopy, ignoreProperties);
}
///
/// Copies the specified source.
///
/// The source.
/// The target.
/// The properties to copy.
/// The ignore properties.
///
/// Copied properties count.
///
///
/// source
/// or
/// target.
///
public static Int32 Copy(IDictionary source, Object? target, IEnumerable? propertiesToCopy = null, params String[] ignoreProperties) {
if(source == null) {
throw new ArgumentNullException(nameof(source));
}
if(target == null) {
throw new ArgumentNullException(nameof(target));
}
return CopyInternal(target, source.ToDictionary(x => x.Key.ToLowerInvariant(), x => Tuple.Create(typeof(Object), x.Value)), propertiesToCopy, ignoreProperties);
}
private static Int32 CopyInternal(Object target, Dictionary> sourceProperties, IEnumerable? propertiesToCopy, IEnumerable? ignoreProperties) {
// Filter properties
IEnumerable? requiredProperties = propertiesToCopy?.Where(p => !String.IsNullOrWhiteSpace(p)).Select(p => p.ToLowerInvariant());
IEnumerable? ignoredProperties = ignoreProperties?.Where(p => !String.IsNullOrWhiteSpace(p)).Select(p => p.ToLowerInvariant());
IEnumerable properties = PropertyTypeCache.DefaultCache.Value.RetrieveFilteredProperties(target.GetType(), true, x => x.CanWrite);
return properties.Select(x => x.Name).Distinct().ToDictionary(x => x.ToLowerInvariant(), x => properties.First(y => y.Name == x)).Where(x => sourceProperties.Keys.Contains(x.Key)).When(() => requiredProperties != null, q => q.Where(y => requiredProperties.Contains(y.Key))).When(() => ignoredProperties != null, q => q.Where(y => !ignoredProperties.Contains(y.Key))).ToDictionary(x => x.Value, x => sourceProperties[x.Key]).Sum(x => TrySetValue(x.Key, x.Value, target) ? 1 : 0);
}
private static Boolean TrySetValue(PropertyInfo propertyInfo, Tuple property, Object target) {
try {
(Type type, Object value) = property;
if(type.IsEnum) {
propertyInfo.SetValue(target,
Enum.ToObject(propertyInfo.PropertyType, value));
return true;
}
if(type.IsValueType || propertyInfo.PropertyType != type) {
return propertyInfo.TrySetBasicType(value, target);
}
if(propertyInfo.PropertyType.IsArray) {
_ = propertyInfo.TrySetArray(value as IEnumerable