Keep parsing internal to FilterKey

This commit is contained in:
Matt Nadareski
2024-10-24 04:01:45 -04:00
parent 4e77883bfc
commit ca282b0ba1
9 changed files with 270 additions and 248 deletions

View File

@@ -24,6 +24,13 @@ namespace SabreTools.Core.Filter
#region Constructors
public ExtraIniItem(string key, string ini)
{
Key = new FilterKey(key);
if (!PopulateFromFile(ini))
Mappings.Clear();
}
public ExtraIniItem(string itemName, string fieldName, string ini)
{
Key = new FilterKey(itemName, fieldName);

View File

@@ -1,3 +1,8 @@
using System;
using System.Linq;
using SabreTools.Core.Tools;
using SabreTools.Models.Metadata;
namespace SabreTools.Core.Filter
{
/// <summary>
@@ -16,15 +21,196 @@ namespace SabreTools.Core.Filter
public readonly string FieldName;
/// <summary>
/// Discrete value constructor
/// Validating combined key constructor
/// </summary>
public FilterKey(string? key)
{
if (!ParseFilterId(key, out string itemName, out string fieldName))
throw new ArgumentException(nameof(key));
ItemName = itemName;
FieldName = fieldName;
}
/// <summary>
/// Validating discrete value constructor
/// </summary>
public FilterKey(string itemName, string fieldName)
{
if (!ParseFilterId(ref itemName, ref fieldName))
throw new ArgumentException(nameof(itemName));
ItemName = itemName;
FieldName = fieldName;
}
/// <inheritdoc/>
public override string ToString() => $"{ItemName}.{FieldName}";
/// <summary>
/// Parse a filter ID string into the item name and field name, if possible
/// </summary>
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 (itemFieldString == null)
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);
}
/// <summary>
/// Parse a filter ID string into the item name and field name, if possible
/// </summary>
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),
};
}
/// <summary>
/// Parse and validate header fields
/// </summary>
private static bool ParseHeaderFilterId(ref string itemName, ref string fieldName)
{
// Get the set of constants
var constants = TypeHelper.GetConstants(typeof(Header));
if (constants == null)
return false;
// Get if there's a match to the constant
string localFieldName = fieldName;
string? constantMatch = constants.FirstOrDefault(c => string.Equals(c, localFieldName, StringComparison.OrdinalIgnoreCase));
if (constantMatch == null)
return false;
// Return the sanitized ID
itemName = MetadataFile.HeaderKey;
fieldName = constantMatch;
return true;
}
/// <summary>
/// Parse and validate machine/game fields
/// </summary>
private static bool ParseMachineFilterId(ref string itemName, ref string fieldName)
{
// Get the set of constants
var constants = TypeHelper.GetConstants(typeof(Machine));
if (constants == null)
return false;
// Get if there's a match to the constant
string localFieldName = fieldName;
string? constantMatch = constants.FirstOrDefault(c => string.Equals(c, localFieldName, StringComparison.OrdinalIgnoreCase));
if (constantMatch == null)
return false;
// Return the sanitized ID
itemName = MetadataFile.MachineKey;
fieldName = constantMatch;
return true;
}
/// <summary>
/// Parse and validate item fields
/// </summary>
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 = itemTypes.FirstOrDefault(t => DatItemContainsField(t, localFieldName));
if (matchedType != null)
{
// Check for a matching field
string? matchedField = GetMatchingField(matchedType, fieldName);
if (matchedField == null)
return false;
itemName = "item";
fieldName = matchedField;
return true;
}
}
else
{
// Check for a matching field
string? matchedField = GetMatchingField(itemName, fieldName);
if (matchedField == null)
return false;
itemName = itemName.ToLowerInvariant();
fieldName = matchedField;
return true;
}
// Nothing was found
return false;
}
/// <summary>
/// Determine if an item type contains a field
/// </summary>
private static bool DatItemContainsField(string itemName, string fieldName)
=> GetMatchingField(itemName, fieldName) != null;
/// <summary>
/// Determine if an item type contains a field
/// </summary>
private static string? GetMatchingField(string itemName, string fieldName)
{
// Get the correct item type
var itemType = TypeHelper.GetDatItemType(itemName.ToLowerInvariant());
if (itemType == null)
return null;
// Get the set of constants
var constants = TypeHelper.GetConstants(itemType);
if (constants == null)
return null;
// Get if there's a match to the constant
string localFieldName = fieldName;
string? constantMatch = constants.FirstOrDefault(c => string.Equals(c, localFieldName, StringComparison.OrdinalIgnoreCase));
return constantMatch;
}
}
}

View File

@@ -31,30 +31,21 @@ namespace SabreTools.Core.Filter
if (!SplitFilterString(filterString, out var keyItem, out Operation operation, out var value))
throw new ArgumentOutOfRangeException(nameof(filterString));
if (!FilterParser.ParseFilterId(keyItem, out string itemName, out string fieldName))
throw new ArgumentOutOfRangeException(nameof(filterString));
Key = new FilterKey(itemName, fieldName);
Key = new FilterKey(keyItem);
Value = value;
Operation = operation;
}
public FilterObject(string itemField, string? value, string? operation)
{
if (!FilterParser.ParseFilterId(itemField, out string itemName, out string fieldName))
throw new ArgumentOutOfRangeException(nameof(value));
Key = new FilterKey(itemName, fieldName);
Key = new FilterKey(itemField);
Value = value;
Operation = GetOperation(operation);
}
public FilterObject(string itemField, string? value, Operation operation)
{
if (!FilterParser.ParseFilterId(itemField, out string itemName, out string fieldName))
throw new ArgumentOutOfRangeException(nameof(value));
Key = new FilterKey(itemName, fieldName);
Key = new FilterKey(itemField);
Value = value;
Operation = operation;
}

View File

@@ -1,175 +0,0 @@
using System;
using System.Linq;
using SabreTools.Core.Tools;
using SabreTools.Models.Metadata;
namespace SabreTools.Core.Filter
{
public static class FilterParser
{
/// <summary>
/// Parse a filter ID string into the item name and field name, if possible
/// </summary>
public 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 (itemFieldString == null)
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);
}
/// <summary>
/// Parse a filter ID string into the item name and field name, if possible
/// </summary>
public 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),
};
}
/// <summary>
/// Parse and validate header fields
/// </summary>
private static bool ParseHeaderFilterId(ref string itemName, ref string fieldName)
{
// Get the set of constants
var constants = TypeHelper.GetConstants(typeof(Header));
if (constants == null)
return false;
// Get if there's a match to the constant
string localFieldName = fieldName;
string? constantMatch = constants.FirstOrDefault(c => string.Equals(c, localFieldName, StringComparison.OrdinalIgnoreCase));
if (constantMatch == null)
return false;
// Return the sanitized ID
itemName = MetadataFile.HeaderKey;
fieldName = constantMatch;
return true;
}
/// <summary>
/// Parse and validate machine/game fields
/// </summary>
private static bool ParseMachineFilterId(ref string itemName, ref string fieldName)
{
// Get the set of constants
var constants = TypeHelper.GetConstants(typeof(Machine));
if (constants == null)
return false;
// Get if there's a match to the constant
string localFieldName = fieldName;
string? constantMatch = constants.FirstOrDefault(c => string.Equals(c, localFieldName, StringComparison.OrdinalIgnoreCase));
if (constantMatch == null)
return false;
// Return the sanitized ID
itemName = MetadataFile.MachineKey;
fieldName = constantMatch;
return true;
}
/// <summary>
/// Parse and validate item fields
/// </summary>
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 = itemTypes.FirstOrDefault(t => DatItemContainsField(t, localFieldName));
if (matchedType != null)
{
// Check for a matching field
string? matchedField = GetMatchingField(matchedType, fieldName);
if (matchedField == null)
return false;
itemName = "item";
fieldName = matchedField;
return true;
}
}
else
{
// Check for a matching field
string? matchedField = GetMatchingField(itemName, fieldName);
if (matchedField == null)
return false;
itemName = itemName.ToLowerInvariant();
fieldName = matchedField;
return true;
}
// Nothing was found
return false;
}
/// <summary>
/// Determine if an item type contains a field
/// </summary>
private static bool DatItemContainsField(string itemName, string fieldName)
=> GetMatchingField(itemName, fieldName) != null;
/// <summary>
/// Determine if an item type contains a field
/// </summary>
private static string? GetMatchingField(string itemName, string fieldName)
{
// Get the correct item type
var itemType = TypeHelper.GetDatItemType(itemName.ToLowerInvariant());
if (itemType == null)
return null;
// Get the set of constants
var constants = TypeHelper.GetConstants(itemType);
if (constants == null)
return null;
// Get if there's a match to the constant
string localFieldName = fieldName;
string? constantMatch = constants.FirstOrDefault(c => string.Equals(c, localFieldName, StringComparison.OrdinalIgnoreCase));
return constantMatch;
}
}
}

View File

@@ -109,25 +109,14 @@ namespace SabreTools.DatFiles
/// <param name="key">Key for the remover to be set</param>
private bool SetSetter(FilterKey key, string value)
{
// Split the key values for validation
string itemName = key.ItemName;
string fieldName = key.FieldName;
// Get the parser pair out of it, if possible
if (!FilterParser.ParseFilterId(ref itemName, ref fieldName))
return false;
// Set the values back on the key
key = new FilterKey(itemName, fieldName);
switch (itemName)
switch (key.ItemName)
{
case Models.Metadata.MetadataFile.HeaderKey:
HeaderFieldMappings[fieldName] = value;
HeaderFieldMappings[key.FieldName] = value;
return true;
case Models.Metadata.MetadataFile.MachineKey:
MachineFieldMappings[fieldName] = value;
MachineFieldMappings[key.FieldName] = value;
return true;
default:

View File

@@ -66,8 +66,7 @@ namespace SabreTools.Filtering
string fieldString = inputTrimmed.Split(':')[0].ToLowerInvariant().Trim('"', ' ', '\t');
string fileString = inputTrimmed.Substring(fieldString.Length + 1).Trim('"', ' ', '\t');
FilterParser.ParseFilterId(fieldString, out string itemName, out string fieldName);
var item = new ExtraIniItem(itemName, fieldName, fileString);
var item = new ExtraIniItem(fieldString, fileString);
if (item.Mappings.Count > 0)
Items.Add(item);
}

View File

@@ -1,4 +1,5 @@
using System.Collections.Generic;
using Compress.Support.Filters;
using SabreTools.Core.Filter;
using SabreTools.DatFiles;
using SabreTools.IO.Logging;
@@ -15,17 +16,17 @@ namespace SabreTools.Filtering
/// <summary>
/// List of header fields to exclude from writing
/// </summary>
public List<string> HeaderFieldNames { get; } = [];
public readonly List<string> HeaderFieldNames = [];
/// <summary>
/// List of machine fields to exclude from writing
/// </summary>
public List<string> MachineFieldNames { get; } = [];
public readonly List<string> MachineFieldNames = [];
/// <summary>
/// List of fields to exclude from writing
/// </summary>
public Dictionary<string, List<string>> ItemFieldNames { get; } = [];
public readonly Dictionary<string, List<string>> ItemFieldNames = [];
#endregion
@@ -92,27 +93,32 @@ namespace SabreTools.Filtering
return false;
// Get the parser pair out of it, if possible
if (!FilterParser.ParseFilterId(field, out string type, out string key))
return false;
switch (type)
try
{
var key = new FilterKey(field);
switch (key.ItemName)
{
case Models.Metadata.MetadataFile.HeaderKey:
HeaderFieldNames.Add(key);
HeaderFieldNames.Add(key.FieldName);
return true;
case Models.Metadata.MetadataFile.MachineKey:
MachineFieldNames.Add(key);
MachineFieldNames.Add(key.FieldName);
return true;
default:
if (!ItemFieldNames.ContainsKey(type))
ItemFieldNames[type] = [];
if (!ItemFieldNames.ContainsKey(key.ItemName))
ItemFieldNames[key.ItemName] = [];
ItemFieldNames[type].Add(key);
ItemFieldNames[key.ItemName].Add(key.FieldName);
return true;
}
}
catch
{
return false;
}
}
#endregion

View File

@@ -2024,12 +2024,15 @@ Some special strings that can be used:
foreach (string fieldName in GetList(features, UpdateFieldListValue))
{
// Ensure the field is valid
if (!FilterParser.ParseFilterId(fieldName, out string itemType, out string key))
continue;
if (itemType != Models.Metadata.MetadataFile.MachineKey)
try
{
var key = new FilterKey(fieldName);
if (key.ItemName != Models.Metadata.MetadataFile.MachineKey)
continue;
updateFields.Add(key);
updateFields.Add(key.FieldName);
}
catch { }
}
return updateFields;
@@ -2044,15 +2047,18 @@ Some special strings that can be used:
foreach (string fieldName in GetList(features, UpdateFieldListValue))
{
// Ensure the field is valid
if (!FilterParser.ParseFilterId(fieldName, out string itemType, out string key))
continue;
if (itemType == Models.Metadata.MetadataFile.HeaderKey || itemType == Models.Metadata.MetadataFile.MachineKey)
try
{
var key = new FilterKey(fieldName);
if (key.ItemName == Models.Metadata.MetadataFile.HeaderKey || key.ItemName == Models.Metadata.MetadataFile.MachineKey)
continue;
if (!updateFields.ContainsKey(itemType))
updateFields[itemType] = [];
if (!updateFields.ContainsKey(key.ItemName))
updateFields[key.ItemName] = [];
updateFields[itemType].Add(key);
updateFields[key.ItemName].Add(key.FieldName);
}
catch { }
}
return updateFields;

View File

@@ -315,18 +315,22 @@ Reset the internal state: reset();";
}
// Read in the individual arguments
bool parsed = FilterParser.ParseFilterId(Arguments[0], out _, out _);
string itemFieldString = Arguments[0];
string extraFile = Arguments[1];
// If we had an invalid input, log and continue
if (!parsed)
try
{
string message = $"{Arguments[0]} was an invalid field name";
_ = new FilterKey(itemFieldString);
}
catch
{
string message = $"{itemFieldString} was an invalid field name";
return (false, message);
}
if (!File.Exists(extraFile))
{
string message = $"{Arguments[1]} was an invalid file name";
string message = $"{extraFile} was an invalid file name";
return (false, message);
}
@@ -337,12 +341,12 @@ Reset the internal state: reset();";
public override void Process(BatchState batchState)
{
// Read in the individual arguments
FilterParser.ParseFilterId(Arguments[0], out string itemName, out string fieldName);
string key = Arguments[0];
string extraFile = Arguments[1];
// Create the extra INI
var extraIni = new ExtraIni();
var extraIniItem = new ExtraIniItem(itemName, fieldName, extraFile);
var extraIniItem = new ExtraIniItem(key, extraFile);
extraIni.Items.Add(extraIniItem);
// Apply the extra INI blindly
@@ -375,7 +379,7 @@ Reset the internal state: reset();";
}
// Read in the individual arguments
bool parsed = FilterParser.ParseFilterId(Arguments[0], out _, out _);
string itemFieldString = Arguments[0];
bool? filterRemove = false;
if (Arguments.Count >= 3)
filterRemove = Arguments[2].AsYesNo();
@@ -384,9 +388,13 @@ Reset the internal state: reset();";
filterPerMachine = Arguments[3].AsYesNo();
// If we had an invalid input, log and continue
if (!parsed)
try
{
string message = $"{Arguments[0]} was an invalid field name";
_ = new FilterKey(itemFieldString);
}
catch
{
string message = $"{itemFieldString} was an invalid field name";
return (false, message);
}
if (filterRemove == null)
@@ -804,12 +812,18 @@ Reset the internal state: reset();";
}
// Read in the individual arguments
bool parsed = FilterParser.ParseFilterId(Arguments[0], out string type, out _);
string itemFieldString = Arguments[0];
// If we had an invalid input, log and continue
if (!parsed || !string.Equals(type, Models.Metadata.MetadataFile.HeaderKey, StringComparison.OrdinalIgnoreCase))
try
{
string message = $"{Arguments[0]} was an invalid field name";
var key = new FilterKey(itemFieldString);
if (!string.Equals(key.ItemName, Models.Metadata.MetadataFile.HeaderKey, StringComparison.OrdinalIgnoreCase))
throw new Exception();
}
catch
{
string message = $"{itemFieldString} was an invalid field name";
return (false, message);
}
@@ -824,8 +838,7 @@ Reset the internal state: reset();";
string value = Arguments[1];
var setter = new Setter();
FilterParser.ParseFilterId(Arguments[0], out string itemName, out string fieldName);
setter.PopulateSetters(new FilterKey(itemName, fieldName), value);
setter.PopulateSetters(new FilterKey(field), value);
// Set the header field
setter.SetFields(batchState.DatFile.Header);