From f5c03427344427fc36336a0adf8c4517ba0029f9 Mon Sep 17 00:00:00 2001 From: Carnagion Date: Sat, 14 May 2022 11:22:34 +0100 Subject: [PATCH 1/5] Make Serializer.Specialized static --- Serializer.cs | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/Serializer.cs b/Serializer.cs index 94fee73..b6d3d1d 100644 --- a/Serializer.cs +++ b/Serializer.cs @@ -20,10 +20,10 @@ public class Serializer : ISerializer /// /// A of specialized s for specific s. These serializers will be used by the when possible. /// - public Dictionary Specialized + public static Dictionary Specialized // Must be static; making it instance will cause a stack overflow due to it being recursively created in inheriting classes { get; - } = new() + } = new(19) { {typeof(string), new SimpleSerializer()}, {typeof(char), new SimpleSerializer()}, @@ -58,7 +58,7 @@ public virtual XmlNode Serialize(object instance, XmlDocument? context = null) Type type = instance.GetType(); // Use a more specialized serializer if possible - ISerializer? serializer = this.GetSpecialSerializerForType(type); + ISerializer? serializer = Serializer.GetSpecialSerializerForType(type); if (serializer is not null) { return serializer.Serialize(instance, context); @@ -138,7 +138,7 @@ public virtual object Deserialize(XmlNode node, Type? type = null) type ??= Serializer.GetTypeToDeserialize(node) ?? throw new SerializationException(node, $"No {nameof(Type)} found to instantiate"); // Use a more specialized deserializer if possible - ISerializer? serializer = this.GetSpecialSerializerForType(type); + ISerializer? serializer = Serializer.GetSpecialSerializerForType(type); if (serializer is not null) { return serializer.Deserialize(node, type); @@ -226,32 +226,32 @@ select assembly.GetType(name)) .FirstOrDefault(); } - private ISerializer? GetSpecialSerializerForType(Type type) + private static ISerializer? GetSpecialSerializerForType(Type type) { ISerializer? serializer; if (type.IsGenericType) { - serializer = this.Specialized.GetValueOrDefault(type); + serializer = Serializer.Specialized.GetValueOrDefault(type); if (serializer is not null) { return serializer; } - Type? match = this.Specialized.Keys + Type? match = Serializer.Specialized.Keys .FirstOrDefault(type.IsExactlyGenericType); if (match is not null) { - return this.Specialized[match]; + return Serializer.Specialized[match]; } - match = this.Specialized.Keys + match = Serializer.Specialized.Keys .FirstOrDefault(type.DerivesFromGenericType); if (match is not null) { - return this.Specialized[match]; + return Serializer.Specialized[match]; } } else { - this.Specialized.TryGetValue(type, out serializer); + Serializer.Specialized.TryGetValue(type, out serializer); } return serializer; } From 53b82e84d333728ec445ebbdb32f8b70fbd207e2 Mon Sep 17 00:00:00 2001 From: Carnagion Date: Sat, 14 May 2022 11:23:16 +0100 Subject: [PATCH 2/5] Fix Serializer.Specialized ordering ambiguity --- Serializer.cs | 3 +- Utility/OrderedDictionary.cs | 297 +++++++++++++++++++++++++++++++++++ 2 files changed, 299 insertions(+), 1 deletion(-) create mode 100644 Utility/OrderedDictionary.cs diff --git a/Serializer.cs b/Serializer.cs index b6d3d1d..5ed59f2 100644 --- a/Serializer.cs +++ b/Serializer.cs @@ -5,6 +5,7 @@ using System.Xml; using Godot.Serialization.Specialized; +using Godot.Serialization.Utility; using Godot.Serialization.Utility.Exceptions; using Godot.Serialization.Utility.Extensions; @@ -20,7 +21,7 @@ public class Serializer : ISerializer /// /// A of specialized s for specific s. These serializers will be used by the when possible. /// - public static Dictionary Specialized // Must be static; making it instance will cause a stack overflow due to it being recursively created in inheriting classes + public static OrderedDictionary Specialized // Must be static; making it instance will cause a stack overflow due to it being recursively created in inheriting classes { get; } = new(19) diff --git a/Utility/OrderedDictionary.cs b/Utility/OrderedDictionary.cs new file mode 100644 index 0000000..0fd06ad --- /dev/null +++ b/Utility/OrderedDictionary.cs @@ -0,0 +1,297 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; + +namespace Godot.Serialization.Utility +{ + /// + /// Represents a generic collection of key-value pairs that can be accessed by the key or by the index, and preserves insertion order. + /// + /// The of key. + /// The of value. + public class OrderedDictionary : IDictionary, IReadOnlyDictionary where TKey : notnull + { + /// + /// Initializes a new with the default initial capacity. + /// + public OrderedDictionary() : this(10) + { + } + + /// + /// Initializes a new with the specified initial capacity. + /// + /// The initial number of key-value pairs the can contain. + public OrderedDictionary(int capacity) + { + this.dictionary = new(capacity); + this.list = new(capacity); + } + + private readonly Dictionary dictionary; + + private readonly List> list; + + /// + /// Returns the number of key-value pairs in the . + /// + public int Count + { + get + { + return this.dictionary.Count; + } + } + + /// + /// Returns an of keys in the . + /// + public IEnumerable Keys + { + get + { + return from pair in this.list + select pair.Key; + } + } + + /// + /// Returns an of values in the . + /// + public IEnumerable Values + { + get + { + return from pair in this.list + select pair.Value; + } + } + + ICollection IDictionary.Keys + { + get + { + return this.Keys.ToArray(); + } + } + + ICollection IDictionary.Values + { + get + { + return this.Values.ToArray(); + } + } + + IEnumerable IReadOnlyDictionary.Keys + { + get + { + return this.Keys; + } + } + + IEnumerable IReadOnlyDictionary.Values + { + get + { + return this.Values; + } + } + + bool ICollection>.IsReadOnly + { + get + { + return false; + } + } + + /// + /// Gets or sets the value associated with . + /// + /// The key. + public TValue this[TKey key] + { + get + { + return this.dictionary[key]; + } + set + { + this[this.IndexOfKey(key)] = value; + } + } + + /// + /// Gets or sets the value of the key at the specified index. + /// + /// The index of the key-value pair. + public TValue this[int index] + { + get + { + return this.dictionary[this.list[index].Key]; + } + set + { + TKey key = this.list[index].Key; + this.dictionary[key] = value; + this.list[index] = new(key, value); + } + } + + /// + /// Determines if the contains as a key. + /// + /// The key to search for. + /// if is contained as a key in the , else . + public bool ContainsKey(TKey key) + { + return this.dictionary.TryGetValue(key, out _); + } + + /// + /// Determines if the contains as a value. + /// + /// The value to search for. + /// if is contained as a value in the , else . + public bool ContainsValue(TValue value) + { + return this.dictionary.ContainsValue(value); + } + + /// + /// Gets the value associated with . + /// + /// The key of the value to get. + /// When this method returns, contains the value associated with the specified key if the key is found; otherwise, the default value for the type of the value parameter. This parameter is passed uninitialized. + /// if the contains an element with the specified key, else . + /// Thrown if is + public bool TryGetValue(TKey key, out TValue value) + { + return key is null ? throw new ArgumentNullException(nameof(key)) : this.dictionary.TryGetValue(key, out value); + } + + /// + /// Returns the index of . + /// + /// The key. + /// The zero-based index of , or -1 if the does not contain it. + public int IndexOfKey(TKey key) + { + return this.Keys.IndexOf(key); + } + + /// + /// Returns the index of . + /// + /// The value. + /// The zero-based index of , or -1 if the does not contain it. + public int IndexOfValue(TValue value) + { + return this.Values.IndexOf(value); + } + + /// + /// Adds and to the . + /// + /// The key. + /// The value. + /// The index of the newly added key-value pair. + public int Add(TKey key, TValue value) + { + this.dictionary.Add(key, value); + this.list.Add(new(key, value)); + return this.Count - 1; + } + + /// + /// Inserts a key-value pair into the at the specified index. + /// + /// The index of the key-value pair. + /// The key. + /// The value. + public void InsertAt(int index, TKey key, TValue value) + { + this.dictionary.Add(key, value); + this.list.Insert(index, new(key, value)); + } + + /// + /// Removes the value with the specified key from the . + /// + /// The key of the element to remove. + /// if the element is found and removed, else . + public bool Remove(TKey key) + { + bool removed = this.dictionary.Remove(key); + if (removed) + { + this.list.RemoveAt(this.IndexOfKey(key)); + } + return removed; + } + + /// + /// Removes the key-value pair at the specified index in the . + /// + /// The zero-based index of the key-value pair (according to insertion order). + public void RemoveAt(int index) + { + TKey key = this.list[index].Key; + this.list.RemoveAt(index); + this.dictionary.Remove(key); + } + + /// + /// Removes all key-value pairs from the . + /// + public void Clear() + { + this.dictionary.Clear(); + this.list.Clear(); + } + + /// + /// Returns an that iterates through the in the order of insertion of items. + /// + /// An for the . + public IEnumerator> GetEnumerator() + { + return this.list.GetEnumerator(); + } + + bool ICollection>.Contains(KeyValuePair pair) + { + return this.list.Contains(pair); + } + + void IDictionary.Add(TKey key, TValue value) + { + this.Add(key, value); + } + + void ICollection>.Add(KeyValuePair pair) + { + (TKey key, TValue value) = pair; + this.Add(key, value); + } + + bool ICollection>.Remove(KeyValuePair pair) + { + return this.Remove(pair.Key); + } + + void ICollection>.CopyTo(KeyValuePair[] array, int index) + { + this.list.CopyTo(array, index); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return this.GetEnumerator(); + } + } +} \ No newline at end of file From 0133f041739917da76f6be65b8c0ddb51ac0f21a Mon Sep 17 00:00:00 2001 From: Carnagion Date: Sat, 14 May 2022 11:24:17 +0100 Subject: [PATCH 3/5] Tweak documentation comment --- Serializer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Serializer.cs b/Serializer.cs index 5ed59f2..eebac57 100644 --- a/Serializer.cs +++ b/Serializer.cs @@ -19,7 +19,7 @@ public class Serializer : ISerializer private const BindingFlags instanceBindingFlags = BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public; /// - /// A of specialized s for specific s. These serializers will be used by the when possible. + /// An of specialized s for specific s. These serializers will be used by the when possible. /// public static OrderedDictionary Specialized // Must be static; making it instance will cause a stack overflow due to it being recursively created in inheriting classes { From bd7f531e67b2f2d3d5182df1fda422c446dfa82e Mon Sep 17 00:00:00 2001 From: Carnagion Date: Sat, 14 May 2022 12:11:44 +0100 Subject: [PATCH 4/5] Tweak csproj file --- GDSerializer.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/GDSerializer.csproj b/GDSerializer.csproj index dea6434..d21f1ba 100644 --- a/GDSerializer.csproj +++ b/GDSerializer.csproj @@ -7,7 +7,7 @@ true true - 0.1.0 + 0.1.1 GDSerializer Carnagion An XML (de)serialization framework for Godot's C# API. From 16500cc06796400a1c608bfcc685922a5706ac86 Mon Sep 17 00:00:00 2001 From: Carnagion Date: Sat, 14 May 2022 12:20:47 +0100 Subject: [PATCH 5/5] Tweak README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 75cd70b..d29ba5f 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ GDSerializer is available as a [NuGet package](https://www.nuget.org/packages/GD Simply include the following lines in a Godot project's `.csproj` file (either by editing the file manually or letting an IDE install the package): ```xml - + ```