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 AggregateProperties(params object[] sources) { var result = new Dictionary(); 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 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 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(); } } }