using System; using System.Collections.Generic; using System.Reflection; using UnityEditor.AddressableAssets.GUI; using UnityEngine; using UnityEngine.AddressableAssets; using UnityEngine.Serialization; namespace UnityEditor.AddressableAssets.Settings { /// /// Contains serialized data in a generic serializable container. /// [Serializable] public class KeyDataStore : ISerializationCallbackReceiver { [Serializable] internal struct Entry { [FormerlySerializedAs("m_assemblyName")] [SerializeField] string m_AssemblyName; internal string AssemblyName { get { return m_AssemblyName; } set { m_AssemblyName = value; } } [FormerlySerializedAs("m_className")] [SerializeField] string m_ClassName; internal string ClassName { get { return m_ClassName; } set { m_ClassName = value; } } [FormerlySerializedAs("m_data")] [SerializeField] string m_Data; internal string Data { get { return m_Data; } set { m_Data = value; } } [FormerlySerializedAs("m_key")] [SerializeField] string m_Key; internal string Key { get { return m_Key; } set { m_Key = value; } } /// public override string ToString() { return Data; } } internal void Reset() { m_SerializedData = null; m_EntryMap = new Dictionary(); } [FormerlySerializedAs("m_serializedData")] [SerializeField] List m_SerializedData; Dictionary m_EntryMap = new Dictionary(); /// /// Delegate that is invoked when data is modified. /// public Action OnSetData { get; set; } /// /// Implementation of ISerializationCallbackReceiver interface, used to convert data to a serializable form. /// public void OnBeforeSerialize() { m_SerializedData = new List(m_EntryMap.Count); foreach (var k in m_EntryMap) m_SerializedData.Add(CreateEntry(k.Key, k.Value)); } Entry CreateEntry(string key, object value) { var entry = new Entry(); entry.Key = key; var objType = value.GetType(); entry.AssemblyName = objType.Assembly.FullName; entry.ClassName = objType.FullName; try { if (objType == typeof(string)) { entry.Data = value as string; } else if (objType.IsEnum) { entry.Data = value.ToString(); } else { var parseMethod = objType.GetMethod("Parse", BindingFlags.Static | BindingFlags.Public, null, CallingConventions.Any, new[] { typeof(string) }, null); if (parseMethod == null || parseMethod.ReturnType != objType) entry.Data = JsonUtility.ToJson(value); else entry.Data = value.ToString(); } } catch (Exception ex) { Addressables.LogWarningFormat("KeyDataStore unable to serizalize entry {0} with value {1}, exception: {2}", key, value, ex); } return entry; } object CreateObject(Entry e) { try { var assembly = Assembly.Load(e.AssemblyName); var objType = assembly.GetType(e.ClassName); if (objType == typeof(string)) return e.Data; if (objType.IsEnum) return Enum.Parse(objType, e.Data); var parseMethod = objType.GetMethod("Parse", BindingFlags.Static | BindingFlags.Public, null, CallingConventions.Any, new[] { typeof(string) }, null); if (parseMethod == null || parseMethod.ReturnType != objType) return JsonUtility.FromJson(e.Data, objType); return parseMethod.Invoke(null, new object[] { e.Data }); } catch (Exception ex) { Addressables.LogWarningFormat("KeyDataStore unable to deserizalize entry {0} from assembly {1} of type {2}, exception: {3}", e.Key, e.AssemblyName, e.ClassName, ex); return null; } } /// /// Implementation of ISerializationCallbackReceiver interface, used to convert data from its serializable form. /// public void OnAfterDeserialize() { m_EntryMap = new Dictionary(m_SerializedData.Count); foreach (var e in m_SerializedData) m_EntryMap.Add(e.Key, CreateObject(e)); m_SerializedData = null; } /// /// The collection of keys stored. /// public IEnumerable Keys { get { return m_EntryMap.Keys; } } /// /// Set the value of a specified key. /// /// The key. /// The data to store. Supported types are strings, POD types, objects that have a static method named 'Parse' that convert a string to an object, and object that are serializable via JSONUtilty. public void SetData(string key, object data) { var isNew = m_EntryMap.ContainsKey(key); m_EntryMap[key] = data; if (OnSetData != null) OnSetData(key, data, isNew); } /// /// Set data for a specified key from a string. /// /// The data key. /// The data string value. public void SetDataFromString(string key, string data) { var existingType = GetDataType(key); if (existingType == null) SetData(key, data); else SetData(key, CreateObject(new Entry { AssemblyName = existingType.Assembly.FullName, ClassName = existingType.FullName, Data = data, Key = key })); } internal Type GetDataType(string key) { object val; if (m_EntryMap.TryGetValue(key, out val)) return val.GetType(); return null; } internal string GetDataString(string key, string defaultValue) { object val; if (m_EntryMap.TryGetValue(key, out val)) return CreateEntry(key, val).ToString(); return defaultValue; } /// /// Get data via a specified key. /// /// The data type. /// The key. /// The default value to return if the data is not found. /// Optional parameter to control whether to add the default value if the data is not found. /// public T GetData(string key, T defaultValue, bool addDefault = false) { try { object val; if (m_EntryMap.TryGetValue(key, out val)) return (T)val; } catch (Exception) { // ignored } if (addDefault) SetData(key, defaultValue); return defaultValue; } } }