Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 41 additions & 3 deletions src/EncDotNet.Iso8211/Iso8211DataDescriptiveRecordReader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -491,12 +491,50 @@ internal static ImmutableArray<Iso8211SubfieldFormat> ParseFormatControls(string

foreach (var part in parts)
{
var parsed = ParseSingleFormat(part.Trim(), out int repeatCount);
var trimmedPart = part.Trim();

// Check for parenthesized sub-groups, e.g. "(3b24)" or "2(b14,I(10))"
int parenStart = trimmedPart.IndexOf('(');
if (parenStart >= 0 && trimmedPart.EndsWith(')'))
{
// Parse any leading repeat count before the opening paren
int outerRepeat = 0;
for (int i = 0; i < parenStart; i++)
{
if (char.IsDigit(trimmedPart[i]))
{
outerRepeat = outerRepeat * 10 + (trimmedPart[i] - '0');
}
else
{
// Not a digit before '(' — not a sub-group, fall through to ParseSingleFormat
outerRepeat = -1;
break;
}
}

if (outerRepeat >= 0)
{
int count = outerRepeat > 0 ? outerRepeat : 1;
// Recursively parse the parenthesized content
var innerContent = trimmedPart.Substring(parenStart + 1, trimmedPart.Length - parenStart - 2);
var innerFormats = ParseFormatControls(innerContent);

for (int r = 0; r < count; r++)
{
formats.AddRange(innerFormats);
}

continue;
}
}

var parsed = ParseSingleFormat(trimmedPart, out int repeatCount);
if (parsed.HasValue)
{
// Expand repeat counts: e.g., "2b24" produces two b24 entries
int count = repeatCount > 0 ? repeatCount : 1;
for (int r = 0; r < count; r++)
int repeatN = repeatCount > 0 ? repeatCount : 1;
for (int r = 0; r < repeatN; r++)
{
formats.Add(parsed.Value);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -921,6 +921,66 @@ public void ParseFormatControls_MultipleRepeatCounts_ExpandAll()
Assert.Equal(4, formats[4].Width);
}

[Fact]
public void ParseFormatControls_NestedParentheses_InnerGroupExpanded()
{
// Act — "(b11,(3b24))" should produce: b11, b24, b24, b24
var formats = Iso8211DataDescriptiveRecordReader.ParseFormatControls("(b11,(3b24))");

// Assert
Assert.Equal(4, formats.Length);

Assert.Equal(Iso8211SubfieldFormatType.UnsignedInteger, formats[0].FormatType);
Assert.Equal(1, formats[0].Width);

for (int i = 1; i < 4; i++)
{
Assert.Equal(Iso8211SubfieldFormatType.SignedInteger, formats[i].FormatType);
Assert.Equal(4, formats[i].Width);
}
}

[Fact]
public void ParseFormatControls_NestedParentheses_RepeatBeforeParen()
{
// Act — "(b11,3(b24))" should produce: b11, b24, b24, b24
var formats = Iso8211DataDescriptiveRecordReader.ParseFormatControls("(b11,3(b24))");

// Assert
Assert.Equal(4, formats.Length);

Assert.Equal(Iso8211SubfieldFormatType.UnsignedInteger, formats[0].FormatType);
Assert.Equal(1, formats[0].Width);

for (int i = 1; i < 4; i++)
{
Assert.Equal(Iso8211SubfieldFormatType.SignedInteger, formats[i].FormatType);
Assert.Equal(4, formats[i].Width);
}
}

[Fact]
public void ParseFormatControls_NestedParentheses_MixedGroup()
{
// Act — "(A,2(I(10),b14))" should produce: A, I(10), b14, I(10), b14
var formats = Iso8211DataDescriptiveRecordReader.ParseFormatControls("(A,2(I(10),b14))");

// Assert
Assert.Equal(5, formats.Length);

Assert.Equal(Iso8211SubfieldFormatType.CharacterData, formats[0].FormatType);

Assert.Equal(Iso8211SubfieldFormatType.Integer, formats[1].FormatType);
Assert.Equal(10, formats[1].Width);
Assert.Equal(Iso8211SubfieldFormatType.UnsignedInteger, formats[2].FormatType);
Assert.Equal(4, formats[2].Width);

Assert.Equal(Iso8211SubfieldFormatType.Integer, formats[3].FormatType);
Assert.Equal(10, formats[3].Width);
Assert.Equal(Iso8211SubfieldFormatType.UnsignedInteger, formats[4].FormatType);
Assert.Equal(4, formats[4].Width);
}

#endregion

#region Repeating Group Tests
Expand Down
Loading