using System; using System.Collections; using System.Collections.Generic; using System.Linq; namespace MatFileHandler { /// /// Implementation of Matlab's "opaque objects" via links to subsystem data. /// internal class OpaqueLink : Opaque, IMatObject { /// /// Initializes a new instance of the class. /// /// Name of the object. /// Description of object's class. /// Name of the object's class. /// Dimensions of the object. /// Raw data containing links to object's storage. /// Links to object's storage. /// Index of object's class. /// Reference to global subsystem data. public OpaqueLink( string name, string typeDescription, string className, int[] dimensions, DataElement data, int[] indexToObjectId, int classIndex, SubsystemData subsystemData) : base(name, typeDescription, className, dimensions, data, subsystemData) { IndexToObjectId = indexToObjectId ?? throw new ArgumentNullException(nameof(indexToObjectId)); ClassIndex = classIndex; } /// /// Gets index of this object's class in subsystem data class list. /// public int ClassIndex { get; } /// public IReadOnlyDictionary[] Data { get { var result = new IReadOnlyDictionary[Count]; for (var i = 0; i < Count; i++) { result[i] = FieldNamesArray.ToDictionary( name => name, name => this[name, i]); } return result; } } /// public IEnumerable FieldNames => FieldNamesArray; /// /// Gets links to the fields stored in subsystem data. /// public int[] IndexToObjectId { get; } /// /// Gets name of this object's class. /// public override string ClassName => SubsystemData.ClassInformation[ClassIndex].Name; private string[] FieldNamesArray => SubsystemData.ClassInformation[ClassIndex].FieldNames.ToArray(); /// public IArray this[string field, params int[] list] { get => TryGetValue(field, out var result, list) ? result! : throw new ArgumentOutOfRangeException(nameof(list)); set => throw new NotImplementedException(); } /// public IReadOnlyDictionary this[params int[] list] { get => ExtractObject(Dimensions.DimFlatten(list)); set => throw new NotImplementedException(); } private OpaqueObjectArrayElement ExtractObject(int i) { return new OpaqueObjectArrayElement(this, i); } private bool TryGetValue(string field, out IArray? output, params int[] list) { var index = Dimensions.DimFlatten(list); var maybeFieldIndex = SubsystemData.ClassInformation[ClassIndex].FindField(field); if (maybeFieldIndex is not int fieldIndex) { output = default; return false; } if (index >= IndexToObjectId.Length) { output = default; return false; } var objectId = IndexToObjectId[index]; var objectInfo = SubsystemData.ObjectInformation[objectId]; var fieldId = objectInfo.FieldLinks[fieldIndex]; output = SubsystemData.Data[fieldId]; return true; } /// /// Provides access to a single object in object array. /// internal class OpaqueObjectArrayElement : IReadOnlyDictionary { private readonly int index; private readonly OpaqueLink parent; /// /// Initializes a new instance of the class. /// /// Parent object array. /// Index of the object in the array. public OpaqueObjectArrayElement(OpaqueLink parent, int index) { this.parent = parent ?? throw new ArgumentNullException(nameof(parent)); this.index = index; } /// public int Count => parent.FieldNamesArray.Length; /// public IEnumerable Keys => parent.FieldNames; /// public IEnumerable Values => parent.FieldNames.Select(fieldName => parent[fieldName, index]); /// public IArray this[string key] => parent[key, index]; /// public bool ContainsKey(string key) { return parent.FieldNames.Contains(key); } /// public IEnumerator> GetEnumerator() { foreach (var field in parent.FieldNamesArray) { yield return new KeyValuePair(field, parent[field, index]); } } #pragma warning disable CS8767 /// public bool TryGetValue(string key, out IArray? value) #pragma warning restore CS8767 { return parent.TryGetValue(key, out value, index); } /// IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } } } }