You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
Connected.Framework/Connected.Interop/Merging/ObjectMerger.cs

128 lines
3.3 KiB

using Connected.ServiceModel;
using System.Collections;
using System.Reflection;
namespace Connected.Interop.Merging
{
internal sealed class ObjectMerger : Merger
{
public void Merge(object destination, params object[] sources)
{
if (destination is null || !HasSource(sources))
return;
var sourceProperties = AggregateProperties(sources);
foreach (var property in Properties.GetImplementedProperties(destination))
MergeProperty(destination, sourceProperties, property);
}
private static bool HasSource(params object[] sources)
{
foreach (var source in sources)
{
if (source is not null)
return true;
}
return false;
}
private static Dictionary<string, object> AggregateProperties(params object[] sources)
{
var result = new Dictionary<string, object>();
for (var i = sources.Length - 1; i >= 0; i--)
{
var source = sources[i];
if (source is null)
continue;
var props = Properties.GetImplementedProperties(source);
foreach (var property in props)
{
if (result.ContainsKey(property.Name))
continue;
result.Add(property.Name, source);
}
if (source is IPropertyProvider provider)
{
foreach (var property in provider.Properties)
{
if (result.ContainsKey(property.Key))
continue;
result.Add(property.Key, property.Value);
}
}
}
return result;
}
private void MergeProperty(object destination, Dictionary<string, object> sourceProperties, PropertyInfo property)
{
if (property.PropertyType.IsTypePrimitive())
{
if (!property.CanWrite)
return;
if (!sourceProperties.TryGetValue(property.Name, out object? source))
return;
property.SetValue(destination, source.GetType().GetProperty(property.Name).GetValue(source));
}
else if (IsArray(property))
MergeEnumerable(destination, sourceProperties, property);
else
MergeObject(destination, sourceProperties, property);
}
private void MergeEnumerable(object destination, Dictionary<string, object> sourceProperties, PropertyInfo property)
{
if (sourceProperties.TryGetValue(property.Name, out object? sourceProperty))
return;
if (property.GetValue(sourceProperty) is not IEnumerable sourceValue)
return;
var sourceElements = GetElements(sourceValue);
var destinationValue = property.GetValue(destination);
if (destinationValue is null && !property.CanWrite)
return;
var addMethod = property.PropertyType.GetMethod(nameof(IList.Add), BindingFlags.Public | BindingFlags.Instance | BindingFlags.NonPublic);
var instance = addMethod is not null ? Activator.CreateInstance(property.PropertyType) : Array.CreateInstance(property.PropertyType, sourceElements.Count);
var elementType = instance.GetType().GetElementType();
for (var i = 0; i < sourceElements.Count; i++)
{
/*
* TODO: handle Dictionary
*/
var sourceElement = sourceElements[i];
var item = Activator.CreateInstance(elementType);
Merge(sourceElement, item);
if (addMethod is not null)
addMethod.Invoke(instance, new object[] { item });
else
((Array)instance).SetValue(item, i);
}
property.SetValue(destination, instance);
}
private void MergeObject(object destination, object source, PropertyInfo property)
{
throw new NotImplementedException();
}
}
}