SessionZero.SzfToolkit-Dotnet/SessionZero.SzfLib/Parser/SzfParser.cs

173 lines
5.7 KiB
C#

using SessionZero.SzfLib.File;
using SessionZero.SzfLib.Helpers;
using SessionZero.SzfLib.Objects;
namespace SessionZero.SzfLib.Parser;
public class SzfParser : ISzfParser
{
/// <summary>
/// Parses the given SZF content and returns the corresponding ISzfObject.
/// </summary>
/// <param name="szfContent"></param>
/// <returns></returns>
public ISzfObject? Parse(string szfContent)
{
var lines = szfContent.Split(new[] { '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries);
string szfObjectTypeName = string.Empty;
Type? szfObjectType = null;
string szfVersion = "1.0.0";
var sections = new Dictionary<string, SzfSection>();
for (int idx = 0; idx < lines.Length; idx++)
{
var line = lines[idx].Trim();
if (line.StartsWith("#") || string.IsNullOrWhiteSpace(line)) continue;
if (line.StartsWith("!type:", StringComparison.OrdinalIgnoreCase))
{
var typeIdentifier = line.Substring("!type:".Length).Trim();
szfObjectTypeName = typeIdentifier;
szfObjectType = SzfHelper.FindSzfObjectTypeByAttributeType(typeIdentifier);
continue;
}
if (line.StartsWith("!schema:", StringComparison.OrdinalIgnoreCase))
{
var version = line.Substring("!schema:".Length).Trim();
if (!string.IsNullOrEmpty(version)) szfVersion = version;
continue;
}
if (line.StartsWith("[") && line.EndsWith("]"))
{
var sectionName = line.Substring(1, line.Length - 2).Trim();
var sectionFields = new List<string>();
int i = idx + 1;
while (i < lines.Length && !(lines[i].Trim().StartsWith("[") && lines[i].Trim().EndsWith("]")))
{
var fieldLine = lines[i].Trim();
if (!string.IsNullOrWhiteSpace(fieldLine) && !fieldLine.StartsWith("#")) sectionFields.Add(fieldLine);
i++;
}
idx = i - 1;
CreateSection(sectionName, sectionFields, sections);
}
}
if (szfObjectType != null && sections.Count > 0)
{
var szfObject = (ISzfObject)Activator.CreateInstance(szfObjectType)!;
szfObject.SzfType = szfObjectTypeName;
szfObject.SzfVersion = szfVersion;
// Only add root sections (those without a dot in their name)
foreach (var section in sections.Values.Where(s => !sections.Keys.Any(k => k.EndsWith("." + s.Name))))
{
szfObject.Sections.Add(section);
}
return szfObject.Parse() ?? szfObject;
}
return null;
}
public ISzfObject? Parse(ISzfFile file)
{
if (file == null)
{
throw new ArgumentNullException(nameof(file), "File cannot be null.");
}
if (string.IsNullOrEmpty(file.Content))
{
throw new ArgumentException("File content cannot be null or empty.", nameof(file.Content));
}
return Parse(file.Content);
}
private SzfSection CreateSection(string sectionPath, IEnumerable<string> fields, Dictionary<string, SzfSection> sections)
{
var nameParts = sectionPath.Split('.');
SzfSection? parent = null;
string currentPath = "";
for (int p = 0; p < nameParts.Length; p++)
{
currentPath = currentPath == "" ? nameParts[p] : currentPath + "." + nameParts[p];
if (!sections.TryGetValue(currentPath, out var section))
{
section = new SzfSection(nameParts[p]);
sections[currentPath] = section;
if (parent != null) parent.Subsections[nameParts[p]] = section;
}
parent = section;
}
foreach (var field in fields)
{
var szfField = CreateField(field);
parent!.Fields.Add(szfField.Name, szfField);
}
return parent!;
}
private SzfField CreateField(string field)
{
// Expected format: FieldName (type) = Value
int typeStart = field.IndexOf('(');
int typeEnd = field.IndexOf(')');
if (typeStart < 0 || typeEnd < 0 || typeEnd < typeStart) throw new FormatException($"Invalid field format: {field}");
string fieldName = field.Substring(0, typeStart).Trim();
string fieldType = field.Substring(typeStart + 1, typeEnd - typeStart - 1).Trim().Replace("-", string.Empty);
// Find the first '=' after the closing parenthesis
int eqIndex = field.IndexOf('=', typeEnd + 1);
string fieldValue = eqIndex >= 0 ? field.Substring(eqIndex + 1).Trim() : string.Empty;
if (!Enum.TryParse(fieldType, true, out SzfFieldType type)) throw new FormatException($"Invalid field type: {fieldType}");
SzfField szfField = new(fieldName, type) { Value = fieldValue };
return szfField;
}
}
public class SzfSection(string name)
{
public string Name { get; set; } = name;
public Dictionary<string, SzfField> Fields { get; set; } = new();
public Dictionary<string, SzfSection> Subsections { get; set; } = new();
}
public class SzfField(string name, SzfFieldType type)
{
public string Name { get; set; } = name;
public SzfFieldType SzfType { get; set; } = type;
public string? Value { get; set; }
}
public enum SzfFieldType
{
Text,
TextField,
Number,
Bool,
Calculated,
System,
EntryReference,
EntryReferenceList,
}