diff --git a/RDMSharp/Metadata/JSON/OneOfTypes/StringType.cs b/RDMSharp/Metadata/JSON/OneOfTypes/StringType.cs index 698a660..b5faec7 100644 --- a/RDMSharp/Metadata/JSON/OneOfTypes/StringType.cs +++ b/RDMSharp/Metadata/JSON/OneOfTypes/StringType.cs @@ -89,7 +89,28 @@ public StringType(string name, MaxBytes = maxBytes; RestrictToASCII = restrictToASCII; } - + public StringType( + string name, + string displayName, + string notes, + string format, + string pattern, + uint length, + bool? restrictToASCII = null) : this( + name, + displayName, + notes, + null, + "string", + format, + pattern, + minLength: length, + maxLength: length, + null, + null, + restrictToASCII) + { + } public override string ToString() { return base.ToString(); @@ -120,13 +141,25 @@ public override byte[] ParsePayloadToData(DataTree dataTree) if (dataTree.Value is string @string) { - if (MaxLength.HasValue) - @string = @string.Substring(0, (int)MaxLength); + if (MinLength.HasValue && MinLength.Value > @string.Length) + throw new ArithmeticException($"The given String is smaller then {nameof(MinLength)}: {MinLength}"); + if (MaxLength.HasValue && MaxLength.Value < @string.Length) + throw new ArithmeticException($"The given String is larger then {nameof(MaxLength)}: {MaxLength}"); + Encoding encoder = null; if (RestrictToASCII == true) - Encoding.ASCII.GetBytes(@string); + encoder = Encoding.ASCII; else - Encoding.UTF8.GetBytes(@string); + encoder = Encoding.UTF8; + + var data = encoder.GetBytes(@string); + + if (MinBytes.HasValue && MinBytes.Value > data.Length) + throw new ArithmeticException($"The given String encoded is smaller then {nameof(MinBytes)}: {MinBytes}"); + if (MaxBytes.HasValue && MaxBytes.Value < data.Length) + throw new ArithmeticException($"The given String encoded is larger then {nameof(MaxBytes)}: {MaxBytes}"); + + return data; } throw new ArithmeticException($"The given Object from {nameof(dataTree.Value)} can't be parsed"); @@ -135,32 +168,62 @@ public override byte[] ParsePayloadToData(DataTree dataTree) public override DataTree ParseDataToPayload(ref byte[] data) { List issueList = new List(); - if (MaxBytes.HasValue && data.Length > MaxBytes) + if (MaxBytes.HasValue && data.Length > MaxBytes.Value) issueList.Add(new DataTreeIssue($"Data length exceeds {nameof(MaxBytes)}, the Data has {data.Length}, but {nameof(MaxBytes)} is {MaxBytes}")); - if (MinBytes.HasValue && data.Length < MinBytes) + if (MinBytes.HasValue && data.Length < MinBytes.Value) issueList.Add(new DataTreeIssue($"Data length falls shorts of {nameof(MinBytes)}, the Data has {data.Length}, but {nameof(MinBytes)} is {MinBytes}")); string str = null; - uint length = (uint)data.Length; - if (MaxLength.HasValue) - length = MaxLength.Value; - if (MaxBytes.HasValue) - length = MaxBytes.Value; - - if (data.Any(c => c == 0)) - length = (uint)data.TakeWhile(c => c != 0).Count() + 1; - + byte[] dataBytes = data; + int charLength = 0; + int byteLength = 0; + int takenBytesCount = 0; if (RestrictToASCII == true) - str = Encoding.ASCII.GetString(data, 0, (int)length); + parse(Encoding.ASCII); else - str = Encoding.UTF8.GetString(data, 0, (int)length); + parse(Encoding.UTF8); + + void parse(Encoding encoder) + { + str = encoder.GetString(dataBytes); + takenBytesCount = dataBytes.Length; + + string[] strings = str.Split((char)0); + if (strings.Where(s => string.IsNullOrEmpty(s)).Count() > 1) + issueList.Add(new DataTreeIssue("More then one Null-Delimiter")); + if (strings.Skip(1).Any(s => !string.IsNullOrEmpty(s))) + issueList.Add(new DataTreeIssue("Trailing Characters")); + + str = strings.First(); + byteLength = encoder.GetBytes(str).Length; + takenBytesCount = byteLength; + charLength = str.Length; + bool repeatParse = false; + if (MaxLength.HasValue && MaxLength < charLength) + { + charLength = (int)MaxLength.Value; + repeatParse = true; + } + if (MaxBytes.HasValue && MaxBytes < byteLength) + { + byteLength = (int)MaxBytes.Value; + repeatParse = true; + } + if (repeatParse) + { + dataBytes = dataBytes.Take(byteLength).ToArray(); + str = encoder.GetString(dataBytes); + takenBytesCount = dataBytes.Length; + } + + } - if (MaxLength.HasValue && str.Length > MaxLength) + if (MaxLength.HasValue && str.Length > MaxLength.Value) issueList.Add(new DataTreeIssue($"String length exceeds {nameof(MaxLength)}, the Data has {str.Length}, but {nameof(MaxLength)} is {MaxLength}")); - if (MinLength.HasValue && str.Length < MinLength) + if (MinLength.HasValue && str.Length < MinLength.Value) issueList.Add(new DataTreeIssue($"String length falls shorts of {nameof(MinLength)}, the Data has {str.Length}, but {nameof(MinLength)} is {MinLength}")); - data = data.Skip((int)length).ToArray(); + data = data.Skip(takenBytesCount).ToArray(); return new DataTree(this.Name, 0, str, issueList.Count != 0 ? issueList.ToArray() : null); } } diff --git a/RDMSharpTests/Metadata/JSON/TestStringType.cs b/RDMSharpTests/Metadata/JSON/TestStringType.cs index 734a5c1..642ce18 100644 --- a/RDMSharpTests/Metadata/JSON/TestStringType.cs +++ b/RDMSharpTests/Metadata/JSON/TestStringType.cs @@ -1,3 +1,4 @@ +using RDMSharp.Metadata; using RDMSharp.Metadata.JSON.OneOfTypes; using RDMSharp.RDM; @@ -8,7 +9,7 @@ public class TestStringType [Test] public void TestMany() { - var stringType = new StringType("NAME", "DISPLAY_NAME", "NOTES", null, "string", null,null,null,null,null,null,null); + var stringType = new StringType("NAME", "DISPLAY_NAME", "NOTES", null, "string", null, null, null, null, null, null, null); Assert.That(stringType.MinLength, Is.Null); Assert.That(stringType.MaxLength, Is.Null); @@ -41,5 +42,138 @@ public void TestMany() Assert.Throws(typeof(ArgumentException), () => stringType = new StringType("NAME", "DISPLAY_NAME", "NOTES", null, "sting", null, null, null, null, null, null, null)); } + [Test] + public void TestParseFixedLengthASCII() + { + var stringType = new StringType("NAME", "DISPLAY_NAME", "NOTES", null, null, 5, true); + string str = "qwert"; + DataTree dataTree = new DataTree("NAME", 0, str); + byte[] data = stringType.ParsePayloadToData(dataTree); + Assert.That(data, Is.EqualTo(new byte[] { 113, 119, 101, 114, 116 })); + DataTree reverseDataTree = stringType.ParseDataToPayload(ref data); + Assert.Multiple(() => + { + Assert.That(data, Has.Length.Zero); + Assert.That(reverseDataTree, Is.EqualTo(dataTree)); + }); + } + [Test] + public void TestParseFixedLengthInBytesUTF8() + { + var stringType = new StringType("NAME", "DISPLAY_NAME", "NOTES", null, "string", null, null, null, null, minBytes: 8, maxBytes: 8, null); + string str = "ÄÜÖß"; + DataTree dataTree = new DataTree("NAME", 0, str); + byte[] data = stringType.ParsePayloadToData(dataTree); + Assert.That(data, Is.EqualTo(new byte[] { 195, 132, 195, 156, 195, 150, 195, 159 })); + DataTree reverseDataTree = stringType.ParseDataToPayload(ref data); + Assert.Multiple(() => + { + Assert.That(data, Has.Length.Zero); + Assert.That(reverseDataTree, Is.EqualTo(dataTree)); + }); + } + [Test] + public void TestParseRangedLengthUTF8() + { + var stringType = new StringType("NAME", "DISPLAY_NAME", "NOTES", null, "string", null, null, 4, 6, null, null, null); + string str = "ÄÜÖß"; + DataTree dataTree = new DataTree("NAME", 0, str); + byte[] data = stringType.ParsePayloadToData(dataTree); + Assert.That(data, Is.EqualTo(new byte[] { 195, 132, 195, 156, 195, 150, 195, 159 })); + DataTree reverseDataTree = stringType.ParseDataToPayload(ref data); + Assert.Multiple(() => + { + Assert.That(data, Has.Length.Zero); + Assert.That(reverseDataTree, Is.EqualTo(dataTree)); + }); + } + [Test] + public void TestParseRangedLengthUTF8Mixed() + { + var stringType = new StringType("NAME", "DISPLAY_NAME", "NOTES", null, "string", null, null, 4, 6, 4, 8, null); + string str = "ÄUÖS"; + DataTree dataTree = new DataTree("NAME", 0, str); + byte[] data = stringType.ParsePayloadToData(dataTree); + Assert.That(data, Is.EqualTo(new byte[] { 195, 132, 85, 195, 150, 83 })); + DataTree reverseDataTree = stringType.ParseDataToPayload(ref data); + Assert.Multiple(() => + { + Assert.That(data, Has.Length.Zero); + Assert.That(reverseDataTree, Is.EqualTo(dataTree)); + }); + } + + [Test] + public void TestParseExceptions() + { + var stringType = new StringType("NAME", "DISPLAY_NAME", "NOTES", null, "string", null, null, 3, 6, 5, 8, null); + string str = "12"; + DataTree dataTree = new DataTree("NAME FAIL", 0, str); + Assert.Throws(typeof(ArithmeticException), () => stringType.ParsePayloadToData(dataTree)); + dataTree = new DataTree("NAME", 0, str); + Assert.Throws(typeof(ArithmeticException), () => stringType.ParsePayloadToData(dataTree)); + str = "1234"; + dataTree = new DataTree("NAME", 0, str); + Assert.Throws(typeof(ArithmeticException), () => stringType.ParsePayloadToData(dataTree)); + str = "1234567"; + dataTree = new DataTree("NAME", 0, str); + Assert.Throws(typeof(ArithmeticException), () => stringType.ParsePayloadToData(dataTree)); + + stringType = new StringType("NAME", "DISPLAY_NAME", "NOTES", null, "string", null, null, null, null, 5, 8, null); + + str = "ÄÖÜ4567"; + dataTree = new DataTree("NAME", 0, str); + Assert.Throws(typeof(ArithmeticException), () => stringType.ParsePayloadToData(dataTree)); + str = "ÄÖÜÜÖÄ"; + dataTree = new DataTree("NAME", 0, str); + Assert.Throws(typeof(ArithmeticException), () => stringType.ParsePayloadToData(dataTree)); + str = null; + dataTree = new DataTree("NAME", 0, str); + Assert.Throws(typeof(ArithmeticException), () => stringType.ParsePayloadToData(dataTree)); + } + + [Test] + public void TestParseBadFormatedData1() + { + var stringType = new StringType("NAME", "DISPLAY_NAME", "NOTES", null, "string", null, null, null, null, 2, 8, null); + byte[] data = new byte[] { 195, 132, 0, 0, 0, 0 }; + var dataTree = stringType.ParseDataToPayload(ref data); + Assert.That(dataTree.Value, Is.EqualTo("Ä")); + Assert.That(dataTree.Issues, Has.Length.EqualTo(1)); + + data = new byte[] { 195, 132, 0, 0, 119, 0 }; + dataTree = stringType.ParseDataToPayload(ref data); + Assert.That(dataTree.Value, Is.EqualTo("Ä")); + Assert.That(dataTree.Issues, Has.Length.EqualTo(2)); + + data = new byte[] { 0, 0, 0, 0, 0, 0 }; + dataTree = stringType.ParseDataToPayload(ref data); + Assert.That(dataTree.Value, Is.EqualTo(string.Empty)); + Assert.That(dataTree.Issues, Has.Length.EqualTo(1)); + + data = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + dataTree = stringType.ParseDataToPayload(ref data); + Assert.That(dataTree.Value, Is.EqualTo(string.Empty)); + Assert.That(dataTree.Issues, Has.Length.EqualTo(2)); + + data = new byte[] { }; + dataTree = stringType.ParseDataToPayload(ref data); + Assert.That(dataTree.Value, Is.EqualTo(string.Empty)); + Assert.That(dataTree.Issues, Has.Length.EqualTo(1)); + } + [Test] + public void TestParseBadFormatedData2() + { + var stringType = new StringType("NAME", "DISPLAY_NAME", "NOTES", null, "string", null, null, 2, 4, null, null, null); + byte[] data = new byte[] { 195, 132, 0, 0, 0, 0 }; + var dataTree = stringType.ParseDataToPayload(ref data); + Assert.That(dataTree.Value, Is.EqualTo("Ä")); + Assert.That(dataTree.Issues, Has.Length.EqualTo(2)); + + data = new byte[] { 195, 132, 119, 119, 119, 119, 0 }; + dataTree = stringType.ParseDataToPayload(ref data); + Assert.That(dataTree.Value, Is.EqualTo("Äwwww")); + Assert.That(dataTree.Issues, Has.Length.EqualTo(1)); + } } } \ No newline at end of file