using MxGateway.Contracts.Proto.Galaxy; namespace MxGateway.Server.Galaxy; public sealed class GalaxyHierarchyIndex { private GalaxyHierarchyIndex( IReadOnlyList objectViews, IReadOnlyDictionary objectViewsById, IReadOnlyDictionary tagsByAddress) { ObjectViews = objectViews; ObjectViewsById = objectViewsById; TagsByAddress = tagsByAddress; } public static GalaxyHierarchyIndex Empty { get; } = new( Array.Empty(), new Dictionary(), new Dictionary(StringComparer.OrdinalIgnoreCase)); public IReadOnlyList ObjectViews { get; } public IReadOnlyDictionary ObjectViewsById { get; } public IReadOnlyDictionary TagsByAddress { get; } public static GalaxyHierarchyIndex Build(IReadOnlyList objects) { if (objects.Count == 0) { return Empty; } Dictionary objectsById = new(); foreach (GalaxyObject obj in objects) { objectsById.TryAdd(obj.GobjectId, obj); } List views = new(objects.Count); Dictionary viewsById = new(); Dictionary tagsByAddress = new(StringComparer.OrdinalIgnoreCase); foreach (GalaxyObject obj in objects) { string path = BuildContainedPath(obj, objectsById); int depth = string.IsNullOrWhiteSpace(path) ? 0 : path.Count(character => character == '/'); GalaxyObjectView view = new(obj, path, depth); views.Add(view); viewsById.TryAdd(obj.GobjectId, view); if (!string.IsNullOrWhiteSpace(obj.TagName)) { tagsByAddress.TryAdd(obj.TagName, new GalaxyTagLookup(obj, Attribute: null, path)); } foreach (GalaxyAttribute attribute in obj.Attributes) { if (!string.IsNullOrWhiteSpace(attribute.FullTagReference)) { tagsByAddress.TryAdd(attribute.FullTagReference, new GalaxyTagLookup(obj, attribute, path)); } } } return new GalaxyHierarchyIndex( views, viewsById, tagsByAddress); } private static string BuildContainedPath( GalaxyObject obj, IReadOnlyDictionary objectsById) { Stack names = new(); HashSet seen = []; GalaxyObject? current = obj; while (current is not null && seen.Add(current.GobjectId)) { names.Push(ResolvePathSegment(current)); current = current.ParentGobjectId != 0 && objectsById.TryGetValue(current.ParentGobjectId, out GalaxyObject? parent) ? parent : null; } return string.Join('/', names.Where(name => !string.IsNullOrWhiteSpace(name))); } private static string ResolvePathSegment(GalaxyObject obj) { if (!string.IsNullOrWhiteSpace(obj.ContainedName)) { return obj.ContainedName; } if (!string.IsNullOrWhiteSpace(obj.BrowseName)) { return obj.BrowseName; } return obj.TagName; } }