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
18 changes: 18 additions & 0 deletions src/EncDotNet.Iso8211/Iso8211DataDescriptiveRecordReader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -360,6 +360,24 @@ private static void ParseSubfieldLabels(
repeatingGroupStartIndex = subfieldIndex;
rawName = rawName.Substring(1);
}
else
{
// Handle mid-string '*' — concatenated array leader/repeating separator.
// ISO 8211 §6.4.3.3: leader labels appear before '*', cell labels after.
int starIndex = rawName.IndexOf('*');
if (starIndex > 0)
{
string leaderName = rawName.Substring(0, starIndex);
if (!string.IsNullOrEmpty(leaderName))
{
names.Add(leaderName);
subfieldIndex++;
}

repeatingGroupStartIndex = subfieldIndex;
rawName = rawName.Substring(starIndex + 1);
}
}

if (!string.IsNullOrEmpty(rawName))
{
Expand Down
6 changes: 4 additions & 2 deletions src/EncDotNet.Iso8211/Iso8211FieldReader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -432,8 +432,10 @@ public IEnumerable<Iso8211SubfieldGroup> GetSubfieldGroups()
}
else if (!inRepeatingGroup && subfieldIndex == _fieldDefinition.RepeatingSubfieldStartIndex && _fieldDefinition.HasRepeatingGroup)
{
// Just entered the repeating group for the first time
groupCount = 1;
// Just entered the repeating group for the first time.
// Set to 0; the wrap-around at line ~424 will increment to 1
// after the first complete group is parsed.
groupCount = 0;
inRepeatingGroup = true;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1426,5 +1426,71 @@ public void Parse_TwoUtFormat_ConcatenatedArrayWithSubfieldLabels_ParsesAsSubfie
Assert.Equal(0, sg3d.RepeatingSubfieldStartIndex);
}

[Fact]
public void Parse_TwoUtFormat_ConcatenatedArrayWithLeaderAndRepeating_ParsesCorrectly()
{
// Arrange — ConcatenatedArray with mid-string '*' separating leader from repeating subfields.
// This is the S-101 C3IL pattern: VCID is a leader subfield, YCOO/XCOO/ZCOO repeat.
var record = CreateDdrRecordTwoUt(
tag: "C3IL",
dataStructureCode: '3',
dataTypeCode: '5',
fieldName: "3-D Coordinate with Leader",
subfieldLabels: "VCID*YCOO!XCOO!ZCOO",
formatControls: "(b11,3b24)");

// Act
var ddr = Iso8211DataDescriptiveRecordReader.Read(record);
var c3il = ddr.FieldDefinitions[0];

// Assert — 4 subfield definitions
Assert.Equal(4, c3il.SubfieldDefinitions.Count);

Assert.Equal("VCID", c3il.SubfieldDefinitions[0].Name);
Assert.Equal(Iso8211SubfieldFormatType.UnsignedInteger, c3il.SubfieldDefinitions[0].Format.FormatType);
Assert.Equal(1, c3il.SubfieldDefinitions[0].Format.Width);

Assert.Equal("YCOO", c3il.SubfieldDefinitions[1].Name);
Assert.Equal(Iso8211SubfieldFormatType.SignedInteger, c3il.SubfieldDefinitions[1].Format.FormatType);
Assert.Equal(4, c3il.SubfieldDefinitions[1].Format.Width);

Assert.Equal("XCOO", c3il.SubfieldDefinitions[2].Name);
Assert.Equal("ZCOO", c3il.SubfieldDefinitions[3].Name);

// Repeating group starts at index 1 (after VCID leader)
Assert.True(c3il.HasRepeatingGroup);
Assert.Equal(1, c3il.RepeatingSubfieldStartIndex);

// VCID is non-repeating leader, YCOO/XCOO/ZCOO are repeating
Assert.False(c3il.SubfieldDefinitions[0].IsRepeating);
Assert.True(c3il.SubfieldDefinitions[1].IsRepeating);
Assert.True(c3il.SubfieldDefinitions[2].IsRepeating);
Assert.True(c3il.SubfieldDefinitions[3].IsRepeating);
}

[Fact]
public void Parse_TwoUtFormat_VectorStyleStarAtStart_StillWorks()
{
// Arrange — regression guard: '*' at the start of labels (Vector-style)
var record = CreateDdrRecordTwoUt(
tag: "SG2D",
dataStructureCode: '1',
dataTypeCode: '6',
fieldName: "2-D Coordinate Field",
subfieldLabels: "*YCOO!XCOO",
formatControls: "(2b24)");

// Act
var ddr = Iso8211DataDescriptiveRecordReader.Read(record);
var sg2d = ddr.FieldDefinitions[0];

// Assert
Assert.Equal(2, sg2d.SubfieldDefinitions.Count);
Assert.Equal("YCOO", sg2d.SubfieldDefinitions[0].Name);
Assert.Equal("XCOO", sg2d.SubfieldDefinitions[1].Name);
Assert.Equal(0, sg2d.RepeatingSubfieldStartIndex);
Assert.True(sg2d.HasRepeatingGroup);
}

#endregion
}
80 changes: 80 additions & 0 deletions tests/EndDotNet.UnitTests/Iso8211FieldReaderTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1191,5 +1191,85 @@ public void GetSubfieldGroups_Ucs2NatfFieldMultipleGroups_ParsesCorrectly()
Assert.Equal("BOB", groups[1].GetSubfield<string>("ATVL"));
}

[Fact]
public void ReadConcatenatedArrayWithLeaderAndRepeatingGroups()
{
// Arrange — C3IL-style field: 1 leader subfield (VCID) + 3 repeating subfields (YCOO, XCOO, ZCOO)
var fieldDef = new Iso8211FieldDefinition
{
Tag = "C3IL",
DataStructureCode = Iso8211DataStructureCode.ConcatenatedArray,
DataTypeCode = Iso8211DataTypeCode.MixedDataTypes,
FieldName = "C3IL",
FormatControls = string.Empty,
SubfieldDefinitions = ImmutableArray.Create(
new Iso8211SubfieldDefinition
{
Name = "VCID",
Format = new Iso8211SubfieldFormat { FormatType = Iso8211SubfieldFormatType.UnsignedInteger, Width = 1 },
Index = 0,
IsRepeating = false
},
new Iso8211SubfieldDefinition
{
Name = "YCOO",
Format = new Iso8211SubfieldFormat { FormatType = Iso8211SubfieldFormatType.SignedInteger, Width = 4 },
Index = 1,
IsRepeating = true
},
new Iso8211SubfieldDefinition
{
Name = "XCOO",
Format = new Iso8211SubfieldFormat { FormatType = Iso8211SubfieldFormatType.SignedInteger, Width = 4 },
Index = 2,
IsRepeating = true
},
new Iso8211SubfieldDefinition
{
Name = "ZCOO",
Format = new Iso8211SubfieldFormat { FormatType = Iso8211SubfieldFormatType.SignedInteger, Width = 4 },
Index = 3,
IsRepeating = true
}),
RepeatingSubfieldStartIndex = 1
};

// 1 leader byte (VCID=7) + 3 sounding points × 12 bytes each = 37 bytes
var data = ConcatFieldData(
new byte[] { 7 }, // VCID = 7
Int32LE(407128000), // YCOO[0]
Int32LE(-740060000), // XCOO[0]
Int32LE(150), // ZCOO[0] — depth
Int32LE(407130000), // YCOO[1]
Int32LE(-740062000), // XCOO[1]
Int32LE(200), // ZCOO[1]
Int32LE(407125000), // YCOO[2]
Int32LE(-740058000), // XCOO[2]
Int32LE(100) // ZCOO[2]
);

var reader = new Iso8211FieldReader(fieldDef, data);

// Assert — leader subfield
Assert.Equal((byte)7, reader.GetFixedSubfield<byte>("VCID"));

// Assert — repeating groups
Assert.Equal(3, reader.GroupCount);
var groups = reader.GetSubfieldGroups().ToArray();
Assert.Equal(3, groups.Length);

Assert.Equal(407128000, groups[0].GetSubfield<int>("YCOO"));
Assert.Equal(-740060000, groups[0].GetSubfield<int>("XCOO"));
Assert.Equal(150, groups[0].GetSubfield<int>("ZCOO"));

Assert.Equal(407130000, groups[1].GetSubfield<int>("YCOO"));
Assert.Equal(-740062000, groups[1].GetSubfield<int>("XCOO"));
Assert.Equal(200, groups[1].GetSubfield<int>("ZCOO"));

Assert.Equal(407125000, groups[2].GetSubfield<int>("YCOO"));
Assert.Equal(-740058000, groups[2].GetSubfield<int>("XCOO"));
Assert.Equal(100, groups[2].GetSubfield<int>("ZCOO"));
}

#endregion
}
Loading