using System; using System.Collections.Generic; using System.Linq; using UnityEngine; using UnityEngine.Localization.Tables; namespace UnityEditor.Localization { class LocalizationTableCollectionCache { // Contains table and shared table data guids that map to the parent collection. Dictionary m_GuidToCollection; internal List m_StringTableCollections; internal List m_AssetTableCollections; public List AssetTableCollections { get { if (m_AssetTableCollections == null) m_AssetTableCollections = LoadTableCollections(); return m_AssetTableCollections; } } public List StringTableCollections { get { if (m_StringTableCollections == null) m_StringTableCollections = LoadTableCollections(); return m_StringTableCollections; } } public Dictionary CollectionDependencies { get { if (m_GuidToCollection == null) { m_GuidToCollection = new Dictionary(); AssetTableCollections.ForEach(CacheDependencies); StringTableCollections.ForEach(CacheDependencies); } return m_GuidToCollection; } } public LocalizationTableCollectionCache() { LocalizationEditorSettings.EditorEvents.CollectionAdded += AddToCache; LocalizationEditorSettings.EditorEvents.CollectionRemoved += OnCollectionRemoved; LocalizationEditorSettings.EditorEvents.TableAddedToCollection += OnTableAddedToCollection; LocalizationEditorSettings.EditorEvents.TableRemovedFromCollection += OnTableRemovedFromCollection; } ~LocalizationTableCollectionCache() { LocalizationEditorSettings.EditorEvents.CollectionAdded -= AddToCache; LocalizationEditorSettings.EditorEvents.CollectionRemoved -= OnCollectionRemoved; LocalizationEditorSettings.EditorEvents.TableAddedToCollection -= OnTableAddedToCollection; LocalizationEditorSettings.EditorEvents.TableRemovedFromCollection -= OnTableRemovedFromCollection; } internal LocalizationTableCollection FindTableCollection(Type collectionType, TableReference tableReference) { if (collectionType == typeof(StringTableCollection)) return FindStringTableCollection(tableReference); if (collectionType == typeof(AssetTableCollection)) return FindAssetTableCollection(tableReference); return null; } public StringTableCollection FindStringTableCollection(TableReference tableReference) => FindTableCollection(StringTableCollections, tableReference); public AssetTableCollection FindAssetTableCollection(TableReference tableReference) => FindTableCollection(AssetTableCollections, tableReference); public LocalizationTableCollection FindCollectionForSharedTableData(SharedTableData sharedTableData) { if (sharedTableData == null) throw new ArgumentNullException(nameof(sharedTableData)); if (AssetDatabase.TryGetGUIDAndLocalFileIdentifier(sharedTableData, out var guid, out long _)) { return FindCollectionFromDependencyGuid(guid); } return null; } public LocalizationTableCollection FindCollectionForTable(LocalizationTable table) { if (table == null) throw new ArgumentNullException(nameof(table)); if (AssetDatabase.TryGetGUIDAndLocalFileIdentifier(table, out var guid, out long _)) { return FindCollectionFromDependencyGuid(guid); } return null; } public LocalizationTableCollection FindCollectionForInstanceId(int tableInstanceID) { if (AssetDatabase.TryGetGUIDAndLocalFileIdentifier(tableInstanceID, out var guid, out long _)) { return FindCollectionFromDependencyGuid(guid); } return null; } public LocalizationTableCollection FindCollectionFromDependencyGuid(string tableGuid) { if (CollectionDependencies.TryGetValue(tableGuid, out var collection)) return collection; return null; } public void FindLooseTablesUsingSharedTableData(SharedTableData sharedTableData, IList foundTables) { if (sharedTableData == null) throw new ArgumentNullException(nameof(sharedTableData)); if (foundTables == null) throw new ArgumentNullException(nameof(foundTables)); // We need to find all tables that reference the shared table data that do not belong to a collection var foundAssets = AssetDatabase.FindAssets("t:LocalizationTable"); foreach (var guid in foundAssets) { // Ignore tables we know about if (CollectionDependencies.ContainsKey(guid)) continue; // Filter by table type var path = AssetDatabase.GUIDToAssetPath(guid); var loadedTable = AssetDatabase.LoadAssetAtPath(path); if (loadedTable.SharedData == sharedTableData) foundTables.Add(loadedTable); } } public void Clear() { m_GuidToCollection = null; m_StringTableCollections = null; m_AssetTableCollections = null; } void OnTableAddedToCollection(LocalizationTableCollection collection, LocalizationTable table) { if (m_GuidToCollection != null) { if (!AssetDatabase.TryGetGUIDAndLocalFileIdentifier(table, out var guid, out long _)) { Debug.LogError("Failed to extract table guid: " + table?.name, table); return; } m_GuidToCollection[guid] = collection; } } void OnTableRemovedFromCollection(LocalizationTableCollection collection, LocalizationTable table) { if (m_GuidToCollection != null) { if (!AssetDatabase.TryGetGUIDAndLocalFileIdentifier(table, out var guid, out long _)) { Debug.LogError("Failed to extract table guid: " + table?.name, table); return; } m_GuidToCollection.Remove(guid); } } void OnCollectionRemoved(LocalizationTableCollection collection) { if (collection is StringTableCollection stringTableCollection) { if (m_StringTableCollections != null && m_StringTableCollections.Contains(stringTableCollection)) { m_StringTableCollections.Remove(stringTableCollection); m_GuidToCollection = null; // Clear cache } } else if (collection is AssetTableCollection assetTableCollection) { if (m_AssetTableCollections != null && m_AssetTableCollections.Contains(assetTableCollection)) { m_AssetTableCollections.Remove(assetTableCollection); m_GuidToCollection = null; // Clear cache } } else { throw new System.Exception("Unhandled collection type: " + collection.GetType()); } } protected virtual string[] FindAssets(string filter) => AssetDatabase.FindAssets(filter); protected virtual TCollection FindTableCollection(List list, TableReference tableReference) where TCollection : LocalizationTableCollection { Debug.Assert(list != null); string name = null; if (tableReference.ReferenceType == TableReference.Type.Guid) { var guid = TableReference.StringFromGuid(tableReference.TableCollectionNameGuid); var path = AssetDatabase.GUIDToAssetPath(guid); var sharedTableData = AssetDatabase.LoadAssetAtPath(AssetDatabase.GUIDToAssetPath(guid)); if (sharedTableData == null) { Debug.LogError($"Could not load Shared Table Data at path '{path}' with guid '{guid}'."); return null; } name = sharedTableData.TableCollectionName; } else { name = tableReference.TableCollectionName; } return list.FirstOrDefault(stc => stc.TableCollectionName == name); } protected virtual List LoadTableCollections() where TCollection : LocalizationTableCollection { var foundCollections = new List(); var foundAssets = FindAssets($"t:{typeof(TCollection).Name}"); foreach (var guid in foundAssets) { var path = AssetDatabase.GUIDToAssetPath(guid); var collection = AssetDatabase.LoadAssetAtPath(path); if (collection == null) { Debug.LogError($"Could not load collection as type {typeof(TCollection).Name} at path {path}."); continue; } var validState = collection.IsValid; if (!validState.valid) { Debug.LogWarning($"Collection {collection.name} is invalid and will be ignored because {validState.error}."); continue; } foundCollections.Add(collection); } return foundCollections.OrderBy(col => col.TableCollectionName).ToList(); } protected virtual void CacheDependencies(LocalizationTableCollection collection) { foreach (var table in collection.Tables) { var guid = LocalizationEditorSettings.Instance.GetAssetGuid(table.GetInstanceId()); m_GuidToCollection[guid] = collection; } m_GuidToCollection[TableReference.StringFromGuid(collection.SharedData.TableCollectionNameGuid)] = collection; } protected virtual void AddToCache(LocalizationTableCollection collection) { var validState = collection.IsValid; if (!validState.valid) { Debug.LogWarning($"Collection {collection.name} is invalid and will be ignored because {validState.error}."); return; } if (collection is StringTableCollection stringTableCollection) { if (m_StringTableCollections != null && !m_StringTableCollections.Contains(stringTableCollection)) { m_StringTableCollections.Add(stringTableCollection); if (m_GuidToCollection != null) { CacheDependencies(stringTableCollection); } } } else if (collection is AssetTableCollection assetTableCollection) { if (m_AssetTableCollections != null && !m_AssetTableCollections.Contains(assetTableCollection)) { m_AssetTableCollections.Add(assetTableCollection); if (m_GuidToCollection != null) { CacheDependencies(assetTableCollection); } } } else { throw new System.Exception("Unhandled collection type: " + collection.GetType()); } } } }