diff --git a/src/indice.Edi/EdiPath.cs b/src/indice.Edi/EdiPath.cs index 7167469..3696361 100644 --- a/src/indice.Edi/EdiPath.cs +++ b/src/indice.Edi/EdiPath.cs @@ -13,7 +13,7 @@ namespace indice.Edi public struct EdiPath : IComparable, IEquatable { - private const string PARSE_PATTERN = @"^([A-Z]{1}[A-Z0-9]{1,3}|\*)?([\[/]{1}(\d+?|\*)\]?)?([\[\/]{1}(\d+?|\*)\]?)?$"; // supports both "STX/2/1 and STX[2][1]" + private const string PARSE_PATTERN = @"^([A-Z]{1}[A-Z0-9]{1,3}|\*)?([\[\/]{1}([\d\.\*]+)\]?)?([\[\/]{1}([\d\.\*]+)\]?)?$"; // supports both "STX/2/1 and STX[2][1]" private readonly EdiPathFragment _SegmentPart; private readonly EdiPathFragment _ElementPart; diff --git a/src/indice.Edi/EdiPathFragment.cs b/src/indice.Edi/EdiPathFragment.cs index cd45a1a..1910bff 100644 --- a/src/indice.Edi/EdiPathFragment.cs +++ b/src/indice.Edi/EdiPathFragment.cs @@ -8,6 +8,7 @@ namespace indice.Edi /// public struct EdiPathFragment : IComparable, IEquatable { + private static Regex _RangePattern = new Regex(@"^(\d+|\*)\.\.(\d+|\*)$"); private readonly string _Value; /// @@ -42,6 +43,46 @@ public int Index { } } + /// + /// Converts the value to a zero based index. + /// + public int Min { + get { + if (!IsRange) return Index; + if ("*..*".Equals(Value)) + throw new InvalidCastException(string.Format("Cannot convert the fragment value \"{0}\" to range. Use * instead", Value)); + var min = _RangePattern.Match(Value).Groups[1].Value; + if ("*".Equals(min)) { + return 0; + } + var index = 0; + if (int.TryParse(min, out index)) { + return index; + } + throw new InvalidCastException(string.Format("Cannot convert the fragment value \"{0}\" to range. Minimum must be the * or a positive integer", Value)); + } + } + + /// + /// Converts the value to a zero based index. + /// + public int Max { + get { + if (!IsRange) return Index; + if ("*..*".Equals(Value)) + throw new InvalidCastException(string.Format("Cannot convert the fragment value \"{0}\" to range. Use * instead", Value)); + var max = _RangePattern.Match(Value).Groups[2].Value; + if ("*".Equals(max)) { + return int.MaxValue; + } + var index = 0; + if (int.TryParse(max, out index)) { + return index; + } + throw new InvalidCastException(string.Format("Cannot convert the fragment value \"{0}\" to range. Maximum must be the * or a positive integer", Value)); + } + } + /// /// If the is true then the current path fragment can hold any type of value. /// This means any type of name in case of the segment name fragment OR any positive for the element and component indices. @@ -52,6 +93,16 @@ public bool IsWildcard { } } + /// + /// If the is true then the current path fragment can hold a range of possible indexes. + /// This means any type of name in case of the segment name fragment OR any positive for the element and component indices. + /// + public bool IsRange { + get { + return Value?.Contains("..") == true; + } + } + /// /// Gets a value indicating whether the current has a value. /// @@ -104,7 +155,15 @@ public override bool Equals(object obj) { /// The object to check equality with /// public bool Equals(EdiPathFragment other) { - return IsWildcard || other.IsWildcard || (HasIndex && Index.Equals(other.Index)) || Value.Equals(other.Value); + bool eq = IsWildcard || other.IsWildcard || (HasIndex && Index.Equals(other.Index)) || Value.Equals(other.Value); + + if (!eq && (IsRange || other.HasIndex)) { + return Min <= other.Index && Max >= other.Index; + } + else if (!eq && (HasIndex || other.IsRange)) { + return other.Min <= Index && other.Max >= Index; + } + return eq; } /// diff --git a/test/indice.Edi.Tests/EdiTextReaderTests.cs b/test/indice.Edi.Tests/EdiTextReaderTests.cs index 9d02821..faaccaa 100644 --- a/test/indice.Edi.Tests/EdiTextReaderTests.cs +++ b/test/indice.Edi.Tests/EdiTextReaderTests.cs @@ -1007,5 +1007,20 @@ public void BadEscapeSequenseErrorCanbeSuppressed() { Assert.NotNull(interchange); Assert.Equal("XYZCOMPANY AVAILABILITY", interchange.AvailabilityRequest.Description[0].Text); } + + + [Fact, Trait(Traits.Tag, "EDIFact"), Trait(Traits.Issue, "#170")] + public void EdiFact_ElementList_PathAnnotation_ShouldWork_OnClass() { + var grammar = EdiGrammar.NewEdiFact(); + var interchange = default(EdiFact_Issue170_ElementList); + using (var stream = Helpers.GetResourceStream("edifact.Issue170.ElementList.edi")) { + interchange = new EdiSerializer().Deserialize(new StreamReader(stream), grammar); + } + Assert.Equal(3, interchange.Msg.InteractiveFreeTexts.Count); + Assert.Equal(3, interchange.Msg.InteractiveFreeTexts[2].Texts.Count); + Assert.Equal("This is the first", interchange.Msg.InteractiveFreeTexts[0].ToString()); + Assert.Equal("And this is the seccond", interchange.Msg.InteractiveFreeTexts[1].ToString()); + Assert.Equal("while this should come third", interchange.Msg.InteractiveFreeTexts[2].ToString()); + } } } diff --git a/test/indice.Edi.Tests/Models/EdiFact_Issue170_ElementList.cs b/test/indice.Edi.Tests/Models/EdiFact_Issue170_ElementList.cs new file mode 100644 index 0000000..86a6b89 --- /dev/null +++ b/test/indice.Edi.Tests/Models/EdiFact_Issue170_ElementList.cs @@ -0,0 +1,44 @@ +using System; +using System.Collections.Generic; +using System.Text; +using indice.Edi.Serialization; + +namespace indice.Edi.Tests.Models +{ + public class EdiFact_Issue170_ElementList + { + public Message Msg { get; set; } + + [EdiMessage] + public class Message + { + public List InteractiveFreeTexts { get; set; } + } + + [EdiSegment, EdiPath("IFT")] + public class InteractiveFreeText + { + [EdiValue("X(3)", Mandatory = true, Description = "C346-4451", Path = "IFT/0")] + public string Code { get; set; } + + [EdiValue("X(4)", Description = "C346-9980", Path = "IFT/0/1")] + public string CodeId { get; set; } + + [EdiValue("X(3)", Description = "C346-4405", Path = "IFT/0/2")] + public string Fare { get; set; } + + public List Texts { get; set; } + + public override string ToString() => string.Join(" ", Texts); + } + + [EdiElement, EdiPath("IFT/1..*")] // This take only index [1], but not from [1..*]] + public class FreeText + { + [EdiValue("X(70)", Path = "IFT/*/0")] + public string Text { get; set; } + + public override string ToString() => Text; + } + } +} diff --git a/test/indice.Edi.Tests/Samples/edifact.Issue170.ElementList.edi b/test/indice.Edi.Tests/Samples/edifact.Issue170.ElementList.edi new file mode 100644 index 0000000..2fcae32 --- /dev/null +++ b/test/indice.Edi.Tests/Samples/edifact.Issue170.ElementList.edi @@ -0,0 +1,7 @@ +UNB+XXXX' +UNH+000001' +IFT+1:1+This+is+the first' +IFT+1:2+And+this is+the seccond' +IFT+1:3+while+this+should come third' +UNT+3' +UNZ+1' \ No newline at end of file