/****************************************************************************** * Copyright (C) Ultraleap, Inc. 2011-2021. * * * * Use subject to the terms of the Apache License 2.0 available at * * http://www.apache.org/licenses/LICENSE-2.0, or another agreement * * between Ultraleap and you, your company or other organization. * ******************************************************************************/ using System; using System.Collections; using System.Collections.Generic; using System.Linq; using System.Text; using UnityEngine; namespace Leap.Unity { [Obsolete("It is no longer required to annotate SerializableDictionary with an SDictionary attribute")] public class SDictionaryAttribute : PropertyAttribute { } public abstract class SerializableDictionaryBase { } public interface ICanReportDuplicateInformation { #if UNITY_EDITOR List GetDuplicationInformation(); void ClearDuplicates(); #endif } public interface ISerializableDictionary { float KeyDisplayRatio(); } /// /// In order to have this class be serialized, you will always need to create your own /// non-generic version specific to your needs. This is the same workflow that exists /// for using the UnityEvent class as well. /// public class SerializableDictionary : SerializableDictionaryBase, IEnumerable>, ICanReportDuplicateInformation, ISerializationCallbackReceiver, ISerializableDictionary { [SerializeField] private List _keys = new List(); [SerializeField] private List _values = new List(); [NonSerialized] private Dictionary _dictionary = new Dictionary(); #region DICTIONARY API public TValue this[TKey key] { get { return _dictionary[key]; } set { _dictionary[key] = value; } } public Dictionary.KeyCollection Keys { get { return _dictionary.Keys; } } public Dictionary.ValueCollection Values { get { return _dictionary.Values; } } public int Count { get { return _dictionary.Count; } } public void Add(TKey key, TValue value) { _dictionary.Add(key, value); } public void Clear() { _dictionary.Clear(); } public bool ContainsKey(TKey key) { return _dictionary.ContainsKey(key); } public bool ContainsValue(TValue value) { return _dictionary.ContainsValue(value); } public bool Remove(TKey key) { return _dictionary.Remove(key); } public bool TryGetValue(TKey key, out TValue value) { return _dictionary.TryGetValue(key, out value); } public Dictionary.Enumerator GetEnumerator() { return _dictionary.GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() { return _dictionary.GetEnumerator(); } public static implicit operator Dictionary(SerializableDictionary serializableDictionary) { return serializableDictionary._dictionary; } #endregion /// /// Returns how much of the display space should be allocated to the key. /// Should be a value in the range 0-1. /// public virtual float KeyDisplayRatio() { return 0.5f; } public override string ToString() { StringBuilder toReturn = new StringBuilder(); List keys = _dictionary.Keys.ToList(); List values = _dictionary.Values.ToList(); toReturn.Append("["); for (int i = 0; i < keys.Count; i++) { toReturn.Append("{"); toReturn.Append(keys[i].ToString()); toReturn.Append(" : "); toReturn.Append(values[i].ToString()); toReturn.Append("}, \n"); } toReturn.Remove(toReturn.Length - 3, 3); toReturn.Append("]"); return toReturn.ToString(); } public void OnAfterDeserialize() { _dictionary.Clear(); if (_keys != null && _values != null) { int count = Mathf.Min(_keys.Count, _values.Count); for (int i = 0; i < count; i++) { TKey key = _keys[i]; TValue value = _values[i]; if (key == null) { continue; } _dictionary[key] = value; } } #if !UNITY_EDITOR _keys.Clear(); _values.Clear(); #endif } #if UNITY_EDITOR public List GetDuplicationInformation() { Dictionary info = new Dictionary(); for (int i = 0; i < _keys.Count; i++) { TKey key = _keys[i]; if (key == null) { continue; } if (info.ContainsKey(key)) { info[key]++; } else { info[key] = 1; } } List dups = new List(); for (int i = 0; i < _keys.Count; i++) { TKey key = _keys[i]; if (key == null) { continue; } dups.Add(info[key]); } return dups; } public void ClearDuplicates() { HashSet takenKeys = new HashSet(); for (int i = 0; i < _keys.Count; i++) { TKey key = _keys[i]; if (takenKeys.Contains(key)) { _keys.RemoveAt(i); _values.RemoveAt(i); i--; } else { takenKeys.Add(key); } } } #endif public void OnBeforeSerialize() { if (_keys == null) { _keys = new List(); } if (_values == null) { _values = new List(); } #if UNITY_EDITOR for (int i = _keys.Count; i-- != 0;) { TKey key = _keys[i]; if (key == null) continue; if (!_dictionary.ContainsKey(key)) { _keys.RemoveAt(i); _values.RemoveAt(i); } } #endif Dictionary.Enumerator enumerator = _dictionary.GetEnumerator(); while (enumerator.MoveNext()) { var pair = enumerator.Current; #if UNITY_EDITOR if (!_keys.Contains(pair.Key)) { _keys.Add(pair.Key); _values.Add(pair.Value); } #else _keys.Add(pair.Key); _values.Add(pair.Value); #endif } } IEnumerator> IEnumerable>.GetEnumerator() { return ((IEnumerable>)_dictionary).GetEnumerator(); } } }