// Copyright (C) Stichting Deltares 2025. All rights reserved. // // This file is part of the Dam Engine. // // The Dam Engine is free software: you can redistribute it and/or modify // it under the terms of the GNU Affero General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Affero General Public License for more details. // // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . // // All names, logos, and references to "Deltares" are registered trademarks of // Stichting Deltares and remain full property of Stichting Deltares at all times. // All rights reserved. // // This file is retrieved from: // https://github.com/Burtsev-Alexey/net-object-deep-copy // // Source code is released under the MIT license. // // The MIT License (MIT) // Copyright (c) 2014 Burtsev Alexey // // Permission is hereby granted, free of charge, to any person obtaining a copy of this // software and associated documentation files (the "Software"), to deal in the Software // without restriction, including without limitation the rights to use, copy, modify, // merge, publish, distribute, sublicense, and/or sell copies of the Software, and to // permit persons to whom the Software is furnished to do so, subject to the following // conditions: // // The above copyright notice and this permission notice shall be included in all copies // or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, // INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A // PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF // CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE // OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. using System; using System.Collections.Generic; using System.Reflection; namespace Deltares.DamEngine.Data.Standard; /// /// Class to handle copying of objects /// public static class ObjectCopier { private static readonly MethodInfo CloneMethod = typeof(Object).GetMethod("MemberwiseClone", BindingFlags.NonPublic | BindingFlags.Instance); /// /// Determines whether this instance is primitive. /// /// The type. /// /// true if the specified type is primitive; otherwise, false. /// public static bool IsPrimitive(this Type type) { if (type == typeof(String)) { return true; } return type.IsValueType && type.IsPrimitive; } /// /// Copies the specified original object. /// /// The original object. /// public static Object Copy(Object originalObject) { return InternalCopy(originalObject, new Dictionary(new ReferenceEqualityComparer())); } /// /// Copies the specified original object with specified type. /// /// /// The original. /// public static T Copy(this T original) { return (T) Copy((Object) original); } private static Object InternalCopy(Object originalObject, IDictionary visited) { if (originalObject == null) { return null; } Type typeToReflect = originalObject.GetType(); if (IsPrimitive(typeToReflect)) { return originalObject; } if (visited.ContainsKey(originalObject)) { return visited[originalObject]; } if (typeof(Delegate).IsAssignableFrom(typeToReflect)) { return null; } object cloneObject = CloneMethod.Invoke(originalObject, null); if (typeToReflect.IsArray) { Type arrayType = typeToReflect.GetElementType(); if (!IsPrimitive(arrayType)) { var clonedArray = (Array) cloneObject; clonedArray.ForEach((array, indices) => array.SetValue(InternalCopy(clonedArray.GetValue(indices), visited), indices)); } } visited.Add(originalObject, cloneObject); CopyFields(originalObject, visited, cloneObject, typeToReflect); RecursiveCopyBaseTypePrivateFields(originalObject, visited, cloneObject, typeToReflect); return cloneObject; } private static void RecursiveCopyBaseTypePrivateFields(object originalObject, IDictionary visited, object cloneObject, Type typeToReflect) { if (typeToReflect.BaseType != null) { RecursiveCopyBaseTypePrivateFields(originalObject, visited, cloneObject, typeToReflect.BaseType); CopyFields(originalObject, visited, cloneObject, typeToReflect.BaseType, BindingFlags.Instance | BindingFlags.NonPublic, info => info.IsPrivate); } } private static void CopyFields(object originalObject, IDictionary visited, object cloneObject, Type typeToReflect, BindingFlags bindingFlags = BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.FlattenHierarchy, Func filter = null) { foreach (FieldInfo fieldInfo in typeToReflect.GetFields(bindingFlags)) { if (filter != null && !filter(fieldInfo)) { continue; } if (IsPrimitive(fieldInfo.FieldType)) { continue; } object originalFieldValue = fieldInfo.GetValue(originalObject); object clonedFieldValue = InternalCopy(originalFieldValue, visited); fieldInfo.SetValue(cloneObject, clonedFieldValue); } } }