Browse Source

added equality check to JsonContent
more code cleanup

Vicente Penades 3 years ago
parent
commit
3dc26e16ec

+ 34 - 0
src/Shared/_Extensions.cs

@@ -246,6 +246,40 @@ namespace SharpGLTF
             return false;
             return false;
         }
         }
 
 
+        #if !NET6_0_OR_GREATER
+        #pragma warning disable CA1307 // Specify StringComparison for clarity
+        #pragma warning disable CA1304 // Specify CultureInfo
+
+        internal static bool Contains(this string self, string value, StringComparison comparisonType)
+        {
+            switch (comparisonType)
+            {
+                case StringComparison.OrdinalIgnoreCase:
+                case StringComparison.InvariantCultureIgnoreCase:
+                    return self.ToUpperInvariant().Contains(value.ToUpperInvariant());
+                case StringComparison.CurrentCultureIgnoreCase:
+                    return self.ToUpper().Contains(value.ToUpper());
+                default: return self.Contains(value);
+            }
+        }
+
+        internal static int GetHashCode(this string text, StringComparison comparisonType)
+        {
+            switch (comparisonType)
+            {
+                case StringComparison.OrdinalIgnoreCase:
+                case StringComparison.InvariantCultureIgnoreCase:
+                    return text.ToUpperInvariant().GetHashCode();
+                case StringComparison.CurrentCultureIgnoreCase:
+                    return text.ToUpper().GetHashCode();
+                default: return text.GetHashCode();
+            }
+        }
+
+        #pragma warning restore CA1304 // Specify CultureInfo
+        #pragma warning restore CA1307 // Specify StringComparison for clarity
+        #endif
+
         internal static int GetContentHashCode<T>(this IEnumerable<T> collection, int count = int.MaxValue)
         internal static int GetContentHashCode<T>(this IEnumerable<T> collection, int count = int.MaxValue)
         {
         {
             if (collection == null) return 0;
             if (collection == null) return 0;

+ 112 - 41
src/SharpGLTF.Core/IO/JsonContent.Impl.cs

@@ -20,6 +20,8 @@ namespace SharpGLTF.IO
 
 
     struct _JsonStaticUtils
     struct _JsonStaticUtils
     {
     {
+        #region serialization
+
         public static string ToJson(Object obj, JSONOPTIONS options)
         public static string ToJson(Object obj, JSONOPTIONS options)
         {
         {
             if (obj == null) return String.Empty;
             if (obj == null) return String.Empty;
@@ -175,7 +177,11 @@ namespace SharpGLTF.IO
             return true;
             return true;
         }
         }
 
 
-        public static bool AreEqualByContent(Object x, Object y, float precission)
+        #endregion
+
+        #region equality
+
+        public static bool AreEqualByContent(Object x, Object y, float precission = 0)
         {
         {
             #pragma warning disable IDE0041 // Use 'is null' check
             #pragma warning disable IDE0041 // Use 'is null' check
             if (Object.ReferenceEquals(x, y)) return true;
             if (Object.ReferenceEquals(x, y)) return true;
@@ -185,35 +191,17 @@ namespace SharpGLTF.IO
 
 
             if (x is IConvertible xval && y is IConvertible yval)
             if (x is IConvertible xval && y is IConvertible yval)
             {
             {
-                return AreEqual(xval, yval, precission);
+                return AreValuesEqual(xval, yval, precission);
             }
             }
 
 
             if (x is IReadOnlyList<object> xarr && y is IReadOnlyList<object> yarr)
             if (x is IReadOnlyList<object> xarr && y is IReadOnlyList<object> yarr)
             {
             {
-                if (xarr.Count != yarr.Count) return false;
-                int c = xarr.Count;
-
-                for (int i = 0; i < c; ++i)
-                {
-                    if (!AreEqualByContent(xarr[i], yarr[i], precission)) return false;
-                }
-
-                return true;
+                return _JsonArray.AreEqualByContent(xarr, yarr, precission);
             }
             }
 
 
             if (x is IReadOnlyDictionary<string, object> xdic && y is IReadOnlyDictionary<string, object> ydic)
             if (x is IReadOnlyDictionary<string, object> xdic && y is IReadOnlyDictionary<string, object> ydic)
             {
             {
-                if (xdic.Count != ydic.Count) return false;
-
-                foreach (var key in xdic.Keys)
-                {
-                    if (!xdic.TryGetValue(key, out Object xdval)) return false;
-                    if (!ydic.TryGetValue(key, out Object ydval)) return false;
-
-                    if (!AreEqualByContent(xdval, ydval, precission)) return false;
-                }
-
-                return true;
+                return _JsonObject.AreEqualByContent(xdic, ydic, precission);
             }
             }
 
 
             bool isValidType(Object z)
             bool isValidType(Object z)
@@ -230,30 +218,33 @@ namespace SharpGLTF.IO
             return false;
             return false;
         }
         }
 
 
-        private static bool AreEqual(IConvertible x, IConvertible y, float precission)
+        private static bool AreValuesEqual(IConvertible x, IConvertible y, float precission = 0)
         {
         {
             var xc = x.GetTypeCode();
             var xc = x.GetTypeCode();
             var yc = y.GetTypeCode();
             var yc = y.GetTypeCode();
 
 
-            if (xc == TypeCode.Decimal || yc == TypeCode.Decimal)
+            if (precission > 0)
             {
             {
-                var xf = y.ToDecimal(CULTURE.InvariantCulture);
-                var yf = y.ToDecimal(CULTURE.InvariantCulture);
-                return Math.Abs((double)(xf - yf)) < precission;
-            }
+                if (xc == TypeCode.Decimal || yc == TypeCode.Decimal)
+                {
+                    var xf = y.ToDecimal(CULTURE.InvariantCulture);
+                    var yf = y.ToDecimal(CULTURE.InvariantCulture);
+                    return Math.Abs((double)(xf - yf)) <= precission;
+                }
 
 
-            if (xc == TypeCode.Double || yc == TypeCode.Double)
-            {
-                var xf = y.ToDouble(CULTURE.InvariantCulture);
-                var yf = y.ToDouble(CULTURE.InvariantCulture);
-                return Math.Abs(xf - yf) < precission;
-            }
+                if (xc == TypeCode.Double || yc == TypeCode.Double)
+                {
+                    var xf = y.ToDouble(CULTURE.InvariantCulture);
+                    var yf = y.ToDouble(CULTURE.InvariantCulture);
+                    return Math.Abs(xf - yf) <= precission;
+                }
 
 
-            if (xc == TypeCode.Single || yc == TypeCode.Single)
-            {
-                var xf = y.ToSingle(CULTURE.InvariantCulture);
-                var yf = y.ToSingle(CULTURE.InvariantCulture);
-                return Math.Abs(xf - yf) < precission;
+                if (xc == TypeCode.Single || yc == TypeCode.Single)
+                {
+                    var xf = y.ToSingle(CULTURE.InvariantCulture);
+                    var yf = y.ToSingle(CULTURE.InvariantCulture);
+                    return Math.Abs(xf - yf) <= precission;
+                }
             }
             }
 
 
             if (xc == yc) return Object.Equals(x, y);
             if (xc == yc) return Object.Equals(x, y);
@@ -296,7 +287,7 @@ namespace SharpGLTF.IO
 
 
                 foreach (var kvp in xdic.OrderBy(item => item.Key))
                 foreach (var kvp in xdic.OrderBy(item => item.Key))
                 {
                 {
-                    h ^= kvp.Key.GetHashCode();
+                    h ^= kvp.Key.GetHashCode(StringComparison.InvariantCulture);
                     h ^= GetStructureHashCode(kvp.Value);
                     h ^= GetStructureHashCode(kvp.Value);
                     h *= 17;
                     h *= 17;
                 }
                 }
@@ -306,11 +297,14 @@ namespace SharpGLTF.IO
 
 
             throw new ArgumentException($"Invalid type: {x.GetType()}", nameof(x));
             throw new ArgumentException($"Invalid type: {x.GetType()}", nameof(x));
         }
         }
+
+        #endregion
     }
     }
 
 
     /// <summary>
     /// <summary>
     /// Represents an inmutable Json Array.
     /// Represents an inmutable Json Array.
     /// </summary>
     /// </summary>
+    [System.Diagnostics.DebuggerDisplay("Json List")]
     readonly struct _JsonArray : IReadOnlyList<object>, IJsonCollection, IList
     readonly struct _JsonArray : IReadOnlyList<object>, IJsonCollection, IList
     {
     {
         #region constructor
         #region constructor
@@ -414,8 +408,45 @@ namespace SharpGLTF.IO
 
 
         #region data
         #region data
 
 
+        [System.Diagnostics.DebuggerBrowsable(System.Diagnostics.DebuggerBrowsableState.RootHidden)]
         private readonly Array _Array;
         private readonly Array _Array;
 
 
+        public override int GetHashCode()
+        {
+            if (_Array == null || _Array.Length == 0) return 0;
+
+            int h = 0;
+            foreach (var item in _Array)
+            {
+                h ^= item.GetHashCode();
+                h *= 17;
+            }
+
+            return h;
+        }
+
+        public override bool Equals(object obj)
+        {
+            return obj is IReadOnlyList<object> other && AreEqualByContent(this, other);
+        }
+
+        public static bool AreEqualByContent(IReadOnlyList<object> xarr, IReadOnlyList<object> yarr, float precission = 0)
+        {
+            if (Object.ReferenceEquals(xarr, yarr)) return true;
+            if (xarr == null) return false;
+            if (yarr == null) return false;
+            if (xarr.Count != yarr.Count) return false;
+
+            int c = xarr.Count;
+
+            for (int i = 0; i < c; ++i)
+            {
+                if (!_JsonStaticUtils.AreEqualByContent(xarr[i], yarr[i], precission)) return false;
+            }
+
+            return true;
+        }
+
         #endregion
         #endregion
 
 
         #region properties
         #region properties
@@ -454,11 +485,12 @@ namespace SharpGLTF.IO
     }
     }
 
 
     /// <summary>
     /// <summary>
-    /// Represents an inmutable Json Object.
+    /// Represents an inmutable Json Object (a Dictionary).
     /// </summary>
     /// </summary>
     /// <remarks>
     /// <remarks>
     /// Supported by converter <see href="https://github.com/dotnet/runtime/blob/76904319b41a1dd0823daaaaae6e56769ed19ed3/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/IReadOnlyDictionaryOfTKeyTValueConverter.cs"/>
     /// Supported by converter <see href="https://github.com/dotnet/runtime/blob/76904319b41a1dd0823daaaaae6e56769ed19ed3/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/IReadOnlyDictionaryOfTKeyTValueConverter.cs"/>
     /// </remarks>
     /// </remarks>
+    [System.Diagnostics.DebuggerDisplay("Json Object")]
     readonly struct _JsonObject : IReadOnlyDictionary<string, object>, IDictionary, IJsonCollection
     readonly struct _JsonObject : IReadOnlyDictionary<string, object>, IDictionary, IJsonCollection
     {
     {
         #region constructor
         #region constructor
@@ -544,8 +576,47 @@ namespace SharpGLTF.IO
 
 
         #region data
         #region data
 
 
+        [System.Diagnostics.DebuggerBrowsable(System.Diagnostics.DebuggerBrowsableState.RootHidden)]
         private readonly Dictionary<String, Object> _Dictionary;
         private readonly Dictionary<String, Object> _Dictionary;
 
 
+        public override int GetHashCode()
+        {
+            if (_Dictionary == null || _Dictionary.Count == 0) return 0;
+
+            int h = 0;
+            foreach (var item in _Dictionary)
+            {
+                h ^= item.Key.GetHashCode(StringComparison.InvariantCulture);
+                h ^= item.Value.GetHashCode();
+                h *= 17;
+            }
+
+            return h;
+        }
+
+        public override bool Equals(object obj)
+        {
+            return obj is IReadOnlyDictionary<string, object> other && AreEqualByContent(this, other);
+        }
+
+        public static bool AreEqualByContent(IReadOnlyDictionary<string, object> xdic, IReadOnlyDictionary<string, object> ydic, float precission = 0)
+        {
+            if (Object.ReferenceEquals(xdic, ydic)) return true;
+            if (xdic == null) return false;
+            if (ydic == null) return false;
+            if (xdic.Count != ydic.Count) return false;
+
+            foreach (var key in xdic.Keys)
+            {
+                if (!xdic.TryGetValue(key, out Object xdval)) return false;
+                if (!ydic.TryGetValue(key, out Object ydval)) return false;
+
+                if (!_JsonStaticUtils.AreEqualByContent(xdval, ydval, precission)) return false;
+            }
+
+            return true;
+        }
+
         #endregion
         #endregion
 
 
         #region properties
         #region properties

+ 10 - 5
src/SharpGLTF.Core/IO/JsonContent.cs

@@ -18,7 +18,7 @@ namespace SharpGLTF.IO
     /// </remarks>
     /// </remarks>
     [System.ComponentModel.ImmutableObject(true)]
     [System.ComponentModel.ImmutableObject(true)]
     [System.Diagnostics.DebuggerDisplay("{ToDebuggerDisplay(),nq}")]
     [System.Diagnostics.DebuggerDisplay("{ToDebuggerDisplay(),nq}")]
-    public readonly struct JsonContent : ICloneable
+    public readonly struct JsonContent : ICloneable, IEquatable<JsonContent>
     {
     {
         #region debug
         #region debug
 
 
@@ -56,6 +56,8 @@ namespace SharpGLTF.IO
         private JsonContent(Object value)
         private JsonContent(Object value)
         {
         {
             _Content = value == null ? null : _JsonStaticUtils.Serialize(value);
             _Content = value == null ? null : _JsonStaticUtils.Serialize(value);
+
+            // don't store empty collections.
             if (_Content is IJsonCollection collection && collection.Count == 0)
             if (_Content is IJsonCollection collection && collection.Count == 0)
                 _Content = null;
                 _Content = null;
         }
         }
@@ -73,14 +75,17 @@ namespace SharpGLTF.IO
 
 
         public override int GetHashCode()
         public override int GetHashCode()
         {
         {
-            // until I figure a correct way of handling this...
-            throw new NotSupportedException("Do not use");
+            return _Content == null ? 0 : _Content.GetHashCode();
         }
         }
 
 
         public override bool Equals(object obj)
         public override bool Equals(object obj)
         {
         {
-            // until I figure a correct way of handling this...
-            throw new NotSupportedException($"Use {nameof(JsonContent.AreEqualByContent)} instead.");
+            return obj is JsonContent other && Equals(other);
+        }
+
+        public bool Equals(JsonContent other)
+        {
+            return Object.Equals(this._Content, other._Content);
         }
         }
 
 
         /// <summary>
         /// <summary>

+ 1 - 1
src/SharpGLTF.Core/Schema2/gltf.MaterialChannel.cs

@@ -55,7 +55,7 @@ namespace SharpGLTF.Schema2
         {
         {
             if (_Material == null) return 0;
             if (_Material == null) return 0;
 
 
-            return _Material.GetHashCode() ^ _Key.GetHashCode();
+            return _Material.GetHashCode() ^ _Key.GetHashCode(StringComparison.InvariantCulture);
         }
         }
 
 
         /// <inheritdoc />
         /// <inheritdoc />

+ 1 - 1
src/SharpGLTF.Core/Validation/ModelException.cs

@@ -84,7 +84,7 @@ namespace SharpGLTF.Validation
 
 
             var gen = mex._Generator;
             var gen = mex._Generator;
 
 
-            if (gen.ToUpperInvariant().Contains("SHARPGLTF"))
+            if (gen.Contains("SHARPGLTF", StringComparison.InvariantCultureIgnoreCase))
             {
             {
                 mex.MessageSuffix = $"Model generated by <{gen}> seems to be malformed.";
                 mex.MessageSuffix = $"Model generated by <{gen}> seems to be malformed.";
                 return;
                 return;

+ 0 - 4
src/SharpGLTF.Toolkit/BaseBuilder.cs

@@ -52,11 +52,7 @@ namespace SharpGLTF
 
 
         protected static int GetContentHashCode(BaseBuilder x)
         protected static int GetContentHashCode(BaseBuilder x)
         {
         {
-            #if NET6_0_OR_GREATER
             return x?.Name?.GetHashCode(StringComparison.InvariantCulture) ?? 0;
             return x?.Name?.GetHashCode(StringComparison.InvariantCulture) ?? 0;
-            #else
-            return x?.Name?.GetHashCode() ?? 0;
-            #endif
         }
         }
 
 
         protected static bool AreEqualByContent(BaseBuilder x, BaseBuilder y)
         protected static bool AreEqualByContent(BaseBuilder x, BaseBuilder y)

+ 1 - 1
src/SharpGLTF.Toolkit/Geometry/Packed/PackedPrimitiveBuilder.cs

@@ -179,7 +179,7 @@ namespace SharpGLTF.Geometry
             var attr = accessor.Attribute;
             var attr = accessor.Attribute;
             attr.Name = name;
             attr.Name = name;
 
 
-            return new SharpGLTF.Memory.MemoryAccessor(accessor.Data, attr);
+            return new Memory.MemoryAccessor(accessor.Data, attr);
         }
         }
 
 
         internal void CopyToMesh(Mesh dstMesh, Converter<TMaterial, Material> materialEvaluator)
         internal void CopyToMesh(Mesh dstMesh, Converter<TMaterial, Material> materialEvaluator)

+ 1 - 1
src/SharpGLTF.Toolkit/Materials/MaterialBuilder.cs

@@ -151,7 +151,7 @@ namespace SharpGLTF.Materials
             h ^= x.AlphaCutoff.GetHashCode();
             h ^= x.AlphaCutoff.GetHashCode();
             h ^= x.DoubleSided.GetHashCode();
             h ^= x.DoubleSided.GetHashCode();
             h ^= x.IndexOfRefraction.GetHashCode();
             h ^= x.IndexOfRefraction.GetHashCode();
-            h ^= x.ShaderStyle.GetHashCode();
+            h ^= x.ShaderStyle.GetHashCode(StringComparison.InvariantCulture);
 
 
             h ^= x._Channels
             h ^= x._Channels
                 .Select(item => ChannelBuilder.GetContentHashCode(item))
                 .Select(item => ChannelBuilder.GetContentHashCode(item))

+ 11 - 2
tests/SharpGLTF.Core.Tests/IO/JsonContentTests.cs

@@ -185,15 +185,24 @@ namespace SharpGLTF.IO
             Assert.IsTrue(AreEqual(a, b));
             Assert.IsTrue(AreEqual(a, b));
             Assert.IsTrue(AreEqual(a, c));            
             Assert.IsTrue(AreEqual(a, c));            
 
 
-            foreach (var dom in new[] { a, b, c})
+            foreach (var dom in new[] { a, b, c })
             {
             {
                 Assert.AreEqual("me", dom.GetValue<string>("author"));
                 Assert.AreEqual("me", dom.GetValue<string>("author"));
                 Assert.AreEqual(17, dom.GetValue<int>("integer1"));
                 Assert.AreEqual(17, dom.GetValue<int>("integer1"));
                 Assert.AreEqual(15.3f, dom.GetValue<float>("single1"));
                 Assert.AreEqual(15.3f, dom.GetValue<float>("single1"));
                 Assert.AreEqual(3, dom.GetValue<int>("array1", 2));
                 Assert.AreEqual(3, dom.GetValue<int>("array1", 2));
                 Assert.AreEqual(2, dom.GetValue<int>("dict2", "d", "a1"));
                 Assert.AreEqual(2, dom.GetValue<int>("dict2", "d", "a1"));
-            }            
+            }
+
+            Assert.AreEqual(b.GetHashCode(), c.GetHashCode());
+            Assert.AreEqual(b, c);
+
+            // clone & compare
+
+            var d = c.DeepClone();
 
 
+            Assert.AreEqual(c.GetHashCode(), d.GetHashCode());
+            Assert.AreEqual(c, d);
         }
         }
 
 
     }
     }