|
|
|
|
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();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|