Skip to content

Commit

Permalink
Merge pull request #3 from Carnagion/development
Browse files Browse the repository at this point in the history
Merge v0.1.1 into stable
  • Loading branch information
Carnagion authored May 14, 2022
2 parents 0adf0a0 + 16500cc commit bb75ca6
Show file tree
Hide file tree
Showing 4 changed files with 312 additions and 14 deletions.
2 changes: 1 addition & 1 deletion GDSerializer.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
<!-- Workaround as Godot does not know how to properly load NuGet packages -->
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<PackageVersion>0.1.0</PackageVersion>
<PackageVersion>0.1.1</PackageVersion>
<Title>GDSerializer</Title>
<Authors>Carnagion</Authors>
<Description>An XML (de)serialization framework for Godot's C# API.</Description>
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
<ItemGroup>
<PackageReference Include="GDSerializer" Version="0.1.0" />
<PackageReference Include="GDSerializer" Version="0.1.1" />
</ItemGroup>
```

Expand Down
25 changes: 13 additions & 12 deletions Serializer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -18,12 +19,12 @@ public class Serializer : ISerializer
private const BindingFlags instanceBindingFlags = BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public;

/// <summary>
/// A <see cref="Dictionary{TKey,TValue}"/> of specialized <see cref="ISerializer"/>s for specific <see cref="Type"/>s. These serializers will be used by the <see cref="Serializer"/> when possible.
/// An <see cref="OrderedDictionary{TKey,TValue}"/> of specialized <see cref="ISerializer"/>s for specific <see cref="Type"/>s. These serializers will be used by the <see cref="Serializer"/> when possible.
/// </summary>
public Dictionary<Type, ISerializer> Specialized
public static OrderedDictionary<Type, ISerializer> 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()},
Expand Down Expand Up @@ -58,7 +59,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);
Expand Down Expand Up @@ -138,7 +139,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);
Expand Down Expand Up @@ -226,32 +227,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;
}
Expand Down
297 changes: 297 additions & 0 deletions Utility/OrderedDictionary.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,297 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;

namespace Godot.Serialization.Utility
{
/// <summary>
/// Represents a generic collection of key-value pairs that can be accessed by the key or by the index, and preserves insertion order.
/// </summary>
/// <typeparam name="TKey">The <see cref="System.Type"/> of key.</typeparam>
/// <typeparam name="TValue">The <see cref="System.Type"/> of value.</typeparam>
public class OrderedDictionary<TKey, TValue> : IDictionary<TKey, TValue>, IReadOnlyDictionary<TKey, TValue> where TKey : notnull
{
/// <summary>
/// Initializes a new <see cref="OrderedDictionary{TKey,TValue}"/> with the default initial capacity.
/// </summary>
public OrderedDictionary() : this(10)
{
}

/// <summary>
/// Initializes a new <see cref="OrderedDictionary{TKey,TValue}"/> with the specified initial capacity.
/// </summary>
/// <param name="capacity">The initial number of key-value pairs the <see cref="OrderedDictionary{TKey,TValue}"/> can contain.</param>
public OrderedDictionary(int capacity)
{
this.dictionary = new(capacity);
this.list = new(capacity);
}

private readonly Dictionary<TKey, TValue> dictionary;

private readonly List<KeyValuePair<TKey, TValue>> list;

/// <summary>
/// Returns the number of key-value pairs in the <see cref="OrderedDictionary{TKey,TValue}"/>.
/// </summary>
public int Count
{
get
{
return this.dictionary.Count;
}
}

/// <summary>
/// Returns an <see cref="IEnumerable{T}"/> of keys in the <see cref="OrderedDictionary{TKey,TValue}"/>.
/// </summary>
public IEnumerable<TKey> Keys
{
get
{
return from pair in this.list
select pair.Key;
}
}

/// <summary>
/// Returns an <see cref="IEnumerable{T}"/> of values in the <see cref="OrderedDictionary{TKey,TValue}"/>.
/// </summary>
public IEnumerable<TValue> Values
{
get
{
return from pair in this.list
select pair.Value;
}
}

ICollection<TKey> IDictionary<TKey, TValue>.Keys
{
get
{
return this.Keys.ToArray();
}
}

ICollection<TValue> IDictionary<TKey, TValue>.Values
{
get
{
return this.Values.ToArray();
}
}

IEnumerable<TKey> IReadOnlyDictionary<TKey, TValue>.Keys
{
get
{
return this.Keys;
}
}

IEnumerable<TValue> IReadOnlyDictionary<TKey, TValue>.Values
{
get
{
return this.Values;
}
}

bool ICollection<KeyValuePair<TKey, TValue>>.IsReadOnly
{
get
{
return false;
}
}

/// <summary>
/// Gets or sets the value associated with <paramref name="key"/>.
/// </summary>
/// <param name="key">The key.</param>
public TValue this[TKey key]
{
get
{
return this.dictionary[key];
}
set
{
this[this.IndexOfKey(key)] = value;
}
}

/// <summary>
/// Gets or sets the value of the key at the specified index.
/// </summary>
/// <param name="index">The index of the key-value pair.</param>
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);
}
}

/// <summary>
/// Determines if the <see cref="OrderedDictionary{TKey,TValue}"/> contains <paramref name="key"/> as a key.
/// </summary>
/// <param name="key">The key to search for.</param>
/// <returns><see langword="true"/> if <paramref name="key"/> is contained as a key in the <see cref="OrderedDictionary{TKey,TValue}"/>, else <see langword="false"/>.</returns>
public bool ContainsKey(TKey key)
{
return this.dictionary.TryGetValue(key, out _);
}

/// <summary>
/// Determines if the <see cref="OrderedDictionary{TKey,TValue}"/> contains <paramref name="value"/> as a value.
/// </summary>
/// <param name="value">The value to search for.</param>
/// <returns><see langword="true"/> if <paramref name="value"/> is contained as a value in the <see cref="OrderedDictionary{TKey,TValue}"/>, else <see langword="false"/>.</returns>
public bool ContainsValue(TValue value)
{
return this.dictionary.ContainsValue(value);
}

/// <summary>
/// Gets the value associated with <paramref name="key"/>.
/// </summary>
/// <param name="key">The key of the value to get.</param>
/// <param name="value">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.</param>
/// <returns><see langword="true"/> if the <see cref="OrderedDictionary{TKey,TValue}"/> contains an element with the specified key, else <see langword="false"/>.</returns>
/// <exception cref="ArgumentNullException">Thrown if <paramref name="key"/> is <see langword="null"/></exception>
public bool TryGetValue(TKey key, out TValue value)
{
return key is null ? throw new ArgumentNullException(nameof(key)) : this.dictionary.TryGetValue(key, out value);
}

/// <summary>
/// Returns the index of <paramref name="key"/>.
/// </summary>
/// <param name="key">The key.</param>
/// <returns>The zero-based index of <paramref name="key"/>, or -1 if the <see cref="OrderedDictionary{TKey,TValue}"/> does not contain it.</returns>
public int IndexOfKey(TKey key)
{
return this.Keys.IndexOf(key);
}

/// <summary>
/// Returns the index of <paramref name="value"/>.
/// </summary>
/// <param name="value">The value.</param>
/// <returns>The zero-based index of <paramref name="value"/>, or -1 if the <see cref="OrderedDictionary{TKey,TValue}"/> does not contain it.</returns>
public int IndexOfValue(TValue value)
{
return this.Values.IndexOf(value);
}

/// <summary>
/// Adds <paramref name="key"/> and <paramref name="value"/> to the <see cref="OrderedDictionary{TKey,TValue}"/>.
/// </summary>
/// <param name="key">The key.</param>
/// <param name="value">The value.</param>
/// <returns>The index of the newly added key-value pair.</returns>
public int Add(TKey key, TValue value)
{
this.dictionary.Add(key, value);
this.list.Add(new(key, value));
return this.Count - 1;
}

/// <summary>
/// Inserts a key-value pair into the <see cref="OrderedDictionary{TKey,TValue}"/> at the specified index.
/// </summary>
/// <param name="index">The index of the key-value pair.</param>
/// <param name="key">The key.</param>
/// <param name="value">The value.</param>
public void InsertAt(int index, TKey key, TValue value)
{
this.dictionary.Add(key, value);
this.list.Insert(index, new(key, value));
}

/// <summary>
/// Removes the value with the specified key from the <see cref="OrderedDictionary{TKey,TValue}"/>.
/// </summary>
/// <param name="key">The key of the element to remove.</param>
/// <returns><see langword="true"/> if the element is found and removed, else <see langword="false"/>.</returns>
public bool Remove(TKey key)
{
bool removed = this.dictionary.Remove(key);
if (removed)
{
this.list.RemoveAt(this.IndexOfKey(key));
}
return removed;
}

/// <summary>
/// Removes the key-value pair at the specified index in the <see cref="OrderedDictionary{TKey,TValue}"/>.
/// </summary>
/// <param name="index">The zero-based index of the key-value pair (according to insertion order).</param>
public void RemoveAt(int index)
{
TKey key = this.list[index].Key;
this.list.RemoveAt(index);
this.dictionary.Remove(key);
}

/// <summary>
/// Removes all key-value pairs from the <see cref="OrderedDictionary{TKey,TValue}"/>.
/// </summary>
public void Clear()
{
this.dictionary.Clear();
this.list.Clear();
}

/// <summary>
/// Returns an <see cref="IEnumerator{T}"/> that iterates through the <see cref="OrderedDictionary{TKey,TValue}"/> in the order of insertion of items.
/// </summary>
/// <returns>An <see cref="IEnumerator{T}"/> for the <see cref="OrderedDictionary{TKey,TValue}"/>.</returns>
public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
{
return this.list.GetEnumerator();
}

bool ICollection<KeyValuePair<TKey, TValue>>.Contains(KeyValuePair<TKey, TValue> pair)
{
return this.list.Contains(pair);
}

void IDictionary<TKey, TValue>.Add(TKey key, TValue value)
{
this.Add(key, value);
}

void ICollection<KeyValuePair<TKey, TValue>>.Add(KeyValuePair<TKey, TValue> pair)
{
(TKey key, TValue value) = pair;
this.Add(key, value);
}

bool ICollection<KeyValuePair<TKey, TValue>>.Remove(KeyValuePair<TKey, TValue> pair)
{
return this.Remove(pair.Key);
}

void ICollection<KeyValuePair<TKey, TValue>>.CopyTo(KeyValuePair<TKey, TValue>[] array, int index)
{
this.list.CopyTo(array, index);
}

IEnumerator IEnumerable.GetEnumerator()
{
return this.GetEnumerator();
}
}
}

0 comments on commit bb75ca6

Please sign in to comment.