Create Filtering object, add helpers

This commit is contained in:
Matt Nadareski
2023-08-11 12:47:59 -04:00
parent 9ff3c4e7b6
commit 2945cb2c58
4 changed files with 342 additions and 22 deletions

View File

@@ -0,0 +1,256 @@
using System;
using SabreTools.Models.Internal;
namespace SabreTools.Filter
{
/// <summary>
/// Represents a single filtering object
/// </summary>
public class FilterObject
{
/// <summary>
/// Key name for the filter
/// </summary>
public string Key { get; init; }
/// <summary>
/// Value to match in the filter
/// </summary>
public string? Value { get; init; }
/// <summary>
/// Operation on how to match the filter
/// </summary>
public Operation Operation { get; init; }
public FilterObject(string keyValue, string? operation)
{
(string? itemName, string? fieldName) = FilterParser.ParseFilterId(keyValue);
if (itemName == null)
throw new ArgumentOutOfRangeException(nameof(keyValue));
this.Key = itemName;
this.Value = fieldName;
this.Operation = GetOperation(operation);
}
public FilterObject(string keyValue, Operation operation)
{
(string? itemName, string? fieldName) = FilterParser.ParseFilterId(keyValue);
if (itemName == null)
throw new ArgumentOutOfRangeException(nameof(keyValue));
this.Key = itemName;
this.Value = fieldName;
this.Operation = operation;
}
public FilterObject(string key, string? value, string? operation)
{
(string? itemName, string? fieldName) = FilterParser.ParseFilterId(key, value);
if (itemName == null)
throw new ArgumentOutOfRangeException(nameof(fieldName));
this.Key = itemName;
this.Value = fieldName;
this.Operation = GetOperation(operation);
}
public FilterObject(string key, string? value, Operation operation)
{
(string? itemName, string? fieldName) = FilterParser.ParseFilterId(key, value);
if (itemName == null)
throw new ArgumentOutOfRangeException(nameof(fieldName));
this.Key = itemName;
this.Value = fieldName;
this.Operation = operation;
}
/// <summary>
/// Derive an operation from the input string, if possible
/// </summary>
private static Operation GetOperation(string? operation)
{
return operation?.ToLowerInvariant() switch
{
"=" => Operation.Equals,
"==" => Operation.Equals,
"!" => Operation.NotEquals,
"!=" => Operation.NotEquals,
">" => Operation.GreaterThan,
">=" => Operation.GreaterThanOrEqual,
"<" => Operation.LessThan,
"<=" => Operation.LessThanOrEqual,
_ => Operation.NONE,
};
}
#region Matching
/// <summary>
/// Determine if a DictionaryBase object matches the key and value
/// </summary>
public bool Matches(DictionaryBase dictionaryBase)
{
return this.Operation switch
{
Operation.Equals => MatchesEqual(dictionaryBase),
Operation.NotEquals => MatchesNotEqual(dictionaryBase),
Operation.GreaterThan => MatchesGreaterThan(dictionaryBase),
Operation.GreaterThanOrEqual => MatchesGreaterThanOrEqual(dictionaryBase),
Operation.LessThan => MatchesLessThan(dictionaryBase),
Operation.LessThanOrEqual => MatchesLessThanOrEqual(dictionaryBase),
_ => false,
};
}
/// <summary>
/// Determines if a value matches exactly
/// </summary>
/// <remarks>TODO: Add regex matching to this method</remarks>
private bool MatchesEqual(DictionaryBase dictionaryBase)
{
if (!dictionaryBase.ContainsKey(this.Key))
return this.Value == null;
string? checkValue = dictionaryBase.ReadString(this.Key);
return checkValue == this.Value;
}
/// <summary>
/// Determines if a value does not match exactly
/// </summary>
/// <remarks>TODO: Add regex matching to this method</remarks>
private bool MatchesNotEqual(DictionaryBase dictionaryBase)
{
if (!dictionaryBase.ContainsKey(this.Key))
return this.Value != null;
string? checkValue = dictionaryBase.ReadString(this.Key);
return checkValue != this.Value;
}
/// <summary>
/// Determines if a value is strictly greater than
/// </summary>
private bool MatchesGreaterThan(DictionaryBase dictionaryBase)
{
if (!dictionaryBase.ContainsKey(this.Key))
return false;
long? checkLongValue = dictionaryBase.ReadLong(this.Key);
if (checkLongValue != null)
{
if (!long.TryParse(this.Value, out long matchValue))
return false;
return checkLongValue > matchValue;
}
double? checkDoubleValue = dictionaryBase.ReadDouble(this.Key);
if (checkDoubleValue != null)
{
if (!double.TryParse(this.Value, out double matchValue))
return false;
return checkDoubleValue > matchValue;
}
return false;
}
/// <summary>
/// Determines if a value is greater than or equal
/// </summary>
private bool MatchesGreaterThanOrEqual(DictionaryBase dictionaryBase)
{
if (!dictionaryBase.ContainsKey(this.Key))
return false;
long? checkLongValue = dictionaryBase.ReadLong(this.Key);
if (checkLongValue != null)
{
if (!long.TryParse(this.Value, out long matchValue))
return false;
return checkLongValue >= matchValue;
}
double? checkDoubleValue = dictionaryBase.ReadDouble(this.Key);
if (checkDoubleValue != null)
{
if (!double.TryParse(this.Value, out double matchValue))
return false;
return checkDoubleValue >= matchValue;
}
return false;
}
/// <summary>
/// Determines if a value is strictly less than
/// </summary>
private bool MatchesLessThan(DictionaryBase dictionaryBase)
{
if (!dictionaryBase.ContainsKey(this.Key))
return false;
long? checkLongValue = dictionaryBase.ReadLong(this.Key);
if (checkLongValue != null)
{
if (!long.TryParse(this.Value, out long matchValue))
return false;
return checkLongValue < matchValue;
}
double? checkDoubleValue = dictionaryBase.ReadDouble(this.Key);
if (checkDoubleValue != null)
{
if (!double.TryParse(this.Value, out double matchValue))
return false;
return checkDoubleValue < matchValue;
}
return false;
}
/// <summary>
/// Determines if a value is less than or equal
/// </summary>
private bool MatchesLessThanOrEqual(DictionaryBase dictionaryBase)
{
if (!dictionaryBase.ContainsKey(this.Key))
return false;
long? checkLongValue = dictionaryBase.ReadLong(this.Key);
if (checkLongValue != null)
{
if (!long.TryParse(this.Value, out long matchValue))
return false;
return checkLongValue <= matchValue;
}
double? checkDoubleValue = dictionaryBase.ReadDouble(this.Key);
if (checkDoubleValue != null)
{
if (!double.TryParse(this.Value, out double matchValue))
return false;
return checkDoubleValue <= matchValue;
}
return false;
}
#endregion
}
}

View File

@@ -12,38 +12,50 @@ namespace SabreTools.Filter
/// <summary>
/// Parse a filter ID string into the item name and field name, if possible
/// </summary>
public static (string?, string?) ParseFilterId(string filterId)
public static (string?, string?) ParseFilterId(string itemFieldString)
{
// If we don't have a filter ID, we can't do anything
if (string.IsNullOrWhiteSpace(filterId))
if (string.IsNullOrWhiteSpace(itemFieldString))
return (null, null);
// If we only have one part, we can't do anything
string[] splitFilter = filterId.Split('.');
string[] splitFilter = itemFieldString.Split('.');
if (splitFilter.Length != 2)
return (null, null);
return ParseFilterId(splitFilter[0], splitFilter[1]);
}
/// <summary>
/// Parse a filter ID string into the item name and field name, if possible
/// </summary>
public static (string?, string?) ParseFilterId(string itemName, string? fieldName)
{
// If we don't have a filter ID, we can't do anything
if (string.IsNullOrWhiteSpace(itemName) || string.IsNullOrWhiteSpace(fieldName))
return (null, null);
// Return santized values based on the split ID
return splitFilter[0].ToLowerInvariant() switch
return itemName.ToLowerInvariant() switch
{
// Header
"header" => ParseHeaderFilterId(splitFilter),
"header" => ParseHeaderFilterId(fieldName),
// Machine
"game" => ParseMachineFilterId(splitFilter),
"machine" => ParseMachineFilterId(splitFilter),
"resource" => ParseMachineFilterId(splitFilter),
"set" => ParseMachineFilterId(splitFilter),
"game" => ParseMachineFilterId(fieldName),
"machine" => ParseMachineFilterId(fieldName),
"resource" => ParseMachineFilterId(fieldName),
"set" => ParseMachineFilterId(fieldName),
// DatItem
_ => ParseDatItemFilterId(splitFilter),
_ => ParseDatItemFilterId(itemName, fieldName),
};
}
/// <summary>
/// Parse and validate header fields
/// </summary>
private static (string?, string?) ParseHeaderFilterId(string[] filterId)
private static (string?, string?) ParseHeaderFilterId(string fieldName)
{
// Get the set of constants
var constants = GetConstants(typeof(Header));
@@ -51,7 +63,7 @@ namespace SabreTools.Filter
return (null, null);
// Get if there's a match to the constant
string? constantMatch = constants.FirstOrDefault(c => string.Equals(c, filterId[1], StringComparison.OrdinalIgnoreCase));
string? constantMatch = constants.FirstOrDefault(c => string.Equals(c, fieldName, StringComparison.OrdinalIgnoreCase));
if (constantMatch == null)
return (null, null);
@@ -62,7 +74,7 @@ namespace SabreTools.Filter
/// <summary>
/// Parse and validate machine/game fields
/// </summary>
private static (string?, string?) ParseMachineFilterId(string[] filterId)
private static (string?, string?) ParseMachineFilterId(string fieldName)
{
// Get the set of constants
var constants = GetConstants(typeof(Machine));
@@ -70,7 +82,7 @@ namespace SabreTools.Filter
return (null, null);
// Get if there's a match to the constant
string? constantMatch = constants.FirstOrDefault(c => string.Equals(c, filterId[1], StringComparison.OrdinalIgnoreCase));
string? constantMatch = constants.FirstOrDefault(c => string.Equals(c, fieldName, StringComparison.OrdinalIgnoreCase));
if (constantMatch == null)
return (null, null);
@@ -81,10 +93,10 @@ namespace SabreTools.Filter
/// <summary>
/// Parse and validate item fields
/// </summary>
private static (string?, string?) ParseDatItemFilterId(string[] filterId)
private static (string?, string?) ParseDatItemFilterId(string itemName, string fieldName)
{
// Get the correct item type
var itemType = GetDatItemType(filterId[0].ToLowerInvariant());
var itemType = GetDatItemType(itemName.ToLowerInvariant());
if (itemType == null)
return (null, null);
@@ -94,12 +106,12 @@ namespace SabreTools.Filter
return (null, null);
// Get if there's a match to the constant
string? constantMatch = constants.FirstOrDefault(c => string.Equals(c, filterId[1], StringComparison.OrdinalIgnoreCase));
string? constantMatch = constants.FirstOrDefault(c => string.Equals(c, fieldName, StringComparison.OrdinalIgnoreCase));
if (constantMatch == null)
return (null, null);
// Return the sanitized ID
return (filterId[0].ToLowerInvariant(), constantMatch);
return (itemName.ToLowerInvariant(), constantMatch);
}
/// <summary>

View File

@@ -0,0 +1,20 @@
namespace SabreTools.Filter
{
/// <summary>
/// Determines what operation is being done
/// </summary>
public enum Operation
{
/// <summary>
/// Default value, does nothing
/// </summary>
NONE,
Equals,
NotEquals,
GreaterThan,
GreaterThanOrEqual,
LessThan,
LessThanOrEqual,
}
}

View File

@@ -12,9 +12,7 @@ namespace SabreTools.Models.Internal
/// </summary>
public T? Read<T>(string key)
{
if (string.IsNullOrWhiteSpace(key))
return default;
if (!ContainsKey(key))
if (!ValidateKey(key))
return default;
return (T?)this[key];
}
@@ -24,6 +22,9 @@ namespace SabreTools.Models.Internal
/// </summary>
public bool? ReadBool(string key)
{
if (!ValidateKey(key))
return null;
bool? asBool = Read<bool>(key);
if (asBool != null)
return asBool;
@@ -44,6 +45,9 @@ namespace SabreTools.Models.Internal
/// </summary>
public double? ReadDouble(string key)
{
if (!ValidateKey(key))
return null;
double? asDouble = Read<double>(key);
if (asDouble != null)
return asDouble;
@@ -60,6 +64,9 @@ namespace SabreTools.Models.Internal
/// </summary>
public long? ReadLong(string key)
{
if (!ValidateKey(key))
return null;
long? asLong = Read<long>(key);
if (asLong != null)
return asLong;
@@ -76,6 +83,9 @@ namespace SabreTools.Models.Internal
/// </summary>
public string? ReadString(string key)
{
if (!ValidateKey(key))
return null;
string? asString = Read<string>(key);
if (asString != null)
return asString;
@@ -84,7 +94,7 @@ namespace SabreTools.Models.Internal
if (asArray != null)
return string.Join(',', asArray);
return null;
return this[key]!.ToString();
}
/// <summary>
@@ -92,6 +102,9 @@ namespace SabreTools.Models.Internal
/// </summary>
public string[]? ReadStringArray(string key)
{
if (!ValidateKey(key))
return null;
string[]? asArray = Read<string[]>(key);
if (asArray != null)
return asArray;
@@ -100,7 +113,26 @@ namespace SabreTools.Models.Internal
if (asString != null)
return new string[] { asString };
asString = this[key]!.ToString();
if (asString != null)
return new string[] { asString };
return null;
}
/// <summary>
/// Check if a key is valid
/// </summary>
private bool ValidateKey(string key)
{
if (string.IsNullOrWhiteSpace(key))
return false;
else if (!ContainsKey(key))
return false;
else if (this[key] == null)
return false;
return true;
}
}
}