Even further seperating the parser from having hardcoded values

This commit is contained in:
Chris Bell 2025-07-01 00:19:17 -05:00
parent d12474c01e
commit 22cce4257d
4 changed files with 69 additions and 25 deletions

View File

@ -4,6 +4,7 @@ using System.Linq;
namespace SessionZero.Data;
[SzfObject(SzfObjectType.CharacterTemplate, "character_template")]
public class CharacterTemplate : SzfObject
{
public string GameSystem { get; set; } = string.Empty;

View File

@ -1,5 +1,6 @@
namespace SessionZero.Data;
[SzfObject(SzfObjectType.Dataset, "dataset")]
public class Dataset : SzfObject
{
public string DatasetType { get; set; } = string.Empty;

View File

@ -0,0 +1,18 @@
// SzfObjectAttribute.cs
using System;
namespace SessionZero.Data;
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)]
public class SzfObjectAttribute : Attribute
{
public SzfObjectType ObjectType { get; }
public string TypeString { get; }
public SzfObjectAttribute(SzfObjectType objectType, string typeString)
{
ObjectType = objectType;
TypeString = typeString;
}
}

View File

@ -1,3 +1,4 @@
using System.Reflection;
using System.Text.RegularExpressions;
namespace SessionZero.Data;
@ -8,6 +9,32 @@ public class SzfParser
private static readonly Regex SectionRegex = new(@"^\[([^\]]+)\]$", RegexOptions.Compiled);
private static readonly Regex FieldRegex = new(@"^(\w+)\s*\(([^)]+)\)\s*=\s*(.*)$", RegexOptions.Compiled);
// Static dictionaries to store type mappings, initialized once
private static readonly Dictionary<SzfObjectType, Type> s_objectTypeToClassMap;
private static readonly Dictionary<string, SzfObjectType> s_stringToObjectTypeMap;
static SzfParser()
{
s_objectTypeToClassMap = new Dictionary<SzfObjectType, Type>();
s_stringToObjectTypeMap = new Dictionary<string, SzfObjectType>();
// Discover types with SzfObjectAttribute in the current assembly
var szfObjectTypes = Assembly.GetExecutingAssembly().GetTypes()
.Where(type => typeof(SzfObject).IsAssignableFrom(type) && !type.IsAbstract)
.Select(type => new
{
Type = type,
Attribute = type.GetCustomAttribute<SzfObjectAttribute>()
})
.Where(x => x.Attribute != null);
foreach (var item in szfObjectTypes)
{
s_objectTypeToClassMap[item.Attribute!.ObjectType] = item.Type;
s_stringToObjectTypeMap[item.Attribute.TypeString.ToLower()] = item.Attribute.ObjectType;
}
}
/// <summary>
/// Parse a SZF file content and return the appropriate SzfObject type
/// </summary>
@ -66,18 +93,16 @@ public class SzfParser
}
/// <summary>
/// Create an instance based on the object type
/// Create an instance based on the object type using the dynamically built map
/// </summary>
private SzfObject CreateInstanceFromType(SzfObjectType objectType)
{
return objectType switch
if (s_objectTypeToClassMap.TryGetValue(objectType, out var type))
{
SzfObjectType.Dataset => new Dataset(),
SzfObjectType.CharacterTemplate => new CharacterTemplate(), // UNCOMMENTED THIS LINE
// SzfObjectType.Character => new Character(),
// SzfObjectType.Session => new Session(),
_ => throw new SzfParseException($"Unsupported object type: {objectType}")
};
return (SzfObject)Activator.CreateInstance(type)!;
}
throw new SzfParseException($"Unsupported object type: {objectType}");
}
/// <summary>
@ -231,35 +256,34 @@ public class SzfParser
}
/// <summary>
/// Parse object type from string
/// Parse object type from string using the dynamically built map
/// </summary>
private SzfObjectType ParseObjectType(string typeString)
{
return typeString.ToLower() switch
if (s_stringToObjectTypeMap.TryGetValue(typeString.ToLower(), out var objectType))
{
"dataset" => SzfObjectType.Dataset,
"character_template" => SzfObjectType.CharacterTemplate,
"character" => SzfObjectType.Character,
"session" => SzfObjectType.Session,
_ => throw new SzfParseException($"Unknown object type: {typeString}")
};
return objectType;
}
throw new SzfParseException($"Unknown object type: {typeString}");
}
/// <summary>
/// Validate that the parsed type matches the requested generic type
/// Validate that the parsed type matches the requested generic type using the attribute
/// </summary>
private void ValidateObjectType<T>(SzfObjectType parsedType) where T : SzfObject
{
var expectedType = typeof(T).Name switch
{
nameof(Dataset) => SzfObjectType.Dataset,
nameof(CharacterTemplate) => SzfObjectType.CharacterTemplate,
_ => throw new SzfParseException($"Unsupported SzfObject type: {typeof(T).Name}")
};
var targetType = typeof(T);
var attribute = targetType.GetCustomAttribute<SzfObjectAttribute>();
if (parsedType != expectedType)
if (attribute == null)
{
throw new SzfParseException($"Type mismatch: Expected {expectedType}, found {parsedType}");
throw new SzfParseException($"Type {targetType.Name} does not have SzfObjectAttribute.");
}
if (parsedType != attribute.ObjectType)
{
throw new SzfParseException($"Type mismatch: Expected {attribute.ObjectType}, found {parsedType}");
}
}