// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 // Standard Library using System; using System.Collections.Generic; using System.Linq; // Unity using UnityEngine; namespace AWS.GameKit.Runtime.Utils { /// /// A Serializable that remembers the order the elements were added. /// /// /// [Serialization] /// /// The class is not Serializable in C# by default. /// This class is able to serialize a dictionary by breaking out the key/value pairs into a key list and value list which can be Serialized. /// /// /// [Order Remembered] /// /// This collection remembers the order the elements were added. /// The ordered elements can be accessed through , , and . /// The elements can be sorted in a custom order by calling . /// /// /// [Time Complexity] /// /// Each method has the following time complexity: /// /// - O(1) /// - O(1) /// - O(1) /// - O(1) /// - O(N) /// - depends on the provided callback function, usually O(N log(N)) /// - O(N) /// /// /// /// The type of the keys in the dictionary. /// The type of the values in the dictionary. [Serializable] public class SerializableOrderedDictionary : ISerializationCallbackReceiver { protected IDictionary _dict = new Dictionary(); // Serialization // The keys and values are stored in sorted order. [HideInInspector] [SerializeField] protected List _keys = new List(); [HideInInspector] [SerializeField] protected List _values = new List(); /// /// The number of key/value pairs in the collection. /// public int Count => _dict.Count; /// /// Get all keys in the collection in the order they were added or later sorted with . /// public IEnumerable Keys => _keys; /// /// Get all values in the collection in the order they were added or later sorted with . /// public IEnumerable Values => _values; /// /// Get all key/value pairs in the collection in the order they were added or later sorted with . /// public IEnumerable> KeyValuePairs => _keys.Zip(_values, (key, value) => new KeyValuePair(key, value)); /// /// This method is called by Unity when this class needs to be Serialized. /// public virtual void OnBeforeSerialize() { // Nothing to do. // The _keys and _values lists are already up-to-date. They will be used in OnAfterDeserialize() to reconstruct this object. } /// /// This method is called by Unity when this class needs to be Deserialized. /// public virtual void OnAfterDeserialize() { // Rebuild the dictionary from the serialized keys/values. _dict.Clear(); if (_keys.Count != _values.Count) { Logging.LogError($"There were {_keys.Count} keys and {_values.Count} values found while deserializing the {nameof(SerializableOrderedDictionary)}. " + $"This collection may contain inaccurate data."); } int lowestCount = Math.Min(_keys.Count, _values.Count); for (int i = 0; i < lowestCount; ++i) { _dict.Add(_keys[i], _values[i]); } } /// /// Determine whether the collection contains the specified key. /// /// The key to locate in the collection. /// Thrown when the provided key is null. public virtual bool ContainsKey(TKey key) { return _dict.ContainsKey(key); } /// /// Add the specified key and value to the dictionary at the end of the ordered list. /// /// The key of the element to add. /// The value of the element to add. /// Thrown when the provided key is null. /// Thrown when an element with the same key already exists in the collection. public virtual void Add(TKey key, TValue value) { _dict.Add(key, value); _keys.Add(key); _values.Add(value); } /// /// Set the value associated with the specified key. /// /// The key of the value to set. /// The value associated with the specified key. If the specified key is not found, creates a new element with the specified key. Otherwise overwrites the existing element. /// Thrown when the provided key is null. public virtual void SetValue(TKey key, TValue value) { if (_dict.ContainsKey(key)) { // Update existing value _dict[key] = value; } else { // Add new element Add(key, value); } } /// /// Get the value associated with the specified key. /// /// The key of the value to get. /// The value associated with the specified key. If the specified key is not found, throws a . /// Thrown when the provided key is null. /// Thrown when the specified key does not exist in the collection. public virtual TValue GetValue(TKey key) { return _dict[key]; } /// /// Remove the value with the specified key from the . /// /// The key of the element to remove. /// Thrown when the provided key is null. public virtual void Remove(TKey key) { if (!_dict.ContainsKey(key)) { return; } _dict.Remove(key); int indexOfElement = _keys.IndexOf(key); _keys.RemoveAt(indexOfElement); _values.RemoveAt(indexOfElement); } /// /// Remove all elements from the collection. /// public virtual void Clear() { _dict.Clear(); _keys.Clear(); _values.Clear(); } /// /// Sort the collection's elements in-place using using the provided sorting function. /// /// /// After calling this method, the new ordering is reflected in , , and . /// /// A function which sorts a list of key/value pairs. The function's input is this collection's ordered key/value pairs. /// The function's output is the same key/value pairs in a newly sorted order. public virtual void Sort(Func>, IEnumerable>> sortFunction) { IEnumerable> sortedElements = sortFunction(KeyValuePairs.ToList()); _keys.Clear(); _values.Clear(); foreach (KeyValuePair keyValuePair in sortedElements) { _keys.Add(keyValuePair.Key); _values.Add(keyValuePair.Value); } } } }