From 76c5dc40e366c278bf20f8672fc15d378b9160fe Mon Sep 17 00:00:00 2001 From: Matt Nadareski Date: Tue, 7 Apr 2026 10:37:03 -0400 Subject: [PATCH] Checkpoint reflection replacement --- .../FilterObjectTests.cs | 84 +++++++++++ SabreTools.Metadata.Filter/FilterKey.cs | 131 ++++++------------ 2 files changed, 126 insertions(+), 89 deletions(-) diff --git a/SabreTools.Metadata.Filter.Test/FilterObjectTests.cs b/SabreTools.Metadata.Filter.Test/FilterObjectTests.cs index 740cd32a..0891efee 100644 --- a/SabreTools.Metadata.Filter.Test/FilterObjectTests.cs +++ b/SabreTools.Metadata.Filter.Test/FilterObjectTests.cs @@ -1898,5 +1898,89 @@ namespace SabreTools.Metadata.Filter.Test } #endregion + + #region SharedFeat + + [Theory] + [InlineData("sharedfeat.name", "name")] + [InlineData("sharedfeat.value", "value")] + public void Matches_SharedFeat(string itemField, string value) + { + var filter = new FilterObject(itemField, value, Operation.Equals); + SharedFeat obj = new SharedFeat + { + Name = "name", + Value = "value", + }; + + bool actual = filter.Matches(obj); + Assert.True(actual); + } + + #endregion + + #region Slot + + [Theory] + [InlineData("slot.name", "name")] + public void Matches_Slot(string itemField, string value) + { + var filter = new FilterObject(itemField, value, Operation.Equals); + Slot obj = new Slot + { + Name = "name", + }; + + bool actual = filter.Matches(obj); + Assert.True(actual); + } + + #endregion + + #region SlotOption + + [Theory] + [InlineData("slotoption.default", "yes")] + [InlineData("slotoption.devname", "devname")] + [InlineData("slotoption.name", "name")] + public void Matches_SlotOption(string itemField, string value) + { + var filter = new FilterObject(itemField, value, Operation.Equals); + SlotOption obj = new SlotOption + { + Default = true, + DevName = "devname", + Name = "name", + }; + + bool actual = filter.Matches(obj); + Assert.True(actual); + } + + #endregion + + #region SoftwareList + + [Theory] + [InlineData("softwarelist.filter", "filter")] + [InlineData("softwarelist.name", "name")] + [InlineData("softwarelist.status", "original")] + [InlineData("softwarelist.tag", "tag")] + public void Matches_SoftwareList(string itemField, string value) + { + var filter = new FilterObject(itemField, value, Operation.Equals); + SoftwareList obj = new SoftwareList + { + Filter = "filter", + Name = "name", + Status = SoftwareListStatus.Original, + Tag = "tag", + }; + + bool actual = filter.Matches(obj); + Assert.True(actual); + } + + #endregion } } diff --git a/SabreTools.Metadata.Filter/FilterKey.cs b/SabreTools.Metadata.Filter/FilterKey.cs index 42bd62a8..5e54de55 100644 --- a/SabreTools.Metadata.Filter/FilterKey.cs +++ b/SabreTools.Metadata.Filter/FilterKey.cs @@ -1,6 +1,4 @@ using System; -using System.Reflection; -using System.Xml.Serialization; using SabreTools.Data.Models.Metadata; namespace SabreTools.Metadata.Filter @@ -735,6 +733,44 @@ namespace SabreTools.Metadata.Filter "savechipserial", ]; + /// + /// Known keys for SharedFeat + /// + private static readonly string[] _sharedFeatKeys = + [ + "name", + "value", + ]; + + /// + /// Known keys for Slot + /// + private static readonly string[] _slotKeys = + [ + "name", + ]; + + /// + /// Known keys for SlotOption + /// + private static readonly string[] _slotOptionKeys = + [ + "default", + "devname", + "name", + ]; + + /// + /// Known keys for SoftwareList + /// + private static readonly string[] _softwareListKeys = + [ + "filter", + "name", + "status", + "tag", + ]; + #endregion /// @@ -948,24 +984,12 @@ namespace SabreTools.Metadata.Filter "rom" => _romKeys, "sample" => _sampleKeys, "serials" => _serialsKeys, + "sharedfeat" => _sharedFeatKeys, + "slot" => _slotKeys, + "slotoption" => _slotOptionKeys, + "softwarelist" => _softwareListKeys, _ => null, }; - - // TODO: Remove this fallback path - if (properties is null) - { - // Get the correct item type - var itemType = GetDatItemType(itemName.ToLowerInvariant()); - if (itemType is null) - return null; - - properties = GetProperties(itemType); - - // Special cases for mismatched names - if (properties is not null && itemType == typeof(Rom)) - properties = [.. properties, "crc"]; - } - if (properties is null) return null; @@ -973,76 +997,5 @@ namespace SabreTools.Metadata.Filter string? propertyMatch = Array.Find(properties, c => string.Equals(c, fieldName, StringComparison.OrdinalIgnoreCase)); return propertyMatch?.ToLowerInvariant(); } - - #region Reflection-based Helpers - - /// - /// Attempt to get the DatItem type from the name - /// - private static Type? GetDatItemType(string? itemType) - { - if (string.IsNullOrEmpty(itemType)) - return null; - - // Loop through all loaded assemblies - foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies()) - { - // If not all types can be loaded, use the ones that could be - Type?[] assemblyTypes = []; - try - { - assemblyTypes = assembly.GetTypes(); - } - catch (ReflectionTypeLoadException rtle) - { - assemblyTypes = Array.FindAll(rtle.Types ?? [], t => t is not null); - } - - // Loop through all types - foreach (Type? type in assemblyTypes) - { - // If the type is invalid - if (type is null) - continue; - - // If the type isn't a class or doesn't implement the interface - if (!type.IsClass || !typeof(DatItem).IsAssignableFrom(type)) - continue; - - // Get the XML type name -#if NET20 || NET35 || NET40 - string? elementName = (Attribute.GetCustomAttribute(type, typeof(XmlRootAttribute)) as XmlRootAttribute)!.ElementName; -#else - string? elementName = type.GetCustomAttribute()?.ElementName; -#endif - if (elementName is null) - continue; - - // If the name matches - if (string.Equals(elementName, itemType, StringComparison.OrdinalIgnoreCase)) - return type; - } - } - - return null; - } - - /// - /// Get property names for the given type, if possible - /// - private static string[]? GetProperties(Type? type) - { - if (type is null) - return null; - - var properties = type.GetProperties(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public); - if (properties is null) - return null; - - string[] propertyNames = Array.ConvertAll(properties, f => f.Name); - return Array.FindAll(propertyNames, s => s.Length > 0); - } - - #endregion } }