From 22cce4257d8ebd797ddfab707c31a835a206d617 Mon Sep 17 00:00:00 2001 From: Chris Bell Date: Tue, 1 Jul 2025 00:19:17 -0500 Subject: [PATCH] Even further seperating the parser from having hardcoded values --- SessionZero/Data/CharacterTemplate.cs | 1 + SessionZero/Data/Dataset.cs | 1 + SessionZero/Data/SzfObjectAttribute.cs | 18 +++++++ SessionZero/Data/SzfParser.cs | 74 +++++++++++++++++--------- 4 files changed, 69 insertions(+), 25 deletions(-) create mode 100644 SessionZero/Data/SzfObjectAttribute.cs diff --git a/SessionZero/Data/CharacterTemplate.cs b/SessionZero/Data/CharacterTemplate.cs index 76d545c..7c5e8aa 100644 --- a/SessionZero/Data/CharacterTemplate.cs +++ b/SessionZero/Data/CharacterTemplate.cs @@ -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; diff --git a/SessionZero/Data/Dataset.cs b/SessionZero/Data/Dataset.cs index 7a86792..7b97002 100644 --- a/SessionZero/Data/Dataset.cs +++ b/SessionZero/Data/Dataset.cs @@ -1,5 +1,6 @@ namespace SessionZero.Data; +[SzfObject(SzfObjectType.Dataset, "dataset")] public class Dataset : SzfObject { public string DatasetType { get; set; } = string.Empty; diff --git a/SessionZero/Data/SzfObjectAttribute.cs b/SessionZero/Data/SzfObjectAttribute.cs new file mode 100644 index 0000000..8bb9f8e --- /dev/null +++ b/SessionZero/Data/SzfObjectAttribute.cs @@ -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; + } +} \ No newline at end of file diff --git a/SessionZero/Data/SzfParser.cs b/SessionZero/Data/SzfParser.cs index 71694a4..8b8eaba 100644 --- a/SessionZero/Data/SzfParser.cs +++ b/SessionZero/Data/SzfParser.cs @@ -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 s_objectTypeToClassMap; + private static readonly Dictionary s_stringToObjectTypeMap; + + static SzfParser() + { + s_objectTypeToClassMap = new Dictionary(); + s_stringToObjectTypeMap = new Dictionary(); + + // 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() + }) + .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; + } + } + /// /// Parse a SZF file content and return the appropriate SzfObject type /// @@ -66,18 +93,16 @@ public class SzfParser } /// - /// Create an instance based on the object type + /// Create an instance based on the object type using the dynamically built map /// 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}"); } /// @@ -231,35 +256,34 @@ public class SzfParser } /// - /// Parse object type from string + /// Parse object type from string using the dynamically built map /// 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}"); } /// - /// Validate that the parsed type matches the requested generic type + /// Validate that the parsed type matches the requested generic type using the attribute /// private void ValidateObjectType(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(); - 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}"); } }