From 3d87c20db6b4be92ac478ee6e73f5e3adb7ed912 Mon Sep 17 00:00:00 2001 From: Pavel Minaev Date: Thu, 20 Dec 2018 18:07:22 -0800 Subject: [PATCH] Reimplement Slots as a true wrapper, and expose raw slot values via (Raw). --- WarBender/Slots.cs | 159 ++++++++++++++++++++++++++++----------------- 1 file changed, 98 insertions(+), 61 deletions(-) diff --git a/WarBender/Slots.cs b/WarBender/Slots.cs index 1404ac1..e8813a7 100644 --- a/WarBender/Slots.cs +++ b/WarBender/Slots.cs @@ -2,20 +2,22 @@ using System.Collections; using System.Collections.Generic; using System.ComponentModel; +using System.Diagnostics; using System.Drawing; using System.Globalization; +using System.IO; using System.Linq; using WarBender.Modules; namespace WarBender { - public class Slots : LengthPrefixedCollection, IList, IEnumerable { - [Browsable(false)] - public new IRecord Parent => (IRecord)((IDataObject)this).Parent; + public class Slots : IDataObject, ICollection, IList { + private IReadOnlyList _slotDefinitions; - [Browsable(false)] - public LengthPrefixedCollection Raw => this; + public Slots() { + } - private IReadOnlyList _slotDefinitions; + [ParenthesizePropertyName(true)] + public LengthPrefixedCollection Raw { get; } = new LengthPrefixedCollection(); public IReadOnlyList SlotDefinitions { get { @@ -27,83 +29,118 @@ public IReadOnlyList SlotDefinitions { } } - protected override void SetParent(IDataObject parent) { + public IDataObjectChild WithParent(IDataObject parent, int index = -1) { if (!(parent is IRecord)) { - throw new InvalidOperationException($"{nameof(Slots)} must have a record as parent"); + throw new ArgumentOutOfRangeException(nameof(parent)); } - _slotDefinitions = null; - base.SetParent(parent); + var raw = ((IDataObject)Raw).WithParent(parent, index); + Trace.Assert(Raw == raw); + return this; } - protected override void OnItemAdded(long item, int index) { - base.OnItemAdded(item, index); - if (_slotDefinitions != null && Count > _slotDefinitions.Count) { - _slotDefinitions = null; - } - } + public string GetKeyOfIndex(int index) => index < SlotDefinitions.Count ? SlotDefinitions[index].Name : null; - public override string GetKeyOfIndex(int index) => SlotDefinitions[index].Name; + private static long RawValue(object value) => + value == null ? -1 : + value is IRecord record ? record.Index : + value is IEntityReference eref ? eref.Index : + value is IConvertible conv ? conv.ToInt64(CultureInfo.InvariantCulture) : + value is Color color ? color.ToArgb() : + throw new ArgumentOutOfRangeException("value"); - public new object this[int index] { - get { - var raw = Raw[index]; - var slotType = SlotDefinitions[index].Type; - - object slot; - if (typeof(Color) == slotType) { - slot = Color.FromArgb((int)raw); - } else if (slotType.IsEnum) { - slot = Enum.ToObject(slotType, raw); - } else if (typeof(IEntity).IsAssignableFrom(slotType)) { - slotType = typeof(EntityReference<,>).MakeGenericType(slotType, typeof(int)); - slot = Activator.CreateInstance(slotType, (int)raw); - } else { - slot = ((IConvertible)raw).ToType(slotType, null); - } + private object TypedValue(long value, int index) { + if (index >= SlotDefinitions.Count) { + return value; + } - if (slot is IDataObjectChild child) { - slot = child.WithParent(this, index); - } + var raw = Raw[index]; + var slotType = SlotDefinitions[index].Type; + + object slot; + if (typeof(Color) == slotType) { + slot = Color.FromArgb((int)raw); + } else if (slotType.IsEnum) { + slot = Enum.ToObject(slotType, raw); + } else if (typeof(IEntity).IsAssignableFrom(slotType)) { + slotType = typeof(EntityReference<,>).MakeGenericType(slotType, typeof(int)); + slot = Activator.CreateInstance(slotType, (int)raw); + } else { + slot = ((IConvertible)raw).ToType(slotType, null); + } - return slot; + if (slot is IDataObjectChild child) { + slot = child.WithParent(this, index); } - set => - Raw[index] = RawValue(value); + + return slot; } - public new object this[string key] { - get => this[GetIndexOfKey(key)]; - set => this[GetIndexOfKey(key)] = value; + public object this[int index] { + get => TypedValue(Raw[index], index); + set => Raw[index] = RawValue(value); } - IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + public IEnumerator GetEnumerator() => Raw.Select((x, i) => TypedValue(x, i)).GetEnumerator(); + + public int Add(object value) => ((ICollection)Raw).Add(RawValue(value)); + + public int GetLength() => Raw.GetLength(); + + public bool Contains(object value) => Raw.Contains(RawValue(value)); + + public void Clear() => Raw.Clear(); + + public int IndexOf(object value) =>Raw.IndexOf(RawValue(value)); + + public void Insert(int index, object value) => ((ICollection)Raw).Insert(index, RawValue(value)); - public new IEnumerator GetEnumerator() { - for (int i = 0; i < Count; ++i) { - yield return this[i]; + public void Remove(object value) => ((ICollection)Raw).Remove(RawValue(value)); + + public void RemoveAt(int index) => Raw.RemoveAt(index); + + public void CopyTo(Array array, int index) { + foreach (var value in this) { + array.SetValue(value, index++); } } - int IList.Add(object value) { - Raw.Add(RawValue(value)); - return Count - 1; - } + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); - bool IList.Contains(object value) => Raw.Contains(RawValue(value)); + void ICollection.Add(object item) => Add(RawValue(item)); - int IList.IndexOf(object value) => Raw.IndexOf(RawValue(value)); + public void CopyTo(object[] array, int arrayIndex) => CopyTo(array, arrayIndex); - void IList.Insert(int index, object value) => Raw.Insert(index, RawValue(value)); + bool ICollection.Remove(object item) => Raw.Remove(RawValue(item)); - void IList.Remove(object value) => Raw.Remove(RawValue(value)); + [ParenthesizePropertyName(true)] + public long SizeInBytes => Raw.SizeInBytes; - private static long RawValue(object value) => - value == null ? -1 : - value is IRecord record ? record.Index : - value is IEntityReference eref ? eref.Index : - value is IConvertible conv ? conv.ToInt64(CultureInfo.InvariantCulture) : - value is Color color ? color.ToArgb() : - throw new ArgumentOutOfRangeException("value"); + IDataObject IDataObjectChild.Parent => Raw.Parent; + + [Browsable(false)] + IRecord Parent => (IRecord)Raw.Parent; + + [Browsable(false)] + public Type ItemType => Raw.ItemType; + + [Browsable(false)] + public bool IsReadOnly => ((ICollection)Raw).IsReadOnly; + + [Browsable(false)] + public bool IsFixedSize => ((ICollection)Raw).IsFixedSize; + + [ParenthesizePropertyName(true)] + public int Count => Raw.Count; + + [Browsable(false)] + public object SyncRoot => ((ICollection)Raw).SyncRoot; + + [Browsable(false)] + public bool IsSynchronized => ((ICollection)Raw).IsSynchronized; + + public void ReadFrom(BinaryReader reader) => Raw.ReadFrom(reader); + + public void WriteTo(BinaryWriter writer) => Raw.WriteTo(writer); } }