using System; using System.Collections.Generic; using System.Text.RegularExpressions; namespace SabreTools.Filtering { /// /// Represents a single filter within the overall filter /// /// Generic type representing the filtered object public class FilterItem { /// /// Single positive value for this filter /// public T? Positive { get; set; } /// /// List of positive values for this filter /// public List PositiveSet { get; set; } = new List(); /// /// Single negative value for this filter /// public T? Negative { get; set; } /// /// List of negative values for this filter /// public List NegativeSet { get; set; } = new List(); /// /// Single neutral value for this filter /// public T? Neutral { get; set; } /// /// List of neutral values for this filter /// public List NeutralSet { get; set; } = new List(); /// /// Check if a value matches the positive filter /// /// Default value to check filter value /// Value to check /// True if the value was found in the positive filter, null on default value, false otherwise public bool? MatchesPositive(T def, T value) { return Matches(this.Positive, def, value); } /// /// Check if a value matches the negative filter /// /// Default value to check filter value /// Value to check /// True if the value was found in the negative filter, null on default value, false otherwise public bool? MatchesNegative(T def, T value) { return Matches(this.Negative, def, value); } /// /// Check if a value matches the neutral filter /// /// Default value to check filter value /// Value to check /// True if the value was found in the neutral filter, null on default value, false otherwise public bool? MatchesNeutral(T def, T value) { return Matches(this.Neutral, def, value); } /// /// Check if the given value matches any of the positive filters /// /// Value to check /// True if the value was found in a positive filter, null on an empty set, false otherwise public bool? MatchesPositiveSet(T? value) { return MatchesSet(this.PositiveSet, value); } /// /// Check if the given value matches any of the negative filters /// /// Value to check /// True if the value was found in a negative filter, null on an empty set, false otherwise public bool? MatchesNegativeSet(T? value) { return MatchesSet(this.NegativeSet, value); } /// /// Check if the given value matches any of the neutral filters /// /// Value to check /// True if the value was found in a neutral filter, null on an empty set, false otherwise public bool? MatchesNeutralSet(T? value) { return MatchesSet(this.NeutralSet, value); } /// /// Check if a value matches the supplied filter /// /// Value to check against /// Default value to check filter value /// Value to check /// True if the value was found in the supplied filter, null on default value, false otherwise private static bool? Matches(T? single, T def, T value) { // If the filter is default, we ignore if (single == null || single.Equals(def)) return null; #if NETFRAMEWORK // TODO: Fix flag matching #else // If we have a flag if (typeof(T).IsEnum && (single as Enum)!.HasFlag((value as Enum)!)) return true; #endif return single.Equals(value); } /// /// Check if a value matches the supplied filter /// /// Set to check against /// Value to check /// True if the value was found in the supplied filter, null on an empty set, false otherwise private static bool? MatchesSet(List set, T? value) { if (set.Count == 0) return null; if (FindValueInList(set, value)) return true; return false; } /// /// Generic code to check if a specific value is in the list given /// /// List to search for the value in /// Value to search the list for /// True if the value could be found, false otherwise private static bool FindValueInList(List haystack, T? needle) { bool found = false; foreach (T? straw in haystack) { if (straw is string strawString) { if (!string.IsNullOrEmpty(strawString) && needle is string needleString) { string regexStraw = strawString; // If the straw has no special characters at all (excluding whitespace), treat it as an exact match if (regexStraw == Regex.Escape(regexStraw).Replace("\\ ", " ")) regexStraw = $"^{regexStraw}$"; // Check if a match is found with the regex found |= Regex.IsMatch(needleString, regexStraw, RegexOptions.IgnoreCase | RegexOptions.CultureInvariant); } } else { found |= (needle?.Equals(straw) ?? false); } } return found; } } }