Exemplo n.º 1
0
        public static string Hash(this IObject iObj, ComparisonConfig comparisonConfig = null, bool hashFromFragment = false)
        {
            if (iObj == null)
            {
                BH.Engine.Reflection.Compute.RecordError("Cannot query the hash of a null object.");
                return("");
            }

            if (hashFromFragment && iObj is IBHoMObject)
            {
                // Instead of computing the Hash, first tryGet the hash in HashFragment
                string hash = (iObj as IBHoMObject).FindFragment <HashFragment>()?.Hash;

                if (!string.IsNullOrWhiteSpace(hash))
                {
                    return(hash);
                }
            }

            // ------ SET UP OF CONFIGURATION ------

            // 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 PropertyExceptions of the config.
            cc.PropertyExceptions = cc.PropertyExceptions ?? new List <string>();
            if (!cc.PropertyExceptions.Contains(nameof(BHoMObject.BHoM_Guid)))
            {
                cc.PropertyExceptions.Add(nameof(BHoMObject.BHoM_Guid));
            }

            // Process the "PropertiesToInclude" property.
            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".
                // Works only for top-level properties.
                IEnumerable <string> 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
            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_copy as IBHoMObject;

            if (bhomobj != null)
            {
                List <IHashFragment> hashFragments = bhomobj.GetAllFragments(typeof(IHashFragment)).OfType <IHashFragment>().ToList();
                hashFragments.ForEach(f => bhomobj.Fragments.Remove(f.GetType()));
                iObj_copy = bhomobj;
            }

            // ----- HASH -----

            // Compute the defining string.
            string hashString = DefiningString(iObj_copy, cc, fractionalDigits, 0);

            if (string.IsNullOrWhiteSpace(hashString))
            {
                // This means that:
                // - all properties of the input object were disregarded due to the settings specified in the ComparisonConfig, or
                // - all properties of the input object that were not disregarded were null or empty,
                // Since a hash has to be always returned, for this scenario we are forced to build a defining string out of the type full name.
                hashString = iObj_copy.GetType().FullName;
            }

            // Return the SHA256 hash of the defining string.
            return(SHA256Hash(hashString));
        }