18using System.Collections.Generic;
21using System.Threading.Tasks;
22using System.Reflection;
23using System.Collections;
25using System.Runtime.CompilerServices;
31 public new bool Equals(
object x,
object y) => ReferenceEquals(x, y);
32 public int GetHashCode(
object obj) => RuntimeHelpers.GetHashCode(obj);
38 internal static class ObjectCopier
40 public static T DeepCopyObject<T>(dynamic source, T destination, DeepCopy deepCopy, HashSet<object> visited =
null)
42 if (source ==
null || destination ==
null)
44 GPAL.
PublishSimpleEvent(GPALEventType.WARNING,
"Source and/or destination must not be null. Returning whichever is not null or default(T).",
null, GPALObjectType.None);
45 return null != source ? source :
null != destination ? destination :
default(T);
48 Type sourceType = source.GetType();
51 if (typeof(IDictionary).IsAssignableFrom(sourceType))
53 if (!typeof(IDictionary).IsAssignableFrom(destination.GetType()))
55 GPAL.PublishSimpleEvent(GPALEventType.WARNING, $
"Destination [{destination.GetType()}] is not a compatible [{sourceType}] dictionary type.", source, GPALObjectType.None);
58 CopyDictionary((IDictionary)source, (IDictionary)destination, deepCopy);
61 else if (typeof(IEnumerable).IsAssignableFrom(sourceType) && !IsImmutableType(sourceType))
63 if (!typeof(IList).IsAssignableFrom(destination.GetType()))
65 GPAL.PublishSimpleEvent(GPALEventType.WARNING, $
"Destination [{destination.GetType()}] is not a compatible list type.", source, GPALObjectType.None);
68 CopyObjects((IEnumerable)source, (IList)destination, deepCopy);
73 if (DeepCopy.Fields == deepCopy || DeepCopy.FieldsAndProperties == deepCopy)
74 destination = CopyFields(source, destination, deepCopy, visited);
75 if (DeepCopy.Properties == deepCopy || DeepCopy.FieldsAndProperties == deepCopy)
76 destination = CopyProperties(source, destination, deepCopy, visited);
81 static void CopyDictionary(IDictionary sourceDict, IDictionary destinationDict, DeepCopy deepCopy)
83 destinationDict.Clear();
85 foreach (var key
in sourceDict.Keys)
89 if (key is
string strKey)
91 else if (key is
int intKey)
98 GPAL.PublishSimpleEvent(GPALEventType.WARNING, $
"Unsupported dynamic key type [{key.GetType().Name}]. Using ToString as fallback.", key, GPALObjectType.None);
99 safeKey = key.ToString();
102 var sourceValue = sourceDict[key];
103 if (sourceValue ==
null)
105 destinationDict[safeKey] =
null;
109 Type valueType = sourceValue.GetType();
110 object destinationValue;
113 if (IsSystemInternalType(valueType) || IsCultureRelatedType(valueType))
115 destinationValue = sourceValue;
119 destinationValue = ConverterHelper.InstantiateOne(valueType);
122 if (!IsImmutableType(valueType))
124 if (DeepCopy.Fields == deepCopy || DeepCopy.FieldsAndProperties == deepCopy)
125 destinationValue = CopyFields(sourceValue, destinationValue, deepCopy);
126 if (DeepCopy.Properties == deepCopy || DeepCopy.FieldsAndProperties == deepCopy)
127 destinationValue = CopyProperties(sourceValue, destinationValue, deepCopy);
131 destinationValue = sourceValue;
135 destinationDict[safeKey] = destinationValue;
138 static IList CopyObjects(IEnumerable sourceObjects, IList destinationObjects, DeepCopy deepCopy)
140 foreach (var source
in sourceObjects)
142 var destination = ConverterHelper.InstantiateOne(source.GetType());
143 if (DeepCopy.Fields == deepCopy || DeepCopy.FieldsAndProperties == deepCopy)
144 destination = CopyFields(source, destination);
145 if (DeepCopy.Properties == deepCopy || DeepCopy.FieldsAndProperties == deepCopy)
146 destination = CopyProperties(source, destination);
147 destinationObjects.Add(destination);
149 return destinationObjects;
151 public static T CopyProperties<T>(
object source, T destination, DeepCopy deepCopy = DeepCopy.FieldsAndProperties, HashSet<object> visited =
null)
153 visited = visited ??
new HashSet<object>(
new ReferenceEqualityComparer());
154 if (!visited.Add(source))
156 GPAL.PublishSimpleEvent(GPALEventType.WARNING, $
"Circular reference detected in CopyProperties @ [{source}].", source, GPALObjectType.None);
160 Type sourceType = source.GetType();
161 Type destType = destination.GetType();
162 PropertyInfo[] properties = sourceType.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
165 if (typeof(IDictionary).IsAssignableFrom(sourceType))
167 if (!typeof(IDictionary).IsAssignableFrom(destType))
169 GPAL.PublishSimpleEvent(GPALEventType.WARNING, $
"Destination [{destType}] is not a compatible [{sourceType}] dictionary type.", source, GPALObjectType.None);
172 CopyDictionary((IDictionary)source, (IDictionary)destination, deepCopy);
175 else if (source is IGPALGrid<string>)
177 destination = (T)sourceType.GetMethod(
"Clone")?.Invoke(source,
null) ?? destination;
182 foreach (PropertyInfo property
in properties)
185 if (property.GetIndexParameters().Length > 0 ||
186 (property.DeclaringType !=
null && (typeof(ICollection).IsAssignableFrom(property.DeclaringType) ||
187 IsSystemInternalType(property.DeclaringType) ||
188 IsCultureRelatedType(property.DeclaringType))))
195 if (property.CanRead && property.CanWrite)
197 object sourceValue =
property.GetValue(source);
198 if (sourceValue ==
null)
200 property.SetValue(destination,
null);
204 Type propType =
property.PropertyType;
207 if (typeof(IEnumerable).IsAssignableFrom(propType) && !IsImmutableType(propType))
209 object destEnumerable = ConverterHelper.InstantiateOne(propType);
210 if (destEnumerable ==
null)
212 GPAL.PublishSimpleEvent(GPALEventType.WARNING, $
"Failed to instantiate enumerable type [{propType.Name}].", source, GPALObjectType.None);
216 if (typeof(IDictionary).IsAssignableFrom(propType))
218 CopyDictionary((IDictionary)sourceValue, (IDictionary)destEnumerable, deepCopy);
220 else if (typeof(IList).IsAssignableFrom(propType))
222 CopyObjects((IEnumerable)sourceValue, (IList)destEnumerable, deepCopy);
227 CopyGenericEnumerable((IEnumerable)sourceValue, destEnumerable, propType, deepCopy, visited);
230 property.SetValue(destination, destEnumerable);
233 else if (!propType.IsValueType && !IsImmutableType(propType) && !IsSystemInternalType(propType) && !IsCultureRelatedType(propType))
235 object destValue = ConverterHelper.InstantiateOne(propType);
236 var nestedVisited =
new HashSet<object>(
new ReferenceEqualityComparer());
237 destValue = DeepCopyObject(sourceValue, destValue, deepCopy, nestedVisited);
238 property.SetValue(destination, destValue);
243 property.SetValue(destination, sourceValue);
249 GPAL.PublishSimpleEvent(GPALEventType.EXCEPTION, $
"Failed to copy property [{property.Name}] of type [{sourceType.Name}].", source, GPALObjectType.Other, ex);
256 public static T CopyFields<T>(
object source, T destination, DeepCopy deepCopy = DeepCopy.FieldsAndProperties, HashSet<object> visited =
null)
258 visited = visited ??
new HashSet<object>(
new ReferenceEqualityComparer());
259 if (!visited.Add(source))
261 GPAL.PublishSimpleEvent(GPALEventType.WARNING, $
"Circular reference detected in CopyFields @ [{source}].", source, GPALObjectType.None);
265 Type sourceType = source.GetType();
266 Type destType = destination.GetType();
267 FieldInfo[] fields = sourceType.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
269 foreach (FieldInfo field
in fields)
272 if (IsSystemInternalType(field.FieldType) || IsCultureRelatedType(field.FieldType))
279 object sourceValue = field.GetValue(source);
280 if (sourceValue ==
null)
282 field.SetValue(destination,
null);
286 Type fieldType = field.FieldType;
289 if (typeof(IEnumerable).IsAssignableFrom(fieldType) && !IsImmutableType(fieldType))
291 object destEnumerable = ConverterHelper.InstantiateOne(fieldType);
292 if (destEnumerable ==
null)
294 GPAL.PublishSimpleEvent(GPALEventType.WARNING, $
"Failed to instantiate enumerable type [{fieldType.Name}].", source, GPALObjectType.None);
298 if (typeof(IDictionary).IsAssignableFrom(fieldType))
300 CopyDictionary((IDictionary)sourceValue, (IDictionary)destEnumerable, deepCopy);
302 else if (typeof(IList).IsAssignableFrom(fieldType))
304 CopyObjects((IEnumerable)sourceValue, (IList)destEnumerable, deepCopy);
309 CopyGenericEnumerable((IEnumerable)sourceValue, destEnumerable, fieldType, deepCopy, visited);
312 field.SetValue(destination, destEnumerable);
315 else if (!fieldType.IsValueType && !IsImmutableType(fieldType) && !IsSystemInternalType(fieldType) && !IsCultureRelatedType(fieldType))
317 object destValue = ConverterHelper.InstantiateOne(fieldType);
318 var nestedVisited =
new HashSet<object>(
new ReferenceEqualityComparer());
319 destValue = DeepCopyObject(sourceValue, destValue, deepCopy, nestedVisited);
320 field.SetValue(destination, destValue);
325 field.SetValue(destination, sourceValue);
330 GPAL.PublishSimpleEvent(GPALEventType.EXCEPTION, $
"Failed to copy field [{field.Name}] of type [{sourceType.Name}].", source, GPALObjectType.Other, ex);
335 private static void CopyGenericEnumerable(IEnumerable sourceEnumerable,
object destEnumerable, Type enumerableType, DeepCopy deepCopy, HashSet<object> visited)
337 MethodInfo addMethod = enumerableType.GetMethod(
"Add");
338 if (addMethod ==
null)
340 GPAL.PublishSimpleEvent(GPALEventType.WARNING, $
"No Add method found for type [{enumerableType.Name}].",
null, GPALObjectType.None);
344 foreach (var item
in sourceEnumerable)
348 addMethod.Invoke(destEnumerable,
new object[] {
null });
352 Type itemType = item.GetType();
353 object destItem =
null;
355 if (!itemType.IsValueType && !IsImmutableType(itemType) && !IsSystemInternalType(itemType) && !IsCultureRelatedType(itemType))
357 if (!visited.Add(item))
359 GPAL.PublishSimpleEvent(GPALEventType.WARNING, $
"Circular reference detected for item of type [{itemType.Name}].", item, GPALObjectType.None);
363 destItem = ConverterHelper.InstantiateOne(itemType);
364 var nestedVisited =
new HashSet<object>(
new ReferenceEqualityComparer());
365 destItem = DeepCopyObject(item, destItem, deepCopy, nestedVisited);
372 addMethod.Invoke(destEnumerable,
new object[] { destItem });
375 private static bool IsImmutableType(Type type)
377 return type == typeof(
string) ||
378 type == typeof(DateTime) ||
379 type == typeof(TimeSpan) ||
380 type == typeof(decimal) ||
381 type == typeof(Guid);
384 private static bool IsSystemInternalType(Type type)
386 return type.Namespace !=
null &&
387 (type.Namespace.StartsWith(
"System.Reflection") ||
388 type.Namespace.StartsWith(
"System.Runtime") ||
389 type.Namespace.StartsWith(
"System.Threading") ||
390 type.Namespace.StartsWith(
"System.Security"));
393 private static bool IsCultureRelatedType(Type type)
395 return type == typeof(System.Globalization.CultureInfo) ||
396 type.Namespace ==
"System.Globalization";
398 public static int GetItemCount(dynamic propertyOrField, dynamic source)
401 if (source is IEnumerable<object> objectEnumerable)
403 return objectEnumerable.Count();
405 else if (source is Array array)
409 else if (source is ICollection collection)
411 return collection.Count;
413 else if (
null != propertyOrField)
415 dynamic enumeration = propertyOrField.GetValue(source);
416 if (
null != enumeration)
417 foreach (dynamic item
in enumeration)
424 static bool IsPropertyEnumerable(PropertyInfo property)
427 return typeof(IEnumerable).IsAssignableFrom(property.PropertyType);
429 static bool IsFieldEnumerable(FieldInfo field)
431 return typeof(IEnumerable).IsAssignableFrom(field.FieldType);
Everything starts here, all the GPAL controls and global settings using fluent syntax are here....
static void PublishSimpleEvent(GPALEventType gPALEventType, string msg, dynamic gPALObject=null, Enums.GPALObjectType gPALObjectType=GPALObjectType.None, Exception ex=null)
Publish a message to either the information channel or exception channel (if exception passed in) Pub...