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 #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) public ExtraIniItem(string itemName, string fieldName, string ini)
{ {
Key = new FilterKey(itemName, fieldName); 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 namespace SabreTools.Core.Filter
{ {
/// <summary> /// <summary>
@@ -16,15 +21,196 @@ namespace SabreTools.Core.Filter
public readonly string FieldName; public readonly string FieldName;
/// <summary> /// <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> /// </summary>
public FilterKey(string itemName, string fieldName) public FilterKey(string itemName, string fieldName)
{ {
if (!ParseFilterId(ref itemName, ref fieldName))
throw new ArgumentException(nameof(itemName));
ItemName = itemName; ItemName = itemName;
FieldName = fieldName; FieldName = fieldName;
} }
/// <inheritdoc/> /// <inheritdoc/>
public override string ToString() => $"{ItemName}.{FieldName}"; 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)) if (!SplitFilterString(filterString, out var keyItem, out Operation operation, out var value))
throw new ArgumentOutOfRangeException(nameof(filterString)); throw new ArgumentOutOfRangeException(nameof(filterString));
if (!FilterParser.ParseFilterId(keyItem, out string itemName, out string fieldName)) Key = new FilterKey(keyItem);
throw new ArgumentOutOfRangeException(nameof(filterString));
Key = new FilterKey(itemName, fieldName);
Value = value; Value = value;
Operation = operation; Operation = operation;
} }
public FilterObject(string itemField, string? value, string? operation) public FilterObject(string itemField, string? value, string? operation)
{ {
if (!FilterParser.ParseFilterId(itemField, out string itemName, out string fieldName)) Key = new FilterKey(itemField);
throw new ArgumentOutOfRangeException(nameof(value));
Key = new FilterKey(itemName, fieldName);
Value = value; Value = value;
Operation = GetOperation(operation); Operation = GetOperation(operation);
} }
public FilterObject(string itemField, string? value, Operation operation) public FilterObject(string itemField, string? value, Operation operation)
{ {
if (!FilterParser.ParseFilterId(itemField, out string itemName, out string fieldName)) Key = new FilterKey(itemField);
throw new ArgumentOutOfRangeException(nameof(value));
Key = new FilterKey(itemName, fieldName);
Value = value; Value = value;
Operation = operation; 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> /// <param name="key">Key for the remover to be set</param>
private bool SetSetter(FilterKey key, string value) private bool SetSetter(FilterKey key, string value)
{ {
// Split the key values for validation switch (key.ItemName)
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)
{ {
case Models.Metadata.MetadataFile.HeaderKey: case Models.Metadata.MetadataFile.HeaderKey:
HeaderFieldMappings[fieldName] = value; HeaderFieldMappings[key.FieldName] = value;
return true; return true;
case Models.Metadata.MetadataFile.MachineKey: case Models.Metadata.MetadataFile.MachineKey:
MachineFieldMappings[fieldName] = value; MachineFieldMappings[key.FieldName] = value;
return true; return true;
default: default:

View File

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

View File

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

View File

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

View File

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