From 75e06985e61d85ca9cd82b9726927679b091fb15 Mon Sep 17 00:00:00 2001 From: Alessio Lombardi Date: Wed, 21 Oct 2020 17:01:57 +0100 Subject: [PATCH 01/28] Adding feature to check for `PersistentId` --- BHoM_Engine/Query/FindFragment.cs | 12 ++++++++++++ Diffing_Engine/Compute/DiffRevisions.cs | 11 +++++++++++ Diffing_Engine/Compute/DiffWithFragmentId.cs | 14 ++++++++++---- Diffing_Engine/Compute/_IDiffing.cs | 17 ++++++++++++++--- 4 files changed, 47 insertions(+), 7 deletions(-) diff --git a/BHoM_Engine/Query/FindFragment.cs b/BHoM_Engine/Query/FindFragment.cs index ae8aea55c..9ef1c9dae 100644 --- a/BHoM_Engine/Query/FindFragment.cs +++ b/BHoM_Engine/Query/FindFragment.cs @@ -51,5 +51,17 @@ public static T FindFragment(this IBHoMObject iBHoMObject, Type fragmentType return (T)System.Convert.ChangeType(fragment, fragmentType); } + + [Description("Returns an instance of a BHoM Fragment if it exists on the object")] + [Input("iBHoMObject", "A generic IBHoMObject object")] + [Input("fragmentType", "The type of fragment to be queried and returned. If not specified, the generic type is taken.")] + [Output("fragment", "The instance of that Fragment if it exists on the object, null otherwise")] + public static IFragment FindFragment(this IBHoMObject iBHoMObject, Type fragmentType) + { + IFragment fragment; + iBHoMObject.Fragments.TryGetValue(fragmentType, out fragment); + + return fragment; + } } } diff --git a/Diffing_Engine/Compute/DiffRevisions.cs b/Diffing_Engine/Compute/DiffRevisions.cs index 0f5f9e63c..4887095c6 100644 --- a/Diffing_Engine/Compute/DiffRevisions.cs +++ b/Diffing_Engine/Compute/DiffRevisions.cs @@ -179,6 +179,17 @@ private static bool AllHaveHashFragment(this IEnumerable bHoMObject return true; } + + private static bool AllHavePersistentIdFragment(this IEnumerable bHoMObjects) + { + // Check if objects have persistentId fragment, and it has not null Id. + if (bHoMObjects == null + || bHoMObjects.Count() == 0 + || bHoMObjects.Select(o => o.FindFragment()).Where(f => f.PersistentId != null).Count() < bHoMObjects.Count()) + return false; + + return true; + } } } diff --git a/Diffing_Engine/Compute/DiffWithFragmentId.cs b/Diffing_Engine/Compute/DiffWithFragmentId.cs index b1c95f5d9..e04e68dac 100644 --- a/Diffing_Engine/Compute/DiffWithFragmentId.cs +++ b/Diffing_Engine/Compute/DiffWithFragmentId.cs @@ -45,11 +45,17 @@ public static partial class Compute [Description("Computes the Diffing for BHoMObjects based on an id stored in a Fragment.")] [Input("pastObjects", "A set of objects coming from a past revision")] [Input("currentObjects", "A set of objects coming from a following Revision")] - [Input("fragmentType", "Fragment Type where the Id of the objects may be found in the BHoMObjects. The diff will be attempted using the Ids found there.")] - [Input("fragmentIdProperty", "Name of the Fragment's property where the Id is stored.")] - [Input("diffConfig", "Sets configs such as properties to be ignored in the diffing, or enable/disable property-by-property diffing.")] - public static Diff DiffWithFragmentId(IEnumerable pastObjects, IEnumerable currentObjects, Type fragmentType, string fragmentIdProperty, DiffConfig diffConfig = null) + [Input("fragmentType", "(Optional - defaults to the `IPersistentId` fragment)\nFragment Type where the Id of the objects may be found in the BHoMObjects. The diff will be attempted using the Ids found there.")] + [Input("fragmentIdProperty", "(Optional - defaults to `PersistentId`)\nName of the property of the Fragment where the Id is stored.")] + [Input("diffConfig", "(Optional) Sets configs such as properties to be ignored in the diffing, or enable/disable property-by-property diffing.")] + public static Diff DiffWithFragmentId(IEnumerable pastObjects, IEnumerable currentObjects, Type fragmentType = null, string fragmentIdProperty = null, DiffConfig diffConfig = null) { + if (fragmentType == null) + { + fragmentType = typeof(IPersistentId); + fragmentIdProperty = nameof(IPersistentId.PersistentId); + } + // Set configurations if diffConfig is null. Clone it for immutability in the UI. DiffConfig diffConfigCopy = diffConfig == null ? new DiffConfig() : (DiffConfig)diffConfig.DeepClone(); diff --git a/Diffing_Engine/Compute/_IDiffing.cs b/Diffing_Engine/Compute/_IDiffing.cs index 3eb6e6ed3..f876778d6 100644 --- a/Diffing_Engine/Compute/_IDiffing.cs +++ b/Diffing_Engine/Compute/_IDiffing.cs @@ -89,9 +89,19 @@ public static Diff Diffing(IEnumerable pastObjs, IEnumerable fol if (bHoMObjects_past.AllHaveHashFragment() && bHoMObjects_following.AllHaveHashFragment()) { BH.Engine.Reflection.Compute.RecordNote($"Calling the diffing method '{nameof(DiffRevisionObjects)}'."); - return DiffRevisionObjects(pastObjs, followingObjs, diffConfigCopy); + return DiffRevisionObjects(bHoMObjects_past, bHoMObjects_following, diffConfigCopy); } + // Check if the BHoMObjects all have a `persistentId` assigned. + // If so, we may attempt the DiffWithFragmentId diffing. + if (bHoMObjects_past.AllHavePersistentIdFragment() && bHoMObjects_following.AllHavePersistentIdFragment()) + { + BH.Engine.Reflection.Compute.RecordNote($"Calling the diffing method '{nameof(DiffWithFragmentId)}'."); + return DiffWithFragmentId(bHoMObjects_past, bHoMObjects_following, typeof(IPersistentId), nameof(IPersistentId.PersistentId), diffConfigCopy); + } + + // If the collections have the same length, and `AllowOneByOneDiffing` is enabled, + // compare objects from the two collections one by one. if (diffConfigCopy.AllowOneByOneDiffing && pastObjs.Count() == followingObjs.Count()) { BH.Engine.Reflection.Compute.RecordNote($"Calling the diffing method '{nameof(DiffOneByOne)}'" + @@ -100,13 +110,14 @@ public static Diff Diffing(IEnumerable pastObjs, IEnumerable fol return DiffOneByOne(pastObjs, followingObjs, diffConfigCopy); } - + // As last resort, compute the hash of each object and compare the objects with the same hash. + // Options on how the hash should be computed can be set in the diffconfig. BH.Engine.Reflection.Compute.RecordNote($"Calling the most generic Diffing method, '{nameof(DiffGenericObjects)}'." + $"\nThis will only identify new/deleted objects; it will not track which object was modified." + $"\nReason: the inputs do not satisfy any of the following conditions (at least one is needed to trigger another more detailed diffing):" + $"\n\t* Not all BHoMObjects have a HashFragment assigned (they didn't pass through a Revision);" + $"\n\t* No {nameof(customDataIdKey)} was input." + - $"\n\t* The input collections have different legths."); + $"\n\t* The input collections have different lengths."); return DiffGenericObjects(pastObjs as dynamic, followingObjs as dynamic, diffConfigCopy); } } From f44db18d4e4c50a2166461bbc2dff11a1d3adb1a Mon Sep 17 00:00:00 2001 From: Alessio Lombardi Date: Tue, 27 Oct 2020 10:14:29 +0000 Subject: [PATCH 02/28] Update DiffRevisions.cs --- Diffing_Engine/Compute/DiffRevisions.cs | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/Diffing_Engine/Compute/DiffRevisions.cs b/Diffing_Engine/Compute/DiffRevisions.cs index 4887095c6..cc1d902eb 100644 --- a/Diffing_Engine/Compute/DiffRevisions.cs +++ b/Diffing_Engine/Compute/DiffRevisions.cs @@ -180,15 +180,21 @@ private static bool AllHaveHashFragment(this IEnumerable bHoMObject return true; } - private static bool AllHavePersistentIdFragment(this IEnumerable bHoMObjects) + private static List WithNonNullPersistentAdapterId(this IEnumerable objects, out List reminder) { - // Check if objects have persistentId fragment, and it has not null Id. - if (bHoMObjects == null - || bHoMObjects.Count() == 0 - || bHoMObjects.Select(o => o.FindFragment()).Where(f => f.PersistentId != null).Count() < bHoMObjects.Count()) - return false; + List output = new List(); + reminder = new List(); - return true; + foreach (var obj in objects) + { + IBHoMObject ibhomobject = obj as IBHoMObject; + if (ibhomobject != null && ibhomobject.FindFragment().PersistentId != null) + output.Add(ibhomobject); + else + reminder.Add(obj); + } + + return output; } } } From 4c5e66d568e5733e96a97923b6c6340d35364eaa Mon Sep 17 00:00:00 2001 From: Alessio Lombardi Date: Tue, 27 Oct 2020 10:46:09 +0000 Subject: [PATCH 03/28] First test with IDiffing leveraging PersistentId. --- Diffing_Engine/Compute/DiffWithFragmentId.cs | 9 ++- Diffing_Engine/Compute/_IDiffing.cs | 34 +++++++---- Diffing_Engine/Diffing_Engine.csproj | 1 + Diffing_Engine/Modify/AddToDiff.cs | 59 ++++++++++++++++++++ 4 files changed, 88 insertions(+), 15 deletions(-) create mode 100644 Diffing_Engine/Modify/AddToDiff.cs diff --git a/Diffing_Engine/Compute/DiffWithFragmentId.cs b/Diffing_Engine/Compute/DiffWithFragmentId.cs index e04e68dac..e8411977c 100644 --- a/Diffing_Engine/Compute/DiffWithFragmentId.cs +++ b/Diffing_Engine/Compute/DiffWithFragmentId.cs @@ -50,10 +50,13 @@ public static partial class Compute [Input("diffConfig", "(Optional) Sets configs such as properties to be ignored in the diffing, or enable/disable property-by-property diffing.")] public static Diff DiffWithFragmentId(IEnumerable pastObjects, IEnumerable currentObjects, Type fragmentType = null, string fragmentIdProperty = null, DiffConfig diffConfig = null) { - if (fragmentType == null) + if (fragmentType == null || string.IsNullOrWhiteSpace(fragmentIdProperty)) { - fragmentType = typeof(IPersistentId); - fragmentIdProperty = nameof(IPersistentId.PersistentId); + fragmentType = typeof(IPersistentAdapterId); + fragmentIdProperty = nameof(IPersistentAdapterId.PersistentId); + + BH.Engine.Reflection.Compute.RecordNote($"No `{nameof(fragmentType)}` or `{nameof(fragmentIdProperty)}` specified." + + $"\nDefaulted to `{typeof(IPersistentAdapterId).FullName}.{nameof(IPersistentAdapterId.PersistentId)}`."); } // Set configurations if diffConfig is null. Clone it for immutability in the UI. diff --git a/Diffing_Engine/Compute/_IDiffing.cs b/Diffing_Engine/Compute/_IDiffing.cs index f876778d6..dbd239c63 100644 --- a/Diffing_Engine/Compute/_IDiffing.cs +++ b/Diffing_Engine/Compute/_IDiffing.cs @@ -92,15 +92,7 @@ public static Diff Diffing(IEnumerable pastObjs, IEnumerable fol return DiffRevisionObjects(bHoMObjects_past, bHoMObjects_following, diffConfigCopy); } - // Check if the BHoMObjects all have a `persistentId` assigned. - // If so, we may attempt the DiffWithFragmentId diffing. - if (bHoMObjects_past.AllHavePersistentIdFragment() && bHoMObjects_following.AllHavePersistentIdFragment()) - { - BH.Engine.Reflection.Compute.RecordNote($"Calling the diffing method '{nameof(DiffWithFragmentId)}'."); - return DiffWithFragmentId(bHoMObjects_past, bHoMObjects_following, typeof(IPersistentId), nameof(IPersistentId.PersistentId), diffConfigCopy); - } - - // If the collections have the same length, and `AllowOneByOneDiffing` is enabled, + // If `AllowOneByOneDiffing` is enabled and the collections have the same length, // compare objects from the two collections one by one. if (diffConfigCopy.AllowOneByOneDiffing && pastObjs.Count() == followingObjs.Count()) { @@ -110,15 +102,33 @@ public static Diff Diffing(IEnumerable pastObjs, IEnumerable fol return DiffOneByOne(pastObjs, followingObjs, diffConfigCopy); } + // Check if the BHoMObjects have a `persistentId` assigned. + // If so, we may attempt the DiffWithFragmentId diffing. + List reminder_past, reminder_following; + List bHoMObjects_past_persistId = bHoMObjects_past.WithNonNullPersistentAdapterId(out reminder_past); + List bHoMObjects_following_persistId = bHoMObjects_following.WithNonNullPersistentAdapterId(out reminder_following); + Diff fragmentDiff = null; + + if (bHoMObjects_past_persistId.Count != 0 && bHoMObjects_following_persistId.Count != 0) + { + BH.Engine.Reflection.Compute.RecordNote($"Calling the diffing method '{nameof(DiffWithFragmentId)}'."); + fragmentDiff = DiffWithFragmentId(bHoMObjects_past_persistId, bHoMObjects_following_persistId, typeof(IPersistentAdapterId), nameof(IPersistentAdapterId.PersistentId), diffConfigCopy); + } + // As last resort, compute the hash of each object and compare the objects with the same hash. - // Options on how the hash should be computed can be set in the diffconfig. + // Remember we can use DiffConfig.PropertiesToConsider in order to tell Diffing to tell the difference only based on certain properties. BH.Engine.Reflection.Compute.RecordNote($"Calling the most generic Diffing method, '{nameof(DiffGenericObjects)}'." + - $"\nThis will only identify new/deleted objects; it will not track which object was modified." + $"\nReason: the inputs do not satisfy any of the following conditions (at least one is needed to trigger another more detailed diffing):" + $"\n\t* Not all BHoMObjects have a HashFragment assigned (they didn't pass through a Revision);" + $"\n\t* No {nameof(customDataIdKey)} was input." + $"\n\t* The input collections have different lengths."); - return DiffGenericObjects(pastObjs as dynamic, followingObjs as dynamic, diffConfigCopy); + + Diff diffGeneric = DiffGenericObjects(pastObjs as dynamic, followingObjs as dynamic, diffConfigCopy); + + if (fragmentDiff == null) + return diffGeneric; + + return fragmentDiff.AddToDiff(diffGeneric); } } } diff --git a/Diffing_Engine/Diffing_Engine.csproj b/Diffing_Engine/Diffing_Engine.csproj index 9ba88ff2b..cd2413685 100644 --- a/Diffing_Engine/Diffing_Engine.csproj +++ b/Diffing_Engine/Diffing_Engine.csproj @@ -72,6 +72,7 @@ + diff --git a/Diffing_Engine/Modify/AddToDiff.cs b/Diffing_Engine/Modify/AddToDiff.cs new file mode 100644 index 000000000..af1ccf349 --- /dev/null +++ b/Diffing_Engine/Modify/AddToDiff.cs @@ -0,0 +1,59 @@ +/* + * This file is part of the Buildings and Habitats object Model (BHoM) + * Copyright (c) 2015 - 2020, the respective contributors. All rights reserved. + * + * Each contributor holds copyright over their respective contributions. + * The project versioning (Git) records all such contribution source information. + * + * + * The BHoM is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3.0 of the License, or + * (at your option) any later version. + * + * The BHoM 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this code. If not, see . + */ + +using BH.oM.Base; +using BH.oM.Data.Collections; +using BH.oM.Diffing; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Security.Cryptography; +using System.Reflection; +using BH.Engine.Serialiser; +using BH.oM.Reflection.Attributes; +using System.ComponentModel; +using BH.Engine.Base; + +namespace BH.Engine.Diffing +{ + public static partial class Modify + { + [Description("Clones the IBHoMObjects, computes their hash and stores it in a HashFragment. " + + "If the object already has a HashFragment, it computes the current one and keeps the old one in the `previousHash` of the HashFragment.")] + public static Diff AddToDiff(this Diff diff, Diff toAdd) + { + Diff newDiff = new Diff( + diff.AddedObjects.Concat(toAdd.AddedObjects), + diff.RemovedObjects.Concat(toAdd.RemovedObjects), + diff.ModifiedObjects.Concat(toAdd.ModifiedObjects), + diff.DiffConfig, + diff.ModifiedPropsPerObject.Concat(toAdd.ModifiedPropsPerObject).ToDictionary(x => x.Key, x => x.Value), + diff.UnchangedObjects.Concat(toAdd.UnchangedObjects) + ); + + return newDiff; + } + } +} + From 78d5bdb2be03ec6d31b9fa20bc153ac67aac7884 Mon Sep 17 00:00:00 2001 From: Alessio Lombardi Date: Thu, 12 Nov 2020 16:44:14 +0000 Subject: [PATCH 04/28] Refactored for consistency (Hash/Diff configs); add persistentId --- BHoM_Engine/Create/IObject/RandomObject.cs | 6 +- BHoM_Engine/Modify/RemoveFragment.cs | 2 +- BHoM_Engine/Query/Hash.cs | 95 +++++--------- Diffing_Engine/Compute/DiffGenericObjects.cs | 6 +- Diffing_Engine/Compute/DiffOneByOne.cs | 2 +- Diffing_Engine/Compute/DiffRevisions.cs | 51 ++++---- Diffing_Engine/Compute/Hash/DiffingHash.cs | 20 +-- Diffing_Engine/Compute/_IDiffing.cs | 34 ++--- Diffing_Engine/Create/DiffConfig.cs | 7 +- Diffing_Engine/Diffing_Engine.csproj | 11 +- .../Modify/{AddToDiff.cs => CombineDiffs.cs} | 15 ++- Diffing_Engine/Modify/PrepareForRevision.cs | 2 +- .../Modify/RemoveDuplicatesByHash.cs | 2 +- Diffing_Engine/Modify/SetHashFragment.cs | 22 +--- Diffing_Engine/Modify/SetRevisionFragment.cs | 96 ++++++++++++++ ...DiffingHashComparer.cs => HashComparer.cs} | 34 ++--- .../EqualityComparers/RevisionHashComparer.cs | 120 ++++++++++++++++++ Diffing_Engine/Query/DifferentProperties.cs | 11 +- .../{GetHashFragment.cs => HashFragment.cs} | 2 +- Diffing_Engine/Query/RevisionFragment.cs | 42 ++++++ .../Query/HasMergeablePropertiesWith.cs | 60 +++++---- .../Compute/AggregateMaterialComposition.cs | 2 +- Physical_Engine/Query/UniqueConstructions.cs | 15 ++- 23 files changed, 447 insertions(+), 210 deletions(-) rename Diffing_Engine/Modify/{AddToDiff.cs => CombineDiffs.cs} (85%) create mode 100644 Diffing_Engine/Modify/SetRevisionFragment.cs rename Diffing_Engine/Objects/EqualityComparers/{DiffingHashComparer.cs => HashComparer.cs} (75%) create mode 100644 Diffing_Engine/Objects/EqualityComparers/RevisionHashComparer.cs rename Diffing_Engine/Query/{GetHashFragment.cs => HashFragment.cs} (95%) create mode 100644 Diffing_Engine/Query/RevisionFragment.cs diff --git a/BHoM_Engine/Create/IObject/RandomObject.cs b/BHoM_Engine/Create/IObject/RandomObject.cs index 5ab4065ee..2019006bf 100644 --- a/BHoM_Engine/Create/IObject/RandomObject.cs +++ b/BHoM_Engine/Create/IObject/RandomObject.cs @@ -215,7 +215,11 @@ private static object GetValue(Type type, Random rnd, int depth) else if (type.IsInterface || type.IsAbstract) { if (depth > 50) return null; - return GetValue(m_ImplementingTypes[type], rnd, depth + 1); + + Type obj = null; + if (!m_ImplementingTypes.TryGetValue(type, out obj)) return null; + + return GetValue(obj, rnd, depth + 1); } else { diff --git a/BHoM_Engine/Modify/RemoveFragment.cs b/BHoM_Engine/Modify/RemoveFragment.cs index 829d8a6cc..1ee8ccf27 100644 --- a/BHoM_Engine/Modify/RemoveFragment.cs +++ b/BHoM_Engine/Modify/RemoveFragment.cs @@ -39,7 +39,7 @@ public static partial class Modify [Input("fragmentType", "The type of fragment that should be removed from the object.")] [Output("iBHoMObject", "The BHoM object with the added fragment.")] public static IBHoMObject RemoveFragment(this IBHoMObject iBHoMObject, Type fragmentType = null) - { + { if (fragmentType == null) return iBHoMObject; if (iBHoMObject == null) return null; IBHoMObject o = iBHoMObject.DeepClone(); diff --git a/BHoM_Engine/Query/Hash.cs b/BHoM_Engine/Query/Hash.cs index c816c4aa4..0d5aa3a97 100644 --- a/BHoM_Engine/Query/Hash.cs +++ b/BHoM_Engine/Query/Hash.cs @@ -46,44 +46,26 @@ public static partial class Query [Description("Computes a Hash code for the iObject. The hash uniquely represents an object's state based on its combined properties and their values.")] [Input("iObj", "iObject the hash code should be calculated for.")] - public static string Hash(this IObject iObj) + public static string Hash(this IObject iObj, HashConfig hashConfig = null) { - return Hash(iObj, null); - } + // Make sure to clone for immutability, and always have a HashConfig. + HashConfig hc = hashConfig == null ? new HashConfig() : hashConfig.DeepClone(); - /***************************************************/ + // Make sure that "BHoM_Guid" is added to the propertyNameExceptions of the HashConfig. + hc.PropertyNameExceptions = hc.PropertyNameExceptions ?? new List(); + if (!hc.PropertyNameExceptions.Contains(nameof(BHoMObject.BHoM_Guid))) + hc.PropertyNameExceptions.Add(nameof(BHoMObject.BHoM_Guid)); - [Description("Computes a Hash code for the iObject with exceptions and custom options.\n" - + "The hash uniquely represents an object's state based on the included properties and their values.")] - [Input("iObj", "iObject the hash code should be calculated for.")] - [Input("propertyNameExceptions", "(Optional) e.g. `Fragments`. If you want to exclude a property that many objects have.")] - [Input("propertyFullNameExceptions", "(Optional) e.g. `BH.oM.Structure.Elements.Bar.Fragments`. If you want to exclude a specific property of an object.")] - [Input("namespaceExceptions", "(Optional) e.g. `BH.oM.Structure`. Any corresponding namespace is ignored.")] - [Input("typeExceptions", "(Optional) e.g. `typeof(Guid)`. Any corresponding type is ignored.")] - [Input("maxNesting", "(Optional) e.g. `100`. If any property is nested into the object over that level, it is ignored.")] - [Input("fractionalDigits", "(Optional) Defaults to 12. Number of digits retained after the comma; applies rounding.")] - [Input("fractionalDigitsPerProperty", "(Optional) e.g. `{ { StartNode.Point.X, 2 } }`. If a property name matches this, applies a rounding to the corresponding number of digits specified.\nSupports * wildcard.")] - public static string Hash( - this IObject iObj, - List propertyNameExceptions = null, //e.g. `BHoM_Guid` - List propertyFullNameExceptions = null, //e.g. `BH.oM.Structure.Elements.Bar.Fragments` – can use * wildcard here. - List namespaceExceptions = null, //e.g. `BH.oM.Structure` - List typeExceptions = null, //e.g. `typeof(Guid)` - int maxNesting = 100, - int fractionalDigits = 12, - Dictionary fractionalDigitsPerProperty = null // e.g. { { StartNode.Point.X, 2 } } – can use * wildcard here. - ) - { - // Make sure that "BHoM_Guid" is added to the propertyNameExceptions. - propertyNameExceptions = propertyNameExceptions ?? new List(); - if (!propertyNameExceptions.Contains(nameof(BHoMObject.BHoM_Guid))) - propertyNameExceptions.Add(nameof(BHoMObject.BHoM_Guid)); + int fractionalDigits = Math.Abs(Convert.ToInt32(Math.Log10(hc.NumericTolerance))); + + string hashString = DefiningString(iObj, hc, fractionalDigits, 0); - string hashString = DefiningString(iObj, 0, maxNesting, propertyNameExceptions, propertyFullNameExceptions, namespaceExceptions, typeExceptions, fractionalDigits, fractionalDigitsPerProperty); + if (string.IsNullOrWhiteSpace(hashString)) + throw new Exception("Error computing the defining string of the object."); return SHA256Hash(hashString); - } + } /***************************************************/ /**** Private Methods ****/ @@ -108,26 +90,10 @@ private static string SHA256Hash(string str) [Description("Generates a string representing the whole structure of the object with its assigned values.")] [Input("obj", "Objects the string should be calculated for.")] + [Input("hc", "HashConfig, options for the hash calculation.")] [Input("nestingLevel", "Nesting level of the property.")] - [Input("maxNesting", "(Optional) e.g. `100`. If any property is nested into the object over that level, it is ignored.")] - [Input("propertyNameExceptions", "(Optional) e.g. `Fragments`. If you want to exclude a property that many objects have.")] - [Input("propertyFullNameExceptions", "(Optional) e.g. `BH.oM.Structure.Elements.Bar.Fragments`. If you want to exclude a specific property of an object.")] - [Input("namespaceExceptions", "(Optional) e.g. `BH.oM.Structure`. Any corresponding namespace is ignored.")] - [Input("typeExceptions", "(Optional) e.g. `typeof(Guid)`. Any corresponding type is ignored.")] - [Input("fractionalDigits", "(Optional) Defaults to 12. Number of digits retained after the comma; applies rounding.")] - [Input("fractionalDigitsPerProperty", "(Optional) e.g. `{ { StartNode.Point.X, 2 } }`. If a property name matches this, applies a rounding to the corresponding number of digits specified.")] [Input("propertyPath", "(Optional) Indicates the 'property path' of the current object, e.g. `BH.oM.Structure.Elements.Bar.StartNode.Point.X`")] - private static string DefiningString( - object obj, - int nestingLevel, - int maxNesting = 100, - List propertyNameExceptions = null, //e.g. "BHoM_Guid" - List propertyFullNameExceptions = null, //e.g. "BH.oM.Structure.Elements.Bar.Fragments" - List namespaceExceptions = null, //e.g. "BH.oM.Structure" - List typeExceptions = null, //e.g. typeof(Guid) - int fractionalDigits = 12, - Dictionary fractionalDigitsPerProperty = null, // e.g. { { StartNode.Point.X, 2 } } – can use * wildcard here. - string propertyPath = null) // Indicates the "property path" of the current object, e.g. BH.oM.Structure.Elements.Bar.StartNode.Point.X + private static string DefiningString(object obj, HashConfig hc, int fractionalDigits, int nestingLevel, string propertyPath = null) { string composedString = ""; string tabs = new String('\t', nestingLevel); @@ -135,9 +101,9 @@ private static string DefiningString( Type type = obj?.GetType(); if (type == null - || (typeExceptions != null && typeExceptions.Contains(type)) - || (namespaceExceptions != null && namespaceExceptions.Where(ex => type.Namespace.Contains(ex)).Any()) - || nestingLevel >= maxNesting) + || (hc.TypeExceptions != null && hc.TypeExceptions.Contains(type)) + || (hc.NamespaceExceptions != null && hc.NamespaceExceptions.Where(ex => type.Namespace.Contains(ex)).Any()) + || nestingLevel >= hc.MaxNesting) { return composedString; } @@ -151,20 +117,20 @@ private static string DefiningString( else if (type.IsArray) { foreach (var element in (obj as dynamic)) - composedString += $"\n{tabs}" + DefiningString(element, nestingLevel + 1, maxNesting, propertyNameExceptions, propertyFullNameExceptions, namespaceExceptions, typeExceptions, fractionalDigits, fractionalDigitsPerProperty, propertyPath); + composedString += $"\n{tabs}" + DefiningString(element, hc, fractionalDigits, nestingLevel + 1, propertyPath); } else if (typeof(IDictionary).IsAssignableFrom(type)) { IDictionary dic = obj as IDictionary; foreach (DictionaryEntry entry in dic) { - composedString += $"\n{tabs}" + $"[{entry.Key.GetType().FullName}]\n{tabs}{entry.Key}:\n { DefiningString(entry.Value, nestingLevel + 1, maxNesting, propertyNameExceptions, propertyFullNameExceptions, namespaceExceptions, typeExceptions, fractionalDigits, fractionalDigitsPerProperty, propertyPath)}"; + composedString += $"\n{tabs}" + $"[{entry.Key.GetType().FullName}]\n{tabs}{entry.Key}:\n { DefiningString(entry.Value, hc, fractionalDigits, nestingLevel + 1, propertyPath)}"; } } else if (typeof(IEnumerable).IsAssignableFrom(type) || typeof(IList).IsAssignableFrom(type) || typeof(ICollection).IsAssignableFrom(type)) { foreach (var element in (obj as dynamic)) - composedString += $"\n{tabs}" + DefiningString(element, nestingLevel + 1, maxNesting, propertyNameExceptions, propertyFullNameExceptions, namespaceExceptions, typeExceptions, fractionalDigits, fractionalDigitsPerProperty, propertyPath); + composedString += $"\n{tabs}" + DefiningString(element, hc, fractionalDigits, nestingLevel + 1, propertyPath); } else if (type.FullName.Contains("System.Collections.Generic.ObjectEqualityComparer`1")) { @@ -173,7 +139,7 @@ private static string DefiningString( else if (type == typeof(System.Data.DataTable)) { DataTable dt = obj as DataTable; - return composedString += $"{type.FullName} {string.Join(", ", dt.Columns.OfType().Select(c => c.ColumnName))}\n{tabs}" + DefiningString(dt.AsEnumerable(), nestingLevel + 1, maxNesting, propertyNameExceptions, propertyFullNameExceptions, namespaceExceptions, typeExceptions, fractionalDigits, fractionalDigitsPerProperty, propertyPath); + return composedString += $"{type.FullName} {string.Join(", ", dt.Columns.OfType().Select(c => c.ColumnName))}\n{tabs}" + DefiningString(dt.AsEnumerable(), hc, fractionalDigits, nestingLevel + 1, propertyPath); } else if (typeof(IObject).IsAssignableFrom(type)) { @@ -181,12 +147,15 @@ private static string DefiningString( foreach (PropertyInfo prop in properties) { - bool isInPropertyNameExceptions = propertyNameExceptions != null && propertyNameExceptions.Where(ex => prop.Name.Contains(ex)).Any(); - bool isInPropertyFullNameExceptions = propertyFullNameExceptions != null && propertyFullNameExceptions.Where(ex => new WildcardPattern(ex).IsMatch(prop.Name + "." + prop.DeclaringType.FullName)).Any(); + bool isInPropertyNameExceptions = hc.PropertyNameExceptions?.Count > 0 && hc.PropertyNameExceptions.Where(ex => prop.Name.Contains(ex)).Any(); + bool isInPropertyFullNameExceptions = hc.PropertyFullNameExceptions?.Count > 0 && hc.PropertyFullNameExceptions.Where(ex => new WildcardPattern(ex).IsMatch(prop.Name + "." + prop.DeclaringType.FullName)).Any(); if (isInPropertyNameExceptions || isInPropertyFullNameExceptions) continue; + if (hc.PropertiesToConsider?.Count() > 0 && !hc.PropertiesToConsider.Contains(prop.Name)) + continue; + object propValue = prop.GetValue(obj); if (propValue != null) { @@ -197,14 +166,14 @@ private static string DefiningString( string outString = ""; - if (fractionalDigitsPerProperty != null && + if (hc.FractionalDigitsPerProperty != null && prop.PropertyType == typeof(double) || prop.PropertyType == typeof(decimal) || prop.PropertyType == typeof(float)) { Dictionary matches = new Dictionary(); string path = propertyPath + "." + prop.Name; - foreach (var kv in fractionalDigitsPerProperty) + foreach (var kv in hc.FractionalDigitsPerProperty) { if (path.Contains(kv.Key) || new WildcardPattern(kv.Key).IsMatch(path)) @@ -212,14 +181,14 @@ private static string DefiningString( } if (matches.Count() > 1) - throw new ArgumentException($"Too many matching results obtained with specified {nameof(fractionalDigitsPerProperty)}."); + throw new ArgumentException($"Too many matching results obtained with specified {nameof(hc.FractionalDigitsPerProperty)}."); int fracDigits = matches.Count() == 1 ? matches.FirstOrDefault().Value : fractionalDigits; - outString = DefiningString(propValue, nestingLevel + 1, maxNesting, propertyNameExceptions, propertyFullNameExceptions, namespaceExceptions, typeExceptions, fracDigits, fractionalDigitsPerProperty, path) ?? ""; + outString = DefiningString(propValue, hc, fracDigits, nestingLevel + 1, path) ?? ""; } else - outString = DefiningString(propValue, nestingLevel + 1, maxNesting, propertyNameExceptions, propertyFullNameExceptions, namespaceExceptions, typeExceptions, fractionalDigits, fractionalDigitsPerProperty, propertyPath) ?? ""; + outString = DefiningString(propValue, hc, fractionalDigits, nestingLevel + 1, propertyPath) ?? ""; if (!string.IsNullOrWhiteSpace(outString)) composedString += $"\n{tabs}" + $"{type.FullName}.{prop.Name}:\n{tabs}{outString} "; diff --git a/Diffing_Engine/Compute/DiffGenericObjects.cs b/Diffing_Engine/Compute/DiffGenericObjects.cs index 7311c7c58..ed4504836 100644 --- a/Diffing_Engine/Compute/DiffGenericObjects.cs +++ b/Diffing_Engine/Compute/DiffGenericObjects.cs @@ -48,7 +48,7 @@ public static partial class Compute [Input("currentObjects", "Following objects. Objects that were created after 'pastObjects'.")] [Input("diffConfig", "Sets configs such as properties to be ignored in the diffing, or enable/disable property-by-property diffing.")] [Input("useExistingHash", "Advanced setting. If the objects already have an HashFragment assigned, but that only has the 'currentHash' populated. Can be used to avoid recomputing hash in some scenarios.")] - public static Diff DiffGenericObjects(IEnumerable pastObjects, IEnumerable currentObjects, DiffConfig diffConfig = null, bool useExistingHash = false) + public static Diff DiffGenericObjects(IEnumerable pastObjects, IEnumerable currentObjects, DiffConfig diffConfig = null, bool keepExistingHash = false) { BH.Engine.Reflection.Compute.RecordNote("This diffing method cannot track modified objects between different revisions." + "\nIt will simply return the objects that appear exclusively in the past set, in the following set, and in both." + @@ -61,7 +61,7 @@ public static Diff DiffGenericObjects(IEnumerable pastObjects, IEnumerab List pastObjects_cloned = BH.Engine.Base.Query.DeepClone(pastObjects).ToList(); List currentObjects_cloned = BH.Engine.Base.Query.DeepClone(currentObjects).ToList(); - if (!useExistingHash) + if (!keepExistingHash) { // Clean any existing hash fragment. // This ensures the hash will be re-computed within this method using the provided DiffConfig. @@ -71,7 +71,7 @@ public static Diff DiffGenericObjects(IEnumerable pastObjects, IEnumerab // Compute the "Diffing" by means of a VennDiagram. // Hashes are computed in the DiffingHashComparer, once per each object (the hash is stored in a hashFragment). - VennDiagram vd = Engine.Data.Create.VennDiagram(pastObjects_cloned, currentObjects_cloned, new DiffingHashComparer(diffConfigCopy, true)); + VennDiagram vd = Engine.Data.Create.VennDiagram(pastObjects_cloned, currentObjects_cloned, new HashComparer(diffConfigCopy, true)); return new Diff(vd.OnlySet2, vd.OnlySet1, null, diffConfigCopy, null, vd.Intersection); } diff --git a/Diffing_Engine/Compute/DiffOneByOne.cs b/Diffing_Engine/Compute/DiffOneByOne.cs index 5285f565e..0b21b4fb4 100644 --- a/Diffing_Engine/Compute/DiffOneByOne.cs +++ b/Diffing_Engine/Compute/DiffOneByOne.cs @@ -49,7 +49,7 @@ public static partial class Compute [Input("currentObjects", "Following objects. Objects that were created after 'pastObjects'.")] [Input("diffConfig", "Sets configs such as properties to be ignored in the diffing, or enable/disable property-by-property diffing.")] [Input("useExistingHash", "Advanced setting. If the objects already have an HashFragment assigned, but that only has the 'currentHash' populated. Can be used to avoid recomputing hash in some scenarios.")] - public static Diff DiffOneByOne(IEnumerable pastObjects, IEnumerable currentObjects, DiffConfig diffConfig = null, bool useExistingHash = false) + public static Diff DiffOneByOne(IEnumerable pastObjects, IEnumerable currentObjects, DiffConfig diffConfig = null) { if (pastObjects.Count() != currentObjects.Count()) { diff --git a/Diffing_Engine/Compute/DiffRevisions.cs b/Diffing_Engine/Compute/DiffRevisions.cs index cc1d902eb..a43e21109 100644 --- a/Diffing_Engine/Compute/DiffRevisions.cs +++ b/Diffing_Engine/Compute/DiffRevisions.cs @@ -63,20 +63,19 @@ private static Diff DiffRevisionObjects(IEnumerable pastRevisionObjs, IE // Dispatch the objects in BHoMObjects and generic objects. IEnumerable prevObjs_BHoM = pastRevisionObjs.OfType(); IEnumerable currObjs_BHoM = followingRevisionObjs.OfType(); - - // If all objects are bhomobjects, just call the appropriate method - if (pastRevisionObjs.Count() != 0 && pastRevisionObjs.Count() == prevObjs_BHoM.Count() && followingRevisionObjs.Count() == currObjs_BHoM.Count()) - return DiffRevisionObjects(prevObjs_BHoM, currObjs_BHoM, diffConfigCopy); - IEnumerable prevObjs_nonBHoM = pastRevisionObjs.Where(o => !(o is IBHoMObject)); IEnumerable currObjs_nonBHoM = followingRevisionObjs.Where(o => !(o is IBHoMObject)); // Compute the specific Diffing for the BHoMObjects. Diff diff = Compute.DiffRevisionObjects(prevObjs_BHoM, currObjs_BHoM, diffConfigCopy); + // If all objects are BHoMObjects, we are done. + if (pastRevisionObjs.Count() != 0 && pastRevisionObjs.Count() == prevObjs_BHoM.Count() && followingRevisionObjs.Count() == currObjs_BHoM.Count()) + return diff; + // Compute the generic Diffing for the other objects. // This is left to the VennDiagram with a HashComparer. - VennDiagram vd = Engine.Data.Create.VennDiagram(prevObjs_nonBHoM, currObjs_nonBHoM, new DiffingHashComparer(diffConfig)); + VennDiagram vd = Engine.Data.Create.VennDiagram(prevObjs_nonBHoM, currObjs_nonBHoM, new RevisionHashComparer(diffConfig)); // Concatenate the results of the two diffing operations. List allPrevObjs = new List(); @@ -106,7 +105,7 @@ private static Diff DiffRevisionObjects(IEnumerable pastObjects, IE List readObjs = pastObjects.ToList(); // Make dictionary with object hashes to speed up the next lookups - Dictionary readObjs_dict = readObjs.ToDictionary(obj => obj.GetHashFragment().CurrentHash, obj => obj); + Dictionary readObjs_dict = readObjs.ToDictionary(obj => obj.RevisionFragment().CurrentHash, obj => obj); // Dispatch the objects: new, modified or old List newObjs = new List(); @@ -116,37 +115,39 @@ private static Diff DiffRevisionObjects(IEnumerable pastObjects, IE var objModifiedProps = new Dictionary>>(); - foreach (var obj in currentObjs) + foreach (IBHoMObject bhomObj in currentObjs) { - var hashFragm = obj.GetHashFragment(); + RevisionFragment revisionFragm = bhomObj.RevisionFragment(); - if (hashFragm?.PreviousHash == null) + if (revisionFragm?.PreviousHash == null) { - newObjs.Add(obj); // It's a new object + newObjs.Add(bhomObj); // It's a new object } - else if (hashFragm.PreviousHash == hashFragm.CurrentHash) + else if (revisionFragm.PreviousHash == revisionFragm.CurrentHash) { // It's NOT been modified if (diffConfigCopy.StoreUnchangedObjects) - unChanged.Add(obj); + unChanged.Add(bhomObj); } - else if (hashFragm.PreviousHash != hashFragm.CurrentHash) + else if (revisionFragm.PreviousHash != revisionFragm.CurrentHash) { - modifiedObjs.Add(obj); // It's been modified + modifiedObjs.Add(bhomObj); // It's been modified if (diffConfigCopy.EnablePropertyDiffing) { // Determine changed properties - IBHoMObject oldObjState = null; - readObjs_dict.TryGetValue(hashFragm.PreviousHash, out oldObjState); + IBHoMObject oldBhomObj = null; + readObjs_dict.TryGetValue(revisionFragm.PreviousHash, out oldBhomObj); - if (oldObjState == null) continue; + if (oldBhomObj == null) continue; - var differentProps = Query.DifferentProperties(obj, oldObjState, diffConfigCopy); + // To compute differentProps in a Revision-Diffing, make sure we remove the RevisionFragment. We don't want to consider that. + var differentProps = Query.DifferentProperties(bhomObj.RemoveFragment(typeof(RevisionFragment)), oldBhomObj.RemoveFragment(typeof(RevisionFragment)), diffConfigCopy); - objModifiedProps.Add(hashFragm.CurrentHash, differentProps); + if (differentProps != null) + objModifiedProps.Add(revisionFragm.CurrentHash, differentProps); } } else @@ -160,8 +161,8 @@ private static Diff DiffRevisionObjects(IEnumerable pastObjects, IE // All ReadObjs that cannot be found by hash in the previousHash of the CurrentObjs are toBeDeleted Dictionary CurrentObjs_withPreviousHash_dict = currentObjs - .Where(obj => obj.GetHashFragment().PreviousHash != null) - .ToDictionary(obj => obj.GetHashFragment().PreviousHash, obj => obj); + .Where(obj => obj.RevisionFragment().PreviousHash != null) + .ToDictionary(obj => obj.RevisionFragment().PreviousHash, obj => obj); oldObjs = readObjs_dict.Keys.Except(CurrentObjs_withPreviousHash_dict.Keys) .Where(k => readObjs_dict.ContainsKey(k)).Select(k => readObjs_dict[k]).ToList(); @@ -169,12 +170,12 @@ private static Diff DiffRevisionObjects(IEnumerable pastObjects, IE return new Diff(newObjs, oldObjs, modifiedObjs, diffConfig, objModifiedProps, unChanged); } - private static bool AllHaveHashFragment(this IEnumerable bHoMObjects) + private static bool AllHaveRevisionFragment(this IEnumerable bHoMObjects) { // Check if objects have hashfragment. if (bHoMObjects == null || bHoMObjects.Count() == 0 - || bHoMObjects.Select(o => o.GetHashFragment()).Where(o => o != null).Count() < bHoMObjects.Count()) + || bHoMObjects.Select(o => o.RevisionFragment()).Where(o => o != null).Count() < bHoMObjects.Count()) return false; return true; @@ -188,7 +189,7 @@ private static List WithNonNullPersistentAdapterId(this IEnumerable foreach (var obj in objects) { IBHoMObject ibhomobject = obj as IBHoMObject; - if (ibhomobject != null && ibhomobject.FindFragment().PersistentId != null) + if (ibhomobject != null && ibhomobject.FindFragment()?.PersistentId != null) output.Add(ibhomobject); else reminder.Add(obj); diff --git a/Diffing_Engine/Compute/Hash/DiffingHash.cs b/Diffing_Engine/Compute/Hash/DiffingHash.cs index d1ca6715a..84ced0750 100644 --- a/Diffing_Engine/Compute/Hash/DiffingHash.cs +++ b/Diffing_Engine/Compute/Hash/DiffingHash.cs @@ -43,32 +43,32 @@ public static partial class Compute ///**** Public Methods ****/ ///***************************************************/ - [Description("Computes the hash code required for the Diffing.")] + [Description("Computes the hash of the BHoMObject, disregarding any HashFragment already attached to it, if present.")] [Input("obj", "Objects the hash code should be calculated for")] [Input("diffConfig", "Sets configs for the hash calculation, such as properties to be ignored.")] - public static string DiffingHash(this object obj, DiffConfig diffConfig = null) + public static string CurrentHash(this object obj, DiffConfig diffConfig = null) { diffConfig = diffConfig == null ? new DiffConfig() : (DiffConfig)diffConfig.DeepClone(); // The following is to consider only the PropertiesToInclude specified in the diffConfig. - // Since the SHA hash algorithm can only consider "exceptions", we need to retrieve all the top level properties, + // Since the hash computation can only consider "exceptions", we need to retrieve all the top level properties, // intersect them with the set of PropertiesToInclude, and treat all the properties that remain out as "exceptions" (not to be considered). - if (diffConfig.PropertiesToConsider.Any()) + if (diffConfig.HashConfig.PropertiesToConsider?.Any() ?? false) { - IEnumerable exceptions = BH.Engine.Reflection.Query.PropertyNames(obj).Except(diffConfig.PropertiesToConsider); - diffConfig.PropertiesToIgnore.AddRange(exceptions); + IEnumerable exceptions = BH.Engine.Reflection.Query.PropertyNames(obj).Except(diffConfig.HashConfig.PropertiesToConsider); + diffConfig.HashConfig.PropertyNameExceptions.AddRange(exceptions); } - // The current Hash must not be considered when computing the hash. Remove HashFragment if present. + // Any HashFragment present on the object must not be considered. Remove if present. IBHoMObject bhomobj = obj as IBHoMObject; if (bhomobj != null) { bhomobj = BH.Engine.Base.Query.DeepClone(obj) as IBHoMObject; - bhomobj.Fragments.Remove(typeof(HashFragment)); - return Base.Query.Hash(bhomobj, diffConfig.PropertiesToIgnore); + List hashFragments = bhomobj.GetAllFragments(typeof(IHashFragment)); + hashFragments.ForEach(f => bhomobj.Fragments.Remove(f.GetType())); } - return Base.Query.Hash(bhomobj, diffConfig.PropertiesToIgnore); // Compute.SHA256Hash(obj, diffConfig.PropertiesToIgnore); + return Base.Query.Hash(bhomobj, diffConfig.HashConfig); // Compute.SHA256Hash(obj, diffConfig.PropertiesToIgnore); } } } diff --git a/Diffing_Engine/Compute/_IDiffing.cs b/Diffing_Engine/Compute/_IDiffing.cs index dbd239c63..75935038b 100644 --- a/Diffing_Engine/Compute/_IDiffing.cs +++ b/Diffing_Engine/Compute/_IDiffing.cs @@ -54,7 +54,8 @@ public static Diff Diffing(IEnumerable pastObjs, IEnumerable fol // Set configurations if diffConfig is null. Clone it for immutability in the UI. DiffConfig diffConfigCopy = diffConfig == null ? new DiffConfig() : (DiffConfig)diffConfig.DeepClone(); - if (pastObjs.Count() == 1 && followingObjs.Count() == 1) + // Check if the inputs specified are Revisions. In that case, use the Diffing-Revision workflow. + if (string.IsNullOrWhiteSpace(customDataIdKey) && pastObjs.Count() == 1 && followingObjs.Count() == 1) { Revision pastRev = pastObjs.First() as Revision; Revision follRev = followingObjs.First() as Revision; @@ -73,6 +74,7 @@ public static Diff Diffing(IEnumerable pastObjs, IEnumerable fol IEnumerable bHoMObjects_past = pastObjs.OfType(); IEnumerable bHoMObjects_following = followingObjs.OfType(); + // If a customDataKey was specified, use the Id found under that key in customdata to perform the Diffing. if (!string.IsNullOrWhiteSpace(customDataIdKey)) { if (bHoMObjects_past.Count() == pastObjs.Count() && bHoMObjects_following.Count() == followingObjs.Count()) @@ -84,26 +86,15 @@ public static Diff Diffing(IEnumerable pastObjs, IEnumerable fol BH.Engine.Reflection.Compute.RecordWarning($"To perform the diffing based on an Id stored in the Custom Data, the inputs must be collections of IBHoMObjects."); } - // Check if the BHoMObjects all have a hashfragment assigned. + // Check if the BHoMObjects all have a RevisionFragment assigned. // If so, we may attempt the Revision diffing. - if (bHoMObjects_past.AllHaveHashFragment() && bHoMObjects_following.AllHaveHashFragment()) + if (bHoMObjects_past.AllHaveRevisionFragment() && bHoMObjects_following.AllHaveRevisionFragment()) { BH.Engine.Reflection.Compute.RecordNote($"Calling the diffing method '{nameof(DiffRevisionObjects)}'."); return DiffRevisionObjects(bHoMObjects_past, bHoMObjects_following, diffConfigCopy); } - // If `AllowOneByOneDiffing` is enabled and the collections have the same length, - // compare objects from the two collections one by one. - if (diffConfigCopy.AllowOneByOneDiffing && pastObjs.Count() == followingObjs.Count()) - { - BH.Engine.Reflection.Compute.RecordNote($"Calling the diffing method '{nameof(DiffOneByOne)}'" + - $"\nThis will only identify 'modified' or 'unchanged' objects. It will work correctly only if the input objects are in the same order."); - - return DiffOneByOne(pastObjs, followingObjs, diffConfigCopy); - } - - // Check if the BHoMObjects have a `persistentId` assigned. - // If so, we may attempt the DiffWithFragmentId diffing. + // Check if the bhomObjects have a persistentId assigned. List reminder_past, reminder_following; List bHoMObjects_past_persistId = bHoMObjects_past.WithNonNullPersistentAdapterId(out reminder_past); List bHoMObjects_following_persistId = bHoMObjects_following.WithNonNullPersistentAdapterId(out reminder_following); @@ -111,9 +102,20 @@ public static Diff Diffing(IEnumerable pastObjs, IEnumerable fol if (bHoMObjects_past_persistId.Count != 0 && bHoMObjects_following_persistId.Count != 0) { + // If the BHoMObjects have a `persistentId` assigned, attempt the DiffWithFragmentId diffing. BH.Engine.Reflection.Compute.RecordNote($"Calling the diffing method '{nameof(DiffWithFragmentId)}'."); fragmentDiff = DiffWithFragmentId(bHoMObjects_past_persistId, bHoMObjects_following_persistId, typeof(IPersistentAdapterId), nameof(IPersistentAdapterId.PersistentId), diffConfigCopy); } + else if (diffConfigCopy.AllowOneByOneDiffing && pastObjs.Count() == followingObjs.Count()) + { + // If objects do not have any persistentId, `AllowOneByOneDiffing` is enabled and the collections have the same length, + // compare objects from the two collections one by one. + + BH.Engine.Reflection.Compute.RecordNote($"Calling the diffing method '{nameof(DiffOneByOne)}'" + + $"\nThis will only identify 'modified' or 'unchanged' objects. It will work correctly only if the input objects are in the same order."); + + return DiffOneByOne(pastObjs, followingObjs, diffConfigCopy); + } // As last resort, compute the hash of each object and compare the objects with the same hash. // Remember we can use DiffConfig.PropertiesToConsider in order to tell Diffing to tell the difference only based on certain properties. @@ -128,7 +130,7 @@ public static Diff Diffing(IEnumerable pastObjs, IEnumerable fol if (fragmentDiff == null) return diffGeneric; - return fragmentDiff.AddToDiff(diffGeneric); + return fragmentDiff.CombineDiffs(diffGeneric); } } } diff --git a/Diffing_Engine/Create/DiffConfig.cs b/Diffing_Engine/Create/DiffConfig.cs index ecf823278..da5c0132d 100644 --- a/Diffing_Engine/Create/DiffConfig.cs +++ b/Diffing_Engine/Create/DiffConfig.cs @@ -59,8 +59,11 @@ public static DiffConfig DiffConfig(bool enablePropertyDiffing = false, bool sto { EnablePropertyDiffing = enablePropertyDiffing, StoreUnchangedObjects = storeUnchangedObjects, - PropertiesToConsider = propertiesToConsider ?? new List(), - PropertiesToIgnore = (propertiesToIgnore == null || !propertiesToIgnore.Any()) ? new List() { "BHoM_Guid" } : propertiesToIgnore, + HashConfig = new HashConfig() + { + PropertiesToConsider = propertiesToConsider, + PropertyNameExceptions = propertiesToIgnore, + }, CustomDataToIgnore = (customDataToIgnore == null || !customDataToIgnore.Any()) ? new List() { "RenderMesh" }: customDataToIgnore }; } diff --git a/Diffing_Engine/Diffing_Engine.csproj b/Diffing_Engine/Diffing_Engine.csproj index cd2413685..fe854651a 100644 --- a/Diffing_Engine/Diffing_Engine.csproj +++ b/Diffing_Engine/Diffing_Engine.csproj @@ -72,8 +72,10 @@ - - + + + + @@ -81,12 +83,13 @@ - + - + + diff --git a/Diffing_Engine/Modify/AddToDiff.cs b/Diffing_Engine/Modify/CombineDiffs.cs similarity index 85% rename from Diffing_Engine/Modify/AddToDiff.cs rename to Diffing_Engine/Modify/CombineDiffs.cs index af1ccf349..80863b59c 100644 --- a/Diffing_Engine/Modify/AddToDiff.cs +++ b/Diffing_Engine/Modify/CombineDiffs.cs @@ -39,11 +39,16 @@ namespace BH.Engine.Diffing { public static partial class Modify { - [Description("Clones the IBHoMObjects, computes their hash and stores it in a HashFragment. " + - "If the object already has a HashFragment, it computes the current one and keeps the old one in the `previousHash` of the HashFragment.")] - public static Diff AddToDiff(this Diff diff, Diff toAdd) + [Description("Combines two given diffs into one, appending objects of the second to the first.")] + public static Diff CombineDiffs(this Diff diff, Diff toAdd) { - Diff newDiff = new Diff( + if (diff == null) + return toAdd; + + if (toAdd == null) + return diff; + + return new Diff( diff.AddedObjects.Concat(toAdd.AddedObjects), diff.RemovedObjects.Concat(toAdd.RemovedObjects), diff.ModifiedObjects.Concat(toAdd.ModifiedObjects), @@ -51,8 +56,6 @@ public static Diff AddToDiff(this Diff diff, Diff toAdd) diff.ModifiedPropsPerObject.Concat(toAdd.ModifiedPropsPerObject).ToDictionary(x => x.Key, x => x.Value), diff.UnchangedObjects.Concat(toAdd.UnchangedObjects) ); - - return newDiff; } } } diff --git a/Diffing_Engine/Modify/PrepareForRevision.cs b/Diffing_Engine/Modify/PrepareForRevision.cs index 9b8ad4e00..c1bedff35 100644 --- a/Diffing_Engine/Modify/PrepareForRevision.cs +++ b/Diffing_Engine/Modify/PrepareForRevision.cs @@ -41,7 +41,7 @@ public static partial class Modify public static IEnumerable PrepareForRevision(this IEnumerable objects, DiffConfig diffConfig = null) where T : IBHoMObject { // Clone the current objects to preserve immutability; calculate and set the hash fragment - IEnumerable objs_cloned = Modify.SetHashFragment(objects, diffConfig); + IEnumerable objs_cloned = Modify.SetRevisionFragment(objects, diffConfig); // Remove duplicates by hash objs_cloned = Modify.RemoveDuplicatesByHash(objs_cloned); diff --git a/Diffing_Engine/Modify/RemoveDuplicatesByHash.cs b/Diffing_Engine/Modify/RemoveDuplicatesByHash.cs index 39a3dd24e..6fbfaf53b 100644 --- a/Diffing_Engine/Modify/RemoveDuplicatesByHash.cs +++ b/Diffing_Engine/Modify/RemoveDuplicatesByHash.cs @@ -42,7 +42,7 @@ public static partial class Modify [Input("objects", "Collection of objects whose duplicates have to be removed. If they don't already have an Hash assigned, it will be calculated.")] public static IEnumerable RemoveDuplicatesByHash(IEnumerable objects) where T : IBHoMObject { - return objects.GroupBy(obj => obj.GetHashFragment().CurrentHash).Select(gr => gr.First()).ToList(); + return objects.GroupBy(obj => obj.RevisionFragment().CurrentHash).Select(gr => gr.First()).ToList(); } } } diff --git a/Diffing_Engine/Modify/SetHashFragment.cs b/Diffing_Engine/Modify/SetHashFragment.cs index 7aba9f891..cf8ef19e2 100644 --- a/Diffing_Engine/Modify/SetHashFragment.cs +++ b/Diffing_Engine/Modify/SetHashFragment.cs @@ -39,8 +39,7 @@ namespace BH.Engine.Diffing { public static partial class Modify { - [Description("Clones the IBHoMObjects, computes their hash and stores it in a HashFragment. " + - "If the object already has a HashFragment, it computes the current one and keeps the old one in the `previousHash` of the HashFragment.")] + [Description("Clones the IBHoMObjects, computes their hash and stores it in a HashFragment.")] public static List SetHashFragment(this IEnumerable objs, DiffConfig diffConfig = null) where T : IBHoMObject { // Clone the current objects to preserve immutability @@ -51,15 +50,12 @@ public static List SetHashFragment(this IEnumerable objs, DiffConfig di // Calculate and set the object hashes foreach (var obj in objs) - { objs_cloned.Add(SetHashFragment(obj, diffConfig)); - } return objs_cloned; } - [Description("Clones the IBHoMObject, computes their hash and stores it in a HashFragment. " + - "If the object already has a HashFragment, it computes the current one and keeps the old one in the `previousHash` of the HashFragment.")] + [Description("Clones the IBHoMObject, computes their hash and stores it in a HashFragment.")] public static T SetHashFragment(T obj, DiffConfig diffConfig = null) where T : IBHoMObject { // Clone the current object to preserve immutability @@ -69,25 +65,19 @@ public static T SetHashFragment(T obj, DiffConfig diffConfig = null) where T diffConfig = diffConfig == null ? new DiffConfig() : diffConfig; // Calculate and set the object hashes - string hash = BH.Engine.Diffing.Compute.DiffingHash(obj_cloned, diffConfig); - - HashFragment existingFragm = obj_cloned.GetHashFragment(); - - obj_cloned.Fragments.AddOrReplace(new HashFragment(hash, existingFragm?.CurrentHash)); + string hash = BH.Engine.Diffing.Compute.CurrentHash(obj_cloned, diffConfig); + obj_cloned.Fragments.AddOrReplace(new HashFragment() { CurrentHash = hash }); return obj_cloned; } - [Description("Clones the IBHoMObject, computes their hash and stores it in a HashFragment. " + - "If the object already has a HashFragment, it computes the current one and keeps the old one in the `previousHash` of the HashFragment.")] + [Description("Clones the IBHoMObject, computes their hash and stores it in a HashFragment.")] public static T SetHashFragment(T obj, string hash) where T : IBHoMObject { // Clone the current object to preserve immutability T obj_cloned = BH.Engine.Base.Query.DeepClone(obj); - HashFragment existingFragm = obj_cloned.GetHashFragment(); - - obj_cloned.Fragments.AddOrReplace(new HashFragment(hash, existingFragm?.CurrentHash)); + obj_cloned.Fragments.AddOrReplace(new HashFragment() { CurrentHash = hash }); return obj_cloned; } diff --git a/Diffing_Engine/Modify/SetRevisionFragment.cs b/Diffing_Engine/Modify/SetRevisionFragment.cs new file mode 100644 index 000000000..9d96a4056 --- /dev/null +++ b/Diffing_Engine/Modify/SetRevisionFragment.cs @@ -0,0 +1,96 @@ +/* + * This file is part of the Buildings and Habitats object Model (BHoM) + * Copyright (c) 2015 - 2020, the respective contributors. All rights reserved. + * + * Each contributor holds copyright over their respective contributions. + * The project versioning (Git) records all such contribution source information. + * + * + * The BHoM is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3.0 of the License, or + * (at your option) any later version. + * + * The BHoM 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this code. If not, see . + */ + +using BH.oM.Base; +using BH.oM.Data.Collections; +using BH.oM.Diffing; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Security.Cryptography; +using System.Reflection; +using BH.Engine.Serialiser; +using BH.oM.Reflection.Attributes; +using System.ComponentModel; +using BH.Engine.Base; + +namespace BH.Engine.Diffing +{ + public static partial class Modify + { + [Description("Clones the IBHoMObjects, computes their hash and stores it in a RevisionFragment. " + + "If the object already has a RevisionFragment, it computes the current one and keeps the old one in the `previousHash` of the RevisionFragment.")] + public static List SetRevisionFragment(this IEnumerable objs, DiffConfig diffConfig = null) where T : IBHoMObject + { + // Clone the current objects to preserve immutability + List objs_cloned = new List(); + + // Set configurations if diffConfig is null + diffConfig = diffConfig == null ? new DiffConfig() : diffConfig; + + // Calculate and set the object hashes + foreach (var obj in objs) + { + objs_cloned.Add(SetRevisionFragment(obj, diffConfig)); + } + + return objs_cloned; + } + + [Description("Clones the IBHoMObject, computes their hash and stores it in a RevisionFragment. " + + "If the object already has a RevisionFragment, it computes the current one and keeps the old one in the `previousHash` of the RevisionFragment.")] + public static T SetRevisionFragment(T obj, DiffConfig diffConfig = null) where T : IBHoMObject + { + // Clone the current object to preserve immutability + T obj_cloned = BH.Engine.Base.Query.DeepClone(obj); + + // Set configurations if diffConfig is null + diffConfig = diffConfig == null ? new DiffConfig() : diffConfig; + + // Calculate and set the object hashes + string hash = BH.Engine.Diffing.Compute.CurrentHash(obj_cloned, diffConfig); + + RevisionFragment existingFragm = obj_cloned.RevisionFragment(); + + obj_cloned.Fragments.AddOrReplace(new RevisionFragment(hash, existingFragm?.CurrentHash)); + + return obj_cloned; + } + + [Description("Clones the IBHoMObject, computes their hash and stores it in a RevisionFragment. " + + "If the object already has a RevisionFragment, it computes the current one and keeps the old one in the `previousHash` of the RevisionFragment.")] + public static T SetRevisionFragment(T obj, string hash) where T : IBHoMObject + { + // Clone the current object to preserve immutability + T obj_cloned = BH.Engine.Base.Query.DeepClone(obj); + + RevisionFragment existingFragm = obj_cloned.RevisionFragment(); + + obj_cloned.Fragments.AddOrReplace(new RevisionFragment(hash, existingFragm?.CurrentHash)); + + return obj_cloned; + } + } +} + diff --git a/Diffing_Engine/Objects/EqualityComparers/DiffingHashComparer.cs b/Diffing_Engine/Objects/EqualityComparers/HashComparer.cs similarity index 75% rename from Diffing_Engine/Objects/EqualityComparers/DiffingHashComparer.cs rename to Diffing_Engine/Objects/EqualityComparers/HashComparer.cs index 422e5517c..be11b336e 100644 --- a/Diffing_Engine/Objects/EqualityComparers/DiffingHashComparer.cs +++ b/Diffing_Engine/Objects/EqualityComparers/HashComparer.cs @@ -36,7 +36,7 @@ namespace BH.Engine.Diffing { - public class DiffingHashComparer : IEqualityComparer //where T : IBHoMObject + public class HashComparer : IEqualityComparer //where T : IBHoMObject { /***************************************************/ /**** Constructors ****/ @@ -45,13 +45,13 @@ public class DiffingHashComparer : IEqualityComparer //where T : IBHoMObje public bool StoreHash { get; set; } = false; public DiffConfig DiffConfig { get; set; } = new DiffConfig(); - public DiffingHashComparer(DiffConfig diffConfig = null) + public HashComparer(DiffConfig diffConfig = null) { if (diffConfig != null) DiffConfig = diffConfig; } - public DiffingHashComparer(DiffConfig diffConfig = null, bool storeHash = false) : this(diffConfig) + public HashComparer(DiffConfig diffConfig = null, bool storeHash = false) : this(diffConfig) { StoreHash = storeHash; } @@ -72,30 +72,30 @@ public bool Equals(T x, T y) if (xbHoM != null && ybHoM != null) { - xHash = xbHoM?.GetHashFragment()?.CurrentHash; - yHash = ybHoM?.GetHashFragment()?.CurrentHash; + xHash = xbHoM?.HashFragment()?.CurrentHash; + yHash = ybHoM?.HashFragment()?.CurrentHash; if (string.IsNullOrWhiteSpace(xHash)) { - xHash = x.DiffingHash(DiffConfig); + xHash = x.CurrentHash(DiffConfig); if (StoreHash) - SetHashFragment(xbHoM, xHash); + Modify.SetHashFragment(xbHoM, xHash); } if (string.IsNullOrWhiteSpace(yHash)) { - yHash = y.DiffingHash(DiffConfig); + yHash = y.CurrentHash(DiffConfig); if (StoreHash) - SetHashFragment(ybHoM, yHash); + Modify.SetHashFragment(ybHoM, yHash); } return xHash == yHash; } - return x.DiffingHash(DiffConfig) == y.DiffingHash(DiffConfig); + return x.CurrentHash(DiffConfig) == y.CurrentHash(DiffConfig); } return false; @@ -108,22 +108,12 @@ public int GetHashCode(T obj) if (typeof(IBHoMObject).IsAssignableFrom(typeof(T))) { IBHoMObject bHoMObject = (IBHoMObject)obj; - HashFragment hashFragment = bHoMObject.GetHashFragment(); + HashFragment hashFragment = bHoMObject.HashFragment(); if (!string.IsNullOrWhiteSpace(hashFragment?.CurrentHash)) return hashFragment.CurrentHash.GetHashCode(); } - return obj.DiffingHash(DiffConfig).GetHashCode(); - } - - /***************************************************/ - - // Modify in-place. - private static bool SetHashFragment(IBHoMObject obj, string hash) - { - HashFragment existingFragm = obj.GetHashFragment(); - - return obj.Fragments.AddOrReplace(new HashFragment(hash, existingFragm?.CurrentHash)); + return obj.CurrentHash(DiffConfig).GetHashCode(); } } } diff --git a/Diffing_Engine/Objects/EqualityComparers/RevisionHashComparer.cs b/Diffing_Engine/Objects/EqualityComparers/RevisionHashComparer.cs new file mode 100644 index 000000000..ca9fa7b64 --- /dev/null +++ b/Diffing_Engine/Objects/EqualityComparers/RevisionHashComparer.cs @@ -0,0 +1,120 @@ +/* + * This file is part of the Buildings and Habitats object Model (BHoM) + * Copyright (c) 2015 - 2020, the respective contributors. All rights reserved. + * + * Each contributor holds copyright over their respective contributions. + * The project versioning (Git) records all such contribution source information. + * + * + * The BHoM is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3.0 of the License, or + * (at your option) any later version. + * + * The BHoM 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this code. If not, see . + */ + +using BH.oM.Base; +using BH.Engine; +using BH.oM.Data.Collections; +using BH.oM.Diffing; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using BH.Engine.Serialiser; +using System.ComponentModel; +using BH.oM.Reflection.Attributes; +using BH.oM.Reflection; +using BH.Engine.Diffing; + +namespace BH.Engine.Diffing +{ + public class RevisionHashComparer : IEqualityComparer //where T : IBHoMObject + { + /***************************************************/ + /**** Constructors ****/ + /***************************************************/ + + public bool StoreHash { get; set; } = false; + public DiffConfig DiffConfig { get; set; } = new DiffConfig(); + + public RevisionHashComparer(DiffConfig diffConfig = null) + { + if (diffConfig != null) + DiffConfig = diffConfig; + } + + public RevisionHashComparer(DiffConfig diffConfig = null, bool storeHash = false) : this(diffConfig) + { + StoreHash = storeHash; + } + + /***************************************************/ + /**** Public Methods ****/ + /***************************************************/ + + public bool Equals(T x, T y) + { + if (x?.GetType() == y?.GetType()) + { + string xHash = null; + string yHash = null; + + IBHoMObject xbHoM = x as IBHoMObject; + IBHoMObject ybHoM = y as IBHoMObject; + + if (xbHoM != null && ybHoM != null) + { + xHash = xbHoM?.RevisionFragment()?.CurrentHash; + yHash = ybHoM?.RevisionFragment()?.CurrentHash; + + if (string.IsNullOrWhiteSpace(xHash)) + { + xHash = x.CurrentHash(DiffConfig); + + if (StoreHash) + xbHoM = Modify.SetRevisionFragment(xbHoM, xHash); + + } + + if (string.IsNullOrWhiteSpace(yHash)) + { + yHash = y.CurrentHash(DiffConfig); + + if (StoreHash) + ybHoM = Modify.SetRevisionFragment(ybHoM, yHash); + } + + return xHash == yHash; + } + + return x.CurrentHash(DiffConfig) == y.CurrentHash(DiffConfig); + } + + return false; + } + + /***************************************************/ + + public int GetHashCode(T obj) + { + if (typeof(IBHoMObject).IsAssignableFrom(typeof(T))) + { + IBHoMObject bHoMObject = (IBHoMObject)obj; + RevisionFragment hashFragment = bHoMObject.RevisionFragment(); + if (!string.IsNullOrWhiteSpace(hashFragment?.CurrentHash)) + return hashFragment.CurrentHash.GetHashCode(); + } + + return obj.CurrentHash(DiffConfig).GetHashCode(); + } + } +} + diff --git a/Diffing_Engine/Query/DifferentProperties.cs b/Diffing_Engine/Query/DifferentProperties.cs index 7e08411f9..807fbf47b 100644 --- a/Diffing_Engine/Query/DifferentProperties.cs +++ b/Diffing_Engine/Query/DifferentProperties.cs @@ -54,15 +54,15 @@ public static Dictionary> DifferentProperties(this // General configurations. comparer.Config.MaxDifferences = diffConfigCopy.MaxPropertyDifferences; - comparer.Config.DoublePrecision = diffConfigCopy.NumericTolerance; + comparer.Config.DoublePrecision = diffConfigCopy.HashConfig.NumericTolerance; // Set the properties to be ignored. - if (!diffConfigCopy.PropertiesToIgnore.Contains("BHoM_Guid")) - diffConfigCopy.PropertiesToIgnore.Add("BHoM_Guid"); + if (!diffConfigCopy.HashConfig.PropertyNameExceptions?.Contains("BHoM_Guid") ?? true) + diffConfigCopy.HashConfig.PropertyNameExceptions.Add("BHoM_Guid"); // the above should be replaced by BH.Engine.Reflection.Compute.RecordWarning($"`BHoM_Guid` should generally be ignored when computing the diffing. Consider adding it to the {nameof(diffConfig.PropertiesToIgnore)}."); // when the bug in the auto Create() method ("auto-property initialisers for ByRef values like lists do not populate default values") is resolved. - comparer.Config.MembersToIgnore = diffConfigCopy.PropertiesToIgnore; + comparer.Config.MembersToIgnore = diffConfigCopy.HashConfig.PropertyNameExceptions; // Removes the CustomData to be ignored. var bhomobj1 = (obj1Copy as IBHoMObject); @@ -82,6 +82,7 @@ public static Dictionary> DifferentProperties(this // Never include the changes in HashFragment. comparer.Config.TypesToIgnore.Add(typeof(HashFragment)); + comparer.Config.TypesToIgnore.Add(typeof(RevisionFragment)); // Perform the comparison. ComparisonResult result = comparer.Compare(obj1Copy, obj2Copy); @@ -107,7 +108,7 @@ public static Dictionary> DifferentProperties(this propertyName = splittedName.FirstOrDefault() + $"['{keyName}']." + splittedName.Last(); } - if (!diffConfigCopy.PropertiesToConsider.Any() || diffConfigCopy.PropertiesToConsider.Contains(difference.PropertyName)) + if (diffConfigCopy.HashConfig.PropertyNameExceptions.Any() && !diffConfigCopy.HashConfig.PropertyNameExceptions.Contains(difference.PropertyName)) dict[propertyName] = new Tuple(difference.Object1, difference.Object2); } diff --git a/Diffing_Engine/Query/GetHashFragment.cs b/Diffing_Engine/Query/HashFragment.cs similarity index 95% rename from Diffing_Engine/Query/GetHashFragment.cs rename to Diffing_Engine/Query/HashFragment.cs index c48c8da29..1239b9cf7 100644 --- a/Diffing_Engine/Query/GetHashFragment.cs +++ b/Diffing_Engine/Query/HashFragment.cs @@ -33,7 +33,7 @@ namespace BH.Engine.Diffing { public static partial class Query { - public static HashFragment GetHashFragment(this IBHoMObject obj) + public static HashFragment HashFragment(this IBHoMObject obj) { return obj.FindFragment(); } diff --git a/Diffing_Engine/Query/RevisionFragment.cs b/Diffing_Engine/Query/RevisionFragment.cs new file mode 100644 index 000000000..db5c33ff3 --- /dev/null +++ b/Diffing_Engine/Query/RevisionFragment.cs @@ -0,0 +1,42 @@ +/* + * This file is part of the Buildings and Habitats object Model (BHoM) + * Copyright (c) 2015 - 2020, the respective contributors. All rights reserved. + * + * Each contributor holds copyright over their respective contributions. + * The project versioning (Git) records all such contribution source information. + * + * + * The BHoM is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3.0 of the License, or + * (at your option) any later version. + * + * The BHoM 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this code. If not, see . + */ + +using BH.oM.Base; +using BH.Engine.Base; +using BH.oM.Diffing; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace BH.Engine.Diffing +{ + public static partial class Query + { + public static RevisionFragment RevisionFragment(this IBHoMObject obj) + { + return obj.FindFragment(); + } + } +} + diff --git a/Environment_Engine/Query/HasMergeablePropertiesWith.cs b/Environment_Engine/Query/HasMergeablePropertiesWith.cs index 8668ad4b8..876090392 100644 --- a/Environment_Engine/Query/HasMergeablePropertiesWith.cs +++ b/Environment_Engine/Query/HasMergeablePropertiesWith.cs @@ -35,6 +35,7 @@ using BH.Engine.Diffing; using BH.oM.Diffing; using BH.oM.Environment.Analysis; +using BH.oM.Base; namespace BH.Engine.Environment { @@ -57,16 +58,19 @@ public static bool HasMergeablePropertiesWith(Panel element, Panel other) { DiffConfig config = new DiffConfig() { - PropertiesToIgnore = new List + HashConfig = new HashConfig() { - "ExternalEdges", - "Openings", - "ConnectedSpaces", - "Type", - "BHoM_Guid", - "CustomData", - }, - NumericTolerance = BH.oM.Geometry.Tolerance.Distance, + PropertyNameExceptions = new List + { + "ExternalEdges", + "Openings", + "ConnectedSpaces", + "Type", + "BHoM_Guid", + "CustomData", + }, + NumericTolerance = BH.oM.Geometry.Tolerance.Distance + } }; return Diffing.Query.DifferentProperties(element, other, config) == null; @@ -80,16 +84,19 @@ public static bool HasMergeablePropertiesWith(Opening element, Opening other) { DiffConfig config = new DiffConfig() { - PropertiesToIgnore = new List + HashConfig = new HashConfig() { - "Edges", - "FrameFactorValue", - "InnerEdges", - "Type", - "BHoM_Guid", - "CustomData", - }, - NumericTolerance = BH.oM.Geometry.Tolerance.Distance, + PropertyNameExceptions = new List + { + "Edges", + "FrameFactorValue", + "InnerEdges", + "Type", + "BHoM_Guid", + "CustomData", + }, + NumericTolerance = BH.oM.Geometry.Tolerance.Distance + } }; return Diffing.Query.DifferentProperties(element, other, config) == null; @@ -112,14 +119,17 @@ public static bool HasMergeablePropertiesWith(Space element, Space other) { DiffConfig config = new DiffConfig() { - PropertiesToIgnore = new List + HashConfig = new HashConfig() { - "Location", - "Type", - "BHoM_Guid", - "CustomData", - }, - NumericTolerance = BH.oM.Geometry.Tolerance.Distance, + PropertyNameExceptions = new List + { + "Location", + "Type", + "BHoM_Guid", + "CustomData", + }, + NumericTolerance = BH.oM.Geometry.Tolerance.Distance, + } }; return Diffing.Query.DifferentProperties(element, other, config) == null; diff --git a/Matter_Engine/Compute/AggregateMaterialComposition.cs b/Matter_Engine/Compute/AggregateMaterialComposition.cs index abebb9acc..8ef363e9f 100644 --- a/Matter_Engine/Compute/AggregateMaterialComposition.cs +++ b/Matter_Engine/Compute/AggregateMaterialComposition.cs @@ -79,7 +79,7 @@ public static MaterialComposition AggregateMaterialComposition(IEnumerable UniqueConstructions(this List con { DiffConfig config = new DiffConfig() { - PropertiesToIgnore = new List + HashConfig = new HashConfig() { - "BHoM_Guid", - "CustomData", - }, - NumericTolerance = BH.oM.Geometry.Tolerance.Distance, + PropertyFullNameExceptions = new List + { + "CustomData" + }, + NumericTolerance = BH.oM.Geometry.Tolerance.Distance + } }; if (!includeConstructionName) - config.PropertiesToIgnore.Add("Name"); + config.HashConfig.PropertyNameExceptions.Add("Name"); List allConstructions = constructions.Where(x => x != null).ToList(); List hashedConstructions = BH.Engine.Diffing.Modify.SetHashFragment(allConstructions, config); From 1e97b39ee3bdc67335293e739e5663a8b3d0d602 Mon Sep 17 00:00:00 2001 From: Alessio Lombardi Date: Fri, 13 Nov 2020 16:12:53 +0000 Subject: [PATCH 05/28] Alignment to latest changes. Simplification. --- Analytical_Engine/Analytical_Engine.csproj | 2 +- Analytical_Engine/Create/Graph/DiffConfig.cs | 12 +- Analytical_Engine/Create/Graph/Graph.cs | 18 +-- .../{DiffEntities.cs => DistinctEntities.cs} | 63 +-------- BHoM_Engine/Query/Hash.cs | 63 ++++++--- Diffing_Engine/Compute/DiffOneByOne.cs | 9 +- Diffing_Engine/Compute/DiffRevisions.cs | 26 ++-- Diffing_Engine/Compute/DiffWithCustomId.cs | 8 +- Diffing_Engine/Compute/DiffWithFragmentId.cs | 6 +- ...{DiffGenericObjects.cs => DiffWithHash.cs} | 20 ++- Diffing_Engine/Compute/Hash/DiffingHash.cs | 75 ----------- Diffing_Engine/Compute/_IDiffing.cs | 98 ++++++++------ Diffing_Engine/Create/Delta.cs | 8 +- Diffing_Engine/Create/DiffConfig.cs | 14 +- Diffing_Engine/Create/Revision.cs | 2 +- Diffing_Engine/Diffing_Engine.csproj | 5 +- Diffing_Engine/Modify/CombineDiffs.cs | 2 +- Diffing_Engine/Modify/PrepareForRevision.cs | 2 +- .../Modify/RemoveDuplicatesByHash.cs | 19 ++- Diffing_Engine/Modify/SetHashFragment.cs | 16 +-- Diffing_Engine/Modify/SetRevisionFragment.cs | 14 +- .../Objects/EqualityComparers/HashComparer.cs | 66 +++++----- .../EqualityComparers/RevisionHashComparer.cs | 120 ------------------ Diffing_Engine/Query/AnyDuplicateByHash.cs | 49 ------- Diffing_Engine/Query/DifferentProperties.cs | 20 +-- .../Query/HasMergeablePropertiesWith.cs | 12 +- .../Compute/AggregateMaterialComposition.cs | 2 +- .../Query/HasMergeablePropertiesWith.cs | 4 +- Physical_Engine/Query/UniqueConstructions.cs | 14 +- 29 files changed, 255 insertions(+), 514 deletions(-) rename Analytical_Engine/Query/Graph/{DiffEntities.cs => DistinctEntities.cs} (58%) rename Diffing_Engine/Compute/{DiffGenericObjects.cs => DiffWithHash.cs} (78%) delete mode 100644 Diffing_Engine/Compute/Hash/DiffingHash.cs delete mode 100644 Diffing_Engine/Objects/EqualityComparers/RevisionHashComparer.cs delete mode 100644 Diffing_Engine/Query/AnyDuplicateByHash.cs diff --git a/Analytical_Engine/Analytical_Engine.csproj b/Analytical_Engine/Analytical_Engine.csproj index ebc17ab8b..073de2f86 100644 --- a/Analytical_Engine/Analytical_Engine.csproj +++ b/Analytical_Engine/Analytical_Engine.csproj @@ -95,12 +95,12 @@ + - diff --git a/Analytical_Engine/Create/Graph/DiffConfig.cs b/Analytical_Engine/Create/Graph/DiffConfig.cs index 7d3992812..a5f8bf3d3 100644 --- a/Analytical_Engine/Create/Graph/DiffConfig.cs +++ b/Analytical_Engine/Create/Graph/DiffConfig.cs @@ -1,4 +1,5 @@ -using BH.oM.Diffing; +using BH.oM.Base; +using BH.oM.Diffing; using BH.oM.Reflection.Attributes; using System; using System.Collections.Generic; @@ -15,23 +16,22 @@ public static partial class Create /**** Public Methods ****/ /***************************************************/ - [Description("Create a simple DiffConfig.")] + [Description("Create a simple DistinctConfig.")] [Input("numericTolerance", "Tolerance used to determine numerical differences." + "\nDefaults to Tolerance.Distance (1e-6).")] [Input("propertiesToConsider", "By default, diffing considers all the properties of the objects." + "\nHere you can specify a list of property names. Only the properties with a name matching any of this list will be considered for diffing." + "\nE.g., if you input 'Name' only the differences in terms of name will be returned." + "\nNOTE: these can be only top-level properties of the object (not the sub-properties).")] - [Output("diffConfig", "DiffConfig.")] - public static DiffConfig DiffConfig(double numericTolerance = oM.Geometry.Tolerance.Distance, List propertiesToConsider = null) + public static DistinctConfig DistinctConfig(double numericTolerance = oM.Geometry.Tolerance.Distance, List propertiesToConsider = null) { - DiffConfig diffConfig = new DiffConfig() + DistinctConfig distinctConfig = new DistinctConfig() { NumericTolerance = numericTolerance, PropertiesToConsider = propertiesToConsider ?? new List(), }; - return diffConfig; + return distinctConfig; } } } diff --git a/Analytical_Engine/Create/Graph/Graph.cs b/Analytical_Engine/Create/Graph/Graph.cs index 3a89da765..fa5faa5ce 100644 --- a/Analytical_Engine/Create/Graph/Graph.cs +++ b/Analytical_Engine/Create/Graph/Graph.cs @@ -47,22 +47,22 @@ public static partial class Create [Description("Create a graph from a collection of IBHoMObjects, property names and decimal places to determine unique graph entities.")] [Input("entities", "A collection of IBHoMOBjects to use as Graph entities. Entities should include DependencyFragments to determine the Graph Relations.")] - [Input("diffConfig", "Configuration of diffing used when attempting to find unique entities.")] + [Input("distinctConfig", "Settings to determine the uniqueness of entities.")] [Output("graph", "Graph.")] - public static Graph Graph(List entities, DiffConfig diffConfig = null) + public static Graph Graph(List entities, DistinctConfig distinctConfig = null) { - return Graph(entities, new List(), diffConfig); + return Graph(entities, new List(), distinctConfig); } /***************************************************/ [Description("Create a graph from a collection of IRelations, property names and decimal places to determine unique graph entities.")] [Input("relations", "A collection of IRelations to use as Graph Relations. Relations should include sub Graphs containing the entities to be used in the Graph.")] - [Input("diffConfig", "Configuration of diffing used when attempting to find unique entities.")] + [Input("distinctConfig", "Settings to determine the uniqueness of entities.")] [Output("graph", "Graph.")] - public static Graph Graph(List relations, DiffConfig diffConfig = null) + public static Graph Graph(List relations, DistinctConfig distinctConfig = null) { - return Graph(new List(), relations, diffConfig); + return Graph(new List(), relations, distinctConfig); } /***************************************************/ @@ -70,9 +70,9 @@ public static Graph Graph(List relations, DiffConfig diffConfig = nul [Description("Create a graph from a collection of IBHoMObjects, a collection of IRelations, property names and decimal places to determine unique graph entities.")] [Input("entities", "Optional collection of IBHoMOBjects to use as Graph entities. Entities can include DependencyFragments to determine the Graph Relations.")] [Input("relations", "Optional collection of IRelations to use as Graph Relations. Relations can include sub Graphs containing the entities to be used in the Graph.")] - [Input("diffConfig", "Configuration of diffing used when attempting to find unique entities.")] + [Input("distinctConfig", "Settings to determine the uniqueness of entities.")] [Output("graph", "Graph.")] - public static Graph Graph(List entities = null, List relations = null, DiffConfig diffConfig = null) + public static Graph Graph(List entities = null, List relations = null, DistinctConfig distinctConfig = null) { Graph graph = new Graph(); @@ -88,7 +88,7 @@ public static Graph Graph(List entities = null, List rel return graph; } - m_MatchedObjects = Query.DiffEntities(clonedEntities, diffConfig); + m_MatchedObjects = Query.DistinctEntities(clonedEntities, distinctConfig); //convert dependency fragments attached to entities and add to relations clonedEntities.ForEach(ent => clonedRelations.AddRange(ent.ToRelation())); diff --git a/Analytical_Engine/Query/Graph/DiffEntities.cs b/Analytical_Engine/Query/Graph/DistinctEntities.cs similarity index 58% rename from Analytical_Engine/Query/Graph/DiffEntities.cs rename to Analytical_Engine/Query/Graph/DistinctEntities.cs index d1c4d9ffb..0fe7ae940 100644 --- a/Analytical_Engine/Query/Graph/DiffEntities.cs +++ b/Analytical_Engine/Query/Graph/DistinctEntities.cs @@ -43,36 +43,27 @@ public static partial class Query [Description("Identifies unique objects from a collection IBHoMObjects using hash comparison.")] [Input("entities", "A collection of IBHoMObjects from which unique instances are identified.")] - [Input("diffConfig", "Configuration of diffing used to find unique entities.")] + [Input("distinctConfig", "Configuration of diffing used to find unique entities.")] [Output("unique entities", "A Dictionary replacement map of the entities where the keys are the Guid of the original entity and the Values the matching IBHoMObject entity.")] - public static Dictionary DiffEntities(this List entities, DiffConfig diffConfig = null) + public static Dictionary DistinctEntities(this List entities, DistinctConfig distinctConfig = null) // this is not a diff, it's finding unique entities { - if (diffConfig == null) - diffConfig = new DiffConfig(); + DistinctConfig dc = distinctConfig ?? new DistinctConfig(); Dictionary replaceMap = new Dictionary(); Dictionary objectHash = new Dictionary(); - //remove old hashes if any - RemoveExistingHashes(entities); - - //find decimal places from numeric tolerance - int decimalPlaces = DecimalPlaces(diffConfig.NumericTolerance); - - //generate hashes - entities.ForEach(ent => objectHash.Add(ent, ent.HashEntity(diffConfig, decimalPlaces))); + HashComparer hashComparer = new HashComparer(dc); foreach (KeyValuePair entityA in objectHash) { foreach (KeyValuePair entityB in objectHash) { //only if same object type - if(entityA.Key.GetType() == entityB.Key.GetType()) + if (entityA.Key.GetType() == entityB.Key.GetType()) { //compare hashes - if (entityA.Value == entityB.Value) + if (hashComparer.Equals(entityA.Value, entityB.Value)) { - //store in map dictionary where key is original Guid and Value is replacement object replaceMap[entityA.Key.BHoM_Guid] = entityB.Key; @@ -84,47 +75,5 @@ public static Dictionary DiffEntities(this List } return replaceMap; } - - /***************************************************/ - /**** Private Methods ****/ - /***************************************************/ - - private static string HashEntity(this IBHoMObject entity, DiffConfig diffConfig, int decimalPlaces) - { - if (diffConfig == null) - diffConfig = new DiffConfig(); - - List propertiesToIgnore = BH.Engine.Reflection.Query.PropertyNames(entity).Except(diffConfig.PropertiesToConsider).ToList(); - - return Base.Query.Hash(entity, propertyNameExceptions: propertiesToIgnore, fractionalDigits: decimalPlaces); - } - - /***************************************************/ - - private static int DecimalPlaces(double numericTolerance) - { - //horrible conversion from numeric tolerance to precision needed for Hashing - int precision = 0; - - while ((decimal)numericTolerance * (decimal)Math.Pow(10, precision) != - Math.Round((decimal)numericTolerance * (decimal)Math.Pow(10, precision))) - precision++; - - return precision; - } - - /***************************************************/ - - private static void RemoveExistingHashes(List entities) - { - foreach(IBHoMObject entity in entities) - { - if (entity.Fragments.Contains(typeof(HashFragment))) - { - entity.Fragments.Remove(typeof(HashFragment)); - } - } - } } - } diff --git a/BHoM_Engine/Query/Hash.cs b/BHoM_Engine/Query/Hash.cs index 0d5aa3a97..dab6e6838 100644 --- a/BHoM_Engine/Query/Hash.cs +++ b/BHoM_Engine/Query/Hash.cs @@ -46,23 +46,44 @@ public static partial class Query [Description("Computes a Hash code for the iObject. The hash uniquely represents an object's state based on its combined properties and their values.")] [Input("iObj", "iObject the hash code should be calculated for.")] - public static string Hash(this IObject iObj, HashConfig hashConfig = null) + public static string Hash(this IObject iObj, DistinctConfig distinctConfig = null) { // Make sure to clone for immutability, and always have a HashConfig. - HashConfig hc = hashConfig == null ? new HashConfig() : hashConfig.DeepClone(); + DistinctConfig hc = distinctConfig == null ? new DistinctConfig() : distinctConfig.DeepClone(); // Make sure that "BHoM_Guid" is added to the propertyNameExceptions of the HashConfig. hc.PropertyNameExceptions = hc.PropertyNameExceptions ?? new List(); if (!hc.PropertyNameExceptions.Contains(nameof(BHoMObject.BHoM_Guid))) hc.PropertyNameExceptions.Add(nameof(BHoMObject.BHoM_Guid)); + // Convert from the Numeric Tolerance to fractionalDigits (required for the hash). int fractionalDigits = Math.Abs(Convert.ToInt32(Math.Log10(hc.NumericTolerance))); + // Process the "PropertiesToInclude" property. + if (hc.PropertiesToConsider?.Any() ?? false) + { + // The hash computation can only consider "exceptions". + // We need to retrieve all the object properties, intersect them with PropertiesToInclude, and treat all those remaining as "exceptions". + IEnumerable exceptions = BH.Engine.Reflection.Query.PropertyNames(iObj).Except(hc.PropertiesToConsider); + hc.PropertyNameExceptions.AddRange(exceptions); + } + + // Any HashFragment present on the object must not be considered when computing the Hash. Remove if present. + IBHoMObject bhomobj = iObj as IBHoMObject; + if (bhomobj != null) + { + bhomobj = BH.Engine.Base.Query.DeepClone(iObj) as IBHoMObject; + List hashFragments = bhomobj.GetAllFragments(typeof(IHashFragment)).OfType().ToList(); + hashFragments.ForEach(f => bhomobj.Fragments.Remove(f.GetType())); + } + + // Compute the defining string. string hashString = DefiningString(iObj, hc, fractionalDigits, 0); if (string.IsNullOrWhiteSpace(hashString)) throw new Exception("Error computing the defining string of the object."); + // Return the SHA256 hash of the defining string. return SHA256Hash(hashString); } @@ -93,7 +114,7 @@ private static string SHA256Hash(string str) [Input("hc", "HashConfig, options for the hash calculation.")] [Input("nestingLevel", "Nesting level of the property.")] [Input("propertyPath", "(Optional) Indicates the 'property path' of the current object, e.g. `BH.oM.Structure.Elements.Bar.StartNode.Point.X`")] - private static string DefiningString(object obj, HashConfig hc, int fractionalDigits, int nestingLevel, string propertyPath = null) + private static string DefiningString(object obj, DistinctConfig dc, int fractionalDigits, int nestingLevel, string propertyPath = null) { string composedString = ""; string tabs = new String('\t', nestingLevel); @@ -101,9 +122,9 @@ private static string DefiningString(object obj, HashConfig hc, int fractionalDi Type type = obj?.GetType(); if (type == null - || (hc.TypeExceptions != null && hc.TypeExceptions.Contains(type)) - || (hc.NamespaceExceptions != null && hc.NamespaceExceptions.Where(ex => type.Namespace.Contains(ex)).Any()) - || nestingLevel >= hc.MaxNesting) + || (dc.TypeExceptions != null && dc.TypeExceptions.Contains(type)) + || (dc.NamespaceExceptions != null && dc.NamespaceExceptions.Where(ex => type.Namespace.Contains(ex)).Any()) + || nestingLevel >= dc.MaxNesting) { return composedString; } @@ -117,20 +138,26 @@ private static string DefiningString(object obj, HashConfig hc, int fractionalDi else if (type.IsArray) { foreach (var element in (obj as dynamic)) - composedString += $"\n{tabs}" + DefiningString(element, hc, fractionalDigits, nestingLevel + 1, propertyPath); + composedString += $"\n{tabs}" + DefiningString(element, dc, fractionalDigits, nestingLevel + 1, propertyPath); } else if (typeof(IDictionary).IsAssignableFrom(type)) { IDictionary dic = obj as IDictionary; + + bool isCustomDataDic = propertyPath.EndsWith("CustomData"); + foreach (DictionaryEntry entry in dic) { - composedString += $"\n{tabs}" + $"[{entry.Key.GetType().FullName}]\n{tabs}{entry.Key}:\n { DefiningString(entry.Value, hc, fractionalDigits, nestingLevel + 1, propertyPath)}"; + if (isCustomDataDic && dc.CustomdataKeysExceptions.Contains(entry.Key)) + continue; + + composedString += $"\n{tabs}" + $"[{entry.Key.GetType().FullName}]\n{tabs}{entry.Key}:\n { DefiningString(entry.Value, dc, fractionalDigits, nestingLevel + 1, propertyPath)}"; } } else if (typeof(IEnumerable).IsAssignableFrom(type) || typeof(IList).IsAssignableFrom(type) || typeof(ICollection).IsAssignableFrom(type)) { foreach (var element in (obj as dynamic)) - composedString += $"\n{tabs}" + DefiningString(element, hc, fractionalDigits, nestingLevel + 1, propertyPath); + composedString += $"\n{tabs}" + DefiningString(element, dc, fractionalDigits, nestingLevel + 1, propertyPath); } else if (type.FullName.Contains("System.Collections.Generic.ObjectEqualityComparer`1")) { @@ -139,7 +166,7 @@ private static string DefiningString(object obj, HashConfig hc, int fractionalDi else if (type == typeof(System.Data.DataTable)) { DataTable dt = obj as DataTable; - return composedString += $"{type.FullName} {string.Join(", ", dt.Columns.OfType().Select(c => c.ColumnName))}\n{tabs}" + DefiningString(dt.AsEnumerable(), hc, fractionalDigits, nestingLevel + 1, propertyPath); + return composedString += $"{type.FullName} {string.Join(", ", dt.Columns.OfType().Select(c => c.ColumnName))}\n{tabs}" + DefiningString(dt.AsEnumerable(), dc, fractionalDigits, nestingLevel + 1, propertyPath); } else if (typeof(IObject).IsAssignableFrom(type)) { @@ -147,13 +174,13 @@ private static string DefiningString(object obj, HashConfig hc, int fractionalDi foreach (PropertyInfo prop in properties) { - bool isInPropertyNameExceptions = hc.PropertyNameExceptions?.Count > 0 && hc.PropertyNameExceptions.Where(ex => prop.Name.Contains(ex)).Any(); - bool isInPropertyFullNameExceptions = hc.PropertyFullNameExceptions?.Count > 0 && hc.PropertyFullNameExceptions.Where(ex => new WildcardPattern(ex).IsMatch(prop.Name + "." + prop.DeclaringType.FullName)).Any(); + bool isInPropertyNameExceptions = dc.PropertyNameExceptions?.Count > 0 && dc.PropertyNameExceptions.Where(ex => prop.Name.Contains(ex)).Any(); + bool isInPropertyFullNameExceptions = dc.PropertyFullNameExceptions?.Count > 0 && dc.PropertyFullNameExceptions.Where(ex => new WildcardPattern(ex).IsMatch(prop.Name + "." + prop.DeclaringType.FullName)).Any(); if (isInPropertyNameExceptions || isInPropertyFullNameExceptions) continue; - if (hc.PropertiesToConsider?.Count() > 0 && !hc.PropertiesToConsider.Contains(prop.Name)) + if (dc.PropertiesToConsider?.Count() > 0 && !dc.PropertiesToConsider.Contains(prop.Name)) continue; object propValue = prop.GetValue(obj); @@ -166,14 +193,14 @@ private static string DefiningString(object obj, HashConfig hc, int fractionalDi string outString = ""; - if (hc.FractionalDigitsPerProperty != null && + if (dc.FractionalDigitsPerProperty != null && prop.PropertyType == typeof(double) || prop.PropertyType == typeof(decimal) || prop.PropertyType == typeof(float)) { Dictionary matches = new Dictionary(); string path = propertyPath + "." + prop.Name; - foreach (var kv in hc.FractionalDigitsPerProperty) + foreach (var kv in dc.FractionalDigitsPerProperty) { if (path.Contains(kv.Key) || new WildcardPattern(kv.Key).IsMatch(path)) @@ -181,14 +208,14 @@ private static string DefiningString(object obj, HashConfig hc, int fractionalDi } if (matches.Count() > 1) - throw new ArgumentException($"Too many matching results obtained with specified {nameof(hc.FractionalDigitsPerProperty)}."); + throw new ArgumentException($"Too many matching results obtained with specified {nameof(dc.FractionalDigitsPerProperty)}."); int fracDigits = matches.Count() == 1 ? matches.FirstOrDefault().Value : fractionalDigits; - outString = DefiningString(propValue, hc, fracDigits, nestingLevel + 1, path) ?? ""; + outString = DefiningString(propValue, dc, fracDigits, nestingLevel + 1, path) ?? ""; } else - outString = DefiningString(propValue, hc, fractionalDigits, nestingLevel + 1, propertyPath) ?? ""; + outString = DefiningString(propValue, dc, fractionalDigits, nestingLevel + 1, propertyPath) ?? ""; if (!string.IsNullOrWhiteSpace(outString)) composedString += $"\n{tabs}" + $"{type.FullName}.{prop.Name}:\n{tabs}{outString} "; diff --git a/Diffing_Engine/Compute/DiffOneByOne.cs b/Diffing_Engine/Compute/DiffOneByOne.cs index 0b21b4fb4..fb29f2625 100644 --- a/Diffing_Engine/Compute/DiffOneByOne.cs +++ b/Diffing_Engine/Compute/DiffOneByOne.cs @@ -48,8 +48,7 @@ public static partial class Compute [Input("pastObjects", "Past objects. Objects whose creation precedes 'currentObjects'.")] [Input("currentObjects", "Following objects. Objects that were created after 'pastObjects'.")] [Input("diffConfig", "Sets configs such as properties to be ignored in the diffing, or enable/disable property-by-property diffing.")] - [Input("useExistingHash", "Advanced setting. If the objects already have an HashFragment assigned, but that only has the 'currentHash' populated. Can be used to avoid recomputing hash in some scenarios.")] - public static Diff DiffOneByOne(IEnumerable pastObjects, IEnumerable currentObjects, DiffConfig diffConfig = null) + public static Diff DiffOneByOne(IEnumerable pastObjects, IEnumerable currentObjects, DiffingConfig diffConfig = null) { if (pastObjects.Count() != currentObjects.Count()) { @@ -57,12 +56,12 @@ public static Diff DiffOneByOne(IEnumerable pastObjects, IEnumerable pastObjects, IEnumerable>(); diff --git a/Diffing_Engine/Compute/DiffRevisions.cs b/Diffing_Engine/Compute/DiffRevisions.cs index a43e21109..b14e72d5c 100644 --- a/Diffing_Engine/Compute/DiffRevisions.cs +++ b/Diffing_Engine/Compute/DiffRevisions.cs @@ -46,7 +46,7 @@ public static partial class Compute [Input("pastRevision", "A past Revision. It must have been created before the 'followingRevision'.")] [Input("followingRevision", "A following Revision. It must have been created after 'pastRevision'.")] [Input("diffConfig", "Sets configs such as properties to be ignored in the diffing, or enable/disable property-by-property diffing.")] - public static Diff DiffRevisions(Revision pastRevision, Revision followingRevision, DiffConfig diffConfig = null) + public static Diff DiffRevisions(Revision pastRevision, Revision followingRevision, DiffingConfig diffConfig = null) { return DiffRevisionObjects(pastRevision.Objects, followingRevision.Objects, diffConfig); } @@ -55,10 +55,10 @@ public static Diff DiffRevisions(Revision pastRevision, Revision followingRevisi // For BHoMObjects, it assumes that they all have a HashFragment assigned (like when they have been passed through a Revision). // For non-BHoMObjects, it performs the VennDiagram comparision with a HashComparer. // Results for BHoMObjects and non are concatenated. - private static Diff DiffRevisionObjects(IEnumerable pastRevisionObjs, IEnumerable followingRevisionObjs, DiffConfig diffConfig = null) + private static Diff DiffRevisionObjects(IEnumerable pastRevisionObjs, IEnumerable followingRevisionObjs, DiffingConfig diffConfig = null) { // Set configurations if diffConfig is null. Clone it for immutability in the UI. - DiffConfig diffConfigCopy = diffConfig == null ? new DiffConfig() : diffConfig.DeepClone() as DiffConfig; + DiffingConfig diffConfigCopy = diffConfig == null ? new DiffingConfig() : diffConfig.DeepClone() as DiffingConfig; // Dispatch the objects in BHoMObjects and generic objects. IEnumerable prevObjs_BHoM = pastRevisionObjs.OfType(); @@ -75,7 +75,7 @@ private static Diff DiffRevisionObjects(IEnumerable pastRevisionObjs, IE // Compute the generic Diffing for the other objects. // This is left to the VennDiagram with a HashComparer. - VennDiagram vd = Engine.Data.Create.VennDiagram(prevObjs_nonBHoM, currObjs_nonBHoM, new RevisionHashComparer(diffConfig)); + VennDiagram vd = Engine.Data.Create.VennDiagram(prevObjs_nonBHoM, currObjs_nonBHoM, new HashComparer(diffConfig.DistinctConfig)); // Concatenate the results of the two diffing operations. List allPrevObjs = new List(); @@ -95,17 +95,17 @@ private static Diff DiffRevisionObjects(IEnumerable pastRevisionObjs, IE } // Computes the Diffing for BHoMObjects that all have a HashFragment assigned (like when they have been passed through a Revision). - private static Diff DiffRevisionObjects(IEnumerable pastObjects, IEnumerable currentObjects, DiffConfig diffConfig = null) + private static Diff DiffRevisionObjects(IEnumerable pastObjects, IEnumerable currentObjects, DiffingConfig diffConfig = null) { // Set configurations if diffConfig is null. Clone it for immutability in the UI. - DiffConfig diffConfigCopy = diffConfig == null ? new DiffConfig() : diffConfig.DeepClone() as DiffConfig; + DiffingConfig dc = diffConfig == null ? new DiffingConfig() : diffConfig.DeepClone() as DiffingConfig; // Take the Revision's objects List currentObjs = currentObjects.ToList(); List readObjs = pastObjects.ToList(); // Make dictionary with object hashes to speed up the next lookups - Dictionary readObjs_dict = readObjs.ToDictionary(obj => obj.RevisionFragment().CurrentHash, obj => obj); + Dictionary readObjs_dict = readObjs.ToDictionary(obj => obj.RevisionFragment().Hash, obj => obj); // Dispatch the objects: new, modified or old List newObjs = new List(); @@ -124,18 +124,18 @@ private static Diff DiffRevisionObjects(IEnumerable pastObjects, IE newObjs.Add(bhomObj); // It's a new object } - else if (revisionFragm.PreviousHash == revisionFragm.CurrentHash) + else if (revisionFragm.PreviousHash == revisionFragm.Hash) { // It's NOT been modified - if (diffConfigCopy.StoreUnchangedObjects) + if (dc.IncludeUnchangedObjects) unChanged.Add(bhomObj); } - else if (revisionFragm.PreviousHash != revisionFragm.CurrentHash) + else if (revisionFragm.PreviousHash != revisionFragm.Hash) { modifiedObjs.Add(bhomObj); // It's been modified - if (diffConfigCopy.EnablePropertyDiffing) + if (dc.EnablePropertyDiffing) { // Determine changed properties IBHoMObject oldBhomObj = null; @@ -144,10 +144,10 @@ private static Diff DiffRevisionObjects(IEnumerable pastObjects, IE if (oldBhomObj == null) continue; // To compute differentProps in a Revision-Diffing, make sure we remove the RevisionFragment. We don't want to consider that. - var differentProps = Query.DifferentProperties(bhomObj.RemoveFragment(typeof(RevisionFragment)), oldBhomObj.RemoveFragment(typeof(RevisionFragment)), diffConfigCopy); + var differentProps = Query.DifferentProperties(bhomObj.RemoveFragment(typeof(RevisionFragment)), oldBhomObj.RemoveFragment(typeof(RevisionFragment)), dc); if (differentProps != null) - objModifiedProps.Add(revisionFragm.CurrentHash, differentProps); + objModifiedProps.Add(revisionFragm.Hash, differentProps); } } else diff --git a/Diffing_Engine/Compute/DiffWithCustomId.cs b/Diffing_Engine/Compute/DiffWithCustomId.cs index 6a5433f8a..39a0e3cd3 100644 --- a/Diffing_Engine/Compute/DiffWithCustomId.cs +++ b/Diffing_Engine/Compute/DiffWithCustomId.cs @@ -48,10 +48,10 @@ public static partial class Compute [Input("customdataIdKey", "Name of the key where the Id of the objects may be found in the BHoMObjects' CustomData. The diff will be attempted using the Ids found there." + "\nE.g. 'Revit_UniqueId' may be used; an id must be stored under object.CustomData['Revit_UniqueId'].")] [Input("diffConfig", "Sets configs such as properties to be ignored in the diffing, or enable/disable property-by-property diffing.")] - public static Diff DiffWithCustomId(IEnumerable pastObjects, IEnumerable currentObjects, string customdataIdKey, DiffConfig diffConfig = null) + public static Diff DiffWithCustomId(IEnumerable pastObjects, IEnumerable currentObjects, string customdataIdKey, DiffingConfig diffConfig = null) { // Set configurations if diffConfig is null. Clone it for immutability in the UI. - DiffConfig diffConfigCopy = diffConfig == null ? new DiffConfig() : (DiffConfig)diffConfig.DeepClone(); + DiffingConfig diffConfigCopy = diffConfig == null ? new DiffingConfig() : (DiffingConfig)diffConfig.DeepClone(); HashSet currentObjectsIds = new HashSet(); HashSet pastObjectsIds = new HashSet(); @@ -109,7 +109,7 @@ public static Diff DiffWithCustomId(IEnumerable pastObjects, IEnume else { // It's NOT been modified - if (diffConfigCopy.StoreUnchangedObjects) + if (diffConfigCopy.IncludeUnchangedObjects) unChanged.Add(currentObj); } } @@ -131,7 +131,7 @@ public static Diff DiffWithCustomId(IEnumerable pastObjects, IEnume $"\n\t * the input objects come from models that were not completely re-created between revisions."); } else if (!diffConfig.EnablePropertyDiffing) - BH.Engine.Reflection.Compute.RecordWarning($"For this Diffing method to detect modified/unchanged objects, you need to set '{nameof(DiffConfig.EnablePropertyDiffing)}' to true in the DiffConfig."); + BH.Engine.Reflection.Compute.RecordWarning($"For this Diffing method to detect modified/unchanged objects, you need to set '{nameof(DiffingConfig.EnablePropertyDiffing)}' to true in the DiffingConfig."); return new Diff(newObjs, deletedObjs, modifiedObjs, diffConfigCopy, objModifiedProps, unChanged); } diff --git a/Diffing_Engine/Compute/DiffWithFragmentId.cs b/Diffing_Engine/Compute/DiffWithFragmentId.cs index e8411977c..316a9d617 100644 --- a/Diffing_Engine/Compute/DiffWithFragmentId.cs +++ b/Diffing_Engine/Compute/DiffWithFragmentId.cs @@ -48,7 +48,7 @@ public static partial class Compute [Input("fragmentType", "(Optional - defaults to the `IPersistentId` fragment)\nFragment Type where the Id of the objects may be found in the BHoMObjects. The diff will be attempted using the Ids found there.")] [Input("fragmentIdProperty", "(Optional - defaults to `PersistentId`)\nName of the property of the Fragment where the Id is stored.")] [Input("diffConfig", "(Optional) Sets configs such as properties to be ignored in the diffing, or enable/disable property-by-property diffing.")] - public static Diff DiffWithFragmentId(IEnumerable pastObjects, IEnumerable currentObjects, Type fragmentType = null, string fragmentIdProperty = null, DiffConfig diffConfig = null) + public static Diff DiffWithFragmentId(IEnumerable pastObjects, IEnumerable currentObjects, Type fragmentType = null, string fragmentIdProperty = null, DiffingConfig diffConfig = null) { if (fragmentType == null || string.IsNullOrWhiteSpace(fragmentIdProperty)) { @@ -60,11 +60,11 @@ public static Diff DiffWithFragmentId(IEnumerable pastObjects, IEnu } // Set configurations if diffConfig is null. Clone it for immutability in the UI. - DiffConfig diffConfigCopy = diffConfig == null ? new DiffConfig() : (DiffConfig)diffConfig.DeepClone(); + DiffingConfig diffConfigCopy = diffConfig == null ? new DiffingConfig() : (DiffingConfig)diffConfig.DeepClone(); if (string.IsNullOrWhiteSpace(fragmentIdProperty)) { - BH.Engine.Reflection.Compute.RecordError($"The DiffConfig must specify a valid {nameof(fragmentIdProperty)}."); + BH.Engine.Reflection.Compute.RecordError($"The DiffingConfig must specify a valid {nameof(fragmentIdProperty)}."); return null; } diff --git a/Diffing_Engine/Compute/DiffGenericObjects.cs b/Diffing_Engine/Compute/DiffWithHash.cs similarity index 78% rename from Diffing_Engine/Compute/DiffGenericObjects.cs rename to Diffing_Engine/Compute/DiffWithHash.cs index ed4504836..1406facad 100644 --- a/Diffing_Engine/Compute/DiffGenericObjects.cs +++ b/Diffing_Engine/Compute/DiffWithHash.cs @@ -42,38 +42,36 @@ namespace BH.Engine.Diffing { public static partial class Compute { - [Description("Computes the diffing for generic objects that do not have any Id or HashFragment assigned." + - "\nShould be seen as last resort if no other diffing method can be applied.")] - [Input("pastObjects", "Past objects. Objects whose creation precedes 'currentObjects'.")] + [Description("Computes the diffing using the Hash of the specified objects. If objects do not have an hash, compute it.")] + [Input("pastObjects", "Objects whose creation predates 'currentObjects'.")] [Input("currentObjects", "Following objects. Objects that were created after 'pastObjects'.")] - [Input("diffConfig", "Sets configs such as properties to be ignored in the diffing, or enable/disable property-by-property diffing.")] + [Input("diffConfig", "Sets configs such as properties to be ignored in the diffing, or enable/disable property-level diffing.")] [Input("useExistingHash", "Advanced setting. If the objects already have an HashFragment assigned, but that only has the 'currentHash' populated. Can be used to avoid recomputing hash in some scenarios.")] - public static Diff DiffGenericObjects(IEnumerable pastObjects, IEnumerable currentObjects, DiffConfig diffConfig = null, bool keepExistingHash = false) + public static Diff DiffWithHash(IEnumerable pastObjects, IEnumerable currentObjects, DiffingConfig diffConfig = null, bool useExistingHash = false) { BH.Engine.Reflection.Compute.RecordNote("This diffing method cannot track modified objects between different revisions." + "\nIt will simply return the objects that appear exclusively in the past set, in the following set, and in both." + $"\nConsider using '{nameof(DiffWithCustomId)}', '{nameof(DiffWithFragmentId)}' or '{nameof(DiffRevisions)}' if this feature is needed."); // Set configurations if diffConfig is null. Clone it for immutability in the UI. - DiffConfig diffConfigCopy = diffConfig == null ? new DiffConfig() : (DiffConfig)diffConfig.DeepClone(); + DiffingConfig dc = diffConfig == null ? new DiffingConfig() : (DiffingConfig)diffConfig.DeepClone(); // Clone objects for immutability in the UI. List pastObjects_cloned = BH.Engine.Base.Query.DeepClone(pastObjects).ToList(); List currentObjects_cloned = BH.Engine.Base.Query.DeepClone(currentObjects).ToList(); - if (!keepExistingHash) + if (!useExistingHash) { - // Clean any existing hash fragment. - // This ensures the hash will be re-computed within this method using the provided DiffConfig. + // Clean any existing hash fragment. This ensures the hash will be re-computed using the provided DiffingConfig. pastObjects_cloned.OfType().ToList().ForEach(o => o.Fragments.Remove(typeof(HashFragment))); currentObjects_cloned.OfType().ToList().ForEach(o => o.Fragments.Remove(typeof(HashFragment))); } // Compute the "Diffing" by means of a VennDiagram. // Hashes are computed in the DiffingHashComparer, once per each object (the hash is stored in a hashFragment). - VennDiagram vd = Engine.Data.Create.VennDiagram(pastObjects_cloned, currentObjects_cloned, new HashComparer(diffConfigCopy, true)); + VennDiagram vd = Engine.Data.Create.VennDiagram(pastObjects_cloned, currentObjects_cloned, new HashComparer(dc.DistinctConfig, true)); - return new Diff(vd.OnlySet2, vd.OnlySet1, null, diffConfigCopy, null, vd.Intersection); + return new Diff(vd.OnlySet2, vd.OnlySet1, null, dc, null, vd.Intersection); } } } diff --git a/Diffing_Engine/Compute/Hash/DiffingHash.cs b/Diffing_Engine/Compute/Hash/DiffingHash.cs deleted file mode 100644 index 84ced0750..000000000 --- a/Diffing_Engine/Compute/Hash/DiffingHash.cs +++ /dev/null @@ -1,75 +0,0 @@ -/* - * This file is part of the Buildings and Habitats object Model (BHoM) - * Copyright (c) 2015 - 2020, the respective contributors. All rights reserved. - * - * Each contributor holds copyright over their respective contributions. - * The project versioning (Git) records all such contribution source information. - * - * - * The BHoM is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3.0 of the License, or - * (at your option) any later version. - * - * The BHoM 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 Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this code. If not, see . - */ - -using BH.oM.Base; -using BH.oM.Data.Collections; -using BH.oM.Diffing; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Security.Cryptography; -using System.Reflection; -using BH.Engine.Serialiser; -using BH.oM.Reflection.Attributes; -using System.ComponentModel; -using BH.Engine.Base; - -namespace BH.Engine.Diffing -{ - public static partial class Compute - { - ///***************************************************/ - ///**** Public Methods ****/ - ///***************************************************/ - - [Description("Computes the hash of the BHoMObject, disregarding any HashFragment already attached to it, if present.")] - [Input("obj", "Objects the hash code should be calculated for")] - [Input("diffConfig", "Sets configs for the hash calculation, such as properties to be ignored.")] - public static string CurrentHash(this object obj, DiffConfig diffConfig = null) - { - diffConfig = diffConfig == null ? new DiffConfig() : (DiffConfig)diffConfig.DeepClone(); - - // The following is to consider only the PropertiesToInclude specified in the diffConfig. - // Since the hash computation can only consider "exceptions", we need to retrieve all the top level properties, - // intersect them with the set of PropertiesToInclude, and treat all the properties that remain out as "exceptions" (not to be considered). - if (diffConfig.HashConfig.PropertiesToConsider?.Any() ?? false) - { - IEnumerable exceptions = BH.Engine.Reflection.Query.PropertyNames(obj).Except(diffConfig.HashConfig.PropertiesToConsider); - diffConfig.HashConfig.PropertyNameExceptions.AddRange(exceptions); - } - - // Any HashFragment present on the object must not be considered. Remove if present. - IBHoMObject bhomobj = obj as IBHoMObject; - if (bhomobj != null) - { - bhomobj = BH.Engine.Base.Query.DeepClone(obj) as IBHoMObject; - List hashFragments = bhomobj.GetAllFragments(typeof(IHashFragment)); - hashFragments.ForEach(f => bhomobj.Fragments.Remove(f.GetType())); - } - - return Base.Query.Hash(bhomobj, diffConfig.HashConfig); // Compute.SHA256Hash(obj, diffConfig.PropertiesToIgnore); - } - } -} - diff --git a/Diffing_Engine/Compute/_IDiffing.cs b/Diffing_Engine/Compute/_IDiffing.cs index 75935038b..d001a3b8b 100644 --- a/Diffing_Engine/Compute/_IDiffing.cs +++ b/Diffing_Engine/Compute/_IDiffing.cs @@ -43,7 +43,7 @@ namespace BH.Engine.Diffing public static partial class Compute { [Description("Dispatches to the most appropriate Diffing method, depending on the provided inputs.")] - public static Diff Diffing(IEnumerable pastObjs, IEnumerable followingObjs, DiffConfig diffConfig = null, string customDataIdKey = null) + public static Diff Diffing(IEnumerable pastObjs, IEnumerable followingObjs, DiffingType diffingType = DiffingType.Automatic, DiffingConfig diffConfig = null) { if (!pastObjs.Any() || !followingObjs.Any()) { @@ -52,86 +52,100 @@ public static Diff Diffing(IEnumerable pastObjs, IEnumerable fol } // Set configurations if diffConfig is null. Clone it for immutability in the UI. - DiffConfig diffConfigCopy = diffConfig == null ? new DiffConfig() : (DiffConfig)diffConfig.DeepClone(); + DiffingConfig dc = diffConfig == null ? new DiffingConfig() : diffConfig.DeepClone(); - // Check if the inputs specified are Revisions. In that case, use the Diffing-Revision workflow. - if (string.IsNullOrWhiteSpace(customDataIdKey) && pastObjs.Count() == 1 && followingObjs.Count() == 1) + // If requested, compute the Diffing comparing each object one by one, in the same order. + if (diffingType == DiffingType.OneByOne) { - Revision pastRev = pastObjs.First() as Revision; - Revision follRev = followingObjs.First() as Revision; + // If objects do not have any persistentId, `AllowOneByOneDiffing` is enabled and the collections have the same length, + // compare objects from the two collections one by one. + + BH.Engine.Reflection.Compute.RecordNote($"Calling the diffing method '{nameof(DiffOneByOne)}'" + + $"\nThis will only identify 'modified' or 'unchanged' objects. It will work correctly only if the input objects are in the same order."); + + return DiffOneByOne(pastObjs, followingObjs, dc); + } - if (pastRev != null && follRev != null) + // Check if the inputs specified are Revisions. In that case, use the Diffing-Revision workflow. + if (diffingType == DiffingType.Automatic || diffingType == DiffingType.Revision) + { + if (pastObjs.Count() == 1 && followingObjs.Count() == 1) { - BH.Engine.Reflection.Compute.RecordNote($"Calling the diffing method '{nameof(DiffRevisions)}'."); + Revision pastRev = pastObjs.First() as Revision; + Revision follRev = followingObjs.First() as Revision; - if (!string.IsNullOrWhiteSpace(customDataIdKey)) - BH.Engine.Reflection.Compute.RecordWarning($"The input {customDataIdKey} is not considered when the input objects are both of type {nameof(Revision)}."); + if (pastRev != null && follRev != null) + { + BH.Engine.Reflection.Compute.RecordNote($"Calling the diffing method '{nameof(DiffRevisions)}'."); - return DiffRevisions(pastRev, follRev, diffConfigCopy); + if (!string.IsNullOrWhiteSpace(dc.CustomDataKey)) + BH.Engine.Reflection.Compute.RecordWarning($"The `{nameof(DiffingConfig)}.{nameof(dc.CustomDataKey)}` is not considered when the input objects are both of type {nameof(Revision)}."); + + return DiffRevisions(pastRev, follRev, dc); + } } + + if (diffingType == DiffingType.Revision) + return DiffingError(diffingType); } IEnumerable bHoMObjects_past = pastObjs.OfType(); IEnumerable bHoMObjects_following = followingObjs.OfType(); + // Check if the BHoMObjects all have a RevisionFragment assigned. + // If so, we may attempt the Revision diffing. + if (bHoMObjects_past.AllHaveRevisionFragment() && bHoMObjects_following.AllHaveRevisionFragment()) + { + BH.Engine.Reflection.Compute.RecordNote($"Calling the diffing method '{nameof(DiffRevisionObjects)}'."); + return DiffRevisionObjects(bHoMObjects_past, bHoMObjects_following, dc); + } + // If a customDataKey was specified, use the Id found under that key in customdata to perform the Diffing. - if (!string.IsNullOrWhiteSpace(customDataIdKey)) + if (diffingType == DiffingType.Automatic || diffingType == DiffingType.CustomDataId) { + if (diffingType == DiffingType.CustomDataId && !string.IsNullOrWhiteSpace(dc.CustomDataKey)) + return DiffingError(diffingType); + if (bHoMObjects_past.Count() == pastObjs.Count() && bHoMObjects_following.Count() == followingObjs.Count()) { BH.Engine.Reflection.Compute.RecordNote($"Calling the diffing method '{nameof(DiffWithCustomId)}'."); - return DiffWithCustomId(bHoMObjects_past, bHoMObjects_following, customDataIdKey, diffConfigCopy); + return DiffWithCustomId(bHoMObjects_past, bHoMObjects_following, dc.CustomDataKey, dc); } else BH.Engine.Reflection.Compute.RecordWarning($"To perform the diffing based on an Id stored in the Custom Data, the inputs must be collections of IBHoMObjects."); } - // Check if the BHoMObjects all have a RevisionFragment assigned. - // If so, we may attempt the Revision diffing. - if (bHoMObjects_past.AllHaveRevisionFragment() && bHoMObjects_following.AllHaveRevisionFragment()) - { - BH.Engine.Reflection.Compute.RecordNote($"Calling the diffing method '{nameof(DiffRevisionObjects)}'."); - return DiffRevisionObjects(bHoMObjects_past, bHoMObjects_following, diffConfigCopy); - } - // Check if the bhomObjects have a persistentId assigned. List reminder_past, reminder_following; List bHoMObjects_past_persistId = bHoMObjects_past.WithNonNullPersistentAdapterId(out reminder_past); List bHoMObjects_following_persistId = bHoMObjects_following.WithNonNullPersistentAdapterId(out reminder_following); Diff fragmentDiff = null; - if (bHoMObjects_past_persistId.Count != 0 && bHoMObjects_following_persistId.Count != 0) - { - // If the BHoMObjects have a `persistentId` assigned, attempt the DiffWithFragmentId diffing. - BH.Engine.Reflection.Compute.RecordNote($"Calling the diffing method '{nameof(DiffWithFragmentId)}'."); - fragmentDiff = DiffWithFragmentId(bHoMObjects_past_persistId, bHoMObjects_following_persistId, typeof(IPersistentAdapterId), nameof(IPersistentAdapterId.PersistentId), diffConfigCopy); - } - else if (diffConfigCopy.AllowOneByOneDiffing && pastObjs.Count() == followingObjs.Count()) + // For the BHoMObjects we can compute the Diff with the persistentId. + if (diffingType == DiffingType.Automatic || diffingType == DiffingType.PersistentId) { - // If objects do not have any persistentId, `AllowOneByOneDiffing` is enabled and the collections have the same length, - // compare objects from the two collections one by one. - - BH.Engine.Reflection.Compute.RecordNote($"Calling the diffing method '{nameof(DiffOneByOne)}'" + - $"\nThis will only identify 'modified' or 'unchanged' objects. It will work correctly only if the input objects are in the same order."); + if (bHoMObjects_past_persistId.Count != 0 && bHoMObjects_following_persistId.Count != 0) - return DiffOneByOne(pastObjs, followingObjs, diffConfigCopy); + BH.Engine.Reflection.Compute.RecordNote($"Calling the diffing method '{nameof(DiffWithFragmentId)}'."); + fragmentDiff = DiffWithFragmentId(bHoMObjects_past_persistId, bHoMObjects_following_persistId, typeof(IPersistentAdapterId), nameof(IPersistentAdapterId.PersistentId), dc); } - // As last resort, compute the hash of each object and compare the objects with the same hash. - // Remember we can use DiffConfig.PropertiesToConsider in order to tell Diffing to tell the difference only based on certain properties. - BH.Engine.Reflection.Compute.RecordNote($"Calling the most generic Diffing method, '{nameof(DiffGenericObjects)}'." + - $"\nReason: the inputs do not satisfy any of the following conditions (at least one is needed to trigger another more detailed diffing):" + - $"\n\t* Not all BHoMObjects have a HashFragment assigned (they didn't pass through a Revision);" + - $"\n\t* No {nameof(customDataIdKey)} was input." + - $"\n\t* The input collections have different lengths."); + // For the remaining objects (= all objects that are not BHoMObjects, and all BHoMObjects not having a PersistentId) we can Diff using the Hash. + BH.Engine.Reflection.Compute.RecordNote($"Calling the most generic Diffing method, '{nameof(DiffWithHash)}'."); - Diff diffGeneric = DiffGenericObjects(pastObjs as dynamic, followingObjs as dynamic, diffConfigCopy); + Diff diffGeneric = DiffWithHash(pastObjs as dynamic, followingObjs as dynamic, dc); if (fragmentDiff == null) return diffGeneric; return fragmentDiff.CombineDiffs(diffGeneric); } + + private static Diff DiffingError(DiffingType diffingType) + { + BH.Engine.Reflection.Compute.RecordError($"Invalid inputs for the selected DiffingType `{diffingType}`."); + return null; + } } } diff --git a/Diffing_Engine/Create/Delta.cs b/Diffing_Engine/Create/Delta.cs index bc5268f48..8bd0ff06d 100644 --- a/Diffing_Engine/Create/Delta.cs +++ b/Diffing_Engine/Create/Delta.cs @@ -46,7 +46,7 @@ public static partial class Create [Input("previousRevision", "A previous Revision")] [Input("currentRevision", "A new Revision")] [Input("diffConfig", "Sets configs such as properties to be ignored in the diffing, or enable/disable property-by-property diffing.\nBy default it takes the diffConfig property of the Revision. This input can be used to override it.")] - public static Delta Delta(Revision pastRevision, Revision currentRevision, DiffConfig diffConfig = null, string comment = null) + public static Delta Delta(Revision pastRevision, Revision currentRevision, DiffingConfig diffConfig = null, string comment = null) { Diff diff = Compute.DiffRevisions(pastRevision, currentRevision, diffConfig); @@ -60,7 +60,7 @@ public static Delta Delta(Revision pastRevision, Revision currentRevision, DiffC [Description("Returns a Delta object containing all the objects of the input Revision, also called `Revision-Based Delta`.")] [Input("revision", "A new Revision")] [Input("diffConfig", "Sets configs such as properties to be ignored in the diffing, or enable/disable property-by-property diffing.\nBy default it takes the diffConfig property of the Revision. This input can be used to override it.")] - public static Delta Delta(Revision revision, DiffConfig diffConfig = null, string comment = null) + public static Delta Delta(Revision revision, DiffingConfig diffConfig = null, string comment = null) { Diff diff = Compute.DiffRevisions(null, revision, diffConfig); @@ -74,7 +74,7 @@ public static Delta Delta(Revision revision, DiffConfig diffConfig = null, strin [Input("comment", "Comment to be stored along the Revision that this Delta will produce.")] [Input("diffConfig", "Sets configs such as properties to be ignored in the diffing, or enable/disable property-by-property diffing.\nBy default it takes the diffConfig property of the Revision. This input can be used to override it.")] public static Delta Delta(List objects, object streamId, string revisionName = null, - string comment = null, DiffConfig diffConfig = null) + string comment = null, DiffingConfig diffConfig = null) { Revision revision = Create.Revision(objects, streamId, revisionName, comment, diffConfig); return Delta(revision, diffConfig, comment); @@ -86,7 +86,7 @@ public static Delta Delta(List objects, object streamId, string rev [Input("revisionName", "Name to be assigned to the Revision that this Delta will produce.")] [Input("comment", "Comment to be stored along the Revision that this Delta will produce.")] [Input("diffConfig", "Sets configs such as properties to be ignored in the diffing, or enable/disable property-by-property diffing.\nBy default it takes the diffConfig property of the Revision. This input can be used to override it.")] - public static Delta Delta(Diff diff, object streamId, Guid revision_from, string comment = null, DiffConfig diffConfig = null) + public static Delta Delta(Diff diff, object streamId, Guid revision_from, string comment = null, DiffingConfig diffConfig = null) { return new Delta(ProcessStreamId(streamId), diff, revision_from, new Guid(), DateTime.UtcNow.Ticks, m_Author, comment); } diff --git a/Diffing_Engine/Create/DiffConfig.cs b/Diffing_Engine/Create/DiffConfig.cs index da5c0132d..cb48cc5c1 100644 --- a/Diffing_Engine/Create/DiffConfig.cs +++ b/Diffing_Engine/Create/DiffConfig.cs @@ -45,26 +45,26 @@ public static partial class Create [Description("Defines configurations for the diffing.")] [Input("enablePropertyDiffing", "Enables the property-level diffing: differences in object properties are stored in the `ModifiedPropsPerObject` dictionary.")] [Input("storeUnchangedObjects", "If enabled, the Diff stores also the objects that did not change (`Unchanged` property).")] - public static DiffConfig DiffConfig(bool enablePropertyDiffing, bool storeUnchangedObjects) + public static DiffingConfig DiffingConfig(bool enablePropertyDiffing, bool storeUnchangedObjects) { - return Create.DiffConfig(enablePropertyDiffing, storeUnchangedObjects, null, null); + return Create.DiffingConfig(enablePropertyDiffing, storeUnchangedObjects, null, null); } [Description("Defines configurations for the diffing.")] [Input("enablePropertyDiffing", "Enables the property-level diffing: differences in object properties are stored in the `ModifiedPropsPerObject` dictionary.")] [Input("storeUnchangedObjects", "If enabled, the Diff stores also the objects that did not change (`Unchanged` property).")] - public static DiffConfig DiffConfig(bool enablePropertyDiffing = false, bool storeUnchangedObjects = true, List propertiesToConsider = null, List propertiesToIgnore = null, List customDataToIgnore = null) + public static DiffingConfig DiffingConfig(bool enablePropertyDiffing = false, bool storeUnchangedObjects = true, List propertiesToConsider = null, List propertiesToIgnore = null, List customDataToIgnore = null) { - return new DiffConfig() + return new DiffingConfig() { EnablePropertyDiffing = enablePropertyDiffing, - StoreUnchangedObjects = storeUnchangedObjects, - HashConfig = new HashConfig() + IncludeUnchangedObjects = storeUnchangedObjects, + DistinctConfig = new DistinctConfig() { PropertiesToConsider = propertiesToConsider, PropertyNameExceptions = propertiesToIgnore, + CustomdataKeysExceptions = (customDataToIgnore == null || !customDataToIgnore.Any()) ? new List() { "RenderMesh" } : customDataToIgnore }, - CustomDataToIgnore = (customDataToIgnore == null || !customDataToIgnore.Any()) ? new List() { "RenderMesh" }: customDataToIgnore }; } } diff --git a/Diffing_Engine/Create/Revision.cs b/Diffing_Engine/Create/Revision.cs index def627af6..653d6032c 100644 --- a/Diffing_Engine/Create/Revision.cs +++ b/Diffing_Engine/Create/Revision.cs @@ -42,7 +42,7 @@ public static partial class Create [Input("revisionName", "Name of the Revision.")] [Input("comment", "Any comment to be added for this Revision. Much like git commit comment.")] [Input("diffConfig", "Diffing settings for this Stream Revision. Hashes of objects contained in this stream will be computed based on these configs.")] - public static Revision Revision(IEnumerable objects, object streamId, string revisionName = null, string comment = null, DiffConfig diffConfig = null) + public static Revision Revision(IEnumerable objects, object streamId, string revisionName = null, string comment = null, DiffingConfig diffConfig = null) { return new Revision(Modify.PrepareForRevision(objects, diffConfig), ProcessStreamId(streamId), diffConfig, revisionName, comment); } diff --git a/Diffing_Engine/Diffing_Engine.csproj b/Diffing_Engine/Diffing_Engine.csproj index fe854651a..06cd2af11 100644 --- a/Diffing_Engine/Diffing_Engine.csproj +++ b/Diffing_Engine/Diffing_Engine.csproj @@ -67,6 +67,7 @@ + @@ -75,9 +76,6 @@ - - - @@ -87,7 +85,6 @@ - diff --git a/Diffing_Engine/Modify/CombineDiffs.cs b/Diffing_Engine/Modify/CombineDiffs.cs index 80863b59c..318db2142 100644 --- a/Diffing_Engine/Modify/CombineDiffs.cs +++ b/Diffing_Engine/Modify/CombineDiffs.cs @@ -52,7 +52,7 @@ public static Diff CombineDiffs(this Diff diff, Diff toAdd) diff.AddedObjects.Concat(toAdd.AddedObjects), diff.RemovedObjects.Concat(toAdd.RemovedObjects), diff.ModifiedObjects.Concat(toAdd.ModifiedObjects), - diff.DiffConfig, + diff.DiffingConfig, diff.ModifiedPropsPerObject.Concat(toAdd.ModifiedPropsPerObject).ToDictionary(x => x.Key, x => x.Value), diff.UnchangedObjects.Concat(toAdd.UnchangedObjects) ); diff --git a/Diffing_Engine/Modify/PrepareForRevision.cs b/Diffing_Engine/Modify/PrepareForRevision.cs index c1bedff35..f13125d24 100644 --- a/Diffing_Engine/Modify/PrepareForRevision.cs +++ b/Diffing_Engine/Modify/PrepareForRevision.cs @@ -38,7 +38,7 @@ namespace BH.Engine.Diffing { public static partial class Modify { - public static IEnumerable PrepareForRevision(this IEnumerable objects, DiffConfig diffConfig = null) where T : IBHoMObject + public static IEnumerable PrepareForRevision(this IEnumerable objects, DiffingConfig diffConfig = null) where T : IBHoMObject { // Clone the current objects to preserve immutability; calculate and set the hash fragment IEnumerable objs_cloned = Modify.SetRevisionFragment(objects, diffConfig); diff --git a/Diffing_Engine/Modify/RemoveDuplicatesByHash.cs b/Diffing_Engine/Modify/RemoveDuplicatesByHash.cs index 6fbfaf53b..157b0f32a 100644 --- a/Diffing_Engine/Modify/RemoveDuplicatesByHash.cs +++ b/Diffing_Engine/Modify/RemoveDuplicatesByHash.cs @@ -33,16 +33,29 @@ using BH.Engine.Serialiser; using BH.oM.Reflection.Attributes; using System.ComponentModel; +using BH.Engine.Base; namespace BH.Engine.Diffing { public static partial class Modify { - [Description("Removes duplicates from a collection of objects. The comparison is made through their Diffing Hash.")] + [Description("Removes duplicates from a collection of objects. The comparison is made using their Hash. If hash is missing, it is computed.")] [Input("objects", "Collection of objects whose duplicates have to be removed. If they don't already have an Hash assigned, it will be calculated.")] - public static IEnumerable RemoveDuplicatesByHash(IEnumerable objects) where T : IBHoMObject + [Input("distinctConfig", "Settings to determine the uniqueness of an Object.")] + [Input("useExistingHash", "If true, if objects already have a HashFragment, use that. If false, recompute the hash for all objects.")] + public static IEnumerable RemoveDuplicatesByHash(IEnumerable objects, DistinctConfig distinctConfig = null, bool useExistingHash = true) where T : IBHoMObject { - return objects.GroupBy(obj => obj.RevisionFragment().CurrentHash).Select(gr => gr.First()).ToList(); + return objects.GroupBy(obj => + { + if (useExistingHash) + { + string existingHash = obj.HashFragment()?.Hash; + if (!string.IsNullOrWhiteSpace(existingHash)) + return existingHash; + } + return obj.Hash(distinctConfig); + } + ).Select(gr => gr.First()).ToList(); } } } diff --git a/Diffing_Engine/Modify/SetHashFragment.cs b/Diffing_Engine/Modify/SetHashFragment.cs index cf8ef19e2..4cd2c0fba 100644 --- a/Diffing_Engine/Modify/SetHashFragment.cs +++ b/Diffing_Engine/Modify/SetHashFragment.cs @@ -40,33 +40,33 @@ namespace BH.Engine.Diffing public static partial class Modify { [Description("Clones the IBHoMObjects, computes their hash and stores it in a HashFragment.")] - public static List SetHashFragment(this IEnumerable objs, DiffConfig diffConfig = null) where T : IBHoMObject + public static List SetHashFragment(this IEnumerable objs, DistinctConfig distinctConfig = null) where T : IBHoMObject { // Clone the current objects to preserve immutability List objs_cloned = new List(); // Set configurations if diffConfig is null - diffConfig = diffConfig == null ? new DiffConfig() : diffConfig; + distinctConfig = distinctConfig == null ? new DistinctConfig() : distinctConfig; // Calculate and set the object hashes foreach (var obj in objs) - objs_cloned.Add(SetHashFragment(obj, diffConfig)); + objs_cloned.Add(SetHashFragment(obj, distinctConfig)); return objs_cloned; } [Description("Clones the IBHoMObject, computes their hash and stores it in a HashFragment.")] - public static T SetHashFragment(T obj, DiffConfig diffConfig = null) where T : IBHoMObject + public static T SetHashFragment(T obj, DistinctConfig distinctConfig = null) where T : IBHoMObject { // Clone the current object to preserve immutability T obj_cloned = BH.Engine.Base.Query.DeepClone(obj); // Set configurations if diffConfig is null - diffConfig = diffConfig == null ? new DiffConfig() : diffConfig; + distinctConfig = distinctConfig == null ? new DistinctConfig() : distinctConfig; // Calculate and set the object hashes - string hash = BH.Engine.Diffing.Compute.CurrentHash(obj_cloned, diffConfig); - obj_cloned.Fragments.AddOrReplace(new HashFragment() { CurrentHash = hash }); + string hash = obj_cloned.Hash(distinctConfig); + obj_cloned.Fragments.AddOrReplace(new HashFragment() { Hash = hash }); return obj_cloned; } @@ -77,7 +77,7 @@ public static T SetHashFragment(T obj, string hash) where T : IBHoMObject // Clone the current object to preserve immutability T obj_cloned = BH.Engine.Base.Query.DeepClone(obj); - obj_cloned.Fragments.AddOrReplace(new HashFragment() { CurrentHash = hash }); + obj_cloned.Fragments.AddOrReplace(new HashFragment() { Hash = hash }); return obj_cloned; } diff --git a/Diffing_Engine/Modify/SetRevisionFragment.cs b/Diffing_Engine/Modify/SetRevisionFragment.cs index 9d96a4056..c8409fcd2 100644 --- a/Diffing_Engine/Modify/SetRevisionFragment.cs +++ b/Diffing_Engine/Modify/SetRevisionFragment.cs @@ -41,13 +41,13 @@ public static partial class Modify { [Description("Clones the IBHoMObjects, computes their hash and stores it in a RevisionFragment. " + "If the object already has a RevisionFragment, it computes the current one and keeps the old one in the `previousHash` of the RevisionFragment.")] - public static List SetRevisionFragment(this IEnumerable objs, DiffConfig diffConfig = null) where T : IBHoMObject + public static List SetRevisionFragment(this IEnumerable objs, DiffingConfig diffConfig = null) where T : IBHoMObject { // Clone the current objects to preserve immutability List objs_cloned = new List(); // Set configurations if diffConfig is null - diffConfig = diffConfig == null ? new DiffConfig() : diffConfig; + diffConfig = diffConfig == null ? new DiffingConfig() : diffConfig; // Calculate and set the object hashes foreach (var obj in objs) @@ -60,20 +60,20 @@ public static List SetRevisionFragment(this IEnumerable objs, DiffConfi [Description("Clones the IBHoMObject, computes their hash and stores it in a RevisionFragment. " + "If the object already has a RevisionFragment, it computes the current one and keeps the old one in the `previousHash` of the RevisionFragment.")] - public static T SetRevisionFragment(T obj, DiffConfig diffConfig = null) where T : IBHoMObject + public static T SetRevisionFragment(T obj, DiffingConfig diffConfig = null) where T : IBHoMObject { // Clone the current object to preserve immutability T obj_cloned = BH.Engine.Base.Query.DeepClone(obj); // Set configurations if diffConfig is null - diffConfig = diffConfig == null ? new DiffConfig() : diffConfig; + diffConfig = diffConfig == null ? new DiffingConfig() : diffConfig; // Calculate and set the object hashes - string hash = BH.Engine.Diffing.Compute.CurrentHash(obj_cloned, diffConfig); + string hash = obj_cloned.Hash(diffConfig.DistinctConfig); RevisionFragment existingFragm = obj_cloned.RevisionFragment(); - obj_cloned.Fragments.AddOrReplace(new RevisionFragment(hash, existingFragm?.CurrentHash)); + obj_cloned.Fragments.AddOrReplace(new RevisionFragment(hash, existingFragm?.Hash)); return obj_cloned; } @@ -87,7 +87,7 @@ public static T SetRevisionFragment(T obj, string hash) where T : IBHoMObject RevisionFragment existingFragm = obj_cloned.RevisionFragment(); - obj_cloned.Fragments.AddOrReplace(new RevisionFragment(hash, existingFragm?.CurrentHash)); + obj_cloned.Fragments.AddOrReplace(new RevisionFragment(hash, existingFragm?.Hash)); return obj_cloned; } diff --git a/Diffing_Engine/Objects/EqualityComparers/HashComparer.cs b/Diffing_Engine/Objects/EqualityComparers/HashComparer.cs index be11b336e..130ce9795 100644 --- a/Diffing_Engine/Objects/EqualityComparers/HashComparer.cs +++ b/Diffing_Engine/Objects/EqualityComparers/HashComparer.cs @@ -33,26 +33,31 @@ using BH.oM.Reflection.Attributes; using BH.oM.Reflection; using BH.Engine.Diffing; +using System.Collections; +using BH.Engine.Base; namespace BH.Engine.Diffing { - public class HashComparer : IEqualityComparer //where T : IBHoMObject + [Description("Computes and compares the Hash of the given Objects.")] + public class HashComparer : IEqualityComparer, IEqualityComparer { /***************************************************/ /**** Constructors ****/ /***************************************************/ + [Description("If true, stores the computed hash for input BHoMObjects as a new HashFragment. False by default.")] public bool StoreHash { get; set; } = false; - public DiffConfig DiffConfig { get; set; } = new DiffConfig(); - public HashComparer(DiffConfig diffConfig = null) - { - if (diffConfig != null) - DiffConfig = diffConfig; - } + [Description("If the objects are IObjects, computes the BHoM Hash using these configurations.")] + public DistinctConfig DistinctConfig { get; set; } = new DistinctConfig(); - public HashComparer(DiffConfig diffConfig = null, bool storeHash = false) : this(diffConfig) + [Input("distinctConfig", "If the objects are IObjects, computes the BHoM Hash using these configurations.")] + [Input("storeHash", "If true, stores the computed hash for input BHoMObjects as a new HashFragment.")] + public HashComparer(DistinctConfig distinctConfig = null, bool storeHash = false) { + if (distinctConfig != null) + DistinctConfig = distinctConfig; + StoreHash = storeHash; } @@ -60,42 +65,32 @@ public HashComparer(DiffConfig diffConfig = null, bool storeHash = false) : this /**** Public Methods ****/ /***************************************************/ - public bool Equals(T x, T y) + public bool Equals(object x, object y) { if (x?.GetType() == y?.GetType()) { string xHash = null; string yHash = null; - IBHoMObject xbHoM = x as IBHoMObject; - IBHoMObject ybHoM = y as IBHoMObject; + IObject xbHoM = x as IObject; + IObject ybHoM = y as IObject; if (xbHoM != null && ybHoM != null) { - xHash = xbHoM?.HashFragment()?.CurrentHash; - yHash = ybHoM?.HashFragment()?.CurrentHash; - - if (string.IsNullOrWhiteSpace(xHash)) - { - xHash = x.CurrentHash(DiffConfig); + xHash = xbHoM.Hash(); + yHash = ybHoM.Hash(); - if (StoreHash) - Modify.SetHashFragment(xbHoM, xHash); - } + if (xbHoM is IBHoMObject && StoreHash) + x = Modify.SetHashFragment(xbHoM as IBHoMObject, xHash); - if (string.IsNullOrWhiteSpace(yHash)) - { - yHash = y.CurrentHash(DiffConfig); - - if (StoreHash) - Modify.SetHashFragment(ybHoM, yHash); - } + if (ybHoM is IBHoMObject && StoreHash) + y = Modify.SetHashFragment(ybHoM as IBHoMObject, yHash); return xHash == yHash; } - return x.CurrentHash(DiffConfig) == y.CurrentHash(DiffConfig); + return GetHashCode(x) == GetHashCode(y); } return false; @@ -103,17 +98,14 @@ public bool Equals(T x, T y) /***************************************************/ - public int GetHashCode(T obj) + public int GetHashCode(object obj) { - if (typeof(IBHoMObject).IsAssignableFrom(typeof(T))) - { - IBHoMObject bHoMObject = (IBHoMObject)obj; - HashFragment hashFragment = bHoMObject.HashFragment(); - if (!string.IsNullOrWhiteSpace(hashFragment?.CurrentHash)) - return hashFragment.CurrentHash.GetHashCode(); - } + IObject iObj = obj as IObject; + + if (iObj != null) + return iObj.Hash(DistinctConfig).GetHashCode(); - return obj.CurrentHash(DiffConfig).GetHashCode(); + return obj.GetHashCode(); } } } diff --git a/Diffing_Engine/Objects/EqualityComparers/RevisionHashComparer.cs b/Diffing_Engine/Objects/EqualityComparers/RevisionHashComparer.cs deleted file mode 100644 index ca9fa7b64..000000000 --- a/Diffing_Engine/Objects/EqualityComparers/RevisionHashComparer.cs +++ /dev/null @@ -1,120 +0,0 @@ -/* - * This file is part of the Buildings and Habitats object Model (BHoM) - * Copyright (c) 2015 - 2020, the respective contributors. All rights reserved. - * - * Each contributor holds copyright over their respective contributions. - * The project versioning (Git) records all such contribution source information. - * - * - * The BHoM is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3.0 of the License, or - * (at your option) any later version. - * - * The BHoM 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 Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this code. If not, see . - */ - -using BH.oM.Base; -using BH.Engine; -using BH.oM.Data.Collections; -using BH.oM.Diffing; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Reflection; -using BH.Engine.Serialiser; -using System.ComponentModel; -using BH.oM.Reflection.Attributes; -using BH.oM.Reflection; -using BH.Engine.Diffing; - -namespace BH.Engine.Diffing -{ - public class RevisionHashComparer : IEqualityComparer //where T : IBHoMObject - { - /***************************************************/ - /**** Constructors ****/ - /***************************************************/ - - public bool StoreHash { get; set; } = false; - public DiffConfig DiffConfig { get; set; } = new DiffConfig(); - - public RevisionHashComparer(DiffConfig diffConfig = null) - { - if (diffConfig != null) - DiffConfig = diffConfig; - } - - public RevisionHashComparer(DiffConfig diffConfig = null, bool storeHash = false) : this(diffConfig) - { - StoreHash = storeHash; - } - - /***************************************************/ - /**** Public Methods ****/ - /***************************************************/ - - public bool Equals(T x, T y) - { - if (x?.GetType() == y?.GetType()) - { - string xHash = null; - string yHash = null; - - IBHoMObject xbHoM = x as IBHoMObject; - IBHoMObject ybHoM = y as IBHoMObject; - - if (xbHoM != null && ybHoM != null) - { - xHash = xbHoM?.RevisionFragment()?.CurrentHash; - yHash = ybHoM?.RevisionFragment()?.CurrentHash; - - if (string.IsNullOrWhiteSpace(xHash)) - { - xHash = x.CurrentHash(DiffConfig); - - if (StoreHash) - xbHoM = Modify.SetRevisionFragment(xbHoM, xHash); - - } - - if (string.IsNullOrWhiteSpace(yHash)) - { - yHash = y.CurrentHash(DiffConfig); - - if (StoreHash) - ybHoM = Modify.SetRevisionFragment(ybHoM, yHash); - } - - return xHash == yHash; - } - - return x.CurrentHash(DiffConfig) == y.CurrentHash(DiffConfig); - } - - return false; - } - - /***************************************************/ - - public int GetHashCode(T obj) - { - if (typeof(IBHoMObject).IsAssignableFrom(typeof(T))) - { - IBHoMObject bHoMObject = (IBHoMObject)obj; - RevisionFragment hashFragment = bHoMObject.RevisionFragment(); - if (!string.IsNullOrWhiteSpace(hashFragment?.CurrentHash)) - return hashFragment.CurrentHash.GetHashCode(); - } - - return obj.CurrentHash(DiffConfig).GetHashCode(); - } - } -} - diff --git a/Diffing_Engine/Query/AnyDuplicateByHash.cs b/Diffing_Engine/Query/AnyDuplicateByHash.cs deleted file mode 100644 index beb8ee45f..000000000 --- a/Diffing_Engine/Query/AnyDuplicateByHash.cs +++ /dev/null @@ -1,49 +0,0 @@ -/* - * This file is part of the Buildings and Habitats object Model (BHoM) - * Copyright (c) 2015 - 2020, the respective contributors. All rights reserved. - * - * Each contributor holds copyright over their respective contributions. - * The project versioning (Git) records all such contribution source information. - * - * - * The BHoM is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3.0 of the License, or - * (at your option) any later version. - * - * The BHoM 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 Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this code. If not, see . - */ - -using BH.Engine.Base; -using BH.oM.Base; -using BH.oM.Reflection.Attributes; -using System; -using System.Collections.Generic; -using System.ComponentModel; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace BH.Engine.Diffing -{ - public static partial class Query - { - /***************************************************/ - /**** Public Methods ****/ - /***************************************************/ - - [Description("Removes duplicates from a collection of objects. The comparison is made through their Diffing Hash.")] - [Input("objects", "Collection of objects whose duplicates have to be removed. If they don't already have an Hash assigned, it will be calculated.")] - public static bool AnyDuplicateByHash(this IEnumerable objects) where T : IBHoMObject - { - return Modify.RemoveDuplicatesByHash(objects).ToList().Count != objects.ToList().Count; - } - } -} - diff --git a/Diffing_Engine/Query/DifferentProperties.cs b/Diffing_Engine/Query/DifferentProperties.cs index 807fbf47b..5de35c2f6 100644 --- a/Diffing_Engine/Query/DifferentProperties.cs +++ b/Diffing_Engine/Query/DifferentProperties.cs @@ -40,10 +40,10 @@ public static partial class Query [Description("Checks two BHoMObjects property by property and returns the differences")] [Input("diffConfig", "Config to be used for the comparison. Can set numeric tolerance, wheter to check the guid, if custom data should be ignored and if any additional properties should be ignored")] [Output("Dictionary whose key is the name of the property, and value is a tuple with its value in obj1 and obj2.")] - public static Dictionary> DifferentProperties(this object obj1, object obj2, DiffConfig diffConfig = null) + public static Dictionary> DifferentProperties(this object obj1, object obj2, DiffingConfig diffConfig = null) { // Set configurations if diffConfig is null. Clone it for immutability in the UI. - DiffConfig diffConfigCopy = diffConfig == null ? new DiffConfig() : diffConfig.DeepClone() as DiffConfig; + DiffingConfig dc = diffConfig == null ? new DiffingConfig() : diffConfig.DeepClone() as DiffingConfig; object obj1Copy = obj1.DeepClone(); object obj2Copy = obj2.DeepClone(); @@ -53,16 +53,16 @@ public static Dictionary> DifferentProperties(this CompareLogic comparer = new CompareLogic(); // General configurations. - comparer.Config.MaxDifferences = diffConfigCopy.MaxPropertyDifferences; - comparer.Config.DoublePrecision = diffConfigCopy.HashConfig.NumericTolerance; + comparer.Config.MaxDifferences = dc.MaxPropertyDifferences; + comparer.Config.DoublePrecision = dc.DistinctConfig.NumericTolerance; // Set the properties to be ignored. - if (!diffConfigCopy.HashConfig.PropertyNameExceptions?.Contains("BHoM_Guid") ?? true) - diffConfigCopy.HashConfig.PropertyNameExceptions.Add("BHoM_Guid"); + if (!dc.DistinctConfig.PropertyNameExceptions?.Contains("BHoM_Guid") ?? true) + dc.DistinctConfig.PropertyNameExceptions.Add("BHoM_Guid"); // the above should be replaced by BH.Engine.Reflection.Compute.RecordWarning($"`BHoM_Guid` should generally be ignored when computing the diffing. Consider adding it to the {nameof(diffConfig.PropertiesToIgnore)}."); // when the bug in the auto Create() method ("auto-property initialisers for ByRef values like lists do not populate default values") is resolved. - comparer.Config.MembersToIgnore = diffConfigCopy.HashConfig.PropertyNameExceptions; + comparer.Config.MembersToIgnore = dc.DistinctConfig.PropertyNameExceptions; // Removes the CustomData to be ignored. var bhomobj1 = (obj1Copy as IBHoMObject); @@ -70,13 +70,13 @@ public static Dictionary> DifferentProperties(this if (bhomobj1 != null) { - diffConfigCopy.CustomDataToIgnore.ForEach(k => bhomobj1.CustomData.Remove(k)); + dc.DistinctConfig.CustomdataKeysExceptions.ForEach(k => bhomobj1.CustomData.Remove(k)); obj1Copy = bhomobj1; } if (bhomobj2 != null) { - diffConfigCopy.CustomDataToIgnore.ForEach(k => bhomobj2.CustomData.Remove(k)); + dc.DistinctConfig.CustomdataKeysExceptions.ForEach(k => bhomobj2.CustomData.Remove(k)); obj2Copy = bhomobj2; } @@ -108,7 +108,7 @@ public static Dictionary> DifferentProperties(this propertyName = splittedName.FirstOrDefault() + $"['{keyName}']." + splittedName.Last(); } - if (diffConfigCopy.HashConfig.PropertyNameExceptions.Any() && !diffConfigCopy.HashConfig.PropertyNameExceptions.Contains(difference.PropertyName)) + if (dc.DistinctConfig.PropertyNameExceptions.Any() && !dc.DistinctConfig.PropertyNameExceptions.Contains(difference.PropertyName)) dict[propertyName] = new Tuple(difference.Object1, difference.Object2); } diff --git a/Environment_Engine/Query/HasMergeablePropertiesWith.cs b/Environment_Engine/Query/HasMergeablePropertiesWith.cs index 876090392..e1f2a0f36 100644 --- a/Environment_Engine/Query/HasMergeablePropertiesWith.cs +++ b/Environment_Engine/Query/HasMergeablePropertiesWith.cs @@ -56,9 +56,9 @@ public static bool HasMergeablePropertiesWith(Edge element, Edge other) [Output("equal", "True if the Objects non-geometrical property is equal to the point that they could be merged into one object")] public static bool HasMergeablePropertiesWith(Panel element, Panel other) { - DiffConfig config = new DiffConfig() + DiffingConfig config = new DiffingConfig() { - HashConfig = new HashConfig() + DistinctConfig = new DistinctConfig() { PropertyNameExceptions = new List { @@ -82,9 +82,9 @@ public static bool HasMergeablePropertiesWith(Panel element, Panel other) [Output("equal", "True if the Objects non-geometrical property is equal to the point that they could be merged into one object")] public static bool HasMergeablePropertiesWith(Opening element, Opening other) { - DiffConfig config = new DiffConfig() + DiffingConfig config = new DiffingConfig() { - HashConfig = new HashConfig() + DistinctConfig = new DistinctConfig() { PropertyNameExceptions = new List { @@ -117,9 +117,9 @@ public static bool HasMergeablePropertiesWith(Node element, Node other) [Output("equal", "True if the Objects non-geometrical property is equal to the point that they could be merged into one object")] public static bool HasMergeablePropertiesWith(Space element, Space other) { - DiffConfig config = new DiffConfig() + DiffingConfig config = new DiffingConfig() { - HashConfig = new HashConfig() + DistinctConfig = new DistinctConfig() { PropertyNameExceptions = new List { diff --git a/Matter_Engine/Compute/AggregateMaterialComposition.cs b/Matter_Engine/Compute/AggregateMaterialComposition.cs index 8ef363e9f..67015ce8e 100644 --- a/Matter_Engine/Compute/AggregateMaterialComposition.cs +++ b/Matter_Engine/Compute/AggregateMaterialComposition.cs @@ -84,7 +84,7 @@ public static MaterialComposition AggregateMaterialComposition(IEnumerable().CurrentHash == mat.FindFragment().CurrentHash) + if (allMaterials[k].FindFragment().Hash == mat.FindFragment().Hash) { allRatios[k] += localMatComps[j].Ratios[i] * localRatios[j]; existed = true; diff --git a/Physical_Engine/Query/HasMergeablePropertiesWith.cs b/Physical_Engine/Query/HasMergeablePropertiesWith.cs index 7ee082a5b..2f2bd5754 100644 --- a/Physical_Engine/Query/HasMergeablePropertiesWith.cs +++ b/Physical_Engine/Query/HasMergeablePropertiesWith.cs @@ -75,7 +75,7 @@ public static bool HasMergeablePropertiesWith(this IFramingElement element, IFra if (element.Property.Name != other.Property.Name) return false; - return Diffing.Query.DifferentProperties(element.Property, other.Property, new DiffConfig()) == null; + return Diffing.Query.DifferentProperties(element.Property, other.Property, new DiffingConfig()) == null; } @@ -102,7 +102,7 @@ public static bool HasMergeablePropertiesWith(this ISurface element, ISurface ot if (element.Construction.Name != other.Construction.Name) return false; - return Diffing.Query.DifferentProperties(element.Construction, other.Construction, new DiffConfig()) == null; + return Diffing.Query.DifferentProperties(element.Construction, other.Construction, new DiffingConfig()) == null; } /***************************************************/ diff --git a/Physical_Engine/Query/UniqueConstructions.cs b/Physical_Engine/Query/UniqueConstructions.cs index bc8eb4b10..d3d3f1d65 100644 --- a/Physical_Engine/Query/UniqueConstructions.cs +++ b/Physical_Engine/Query/UniqueConstructions.cs @@ -44,24 +44,20 @@ public static partial class Query [Output("uniqueConstructions", "A collection of unique Construction objects")] public static List UniqueConstructions(this List constructions, bool includeConstructionName = false) { - DiffConfig config = new DiffConfig() + DistinctConfig dc = new DistinctConfig() { - HashConfig = new HashConfig() - { - PropertyFullNameExceptions = new List + PropertyNameExceptions = new List { "CustomData" }, - NumericTolerance = BH.oM.Geometry.Tolerance.Distance - } + NumericTolerance = BH.oM.Geometry.Tolerance.Distance }; if (!includeConstructionName) - config.HashConfig.PropertyNameExceptions.Add("Name"); + dc.PropertyNameExceptions.Add("Name"); List allConstructions = constructions.Where(x => x != null).ToList(); - List hashedConstructions = BH.Engine.Diffing.Modify.SetHashFragment(allConstructions, config); - List uniqueConstructions = BH.Engine.Diffing.Modify.RemoveDuplicatesByHash(hashedConstructions).ToList(); + List uniqueConstructions = BH.Engine.Diffing.Modify.RemoveDuplicatesByHash(allConstructions, dc).ToList(); return uniqueConstructions; } From a7ed1f8b6720a245e3213487691f62d1a1484deb Mon Sep 17 00:00:00 2001 From: Alessio Lombardi Date: Fri, 13 Nov 2020 17:50:29 +0000 Subject: [PATCH 06/28] Align to minor changes in oM; HashComparer generic again --- Analytical_Engine/Analytical_Engine.csproj | 2 +- .../Create/Graph/{DiffConfig.cs => DistinctConfig.cs} | 11 +++++------ BHoM_Engine/Query/Hash.cs | 6 +++--- Diffing_Engine/Create/DiffConfig.cs | 4 ++-- .../Objects/EqualityComparers/HashComparer.cs | 10 +++++----- 5 files changed, 16 insertions(+), 17 deletions(-) rename Analytical_Engine/Create/Graph/{DiffConfig.cs => DistinctConfig.cs} (76%) diff --git a/Analytical_Engine/Analytical_Engine.csproj b/Analytical_Engine/Analytical_Engine.csproj index 073de2f86..349c8d741 100644 --- a/Analytical_Engine/Analytical_Engine.csproj +++ b/Analytical_Engine/Analytical_Engine.csproj @@ -75,7 +75,7 @@ - + diff --git a/Analytical_Engine/Create/Graph/DiffConfig.cs b/Analytical_Engine/Create/Graph/DistinctConfig.cs similarity index 76% rename from Analytical_Engine/Create/Graph/DiffConfig.cs rename to Analytical_Engine/Create/Graph/DistinctConfig.cs index a5f8bf3d3..a5b6fe5da 100644 --- a/Analytical_Engine/Create/Graph/DiffConfig.cs +++ b/Analytical_Engine/Create/Graph/DistinctConfig.cs @@ -19,16 +19,15 @@ public static partial class Create [Description("Create a simple DistinctConfig.")] [Input("numericTolerance", "Tolerance used to determine numerical differences." + "\nDefaults to Tolerance.Distance (1e-6).")] - [Input("propertiesToConsider", "By default, diffing considers all the properties of the objects." + - "\nHere you can specify a list of property names. Only the properties with a name matching any of this list will be considered for diffing." + - "\nE.g., if you input 'Name' only the differences in terms of name will be returned." + - "\nNOTE: these can be only top-level properties of the object (not the sub-properties).")] - public static DistinctConfig DistinctConfig(double numericTolerance = oM.Geometry.Tolerance.Distance, List propertiesToConsider = null) + [Input("propertyNamesToConsider", "By default, all the properties of the objects are considered in determining uniqueness." + + "\nHere you can specify a list of property names. Only the properties with a name matching any of this list will be considered." + + "\nE.g., if you input 'Name' only the differences in terms of name will be returned.")] + public static DistinctConfig DistinctConfig(double numericTolerance = oM.Geometry.Tolerance.Distance, List propertyNamesToConsider = null) { DistinctConfig distinctConfig = new DistinctConfig() { NumericTolerance = numericTolerance, - PropertiesToConsider = propertiesToConsider ?? new List(), + PropertyNamesToConsider = propertyNamesToConsider ?? new List(), }; return distinctConfig; diff --git a/BHoM_Engine/Query/Hash.cs b/BHoM_Engine/Query/Hash.cs index dab6e6838..f0016257b 100644 --- a/BHoM_Engine/Query/Hash.cs +++ b/BHoM_Engine/Query/Hash.cs @@ -60,11 +60,11 @@ public static string Hash(this IObject iObj, DistinctConfig distinctConfig = nul int fractionalDigits = Math.Abs(Convert.ToInt32(Math.Log10(hc.NumericTolerance))); // Process the "PropertiesToInclude" property. - if (hc.PropertiesToConsider?.Any() ?? false) + if (hc.PropertyNamesToConsider?.Any() ?? false) { // The hash computation can only consider "exceptions". // We need to retrieve all the object properties, intersect them with PropertiesToInclude, and treat all those remaining as "exceptions". - IEnumerable exceptions = BH.Engine.Reflection.Query.PropertyNames(iObj).Except(hc.PropertiesToConsider); + IEnumerable exceptions = BH.Engine.Reflection.Query.PropertyNames(iObj).Except(hc.PropertyNamesToConsider); hc.PropertyNameExceptions.AddRange(exceptions); } @@ -180,7 +180,7 @@ private static string DefiningString(object obj, DistinctConfig dc, int fraction if (isInPropertyNameExceptions || isInPropertyFullNameExceptions) continue; - if (dc.PropertiesToConsider?.Count() > 0 && !dc.PropertiesToConsider.Contains(prop.Name)) + if (dc.PropertyNamesToConsider?.Count() > 0 && !dc.PropertyNamesToConsider.Contains(prop.Name)) continue; object propValue = prop.GetValue(obj); diff --git a/Diffing_Engine/Create/DiffConfig.cs b/Diffing_Engine/Create/DiffConfig.cs index cb48cc5c1..90077023a 100644 --- a/Diffing_Engine/Create/DiffConfig.cs +++ b/Diffing_Engine/Create/DiffConfig.cs @@ -53,7 +53,7 @@ public static DiffingConfig DiffingConfig(bool enablePropertyDiffing, bool store [Description("Defines configurations for the diffing.")] [Input("enablePropertyDiffing", "Enables the property-level diffing: differences in object properties are stored in the `ModifiedPropsPerObject` dictionary.")] [Input("storeUnchangedObjects", "If enabled, the Diff stores also the objects that did not change (`Unchanged` property).")] - public static DiffingConfig DiffingConfig(bool enablePropertyDiffing = false, bool storeUnchangedObjects = true, List propertiesToConsider = null, List propertiesToIgnore = null, List customDataToIgnore = null) + public static DiffingConfig DiffingConfig(bool enablePropertyDiffing = false, bool storeUnchangedObjects = true, List propertyNamesToConsider = null, List propertiesToIgnore = null, List customDataToIgnore = null) { return new DiffingConfig() { @@ -61,7 +61,7 @@ public static DiffingConfig DiffingConfig(bool enablePropertyDiffing = false, bo IncludeUnchangedObjects = storeUnchangedObjects, DistinctConfig = new DistinctConfig() { - PropertiesToConsider = propertiesToConsider, + PropertyNamesToConsider = propertyNamesToConsider, PropertyNameExceptions = propertiesToIgnore, CustomdataKeysExceptions = (customDataToIgnore == null || !customDataToIgnore.Any()) ? new List() { "RenderMesh" } : customDataToIgnore }, diff --git a/Diffing_Engine/Objects/EqualityComparers/HashComparer.cs b/Diffing_Engine/Objects/EqualityComparers/HashComparer.cs index 130ce9795..6efa72e02 100644 --- a/Diffing_Engine/Objects/EqualityComparers/HashComparer.cs +++ b/Diffing_Engine/Objects/EqualityComparers/HashComparer.cs @@ -39,7 +39,7 @@ namespace BH.Engine.Diffing { [Description("Computes and compares the Hash of the given Objects.")] - public class HashComparer : IEqualityComparer, IEqualityComparer + public class HashComparer : IEqualityComparer { /***************************************************/ /**** Constructors ****/ @@ -65,7 +65,7 @@ public HashComparer(DistinctConfig distinctConfig = null, bool storeHash = false /**** Public Methods ****/ /***************************************************/ - public bool Equals(object x, object y) + public bool Equals(T x, T y) { if (x?.GetType() == y?.GetType()) { @@ -81,10 +81,10 @@ public bool Equals(object x, object y) yHash = ybHoM.Hash(); if (xbHoM is IBHoMObject && StoreHash) - x = Modify.SetHashFragment(xbHoM as IBHoMObject, xHash); + x = (T)Modify.SetHashFragment(xbHoM as IBHoMObject, xHash); if (ybHoM is IBHoMObject && StoreHash) - y = Modify.SetHashFragment(ybHoM as IBHoMObject, yHash); + y = (T)Modify.SetHashFragment(ybHoM as IBHoMObject, yHash); return xHash == yHash; } @@ -98,7 +98,7 @@ public bool Equals(object x, object y) /***************************************************/ - public int GetHashCode(object obj) + public int GetHashCode(T obj) { IObject iObj = obj as IObject; From 235b4f894e3cdf0d0d813523c4785179525d7777 Mon Sep 17 00:00:00 2001 From: Alessio Lombardi Date: Mon, 16 Nov 2020 10:17:20 +0000 Subject: [PATCH 07/28] Align to ComparisonConfig rename from DistinctConfig. Fixes Fixing CombineDiffs (adding null checks); Fixing Hash(): failing to pick cloned object with HashFragment removed. Also added comments and tidy up for clarity. --- .../Create/Graph/DistinctConfig.cs | 6 ++--- Analytical_Engine/Create/Graph/Graph.cs | 6 ++--- .../Query/Graph/DistinctEntities.cs | 4 +-- BHoM_Engine/Query/Hash.cs | 26 ++++++++++++------- Diffing_Engine/Compute/DiffRevisions.cs | 22 ++++++++-------- Diffing_Engine/Compute/DiffWithHash.cs | 2 +- Diffing_Engine/Compute/_IDiffing.cs | 2 +- Diffing_Engine/Create/Delta.cs | 24 ++++++++--------- Diffing_Engine/Create/DiffConfig.cs | 2 +- Diffing_Engine/Modify/CombineDiffs.cs | 14 ++++++---- .../Modify/RemoveDuplicatesByHash.cs | 2 +- Diffing_Engine/Modify/SetHashFragment.cs | 8 +++--- Diffing_Engine/Modify/SetRevisionFragment.cs | 16 ++++++------ .../Objects/EqualityComparers/HashComparer.cs | 8 +++--- Diffing_Engine/Query/DifferentProperties.cs | 24 ++++++++--------- .../Query/HasMergeablePropertiesWith.cs | 6 ++--- Physical_Engine/Query/UniqueConstructions.cs | 2 +- 17 files changed, 93 insertions(+), 81 deletions(-) diff --git a/Analytical_Engine/Create/Graph/DistinctConfig.cs b/Analytical_Engine/Create/Graph/DistinctConfig.cs index a5b6fe5da..8998fd096 100644 --- a/Analytical_Engine/Create/Graph/DistinctConfig.cs +++ b/Analytical_Engine/Create/Graph/DistinctConfig.cs @@ -16,15 +16,15 @@ public static partial class Create /**** Public Methods ****/ /***************************************************/ - [Description("Create a simple DistinctConfig.")] + [Description("Create a simple ComparisonConfig.")] [Input("numericTolerance", "Tolerance used to determine numerical differences." + "\nDefaults to Tolerance.Distance (1e-6).")] [Input("propertyNamesToConsider", "By default, all the properties of the objects are considered in determining uniqueness." + "\nHere you can specify a list of property names. Only the properties with a name matching any of this list will be considered." + "\nE.g., if you input 'Name' only the differences in terms of name will be returned.")] - public static DistinctConfig DistinctConfig(double numericTolerance = oM.Geometry.Tolerance.Distance, List propertyNamesToConsider = null) + public static ComparisonConfig ComparisonConfig(double numericTolerance = oM.Geometry.Tolerance.Distance, List propertyNamesToConsider = null) { - DistinctConfig distinctConfig = new DistinctConfig() + ComparisonConfig distinctConfig = new ComparisonConfig() { NumericTolerance = numericTolerance, PropertyNamesToConsider = propertyNamesToConsider ?? new List(), diff --git a/Analytical_Engine/Create/Graph/Graph.cs b/Analytical_Engine/Create/Graph/Graph.cs index fa5faa5ce..21b4224b5 100644 --- a/Analytical_Engine/Create/Graph/Graph.cs +++ b/Analytical_Engine/Create/Graph/Graph.cs @@ -49,7 +49,7 @@ public static partial class Create [Input("entities", "A collection of IBHoMOBjects to use as Graph entities. Entities should include DependencyFragments to determine the Graph Relations.")] [Input("distinctConfig", "Settings to determine the uniqueness of entities.")] [Output("graph", "Graph.")] - public static Graph Graph(List entities, DistinctConfig distinctConfig = null) + public static Graph Graph(List entities, ComparisonConfig distinctConfig = null) { return Graph(entities, new List(), distinctConfig); } @@ -60,7 +60,7 @@ public static Graph Graph(List entities, DistinctConfig distinctCon [Input("relations", "A collection of IRelations to use as Graph Relations. Relations should include sub Graphs containing the entities to be used in the Graph.")] [Input("distinctConfig", "Settings to determine the uniqueness of entities.")] [Output("graph", "Graph.")] - public static Graph Graph(List relations, DistinctConfig distinctConfig = null) + public static Graph Graph(List relations, ComparisonConfig distinctConfig = null) { return Graph(new List(), relations, distinctConfig); } @@ -72,7 +72,7 @@ public static Graph Graph(List relations, DistinctConfig distinctConf [Input("relations", "Optional collection of IRelations to use as Graph Relations. Relations can include sub Graphs containing the entities to be used in the Graph.")] [Input("distinctConfig", "Settings to determine the uniqueness of entities.")] [Output("graph", "Graph.")] - public static Graph Graph(List entities = null, List relations = null, DistinctConfig distinctConfig = null) + public static Graph Graph(List entities = null, List relations = null, ComparisonConfig distinctConfig = null) { Graph graph = new Graph(); diff --git a/Analytical_Engine/Query/Graph/DistinctEntities.cs b/Analytical_Engine/Query/Graph/DistinctEntities.cs index 0fe7ae940..4726a6bb0 100644 --- a/Analytical_Engine/Query/Graph/DistinctEntities.cs +++ b/Analytical_Engine/Query/Graph/DistinctEntities.cs @@ -45,9 +45,9 @@ public static partial class Query [Input("entities", "A collection of IBHoMObjects from which unique instances are identified.")] [Input("distinctConfig", "Configuration of diffing used to find unique entities.")] [Output("unique entities", "A Dictionary replacement map of the entities where the keys are the Guid of the original entity and the Values the matching IBHoMObject entity.")] - public static Dictionary DistinctEntities(this List entities, DistinctConfig distinctConfig = null) // this is not a diff, it's finding unique entities + public static Dictionary DistinctEntities(this List entities, ComparisonConfig distinctConfig = null) // this is not a diff, it's finding unique entities { - DistinctConfig dc = distinctConfig ?? new DistinctConfig(); + ComparisonConfig dc = distinctConfig ?? new ComparisonConfig(); Dictionary replaceMap = new Dictionary(); Dictionary objectHash = new Dictionary(); diff --git a/BHoM_Engine/Query/Hash.cs b/BHoM_Engine/Query/Hash.cs index f0016257b..62081d41a 100644 --- a/BHoM_Engine/Query/Hash.cs +++ b/BHoM_Engine/Query/Hash.cs @@ -46,12 +46,14 @@ public static partial class Query [Description("Computes a Hash code for the iObject. The hash uniquely represents an object's state based on its combined properties and their values.")] [Input("iObj", "iObject the hash code should be calculated for.")] - public static string Hash(this IObject iObj, DistinctConfig distinctConfig = null) + public static string Hash(this IObject iObj, ComparisonConfig distinctConfig = null) { - // Make sure to clone for immutability, and always have a HashConfig. - DistinctConfig hc = distinctConfig == null ? new DistinctConfig() : distinctConfig.DeepClone(); + // ------ SET UP OF CONFIGURATION ------ - // Make sure that "BHoM_Guid" is added to the propertyNameExceptions of the HashConfig. + // Make sure we always have a config object. Clone for immutability. + ComparisonConfig hc = distinctConfig == null ? new ComparisonConfig() : distinctConfig.DeepClone(); + + // Make sure that "BHoM_Guid" is added to the propertyNameExceptions of the config. hc.PropertyNameExceptions = hc.PropertyNameExceptions ?? new List(); if (!hc.PropertyNameExceptions.Contains(nameof(BHoMObject.BHoM_Guid))) hc.PropertyNameExceptions.Add(nameof(BHoMObject.BHoM_Guid)); @@ -68,24 +70,30 @@ public static string Hash(this IObject iObj, DistinctConfig distinctConfig = nul hc.PropertyNameExceptions.AddRange(exceptions); } + // ----- SET UP OF INPUT OBJECT ----- + + // Copy the object for immutability + IObject iObj_copy = iObj.ShallowClone(); + // Any HashFragment present on the object must not be considered when computing the Hash. Remove if present. - IBHoMObject bhomobj = iObj as IBHoMObject; + IBHoMObject bhomobj = iObj_copy as IBHoMObject; if (bhomobj != null) { - bhomobj = BH.Engine.Base.Query.DeepClone(iObj) as IBHoMObject; List hashFragments = bhomobj.GetAllFragments(typeof(IHashFragment)).OfType().ToList(); hashFragments.ForEach(f => bhomobj.Fragments.Remove(f.GetType())); + iObj_copy = bhomobj; } + // ----- HASH ----- + // Compute the defining string. - string hashString = DefiningString(iObj, hc, fractionalDigits, 0); + string hashString = DefiningString(iObj_copy, hc, fractionalDigits, 0); if (string.IsNullOrWhiteSpace(hashString)) throw new Exception("Error computing the defining string of the object."); // Return the SHA256 hash of the defining string. return SHA256Hash(hashString); - } /***************************************************/ @@ -114,7 +122,7 @@ private static string SHA256Hash(string str) [Input("hc", "HashConfig, options for the hash calculation.")] [Input("nestingLevel", "Nesting level of the property.")] [Input("propertyPath", "(Optional) Indicates the 'property path' of the current object, e.g. `BH.oM.Structure.Elements.Bar.StartNode.Point.X`")] - private static string DefiningString(object obj, DistinctConfig dc, int fractionalDigits, int nestingLevel, string propertyPath = null) + private static string DefiningString(object obj, ComparisonConfig dc, int fractionalDigits, int nestingLevel, string propertyPath = null) { string composedString = ""; string tabs = new String('\t', nestingLevel); diff --git a/Diffing_Engine/Compute/DiffRevisions.cs b/Diffing_Engine/Compute/DiffRevisions.cs index b14e72d5c..fb54621d1 100644 --- a/Diffing_Engine/Compute/DiffRevisions.cs +++ b/Diffing_Engine/Compute/DiffRevisions.cs @@ -45,20 +45,20 @@ public static partial class Compute [Description("Computes the diffing for Revisions containing objects of any type (also non-BHoMObjects).")] [Input("pastRevision", "A past Revision. It must have been created before the 'followingRevision'.")] [Input("followingRevision", "A following Revision. It must have been created after 'pastRevision'.")] - [Input("diffConfig", "Sets configs such as properties to be ignored in the diffing, or enable/disable property-by-property diffing.")] - public static Diff DiffRevisions(Revision pastRevision, Revision followingRevision, DiffingConfig diffConfig = null) + [Input("DiffingConfig", "Sets configs such as properties to be ignored in the diffing, or enable/disable property-by-property diffing.")] + public static Diff DiffRevisions(Revision pastRevision, Revision followingRevision, DiffingConfig DiffingConfig = null) { - return DiffRevisionObjects(pastRevision.Objects, followingRevision.Objects, diffConfig); + return DiffRevisionObjects(pastRevision.Objects, followingRevision.Objects, DiffingConfig); } // Computes the diffing for IEnumerable. // For BHoMObjects, it assumes that they all have a HashFragment assigned (like when they have been passed through a Revision). // For non-BHoMObjects, it performs the VennDiagram comparision with a HashComparer. // Results for BHoMObjects and non are concatenated. - private static Diff DiffRevisionObjects(IEnumerable pastRevisionObjs, IEnumerable followingRevisionObjs, DiffingConfig diffConfig = null) + private static Diff DiffRevisionObjects(IEnumerable pastRevisionObjs, IEnumerable followingRevisionObjs, DiffingConfig DiffingConfig = null) { - // Set configurations if diffConfig is null. Clone it for immutability in the UI. - DiffingConfig diffConfigCopy = diffConfig == null ? new DiffingConfig() : diffConfig.DeepClone() as DiffingConfig; + // Set configurations if DiffingConfig is null. Clone it for immutability in the UI. + DiffingConfig diffConfigCopy = DiffingConfig == null ? new DiffingConfig() : DiffingConfig.DeepClone() as DiffingConfig; // Dispatch the objects in BHoMObjects and generic objects. IEnumerable prevObjs_BHoM = pastRevisionObjs.OfType(); @@ -75,7 +75,7 @@ private static Diff DiffRevisionObjects(IEnumerable pastRevisionObjs, IE // Compute the generic Diffing for the other objects. // This is left to the VennDiagram with a HashComparer. - VennDiagram vd = Engine.Data.Create.VennDiagram(prevObjs_nonBHoM, currObjs_nonBHoM, new HashComparer(diffConfig.DistinctConfig)); + VennDiagram vd = Engine.Data.Create.VennDiagram(prevObjs_nonBHoM, currObjs_nonBHoM, new HashComparer(DiffingConfig.ComparisonConfig)); // Concatenate the results of the two diffing operations. List allPrevObjs = new List(); @@ -95,10 +95,10 @@ private static Diff DiffRevisionObjects(IEnumerable pastRevisionObjs, IE } // Computes the Diffing for BHoMObjects that all have a HashFragment assigned (like when they have been passed through a Revision). - private static Diff DiffRevisionObjects(IEnumerable pastObjects, IEnumerable currentObjects, DiffingConfig diffConfig = null) + private static Diff DiffRevisionObjects(IEnumerable pastObjects, IEnumerable currentObjects, DiffingConfig DiffingConfig = null) { - // Set configurations if diffConfig is null. Clone it for immutability in the UI. - DiffingConfig dc = diffConfig == null ? new DiffingConfig() : diffConfig.DeepClone() as DiffingConfig; + // Set configurations if DiffingConfig is null. Clone it for immutability in the UI. + DiffingConfig dc = DiffingConfig == null ? new DiffingConfig() : DiffingConfig.DeepClone() as DiffingConfig; // Take the Revision's objects List currentObjs = currentObjects.ToList(); @@ -167,7 +167,7 @@ private static Diff DiffRevisionObjects(IEnumerable pastObjects, IE oldObjs = readObjs_dict.Keys.Except(CurrentObjs_withPreviousHash_dict.Keys) .Where(k => readObjs_dict.ContainsKey(k)).Select(k => readObjs_dict[k]).ToList(); - return new Diff(newObjs, oldObjs, modifiedObjs, diffConfig, objModifiedProps, unChanged); + return new Diff(newObjs, oldObjs, modifiedObjs, DiffingConfig, objModifiedProps, unChanged); } private static bool AllHaveRevisionFragment(this IEnumerable bHoMObjects) diff --git a/Diffing_Engine/Compute/DiffWithHash.cs b/Diffing_Engine/Compute/DiffWithHash.cs index 1406facad..674ae75ec 100644 --- a/Diffing_Engine/Compute/DiffWithHash.cs +++ b/Diffing_Engine/Compute/DiffWithHash.cs @@ -69,7 +69,7 @@ public static Diff DiffWithHash(IEnumerable pastObjects, IEnumerable vd = Engine.Data.Create.VennDiagram(pastObjects_cloned, currentObjects_cloned, new HashComparer(dc.DistinctConfig, true)); + VennDiagram vd = Engine.Data.Create.VennDiagram(pastObjects_cloned, currentObjects_cloned, new HashComparer(dc.ComparisonConfig, true)); return new Diff(vd.OnlySet2, vd.OnlySet1, null, dc, null, vd.Intersection); } diff --git a/Diffing_Engine/Compute/_IDiffing.cs b/Diffing_Engine/Compute/_IDiffing.cs index d001a3b8b..bce23f49c 100644 --- a/Diffing_Engine/Compute/_IDiffing.cs +++ b/Diffing_Engine/Compute/_IDiffing.cs @@ -106,7 +106,7 @@ public static Diff Diffing(IEnumerable pastObjs, IEnumerable fol if (diffingType == DiffingType.CustomDataId && !string.IsNullOrWhiteSpace(dc.CustomDataKey)) return DiffingError(diffingType); - if (bHoMObjects_past.Count() == pastObjs.Count() && bHoMObjects_following.Count() == followingObjs.Count()) + if (!string.IsNullOrWhiteSpace(dc.CustomDataKey) && bHoMObjects_past.Count() == pastObjs.Count() && bHoMObjects_following.Count() == followingObjs.Count()) { BH.Engine.Reflection.Compute.RecordNote($"Calling the diffing method '{nameof(DiffWithCustomId)}'."); return DiffWithCustomId(bHoMObjects_past, bHoMObjects_following, dc.CustomDataKey, dc); diff --git a/Diffing_Engine/Create/Delta.cs b/Diffing_Engine/Create/Delta.cs index 8bd0ff06d..b665cd4f6 100644 --- a/Diffing_Engine/Create/Delta.cs +++ b/Diffing_Engine/Create/Delta.cs @@ -45,10 +45,10 @@ public static partial class Create [Description("Returns a Delta object with the Diff between the two input Revisions, also called `Diff-based Delta`.")] [Input("previousRevision", "A previous Revision")] [Input("currentRevision", "A new Revision")] - [Input("diffConfig", "Sets configs such as properties to be ignored in the diffing, or enable/disable property-by-property diffing.\nBy default it takes the diffConfig property of the Revision. This input can be used to override it.")] - public static Delta Delta(Revision pastRevision, Revision currentRevision, DiffingConfig diffConfig = null, string comment = null) + [Input("DiffingConfig", "Sets configs such as properties to be ignored in the diffing, or enable/disable property-by-property diffing.\nBy default it takes the DiffingConfig property of the Revision. This input can be used to override it.")] + public static Delta Delta(Revision pastRevision, Revision currentRevision, DiffingConfig DiffingConfig = null, string comment = null) { - Diff diff = Compute.DiffRevisions(pastRevision, currentRevision, diffConfig); + Diff diff = Compute.DiffRevisions(pastRevision, currentRevision, DiffingConfig); return new Delta(pastRevision.StreamId, diff, pastRevision.RevisionId, currentRevision.RevisionId, DateTime.UtcNow.Ticks, m_Author, comment); } @@ -59,10 +59,10 @@ public static Delta Delta(Revision pastRevision, Revision currentRevision, Diffi [Description("Returns a Delta object containing all the objects of the input Revision, also called `Revision-Based Delta`.")] [Input("revision", "A new Revision")] - [Input("diffConfig", "Sets configs such as properties to be ignored in the diffing, or enable/disable property-by-property diffing.\nBy default it takes the diffConfig property of the Revision. This input can be used to override it.")] - public static Delta Delta(Revision revision, DiffingConfig diffConfig = null, string comment = null) + [Input("DiffingConfig", "Sets configs such as properties to be ignored in the diffing, or enable/disable property-by-property diffing.\nBy default it takes the DiffingConfig property of the Revision. This input can be used to override it.")] + public static Delta Delta(Revision revision, DiffingConfig DiffingConfig = null, string comment = null) { - Diff diff = Compute.DiffRevisions(null, revision, diffConfig); + Diff diff = Compute.DiffRevisions(null, revision, DiffingConfig); return new Delta(revision.StreamId, diff, revision.RevisionId, new Guid(), DateTime.UtcNow.Ticks, m_Author, comment); } @@ -72,12 +72,12 @@ public static Delta Delta(Revision revision, DiffingConfig diffConfig = null, st [Input("streamId", "Id of the Stream that will own the revision produced by this Delta.")] [Input("revisionName", "Name to be assigned to the Revision that this Delta will produce.")] [Input("comment", "Comment to be stored along the Revision that this Delta will produce.")] - [Input("diffConfig", "Sets configs such as properties to be ignored in the diffing, or enable/disable property-by-property diffing.\nBy default it takes the diffConfig property of the Revision. This input can be used to override it.")] + [Input("DiffingConfig", "Sets configs such as properties to be ignored in the diffing, or enable/disable property-by-property diffing.\nBy default it takes the DiffingConfig property of the Revision. This input can be used to override it.")] public static Delta Delta(List objects, object streamId, string revisionName = null, - string comment = null, DiffingConfig diffConfig = null) + string comment = null, DiffingConfig DiffingConfig = null) { - Revision revision = Create.Revision(objects, streamId, revisionName, comment, diffConfig); - return Delta(revision, diffConfig, comment); + Revision revision = Create.Revision(objects, streamId, revisionName, comment, DiffingConfig); + return Delta(revision, DiffingConfig, comment); } [Description("Returns a Delta object based on the provided Diff.")] @@ -85,8 +85,8 @@ public static Delta Delta(List objects, object streamId, string rev [Input("streamId", "Id of the Stream that will own the revision produced by this Delta.")] [Input("revisionName", "Name to be assigned to the Revision that this Delta will produce.")] [Input("comment", "Comment to be stored along the Revision that this Delta will produce.")] - [Input("diffConfig", "Sets configs such as properties to be ignored in the diffing, or enable/disable property-by-property diffing.\nBy default it takes the diffConfig property of the Revision. This input can be used to override it.")] - public static Delta Delta(Diff diff, object streamId, Guid revision_from, string comment = null, DiffingConfig diffConfig = null) + [Input("DiffingConfig", "Sets configs such as properties to be ignored in the diffing, or enable/disable property-by-property diffing.\nBy default it takes the DiffingConfig property of the Revision. This input can be used to override it.")] + public static Delta Delta(Diff diff, object streamId, Guid revision_from, string comment = null, DiffingConfig DiffingConfig = null) { return new Delta(ProcessStreamId(streamId), diff, revision_from, new Guid(), DateTime.UtcNow.Ticks, m_Author, comment); } diff --git a/Diffing_Engine/Create/DiffConfig.cs b/Diffing_Engine/Create/DiffConfig.cs index 90077023a..d217d63a9 100644 --- a/Diffing_Engine/Create/DiffConfig.cs +++ b/Diffing_Engine/Create/DiffConfig.cs @@ -59,7 +59,7 @@ public static DiffingConfig DiffingConfig(bool enablePropertyDiffing = false, bo { EnablePropertyDiffing = enablePropertyDiffing, IncludeUnchangedObjects = storeUnchangedObjects, - DistinctConfig = new DistinctConfig() + ComparisonConfig = new ComparisonConfig() { PropertyNamesToConsider = propertyNamesToConsider, PropertyNameExceptions = propertiesToIgnore, diff --git a/Diffing_Engine/Modify/CombineDiffs.cs b/Diffing_Engine/Modify/CombineDiffs.cs index 318db2142..f8d01a420 100644 --- a/Diffing_Engine/Modify/CombineDiffs.cs +++ b/Diffing_Engine/Modify/CombineDiffs.cs @@ -49,12 +49,16 @@ public static Diff CombineDiffs(this Diff diff, Diff toAdd) return diff; return new Diff( - diff.AddedObjects.Concat(toAdd.AddedObjects), - diff.RemovedObjects.Concat(toAdd.RemovedObjects), - diff.ModifiedObjects.Concat(toAdd.ModifiedObjects), + diff.AddedObjects != null ? diff.AddedObjects.Concat(toAdd.AddedObjects ?? new List()) : toAdd.AddedObjects ?? new List(), + diff.RemovedObjects != null ? diff.RemovedObjects.Concat(toAdd.RemovedObjects ?? new List()) : toAdd.RemovedObjects ?? new List(), + diff.ModifiedObjects != null ? diff.ModifiedObjects.Concat(toAdd.ModifiedObjects ?? new List()) : toAdd.ModifiedObjects ?? new List(), diff.DiffingConfig, - diff.ModifiedPropsPerObject.Concat(toAdd.ModifiedPropsPerObject).ToDictionary(x => x.Key, x => x.Value), - diff.UnchangedObjects.Concat(toAdd.UnchangedObjects) + diff.ModifiedPropsPerObject != null ? + diff.ModifiedPropsPerObject + .Concat(toAdd.ModifiedPropsPerObject ?? new Dictionary>>()) + .ToDictionary(x => x.Key, x => x.Value) + : toAdd.ModifiedPropsPerObject ?? new Dictionary>>(), + diff.UnchangedObjects != null ? diff.UnchangedObjects.Concat(toAdd.UnchangedObjects ?? new List()) : toAdd.UnchangedObjects ?? new List() ); } } diff --git a/Diffing_Engine/Modify/RemoveDuplicatesByHash.cs b/Diffing_Engine/Modify/RemoveDuplicatesByHash.cs index 157b0f32a..b8c77ac37 100644 --- a/Diffing_Engine/Modify/RemoveDuplicatesByHash.cs +++ b/Diffing_Engine/Modify/RemoveDuplicatesByHash.cs @@ -43,7 +43,7 @@ public static partial class Modify [Input("objects", "Collection of objects whose duplicates have to be removed. If they don't already have an Hash assigned, it will be calculated.")] [Input("distinctConfig", "Settings to determine the uniqueness of an Object.")] [Input("useExistingHash", "If true, if objects already have a HashFragment, use that. If false, recompute the hash for all objects.")] - public static IEnumerable RemoveDuplicatesByHash(IEnumerable objects, DistinctConfig distinctConfig = null, bool useExistingHash = true) where T : IBHoMObject + public static IEnumerable RemoveDuplicatesByHash(IEnumerable objects, ComparisonConfig distinctConfig = null, bool useExistingHash = true) where T : IBHoMObject { return objects.GroupBy(obj => { diff --git a/Diffing_Engine/Modify/SetHashFragment.cs b/Diffing_Engine/Modify/SetHashFragment.cs index 4cd2c0fba..4d6fbaa4a 100644 --- a/Diffing_Engine/Modify/SetHashFragment.cs +++ b/Diffing_Engine/Modify/SetHashFragment.cs @@ -40,13 +40,13 @@ namespace BH.Engine.Diffing public static partial class Modify { [Description("Clones the IBHoMObjects, computes their hash and stores it in a HashFragment.")] - public static List SetHashFragment(this IEnumerable objs, DistinctConfig distinctConfig = null) where T : IBHoMObject + public static List SetHashFragment(this IEnumerable objs, ComparisonConfig distinctConfig = null) where T : IBHoMObject { // Clone the current objects to preserve immutability List objs_cloned = new List(); // Set configurations if diffConfig is null - distinctConfig = distinctConfig == null ? new DistinctConfig() : distinctConfig; + distinctConfig = distinctConfig == null ? new ComparisonConfig() : distinctConfig; // Calculate and set the object hashes foreach (var obj in objs) @@ -56,13 +56,13 @@ public static List SetHashFragment(this IEnumerable objs, DistinctConfi } [Description("Clones the IBHoMObject, computes their hash and stores it in a HashFragment.")] - public static T SetHashFragment(T obj, DistinctConfig distinctConfig = null) where T : IBHoMObject + public static T SetHashFragment(T obj, ComparisonConfig distinctConfig = null) where T : IBHoMObject { // Clone the current object to preserve immutability T obj_cloned = BH.Engine.Base.Query.DeepClone(obj); // Set configurations if diffConfig is null - distinctConfig = distinctConfig == null ? new DistinctConfig() : distinctConfig; + distinctConfig = distinctConfig == null ? new ComparisonConfig() : distinctConfig; // Calculate and set the object hashes string hash = obj_cloned.Hash(distinctConfig); diff --git a/Diffing_Engine/Modify/SetRevisionFragment.cs b/Diffing_Engine/Modify/SetRevisionFragment.cs index c8409fcd2..12f0cdd40 100644 --- a/Diffing_Engine/Modify/SetRevisionFragment.cs +++ b/Diffing_Engine/Modify/SetRevisionFragment.cs @@ -41,18 +41,18 @@ public static partial class Modify { [Description("Clones the IBHoMObjects, computes their hash and stores it in a RevisionFragment. " + "If the object already has a RevisionFragment, it computes the current one and keeps the old one in the `previousHash` of the RevisionFragment.")] - public static List SetRevisionFragment(this IEnumerable objs, DiffingConfig diffConfig = null) where T : IBHoMObject + public static List SetRevisionFragment(this IEnumerable objs, DiffingConfig DiffingConfig = null) where T : IBHoMObject { // Clone the current objects to preserve immutability List objs_cloned = new List(); - // Set configurations if diffConfig is null - diffConfig = diffConfig == null ? new DiffingConfig() : diffConfig; + // Set configurations if DiffingConfig is null + DiffingConfig = DiffingConfig == null ? new DiffingConfig() : DiffingConfig; // Calculate and set the object hashes foreach (var obj in objs) { - objs_cloned.Add(SetRevisionFragment(obj, diffConfig)); + objs_cloned.Add(SetRevisionFragment(obj, DiffingConfig)); } return objs_cloned; @@ -60,16 +60,16 @@ public static List SetRevisionFragment(this IEnumerable objs, DiffingCo [Description("Clones the IBHoMObject, computes their hash and stores it in a RevisionFragment. " + "If the object already has a RevisionFragment, it computes the current one and keeps the old one in the `previousHash` of the RevisionFragment.")] - public static T SetRevisionFragment(T obj, DiffingConfig diffConfig = null) where T : IBHoMObject + public static T SetRevisionFragment(T obj, DiffingConfig DiffingConfig = null) where T : IBHoMObject { // Clone the current object to preserve immutability T obj_cloned = BH.Engine.Base.Query.DeepClone(obj); - // Set configurations if diffConfig is null - diffConfig = diffConfig == null ? new DiffingConfig() : diffConfig; + // Set configurations if DiffingConfig is null + DiffingConfig = DiffingConfig == null ? new DiffingConfig() : DiffingConfig; // Calculate and set the object hashes - string hash = obj_cloned.Hash(diffConfig.DistinctConfig); + string hash = obj_cloned.Hash(DiffingConfig.ComparisonConfig); RevisionFragment existingFragm = obj_cloned.RevisionFragment(); diff --git a/Diffing_Engine/Objects/EqualityComparers/HashComparer.cs b/Diffing_Engine/Objects/EqualityComparers/HashComparer.cs index 6efa72e02..142309147 100644 --- a/Diffing_Engine/Objects/EqualityComparers/HashComparer.cs +++ b/Diffing_Engine/Objects/EqualityComparers/HashComparer.cs @@ -49,14 +49,14 @@ public class HashComparer : IEqualityComparer public bool StoreHash { get; set; } = false; [Description("If the objects are IObjects, computes the BHoM Hash using these configurations.")] - public DistinctConfig DistinctConfig { get; set; } = new DistinctConfig(); + public ComparisonConfig ComparisonConfig { get; set; } = new ComparisonConfig(); [Input("distinctConfig", "If the objects are IObjects, computes the BHoM Hash using these configurations.")] [Input("storeHash", "If true, stores the computed hash for input BHoMObjects as a new HashFragment.")] - public HashComparer(DistinctConfig distinctConfig = null, bool storeHash = false) + public HashComparer(ComparisonConfig distinctConfig = null, bool storeHash = false) { if (distinctConfig != null) - DistinctConfig = distinctConfig; + ComparisonConfig = distinctConfig; StoreHash = storeHash; } @@ -103,7 +103,7 @@ public int GetHashCode(T obj) IObject iObj = obj as IObject; if (iObj != null) - return iObj.Hash(DistinctConfig).GetHashCode(); + return iObj.Hash(ComparisonConfig).GetHashCode(); return obj.GetHashCode(); } diff --git a/Diffing_Engine/Query/DifferentProperties.cs b/Diffing_Engine/Query/DifferentProperties.cs index 5de35c2f6..b7a9d0cfd 100644 --- a/Diffing_Engine/Query/DifferentProperties.cs +++ b/Diffing_Engine/Query/DifferentProperties.cs @@ -38,12 +38,12 @@ namespace BH.Engine.Diffing public static partial class Query { [Description("Checks two BHoMObjects property by property and returns the differences")] - [Input("diffConfig", "Config to be used for the comparison. Can set numeric tolerance, wheter to check the guid, if custom data should be ignored and if any additional properties should be ignored")] + [Input("DiffingConfig", "Config to be used for the comparison. Can set numeric tolerance, wheter to check the guid, if custom data should be ignored and if any additional properties should be ignored")] [Output("Dictionary whose key is the name of the property, and value is a tuple with its value in obj1 and obj2.")] - public static Dictionary> DifferentProperties(this object obj1, object obj2, DiffingConfig diffConfig = null) + public static Dictionary> DifferentProperties(this object obj1, object obj2, DiffingConfig DiffingConfig = null) { - // Set configurations if diffConfig is null. Clone it for immutability in the UI. - DiffingConfig dc = diffConfig == null ? new DiffingConfig() : diffConfig.DeepClone() as DiffingConfig; + // Set configurations if DiffingConfig is null. Clone it for immutability in the UI. + DiffingConfig dc = DiffingConfig == null ? new DiffingConfig() : DiffingConfig.DeepClone() as DiffingConfig; object obj1Copy = obj1.DeepClone(); object obj2Copy = obj2.DeepClone(); @@ -54,15 +54,15 @@ public static Dictionary> DifferentProperties(this // General configurations. comparer.Config.MaxDifferences = dc.MaxPropertyDifferences; - comparer.Config.DoublePrecision = dc.DistinctConfig.NumericTolerance; + comparer.Config.DoublePrecision = dc.ComparisonConfig.NumericTolerance; // Set the properties to be ignored. - if (!dc.DistinctConfig.PropertyNameExceptions?.Contains("BHoM_Guid") ?? true) - dc.DistinctConfig.PropertyNameExceptions.Add("BHoM_Guid"); - // the above should be replaced by BH.Engine.Reflection.Compute.RecordWarning($"`BHoM_Guid` should generally be ignored when computing the diffing. Consider adding it to the {nameof(diffConfig.PropertiesToIgnore)}."); + if (!dc.ComparisonConfig.PropertyNameExceptions?.Contains("BHoM_Guid") ?? true) + dc.ComparisonConfig.PropertyNameExceptions.Add("BHoM_Guid"); + // the above should be replaced by BH.Engine.Reflection.Compute.RecordWarning($"`BHoM_Guid` should generally be ignored when computing the diffing. Consider adding it to the {nameof(DiffingConfig.PropertiesToIgnore)}."); // when the bug in the auto Create() method ("auto-property initialisers for ByRef values like lists do not populate default values") is resolved. - comparer.Config.MembersToIgnore = dc.DistinctConfig.PropertyNameExceptions; + comparer.Config.MembersToIgnore = dc.ComparisonConfig.PropertyNameExceptions; // Removes the CustomData to be ignored. var bhomobj1 = (obj1Copy as IBHoMObject); @@ -70,13 +70,13 @@ public static Dictionary> DifferentProperties(this if (bhomobj1 != null) { - dc.DistinctConfig.CustomdataKeysExceptions.ForEach(k => bhomobj1.CustomData.Remove(k)); + dc.ComparisonConfig.CustomdataKeysExceptions.ForEach(k => bhomobj1.CustomData.Remove(k)); obj1Copy = bhomobj1; } if (bhomobj2 != null) { - dc.DistinctConfig.CustomdataKeysExceptions.ForEach(k => bhomobj2.CustomData.Remove(k)); + dc.ComparisonConfig.CustomdataKeysExceptions.ForEach(k => bhomobj2.CustomData.Remove(k)); obj2Copy = bhomobj2; } @@ -108,7 +108,7 @@ public static Dictionary> DifferentProperties(this propertyName = splittedName.FirstOrDefault() + $"['{keyName}']." + splittedName.Last(); } - if (dc.DistinctConfig.PropertyNameExceptions.Any() && !dc.DistinctConfig.PropertyNameExceptions.Contains(difference.PropertyName)) + if (dc.ComparisonConfig.PropertyNameExceptions.Any() && !dc.ComparisonConfig.PropertyNameExceptions.Contains(difference.PropertyName)) dict[propertyName] = new Tuple(difference.Object1, difference.Object2); } diff --git a/Environment_Engine/Query/HasMergeablePropertiesWith.cs b/Environment_Engine/Query/HasMergeablePropertiesWith.cs index e1f2a0f36..5d15e530a 100644 --- a/Environment_Engine/Query/HasMergeablePropertiesWith.cs +++ b/Environment_Engine/Query/HasMergeablePropertiesWith.cs @@ -58,7 +58,7 @@ public static bool HasMergeablePropertiesWith(Panel element, Panel other) { DiffingConfig config = new DiffingConfig() { - DistinctConfig = new DistinctConfig() + ComparisonConfig = new ComparisonConfig() { PropertyNameExceptions = new List { @@ -84,7 +84,7 @@ public static bool HasMergeablePropertiesWith(Opening element, Opening other) { DiffingConfig config = new DiffingConfig() { - DistinctConfig = new DistinctConfig() + ComparisonConfig = new ComparisonConfig() { PropertyNameExceptions = new List { @@ -119,7 +119,7 @@ public static bool HasMergeablePropertiesWith(Space element, Space other) { DiffingConfig config = new DiffingConfig() { - DistinctConfig = new DistinctConfig() + ComparisonConfig = new ComparisonConfig() { PropertyNameExceptions = new List { diff --git a/Physical_Engine/Query/UniqueConstructions.cs b/Physical_Engine/Query/UniqueConstructions.cs index d3d3f1d65..5b9203c38 100644 --- a/Physical_Engine/Query/UniqueConstructions.cs +++ b/Physical_Engine/Query/UniqueConstructions.cs @@ -44,7 +44,7 @@ public static partial class Query [Output("uniqueConstructions", "A collection of unique Construction objects")] public static List UniqueConstructions(this List constructions, bool includeConstructionName = false) { - DistinctConfig dc = new DistinctConfig() + ComparisonConfig dc = new ComparisonConfig() { PropertyNameExceptions = new List { From 24090493460e564e74e108eb0f54c569fbf1566a Mon Sep 17 00:00:00 2001 From: Alessio Lombardi Date: Mon, 16 Nov 2020 10:59:26 +0000 Subject: [PATCH 08/28] Update SetHashFragment.cs --- Diffing_Engine/Modify/SetHashFragment.cs | 6 ------ 1 file changed, 6 deletions(-) diff --git a/Diffing_Engine/Modify/SetHashFragment.cs b/Diffing_Engine/Modify/SetHashFragment.cs index 4d6fbaa4a..0380b25b4 100644 --- a/Diffing_Engine/Modify/SetHashFragment.cs +++ b/Diffing_Engine/Modify/SetHashFragment.cs @@ -45,9 +45,6 @@ public static List SetHashFragment(this IEnumerable objs, ComparisonCon // Clone the current objects to preserve immutability List objs_cloned = new List(); - // Set configurations if diffConfig is null - distinctConfig = distinctConfig == null ? new ComparisonConfig() : distinctConfig; - // Calculate and set the object hashes foreach (var obj in objs) objs_cloned.Add(SetHashFragment(obj, distinctConfig)); @@ -61,9 +58,6 @@ public static T SetHashFragment(T obj, ComparisonConfig distinctConfig = null // Clone the current object to preserve immutability T obj_cloned = BH.Engine.Base.Query.DeepClone(obj); - // Set configurations if diffConfig is null - distinctConfig = distinctConfig == null ? new ComparisonConfig() : distinctConfig; - // Calculate and set the object hashes string hash = obj_cloned.Hash(distinctConfig); obj_cloned.Fragments.AddOrReplace(new HashFragment() { Hash = hash }); From df91b2893e8d19846432bc64126c02ceb2434bbc Mon Sep 17 00:00:00 2001 From: Alessio Lombardi Date: Mon, 16 Nov 2020 16:05:22 +0000 Subject: [PATCH 09/28] Minor renaming. --- .../Create/Graph/DistinctConfig.cs | 4 +- Analytical_Engine/Create/Graph/Graph.cs | 18 +++--- .../Query/Graph/DistinctEntities.cs | 8 +-- BHoM_Engine/Query/Hash.cs | 58 ++++++++++--------- .../Modify/RemoveDuplicatesByHash.cs | 6 +- Diffing_Engine/Modify/SetHashFragment.cs | 8 +-- .../Objects/EqualityComparers/HashComparer.cs | 8 +-- 7 files changed, 56 insertions(+), 54 deletions(-) diff --git a/Analytical_Engine/Create/Graph/DistinctConfig.cs b/Analytical_Engine/Create/Graph/DistinctConfig.cs index 8998fd096..c699c3391 100644 --- a/Analytical_Engine/Create/Graph/DistinctConfig.cs +++ b/Analytical_Engine/Create/Graph/DistinctConfig.cs @@ -24,13 +24,13 @@ public static partial class Create "\nE.g., if you input 'Name' only the differences in terms of name will be returned.")] public static ComparisonConfig ComparisonConfig(double numericTolerance = oM.Geometry.Tolerance.Distance, List propertyNamesToConsider = null) { - ComparisonConfig distinctConfig = new ComparisonConfig() + ComparisonConfig cc = new ComparisonConfig() { NumericTolerance = numericTolerance, PropertyNamesToConsider = propertyNamesToConsider ?? new List(), }; - return distinctConfig; + return cc; } } } diff --git a/Analytical_Engine/Create/Graph/Graph.cs b/Analytical_Engine/Create/Graph/Graph.cs index 21b4224b5..4ce6af40d 100644 --- a/Analytical_Engine/Create/Graph/Graph.cs +++ b/Analytical_Engine/Create/Graph/Graph.cs @@ -47,22 +47,22 @@ public static partial class Create [Description("Create a graph from a collection of IBHoMObjects, property names and decimal places to determine unique graph entities.")] [Input("entities", "A collection of IBHoMOBjects to use as Graph entities. Entities should include DependencyFragments to determine the Graph Relations.")] - [Input("distinctConfig", "Settings to determine the uniqueness of entities.")] + [Input("comparisonConfig", "Settings to determine the uniqueness of entities.")] [Output("graph", "Graph.")] - public static Graph Graph(List entities, ComparisonConfig distinctConfig = null) + public static Graph Graph(List entities, ComparisonConfig comparisonConfig = null) { - return Graph(entities, new List(), distinctConfig); + return Graph(entities, new List(), comparisonConfig); } /***************************************************/ [Description("Create a graph from a collection of IRelations, property names and decimal places to determine unique graph entities.")] [Input("relations", "A collection of IRelations to use as Graph Relations. Relations should include sub Graphs containing the entities to be used in the Graph.")] - [Input("distinctConfig", "Settings to determine the uniqueness of entities.")] + [Input("comparisonConfig", "Settings to determine the uniqueness of entities.")] [Output("graph", "Graph.")] - public static Graph Graph(List relations, ComparisonConfig distinctConfig = null) + public static Graph Graph(List relations, ComparisonConfig comparisonConfig = null) { - return Graph(new List(), relations, distinctConfig); + return Graph(new List(), relations, comparisonConfig); } /***************************************************/ @@ -70,9 +70,9 @@ public static Graph Graph(List relations, ComparisonConfig distinctCo [Description("Create a graph from a collection of IBHoMObjects, a collection of IRelations, property names and decimal places to determine unique graph entities.")] [Input("entities", "Optional collection of IBHoMOBjects to use as Graph entities. Entities can include DependencyFragments to determine the Graph Relations.")] [Input("relations", "Optional collection of IRelations to use as Graph Relations. Relations can include sub Graphs containing the entities to be used in the Graph.")] - [Input("distinctConfig", "Settings to determine the uniqueness of entities.")] + [Input("comparisonConfig", "Settings to determine the uniqueness of entities.")] [Output("graph", "Graph.")] - public static Graph Graph(List entities = null, List relations = null, ComparisonConfig distinctConfig = null) + public static Graph Graph(List entities = null, List relations = null, ComparisonConfig comparisonConfig = null) { Graph graph = new Graph(); @@ -88,7 +88,7 @@ public static Graph Graph(List entities = null, List rel return graph; } - m_MatchedObjects = Query.DistinctEntities(clonedEntities, distinctConfig); + m_MatchedObjects = Query.DistinctEntities(clonedEntities, comparisonConfig); //convert dependency fragments attached to entities and add to relations clonedEntities.ForEach(ent => clonedRelations.AddRange(ent.ToRelation())); diff --git a/Analytical_Engine/Query/Graph/DistinctEntities.cs b/Analytical_Engine/Query/Graph/DistinctEntities.cs index 4726a6bb0..98e8ab7d5 100644 --- a/Analytical_Engine/Query/Graph/DistinctEntities.cs +++ b/Analytical_Engine/Query/Graph/DistinctEntities.cs @@ -43,16 +43,16 @@ public static partial class Query [Description("Identifies unique objects from a collection IBHoMObjects using hash comparison.")] [Input("entities", "A collection of IBHoMObjects from which unique instances are identified.")] - [Input("distinctConfig", "Configuration of diffing used to find unique entities.")] + [Input("comparisonConfig", "Configuration of diffing used to find unique entities.")] [Output("unique entities", "A Dictionary replacement map of the entities where the keys are the Guid of the original entity and the Values the matching IBHoMObject entity.")] - public static Dictionary DistinctEntities(this List entities, ComparisonConfig distinctConfig = null) // this is not a diff, it's finding unique entities + public static Dictionary DistinctEntities(this List entities, ComparisonConfig comparisonConfig = null) // this is not a diff, it's finding unique entities { - ComparisonConfig dc = distinctConfig ?? new ComparisonConfig(); + ComparisonConfig cc = comparisonConfig ?? new ComparisonConfig(); Dictionary replaceMap = new Dictionary(); Dictionary objectHash = new Dictionary(); - HashComparer hashComparer = new HashComparer(dc); + HashComparer hashComparer = new HashComparer(cc); foreach (KeyValuePair entityA in objectHash) { diff --git a/BHoM_Engine/Query/Hash.cs b/BHoM_Engine/Query/Hash.cs index 62081d41a..05424ee82 100644 --- a/BHoM_Engine/Query/Hash.cs +++ b/BHoM_Engine/Query/Hash.cs @@ -44,30 +44,32 @@ public static partial class Query /**** Public Methods ****/ /***************************************************/ - [Description("Computes a Hash code for the iObject. The hash uniquely represents an object's state based on its combined properties and their values.")] + [Description("Computes a Hash code for the iObject. The hash uniquely represents an object's state, based on its properties and their values. It can be used for comparisons." + + "\nYou can change how the hash is computed by changing the settings in the ComparisonConfig.")] [Input("iObj", "iObject the hash code should be calculated for.")] - public static string Hash(this IObject iObj, ComparisonConfig distinctConfig = null) + [Input("comparisonConfig", "Configure how the hash is computed.")] + public static string Hash(this IObject iObj, ComparisonConfig comparisonConfig = null) { // ------ SET UP OF CONFIGURATION ------ // Make sure we always have a config object. Clone for immutability. - ComparisonConfig hc = distinctConfig == null ? new ComparisonConfig() : distinctConfig.DeepClone(); + ComparisonConfig cc = comparisonConfig == null ? new ComparisonConfig() : comparisonConfig.DeepClone(); // Make sure that "BHoM_Guid" is added to the propertyNameExceptions of the config. - hc.PropertyNameExceptions = hc.PropertyNameExceptions ?? new List(); - if (!hc.PropertyNameExceptions.Contains(nameof(BHoMObject.BHoM_Guid))) - hc.PropertyNameExceptions.Add(nameof(BHoMObject.BHoM_Guid)); + cc.PropertyNameExceptions = cc.PropertyNameExceptions ?? new List(); + if (!cc.PropertyNameExceptions.Contains(nameof(BHoMObject.BHoM_Guid))) + cc.PropertyNameExceptions.Add(nameof(BHoMObject.BHoM_Guid)); // Convert from the Numeric Tolerance to fractionalDigits (required for the hash). - int fractionalDigits = Math.Abs(Convert.ToInt32(Math.Log10(hc.NumericTolerance))); + int fractionalDigits = Math.Abs(Convert.ToInt32(Math.Log10(cc.NumericTolerance))); // Process the "PropertiesToInclude" property. - if (hc.PropertyNamesToConsider?.Any() ?? false) + if (cc.PropertyNamesToConsider?.Any() ?? false) { // The hash computation can only consider "exceptions". // We need to retrieve all the object properties, intersect them with PropertiesToInclude, and treat all those remaining as "exceptions". - IEnumerable exceptions = BH.Engine.Reflection.Query.PropertyNames(iObj).Except(hc.PropertyNamesToConsider); - hc.PropertyNameExceptions.AddRange(exceptions); + IEnumerable exceptions = BH.Engine.Reflection.Query.PropertyNames(iObj).Except(cc.PropertyNamesToConsider); + cc.PropertyNameExceptions.AddRange(exceptions); } // ----- SET UP OF INPUT OBJECT ----- @@ -87,7 +89,7 @@ public static string Hash(this IObject iObj, ComparisonConfig distinctConfig = n // ----- HASH ----- // Compute the defining string. - string hashString = DefiningString(iObj_copy, hc, fractionalDigits, 0); + string hashString = DefiningString(iObj_copy, cc, fractionalDigits, 0); if (string.IsNullOrWhiteSpace(hashString)) throw new Exception("Error computing the defining string of the object."); @@ -122,7 +124,7 @@ private static string SHA256Hash(string str) [Input("hc", "HashConfig, options for the hash calculation.")] [Input("nestingLevel", "Nesting level of the property.")] [Input("propertyPath", "(Optional) Indicates the 'property path' of the current object, e.g. `BH.oM.Structure.Elements.Bar.StartNode.Point.X`")] - private static string DefiningString(object obj, ComparisonConfig dc, int fractionalDigits, int nestingLevel, string propertyPath = null) + private static string DefiningString(object obj, ComparisonConfig cc, int fractionalDigits, int nestingLevel, string propertyPath = null) { string composedString = ""; string tabs = new String('\t', nestingLevel); @@ -130,9 +132,9 @@ private static string DefiningString(object obj, ComparisonConfig dc, int fracti Type type = obj?.GetType(); if (type == null - || (dc.TypeExceptions != null && dc.TypeExceptions.Contains(type)) - || (dc.NamespaceExceptions != null && dc.NamespaceExceptions.Where(ex => type.Namespace.Contains(ex)).Any()) - || nestingLevel >= dc.MaxNesting) + || (cc.TypeExceptions != null && cc.TypeExceptions.Contains(type)) + || (cc.NamespaceExceptions != null && cc.NamespaceExceptions.Where(ex => type.Namespace.Contains(ex)).Any()) + || nestingLevel >= cc.MaxNesting) { return composedString; } @@ -146,7 +148,7 @@ private static string DefiningString(object obj, ComparisonConfig dc, int fracti else if (type.IsArray) { foreach (var element in (obj as dynamic)) - composedString += $"\n{tabs}" + DefiningString(element, dc, fractionalDigits, nestingLevel + 1, propertyPath); + composedString += $"\n{tabs}" + DefiningString(element, cc, fractionalDigits, nestingLevel + 1, propertyPath); } else if (typeof(IDictionary).IsAssignableFrom(type)) { @@ -156,16 +158,16 @@ private static string DefiningString(object obj, ComparisonConfig dc, int fracti foreach (DictionaryEntry entry in dic) { - if (isCustomDataDic && dc.CustomdataKeysExceptions.Contains(entry.Key)) + if (isCustomDataDic && cc.CustomdataKeysExceptions.Contains(entry.Key)) continue; - composedString += $"\n{tabs}" + $"[{entry.Key.GetType().FullName}]\n{tabs}{entry.Key}:\n { DefiningString(entry.Value, dc, fractionalDigits, nestingLevel + 1, propertyPath)}"; + composedString += $"\n{tabs}" + $"[{entry.Key.GetType().FullName}]\n{tabs}{entry.Key}:\n { DefiningString(entry.Value, cc, fractionalDigits, nestingLevel + 1, propertyPath)}"; } } else if (typeof(IEnumerable).IsAssignableFrom(type) || typeof(IList).IsAssignableFrom(type) || typeof(ICollection).IsAssignableFrom(type)) { foreach (var element in (obj as dynamic)) - composedString += $"\n{tabs}" + DefiningString(element, dc, fractionalDigits, nestingLevel + 1, propertyPath); + composedString += $"\n{tabs}" + DefiningString(element, cc, fractionalDigits, nestingLevel + 1, propertyPath); } else if (type.FullName.Contains("System.Collections.Generic.ObjectEqualityComparer`1")) { @@ -174,7 +176,7 @@ private static string DefiningString(object obj, ComparisonConfig dc, int fracti else if (type == typeof(System.Data.DataTable)) { DataTable dt = obj as DataTable; - return composedString += $"{type.FullName} {string.Join(", ", dt.Columns.OfType().Select(c => c.ColumnName))}\n{tabs}" + DefiningString(dt.AsEnumerable(), dc, fractionalDigits, nestingLevel + 1, propertyPath); + return composedString += $"{type.FullName} {string.Join(", ", dt.Columns.OfType().Select(c => c.ColumnName))}\n{tabs}" + DefiningString(dt.AsEnumerable(), cc, fractionalDigits, nestingLevel + 1, propertyPath); } else if (typeof(IObject).IsAssignableFrom(type)) { @@ -182,13 +184,13 @@ private static string DefiningString(object obj, ComparisonConfig dc, int fracti foreach (PropertyInfo prop in properties) { - bool isInPropertyNameExceptions = dc.PropertyNameExceptions?.Count > 0 && dc.PropertyNameExceptions.Where(ex => prop.Name.Contains(ex)).Any(); - bool isInPropertyFullNameExceptions = dc.PropertyFullNameExceptions?.Count > 0 && dc.PropertyFullNameExceptions.Where(ex => new WildcardPattern(ex).IsMatch(prop.Name + "." + prop.DeclaringType.FullName)).Any(); + bool isInPropertyNameExceptions = cc.PropertyNameExceptions?.Count > 0 && cc.PropertyNameExceptions.Where(ex => prop.Name.Contains(ex)).Any(); + bool isInPropertyFullNameExceptions = cc.PropertyFullNameExceptions?.Count > 0 && cc.PropertyFullNameExceptions.Where(ex => new WildcardPattern(ex).IsMatch(prop.Name + "." + prop.DeclaringType.FullName)).Any(); if (isInPropertyNameExceptions || isInPropertyFullNameExceptions) continue; - if (dc.PropertyNamesToConsider?.Count() > 0 && !dc.PropertyNamesToConsider.Contains(prop.Name)) + if (cc.PropertyNamesToConsider?.Count() > 0 && !cc.PropertyNamesToConsider.Contains(prop.Name)) continue; object propValue = prop.GetValue(obj); @@ -201,14 +203,14 @@ private static string DefiningString(object obj, ComparisonConfig dc, int fracti string outString = ""; - if (dc.FractionalDigitsPerProperty != null && + if (cc.FractionalDigitsPerProperty != null && prop.PropertyType == typeof(double) || prop.PropertyType == typeof(decimal) || prop.PropertyType == typeof(float)) { Dictionary matches = new Dictionary(); string path = propertyPath + "." + prop.Name; - foreach (var kv in dc.FractionalDigitsPerProperty) + foreach (var kv in cc.FractionalDigitsPerProperty) { if (path.Contains(kv.Key) || new WildcardPattern(kv.Key).IsMatch(path)) @@ -216,14 +218,14 @@ private static string DefiningString(object obj, ComparisonConfig dc, int fracti } if (matches.Count() > 1) - throw new ArgumentException($"Too many matching results obtained with specified {nameof(dc.FractionalDigitsPerProperty)}."); + throw new ArgumentException($"Too many matching results obtained with specified {nameof(cc.FractionalDigitsPerProperty)}."); int fracDigits = matches.Count() == 1 ? matches.FirstOrDefault().Value : fractionalDigits; - outString = DefiningString(propValue, dc, fracDigits, nestingLevel + 1, path) ?? ""; + outString = DefiningString(propValue, cc, fracDigits, nestingLevel + 1, path) ?? ""; } else - outString = DefiningString(propValue, dc, fractionalDigits, nestingLevel + 1, propertyPath) ?? ""; + outString = DefiningString(propValue, cc, fractionalDigits, nestingLevel + 1, propertyPath) ?? ""; if (!string.IsNullOrWhiteSpace(outString)) composedString += $"\n{tabs}" + $"{type.FullName}.{prop.Name}:\n{tabs}{outString} "; diff --git a/Diffing_Engine/Modify/RemoveDuplicatesByHash.cs b/Diffing_Engine/Modify/RemoveDuplicatesByHash.cs index b8c77ac37..705b17ac9 100644 --- a/Diffing_Engine/Modify/RemoveDuplicatesByHash.cs +++ b/Diffing_Engine/Modify/RemoveDuplicatesByHash.cs @@ -41,9 +41,9 @@ public static partial class Modify { [Description("Removes duplicates from a collection of objects. The comparison is made using their Hash. If hash is missing, it is computed.")] [Input("objects", "Collection of objects whose duplicates have to be removed. If they don't already have an Hash assigned, it will be calculated.")] - [Input("distinctConfig", "Settings to determine the uniqueness of an Object.")] + [Input("comparisonConfig", "Settings to determine the uniqueness of an Object.")] [Input("useExistingHash", "If true, if objects already have a HashFragment, use that. If false, recompute the hash for all objects.")] - public static IEnumerable RemoveDuplicatesByHash(IEnumerable objects, ComparisonConfig distinctConfig = null, bool useExistingHash = true) where T : IBHoMObject + public static IEnumerable RemoveDuplicatesByHash(IEnumerable objects, ComparisonConfig comparisonConfig = null, bool useExistingHash = true) where T : IBHoMObject { return objects.GroupBy(obj => { @@ -53,7 +53,7 @@ public static IEnumerable RemoveDuplicatesByHash(IEnumerable objects, C if (!string.IsNullOrWhiteSpace(existingHash)) return existingHash; } - return obj.Hash(distinctConfig); + return obj.Hash(comparisonConfig); } ).Select(gr => gr.First()).ToList(); } diff --git a/Diffing_Engine/Modify/SetHashFragment.cs b/Diffing_Engine/Modify/SetHashFragment.cs index 0380b25b4..61bb410f3 100644 --- a/Diffing_Engine/Modify/SetHashFragment.cs +++ b/Diffing_Engine/Modify/SetHashFragment.cs @@ -40,26 +40,26 @@ namespace BH.Engine.Diffing public static partial class Modify { [Description("Clones the IBHoMObjects, computes their hash and stores it in a HashFragment.")] - public static List SetHashFragment(this IEnumerable objs, ComparisonConfig distinctConfig = null) where T : IBHoMObject + public static List SetHashFragment(this IEnumerable objs, ComparisonConfig comparisonConfig = null) where T : IBHoMObject { // Clone the current objects to preserve immutability List objs_cloned = new List(); // Calculate and set the object hashes foreach (var obj in objs) - objs_cloned.Add(SetHashFragment(obj, distinctConfig)); + objs_cloned.Add(SetHashFragment(obj, comparisonConfig)); return objs_cloned; } [Description("Clones the IBHoMObject, computes their hash and stores it in a HashFragment.")] - public static T SetHashFragment(T obj, ComparisonConfig distinctConfig = null) where T : IBHoMObject + public static T SetHashFragment(T obj, ComparisonConfig comparisonConfig = null) where T : IBHoMObject { // Clone the current object to preserve immutability T obj_cloned = BH.Engine.Base.Query.DeepClone(obj); // Calculate and set the object hashes - string hash = obj_cloned.Hash(distinctConfig); + string hash = obj_cloned.Hash(comparisonConfig); obj_cloned.Fragments.AddOrReplace(new HashFragment() { Hash = hash }); return obj_cloned; diff --git a/Diffing_Engine/Objects/EqualityComparers/HashComparer.cs b/Diffing_Engine/Objects/EqualityComparers/HashComparer.cs index 142309147..ec0a3a036 100644 --- a/Diffing_Engine/Objects/EqualityComparers/HashComparer.cs +++ b/Diffing_Engine/Objects/EqualityComparers/HashComparer.cs @@ -51,12 +51,12 @@ public class HashComparer : IEqualityComparer [Description("If the objects are IObjects, computes the BHoM Hash using these configurations.")] public ComparisonConfig ComparisonConfig { get; set; } = new ComparisonConfig(); - [Input("distinctConfig", "If the objects are IObjects, computes the BHoM Hash using these configurations.")] + [Input("comparisonConfig", "If the objects are IObjects, computes the BHoM Hash using these configurations.")] [Input("storeHash", "If true, stores the computed hash for input BHoMObjects as a new HashFragment.")] - public HashComparer(ComparisonConfig distinctConfig = null, bool storeHash = false) + public HashComparer(ComparisonConfig comparisonConfig = null, bool storeHash = false) { - if (distinctConfig != null) - ComparisonConfig = distinctConfig; + if (comparisonConfig != null) + ComparisonConfig = comparisonConfig; StoreHash = storeHash; } From 38930f3596a8a71c25926f4b49e0a77771d9888c Mon Sep 17 00:00:00 2001 From: Alessio Lombardi Date: Mon, 16 Nov 2020 17:18:50 +0000 Subject: [PATCH 10/28] Moved HashComparer in Base Engine; fixing ComparisonConfig not passed --- BHoM_Engine/BHoM_Engine.csproj | 1 + .../Objects/EqualityComparers/HashComparer.cs | 19 +++++++++++++------ Diffing_Engine/Diffing_Engine.csproj | 1 - 3 files changed, 14 insertions(+), 7 deletions(-) rename {Diffing_Engine => BHoM_Engine}/Objects/EqualityComparers/HashComparer.cs (88%) diff --git a/BHoM_Engine/BHoM_Engine.csproj b/BHoM_Engine/BHoM_Engine.csproj index 2a530a875..5add4fdd2 100644 --- a/BHoM_Engine/BHoM_Engine.csproj +++ b/BHoM_Engine/BHoM_Engine.csproj @@ -61,6 +61,7 @@ + diff --git a/Diffing_Engine/Objects/EqualityComparers/HashComparer.cs b/BHoM_Engine/Objects/EqualityComparers/HashComparer.cs similarity index 88% rename from Diffing_Engine/Objects/EqualityComparers/HashComparer.cs rename to BHoM_Engine/Objects/EqualityComparers/HashComparer.cs index ec0a3a036..2351ba3cc 100644 --- a/Diffing_Engine/Objects/EqualityComparers/HashComparer.cs +++ b/BHoM_Engine/Objects/EqualityComparers/HashComparer.cs @@ -22,8 +22,6 @@ using BH.oM.Base; using BH.Engine; -using BH.oM.Data.Collections; -using BH.oM.Diffing; using System; using System.Collections.Generic; using System.Linq; @@ -77,14 +75,14 @@ public bool Equals(T x, T y) if (xbHoM != null && ybHoM != null) { - xHash = xbHoM.Hash(); - yHash = ybHoM.Hash(); + xHash = xbHoM.Hash(ComparisonConfig); + yHash = ybHoM.Hash(ComparisonConfig); if (xbHoM is IBHoMObject && StoreHash) - x = (T)Modify.SetHashFragment(xbHoM as IBHoMObject, xHash); + x = (T)SetHashFragment(xbHoM as IBHoMObject, xHash); if (ybHoM is IBHoMObject && StoreHash) - y = (T)Modify.SetHashFragment(ybHoM as IBHoMObject, yHash); + y = (T)SetHashFragment(ybHoM as IBHoMObject, yHash); return xHash == yHash; } @@ -107,6 +105,15 @@ public int GetHashCode(T obj) return obj.GetHashCode(); } + + /***************************************************/ + + private Y SetHashFragment(Y obj, string hash) where Y : IBHoMObject + { + obj.Fragments.AddOrReplace(new HashFragment() { Hash = hash }); + + return obj; + } } } diff --git a/Diffing_Engine/Diffing_Engine.csproj b/Diffing_Engine/Diffing_Engine.csproj index 06cd2af11..424e64413 100644 --- a/Diffing_Engine/Diffing_Engine.csproj +++ b/Diffing_Engine/Diffing_Engine.csproj @@ -75,7 +75,6 @@ - From 35d0b99743dd7264263eeddd3818d6397526cbcc Mon Sep 17 00:00:00 2001 From: Alessio Lombardi Date: Tue, 17 Nov 2020 11:07:54 +0000 Subject: [PATCH 11/28] Moved SetHashFragment in Base. Add `hashFromFragment` to Hash() --- BHoM_Engine/BHoM_Engine.csproj | 1 + .../Modify/SetHashFragment.cs | 34 +++++++-------- BHoM_Engine/Query/Hash.cs | 12 +++++- Diffing_Engine/Diffing_Engine.csproj | 2 - .../Modify/RemoveDuplicatesByHash.cs | 10 +---- Diffing_Engine/Query/HashFragment.cs | 42 ------------------- 6 files changed, 28 insertions(+), 73 deletions(-) rename {Diffing_Engine => BHoM_Engine}/Modify/SetHashFragment.cs (75%) delete mode 100644 Diffing_Engine/Query/HashFragment.cs diff --git a/BHoM_Engine/BHoM_Engine.csproj b/BHoM_Engine/BHoM_Engine.csproj index 5add4fdd2..80a72b17f 100644 --- a/BHoM_Engine/BHoM_Engine.csproj +++ b/BHoM_Engine/BHoM_Engine.csproj @@ -61,6 +61,7 @@ + diff --git a/Diffing_Engine/Modify/SetHashFragment.cs b/BHoM_Engine/Modify/SetHashFragment.cs similarity index 75% rename from Diffing_Engine/Modify/SetHashFragment.cs rename to BHoM_Engine/Modify/SetHashFragment.cs index 61bb410f3..028f914cb 100644 --- a/Diffing_Engine/Modify/SetHashFragment.cs +++ b/BHoM_Engine/Modify/SetHashFragment.cs @@ -20,29 +20,27 @@ * along with this code. If not, see . */ -using BH.oM.Base; -using BH.oM.Data.Collections; -using BH.oM.Diffing; using System; using System.Collections.Generic; +using System.ComponentModel; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Security.Cryptography; using System.Reflection; -using BH.Engine.Serialiser; +using BH.oM.Base; using BH.oM.Reflection.Attributes; -using System.ComponentModel; -using BH.Engine.Base; +using BH.Engine.Serialiser; -namespace BH.Engine.Diffing +namespace BH.Engine.Base { public static partial class Modify { - [Description("Clones the IBHoMObjects, computes their hash and stores it in a HashFragment.")] + [Description("Computes the hash of the input BHoMObjects and stores it in a HashFragment for each of them." + + "\nIf the hashFragment already existed, it is replaced.")] public static List SetHashFragment(this IEnumerable objs, ComparisonConfig comparisonConfig = null) where T : IBHoMObject { - // Clone the current objects to preserve immutability + // Each object will be cloned to avoid modification by reference. List objs_cloned = new List(); // Calculate and set the object hashes @@ -52,23 +50,21 @@ public static List SetHashFragment(this IEnumerable objs, ComparisonCon return objs_cloned; } - [Description("Clones the IBHoMObject, computes their hash and stores it in a HashFragment.")] + [Description("Computes the hash of the BHoMObject and stores it in a HashFragment." + + "\nIf the hashFragment already existed, it is replaced.")] public static T SetHashFragment(T obj, ComparisonConfig comparisonConfig = null) where T : IBHoMObject { - // Clone the current object to preserve immutability - T obj_cloned = BH.Engine.Base.Query.DeepClone(obj); - // Calculate and set the object hashes - string hash = obj_cloned.Hash(comparisonConfig); - obj_cloned.Fragments.AddOrReplace(new HashFragment() { Hash = hash }); - - return obj_cloned; + string hash = obj.Hash(comparisonConfig); + + return SetHashFragment(obj, hash); } - [Description("Clones the IBHoMObject, computes their hash and stores it in a HashFragment.")] + [Description("Clones the IBHoMObject, computes its hash and stores it in a HashFragment." + + "\nIf the hashFragment already existed, it is replaced.")] public static T SetHashFragment(T obj, string hash) where T : IBHoMObject { - // Clone the current object to preserve immutability + // Clone the current object to avoid modification by reference. T obj_cloned = BH.Engine.Base.Query.DeepClone(obj); obj_cloned.Fragments.AddOrReplace(new HashFragment() { Hash = hash }); diff --git a/BHoM_Engine/Query/Hash.cs b/BHoM_Engine/Query/Hash.cs index 05424ee82..fc59562d9 100644 --- a/BHoM_Engine/Query/Hash.cs +++ b/BHoM_Engine/Query/Hash.cs @@ -48,8 +48,18 @@ public static partial class Query "\nYou can change how the hash is computed by changing the settings in the ComparisonConfig.")] [Input("iObj", "iObject the hash code should be calculated for.")] [Input("comparisonConfig", "Configure how the hash is computed.")] - public static string Hash(this IObject iObj, ComparisonConfig comparisonConfig = null) + [Input("hashFromFragment", "If true, if the object is a BHoMObject storing a HashFragment, retrieve the hash from it instead of computing the hash.")] + public static string Hash(this IObject iObj, ComparisonConfig comparisonConfig = null, bool hashFromFragment = false) { + if (hashFromFragment && iObj is IBHoMObject) + { + // Instead of computing the Hash, first tryGet the hash in HashFragment + string hash = (iObj as IBHoMObject).FindFragment()?.Hash; + + if (!string.IsNullOrWhiteSpace(hash)) + return hash; + } + // ------ SET UP OF CONFIGURATION ------ // Make sure we always have a config object. Clone for immutability. diff --git a/Diffing_Engine/Diffing_Engine.csproj b/Diffing_Engine/Diffing_Engine.csproj index 424e64413..9fd519e77 100644 --- a/Diffing_Engine/Diffing_Engine.csproj +++ b/Diffing_Engine/Diffing_Engine.csproj @@ -74,7 +74,6 @@ - @@ -84,7 +83,6 @@ - diff --git a/Diffing_Engine/Modify/RemoveDuplicatesByHash.cs b/Diffing_Engine/Modify/RemoveDuplicatesByHash.cs index 705b17ac9..055c7088d 100644 --- a/Diffing_Engine/Modify/RemoveDuplicatesByHash.cs +++ b/Diffing_Engine/Modify/RemoveDuplicatesByHash.cs @@ -46,15 +46,7 @@ public static partial class Modify public static IEnumerable RemoveDuplicatesByHash(IEnumerable objects, ComparisonConfig comparisonConfig = null, bool useExistingHash = true) where T : IBHoMObject { return objects.GroupBy(obj => - { - if (useExistingHash) - { - string existingHash = obj.HashFragment()?.Hash; - if (!string.IsNullOrWhiteSpace(existingHash)) - return existingHash; - } - return obj.Hash(comparisonConfig); - } + obj.Hash(comparisonConfig, useExistingHash) ).Select(gr => gr.First()).ToList(); } } diff --git a/Diffing_Engine/Query/HashFragment.cs b/Diffing_Engine/Query/HashFragment.cs deleted file mode 100644 index 1239b9cf7..000000000 --- a/Diffing_Engine/Query/HashFragment.cs +++ /dev/null @@ -1,42 +0,0 @@ -/* - * This file is part of the Buildings and Habitats object Model (BHoM) - * Copyright (c) 2015 - 2020, the respective contributors. All rights reserved. - * - * Each contributor holds copyright over their respective contributions. - * The project versioning (Git) records all such contribution source information. - * - * - * The BHoM is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3.0 of the License, or - * (at your option) any later version. - * - * The BHoM 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 Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this code. If not, see . - */ - -using BH.oM.Base; -using BH.Engine.Base; -using BH.oM.Diffing; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace BH.Engine.Diffing -{ - public static partial class Query - { - public static HashFragment HashFragment(this IBHoMObject obj) - { - return obj.FindFragment(); - } - } -} - From 008db52561a9fac40d19074d0a758853277f8301 Mon Sep 17 00:00:00 2001 From: Alessio Lombardi Date: Tue, 17 Nov 2020 18:25:02 +0000 Subject: [PATCH 12/28] File renaming: ComparisonConfig --- Analytical_Engine/Analytical_Engine.csproj | 2 +- .../Create/Graph/{DistinctConfig.cs => ComparisonConfig.cs} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename Analytical_Engine/Create/Graph/{DistinctConfig.cs => ComparisonConfig.cs} (100%) diff --git a/Analytical_Engine/Analytical_Engine.csproj b/Analytical_Engine/Analytical_Engine.csproj index 349c8d741..c0c1ce184 100644 --- a/Analytical_Engine/Analytical_Engine.csproj +++ b/Analytical_Engine/Analytical_Engine.csproj @@ -75,7 +75,7 @@ - + diff --git a/Analytical_Engine/Create/Graph/DistinctConfig.cs b/Analytical_Engine/Create/Graph/ComparisonConfig.cs similarity index 100% rename from Analytical_Engine/Create/Graph/DistinctConfig.cs rename to Analytical_Engine/Create/Graph/ComparisonConfig.cs From 79bbc49808b81ee1620ea86decc70825d121e0fd Mon Sep 17 00:00:00 2001 From: Alessio Lombardi Date: Thu, 19 Nov 2020 10:37:26 +0000 Subject: [PATCH 13/28] Some renamings. --- Analytical_Engine/Analytical_Engine.csproj | 9 ++++++++- Analytical_Engine/Create/Graph/Graph.cs | 2 +- ...ities.cs => UniqueEntitiesReplacementMap.cs} | 17 ++++++++++------- Diffing_Engine/Query/ListModifiedProperties.cs | 4 ++-- 4 files changed, 21 insertions(+), 11 deletions(-) rename Analytical_Engine/Query/Graph/{DistinctEntities.cs => UniqueEntitiesReplacementMap.cs} (79%) diff --git a/Analytical_Engine/Analytical_Engine.csproj b/Analytical_Engine/Analytical_Engine.csproj index c0c1ce184..1af651700 100644 --- a/Analytical_Engine/Analytical_Engine.csproj +++ b/Analytical_Engine/Analytical_Engine.csproj @@ -38,6 +38,9 @@ C:\ProgramData\BHoM\Assemblies\BHoM.dll False + + ..\..\..\..\..\ProgramData\BHoM\Assemblies\Data_oM.dll + C:\ProgramData\BHoM\Assemblies\Diffing_oM.dll False @@ -95,7 +98,7 @@ - + @@ -133,6 +136,10 @@ {1ad45c88-dd54-48e5-951f-55edfeb70e35} BHoM_Engine + + {8082ca2a-ac5c-4690-9f09-960e0d3e4102} + Data_Engine + {073dfd36-0829-4792-8c32-67bf692a9413} Diffing_Engine diff --git a/Analytical_Engine/Create/Graph/Graph.cs b/Analytical_Engine/Create/Graph/Graph.cs index 4ce6af40d..c50602318 100644 --- a/Analytical_Engine/Create/Graph/Graph.cs +++ b/Analytical_Engine/Create/Graph/Graph.cs @@ -88,7 +88,7 @@ public static Graph Graph(List entities = null, List rel return graph; } - m_MatchedObjects = Query.DistinctEntities(clonedEntities, comparisonConfig); + m_MatchedObjects = Query.UniqueEntitiesReplacementMap(clonedEntities, comparisonConfig); //convert dependency fragments attached to entities and add to relations clonedEntities.ForEach(ent => clonedRelations.AddRange(ent.ToRelation())); diff --git a/Analytical_Engine/Query/Graph/DistinctEntities.cs b/Analytical_Engine/Query/Graph/UniqueEntitiesReplacementMap.cs similarity index 79% rename from Analytical_Engine/Query/Graph/DistinctEntities.cs rename to Analytical_Engine/Query/Graph/UniqueEntitiesReplacementMap.cs index 98e8ab7d5..8cf15361b 100644 --- a/Analytical_Engine/Query/Graph/DistinctEntities.cs +++ b/Analytical_Engine/Query/Graph/UniqueEntitiesReplacementMap.cs @@ -32,6 +32,7 @@ using System.Threading.Tasks; using System.ComponentModel; using BH.oM.Reflection.Attributes; +using BH.oM.Data.Collections; namespace BH.Engine.Analytical { @@ -44,8 +45,8 @@ public static partial class Query [Description("Identifies unique objects from a collection IBHoMObjects using hash comparison.")] [Input("entities", "A collection of IBHoMObjects from which unique instances are identified.")] [Input("comparisonConfig", "Configuration of diffing used to find unique entities.")] - [Output("unique entities", "A Dictionary replacement map of the entities where the keys are the Guid of the original entity and the Values the matching IBHoMObject entity.")] - public static Dictionary DistinctEntities(this List entities, ComparisonConfig comparisonConfig = null) // this is not a diff, it's finding unique entities + [Output("replacementMap", "A Dictionary replacement map of the entities where the keys are the Guid of the original entity and the Values the matching IBHoMObject entity.")] + public static Dictionary UniqueEntitiesReplacementMap(this List entities, ComparisonConfig comparisonConfig = null) { ComparisonConfig cc = comparisonConfig ?? new ComparisonConfig(); @@ -54,18 +55,20 @@ public static Dictionary DistinctEntities(this List hashComparer = new HashComparer(cc); - foreach (KeyValuePair entityA in objectHash) + // Compute the "Diffing" by means of a VennDiagram. + // Hashes are computed in the DiffingHashComparer, once per each object (the hash is stored in a hashFragment). + foreach (var objA in entities) { - foreach (KeyValuePair entityB in objectHash) + foreach (var objB in entities) { //only if same object type - if (entityA.Key.GetType() == entityB.Key.GetType()) + if (objA.GetType() == objB.GetType()) { //compare hashes - if (hashComparer.Equals(entityA.Value, entityB.Value)) + if (hashComparer.Equals(objA, objB)) { //store in map dictionary where key is original Guid and Value is replacement object - replaceMap[entityA.Key.BHoM_Guid] = entityB.Key; + replaceMap[objA.BHoM_Guid] = objA; //first match has been found so break inner loop. break; diff --git a/Diffing_Engine/Query/ListModifiedProperties.cs b/Diffing_Engine/Query/ListModifiedProperties.cs index 1621b3f27..66dbf963f 100644 --- a/Diffing_Engine/Query/ListModifiedProperties.cs +++ b/Diffing_Engine/Query/ListModifiedProperties.cs @@ -39,8 +39,8 @@ public static partial class Query [MultiOutput(0, "identifier", "Identifier of the objects which have some modified properties.\nWhen using Revisions, this is the Hash of the objects. When Diffing using CustomData, this is the specified Id.")] [MultiOutput(1, "propNames", "List of properties changed per each object.")] - [MultiOutput(2, "value_Current", "List of current values of the properties.")] - [MultiOutput(3, "value_Past", "List of past values of the properties.")] + [MultiOutput(2, "value_obj1", "List of current values of the properties.")] + [MultiOutput(3, "value_obj2", "List of past values of the properties.")] public static Output>, List>, List>, List>> ListModifiedProperties(Dictionary>> modProps, List filterNames = null) { var output = new Output>, List>, List>, List>>(); From 206a0a7ea71abb43151232873d228460a395347e Mon Sep 17 00:00:00 2001 From: Alessio Lombardi Date: Thu, 19 Nov 2020 10:48:09 +0000 Subject: [PATCH 14/28] Removed unused reference --- Analytical_Engine/Analytical_Engine.csproj | 3 --- 1 file changed, 3 deletions(-) diff --git a/Analytical_Engine/Analytical_Engine.csproj b/Analytical_Engine/Analytical_Engine.csproj index 1af651700..c716c4d78 100644 --- a/Analytical_Engine/Analytical_Engine.csproj +++ b/Analytical_Engine/Analytical_Engine.csproj @@ -38,9 +38,6 @@ C:\ProgramData\BHoM\Assemblies\BHoM.dll False - - ..\..\..\..\..\ProgramData\BHoM\Assemblies\Data_oM.dll - C:\ProgramData\BHoM\Assemblies\Diffing_oM.dll False From a7441d60cb3e939452a3aee9b284bf8408526319 Mon Sep 17 00:00:00 2001 From: Alessio Lombardi Date: Thu, 19 Nov 2020 10:49:58 +0000 Subject: [PATCH 15/28] Update UniqueEntitiesReplacementMap.cs --- Analytical_Engine/Query/Graph/UniqueEntitiesReplacementMap.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/Analytical_Engine/Query/Graph/UniqueEntitiesReplacementMap.cs b/Analytical_Engine/Query/Graph/UniqueEntitiesReplacementMap.cs index 8cf15361b..ef48dd766 100644 --- a/Analytical_Engine/Query/Graph/UniqueEntitiesReplacementMap.cs +++ b/Analytical_Engine/Query/Graph/UniqueEntitiesReplacementMap.cs @@ -32,7 +32,6 @@ using System.Threading.Tasks; using System.ComponentModel; using BH.oM.Reflection.Attributes; -using BH.oM.Data.Collections; namespace BH.Engine.Analytical { From a941ae3480410afc2b021b66f5d95e782b59a17c Mon Sep 17 00:00:00 2001 From: Alessio Lombardi Date: Thu, 19 Nov 2020 10:50:41 +0000 Subject: [PATCH 16/28] Update UniqueEntitiesReplacementMap.cs --- Analytical_Engine/Query/Graph/UniqueEntitiesReplacementMap.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Analytical_Engine/Query/Graph/UniqueEntitiesReplacementMap.cs b/Analytical_Engine/Query/Graph/UniqueEntitiesReplacementMap.cs index ef48dd766..71d366556 100644 --- a/Analytical_Engine/Query/Graph/UniqueEntitiesReplacementMap.cs +++ b/Analytical_Engine/Query/Graph/UniqueEntitiesReplacementMap.cs @@ -67,7 +67,7 @@ public static Dictionary UniqueEntitiesReplacementMap(this Li if (hashComparer.Equals(objA, objB)) { //store in map dictionary where key is original Guid and Value is replacement object - replaceMap[objA.BHoM_Guid] = objA; + replaceMap[objA.BHoM_Guid] = objB; //first match has been found so break inner loop. break; From c13a00297d473f3f2b9dc4fddebd5f9f5abd8da4 Mon Sep 17 00:00:00 2001 From: Alessio Lombardi Date: Thu, 19 Nov 2020 12:48:45 +0000 Subject: [PATCH 17/28] Fixing bug in Query.Hash() for PropertyNamesToConsider --- BHoM_Engine/Query/Hash.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/BHoM_Engine/Query/Hash.cs b/BHoM_Engine/Query/Hash.cs index fc59562d9..f4cfd492e 100644 --- a/BHoM_Engine/Query/Hash.cs +++ b/BHoM_Engine/Query/Hash.cs @@ -194,13 +194,16 @@ private static string DefiningString(object obj, ComparisonConfig cc, int fracti foreach (PropertyInfo prop in properties) { + string propertyPath = propertyPath + "." + prop.Name; + string propertyFullName = prop.DeclaringType.FullName + "." + prop.Name; + bool isInPropertyNameExceptions = cc.PropertyNameExceptions?.Count > 0 && cc.PropertyNameExceptions.Where(ex => prop.Name.Contains(ex)).Any(); bool isInPropertyFullNameExceptions = cc.PropertyFullNameExceptions?.Count > 0 && cc.PropertyFullNameExceptions.Where(ex => new WildcardPattern(ex).IsMatch(prop.Name + "." + prop.DeclaringType.FullName)).Any(); if (isInPropertyNameExceptions || isInPropertyFullNameExceptions) continue; - if (cc.PropertyNamesToConsider?.Count() > 0 && !cc.PropertyNamesToConsider.Contains(prop.Name)) + if (cc.PropertyNamesToConsider?.Count() > 0 && !cc.PropertyNamesToConsider.Any(pn => (propertyPath + "." + prop.Name).Contains(pn))) //!cc.PropertyNamesToConsider.Contains(prop.Name)) continue; object propValue = prop.GetValue(obj); From 47743844861a2bc62a97d537b73048c5633313f6 Mon Sep 17 00:00:00 2001 From: Alessio Lombardi Date: Thu, 19 Nov 2020 16:48:18 +0000 Subject: [PATCH 18/28] Changes on propertiesToInclude/Exceptions --- .../Create/Graph/ComparisonConfig.cs | 2 +- BHoM_Engine/Query/Hash.cs | 47 ++++++++++--------- .../{DiffConfig.cs => DiffingConfig.cs} | 10 ++-- Diffing_Engine/Diffing_Engine.csproj | 2 +- Diffing_Engine/Query/DifferentProperties.cs | 8 ++-- .../Query/HasMergeablePropertiesWith.cs | 6 +-- Physical_Engine/Query/UniqueConstructions.cs | 4 +- 7 files changed, 41 insertions(+), 38 deletions(-) rename Diffing_Engine/Create/{DiffConfig.cs => DiffingConfig.cs} (88%) diff --git a/Analytical_Engine/Create/Graph/ComparisonConfig.cs b/Analytical_Engine/Create/Graph/ComparisonConfig.cs index c699c3391..4869d3941 100644 --- a/Analytical_Engine/Create/Graph/ComparisonConfig.cs +++ b/Analytical_Engine/Create/Graph/ComparisonConfig.cs @@ -27,7 +27,7 @@ public static ComparisonConfig ComparisonConfig(double numericTolerance = oM.Geo ComparisonConfig cc = new ComparisonConfig() { NumericTolerance = numericTolerance, - PropertyNamesToConsider = propertyNamesToConsider ?? new List(), + //PropertyNamesToConsider = propertyNamesToConsider ?? new List(), }; return cc; diff --git a/BHoM_Engine/Query/Hash.cs b/BHoM_Engine/Query/Hash.cs index f4cfd492e..8279f6f11 100644 --- a/BHoM_Engine/Query/Hash.cs +++ b/BHoM_Engine/Query/Hash.cs @@ -65,23 +65,29 @@ public static string Hash(this IObject iObj, ComparisonConfig comparisonConfig = // Make sure we always have a config object. Clone for immutability. ComparisonConfig cc = comparisonConfig == null ? new ComparisonConfig() : comparisonConfig.DeepClone(); - // Make sure that "BHoM_Guid" is added to the propertyNameExceptions of the config. - cc.PropertyNameExceptions = cc.PropertyNameExceptions ?? new List(); - if (!cc.PropertyNameExceptions.Contains(nameof(BHoMObject.BHoM_Guid))) - cc.PropertyNameExceptions.Add(nameof(BHoMObject.BHoM_Guid)); - - // Convert from the Numeric Tolerance to fractionalDigits (required for the hash). - int fractionalDigits = Math.Abs(Convert.ToInt32(Math.Log10(cc.NumericTolerance))); + // Make sure that "BHoM_Guid" is added to the PropertyExceptions of the config. + cc.PropertyExceptions = cc.PropertyExceptions ?? new List(); + if (!cc.PropertyExceptions.Contains(nameof(BHoMObject.BHoM_Guid))) + cc.PropertyExceptions.Add(nameof(BHoMObject.BHoM_Guid)); // Process the "PropertiesToInclude" property. - if (cc.PropertyNamesToConsider?.Any() ?? false) + if (cc.PropertiesToConsider?.Any() ?? false) { // The hash computation can only consider "exceptions". // We need to retrieve all the object properties, intersect them with PropertiesToInclude, and treat all those remaining as "exceptions". - IEnumerable exceptions = BH.Engine.Reflection.Query.PropertyNames(iObj).Except(cc.PropertyNamesToConsider); - cc.PropertyNameExceptions.AddRange(exceptions); + // Works only for top-level properties. + IEnumerable exceptions = BH.Engine.Reflection.Query.PropertyNames(iObj).Except(cc.PropertiesToConsider); + cc.PropertyExceptions.AddRange(exceptions); } + // Make sure that the single Property exceptions are either: + // - explicitly referring to a property in its "property path": e.g. Bar.StartNode.Point.X + // - OR if it's only a property name e.g. BHoM_Guid make sure that we prepend the wildcard so we can match the single property inside any property path: e.g. *BHoM_Guid + cc.PropertyExceptions = cc.PropertyExceptions.Select(pe => pe = pe.Contains('.') ? pe : "*" + pe).ToList(); + + // Convert from the Numeric Tolerance to fractionalDigits (required for the hash). + int fractionalDigits = Math.Abs(Convert.ToInt32(Math.Log10(cc.NumericTolerance))); + // ----- SET UP OF INPUT OBJECT ----- // Copy the object for immutability @@ -194,25 +200,22 @@ private static string DefiningString(object obj, ComparisonConfig cc, int fracti foreach (PropertyInfo prop in properties) { - string propertyPath = propertyPath + "." + prop.Name; - string propertyFullName = prop.DeclaringType.FullName + "." + prop.Name; - - bool isInPropertyNameExceptions = cc.PropertyNameExceptions?.Count > 0 && cc.PropertyNameExceptions.Where(ex => prop.Name.Contains(ex)).Any(); - bool isInPropertyFullNameExceptions = cc.PropertyFullNameExceptions?.Count > 0 && cc.PropertyFullNameExceptions.Where(ex => new WildcardPattern(ex).IsMatch(prop.Name + "." + prop.DeclaringType.FullName)).Any(); + if (string.IsNullOrWhiteSpace(propertyPath)) + propertyPath = type.FullName + "." + prop.Name; + else + propertyPath += "." + prop.Name; - if (isInPropertyNameExceptions || isInPropertyFullNameExceptions) + bool isInPropertyExceptions = cc.PropertyExceptions?.Count > 0 && cc.PropertyExceptions.Where(ex => new WildcardPattern(ex).IsMatch(propertyPath)).Any(); + if (isInPropertyExceptions) continue; - if (cc.PropertyNamesToConsider?.Count() > 0 && !cc.PropertyNamesToConsider.Any(pn => (propertyPath + "." + prop.Name).Contains(pn))) //!cc.PropertyNamesToConsider.Contains(prop.Name)) - continue; + //if (cc.PropertyNamesToConsider?.Count() > 0 && !cc.PropertyNamesToConsider.Any(pn => (propertyPath).Contains(pn))) //!cc.PropertyNamesToConsider.Contains(prop.Name)) + // continue; object propValue = prop.GetValue(obj); if (propValue != null) { - if (string.IsNullOrWhiteSpace(propertyPath)) - propertyPath = type.FullName + "." + prop.Name; - else if (prop.PropertyType.IsClass) - propertyPath += "." + prop.Name; + string outString = ""; diff --git a/Diffing_Engine/Create/DiffConfig.cs b/Diffing_Engine/Create/DiffingConfig.cs similarity index 88% rename from Diffing_Engine/Create/DiffConfig.cs rename to Diffing_Engine/Create/DiffingConfig.cs index d217d63a9..38a848255 100644 --- a/Diffing_Engine/Create/DiffConfig.cs +++ b/Diffing_Engine/Create/DiffingConfig.cs @@ -47,13 +47,13 @@ public static partial class Create [Input("storeUnchangedObjects", "If enabled, the Diff stores also the objects that did not change (`Unchanged` property).")] public static DiffingConfig DiffingConfig(bool enablePropertyDiffing, bool storeUnchangedObjects) { - return Create.DiffingConfig(enablePropertyDiffing, storeUnchangedObjects, null, null); + return Create.DiffingConfig(enablePropertyDiffing, storeUnchangedObjects, null); } [Description("Defines configurations for the diffing.")] [Input("enablePropertyDiffing", "Enables the property-level diffing: differences in object properties are stored in the `ModifiedPropsPerObject` dictionary.")] [Input("storeUnchangedObjects", "If enabled, the Diff stores also the objects that did not change (`Unchanged` property).")] - public static DiffingConfig DiffingConfig(bool enablePropertyDiffing = false, bool storeUnchangedObjects = true, List propertyNamesToConsider = null, List propertiesToIgnore = null, List customDataToIgnore = null) + public static DiffingConfig DiffingConfig(bool enablePropertyDiffing = false, bool storeUnchangedObjects = true, List propertyNamesToConsider = null) { return new DiffingConfig() { @@ -61,9 +61,9 @@ public static DiffingConfig DiffingConfig(bool enablePropertyDiffing = false, bo IncludeUnchangedObjects = storeUnchangedObjects, ComparisonConfig = new ComparisonConfig() { - PropertyNamesToConsider = propertyNamesToConsider, - PropertyNameExceptions = propertiesToIgnore, - CustomdataKeysExceptions = (customDataToIgnore == null || !customDataToIgnore.Any()) ? new List() { "RenderMesh" } : customDataToIgnore + //PropertyNamesToConsider = propertyNamesToConsider, + //PropertyExceptions = propertiesToIgnore, + //CustomdataKeysExceptions = (customDataToIgnore == null || !customDataToIgnore.Any()) ? new List() { "RenderMesh" } : customDataToIgnore }, }; } diff --git a/Diffing_Engine/Diffing_Engine.csproj b/Diffing_Engine/Diffing_Engine.csproj index 9fd519e77..d05cfbf5c 100644 --- a/Diffing_Engine/Diffing_Engine.csproj +++ b/Diffing_Engine/Diffing_Engine.csproj @@ -75,7 +75,7 @@ - + diff --git a/Diffing_Engine/Query/DifferentProperties.cs b/Diffing_Engine/Query/DifferentProperties.cs index b7a9d0cfd..1fb437127 100644 --- a/Diffing_Engine/Query/DifferentProperties.cs +++ b/Diffing_Engine/Query/DifferentProperties.cs @@ -57,12 +57,12 @@ public static Dictionary> DifferentProperties(this comparer.Config.DoublePrecision = dc.ComparisonConfig.NumericTolerance; // Set the properties to be ignored. - if (!dc.ComparisonConfig.PropertyNameExceptions?.Contains("BHoM_Guid") ?? true) - dc.ComparisonConfig.PropertyNameExceptions.Add("BHoM_Guid"); + if (!dc.ComparisonConfig.PropertyExceptions?.Contains("BHoM_Guid") ?? true) + dc.ComparisonConfig.PropertyExceptions.Add("BHoM_Guid"); // the above should be replaced by BH.Engine.Reflection.Compute.RecordWarning($"`BHoM_Guid` should generally be ignored when computing the diffing. Consider adding it to the {nameof(DiffingConfig.PropertiesToIgnore)}."); // when the bug in the auto Create() method ("auto-property initialisers for ByRef values like lists do not populate default values") is resolved. - comparer.Config.MembersToIgnore = dc.ComparisonConfig.PropertyNameExceptions; + comparer.Config.MembersToIgnore = dc.ComparisonConfig.PropertyExceptions; // Removes the CustomData to be ignored. var bhomobj1 = (obj1Copy as IBHoMObject); @@ -108,7 +108,7 @@ public static Dictionary> DifferentProperties(this propertyName = splittedName.FirstOrDefault() + $"['{keyName}']." + splittedName.Last(); } - if (dc.ComparisonConfig.PropertyNameExceptions.Any() && !dc.ComparisonConfig.PropertyNameExceptions.Contains(difference.PropertyName)) + if (dc.ComparisonConfig.PropertyExceptions.Any() && !dc.ComparisonConfig.PropertyExceptions.Contains(difference.PropertyName)) dict[propertyName] = new Tuple(difference.Object1, difference.Object2); } diff --git a/Environment_Engine/Query/HasMergeablePropertiesWith.cs b/Environment_Engine/Query/HasMergeablePropertiesWith.cs index 5d15e530a..05c78fbd4 100644 --- a/Environment_Engine/Query/HasMergeablePropertiesWith.cs +++ b/Environment_Engine/Query/HasMergeablePropertiesWith.cs @@ -60,7 +60,7 @@ public static bool HasMergeablePropertiesWith(Panel element, Panel other) { ComparisonConfig = new ComparisonConfig() { - PropertyNameExceptions = new List + PropertyExceptions = new List { "ExternalEdges", "Openings", @@ -86,7 +86,7 @@ public static bool HasMergeablePropertiesWith(Opening element, Opening other) { ComparisonConfig = new ComparisonConfig() { - PropertyNameExceptions = new List + PropertyExceptions = new List { "Edges", "FrameFactorValue", @@ -121,7 +121,7 @@ public static bool HasMergeablePropertiesWith(Space element, Space other) { ComparisonConfig = new ComparisonConfig() { - PropertyNameExceptions = new List + PropertyExceptions = new List { "Location", "Type", diff --git a/Physical_Engine/Query/UniqueConstructions.cs b/Physical_Engine/Query/UniqueConstructions.cs index 5b9203c38..1493007e2 100644 --- a/Physical_Engine/Query/UniqueConstructions.cs +++ b/Physical_Engine/Query/UniqueConstructions.cs @@ -46,7 +46,7 @@ public static List UniqueConstructions(this List con { ComparisonConfig dc = new ComparisonConfig() { - PropertyNameExceptions = new List + PropertyExceptions = new List { "CustomData" }, @@ -54,7 +54,7 @@ public static List UniqueConstructions(this List con }; if (!includeConstructionName) - dc.PropertyNameExceptions.Add("Name"); + dc.PropertyExceptions.Add("Name"); List allConstructions = constructions.Where(x => x != null).ToList(); List uniqueConstructions = BH.Engine.Diffing.Modify.RemoveDuplicatesByHash(allConstructions, dc).ToList(); From 9326473cde3d4037a0c752a64b1bbba4bb834e02 Mon Sep 17 00:00:00 2001 From: Alessio Lombardi Date: Thu, 19 Nov 2020 16:49:44 +0000 Subject: [PATCH 19/28] Update Hash.cs --- BHoM_Engine/Query/Hash.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BHoM_Engine/Query/Hash.cs b/BHoM_Engine/Query/Hash.cs index 8279f6f11..fce22a492 100644 --- a/BHoM_Engine/Query/Hash.cs +++ b/BHoM_Engine/Query/Hash.cs @@ -137,7 +137,7 @@ private static string SHA256Hash(string str) [Description("Generates a string representing the whole structure of the object with its assigned values.")] [Input("obj", "Objects the string should be calculated for.")] - [Input("hc", "HashConfig, options for the hash calculation.")] + [Input("cc", "HashConfig, options for the hash calculation.")] [Input("nestingLevel", "Nesting level of the property.")] [Input("propertyPath", "(Optional) Indicates the 'property path' of the current object, e.g. `BH.oM.Structure.Elements.Bar.StartNode.Point.X`")] private static string DefiningString(object obj, ComparisonConfig cc, int fractionalDigits, int nestingLevel, string propertyPath = null) From b8664b620b6f9d0b02442b840d4b293ec788a126 Mon Sep 17 00:00:00 2001 From: Alessio Lombardi Date: Mon, 23 Nov 2020 09:23:47 +0000 Subject: [PATCH 20/28] Update BHoM_Engine/Create/IObject/RandomObject.cs Co-authored-by: Fraser Greenroyd --- BHoM_Engine/Create/IObject/RandomObject.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/BHoM_Engine/Create/IObject/RandomObject.cs b/BHoM_Engine/Create/IObject/RandomObject.cs index 2019006bf..4eb14e7fa 100644 --- a/BHoM_Engine/Create/IObject/RandomObject.cs +++ b/BHoM_Engine/Create/IObject/RandomObject.cs @@ -217,7 +217,8 @@ private static object GetValue(Type type, Random rnd, int depth) if (depth > 50) return null; Type obj = null; - if (!m_ImplementingTypes.TryGetValue(type, out obj)) return null; + if (!m_ImplementingTypes.TryGetValue(type, out obj)) + return null; return GetValue(obj, rnd, depth + 1); } @@ -308,4 +309,3 @@ private static IEnumerable RandomEnumerable(Type type, Random rnd, int depth) /***************************************************/ } } - From 41c4ba36abf55356ae290b77c44b4c59bcefcfd9 Mon Sep 17 00:00:00 2001 From: Alessio Lombardi Date: Mon, 23 Nov 2020 09:41:25 +0000 Subject: [PATCH 21/28] Update Diffing_Engine/Compute/_IDiffing.cs Co-authored-by: Fraser Greenroyd --- Diffing_Engine/Compute/_IDiffing.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Diffing_Engine/Compute/_IDiffing.cs b/Diffing_Engine/Compute/_IDiffing.cs index bce23f49c..507023ee0 100644 --- a/Diffing_Engine/Compute/_IDiffing.cs +++ b/Diffing_Engine/Compute/_IDiffing.cs @@ -116,7 +116,8 @@ public static Diff Diffing(IEnumerable pastObjs, IEnumerable fol } // Check if the bhomObjects have a persistentId assigned. - List reminder_past, reminder_following; + List reminder_past; + List reminder_following; List bHoMObjects_past_persistId = bHoMObjects_past.WithNonNullPersistentAdapterId(out reminder_past); List bHoMObjects_following_persistId = bHoMObjects_following.WithNonNullPersistentAdapterId(out reminder_following); Diff fragmentDiff = null; @@ -148,4 +149,3 @@ private static Diff DiffingError(DiffingType diffingType) } } } - From 2fd402b1d2945230e3d8bce558e0e440db2afbc2 Mon Sep 17 00:00:00 2001 From: Alessio Lombardi Date: Mon, 23 Nov 2020 12:43:04 +0000 Subject: [PATCH 22/28] Update Physical_Engine/Query/UniqueConstructions.cs Co-authored-by: Fraser Greenroyd --- Physical_Engine/Query/UniqueConstructions.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Physical_Engine/Query/UniqueConstructions.cs b/Physical_Engine/Query/UniqueConstructions.cs index 1493007e2..c6c1af5f1 100644 --- a/Physical_Engine/Query/UniqueConstructions.cs +++ b/Physical_Engine/Query/UniqueConstructions.cs @@ -47,9 +47,9 @@ public static List UniqueConstructions(this List con ComparisonConfig dc = new ComparisonConfig() { PropertyExceptions = new List - { - "CustomData" - }, + { + "CustomData" + }, NumericTolerance = BH.oM.Geometry.Tolerance.Distance }; From 77c93e736ea1d37d805f6a03c8eaf29722494131 Mon Sep 17 00:00:00 2001 From: Alessio Lombardi Date: Mon, 23 Nov 2020 12:44:36 +0000 Subject: [PATCH 23/28] suggested changes --- .../Create/Graph/ComparisonConfig.cs | 27 +++++++++++++++++-- .../Compute/{_IDiffing.cs => IDiffing.cs} | 2 +- Diffing_Engine/Diffing_Engine.csproj | 2 +- .../Query/ListDifferentProperties.cs | 4 +-- .../Query/ListModifiedProperties.cs | 4 +-- 5 files changed, 31 insertions(+), 8 deletions(-) rename Diffing_Engine/Compute/{_IDiffing.cs => IDiffing.cs} (97%) diff --git a/Analytical_Engine/Create/Graph/ComparisonConfig.cs b/Analytical_Engine/Create/Graph/ComparisonConfig.cs index 4869d3941..dae394085 100644 --- a/Analytical_Engine/Create/Graph/ComparisonConfig.cs +++ b/Analytical_Engine/Create/Graph/ComparisonConfig.cs @@ -1,4 +1,26 @@ -using BH.oM.Base; +/* + * This file is part of the Buildings and Habitats object Model (BHoM) + * Copyright (c) 2015 - 2020, the respective contributors. All rights reserved. + * + * Each contributor holds copyright over their respective contributions. + * The project versioning (Git) records all such contribution source information. + * + * + * The BHoM is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3.0 of the License, or + * (at your option) any later version. + * + * The BHoM 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this code. If not, see . + */ + +using BH.oM.Base; using BH.oM.Diffing; using BH.oM.Reflection.Attributes; using System; @@ -21,13 +43,14 @@ public static partial class Create "\nDefaults to Tolerance.Distance (1e-6).")] [Input("propertyNamesToConsider", "By default, all the properties of the objects are considered in determining uniqueness." + "\nHere you can specify a list of property names. Only the properties with a name matching any of this list will be considered." + + "\nWorks only for top-level properties." + "\nE.g., if you input 'Name' only the differences in terms of name will be returned.")] public static ComparisonConfig ComparisonConfig(double numericTolerance = oM.Geometry.Tolerance.Distance, List propertyNamesToConsider = null) { ComparisonConfig cc = new ComparisonConfig() { NumericTolerance = numericTolerance, - //PropertyNamesToConsider = propertyNamesToConsider ?? new List(), + PropertiesToConsider = propertyNamesToConsider ?? new List(), }; return cc; diff --git a/Diffing_Engine/Compute/_IDiffing.cs b/Diffing_Engine/Compute/IDiffing.cs similarity index 97% rename from Diffing_Engine/Compute/_IDiffing.cs rename to Diffing_Engine/Compute/IDiffing.cs index 507023ee0..be202ccd8 100644 --- a/Diffing_Engine/Compute/_IDiffing.cs +++ b/Diffing_Engine/Compute/IDiffing.cs @@ -43,7 +43,7 @@ namespace BH.Engine.Diffing public static partial class Compute { [Description("Dispatches to the most appropriate Diffing method, depending on the provided inputs.")] - public static Diff Diffing(IEnumerable pastObjs, IEnumerable followingObjs, DiffingType diffingType = DiffingType.Automatic, DiffingConfig diffConfig = null) + public static Diff IDiffing(IEnumerable pastObjs, IEnumerable followingObjs, DiffingType diffingType = DiffingType.Automatic, DiffingConfig diffConfig = null) { if (!pastObjs.Any() || !followingObjs.Any()) { diff --git a/Diffing_Engine/Diffing_Engine.csproj b/Diffing_Engine/Diffing_Engine.csproj index d05cfbf5c..60c5abbf1 100644 --- a/Diffing_Engine/Diffing_Engine.csproj +++ b/Diffing_Engine/Diffing_Engine.csproj @@ -68,7 +68,7 @@ - + diff --git a/Diffing_Engine/Query/ListDifferentProperties.cs b/Diffing_Engine/Query/ListDifferentProperties.cs index 9fa03e48c..22e5b4ad0 100644 --- a/Diffing_Engine/Query/ListDifferentProperties.cs +++ b/Diffing_Engine/Query/ListDifferentProperties.cs @@ -38,8 +38,8 @@ public static partial class Query { [MultiOutput(0, "propNames", "List of properties changed per each object.")] - [MultiOutput(1, "value_Current", "List of current values of the properties.")] - [MultiOutput(2, "value_Past", "List of past values of the properties.")] + [MultiOutput(1, "value_obj1", "List of current values of the properties.")] + [MultiOutput(2, "value_obj2", "List of past values of the properties.")] public static Output, List, List> ListDifferentProperties(Dictionary> diffProps) { var output = new Output, List, List>(); diff --git a/Diffing_Engine/Query/ListModifiedProperties.cs b/Diffing_Engine/Query/ListModifiedProperties.cs index 66dbf963f..3437f41e8 100644 --- a/Diffing_Engine/Query/ListModifiedProperties.cs +++ b/Diffing_Engine/Query/ListModifiedProperties.cs @@ -39,8 +39,8 @@ public static partial class Query [MultiOutput(0, "identifier", "Identifier of the objects which have some modified properties.\nWhen using Revisions, this is the Hash of the objects. When Diffing using CustomData, this is the specified Id.")] [MultiOutput(1, "propNames", "List of properties changed per each object.")] - [MultiOutput(2, "value_obj1", "List of current values of the properties.")] - [MultiOutput(3, "value_obj2", "List of past values of the properties.")] + [MultiOutput(2, "value_current", "List of current values of the properties.")] + [MultiOutput(3, "value_past", "List of past values of the properties.")] public static Output>, List>, List>, List>> ListModifiedProperties(Dictionary>> modProps, List filterNames = null) { var output = new Output>, List>, List>, List>>(); From e5fc6076189b5fa3fb0ea992e879a18fdbbab6c2 Mon Sep 17 00:00:00 2001 From: Alessio Lombardi Date: Thu, 26 Nov 2020 16:39:12 +0000 Subject: [PATCH 24/28] Restoring version of UniqueEntities without HashComparer --- .../Query/Graph/UniqueEntitiesReplacementMap.cs | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/Analytical_Engine/Query/Graph/UniqueEntitiesReplacementMap.cs b/Analytical_Engine/Query/Graph/UniqueEntitiesReplacementMap.cs index 71d366556..5d66e1fd7 100644 --- a/Analytical_Engine/Query/Graph/UniqueEntitiesReplacementMap.cs +++ b/Analytical_Engine/Query/Graph/UniqueEntitiesReplacementMap.cs @@ -50,24 +50,23 @@ public static Dictionary UniqueEntitiesReplacementMap(this Li ComparisonConfig cc = comparisonConfig ?? new ComparisonConfig(); Dictionary replaceMap = new Dictionary(); - Dictionary objectHash = new Dictionary(); + Dictionary entitiesHash = new Dictionary(); - HashComparer hashComparer = new HashComparer(cc); + // populate dictionary + entities.ForEach(ent => entitiesHash.Add(ent, ent.Hash(cc))); - // Compute the "Diffing" by means of a VennDiagram. - // Hashes are computed in the DiffingHashComparer, once per each object (the hash is stored in a hashFragment). - foreach (var objA in entities) + foreach (KeyValuePair entityA in entitiesHash) { - foreach (var objB in entities) + foreach (KeyValuePair entityB in entitiesHash) { //only if same object type - if (objA.GetType() == objB.GetType()) + if (entityA.Key.GetType() == entityB.Key.GetType()) { //compare hashes - if (hashComparer.Equals(objA, objB)) + if (entityA.Value == entityB.Value) { //store in map dictionary where key is original Guid and Value is replacement object - replaceMap[objA.BHoM_Guid] = objB; + replaceMap[entityA.Key.BHoM_Guid] = entityB.Key; //first match has been found so break inner loop. break; From 843acf5eeb3ee0bff9177243d0e212e2838ed871 Mon Sep 17 00:00:00 2001 From: Alessio Lombardi Date: Thu, 26 Nov 2020 16:41:57 +0000 Subject: [PATCH 25/28] Update Hash for performance reasons --- BHoM_Engine/Query/Hash.cs | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/BHoM_Engine/Query/Hash.cs b/BHoM_Engine/Query/Hash.cs index fce22a492..7b061fe26 100644 --- a/BHoM_Engine/Query/Hash.cs +++ b/BHoM_Engine/Query/Hash.cs @@ -83,7 +83,7 @@ public static string Hash(this IObject iObj, ComparisonConfig comparisonConfig = // Make sure that the single Property exceptions are either: // - explicitly referring to a property in its "property path": e.g. Bar.StartNode.Point.X // - OR if it's only a property name e.g. BHoM_Guid make sure that we prepend the wildcard so we can match the single property inside any property path: e.g. *BHoM_Guid - cc.PropertyExceptions = cc.PropertyExceptions.Select(pe => pe = pe.Contains('.') ? pe : "*" + pe).ToList(); + //cc.PropertyExceptions = cc.PropertyExceptions.Select(pe => pe = pe.Contains('.') ? pe : "*" + pe).ToList(); // Convert from the Numeric Tolerance to fractionalDigits (required for the hash). int fractionalDigits = Math.Abs(Convert.ToInt32(Math.Log10(cc.NumericTolerance))); @@ -200,12 +200,7 @@ private static string DefiningString(object obj, ComparisonConfig cc, int fracti foreach (PropertyInfo prop in properties) { - if (string.IsNullOrWhiteSpace(propertyPath)) - propertyPath = type.FullName + "." + prop.Name; - else - propertyPath += "." + prop.Name; - - bool isInPropertyExceptions = cc.PropertyExceptions?.Count > 0 && cc.PropertyExceptions.Where(ex => new WildcardPattern(ex).IsMatch(propertyPath)).Any(); + bool isInPropertyExceptions = cc.PropertyExceptions?.Count > 0 && cc.PropertyExceptions.Where(ex => prop.Name == ex).Any(); if (isInPropertyExceptions) continue; @@ -215,7 +210,10 @@ private static string DefiningString(object obj, ComparisonConfig cc, int fracti object propValue = prop.GetValue(obj); if (propValue != null) { - + if (string.IsNullOrWhiteSpace(propertyPath)) + propertyPath = type.FullName + "." + prop.Name; + else + propertyPath += "." + prop.Name; string outString = ""; From 37616a6f30ed5cf7e95d22e2dac1d5d98c03c0a9 Mon Sep 17 00:00:00 2001 From: Alessio Lombardi Date: Tue, 1 Dec 2020 12:01:11 +0000 Subject: [PATCH 26/28] Other changes required for compliance --- Diffing_Engine/Compute/DiffRevisions.cs | 16 ++++++++-------- Diffing_Engine/Create/Delta.cs | 16 ++++++++-------- Diffing_Engine/Modify/RemoveDuplicatesByHash.cs | 2 +- Diffing_Engine/Modify/SetRevisionFragment.cs | 14 +++++++------- Diffing_Engine/Query/DifferentProperties.cs | 4 ++-- Diffing_Engine/Query/ListDifferentProperties.cs | 3 +-- Diffing_Engine/Query/ListModifiedProperties.cs | 3 +-- 7 files changed, 28 insertions(+), 30 deletions(-) diff --git a/Diffing_Engine/Compute/DiffRevisions.cs b/Diffing_Engine/Compute/DiffRevisions.cs index fb54621d1..03f2b92f2 100644 --- a/Diffing_Engine/Compute/DiffRevisions.cs +++ b/Diffing_Engine/Compute/DiffRevisions.cs @@ -46,19 +46,19 @@ public static partial class Compute [Input("pastRevision", "A past Revision. It must have been created before the 'followingRevision'.")] [Input("followingRevision", "A following Revision. It must have been created after 'pastRevision'.")] [Input("DiffingConfig", "Sets configs such as properties to be ignored in the diffing, or enable/disable property-by-property diffing.")] - public static Diff DiffRevisions(Revision pastRevision, Revision followingRevision, DiffingConfig DiffingConfig = null) + public static Diff DiffRevisions(Revision pastRevision, Revision followingRevision, DiffingConfig diffingConfig = null) { - return DiffRevisionObjects(pastRevision.Objects, followingRevision.Objects, DiffingConfig); + return DiffRevisionObjects(pastRevision.Objects, followingRevision.Objects, diffingConfig); } // Computes the diffing for IEnumerable. // For BHoMObjects, it assumes that they all have a HashFragment assigned (like when they have been passed through a Revision). // For non-BHoMObjects, it performs the VennDiagram comparision with a HashComparer. // Results for BHoMObjects and non are concatenated. - private static Diff DiffRevisionObjects(IEnumerable pastRevisionObjs, IEnumerable followingRevisionObjs, DiffingConfig DiffingConfig = null) + private static Diff DiffRevisionObjects(IEnumerable pastRevisionObjs, IEnumerable followingRevisionObjs, DiffingConfig diffingConfig = null) { // Set configurations if DiffingConfig is null. Clone it for immutability in the UI. - DiffingConfig diffConfigCopy = DiffingConfig == null ? new DiffingConfig() : DiffingConfig.DeepClone() as DiffingConfig; + DiffingConfig diffConfigCopy = diffingConfig == null ? new DiffingConfig() : diffingConfig.DeepClone() as DiffingConfig; // Dispatch the objects in BHoMObjects and generic objects. IEnumerable prevObjs_BHoM = pastRevisionObjs.OfType(); @@ -75,7 +75,7 @@ private static Diff DiffRevisionObjects(IEnumerable pastRevisionObjs, IE // Compute the generic Diffing for the other objects. // This is left to the VennDiagram with a HashComparer. - VennDiagram vd = Engine.Data.Create.VennDiagram(prevObjs_nonBHoM, currObjs_nonBHoM, new HashComparer(DiffingConfig.ComparisonConfig)); + VennDiagram vd = Engine.Data.Create.VennDiagram(prevObjs_nonBHoM, currObjs_nonBHoM, new HashComparer(diffingConfig.ComparisonConfig)); // Concatenate the results of the two diffing operations. List allPrevObjs = new List(); @@ -95,10 +95,10 @@ private static Diff DiffRevisionObjects(IEnumerable pastRevisionObjs, IE } // Computes the Diffing for BHoMObjects that all have a HashFragment assigned (like when they have been passed through a Revision). - private static Diff DiffRevisionObjects(IEnumerable pastObjects, IEnumerable currentObjects, DiffingConfig DiffingConfig = null) + private static Diff DiffRevisionObjects(IEnumerable pastObjects, IEnumerable currentObjects, DiffingConfig diffingConfig = null) { // Set configurations if DiffingConfig is null. Clone it for immutability in the UI. - DiffingConfig dc = DiffingConfig == null ? new DiffingConfig() : DiffingConfig.DeepClone() as DiffingConfig; + DiffingConfig dc = diffingConfig == null ? new DiffingConfig() : diffingConfig.DeepClone() as DiffingConfig; // Take the Revision's objects List currentObjs = currentObjects.ToList(); @@ -167,7 +167,7 @@ private static Diff DiffRevisionObjects(IEnumerable pastObjects, IE oldObjs = readObjs_dict.Keys.Except(CurrentObjs_withPreviousHash_dict.Keys) .Where(k => readObjs_dict.ContainsKey(k)).Select(k => readObjs_dict[k]).ToList(); - return new Diff(newObjs, oldObjs, modifiedObjs, DiffingConfig, objModifiedProps, unChanged); + return new Diff(newObjs, oldObjs, modifiedObjs, diffingConfig, objModifiedProps, unChanged); } private static bool AllHaveRevisionFragment(this IEnumerable bHoMObjects) diff --git a/Diffing_Engine/Create/Delta.cs b/Diffing_Engine/Create/Delta.cs index b665cd4f6..12ab5e1db 100644 --- a/Diffing_Engine/Create/Delta.cs +++ b/Diffing_Engine/Create/Delta.cs @@ -46,9 +46,9 @@ public static partial class Create [Input("previousRevision", "A previous Revision")] [Input("currentRevision", "A new Revision")] [Input("DiffingConfig", "Sets configs such as properties to be ignored in the diffing, or enable/disable property-by-property diffing.\nBy default it takes the DiffingConfig property of the Revision. This input can be used to override it.")] - public static Delta Delta(Revision pastRevision, Revision currentRevision, DiffingConfig DiffingConfig = null, string comment = null) + public static Delta Delta(Revision pastRevision, Revision currentRevision, DiffingConfig diffingConfig = null, string comment = null) { - Diff diff = Compute.DiffRevisions(pastRevision, currentRevision, DiffingConfig); + Diff diff = Compute.DiffRevisions(pastRevision, currentRevision, diffingConfig); return new Delta(pastRevision.StreamId, diff, pastRevision.RevisionId, currentRevision.RevisionId, DateTime.UtcNow.Ticks, m_Author, comment); } @@ -60,9 +60,9 @@ public static Delta Delta(Revision pastRevision, Revision currentRevision, Diffi [Description("Returns a Delta object containing all the objects of the input Revision, also called `Revision-Based Delta`.")] [Input("revision", "A new Revision")] [Input("DiffingConfig", "Sets configs such as properties to be ignored in the diffing, or enable/disable property-by-property diffing.\nBy default it takes the DiffingConfig property of the Revision. This input can be used to override it.")] - public static Delta Delta(Revision revision, DiffingConfig DiffingConfig = null, string comment = null) + public static Delta Delta(Revision revision, DiffingConfig diffingConfig = null, string comment = null) { - Diff diff = Compute.DiffRevisions(null, revision, DiffingConfig); + Diff diff = Compute.DiffRevisions(null, revision, diffingConfig); return new Delta(revision.StreamId, diff, revision.RevisionId, new Guid(), DateTime.UtcNow.Ticks, m_Author, comment); } @@ -74,10 +74,10 @@ public static Delta Delta(Revision revision, DiffingConfig DiffingConfig = null, [Input("comment", "Comment to be stored along the Revision that this Delta will produce.")] [Input("DiffingConfig", "Sets configs such as properties to be ignored in the diffing, or enable/disable property-by-property diffing.\nBy default it takes the DiffingConfig property of the Revision. This input can be used to override it.")] public static Delta Delta(List objects, object streamId, string revisionName = null, - string comment = null, DiffingConfig DiffingConfig = null) + string comment = null, DiffingConfig diffingConfig = null) { - Revision revision = Create.Revision(objects, streamId, revisionName, comment, DiffingConfig); - return Delta(revision, DiffingConfig, comment); + Revision revision = Create.Revision(objects, streamId, revisionName, comment, diffingConfig); + return Delta(revision, diffingConfig, comment); } [Description("Returns a Delta object based on the provided Diff.")] @@ -86,7 +86,7 @@ public static Delta Delta(List objects, object streamId, string rev [Input("revisionName", "Name to be assigned to the Revision that this Delta will produce.")] [Input("comment", "Comment to be stored along the Revision that this Delta will produce.")] [Input("DiffingConfig", "Sets configs such as properties to be ignored in the diffing, or enable/disable property-by-property diffing.\nBy default it takes the DiffingConfig property of the Revision. This input can be used to override it.")] - public static Delta Delta(Diff diff, object streamId, Guid revision_from, string comment = null, DiffingConfig DiffingConfig = null) + public static Delta Delta(Diff diff, object streamId, Guid revision_from, string comment = null, DiffingConfig diffingConfig = null) { return new Delta(ProcessStreamId(streamId), diff, revision_from, new Guid(), DateTime.UtcNow.Ticks, m_Author, comment); } diff --git a/Diffing_Engine/Modify/RemoveDuplicatesByHash.cs b/Diffing_Engine/Modify/RemoveDuplicatesByHash.cs index 055c7088d..98872d8ab 100644 --- a/Diffing_Engine/Modify/RemoveDuplicatesByHash.cs +++ b/Diffing_Engine/Modify/RemoveDuplicatesByHash.cs @@ -43,7 +43,7 @@ public static partial class Modify [Input("objects", "Collection of objects whose duplicates have to be removed. If they don't already have an Hash assigned, it will be calculated.")] [Input("comparisonConfig", "Settings to determine the uniqueness of an Object.")] [Input("useExistingHash", "If true, if objects already have a HashFragment, use that. If false, recompute the hash for all objects.")] - public static IEnumerable RemoveDuplicatesByHash(IEnumerable objects, ComparisonConfig comparisonConfig = null, bool useExistingHash = true) where T : IBHoMObject + public static IEnumerable RemoveDuplicatesByHash(this IEnumerable objects, ComparisonConfig comparisonConfig = null, bool useExistingHash = true) where T : IBHoMObject { return objects.GroupBy(obj => obj.Hash(comparisonConfig, useExistingHash) diff --git a/Diffing_Engine/Modify/SetRevisionFragment.cs b/Diffing_Engine/Modify/SetRevisionFragment.cs index 12f0cdd40..3873ae77c 100644 --- a/Diffing_Engine/Modify/SetRevisionFragment.cs +++ b/Diffing_Engine/Modify/SetRevisionFragment.cs @@ -41,18 +41,18 @@ public static partial class Modify { [Description("Clones the IBHoMObjects, computes their hash and stores it in a RevisionFragment. " + "If the object already has a RevisionFragment, it computes the current one and keeps the old one in the `previousHash` of the RevisionFragment.")] - public static List SetRevisionFragment(this IEnumerable objs, DiffingConfig DiffingConfig = null) where T : IBHoMObject + public static List SetRevisionFragment(this IEnumerable objs, DiffingConfig diffingConfig = null) where T : IBHoMObject { // Clone the current objects to preserve immutability List objs_cloned = new List(); // Set configurations if DiffingConfig is null - DiffingConfig = DiffingConfig == null ? new DiffingConfig() : DiffingConfig; + diffingConfig = diffingConfig == null ? new DiffingConfig() : diffingConfig; // Calculate and set the object hashes foreach (var obj in objs) { - objs_cloned.Add(SetRevisionFragment(obj, DiffingConfig)); + objs_cloned.Add(SetRevisionFragment(obj, diffingConfig)); } return objs_cloned; @@ -60,16 +60,16 @@ public static List SetRevisionFragment(this IEnumerable objs, DiffingCo [Description("Clones the IBHoMObject, computes their hash and stores it in a RevisionFragment. " + "If the object already has a RevisionFragment, it computes the current one and keeps the old one in the `previousHash` of the RevisionFragment.")] - public static T SetRevisionFragment(T obj, DiffingConfig DiffingConfig = null) where T : IBHoMObject + public static T SetRevisionFragment(this T obj, DiffingConfig diffingConfig = null) where T : IBHoMObject { // Clone the current object to preserve immutability T obj_cloned = BH.Engine.Base.Query.DeepClone(obj); // Set configurations if DiffingConfig is null - DiffingConfig = DiffingConfig == null ? new DiffingConfig() : DiffingConfig; + diffingConfig = diffingConfig == null ? new DiffingConfig() : diffingConfig; // Calculate and set the object hashes - string hash = obj_cloned.Hash(DiffingConfig.ComparisonConfig); + string hash = obj_cloned.Hash(diffingConfig.ComparisonConfig); RevisionFragment existingFragm = obj_cloned.RevisionFragment(); @@ -80,7 +80,7 @@ public static T SetRevisionFragment(T obj, DiffingConfig DiffingConfig = null [Description("Clones the IBHoMObject, computes their hash and stores it in a RevisionFragment. " + "If the object already has a RevisionFragment, it computes the current one and keeps the old one in the `previousHash` of the RevisionFragment.")] - public static T SetRevisionFragment(T obj, string hash) where T : IBHoMObject + public static T SetRevisionFragment(this T obj, string hash) where T : IBHoMObject { // Clone the current object to preserve immutability T obj_cloned = BH.Engine.Base.Query.DeepClone(obj); diff --git a/Diffing_Engine/Query/DifferentProperties.cs b/Diffing_Engine/Query/DifferentProperties.cs index 1fb437127..f98608923 100644 --- a/Diffing_Engine/Query/DifferentProperties.cs +++ b/Diffing_Engine/Query/DifferentProperties.cs @@ -40,10 +40,10 @@ public static partial class Query [Description("Checks two BHoMObjects property by property and returns the differences")] [Input("DiffingConfig", "Config to be used for the comparison. Can set numeric tolerance, wheter to check the guid, if custom data should be ignored and if any additional properties should be ignored")] [Output("Dictionary whose key is the name of the property, and value is a tuple with its value in obj1 and obj2.")] - public static Dictionary> DifferentProperties(this object obj1, object obj2, DiffingConfig DiffingConfig = null) + public static Dictionary> DifferentProperties(this object obj1, object obj2, DiffingConfig diffingConfig = null) { // Set configurations if DiffingConfig is null. Clone it for immutability in the UI. - DiffingConfig dc = DiffingConfig == null ? new DiffingConfig() : DiffingConfig.DeepClone() as DiffingConfig; + DiffingConfig dc = diffingConfig == null ? new DiffingConfig() : diffingConfig.DeepClone() as DiffingConfig; object obj1Copy = obj1.DeepClone(); object obj2Copy = obj2.DeepClone(); diff --git a/Diffing_Engine/Query/ListDifferentProperties.cs b/Diffing_Engine/Query/ListDifferentProperties.cs index 22e5b4ad0..16be23d50 100644 --- a/Diffing_Engine/Query/ListDifferentProperties.cs +++ b/Diffing_Engine/Query/ListDifferentProperties.cs @@ -36,11 +36,10 @@ namespace BH.Engine.Diffing { public static partial class Query { - [MultiOutput(0, "propNames", "List of properties changed per each object.")] [MultiOutput(1, "value_obj1", "List of current values of the properties.")] [MultiOutput(2, "value_obj2", "List of past values of the properties.")] - public static Output, List, List> ListDifferentProperties(Dictionary> diffProps) + public static Output, List, List> ListDifferentProperties(this Dictionary> diffProps) { var output = new Output, List, List>(); diff --git a/Diffing_Engine/Query/ListModifiedProperties.cs b/Diffing_Engine/Query/ListModifiedProperties.cs index 3437f41e8..f1957edb5 100644 --- a/Diffing_Engine/Query/ListModifiedProperties.cs +++ b/Diffing_Engine/Query/ListModifiedProperties.cs @@ -36,12 +36,11 @@ namespace BH.Engine.Diffing { public static partial class Query { - [MultiOutput(0, "identifier", "Identifier of the objects which have some modified properties.\nWhen using Revisions, this is the Hash of the objects. When Diffing using CustomData, this is the specified Id.")] [MultiOutput(1, "propNames", "List of properties changed per each object.")] [MultiOutput(2, "value_current", "List of current values of the properties.")] [MultiOutput(3, "value_past", "List of past values of the properties.")] - public static Output>, List>, List>, List>> ListModifiedProperties(Dictionary>> modProps, List filterNames = null) + public static Output>, List>, List>, List>> ListModifiedProperties(this Dictionary>> modProps, List filterNames = null) { var output = new Output>, List>, List>, List>>(); From 6a6b160252d5e4c2a31a2ed839423086a590dc6d Mon Sep 17 00:00:00 2001 From: Alessio Lombardi Date: Tue, 1 Dec 2020 12:01:18 +0000 Subject: [PATCH 27/28] Update SetHashFragment.cs --- BHoM_Engine/Modify/SetHashFragment.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/BHoM_Engine/Modify/SetHashFragment.cs b/BHoM_Engine/Modify/SetHashFragment.cs index 028f914cb..c71034501 100644 --- a/BHoM_Engine/Modify/SetHashFragment.cs +++ b/BHoM_Engine/Modify/SetHashFragment.cs @@ -38,7 +38,7 @@ public static partial class Modify { [Description("Computes the hash of the input BHoMObjects and stores it in a HashFragment for each of them." + "\nIf the hashFragment already existed, it is replaced.")] - public static List SetHashFragment(this IEnumerable objs, ComparisonConfig comparisonConfig = null) where T : IBHoMObject + public static IEnumerable SetHashFragment(this IEnumerable objs, ComparisonConfig comparisonConfig = null) where T : IBHoMObject { // Each object will be cloned to avoid modification by reference. List objs_cloned = new List(); @@ -52,7 +52,7 @@ public static List SetHashFragment(this IEnumerable objs, ComparisonCon [Description("Computes the hash of the BHoMObject and stores it in a HashFragment." + "\nIf the hashFragment already existed, it is replaced.")] - public static T SetHashFragment(T obj, ComparisonConfig comparisonConfig = null) where T : IBHoMObject + public static T SetHashFragment(this T obj, ComparisonConfig comparisonConfig = null) where T : IBHoMObject { // Calculate and set the object hashes string hash = obj.Hash(comparisonConfig); @@ -62,7 +62,7 @@ public static T SetHashFragment(T obj, ComparisonConfig comparisonConfig = nu [Description("Clones the IBHoMObject, computes its hash and stores it in a HashFragment." + "\nIf the hashFragment already existed, it is replaced.")] - public static T SetHashFragment(T obj, string hash) where T : IBHoMObject + public static T SetHashFragment(this T obj, string hash) where T : IBHoMObject { // Clone the current object to avoid modification by reference. T obj_cloned = BH.Engine.Base.Query.DeepClone(obj); From 0932595930917bb0ed94f76f42aca19d5c14b582 Mon Sep 17 00:00:00 2001 From: Alessio Lombardi Date: Tue, 1 Dec 2020 16:47:34 +0000 Subject: [PATCH 28/28] Update SetHashFragment.cs --- BHoM_Engine/Modify/SetHashFragment.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BHoM_Engine/Modify/SetHashFragment.cs b/BHoM_Engine/Modify/SetHashFragment.cs index c71034501..1069627cd 100644 --- a/BHoM_Engine/Modify/SetHashFragment.cs +++ b/BHoM_Engine/Modify/SetHashFragment.cs @@ -38,7 +38,7 @@ public static partial class Modify { [Description("Computes the hash of the input BHoMObjects and stores it in a HashFragment for each of them." + "\nIf the hashFragment already existed, it is replaced.")] - public static IEnumerable SetHashFragment(this IEnumerable objs, ComparisonConfig comparisonConfig = null) where T : IBHoMObject + public static List SetHashFragment(this List objs, ComparisonConfig comparisonConfig = null) where T : IBHoMObject { // Each object will be cloned to avoid modification by reference. List objs_cloned = new List();