using System; using SabreTools.Data.Models.Metadata; namespace SabreTools.Metadata.Filter { /// /// Represents a single filter key /// public class FilterKey { /// /// Item name associated with the filter /// public readonly string ItemName; /// /// Field name associated with the filter /// public readonly string FieldName; /// /// Validating combined key constructor /// public FilterKey(string? key) { if (!ParseFilterId(key, out string itemName, out string fieldName)) throw new ArgumentException($"{nameof(key)} could not be parsed", nameof(key)); ItemName = itemName; FieldName = fieldName; } /// /// Validating discrete value constructor /// public FilterKey(string itemName, string fieldName) { if (!ParseFilterId(ref itemName, ref fieldName)) throw new ArgumentException($"{nameof(itemName)} was not recognized", nameof(itemName)); ItemName = itemName; FieldName = fieldName; } /// public override string ToString() => $"{ItemName}.{FieldName}"; /// /// Parse a filter ID string into the item name and field name, if possible /// private static bool ParseFilterId(string? itemFieldString, out string itemName, out string fieldName) { // Set default values itemName = string.Empty; fieldName = string.Empty; // If we don't have a filter ID, we can't do anything if (string.IsNullOrEmpty(itemFieldString)) return false; // If we only have one part, we can't do anything string[] splitFilter = itemFieldString!.Split('.'); if (splitFilter.Length != 2) return false; // Set and sanitize the filter ID itemName = splitFilter[0]; fieldName = splitFilter[1]; return ParseFilterId(ref itemName, ref fieldName); } /// /// Parse a filter ID string into the item name and field name, if possible /// private static bool ParseFilterId(ref string itemName, ref string fieldName) { // If we don't have a filter ID, we can't do anything if (string.IsNullOrEmpty(itemName) || string.IsNullOrEmpty(fieldName)) return false; // Return santized values based on the split ID return itemName.ToLowerInvariant() switch { // Header "header" => ParseHeaderFilterId(ref itemName, ref fieldName), // Machine "game" => ParseMachineFilterId(ref itemName, ref fieldName), "machine" => ParseMachineFilterId(ref itemName, ref fieldName), "resource" => ParseMachineFilterId(ref itemName, ref fieldName), "set" => ParseMachineFilterId(ref itemName, ref fieldName), // DatItem "datitem" => ParseDatItemFilterId(ref itemName, ref fieldName), "item" => ParseDatItemFilterId(ref itemName, ref fieldName), _ => ParseDatItemFilterId(ref itemName, ref fieldName), }; } /// /// Parse and validate header fields /// private static bool ParseHeaderFilterId(ref string itemName, ref string fieldName) { // Get the set of constants var constants = TypeHelper.GetConstants(typeof(Header)); if (constants is not null) { // Get if there's a match to a constant string localFieldName = fieldName; string? constantMatch = Array.Find(constants, c => string.Equals(c, localFieldName, StringComparison.OrdinalIgnoreCase)); if (constantMatch is not null) { // Return the sanitized ID itemName = MetadataFile.HeaderKey; fieldName = constantMatch; return true; } } // Get the set of properties var properties = TypeHelper.GetProperties(typeof(Header)); if (properties is not null) { // Get if there's a match to a property string localFieldName = fieldName; string? propertyMatch = Array.Find(properties, c => string.Equals(c, localFieldName, StringComparison.OrdinalIgnoreCase)); if (propertyMatch is not null) { // Return the sanitized ID itemName = MetadataFile.HeaderKey; fieldName = propertyMatch.ToLowerInvariant(); return true; } } return false; } /// /// Parse and validate machine/game fields /// private static bool ParseMachineFilterId(ref string itemName, ref string fieldName) { // Get the set of constants var constants = TypeHelper.GetConstants(typeof(Machine)); if (constants is not null) { // Get if there's a match to a constant string localFieldName = fieldName; string? constantMatch = Array.Find(constants, c => string.Equals(c, localFieldName, StringComparison.OrdinalIgnoreCase)); if (constantMatch is not null) { // Return the sanitized ID itemName = MetadataFile.MachineKey; fieldName = constantMatch; return true; } } // Get the set of properties var properties = TypeHelper.GetProperties(typeof(Machine)); if (properties is not null) { // Get if there's a match to a property string localFieldName = fieldName; string? propertyMatch = Array.Find(properties, c => string.Equals(c, localFieldName, StringComparison.OrdinalIgnoreCase)); if (propertyMatch is not null) { // Return the sanitized ID itemName = MetadataFile.MachineKey; fieldName = propertyMatch.ToLowerInvariant(); return true; } } return false; } /// /// Parse and validate item fields /// private static bool ParseDatItemFilterId(ref string itemName, ref string fieldName) { // Special case if the item name is reserved if (string.Equals(itemName, "datitem", StringComparison.OrdinalIgnoreCase) || string.Equals(itemName, "item", StringComparison.OrdinalIgnoreCase)) { // Get all item types var itemTypes = TypeHelper.GetDatItemTypeNames(); // If we get any matches string localFieldName = fieldName; string? matchedType = Array.Find(itemTypes, t => DatItemContainsField(t, localFieldName)); if (matchedType is not null) { // Check for a matching field string? matchedField = GetMatchingField(matchedType, fieldName); if (matchedField is null) return false; itemName = "item"; fieldName = matchedField; return true; } } else { // Check for a matching field string? matchedField = GetMatchingField(itemName, fieldName); if (matchedField is null) return false; itemName = itemName.ToLowerInvariant(); fieldName = matchedField; return true; } // Nothing was found return false; } /// /// Determine if an item type contains a field /// private static bool DatItemContainsField(string itemName, string fieldName) => GetMatchingField(itemName, fieldName) is not null; /// /// Determine if an item type contains a field /// private static string? GetMatchingField(string itemName, string fieldName) { // Get the correct item type var itemType = TypeHelper.GetDatItemType(itemName.ToLowerInvariant()); if (itemType is null) return null; // Get the set of constants var constants = TypeHelper.GetConstants(itemType); if (constants is not null) { // Get if there's a match to a constant string? constantMatch = Array.Find(constants, c => string.Equals(c, fieldName, StringComparison.OrdinalIgnoreCase)); if (constantMatch is not null) return constantMatch; } // Get the set of properties var properties = TypeHelper.GetProperties(itemType); if (properties is not null) { // Get if there's a match to a property string? propertyMatch = Array.Find(properties, c => string.Equals(c, fieldName, StringComparison.OrdinalIgnoreCase)); if (propertyMatch is not null) return propertyMatch.ToLowerInvariant(); } return null; } } }