Seperation of concerns for szf object parsing

This commit is contained in:
Chris Bell 2025-06-30 23:46:05 -05:00
parent 2e212e2c37
commit 66dd01020c
3 changed files with 127 additions and 94 deletions

View File

@ -26,6 +26,123 @@ public class Dataset : SzfObject
ImageUrl = imageField.Value?.ToString() ?? string.Empty;
}
/// <summary>
/// Parses sections specific to the Dataset object, such as entries and their subsections.
/// This method is called by the SzfParser to populate Dataset-specific data.
/// </summary>
/// <param name="sections">A list of all parsed sections from the SZF file.</param>
public override void ParseSections(List<SzfSection> sections)
{
// Dictionary to hold DatasetEntry objects by their name for easy lookup
var entriesByName = new Dictionary<string, DatasetEntry>(StringComparer.OrdinalIgnoreCase);
// First pass: Identify and populate top-level DatasetEntry objects
foreach (var szfSection in sections)
{
if (szfSection.Name.StartsWith("Entry:", StringComparison.OrdinalIgnoreCase))
{
var entryName = szfSection.Name.Substring("Entry:".Length).Trim();
if (string.IsNullOrEmpty(entryName)) continue;
var newEntry = new DatasetEntry { Name = entryName };
foreach (var field in szfSection.Fields)
{
newEntry.Fields.Add(ConvertToDatasetField(field));
}
Entries.Add(newEntry);
entriesByName[entryName] = newEntry;
}
}
// Second pass: Populate sections and subsections for each entry
foreach (var szfSection in sections)
{
// We are looking for sections like "Entry.EntryName.SectionName" or "Entry.EntryName.SectionName.SubSectionName"
if (!szfSection.Name.StartsWith("Entry.", StringComparison.OrdinalIgnoreCase)) continue;
// Split the section name by '.' to identify parts
var nameParts = szfSection.Name.Split('.');
// Ensure it's a section of an entry (e.g., "Entry.EntryName.Section")
if (nameParts.Length < 3 || !nameParts[0].Equals("Entry", StringComparison.OrdinalIgnoreCase)) continue;
var entryName = nameParts[1];
if (!entriesByName.TryGetValue(entryName, out var targetEntry)) continue; // Entry not found, skip
// We need to build the hierarchy of DatasetSection objects
DatasetSection? currentParentSection = null;
List<DatasetSection> currentSectionList = targetEntry.Sections;
for (int i = 2; i < nameParts.Length; i++) // Start from the first section name after "Entry.EntryName"
{
var sectionNamePart = nameParts[i];
var existingSection = currentSectionList.FirstOrDefault(s =>
s.Name.Equals(sectionNamePart, StringComparison.OrdinalIgnoreCase));
if (existingSection == null)
{
// Create new section
var newSection = new DatasetSection { Name = sectionNamePart };
currentSectionList.Add(newSection);
currentParentSection = newSection;
}
else
{
currentParentSection = existingSection;
}
// If this is the last part of the section name, populate its fields
if (i == nameParts.Length - 1)
{
foreach (var field in szfSection.Fields)
{
currentParentSection.Fields.Add(ConvertToDatasetField(field));
}
}
// Move to the next level down in the hierarchy
currentSectionList = currentParentSection.Subsections;
}
}
}
/// <summary>
/// Convert SzfField to DatasetField
/// </summary>
private DatasetField ConvertToDatasetField(SzfField szfField)
{
return new DatasetField
{
Name = szfField.Name,
Type = ParseDatasetFieldType(szfField.Type),
Value = szfField.Value
};
}
/// <summary>
/// Parse DatasetFieldType from string
/// </summary>
private DatasetFieldType ParseDatasetFieldType(string typeString)
{
return typeString.ToLower() switch
{
"text" => DatasetFieldType.Text,
"text-field" => DatasetFieldType.TextField,
"number" => DatasetFieldType.Number,
"bool" => DatasetFieldType.Boolean,
"group" => DatasetFieldType.Group,
"calculated" => DatasetFieldType.Calculated,
"system" => DatasetFieldType.System,
"dataset-reference" => DatasetFieldType.DatasetReference,
"dataset-type" => DatasetFieldType.DatasetType,
"dataset-reference-multiple" => DatasetFieldType.DatasetReferenceMultiple,
"dataset-type-multiple" => DatasetFieldType.DatasetTypeMultiple,
_ => DatasetFieldType.Text
};
}
public override SzfValidationResult Validate()
{
var result = base.Validate();

View File

@ -15,6 +15,13 @@ public abstract class SzfObject
public abstract SzfObjectType ObjectType { get; }
/// <summary>
/// Abstract method for SzfObjects to parse their specific sections from the raw parsed structure.
/// This promotes separation of concerns by moving type-specific parsing logic into the SzfObject itself.
/// </summary>
/// <param name="sections">A list of all parsed sections from the SZF file.</param>
public abstract void ParseSections(List<SzfSection> sections);
public virtual SzfValidationResult Validate()
{
var result = new SzfValidationResult { IsValid = true };

View File

@ -281,99 +281,8 @@ public class SzfParser
obj.PopulateFromMetadata(metadata);
}
// Process type-specific content
ProcessTypeSpecificContent(obj, parseResult);
}
/// <summary>
/// Process type-specific content based on the object type
/// </summary>
private void ProcessTypeSpecificContent(SzfObject obj, SzfParseResult parseResult)
{
switch (obj)
{
case Dataset dataset:
ProcessDatasetContent(dataset, parseResult);
break;
// Add other types as they're implemented
}
}
/// <summary>
/// Process dataset-specific content (entries and their sections)
/// </summary>
private void ProcessDatasetContent(Dataset dataset, SzfParseResult parseResult)
{
var entrySections = parseResult.Sections
.Where(s => s.Name.StartsWith("Entry:", StringComparison.OrdinalIgnoreCase))
.ToList();
foreach (var entrySection in entrySections)
{
var entryName = entrySection.Name.Substring(6).Trim(); // Remove "Entry: " prefix
var entry = new DatasetEntry { Name = entryName };
// Add fields directly on the entry
foreach (var field in entrySection.Fields)
{
entry.Fields.Add(ConvertToDatasetField(field));
}
// Find subsections for this entry
var entrySubsections = parseResult.Sections
.Where(s => s.Name.StartsWith($"Entry.{entryName}.", StringComparison.OrdinalIgnoreCase))
.ToList();
foreach (var subsection in entrySubsections)
{
var sectionName = subsection.Name.Substring($"Entry.{entryName}.".Length);
var datasetSection = new DatasetSection { Name = sectionName };
foreach (var field in subsection.Fields)
{
datasetSection.Fields.Add(ConvertToDatasetField(field));
}
entry.Sections.Add(datasetSection);
}
dataset.Entries.Add(entry);
}
}
/// <summary>
/// Convert SzfField to DatasetField
/// </summary>
private DatasetField ConvertToDatasetField(SzfField szfField)
{
return new DatasetField
{
Name = szfField.Name,
Type = ParseDatasetFieldType(szfField.Type),
Value = szfField.Value
};
}
/// <summary>
/// Parse DatasetFieldType from string
/// </summary>
private DatasetFieldType ParseDatasetFieldType(string typeString)
{
return typeString.ToLower() switch
{
"text" => DatasetFieldType.Text,
"text-field" => DatasetFieldType.TextField,
"number" => DatasetFieldType.Number,
"bool" => DatasetFieldType.Boolean,
"group" => DatasetFieldType.Group,
"calculated" => DatasetFieldType.Calculated,
"system" => DatasetFieldType.System,
"dataset-reference" => DatasetFieldType.DatasetReference,
"dataset-type" => DatasetFieldType.DatasetType,
"dataset-reference-multiple" => DatasetFieldType.DatasetReferenceMultiple,
"dataset-type-multiple" => DatasetFieldType.DatasetTypeMultiple,
_ => DatasetFieldType.Text
};
// Delegate type-specific section parsing to the SzfObject itself
obj.ParseSections(parseResult.Sections);
}
}
@ -391,7 +300,7 @@ internal class SzfParseResult
/// <summary>
/// Internal class representing a parsed section
/// </summary>
internal class SzfSection
public class SzfSection
{
public string Name { get; set; } = string.Empty;
public List<SzfField> Fields { get; set; } = new();