From 435fc85a4b11f3c587f426c1c5571fadc5924259 Mon Sep 17 00:00:00 2001 From: Ian Gralinski Date: Wed, 10 Nov 2021 11:06:51 +1100 Subject: [PATCH 01/13] Issue #232 (HL7 2.5.1: Failure to parse ORDER group repetitions in OML_O33) * Added unit test that exposes the failure to parse ORDER group repetitions. * Added a ParserConfiguration class which holds the "non-greedy mode" setting. * Updated MessageIterator to handle non-greedy parsing when using PipeParser. * Updated PipeParser to hold ParserConfiguration. --- src/NHapi.Base/Parser/MessageIterator.cs | 41 ++++++++++++++++- src/NHapi.Base/Parser/ParserConfiguration.cs | 46 +++++++++++++++++++ src/NHapi.Base/Parser/PipeParser.cs | 11 +++-- .../NHapi.NUnit/Parser/PipeParserV251Tests.cs | 33 +++++++++++++ 4 files changed, 127 insertions(+), 4 deletions(-) create mode 100644 src/NHapi.Base/Parser/ParserConfiguration.cs diff --git a/src/NHapi.Base/Parser/MessageIterator.cs b/src/NHapi.Base/Parser/MessageIterator.cs index 76d3ae247..c8e6b6700 100644 --- a/src/NHapi.Base/Parser/MessageIterator.cs +++ b/src/NHapi.Base/Parser/MessageIterator.cs @@ -29,6 +29,7 @@ public class MessageIterator : IEnumerator private readonly IMessage message; private readonly bool handleUnexpectedSegments; + private readonly ParserBase parser; private string direction; private bool nextIsSet; @@ -40,12 +41,18 @@ static MessageIterator() } /// Creates a new instance of MessageIterator. - public MessageIterator(IMessage start, IStructureDefinition startDefinition, string direction, bool handleUnexpectedSegments) + public MessageIterator( + IMessage start, + IStructureDefinition startDefinition, + string direction, + bool handleUnexpectedSegments, + ParserBase parser) { this.message = start; this.direction = direction; this.handleUnexpectedSegments = handleUnexpectedSegments; this.currentDefinitionPath.Add(new Position(startDefinition, -1)); + this.parser = parser; } ///

Returns the next node in the message. Sometimes the next node is @@ -117,6 +124,18 @@ public virtual bool MoveNext() var structureDefinition = currentPosition.StructureDefinition; + if (parser is PipeParser pipeParser && pipeParser.ParserConfiguration.NonGreedyMode) + { + var nonGreedyPosition = CouldBeNonGreedy(); + if (nonGreedyPosition != null) + { + while (!Equals(GetCurrentPosition().StructureDefinition, nonGreedyPosition)) + { + currentDefinitionPath.Remove(currentDefinitionPath.Last()); + } + } + } + if (structureDefinition.IsSegment && structureDefinition.Name.StartsWith(direction, StringComparison.Ordinal) && (structureDefinition.IsRepeating || currentPosition.Repetition == -1)) { nextIsSet = true; @@ -182,6 +201,26 @@ public virtual void Reset() { } + private IStructureDefinition CouldBeNonGreedy() + { + for (var i = currentDefinitionPath.Count - 1; i >= 1; i--) + { + var position = currentDefinitionPath[i]; + var currentPosition = position.StructureDefinition; + + if (currentPosition.Position > 0) + { + var parent = currentPosition.Parent; + if (parent.IsRepeating && parent.GetAllPossibleFirstChildren().Contains(direction)) + { + return parent; + } + } + } + + return null; + } + private Position GetCurrentPosition() { return currentDefinitionPath.Last(); diff --git a/src/NHapi.Base/Parser/ParserConfiguration.cs b/src/NHapi.Base/Parser/ParserConfiguration.cs new file mode 100644 index 000000000..4b10d3f26 --- /dev/null +++ b/src/NHapi.Base/Parser/ParserConfiguration.cs @@ -0,0 +1,46 @@ +namespace NHapi.Base.Parser +{ + public class ParserConfiguration + { + public ParserConfiguration() + { + NonGreedyMode = false; + } + + ///

+ /// If set to true (default is false), pipe parser will be put in non-greedy mode. This setting applies only to + /// Pipe Parsers and will have no effect on XML Parsers. + /// + /// In non-greedy mode, if the message structure being parsed has an ambiguous choice of where to put a segment + /// because there is a segment matching the current segment name in both a later position in the message, and + /// in an earlier position as a part of a repeating group, the earlier position will be chosen. + /// + /// This is perhaps best explained with an example. Consider the following structure: + /// MSH + /// GROUP_1 (start) + /// { + /// AAA + /// BBB + /// GROUP_2 (start) + /// { + /// AAA + /// } + /// GROUP_2 (end) + /// } + /// GROUP_1 (end) + /// + /// For the above example, consider a message containing the following segments: + /// MSH + /// AAA + /// BBB + /// AAA + /// + /// In this example, when the second AAA segment is encountered, there are two possible choices. It would be + /// placed in GROUP_2, or it could be placed in a second repetition of GROUP_1. By default it will be placed in + /// GROUP_2, but in non-greedy mode it will be put in a new repetition of GROUP_1. + /// + /// This mode is useful for example when parsing OML^O21 messages containing multiple orders. + /// + public bool NonGreedyMode { get; set; } + } +} diff --git a/src/NHapi.Base/Parser/PipeParser.cs b/src/NHapi.Base/Parser/PipeParser.cs index 8863ece7a..59856f23e 100644 --- a/src/NHapi.Base/Parser/PipeParser.cs +++ b/src/NHapi.Base/Parser/PipeParser.cs @@ -59,21 +59,26 @@ static PipeParser() /// /// Creates a new PipeParser. /// - public PipeParser() + public PipeParser(ParserConfiguration parserConfiguration = null) { + ParserConfiguration = parserConfiguration ?? new ParserConfiguration(); } /// /// Creates a new PipeParser. /// - public PipeParser(IModelClassFactory factory) + public PipeParser(IModelClassFactory factory, ParserConfiguration parserConfiguration = null) : base(factory) { + ParserConfiguration = parserConfiguration ?? new ParserConfiguration(); } /// public override string DefaultEncoding => "VB"; + public ParserConfiguration ParserConfiguration { get; } + + /// /// Splits the given composite string into an array of components using /// the given delimiter. @@ -405,7 +410,7 @@ public virtual void Parse(ISegment destination, string segment, EncodingCharacte public override void Parse(IMessage message, string @string) { var structureDefinition = GetStructureDefinition(message); - var messageIterator = new MessageIterator(message, structureDefinition, "MSH", true); + var messageIterator = new MessageIterator(message, structureDefinition, "MSH", true, this); var segments = Split(@string, SegmentDelimiter); diff --git a/tests/NHapi.NUnit/Parser/PipeParserV251Tests.cs b/tests/NHapi.NUnit/Parser/PipeParserV251Tests.cs index f98e65cef..d832c5426 100644 --- a/tests/NHapi.NUnit/Parser/PipeParserV251Tests.cs +++ b/tests/NHapi.NUnit/Parser/PipeParserV251Tests.cs @@ -174,5 +174,38 @@ public void TestObx5DataTypeIsSetFromObx2_AndAllDataTypesAreConstructable(Type e typeof(XPN), typeof(XTN), }; + + /// + /// https://github.com/nHapiNET/nHapi/issues/232. + /// + [Test] + public void ParseORDERGroupRepetitionsIn_OML_O33() + { + var twoTestOrderMessage = + @"MSH|^~\&|||||20210921154451+1000||OML^O33^OML_O33|20210921154451|P|2.5.1|||NE|AL||UNICODE UTF-8|||LAB-28^IHE" + + '\r' + + @"SPM|1|||SER^Serum^HL70487|||||||P^Patient^HL70369" + '\r' + + @"SAC|||Found_TT1_TT2_SER_098" + '\r' + + @"ORC|NW||||||||20210921154451" + '\r' + + @"OBR|1|AWOS_ID_Found_TT1_TT2_SER_098_0||TT1^Test Type 1^99000" + '\r' + + @"ORC|NW||||||||20210921154451" + '\r' + + @"OBR|2|AWOS_ID_Found_TT1_TT2_SER_098_1||TT2^Test Type 2^99000"; + + var parser = new PipeParser(new ParserConfiguration { NonGreedyMode = true }); + var oml = parser.Parse(twoTestOrderMessage) as OML_O33; + + Assert.NotNull(oml); + + var specimen = oml.SPECIMENs.ElementAt(0); + Assert.AreEqual(2, specimen.ORDERRepetitionsUsed); + + var firstOrder = specimen.ORDERs.ElementAt(0); + var firstObr = firstOrder.OBSERVATION_REQUEST.OBR.UniversalServiceIdentifier; + Assert.AreEqual("TT1", firstObr.Identifier.Value); + + var secondOrder = specimen.ORDERs.ElementAt(1); + var secondObr = secondOrder.OBSERVATION_REQUEST.OBR.UniversalServiceIdentifier; + Assert.AreEqual("TT2", secondObr.Identifier.Value); + } } } \ No newline at end of file From aff4296ea07d63dda82a3134861040b05844f13b Mon Sep 17 00:00:00 2001 From: IanGralinski-Invetech <91176557+IanGralinski-Invetech@users.noreply.github.com> Date: Wed, 10 Nov 2021 11:32:37 +1100 Subject: [PATCH 02/13] Fix static analysis issues (removed constructors with optional parameters). --- src/NHapi.Base/Parser/PipeParser.cs | 27 +++++++++++++++++++++++---- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/src/NHapi.Base/Parser/PipeParser.cs b/src/NHapi.Base/Parser/PipeParser.cs index 59856f23e..6df02a3ae 100644 --- a/src/NHapi.Base/Parser/PipeParser.cs +++ b/src/NHapi.Base/Parser/PipeParser.cs @@ -59,18 +59,37 @@ static PipeParser() /// /// Creates a new PipeParser. /// - public PipeParser(ParserConfiguration parserConfiguration = null) + public PipeParser() { - ParserConfiguration = parserConfiguration ?? new ParserConfiguration(); + ParserConfiguration = new ParserConfiguration(); } /// /// Creates a new PipeParser. + /// Contains configuration that will be applied when parsing. /// - public PipeParser(IModelClassFactory factory, ParserConfiguration parserConfiguration = null) + public PipeParser(ParserConfiguration parserConfiguration) + { + ParserConfiguration = parserConfiguration; + } + + /// + /// Creates a new PipeParser. + /// + public PipeParser(IModelClassFactory factory) + : base(factory) + { + ParserConfiguration = new ParserConfiguration(); + } + + /// + /// Creates a new PipeParser. + /// Contains configuration that will be applied when parsing. + /// + public PipeParser(IModelClassFactory factory, ParserConfiguration parserConfiguration) : base(factory) { - ParserConfiguration = parserConfiguration ?? new ParserConfiguration(); + ParserConfiguration = parserConfiguration; } /// From b07ee0b93209d53eec30295a1fa1290c49ae905e Mon Sep 17 00:00:00 2001 From: IanGralinski-Invetech <91176557+IanGralinski-Invetech@users.noreply.github.com> Date: Thu, 11 Nov 2021 12:11:33 +1100 Subject: [PATCH 03/13] * Changed Parsers so that ParserConfiguration is passed in when calling Parse instead of being provided as a constructor parameter. * Updated MessageIterator to take ParserConfiguration instead of ParserBase in its constructor. * Other minor updates from review feedback (documentation, naming, etc.) Issue #232 --- src/NHapi.Base/Parser/DefaultXMLParser.cs | 2 +- src/NHapi.Base/Parser/LegacyPipeParser.cs | 4 +- src/NHapi.Base/Parser/MessageIterator.cs | 20 ++--- src/NHapi.Base/Parser/ParserBase.cs | 16 ++-- src/NHapi.Base/Parser/ParserConfiguration.cs | 77 +++++++++++-------- src/NHapi.Base/Parser/PipeParser.cs | 34 ++------ src/NHapi.Base/Parser/XMLParser.cs | 4 +- .../NHapi.NUnit/Parser/PipeParserV251Tests.cs | 4 +- 8 files changed, 80 insertions(+), 81 deletions(-) diff --git a/src/NHapi.Base/Parser/DefaultXMLParser.cs b/src/NHapi.Base/Parser/DefaultXMLParser.cs index 7c9367265..b182375e2 100644 --- a/src/NHapi.Base/Parser/DefaultXMLParser.cs +++ b/src/NHapi.Base/Parser/DefaultXMLParser.cs @@ -151,7 +151,7 @@ public override IMessage ParseDocument(XmlDocument xmlMessage, string version) } /// - public override void Parse(IMessage message, string @string) + public override void Parse(IMessage message, string @string, ParserConfiguration parserConfiguration = default) { try { diff --git a/src/NHapi.Base/Parser/LegacyPipeParser.cs b/src/NHapi.Base/Parser/LegacyPipeParser.cs index 49bc7cb58..1ad405a84 100644 --- a/src/NHapi.Base/Parser/LegacyPipeParser.cs +++ b/src/NHapi.Base/Parser/LegacyPipeParser.cs @@ -257,7 +257,7 @@ public static string StripLeadingWhitespace(string in_Renamed) return out_Renamed.ToString(); } - public override void Parse(IMessage message, string @string) + public override void Parse(IMessage message, string @string, ParserConfiguration parserConfiguration = default) { var messageIter = new Util.MessageIterator(message, "MSH", true); FilterIterator.IPredicate segmentsOnly = new AnonymousClassPredicate(this); @@ -743,7 +743,7 @@ protected internal override string DoEncode(IMessage source) /// EncodingNotSupportedException if the message encoded. /// is not supported by this parser. /// - protected internal override IMessage DoParse(string message, string version) + protected internal override IMessage DoParse(string message, string version, ParserConfiguration parserConfiguration = default) { // try to instantiate a message object of the right class var structure = GetStructure(message); diff --git a/src/NHapi.Base/Parser/MessageIterator.cs b/src/NHapi.Base/Parser/MessageIterator.cs index c8e6b6700..762aca3c7 100644 --- a/src/NHapi.Base/Parser/MessageIterator.cs +++ b/src/NHapi.Base/Parser/MessageIterator.cs @@ -29,7 +29,7 @@ public class MessageIterator : IEnumerator private readonly IMessage message; private readonly bool handleUnexpectedSegments; - private readonly ParserBase parser; + private readonly ParserConfiguration parserConfig; private string direction; private bool nextIsSet; @@ -46,13 +46,13 @@ public MessageIterator( IStructureDefinition startDefinition, string direction, bool handleUnexpectedSegments, - ParserBase parser) + ParserConfiguration parserConfig) { this.message = start; this.direction = direction; this.handleUnexpectedSegments = handleUnexpectedSegments; this.currentDefinitionPath.Add(new Position(startDefinition, -1)); - this.parser = parser; + this.parserConfig = parserConfig; } ///

Returns the next node in the message. Sometimes the next node is @@ -73,9 +73,9 @@ public MessageIterator( ///

  • If at the end of a group: If name of group or any of its "first /// descendants" matches direction, then next position means next rep of group. Otherwise /// if direction matches name of next sibling of the group, or any of its first - /// descendents, next position means next sibling of the group. Otherwise, next means a + /// descendants, next position means next sibling of the group. Otherwise, next means a /// new segment added to the group (with a name that matches "direction").
  • - ///
  • "First descendents" means first child, or first child of the first child, + ///
  • "First descendants" means first child, or first child of the first child, /// or first child of the first child of the first child, etc.
  • ///
    public virtual object Current @@ -124,12 +124,14 @@ public virtual bool MoveNext() var structureDefinition = currentPosition.StructureDefinition; - if (parser is PipeParser pipeParser && pipeParser.ParserConfiguration.NonGreedyMode) + if (parserConfig.NonGreedyMode) { - var nonGreedyPosition = CouldBeNonGreedy(); + var nonGreedyPosition = LocateNonGreedyStructure(); if (nonGreedyPosition != null) { - while (!Equals(GetCurrentPosition().StructureDefinition, nonGreedyPosition)) + Log.Info($"Found non greedy parsing choice, moving to {nonGreedyPosition.Name}"); + + while (GetCurrentPosition().StructureDefinition != nonGreedyPosition) { currentDefinitionPath.Remove(currentDefinitionPath.Last()); } @@ -201,7 +203,7 @@ public virtual void Reset() { } - private IStructureDefinition CouldBeNonGreedy() + private IStructureDefinition LocateNonGreedyStructure() { for (var i = currentDefinitionPath.Count - 1; i >= 1; i--) { diff --git a/src/NHapi.Base/Parser/ParserBase.cs b/src/NHapi.Base/Parser/ParserBase.cs index 6adebc52c..558ff86d2 100644 --- a/src/NHapi.Base/Parser/ParserBase.cs +++ b/src/NHapi.Base/Parser/ParserBase.cs @@ -189,10 +189,11 @@ public static string GetMessageStructureForEvent(string name, string version) /// Parses a message string and returns the corresponding Message object. ///
    /// A string that contains an HL7 message. + /// Contains configuration that will be applied when parsing. /// A object parsed from the given string. /// If the message is not correctly formatted. /// If the message encoded is not supported by this parser. - public virtual IMessage Parse(string message) + public virtual IMessage Parse(string message, ParserConfiguration parserConfiguration = default) { var encoding = GetEncoding(message); @@ -225,7 +226,7 @@ public virtual IMessage Parse(string message) ErrorCode.UNSUPPORTED_VERSION_ID); } - return Parse(message, version); + return Parse(message, version, parserConfiguration); } /// @@ -233,8 +234,9 @@ public virtual IMessage Parse(string message) /// /// A string that contains an HL7 message. /// the name of the HL7 version to which the message belongs (eg "2.5"). + /// Contains configuration that will be applied when parsing. /// - public virtual IMessage Parse(string message, string version) + public virtual IMessage Parse(string message, string version, ParserConfiguration parserConfiguration = default) { var encoding = GetEncoding(message); if (!SupportsEncoding(encoding)) @@ -244,7 +246,7 @@ public virtual IMessage Parse(string message, string version) } messageValidator.Validate(message, encoding.Equals("XML"), version); - var result = DoParse(message, version); + var result = DoParse(message, version, parserConfiguration); messageValidator.Validate(result); return result; @@ -255,8 +257,9 @@ public virtual IMessage Parse(string message, string version) ///
    /// The message to encode. /// The string to parse. + /// Contains configuration that will be applied when parsing. /// If there is a problem encoding. - public abstract void Parse(IMessage message, string @string); + public abstract void Parse(IMessage message, string @string, ParserConfiguration parserConfiguration = default); /// /// Formats a object into an HL7 message string using the given encoding. @@ -401,10 +404,11 @@ public bool SupportsEncoding(string encoding) /// /// a String that contains an HL7 message. /// the name of the HL7 version to which the message belongs (eg "2.5"). + /// Contains configuration that will be applied when parsing. /// A HAPI Message object parsed from the given String. /// Thrown if the data fields in the message do not permit encoding (e.g. required fields are null). /// Thrown if the requested encoding is not supported by this parser. - protected internal abstract IMessage DoParse(string message, string version); + protected internal abstract IMessage DoParse(string message, string version, ParserConfiguration parserConfiguration = default); /// /// Note that the validation context of the resulting message is set to this parsers validation diff --git a/src/NHapi.Base/Parser/ParserConfiguration.cs b/src/NHapi.Base/Parser/ParserConfiguration.cs index 4b10d3f26..52d503d6e 100644 --- a/src/NHapi.Base/Parser/ParserConfiguration.cs +++ b/src/NHapi.Base/Parser/ParserConfiguration.cs @@ -8,39 +8,54 @@ public ParserConfiguration() } /// - /// If set to true (default is false), pipe parser will be put in non-greedy mode. This setting applies only to - /// Pipe Parsers and will have no effect on XML Parsers. - /// - /// In non-greedy mode, if the message structure being parsed has an ambiguous choice of where to put a segment - /// because there is a segment matching the current segment name in both a later position in the message, and - /// in an earlier position as a part of a repeating group, the earlier position will be chosen. - /// - /// This is perhaps best explained with an example. Consider the following structure: - /// MSH - /// GROUP_1 (start) - /// { - /// AAA - /// BBB - /// GROUP_2 (start) - /// { - /// AAA - /// } - /// GROUP_2 (end) - /// } - /// GROUP_1 (end) - /// - /// For the above example, consider a message containing the following segments: - /// MSH - /// AAA - /// BBB - /// AAA + /// If set to true (default is false), pipe parser will be put in non-greedy mode. This setting + /// applies only to and will have no effect on . + /// /// - /// In this example, when the second AAA segment is encountered, there are two possible choices. It would be - /// placed in GROUP_2, or it could be placed in a second repetition of GROUP_1. By default it will be placed in - /// GROUP_2, but in non-greedy mode it will be put in a new repetition of GROUP_1. + /// + /// + /// In non-greedy mode, if the message structure being parsed has an ambiguous choice of where to put a segment + /// because there is a segment matching the current segment name in both a later position in the message, and + /// in an earlier position as a part of a repeating group, the earlier position will be chosen. + /// + /// + /// This mode is useful for example when parsing OML^O21 messages containing multiple orders. + /// + /// /// - /// This mode is useful for example when parsing OML^O21 messages containing multiple orders. - /// + /// + /// + /// This is perhaps best explained with an example. Consider the following structure: + /// + /// MSH + /// GROUP_1 (start) + /// { + /// AAA + /// BBB + /// GROUP_2 (start) + /// { + /// AAA + /// } + /// GROUP_2 (end) + /// } + /// GROUP_1 (end) + /// + /// + /// + /// For the above example, consider a message containing the following segments: + /// + /// MSH + /// AAA + /// BBB + /// AAA + /// + /// + /// + /// In this example, when the second AAA segment is encountered, there are two possible choices. It would be + /// placed in GROUP_2, or it could be placed in a second repetition of GROUP_1. By default it will be placed in + /// GROUP_2, but in non-greedy mode it will be put in a new repetition of GROUP_1. + /// + /// public bool NonGreedyMode { get; set; } } } diff --git a/src/NHapi.Base/Parser/PipeParser.cs b/src/NHapi.Base/Parser/PipeParser.cs index 6df02a3ae..6bf18190b 100644 --- a/src/NHapi.Base/Parser/PipeParser.cs +++ b/src/NHapi.Base/Parser/PipeParser.cs @@ -61,43 +61,20 @@ static PipeParser() /// public PipeParser() { - ParserConfiguration = new ParserConfiguration(); - } - - /// - /// Creates a new PipeParser. - /// Contains configuration that will be applied when parsing. - /// - public PipeParser(ParserConfiguration parserConfiguration) - { - ParserConfiguration = parserConfiguration; } /// /// Creates a new PipeParser. + /// Looks up classes for message model components. /// public PipeParser(IModelClassFactory factory) : base(factory) { - ParserConfiguration = new ParserConfiguration(); - } - - /// - /// Creates a new PipeParser. - /// Contains configuration that will be applied when parsing. - /// - public PipeParser(IModelClassFactory factory, ParserConfiguration parserConfiguration) - : base(factory) - { - ParserConfiguration = parserConfiguration; } /// public override string DefaultEncoding => "VB"; - public ParserConfiguration ParserConfiguration { get; } - - /// /// Splits the given composite string into an array of components using /// the given delimiter. @@ -426,10 +403,11 @@ public virtual void Parse(ISegment destination, string segment, EncodingCharacte } /// - public override void Parse(IMessage message, string @string) + public override void Parse(IMessage message, string @string, ParserConfiguration parserConfiguration = default) { var structureDefinition = GetStructureDefinition(message); - var messageIterator = new MessageIterator(message, structureDefinition, "MSH", true, this); + var parserConfig = parserConfiguration ?? new ParserConfiguration(); + var messageIterator = new MessageIterator(message, structureDefinition, "MSH", true, parserConfig); var segments = Split(@string, SegmentDelimiter); @@ -725,12 +703,12 @@ protected internal override string DoEncode(IMessage source) } /// - protected internal override IMessage DoParse(string message, string version) + protected internal override IMessage DoParse(string message, string version, ParserConfiguration parserConfiguration = default) { // try to instantiate a message object of the right class var structure = GetStructure(message); var m = InstantiateMessage(structure.Structure, version, structure.ExplicitlyDefined); - Parse(m, message); + Parse(m, message, parserConfiguration); return m; } diff --git a/src/NHapi.Base/Parser/XMLParser.cs b/src/NHapi.Base/Parser/XMLParser.cs index 776f2bfa2..a72d19c70 100644 --- a/src/NHapi.Base/Parser/XMLParser.cs +++ b/src/NHapi.Base/Parser/XMLParser.cs @@ -475,7 +475,7 @@ protected internal virtual string RemoveWhitespace(string s) /// XML Document object (using Xerces) from the given String, and calls the abstract /// method . /// - protected internal override IMessage DoParse(string message, string version) + protected internal override IMessage DoParse(string message, string version, ParserConfiguration parserConfiguration = default) { IMessage m = null; @@ -821,7 +821,7 @@ public override XmlDocument EncodeDocument(IMessage source) return null; } - public override void Parse(IMessage message, string @string) + public override void Parse(IMessage message, string @string, ParserConfiguration parserConfiguration = default) { } diff --git a/tests/NHapi.NUnit/Parser/PipeParserV251Tests.cs b/tests/NHapi.NUnit/Parser/PipeParserV251Tests.cs index d832c5426..db3765870 100644 --- a/tests/NHapi.NUnit/Parser/PipeParserV251Tests.cs +++ b/tests/NHapi.NUnit/Parser/PipeParserV251Tests.cs @@ -191,8 +191,8 @@ public void ParseORDERGroupRepetitionsIn_OML_O33() @"ORC|NW||||||||20210921154451" + '\r' + @"OBR|2|AWOS_ID_Found_TT1_TT2_SER_098_1||TT2^Test Type 2^99000"; - var parser = new PipeParser(new ParserConfiguration { NonGreedyMode = true }); - var oml = parser.Parse(twoTestOrderMessage) as OML_O33; + var parser = new PipeParser(); + var oml = parser.Parse(twoTestOrderMessage, new ParserConfiguration { NonGreedyMode = true }) as OML_O33; Assert.NotNull(oml); From e99123ff87dba5cc2caaea026fad8d6cee1706a7 Mon Sep 17 00:00:00 2001 From: IanGralinski-Invetech <91176557+IanGralinski-Invetech@users.noreply.github.com> Date: Thu, 11 Nov 2021 12:40:36 +1100 Subject: [PATCH 04/13] Changed default arguments to use overloading to address static analysis errors. Issue #232 --- src/NHapi.Base/Parser/DefaultXMLParser.cs | 2 +- src/NHapi.Base/Parser/LegacyPipeParser.cs | 6 ++-- src/NHapi.Base/Parser/ParserBase.cs | 42 ++++++++++++++++++++--- src/NHapi.Base/Parser/PipeParser.cs | 4 +-- src/NHapi.Base/Parser/XMLParser.cs | 4 +-- 5 files changed, 46 insertions(+), 12 deletions(-) diff --git a/src/NHapi.Base/Parser/DefaultXMLParser.cs b/src/NHapi.Base/Parser/DefaultXMLParser.cs index b182375e2..6c824d998 100644 --- a/src/NHapi.Base/Parser/DefaultXMLParser.cs +++ b/src/NHapi.Base/Parser/DefaultXMLParser.cs @@ -151,7 +151,7 @@ public override IMessage ParseDocument(XmlDocument xmlMessage, string version) } /// - public override void Parse(IMessage message, string @string, ParserConfiguration parserConfiguration = default) + public override void Parse(IMessage message, string @string, ParserConfiguration parserConfiguration) { try { diff --git a/src/NHapi.Base/Parser/LegacyPipeParser.cs b/src/NHapi.Base/Parser/LegacyPipeParser.cs index 1ad405a84..338951617 100644 --- a/src/NHapi.Base/Parser/LegacyPipeParser.cs +++ b/src/NHapi.Base/Parser/LegacyPipeParser.cs @@ -257,7 +257,7 @@ public static string StripLeadingWhitespace(string in_Renamed) return out_Renamed.ToString(); } - public override void Parse(IMessage message, string @string, ParserConfiguration parserConfiguration = default) + public override void Parse(IMessage message, string @string, ParserConfiguration parserConfiguration) { var messageIter = new Util.MessageIterator(message, "MSH", true); FilterIterator.IPredicate segmentsOnly = new AnonymousClassPredicate(this); @@ -743,13 +743,13 @@ protected internal override string DoEncode(IMessage source) /// EncodingNotSupportedException if the message encoded. /// is not supported by this parser. /// - protected internal override IMessage DoParse(string message, string version, ParserConfiguration parserConfiguration = default) + protected internal override IMessage DoParse(string message, string version, ParserConfiguration parserConfiguration) { // try to instantiate a message object of the right class var structure = GetStructure(message); var m = InstantiateMessage(structure.Structure, version, structure.ExplicitlyDefined); - Parse(m, message); + Parse(m, message, parserConfiguration); return m; } diff --git a/src/NHapi.Base/Parser/ParserBase.cs b/src/NHapi.Base/Parser/ParserBase.cs index 558ff86d2..3403721df 100644 --- a/src/NHapi.Base/Parser/ParserBase.cs +++ b/src/NHapi.Base/Parser/ParserBase.cs @@ -185,6 +185,18 @@ public static string GetMessageStructureForEvent(string name, string version) return structure; } + /// + /// Parses a message string and returns the corresponding Message object. + /// + /// A string that contains an HL7 message. + /// A object parsed from the given string. + /// If the message is not correctly formatted. + /// If the message encoded is not supported by this parser. + public virtual IMessage Parse(string message) + { + return Parse(message, parserConfiguration: null); + } + /// /// Parses a message string and returns the corresponding Message object. /// @@ -193,7 +205,7 @@ public static string GetMessageStructureForEvent(string name, string version) /// A object parsed from the given string. /// If the message is not correctly formatted. /// If the message encoded is not supported by this parser. - public virtual IMessage Parse(string message, ParserConfiguration parserConfiguration = default) + public virtual IMessage Parse(string message, ParserConfiguration parserConfiguration) { var encoding = GetEncoding(message); @@ -229,6 +241,17 @@ public virtual IMessage Parse(string message, ParserConfiguration parserConfigur return Parse(message, version, parserConfiguration); } + /// + /// Parse a message to a specific assembly. + /// + /// A string that contains an HL7 message. + /// the name of the HL7 version to which the message belongs (eg "2.5"). + /// + public virtual IMessage Parse(string message, string version) + { + return Parse(message, version, parserConfiguration: null); + } + /// /// Parse a message to a specific assembly. /// @@ -236,7 +259,7 @@ public virtual IMessage Parse(string message, ParserConfiguration parserConfigur /// the name of the HL7 version to which the message belongs (eg "2.5"). /// Contains configuration that will be applied when parsing. /// - public virtual IMessage Parse(string message, string version, ParserConfiguration parserConfiguration = default) + public virtual IMessage Parse(string message, string version, ParserConfiguration parserConfiguration) { var encoding = GetEncoding(message); if (!SupportsEncoding(encoding)) @@ -252,6 +275,17 @@ public virtual IMessage Parse(string message, string version, ParserConfiguratio return result; } + /// + /// Parses a particular message and returns the encoded structure. + /// + /// The message to encode. + /// The string to parse. + /// If there is a problem encoding. + public void Parse(IMessage message, string @string) + { + Parse(message, @string, parserConfiguration: null); + } + /// /// Parses a particular message and returns the encoded structure. /// @@ -259,7 +293,7 @@ public virtual IMessage Parse(string message, string version, ParserConfiguratio /// The string to parse. /// Contains configuration that will be applied when parsing. /// If there is a problem encoding. - public abstract void Parse(IMessage message, string @string, ParserConfiguration parserConfiguration = default); + public abstract void Parse(IMessage message, string @string, ParserConfiguration parserConfiguration); /// /// Formats a object into an HL7 message string using the given encoding. @@ -408,7 +442,7 @@ public bool SupportsEncoding(string encoding) /// A HAPI Message object parsed from the given String. /// Thrown if the data fields in the message do not permit encoding (e.g. required fields are null). /// Thrown if the requested encoding is not supported by this parser. - protected internal abstract IMessage DoParse(string message, string version, ParserConfiguration parserConfiguration = default); + protected internal abstract IMessage DoParse(string message, string version, ParserConfiguration parserConfiguration); /// /// Note that the validation context of the resulting message is set to this parsers validation diff --git a/src/NHapi.Base/Parser/PipeParser.cs b/src/NHapi.Base/Parser/PipeParser.cs index 6bf18190b..729cc5e35 100644 --- a/src/NHapi.Base/Parser/PipeParser.cs +++ b/src/NHapi.Base/Parser/PipeParser.cs @@ -403,7 +403,7 @@ public virtual void Parse(ISegment destination, string segment, EncodingCharacte } /// - public override void Parse(IMessage message, string @string, ParserConfiguration parserConfiguration = default) + public override void Parse(IMessage message, string @string, ParserConfiguration parserConfiguration) { var structureDefinition = GetStructureDefinition(message); var parserConfig = parserConfiguration ?? new ParserConfiguration(); @@ -703,7 +703,7 @@ protected internal override string DoEncode(IMessage source) } /// - protected internal override IMessage DoParse(string message, string version, ParserConfiguration parserConfiguration = default) + protected internal override IMessage DoParse(string message, string version, ParserConfiguration parserConfiguration) { // try to instantiate a message object of the right class var structure = GetStructure(message); diff --git a/src/NHapi.Base/Parser/XMLParser.cs b/src/NHapi.Base/Parser/XMLParser.cs index a72d19c70..b89713ca5 100644 --- a/src/NHapi.Base/Parser/XMLParser.cs +++ b/src/NHapi.Base/Parser/XMLParser.cs @@ -475,7 +475,7 @@ protected internal virtual string RemoveWhitespace(string s) /// XML Document object (using Xerces) from the given String, and calls the abstract /// method . /// - protected internal override IMessage DoParse(string message, string version, ParserConfiguration parserConfiguration = default) + protected internal override IMessage DoParse(string message, string version, ParserConfiguration parserConfiguration) { IMessage m = null; @@ -821,7 +821,7 @@ public override XmlDocument EncodeDocument(IMessage source) return null; } - public override void Parse(IMessage message, string @string, ParserConfiguration parserConfiguration = default) + public override void Parse(IMessage message, string @string, ParserConfiguration parserConfiguration) { } From a85d5a55002e9e1ebf14dc236d7251f7aa6a9313 Mon Sep 17 00:00:00 2001 From: IanGralinski-Invetech <91176557+IanGralinski-Invetech@users.noreply.github.com> Date: Thu, 11 Nov 2021 23:12:37 +1100 Subject: [PATCH 05/13] Added default ParserConfiguration as a static member of ParserBase. Replaced call sites where null was being passed in for ParserConfiguration with the default constructed one. Issue #232 --- src/NHapi.Base/Parser/ParserBase.cs | 15 +++++++++------ src/NHapi.Base/Parser/PipeParser.cs | 3 +-- tests/NHapi.NUnit/Parser/PipeParserV251Tests.cs | 17 ++++++++--------- 3 files changed, 18 insertions(+), 17 deletions(-) diff --git a/src/NHapi.Base/Parser/ParserBase.cs b/src/NHapi.Base/Parser/ParserBase.cs index 3403721df..f9368fe2e 100644 --- a/src/NHapi.Base/Parser/ParserBase.cs +++ b/src/NHapi.Base/Parser/ParserBase.cs @@ -45,6 +45,7 @@ namespace NHapi.Base.Parser public abstract class ParserBase { private static readonly IHapiLog Log; + private static readonly ParserConfiguration DefaultParserConfiguration = new ParserConfiguration(); private IValidationContext validationContext; private MessageValidator messageValidator; @@ -186,7 +187,8 @@ public static string GetMessageStructureForEvent(string name, string version) } /// - /// Parses a message string and returns the corresponding Message object. + /// Parses a message string and returns the corresponding Message object. Uses the default + /// . /// /// A string that contains an HL7 message. /// A object parsed from the given string. @@ -194,7 +196,7 @@ public static string GetMessageStructureForEvent(string name, string version) /// If the message encoded is not supported by this parser. public virtual IMessage Parse(string message) { - return Parse(message, parserConfiguration: null); + return Parse(message, DefaultParserConfiguration); } /// @@ -242,14 +244,14 @@ public virtual IMessage Parse(string message, ParserConfiguration parserConfigur } /// - /// Parse a message to a specific assembly. + /// Parse a message to a specific assembly. Uses the default . /// /// A string that contains an HL7 message. /// the name of the HL7 version to which the message belongs (eg "2.5"). /// public virtual IMessage Parse(string message, string version) { - return Parse(message, version, parserConfiguration: null); + return Parse(message, version, DefaultParserConfiguration); } /// @@ -276,14 +278,15 @@ public virtual IMessage Parse(string message, string version, ParserConfiguratio } /// - /// Parses a particular message and returns the encoded structure. + /// Parses a particular message and returns the encoded structure. Uses the default + /// . /// /// The message to encode. /// The string to parse. /// If there is a problem encoding. public void Parse(IMessage message, string @string) { - Parse(message, @string, parserConfiguration: null); + Parse(message, @string, DefaultParserConfiguration); } /// diff --git a/src/NHapi.Base/Parser/PipeParser.cs b/src/NHapi.Base/Parser/PipeParser.cs index 729cc5e35..a15af46c4 100644 --- a/src/NHapi.Base/Parser/PipeParser.cs +++ b/src/NHapi.Base/Parser/PipeParser.cs @@ -406,8 +406,7 @@ public virtual void Parse(ISegment destination, string segment, EncodingCharacte public override void Parse(IMessage message, string @string, ParserConfiguration parserConfiguration) { var structureDefinition = GetStructureDefinition(message); - var parserConfig = parserConfiguration ?? new ParserConfiguration(); - var messageIterator = new MessageIterator(message, structureDefinition, "MSH", true, parserConfig); + var messageIterator = new MessageIterator(message, structureDefinition, "MSH", true, parserConfiguration); var segments = Split(@string, SegmentDelimiter); diff --git a/tests/NHapi.NUnit/Parser/PipeParserV251Tests.cs b/tests/NHapi.NUnit/Parser/PipeParserV251Tests.cs index db3765870..a82c2dcff 100644 --- a/tests/NHapi.NUnit/Parser/PipeParserV251Tests.cs +++ b/tests/NHapi.NUnit/Parser/PipeParserV251Tests.cs @@ -181,15 +181,14 @@ public void TestObx5DataTypeIsSetFromObx2_AndAllDataTypesAreConstructable(Type e [Test] public void ParseORDERGroupRepetitionsIn_OML_O33() { - var twoTestOrderMessage = - @"MSH|^~\&|||||20210921154451+1000||OML^O33^OML_O33|20210921154451|P|2.5.1|||NE|AL||UNICODE UTF-8|||LAB-28^IHE" + - '\r' + - @"SPM|1|||SER^Serum^HL70487|||||||P^Patient^HL70369" + '\r' + - @"SAC|||Found_TT1_TT2_SER_098" + '\r' + - @"ORC|NW||||||||20210921154451" + '\r' + - @"OBR|1|AWOS_ID_Found_TT1_TT2_SER_098_0||TT1^Test Type 1^99000" + '\r' + - @"ORC|NW||||||||20210921154451" + '\r' + - @"OBR|2|AWOS_ID_Found_TT1_TT2_SER_098_1||TT2^Test Type 2^99000"; + const string twoTestOrderMessage = + "MSH|^~\\&|||||20210921154451+1000||OML^O33^OML_O33|20210921154451|P|2.5.1|||NE|AL||UNICODE UTF-8|||LAB-28^IHE\r" + + "SPM|1|||SER^Serum^HL70487|||||||P^Patient^HL70369\r" + + "SAC|||Found_TT1_TT2_SER_098\r" + + "ORC|NW||||||||20210921154451\r" + + "OBR|1|AWOS_ID_Found_TT1_TT2_SER_098_0||TT1^Test Type 1^99000\r" + + "ORC|NW||||||||20210921154451\r" + + "OBR|2|AWOS_ID_Found_TT1_TT2_SER_098_1||TT2^Test Type 2^99000"; var parser = new PipeParser(); var oml = parser.Parse(twoTestOrderMessage, new ParserConfiguration { NonGreedyMode = true }) as OML_O33; From 68ad177c0911bfe3d2d85d3c1fc9ef033aed5c35 Mon Sep 17 00:00:00 2001 From: IanGralinski-Invetech <91176557+IanGralinski-Invetech@users.noreply.github.com> Date: Thu, 11 Nov 2021 23:54:20 +1100 Subject: [PATCH 06/13] Updated PipeParser to throw an exception if the provided ParserConfiguration is null. Issue #232 --- src/NHapi.Base/Parser/ParserBase.cs | 1 + src/NHapi.Base/Parser/PipeParser.cs | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/src/NHapi.Base/Parser/ParserBase.cs b/src/NHapi.Base/Parser/ParserBase.cs index f9368fe2e..d646eb6ab 100644 --- a/src/NHapi.Base/Parser/ParserBase.cs +++ b/src/NHapi.Base/Parser/ParserBase.cs @@ -296,6 +296,7 @@ public void Parse(IMessage message, string @string) /// The string to parse. /// Contains configuration that will be applied when parsing. /// If there is a problem encoding. + /// If is null. public abstract void Parse(IMessage message, string @string, ParserConfiguration parserConfiguration); /// diff --git a/src/NHapi.Base/Parser/PipeParser.cs b/src/NHapi.Base/Parser/PipeParser.cs index a15af46c4..9d1c9e70b 100644 --- a/src/NHapi.Base/Parser/PipeParser.cs +++ b/src/NHapi.Base/Parser/PipeParser.cs @@ -405,6 +405,11 @@ public virtual void Parse(ISegment destination, string segment, EncodingCharacte /// public override void Parse(IMessage message, string @string, ParserConfiguration parserConfiguration) { + if (parserConfiguration is null) + { + throw new ArgumentNullException(nameof(parserConfiguration)); + } + var structureDefinition = GetStructureDefinition(message); var messageIterator = new MessageIterator(message, structureDefinition, "MSH", true, parserConfiguration); From aee3cfe34be72b34d526abf32da0656170dcf72d Mon Sep 17 00:00:00 2001 From: IanGralinski-Invetech <91176557+IanGralinski-Invetech@users.noreply.github.com> Date: Fri, 12 Nov 2021 11:28:17 +1100 Subject: [PATCH 07/13] Added unit test to check that exception is thrown when provided ParserConfiguration is null. Updated XML method documentation for remaining methods that can throw when the provided ParserConfiguration is null. Issue #232 --- src/NHapi.Base/Parser/ParserBase.cs | 3 +++ tests/NHapi.NUnit/Parser/PipeParserTests.cs | 18 ++++++++++++++++++ 2 files changed, 21 insertions(+) diff --git a/src/NHapi.Base/Parser/ParserBase.cs b/src/NHapi.Base/Parser/ParserBase.cs index d646eb6ab..dc37677fd 100644 --- a/src/NHapi.Base/Parser/ParserBase.cs +++ b/src/NHapi.Base/Parser/ParserBase.cs @@ -207,6 +207,7 @@ public virtual IMessage Parse(string message) /// A object parsed from the given string. /// If the message is not correctly formatted. /// If the message encoded is not supported by this parser. + /// If is null. public virtual IMessage Parse(string message, ParserConfiguration parserConfiguration) { var encoding = GetEncoding(message); @@ -261,6 +262,7 @@ public virtual IMessage Parse(string message, string version) /// the name of the HL7 version to which the message belongs (eg "2.5"). /// Contains configuration that will be applied when parsing. /// + /// If is null. public virtual IMessage Parse(string message, string version, ParserConfiguration parserConfiguration) { var encoding = GetEncoding(message); @@ -446,6 +448,7 @@ public bool SupportsEncoding(string encoding) /// A HAPI Message object parsed from the given String. /// Thrown if the data fields in the message do not permit encoding (e.g. required fields are null). /// Thrown if the requested encoding is not supported by this parser. + /// If is null. protected internal abstract IMessage DoParse(string message, string version, ParserConfiguration parserConfiguration); /// diff --git a/tests/NHapi.NUnit/Parser/PipeParserTests.cs b/tests/NHapi.NUnit/Parser/PipeParserTests.cs index 5d5f4ab9f..fc20708cd 100644 --- a/tests/NHapi.NUnit/Parser/PipeParserTests.cs +++ b/tests/NHapi.NUnit/Parser/PipeParserTests.cs @@ -172,5 +172,23 @@ public void UnEscapesData() "'Thirty days have September,\rApril\nJune,\nand November.\nWhen short February is done,\\X0A\\all the rest have 31.'"; Assert.AreEqual(expectedResult, segmentData); } + + /// + /// Check that an is thrown when a null is + /// provided to Parse method calls. + /// + [Test] + public void ParseWithNullConfigThrows() + { + var parser = new PipeParser(); + IMessage nullMessage = null; + const string version = "2.5.1"; + ParserConfiguration nullConfiguration = null; + + Assert.Throws(() => parser.Parse(GetMessage(), nullConfiguration)); + Assert.Throws(() => + parser.Parse(nullMessage, GetMessage(), nullConfiguration)); + Assert.Throws(() => parser.Parse(GetMessage(), version, nullConfiguration)); + } } } \ No newline at end of file From ab7ebe822d51380d15ad7116bcd6312d41a13d0d Mon Sep 17 00:00:00 2001 From: IanGralinski-Invetech <91176557+IanGralinski-Invetech@users.noreply.github.com> Date: Tue, 16 Nov 2021 18:33:17 +1100 Subject: [PATCH 08/13] Copied two PipeParser unit tests from HAPI. Issue #232 --- tests/NHapi.NUnit/Parser/PipeParserTests.cs | 197 ++++++++++++++++++++ 1 file changed, 197 insertions(+) diff --git a/tests/NHapi.NUnit/Parser/PipeParserTests.cs b/tests/NHapi.NUnit/Parser/PipeParserTests.cs index fc20708cd..cd6998958 100644 --- a/tests/NHapi.NUnit/Parser/PipeParserTests.cs +++ b/tests/NHapi.NUnit/Parser/PipeParserTests.cs @@ -190,5 +190,202 @@ public void ParseWithNullConfigThrows() parser.Parse(nullMessage, GetMessage(), nullConfiguration)); Assert.Throws(() => parser.Parse(GetMessage(), version, nullConfiguration)); } + + private static void SetMessageHeader(Model.V251.Message.OML_O21 msg, string messageCode, string messageTriggerEvent, string processingId) + { + var msh = msg.MSH; + + Terser.Set(msh, 1, 0, 1, 1, "|"); + Terser.Set(msh, 2, 0, 1, 1, "^~\\&"); + Terser.Set(msh, 7, 0, 1, 1, DateTime.Now.ToString("yyyyMMddHHmmssK")); + Terser.Set(msh, 9, 0, 1, 1, messageCode); + Terser.Set(msh, 9, 0, 2, 1, messageTriggerEvent); + Terser.Set(msh, 10, 0, 1, 1, Guid.NewGuid().ToString()); + Terser.Set(msh, 11, 0, 1, 1, processingId); + Terser.Set(msh, 12, 0, 1, 1, msg.Version); + } + + /// + /// This test is ported from HAPI: + /// https://github.com/hapifhir/hapi-hl7v2/blob/3333e3aeae60afb7493f6570456e6280c0e16c0b/hapi-test/src/test/java/ca/uhn/hl7v2/parser/NewPipeParserTest.java#L158 + /// See http://sourceforge.net/p/hl7api/bugs/171/. + /// + [Test] + public void GreedyMode() + { + var msg = new Model.V251.Message.OML_O21(); + SetMessageHeader(msg, "OML", "O21", "T"); + + for (var i = 0; i < 5; i++) + { + var orc = msg.GetORDER(i).ORC; + var obr4 = msg.GetORDER(i).OBSERVATION_REQUEST.OBR.UniversalServiceIdentifier; + + orc.OrderControl.Value = "NW"; + orc.PlacerOrderNumber.EntityIdentifier.Value = "ORCID1"; + orc.PlacerOrderNumber.NamespaceID.Value = "HCE"; + orc.PlacerGroupNumber.EntityIdentifier.Value = "grupo"; + + obr4.Identifier.Value = "STDIO1"; + obr4.NameOfCodingSystem.Value = "LOINC"; + } + + // Parse and encode + var pp = new PipeParser(); + msg = pp.Parse( + pp.Encode(msg), + new ParserConfiguration { NonGreedyMode = true }) as Model.V251.Message.OML_O21; + Assert.NotNull(msg); + + for (var i = 0; i < 5; i++) + { + var actual = msg.GetORDER(i).ORC.OrderControl.Value; + Assert.AreEqual("NW", actual); + + actual = msg.GetORDER(i).OBSERVATION_REQUEST.OBR.UniversalServiceIdentifier.Identifier.Value; + Assert.AreEqual("STDIO1", actual); + } + + // Now turn off greedy mode + msg = pp.Parse( + pp.Encode(msg), + new ParserConfiguration { NonGreedyMode = false }) as Model.V251.Message.OML_O21; + Assert.NotNull(msg); + + { + var actual = msg.GetORDER(0).ORC.OrderControl.Value; + Assert.AreEqual("NW", actual); + + actual = msg.GetORDER(0).OBSERVATION_REQUEST.OBR.UniversalServiceIdentifier.Identifier.Value; + Assert.AreEqual("STDIO1", actual); + } + + for (var i = 1; i < 5; i++) + { + var actual = msg.GetORDER(i).ORC.OrderControl.Value; + Assert.IsNull(actual); + + actual = msg.GetORDER(i).OBSERVATION_REQUEST.OBR.UniversalServiceIdentifier.Identifier.Value; + Assert.IsNull(actual); + } + } + + /// + /// This test is ported from HAPI: + /// https://github.com/hapifhir/hapi-hl7v2/blob/3333e3aeae60afb7493f6570456e6280c0e16c0b/hapi-test/src/test/java/ca/uhn/hl7v2/parser/NewPipeParserTest.java#L217 + /// See http://sourceforge.net/p/hl7api/bugs/171/. + /// + [Test] + public void MoreGreedyMode() + { + // OML_O21 messages extracted from https://sourceforge.net/p/hl7api/bugs/171/attachment/IHE_LTW_Examples.java + var messages = new System.Collections.Generic.List + { + "MSH|^~\\&|OP|Entero-gastric|OF|Chemistry|200309060820||OML^O21^OML_O21|msgOP123|T|2.5.1|123\r" + + "PID|1||12345^5^M10^Memphis_Hosp^PI||EVERYMAN^ADAM^^JR^^^L||19800101|M\r" + + "PV1|1|O|Ward|||||||||||||||12345\r" + + "ORC|NW|12345678^gastric||666^gastric|||||200309060710|222221^NURSE^NANCY|||||||||||Entero-gastric^^^^^^FI^^^EG02\r" + + "TQ1|||||||||A\r" + + "OBR||12345678^gastric||82951^Glucose Tolerance Test^C4||||||1234^BLEEDER|P|||||222222^PHYSICIAN^^^^DR|821\r" + + "OBX|1|NM|GLUCOSE||75|g|||||F|||200309060735\r" + + "SPM|1|123456781^gastric||SER|||||||P||||||200309060735|||||||||1\r" + + "SPM|2|123456782^gastric||SER|||||||P||||||200309060755|||||||||1\r" + + "SPM|3|123456783^gastric||SER|||||||P||||||200309060815|||||||||1", + + "MSH|^~\\&|OF|Chemistry|OP|Entero-gastric|200309060825||OML^O21^OML_O21|msgOF102|T|2.5.1|123\r" + + "PID|1||12345^5^M10^Memphis_Hosp^PI||EVERYMAN^ADAM^^JR^^^L||19800101|M\r" + + "PV1|1|O|Ward|||||||||||||||12345\r" + + "ORC|SC|12345678^gastric||666^gastric|IP||||200309060824|222221^NURSE^NANCY |||||||||||Entero-gastric^^^^^^FI^^^EG02\r" + + "TQ1|||||||||A\r" + + "OBR||12345678^gastric|555^chemistry|82951^Glucose Tolerance Test^C4||||||1234^BLEEDER|P|||||222222^PHYSICIAN^^^^DR|821||||||||I\r" + + "SPM|1|123456781^gastric||SER|||||||P||||||200309060735|200309060821||Y||||||1\r" + + "SPM|2|123456782^gastric||SER|||||||P||||||200309060755|200309060821||Y||||||1\r" + + "SPM|3|123456783^gastric||SER|||||||P||||||200309060815|200309060821||N|RB^Broken container|||||1", + + "MSH|^~\\&|OP|Entero-gastric|OF|Chemistry|200309060900||OML^O21^OML_O21|msgOP124|T|2.5.1|123\r" + + "PID|1||12345^5^M10^Memphis_Hosp^PI||EVERYMAN^ADAM^^JR^^^L||19800101|M\r" + + "PV1|1|O|Ward|||||||||||||||12345\r" + + "ORC|XO|12345678^gastric||666^gastric|||||200309060855|222221^NURSE^NANCY|||||||||||Entero-gastric^^^^^^FI^^^EG02\r" + + "TQ1|||||||||A\r" + + "OBR||12345678^gastric||82951^Glucose Tolerance Test^C4||||||1234^BLEEDER|S|||||222222^PHYSICIAN^^^^DR|821\r" + + "OBX|1|NM|GLUCOSE||75|g|||||F|||200309060735\r" + + "SPM|1|123456781^gastric||SER|||||||P||||||200309060735|||||||||1\r" + + "SPM|2|123456782^gastric||SER|||||||P||||||200309060755|||||||||1\r" + + "SPM|3|123456783^gastric||SER|||||||P||||||200309060815|||||||||1\r" + + "SPM|4|123456784^gastric||SER|||||||P||||||200309060835|||||||||1\r" + + "SPM|5|123456785^gastric||SER|||||||P||||||200309060855|||||||||1", + + "MSH|^~\\&|OF|Chemistry|OP|Entero-gastric|200309060930||OML^O21^OML_O21|msgOF109|T|2.5.1|123\r" + + "PID|1||12345^5^M10^Memphis_Hosp^PI||EVERYMAN^ADAM^^JR^^^L||19800101|M\r" + + "PV1|1|O|Ward|||||||||||||||12345\r" + + "ORC|SC|12345678^gastric||666^gastric|CM||||200309060929|222221^NURSE^NANCY|||||||||||Entero-gastric^^^^^^FI^^^EG02\r" + + "TQ1|||||||||A\r" + + "OBR||12345678^gastric|555^chemistry|82951^Glucose Tolerance Test^C4||||||1234^BLEEDER|S|||||222222^PHYSICIAN^^^^DR|821|||||200309060929|||F|||||||444444&CHEMISTRY-EXPERT&Jane&&&&&&MEMPHIS HOSPITAL^200309060929\r" + + "SPM|1|123456781^gastric ||SER|||||||P||||||200309060735|200309060821||Y||||||1\r" + + "SPM|2|123456782^gastric ||SER|||||||P||||||200309060755|200309060821||Y||||||1\r" + + "SPM|3|123456783^gastric ||SER|||||||P||||||200309060815|200309060821||N|RB|||||1\r" + + "SPM|4|123456784^gastric ||SER|||||||P||||||200309060835|200309060902||Y||||||1\r" + + "SPM|5|123456785^gastric ||SER|||||||P||||||200309060855|200309060902||Y||||||1", + + "MSH|^~\\&|HIS|ASE|LIS|Rapela|||OML^O21||T|2.5\r" + + "PID|||14^^^^HC-HCE||BARRIONUEVO^RODOLFO SEBASTIN\r" + + "IN1|1|260^^Planes-HCE|10464^^^^OOSS-HCE|OSDE 210\r" + + "ORC|NW|1^HCE\r" + + "OBR|1|1^HCE||1041^FAUCES RAPIDO (EIA O LATEX)^Desarrollo^2117^FAUCES RAPIDO (EIA O LATEX)^Cod-HCE|S|201301221046\r" + + "ORC|NW|2^HCE\r" + + "OBR|2|2^HCE||1050^OSTEOCALCINA SERICA^Desarrollo^2121^OSTEOCALCINA SERICA^Cod-HCE|S|201301221046\r" + + "ORC|NW|3^HCE\r" + + "OBR|3|3^HCE||10525^HEPATITIS B GENOTIPO^Desarrollo^2126^HEPATITIS B GENOTIPO^Cod-HCE|S|201301221046\r" + + "ORC|NW|4^HCE\r" + + "OBR|4|4^HCE||10526^HEMOCULTIVO BACTEC - 1 MUESTRA (CULTIVO RAPIDO)^Desarrollo^2127^HEMOCULTIVO BACTEC - 1 MUESTRA (CULTIVO RAPIDO)^Cod-HCE|S|201301221046", + + "MSH|^~\\&|OF|Chemistry|AM|Automation|200309060825||OML^O21^OML_O21|msgOF101|T|2.5|123||||USA||EN\r" + + "PID|1||12345^5^M10^Memphis_Hosp^PI||EVERYMAN^ADAM^^JR^^^L||19800101|M\r" + + "PV1|1|O|Ward|||||||||||||||12345\r" + + "ORC|NW|||666^gastric|||||200309060824|222221^NURSE^NANCY|||||||||||Entero-gastric^^^^^^FI^^^EG02\r" + + "TQ1|||||||||A\r" + + "OBR||555_1^chemistry||GLUC^GLUCOSE^L||||||1234^BLEEDER|S|||||222222^PHYSICIAN^^^^DR|821\r" + + "SPM|1|123456781^gastric ||SER|||||||P||||||200309060735|200309060821||||||||1\r" + + "ORC|NW|||666^gastric|||||200309060710|222221^NURSE^NANCY|||||||||||Entero-gastric^^^^^^FI^^^EG02\r" + + "TQ1|||||||||A\r" + + "OBR||555_2^chemistry||GLUC^GLUCOSE^L||||||1234^BLEEDER|S|||||222222^PHYSICIAN^^^^DR|821\r" + + "SPM|1|123456782^gastric||SER|||||||P||||||200309060755|200309060821||||||||1", + + "MSH|^~\\&|HIS|ASE|LIS|Rapela|||OML^O21||T|2.5\r" + + "PID|||14^^^^HC-HCE||BARRIONUEVO^RODOLFO SEBASTIN\r" + + "ORC|NW|||GROUP1^HCE\r" + + "TQ1|\r" + + "OBR||id1^HCE|81641^RAD|73666^Bilateral Feet|\r" + + "SPM|1|123456781^gastric||SER\r" + + "ORC|NW|||GROUP1^HCE\r" + + "TQ1|\r" + + "OBR||id2^HCE|81642^RAD|73642^Bilateral Hand PA|\r" + + "SPM|2|123456782^gastric||SER\r" + + "ORC|NW|||GROUP1^HCE\r" + + "TQ1|\r" + + "OBR||id3^HCE|81643^RAD|73916^Bilateral Knees|\r" + + "SPM|3|123456783^gastric||SER", + }; + + var parser = new PipeParser(); + var greedyConfig = new ParserConfiguration { NonGreedyMode = true }; + + foreach (var message in messages) + { + var oml25 = parser.Parse(message, greedyConfig) as Model.V25.Message.OML_O21; + var oml251 = parser.Parse(message, greedyConfig) as Model.V251.Message.OML_O21; + + if (oml25 is null) + { + Assert.NotNull(oml251); + Assert.AreEqual(0, oml251.GetORDER(0).OBSERVATION_REQUEST.PRIOR_RESULTRepetitionsUsed); + } + else + { + Assert.NotNull(oml25); + Assert.AreEqual(0, oml25.GetORDER(0).OBSERVATION_REQUEST.PRIOR_RESULTRepetitionsUsed); + } + } + } } } \ No newline at end of file From 3f948ddd133af87422723852e51fb2657c3ebd79 Mon Sep 17 00:00:00 2001 From: IanGralinski-Invetech <91176557+IanGralinski-Invetech@users.noreply.github.com> Date: Tue, 16 Nov 2021 18:52:51 +1100 Subject: [PATCH 09/13] Renamed ParserConfiguration to ParserOptions. Issue #232 --- src/NHapi.Base/Parser/DefaultXMLParser.cs | 2 +- src/NHapi.Base/Parser/LegacyPipeParser.cs | 4 ++-- src/NHapi.Base/Parser/MessageIterator.cs | 4 ++-- src/NHapi.Base/Parser/ParserBase.cs | 16 ++++++++-------- .../{ParserConfiguration.cs => ParserOptions.cs} | 4 ++-- src/NHapi.Base/Parser/PipeParser.cs | 4 ++-- src/NHapi.Base/Parser/XMLParser.cs | 4 ++-- tests/NHapi.NUnit/Parser/PipeParserTests.cs | 10 +++++----- tests/NHapi.NUnit/Parser/PipeParserV251Tests.cs | 2 +- 9 files changed, 25 insertions(+), 25 deletions(-) rename src/NHapi.Base/Parser/{ParserConfiguration.cs => ParserOptions.cs} (96%) diff --git a/src/NHapi.Base/Parser/DefaultXMLParser.cs b/src/NHapi.Base/Parser/DefaultXMLParser.cs index 6c824d998..79757f1e1 100644 --- a/src/NHapi.Base/Parser/DefaultXMLParser.cs +++ b/src/NHapi.Base/Parser/DefaultXMLParser.cs @@ -151,7 +151,7 @@ public override IMessage ParseDocument(XmlDocument xmlMessage, string version) } /// - public override void Parse(IMessage message, string @string, ParserConfiguration parserConfiguration) + public override void Parse(IMessage message, string @string, ParserOptions parserConfiguration) { try { diff --git a/src/NHapi.Base/Parser/LegacyPipeParser.cs b/src/NHapi.Base/Parser/LegacyPipeParser.cs index 338951617..917452801 100644 --- a/src/NHapi.Base/Parser/LegacyPipeParser.cs +++ b/src/NHapi.Base/Parser/LegacyPipeParser.cs @@ -257,7 +257,7 @@ public static string StripLeadingWhitespace(string in_Renamed) return out_Renamed.ToString(); } - public override void Parse(IMessage message, string @string, ParserConfiguration parserConfiguration) + public override void Parse(IMessage message, string @string, ParserOptions parserConfiguration) { var messageIter = new Util.MessageIterator(message, "MSH", true); FilterIterator.IPredicate segmentsOnly = new AnonymousClassPredicate(this); @@ -743,7 +743,7 @@ protected internal override string DoEncode(IMessage source) /// EncodingNotSupportedException if the message encoded. /// is not supported by this parser. /// - protected internal override IMessage DoParse(string message, string version, ParserConfiguration parserConfiguration) + protected internal override IMessage DoParse(string message, string version, ParserOptions parserConfiguration) { // try to instantiate a message object of the right class var structure = GetStructure(message); diff --git a/src/NHapi.Base/Parser/MessageIterator.cs b/src/NHapi.Base/Parser/MessageIterator.cs index 762aca3c7..81a79bdfc 100644 --- a/src/NHapi.Base/Parser/MessageIterator.cs +++ b/src/NHapi.Base/Parser/MessageIterator.cs @@ -29,7 +29,7 @@ public class MessageIterator : IEnumerator private readonly IMessage message; private readonly bool handleUnexpectedSegments; - private readonly ParserConfiguration parserConfig; + private readonly ParserOptions parserConfig; private string direction; private bool nextIsSet; @@ -46,7 +46,7 @@ public MessageIterator( IStructureDefinition startDefinition, string direction, bool handleUnexpectedSegments, - ParserConfiguration parserConfig) + ParserOptions parserConfig) { this.message = start; this.direction = direction; diff --git a/src/NHapi.Base/Parser/ParserBase.cs b/src/NHapi.Base/Parser/ParserBase.cs index dc37677fd..ce443044f 100644 --- a/src/NHapi.Base/Parser/ParserBase.cs +++ b/src/NHapi.Base/Parser/ParserBase.cs @@ -45,7 +45,7 @@ namespace NHapi.Base.Parser public abstract class ParserBase { private static readonly IHapiLog Log; - private static readonly ParserConfiguration DefaultParserConfiguration = new ParserConfiguration(); + private static readonly ParserOptions DefaultParserConfiguration = new ParserOptions(); private IValidationContext validationContext; private MessageValidator messageValidator; @@ -188,7 +188,7 @@ public static string GetMessageStructureForEvent(string name, string version) /// /// Parses a message string and returns the corresponding Message object. Uses the default - /// . + /// . /// /// A string that contains an HL7 message. /// A object parsed from the given string. @@ -208,7 +208,7 @@ public virtual IMessage Parse(string message) /// If the message is not correctly formatted. /// If the message encoded is not supported by this parser. /// If is null. - public virtual IMessage Parse(string message, ParserConfiguration parserConfiguration) + public virtual IMessage Parse(string message, ParserOptions parserConfiguration) { var encoding = GetEncoding(message); @@ -245,7 +245,7 @@ public virtual IMessage Parse(string message, ParserConfiguration parserConfigur } /// - /// Parse a message to a specific assembly. Uses the default . + /// Parse a message to a specific assembly. Uses the default . /// /// A string that contains an HL7 message. /// the name of the HL7 version to which the message belongs (eg "2.5"). @@ -263,7 +263,7 @@ public virtual IMessage Parse(string message, string version) /// Contains configuration that will be applied when parsing. /// /// If is null. - public virtual IMessage Parse(string message, string version, ParserConfiguration parserConfiguration) + public virtual IMessage Parse(string message, string version, ParserOptions parserConfiguration) { var encoding = GetEncoding(message); if (!SupportsEncoding(encoding)) @@ -281,7 +281,7 @@ public virtual IMessage Parse(string message, string version, ParserConfiguratio /// /// Parses a particular message and returns the encoded structure. Uses the default - /// . + /// . /// /// The message to encode. /// The string to parse. @@ -299,7 +299,7 @@ public void Parse(IMessage message, string @string) /// Contains configuration that will be applied when parsing. /// If there is a problem encoding. /// If is null. - public abstract void Parse(IMessage message, string @string, ParserConfiguration parserConfiguration); + public abstract void Parse(IMessage message, string @string, ParserOptions parserConfiguration); /// /// Formats a object into an HL7 message string using the given encoding. @@ -449,7 +449,7 @@ public bool SupportsEncoding(string encoding) /// Thrown if the data fields in the message do not permit encoding (e.g. required fields are null). /// Thrown if the requested encoding is not supported by this parser. /// If is null. - protected internal abstract IMessage DoParse(string message, string version, ParserConfiguration parserConfiguration); + protected internal abstract IMessage DoParse(string message, string version, ParserOptions parserConfiguration); /// /// Note that the validation context of the resulting message is set to this parsers validation diff --git a/src/NHapi.Base/Parser/ParserConfiguration.cs b/src/NHapi.Base/Parser/ParserOptions.cs similarity index 96% rename from src/NHapi.Base/Parser/ParserConfiguration.cs rename to src/NHapi.Base/Parser/ParserOptions.cs index 52d503d6e..b6fb8f910 100644 --- a/src/NHapi.Base/Parser/ParserConfiguration.cs +++ b/src/NHapi.Base/Parser/ParserOptions.cs @@ -1,8 +1,8 @@ namespace NHapi.Base.Parser { - public class ParserConfiguration + public class ParserOptions { - public ParserConfiguration() + public ParserOptions() { NonGreedyMode = false; } diff --git a/src/NHapi.Base/Parser/PipeParser.cs b/src/NHapi.Base/Parser/PipeParser.cs index 9d1c9e70b..61df8a194 100644 --- a/src/NHapi.Base/Parser/PipeParser.cs +++ b/src/NHapi.Base/Parser/PipeParser.cs @@ -403,7 +403,7 @@ public virtual void Parse(ISegment destination, string segment, EncodingCharacte } /// - public override void Parse(IMessage message, string @string, ParserConfiguration parserConfiguration) + public override void Parse(IMessage message, string @string, ParserOptions parserConfiguration) { if (parserConfiguration is null) { @@ -707,7 +707,7 @@ protected internal override string DoEncode(IMessage source) } /// - protected internal override IMessage DoParse(string message, string version, ParserConfiguration parserConfiguration) + protected internal override IMessage DoParse(string message, string version, ParserOptions parserConfiguration) { // try to instantiate a message object of the right class var structure = GetStructure(message); diff --git a/src/NHapi.Base/Parser/XMLParser.cs b/src/NHapi.Base/Parser/XMLParser.cs index b89713ca5..d5d46654d 100644 --- a/src/NHapi.Base/Parser/XMLParser.cs +++ b/src/NHapi.Base/Parser/XMLParser.cs @@ -475,7 +475,7 @@ protected internal virtual string RemoveWhitespace(string s) /// XML Document object (using Xerces) from the given String, and calls the abstract /// method . /// - protected internal override IMessage DoParse(string message, string version, ParserConfiguration parserConfiguration) + protected internal override IMessage DoParse(string message, string version, ParserOptions parserConfiguration) { IMessage m = null; @@ -821,7 +821,7 @@ public override XmlDocument EncodeDocument(IMessage source) return null; } - public override void Parse(IMessage message, string @string, ParserConfiguration parserConfiguration) + public override void Parse(IMessage message, string @string, ParserOptions parserConfiguration) { } diff --git a/tests/NHapi.NUnit/Parser/PipeParserTests.cs b/tests/NHapi.NUnit/Parser/PipeParserTests.cs index cd6998958..261222288 100644 --- a/tests/NHapi.NUnit/Parser/PipeParserTests.cs +++ b/tests/NHapi.NUnit/Parser/PipeParserTests.cs @@ -174,7 +174,7 @@ public void UnEscapesData() } /// - /// Check that an is thrown when a null is + /// Check that an is thrown when a null is /// provided to Parse method calls. /// [Test] @@ -183,7 +183,7 @@ public void ParseWithNullConfigThrows() var parser = new PipeParser(); IMessage nullMessage = null; const string version = "2.5.1"; - ParserConfiguration nullConfiguration = null; + ParserOptions nullConfiguration = null; Assert.Throws(() => parser.Parse(GetMessage(), nullConfiguration)); Assert.Throws(() => @@ -234,7 +234,7 @@ public void GreedyMode() var pp = new PipeParser(); msg = pp.Parse( pp.Encode(msg), - new ParserConfiguration { NonGreedyMode = true }) as Model.V251.Message.OML_O21; + new ParserOptions { NonGreedyMode = true }) as Model.V251.Message.OML_O21; Assert.NotNull(msg); for (var i = 0; i < 5; i++) @@ -249,7 +249,7 @@ public void GreedyMode() // Now turn off greedy mode msg = pp.Parse( pp.Encode(msg), - new ParserConfiguration { NonGreedyMode = false }) as Model.V251.Message.OML_O21; + new ParserOptions { NonGreedyMode = false }) as Model.V251.Message.OML_O21; Assert.NotNull(msg); { @@ -368,7 +368,7 @@ public void MoreGreedyMode() }; var parser = new PipeParser(); - var greedyConfig = new ParserConfiguration { NonGreedyMode = true }; + var greedyConfig = new ParserOptions { NonGreedyMode = true }; foreach (var message in messages) { diff --git a/tests/NHapi.NUnit/Parser/PipeParserV251Tests.cs b/tests/NHapi.NUnit/Parser/PipeParserV251Tests.cs index a82c2dcff..9cd994c95 100644 --- a/tests/NHapi.NUnit/Parser/PipeParserV251Tests.cs +++ b/tests/NHapi.NUnit/Parser/PipeParserV251Tests.cs @@ -191,7 +191,7 @@ public void ParseORDERGroupRepetitionsIn_OML_O33() + "OBR|2|AWOS_ID_Found_TT1_TT2_SER_098_1||TT2^Test Type 2^99000"; var parser = new PipeParser(); - var oml = parser.Parse(twoTestOrderMessage, new ParserConfiguration { NonGreedyMode = true }) as OML_O33; + var oml = parser.Parse(twoTestOrderMessage, new ParserOptions { NonGreedyMode = true }) as OML_O33; Assert.NotNull(oml); From 154598a6aee9c41609dc1fb6464c0923aa8ee999 Mon Sep 17 00:00:00 2001 From: IanGralinski-Invetech <91176557+IanGralinski-Invetech@users.noreply.github.com> Date: Wed, 17 Nov 2021 10:33:13 +1100 Subject: [PATCH 10/13] Renamed parameters/variables to follow change of ParserConfiguration to ParserOptions. Issue #232 --- src/NHapi.Base/Parser/DefaultXMLParser.cs | 2 +- src/NHapi.Base/Parser/LegacyPipeParser.cs | 6 ++-- src/NHapi.Base/Parser/MessageIterator.cs | 8 ++--- src/NHapi.Base/Parser/ParserBase.cs | 36 ++++++++++----------- src/NHapi.Base/Parser/PipeParser.cs | 12 +++---- src/NHapi.Base/Parser/XMLParser.cs | 4 +-- tests/NHapi.NUnit/Parser/PipeParserTests.cs | 10 +++--- 7 files changed, 39 insertions(+), 39 deletions(-) diff --git a/src/NHapi.Base/Parser/DefaultXMLParser.cs b/src/NHapi.Base/Parser/DefaultXMLParser.cs index 79757f1e1..ce1600fbe 100644 --- a/src/NHapi.Base/Parser/DefaultXMLParser.cs +++ b/src/NHapi.Base/Parser/DefaultXMLParser.cs @@ -151,7 +151,7 @@ public override IMessage ParseDocument(XmlDocument xmlMessage, string version) } /// - public override void Parse(IMessage message, string @string, ParserOptions parserConfiguration) + public override void Parse(IMessage message, string @string, ParserOptions parserOptions) { try { diff --git a/src/NHapi.Base/Parser/LegacyPipeParser.cs b/src/NHapi.Base/Parser/LegacyPipeParser.cs index 917452801..966f69485 100644 --- a/src/NHapi.Base/Parser/LegacyPipeParser.cs +++ b/src/NHapi.Base/Parser/LegacyPipeParser.cs @@ -257,7 +257,7 @@ public static string StripLeadingWhitespace(string in_Renamed) return out_Renamed.ToString(); } - public override void Parse(IMessage message, string @string, ParserOptions parserConfiguration) + public override void Parse(IMessage message, string @string, ParserOptions parserOptions) { var messageIter = new Util.MessageIterator(message, "MSH", true); FilterIterator.IPredicate segmentsOnly = new AnonymousClassPredicate(this); @@ -743,13 +743,13 @@ protected internal override string DoEncode(IMessage source) /// EncodingNotSupportedException if the message encoded. /// is not supported by this parser. /// - protected internal override IMessage DoParse(string message, string version, ParserOptions parserConfiguration) + protected internal override IMessage DoParse(string message, string version, ParserOptions parserOptions) { // try to instantiate a message object of the right class var structure = GetStructure(message); var m = InstantiateMessage(structure.Structure, version, structure.ExplicitlyDefined); - Parse(m, message, parserConfiguration); + Parse(m, message, parserOptions); return m; } diff --git a/src/NHapi.Base/Parser/MessageIterator.cs b/src/NHapi.Base/Parser/MessageIterator.cs index 81a79bdfc..4a9a415ee 100644 --- a/src/NHapi.Base/Parser/MessageIterator.cs +++ b/src/NHapi.Base/Parser/MessageIterator.cs @@ -29,7 +29,7 @@ public class MessageIterator : IEnumerator private readonly IMessage message; private readonly bool handleUnexpectedSegments; - private readonly ParserOptions parserConfig; + private readonly ParserOptions parserOptions; private string direction; private bool nextIsSet; @@ -46,13 +46,13 @@ public MessageIterator( IStructureDefinition startDefinition, string direction, bool handleUnexpectedSegments, - ParserOptions parserConfig) + ParserOptions parserOptions) { this.message = start; this.direction = direction; this.handleUnexpectedSegments = handleUnexpectedSegments; this.currentDefinitionPath.Add(new Position(startDefinition, -1)); - this.parserConfig = parserConfig; + this.parserOptions = parserOptions; } ///

    Returns the next node in the message. Sometimes the next node is @@ -124,7 +124,7 @@ public virtual bool MoveNext() var structureDefinition = currentPosition.StructureDefinition; - if (parserConfig.NonGreedyMode) + if (parserOptions.NonGreedyMode) { var nonGreedyPosition = LocateNonGreedyStructure(); if (nonGreedyPosition != null) diff --git a/src/NHapi.Base/Parser/ParserBase.cs b/src/NHapi.Base/Parser/ParserBase.cs index ce443044f..ad501a055 100644 --- a/src/NHapi.Base/Parser/ParserBase.cs +++ b/src/NHapi.Base/Parser/ParserBase.cs @@ -45,7 +45,7 @@ namespace NHapi.Base.Parser public abstract class ParserBase { private static readonly IHapiLog Log; - private static readonly ParserOptions DefaultParserConfiguration = new ParserOptions(); + private static readonly ParserOptions DefaultParserOptions = new ParserOptions(); private IValidationContext validationContext; private MessageValidator messageValidator; @@ -196,19 +196,19 @@ public static string GetMessageStructureForEvent(string name, string version) /// If the message encoded is not supported by this parser. public virtual IMessage Parse(string message) { - return Parse(message, DefaultParserConfiguration); + return Parse(message, DefaultParserOptions); } ///

    /// Parses a message string and returns the corresponding Message object. /// /// A string that contains an HL7 message. - /// Contains configuration that will be applied when parsing. + /// Contains configuration that will be applied when parsing. /// A object parsed from the given string. /// If the message is not correctly formatted. /// If the message encoded is not supported by this parser. - /// If is null. - public virtual IMessage Parse(string message, ParserOptions parserConfiguration) + /// If is null. + public virtual IMessage Parse(string message, ParserOptions parserOptions) { var encoding = GetEncoding(message); @@ -241,7 +241,7 @@ public virtual IMessage Parse(string message, ParserOptions parserConfiguration) ErrorCode.UNSUPPORTED_VERSION_ID); } - return Parse(message, version, parserConfiguration); + return Parse(message, version, parserOptions); } /// @@ -252,7 +252,7 @@ public virtual IMessage Parse(string message, ParserOptions parserConfiguration) /// public virtual IMessage Parse(string message, string version) { - return Parse(message, version, DefaultParserConfiguration); + return Parse(message, version, DefaultParserOptions); } /// @@ -260,10 +260,10 @@ public virtual IMessage Parse(string message, string version) /// /// A string that contains an HL7 message. /// the name of the HL7 version to which the message belongs (eg "2.5"). - /// Contains configuration that will be applied when parsing. + /// Contains configuration that will be applied when parsing. /// - /// If is null. - public virtual IMessage Parse(string message, string version, ParserOptions parserConfiguration) + /// If is null. + public virtual IMessage Parse(string message, string version, ParserOptions parserOptions) { var encoding = GetEncoding(message); if (!SupportsEncoding(encoding)) @@ -273,7 +273,7 @@ public virtual IMessage Parse(string message, string version, ParserOptions pars } messageValidator.Validate(message, encoding.Equals("XML"), version); - var result = DoParse(message, version, parserConfiguration); + var result = DoParse(message, version, parserOptions); messageValidator.Validate(result); return result; @@ -288,7 +288,7 @@ public virtual IMessage Parse(string message, string version, ParserOptions pars /// If there is a problem encoding. public void Parse(IMessage message, string @string) { - Parse(message, @string, DefaultParserConfiguration); + Parse(message, @string, DefaultParserOptions); } /// @@ -296,10 +296,10 @@ public void Parse(IMessage message, string @string) /// /// The message to encode. /// The string to parse. - /// Contains configuration that will be applied when parsing. + /// Contains configuration that will be applied when parsing. /// If there is a problem encoding. - /// If is null. - public abstract void Parse(IMessage message, string @string, ParserOptions parserConfiguration); + /// If is null. + public abstract void Parse(IMessage message, string @string, ParserOptions parserOptions); /// /// Formats a object into an HL7 message string using the given encoding. @@ -444,12 +444,12 @@ public bool SupportsEncoding(string encoding) /// /// a String that contains an HL7 message. /// the name of the HL7 version to which the message belongs (eg "2.5"). - /// Contains configuration that will be applied when parsing. + /// Contains configuration that will be applied when parsing. /// A HAPI Message object parsed from the given String. /// Thrown if the data fields in the message do not permit encoding (e.g. required fields are null). /// Thrown if the requested encoding is not supported by this parser. - /// If is null. - protected internal abstract IMessage DoParse(string message, string version, ParserOptions parserConfiguration); + /// If is null. + protected internal abstract IMessage DoParse(string message, string version, ParserOptions parserOptions); /// /// Note that the validation context of the resulting message is set to this parsers validation diff --git a/src/NHapi.Base/Parser/PipeParser.cs b/src/NHapi.Base/Parser/PipeParser.cs index 61df8a194..378690e14 100644 --- a/src/NHapi.Base/Parser/PipeParser.cs +++ b/src/NHapi.Base/Parser/PipeParser.cs @@ -403,15 +403,15 @@ public virtual void Parse(ISegment destination, string segment, EncodingCharacte } /// - public override void Parse(IMessage message, string @string, ParserOptions parserConfiguration) + public override void Parse(IMessage message, string @string, ParserOptions parserOptions) { - if (parserConfiguration is null) + if (parserOptions is null) { - throw new ArgumentNullException(nameof(parserConfiguration)); + throw new ArgumentNullException(nameof(parserOptions)); } var structureDefinition = GetStructureDefinition(message); - var messageIterator = new MessageIterator(message, structureDefinition, "MSH", true, parserConfiguration); + var messageIterator = new MessageIterator(message, structureDefinition, "MSH", true, parserOptions); var segments = Split(@string, SegmentDelimiter); @@ -707,12 +707,12 @@ protected internal override string DoEncode(IMessage source) } /// - protected internal override IMessage DoParse(string message, string version, ParserOptions parserConfiguration) + protected internal override IMessage DoParse(string message, string version, ParserOptions parserOptions) { // try to instantiate a message object of the right class var structure = GetStructure(message); var m = InstantiateMessage(structure.Structure, version, structure.ExplicitlyDefined); - Parse(m, message, parserConfiguration); + Parse(m, message, parserOptions); return m; } diff --git a/src/NHapi.Base/Parser/XMLParser.cs b/src/NHapi.Base/Parser/XMLParser.cs index d5d46654d..3fb26467a 100644 --- a/src/NHapi.Base/Parser/XMLParser.cs +++ b/src/NHapi.Base/Parser/XMLParser.cs @@ -475,7 +475,7 @@ protected internal virtual string RemoveWhitespace(string s) /// XML Document object (using Xerces) from the given String, and calls the abstract /// method . /// - protected internal override IMessage DoParse(string message, string version, ParserOptions parserConfiguration) + protected internal override IMessage DoParse(string message, string version, ParserOptions parserOptions) { IMessage m = null; @@ -821,7 +821,7 @@ public override XmlDocument EncodeDocument(IMessage source) return null; } - public override void Parse(IMessage message, string @string, ParserOptions parserConfiguration) + public override void Parse(IMessage message, string @string, ParserOptions parserOptions) { } diff --git a/tests/NHapi.NUnit/Parser/PipeParserTests.cs b/tests/NHapi.NUnit/Parser/PipeParserTests.cs index 261222288..564b29cd4 100644 --- a/tests/NHapi.NUnit/Parser/PipeParserTests.cs +++ b/tests/NHapi.NUnit/Parser/PipeParserTests.cs @@ -231,9 +231,9 @@ public void GreedyMode() } // Parse and encode - var pp = new PipeParser(); - msg = pp.Parse( - pp.Encode(msg), + var parser = new PipeParser(); + msg = parser.Parse( + parser.Encode(msg), new ParserOptions { NonGreedyMode = true }) as Model.V251.Message.OML_O21; Assert.NotNull(msg); @@ -247,8 +247,8 @@ public void GreedyMode() } // Now turn off greedy mode - msg = pp.Parse( - pp.Encode(msg), + msg = parser.Parse( + parser.Encode(msg), new ParserOptions { NonGreedyMode = false }) as Model.V251.Message.OML_O21; Assert.NotNull(msg); From ecbf65b5a2b5988cc216d38fb3d41eb2f8b26fb5 Mon Sep 17 00:00:00 2001 From: IanGralinski-Invetech <91176557+IanGralinski-Invetech@users.noreply.github.com> Date: Wed, 17 Nov 2021 12:13:36 +1100 Subject: [PATCH 11/13] Moved test data messages for non-greedy mode to a separate file. Issue #232 --- tests/NHapi.NUnit/NHapi.NUnit.csproj | 4 + tests/NHapi.NUnit/Parser/PipeParserTests.cs | 94 ++----------------- .../TestData/Parser/OML_O21_messages.txt | 84 +++++++++++++++++ 3 files changed, 94 insertions(+), 88 deletions(-) create mode 100644 tests/NHapi.NUnit/TestData/Parser/OML_O21_messages.txt diff --git a/tests/NHapi.NUnit/NHapi.NUnit.csproj b/tests/NHapi.NUnit/NHapi.NUnit.csproj index 2296fcb54..bdacad5f7 100644 --- a/tests/NHapi.NUnit/NHapi.NUnit.csproj +++ b/tests/NHapi.NUnit/NHapi.NUnit.csproj @@ -6,6 +6,7 @@ + @@ -15,6 +16,9 @@ + + PreserveNewest + PreserveNewest diff --git a/tests/NHapi.NUnit/Parser/PipeParserTests.cs b/tests/NHapi.NUnit/Parser/PipeParserTests.cs index 564b29cd4..fef62e267 100644 --- a/tests/NHapi.NUnit/Parser/PipeParserTests.cs +++ b/tests/NHapi.NUnit/Parser/PipeParserTests.cs @@ -1,6 +1,7 @@ namespace NHapi.NUnit.Parser { using System; + using System.IO; using global::NUnit.Framework; @@ -278,94 +279,11 @@ public void GreedyMode() [Test] public void MoreGreedyMode() { - // OML_O21 messages extracted from https://sourceforge.net/p/hl7api/bugs/171/attachment/IHE_LTW_Examples.java - var messages = new System.Collections.Generic.List - { - "MSH|^~\\&|OP|Entero-gastric|OF|Chemistry|200309060820||OML^O21^OML_O21|msgOP123|T|2.5.1|123\r" + - "PID|1||12345^5^M10^Memphis_Hosp^PI||EVERYMAN^ADAM^^JR^^^L||19800101|M\r" + - "PV1|1|O|Ward|||||||||||||||12345\r" + - "ORC|NW|12345678^gastric||666^gastric|||||200309060710|222221^NURSE^NANCY|||||||||||Entero-gastric^^^^^^FI^^^EG02\r" + - "TQ1|||||||||A\r" + - "OBR||12345678^gastric||82951^Glucose Tolerance Test^C4||||||1234^BLEEDER|P|||||222222^PHYSICIAN^^^^DR|821\r" + - "OBX|1|NM|GLUCOSE||75|g|||||F|||200309060735\r" + - "SPM|1|123456781^gastric||SER|||||||P||||||200309060735|||||||||1\r" + - "SPM|2|123456782^gastric||SER|||||||P||||||200309060755|||||||||1\r" + - "SPM|3|123456783^gastric||SER|||||||P||||||200309060815|||||||||1", - - "MSH|^~\\&|OF|Chemistry|OP|Entero-gastric|200309060825||OML^O21^OML_O21|msgOF102|T|2.5.1|123\r" + - "PID|1||12345^5^M10^Memphis_Hosp^PI||EVERYMAN^ADAM^^JR^^^L||19800101|M\r" + - "PV1|1|O|Ward|||||||||||||||12345\r" + - "ORC|SC|12345678^gastric||666^gastric|IP||||200309060824|222221^NURSE^NANCY |||||||||||Entero-gastric^^^^^^FI^^^EG02\r" + - "TQ1|||||||||A\r" + - "OBR||12345678^gastric|555^chemistry|82951^Glucose Tolerance Test^C4||||||1234^BLEEDER|P|||||222222^PHYSICIAN^^^^DR|821||||||||I\r" + - "SPM|1|123456781^gastric||SER|||||||P||||||200309060735|200309060821||Y||||||1\r" + - "SPM|2|123456782^gastric||SER|||||||P||||||200309060755|200309060821||Y||||||1\r" + - "SPM|3|123456783^gastric||SER|||||||P||||||200309060815|200309060821||N|RB^Broken container|||||1", - - "MSH|^~\\&|OP|Entero-gastric|OF|Chemistry|200309060900||OML^O21^OML_O21|msgOP124|T|2.5.1|123\r" + - "PID|1||12345^5^M10^Memphis_Hosp^PI||EVERYMAN^ADAM^^JR^^^L||19800101|M\r" + - "PV1|1|O|Ward|||||||||||||||12345\r" + - "ORC|XO|12345678^gastric||666^gastric|||||200309060855|222221^NURSE^NANCY|||||||||||Entero-gastric^^^^^^FI^^^EG02\r" + - "TQ1|||||||||A\r" + - "OBR||12345678^gastric||82951^Glucose Tolerance Test^C4||||||1234^BLEEDER|S|||||222222^PHYSICIAN^^^^DR|821\r" + - "OBX|1|NM|GLUCOSE||75|g|||||F|||200309060735\r" + - "SPM|1|123456781^gastric||SER|||||||P||||||200309060735|||||||||1\r" + - "SPM|2|123456782^gastric||SER|||||||P||||||200309060755|||||||||1\r" + - "SPM|3|123456783^gastric||SER|||||||P||||||200309060815|||||||||1\r" + - "SPM|4|123456784^gastric||SER|||||||P||||||200309060835|||||||||1\r" + - "SPM|5|123456785^gastric||SER|||||||P||||||200309060855|||||||||1", - - "MSH|^~\\&|OF|Chemistry|OP|Entero-gastric|200309060930||OML^O21^OML_O21|msgOF109|T|2.5.1|123\r" + - "PID|1||12345^5^M10^Memphis_Hosp^PI||EVERYMAN^ADAM^^JR^^^L||19800101|M\r" + - "PV1|1|O|Ward|||||||||||||||12345\r" + - "ORC|SC|12345678^gastric||666^gastric|CM||||200309060929|222221^NURSE^NANCY|||||||||||Entero-gastric^^^^^^FI^^^EG02\r" + - "TQ1|||||||||A\r" + - "OBR||12345678^gastric|555^chemistry|82951^Glucose Tolerance Test^C4||||||1234^BLEEDER|S|||||222222^PHYSICIAN^^^^DR|821|||||200309060929|||F|||||||444444&CHEMISTRY-EXPERT&Jane&&&&&&MEMPHIS HOSPITAL^200309060929\r" + - "SPM|1|123456781^gastric ||SER|||||||P||||||200309060735|200309060821||Y||||||1\r" + - "SPM|2|123456782^gastric ||SER|||||||P||||||200309060755|200309060821||Y||||||1\r" + - "SPM|3|123456783^gastric ||SER|||||||P||||||200309060815|200309060821||N|RB|||||1\r" + - "SPM|4|123456784^gastric ||SER|||||||P||||||200309060835|200309060902||Y||||||1\r" + - "SPM|5|123456785^gastric ||SER|||||||P||||||200309060855|200309060902||Y||||||1", - - "MSH|^~\\&|HIS|ASE|LIS|Rapela|||OML^O21||T|2.5\r" + - "PID|||14^^^^HC-HCE||BARRIONUEVO^RODOLFO SEBASTIN\r" + - "IN1|1|260^^Planes-HCE|10464^^^^OOSS-HCE|OSDE 210\r" + - "ORC|NW|1^HCE\r" + - "OBR|1|1^HCE||1041^FAUCES RAPIDO (EIA O LATEX)^Desarrollo^2117^FAUCES RAPIDO (EIA O LATEX)^Cod-HCE|S|201301221046\r" + - "ORC|NW|2^HCE\r" + - "OBR|2|2^HCE||1050^OSTEOCALCINA SERICA^Desarrollo^2121^OSTEOCALCINA SERICA^Cod-HCE|S|201301221046\r" + - "ORC|NW|3^HCE\r" + - "OBR|3|3^HCE||10525^HEPATITIS B GENOTIPO^Desarrollo^2126^HEPATITIS B GENOTIPO^Cod-HCE|S|201301221046\r" + - "ORC|NW|4^HCE\r" + - "OBR|4|4^HCE||10526^HEMOCULTIVO BACTEC - 1 MUESTRA (CULTIVO RAPIDO)^Desarrollo^2127^HEMOCULTIVO BACTEC - 1 MUESTRA (CULTIVO RAPIDO)^Cod-HCE|S|201301221046", - - "MSH|^~\\&|OF|Chemistry|AM|Automation|200309060825||OML^O21^OML_O21|msgOF101|T|2.5|123||||USA||EN\r" + - "PID|1||12345^5^M10^Memphis_Hosp^PI||EVERYMAN^ADAM^^JR^^^L||19800101|M\r" + - "PV1|1|O|Ward|||||||||||||||12345\r" + - "ORC|NW|||666^gastric|||||200309060824|222221^NURSE^NANCY|||||||||||Entero-gastric^^^^^^FI^^^EG02\r" + - "TQ1|||||||||A\r" + - "OBR||555_1^chemistry||GLUC^GLUCOSE^L||||||1234^BLEEDER|S|||||222222^PHYSICIAN^^^^DR|821\r" + - "SPM|1|123456781^gastric ||SER|||||||P||||||200309060735|200309060821||||||||1\r" + - "ORC|NW|||666^gastric|||||200309060710|222221^NURSE^NANCY|||||||||||Entero-gastric^^^^^^FI^^^EG02\r" + - "TQ1|||||||||A\r" + - "OBR||555_2^chemistry||GLUC^GLUCOSE^L||||||1234^BLEEDER|S|||||222222^PHYSICIAN^^^^DR|821\r" + - "SPM|1|123456782^gastric||SER|||||||P||||||200309060755|200309060821||||||||1", - - "MSH|^~\\&|HIS|ASE|LIS|Rapela|||OML^O21||T|2.5\r" + - "PID|||14^^^^HC-HCE||BARRIONUEVO^RODOLFO SEBASTIN\r" + - "ORC|NW|||GROUP1^HCE\r" + - "TQ1|\r" + - "OBR||id1^HCE|81641^RAD|73666^Bilateral Feet|\r" + - "SPM|1|123456781^gastric||SER\r" + - "ORC|NW|||GROUP1^HCE\r" + - "TQ1|\r" + - "OBR||id2^HCE|81642^RAD|73642^Bilateral Hand PA|\r" + - "SPM|2|123456782^gastric||SER\r" + - "ORC|NW|||GROUP1^HCE\r" + - "TQ1|\r" + - "OBR||id3^HCE|81643^RAD|73916^Bilateral Knees|\r" + - "SPM|3|123456783^gastric||SER", - }; + var testDataDir = Path.Combine(TestContext.CurrentContext.TestDirectory, "TestData", "Parser"); + var messagesAsString = File.ReadAllText(Path.Combine(testDataDir, "OML_O21_messages.txt")); + var messages = messagesAsString.Split( + new[] { Environment.NewLine + Environment.NewLine }, + StringSplitOptions.RemoveEmptyEntries); var parser = new PipeParser(); var greedyConfig = new ParserOptions { NonGreedyMode = true }; diff --git a/tests/NHapi.NUnit/TestData/Parser/OML_O21_messages.txt b/tests/NHapi.NUnit/TestData/Parser/OML_O21_messages.txt new file mode 100644 index 000000000..ac146215e --- /dev/null +++ b/tests/NHapi.NUnit/TestData/Parser/OML_O21_messages.txt @@ -0,0 +1,84 @@ +MSH|^~\&|OP|Entero-gastric|OF|Chemistry|200309060820||OML^O21^OML_O21|msgOP123|T|2.5.1|123 +PID|1||12345^5^M10^Memphis_Hosp^PI||EVERYMAN^ADAM^^JR^^^L||19800101|M +PV1|1|O|Ward|||||||||||||||12345 +ORC|NW|12345678^gastric||666^gastric|||||200309060710|222221^NURSE^NANCY|||||||||||Entero-gastric^^^^^^FI^^^EG02 +TQ1|||||||||A +OBR||12345678^gastric||82951^Glucose Tolerance Test^C4||||||1234^BLEEDER|P|||||222222^PHYSICIAN^^^^DR|821 +OBX|1|NM|GLUCOSE||75|g|||||F|||200309060735 +SPM|1|123456781^gastric||SER|||||||P||||||200309060735|||||||||1 +SPM|2|123456782^gastric||SER|||||||P||||||200309060755|||||||||1 +SPM|3|123456783^gastric||SER|||||||P||||||200309060815|||||||||1 + +MSH|^~\&|OF|Chemistry|OP|Entero-gastric|200309060825||OML^O21^OML_O21|msgOF102|T|2.5.1|123 +PID|1||12345^5^M10^Memphis_Hosp^PI||EVERYMAN^ADAM^^JR^^^L||19800101|M +PV1|1|O|Ward|||||||||||||||12345 +ORC|SC|12345678^gastric||666^gastric|IP||||200309060824|222221^NURSE^NANCY |||||||||||Entero-gastric^^^^^^FI^^^EG02 +TQ1|||||||||A +OBR||12345678^gastric|555^chemistry|82951^Glucose Tolerance Test^C4||||||1234^BLEEDER|P|||||222222^PHYSICIAN^^^^DR|821||||||||I +SPM|1|123456781^gastric||SER|||||||P||||||200309060735|200309060821||Y||||||1 +SPM|2|123456782^gastric||SER|||||||P||||||200309060755|200309060821||Y||||||1 +SPM|3|123456783^gastric||SER|||||||P||||||200309060815|200309060821||N|RB^Broken container|||||1 + +MSH|^~\&|OP|Entero-gastric|OF|Chemistry|200309060900||OML^O21^OML_O21|msgOP124|T|2.5.1|123 +PID|1||12345^5^M10^Memphis_Hosp^PI||EVERYMAN^ADAM^^JR^^^L||19800101|M +PV1|1|O|Ward|||||||||||||||12345 +ORC|XO|12345678^gastric||666^gastric|||||200309060855|222221^NURSE^NANCY|||||||||||Entero-gastric^^^^^^FI^^^EG02 +TQ1|||||||||A +OBR||12345678^gastric||82951^Glucose Tolerance Test^C4||||||1234^BLEEDER|S|||||222222^PHYSICIAN^^^^DR|821 +OBX|1|NM|GLUCOSE||75|g|||||F|||200309060735 +SPM|1|123456781^gastric||SER|||||||P||||||200309060735|||||||||1 +SPM|2|123456782^gastric||SER|||||||P||||||200309060755|||||||||1 +SPM|3|123456783^gastric||SER|||||||P||||||200309060815|||||||||1 +SPM|4|123456784^gastric||SER|||||||P||||||200309060835|||||||||1 +SPM|5|123456785^gastric||SER|||||||P||||||200309060855|||||||||1 + +MSH|^~\&|OF|Chemistry|OP|Entero-gastric|200309060930||OML^O21^OML_O21|msgOF109|T|2.5.1|123 +PID|1||12345^5^M10^Memphis_Hosp^PI||EVERYMAN^ADAM^^JR^^^L||19800101|M +PV1|1|O|Ward|||||||||||||||12345 +ORC|SC|12345678^gastric||666^gastric|CM||||200309060929|222221^NURSE^NANCY|||||||||||Entero-gastric^^^^^^FI^^^EG02 +TQ1|||||||||A +OBR||12345678^gastric|555^chemistry|82951^Glucose Tolerance Test^C4||||||1234^BLEEDER|S|||||222222^PHYSICIAN^^^^DR|821|||||200309060929|||F|||||||444444&CHEMISTRY-EXPERT&Jane&&&&&&MEMPHIS HOSPITAL^200309060929 +SPM|1|123456781^gastric ||SER|||||||P||||||200309060735|200309060821||Y||||||1 +SPM|2|123456782^gastric ||SER|||||||P||||||200309060755|200309060821||Y||||||1 +SPM|3|123456783^gastric ||SER|||||||P||||||200309060815|200309060821||N|RB|||||1 +SPM|4|123456784^gastric ||SER|||||||P||||||200309060835|200309060902||Y||||||1 +SPM|5|123456785^gastric ||SER|||||||P||||||200309060855|200309060902||Y||||||1 + +MSH|^~\&|HIS|ASE|LIS|Rapela|||OML^O21||T|2.5 +PID|||14^^^^HC-HCE||BARRIONUEVO^RODOLFO SEBASTIÁN +IN1|1|260^^Planes-HCE|10464^^^^OOSS-HCE|OSDE 210 +ORC|NW|1^HCE +OBR|1|1^HCE||1041^FAUCES RAPIDO (EIA O LATEX)^Desarrollo^2117^FAUCES RAPIDO (EIA O LATEX)^Cod-HCE|S|201301221046 +ORC|NW|2^HCE +OBR|2|2^HCE||1050^OSTEOCALCINA SERICA^Desarrollo^2121^OSTEOCALCINA SERICA^Cod-HCE|S|201301221046 +ORC|NW|3^HCE +OBR|3|3^HCE||10525^HEPATITIS B GENOTIPO^Desarrollo^2126^HEPATITIS B GENOTIPO^Cod-HCE|S|201301221046 +ORC|NW|4^HCE +OBR|4|4^HCE||10526^HEMOCULTIVO BACTEC - 1 MUESTRA (CULTIVO RAPIDO)^Desarrollo^2127^HEMOCULTIVO BACTEC - 1 MUESTRA (CULTIVO RAPIDO)^Cod-HCE|S|201301221046 + +MSH|^~\&|OF|Chemistry|AM|Automation|200309060825||OML^O21^OML_O21|msgOF101|T|2.5|123||||USA||EN +PID|1||12345^5^M10^Memphis_Hosp^PI||EVERYMAN^ADAM^^JR^^^L||19800101|M +PV1|1|O|Ward|||||||||||||||12345 +ORC|NW|||666^gastric|||||200309060824|222221^NURSE^NANCY|||||||||||Entero-gastric^^^^^^FI^^^EG02 +TQ1|||||||||A +OBR||555_1^chemistry||GLUC^GLUCOSE^L||||||1234^BLEEDER|S|||||222222^PHYSICIAN^^^^DR|821 +SPM|1|123456781^gastric ||SER|||||||P||||||200309060735|200309060821||||||||1 +ORC|NW|||666^gastric|||||200309060710|222221^NURSE^NANCY|||||||||||Entero-gastric^^^^^^FI^^^EG02 +TQ1|||||||||A +OBR||555_2^chemistry||GLUC^GLUCOSE^L||||||1234^BLEEDER|S|||||222222^PHYSICIAN^^^^DR|821 +SPM|1|123456782^gastric||SER|||||||P||||||200309060755|200309060821||||||||1 + +MSH|^~\&|HIS|ASE|LIS|Rapela|||OML^O21||T|2.5 +PID|||14^^^^HC-HCE||BARRIONUEVO^RODOLFO SEBASTIÁN +ORC|NW|||GROUP1^HCE +TQ1| +OBR||id1^HCE|81641^RAD|73666^Bilateral Feet| +SPM|1|123456781^gastric||SER +ORC|NW|||GROUP1^HCE +TQ1| +OBR||id2^HCE|81642^RAD|73642^Bilateral Hand PA| +SPM|2|123456782^gastric||SER +ORC|NW|||GROUP1^HCE +TQ1| +OBR||id3^HCE|81643^RAD|73916^Bilateral Knees| +SPM|3|123456783^gastric||SER \ No newline at end of file From 2b04bcb08d3bd02592ca3d1a3120569b5b3c2762 Mon Sep 17 00:00:00 2001 From: IanGralinski-Invetech <91176557+IanGralinski-Invetech@users.noreply.github.com> Date: Wed, 17 Nov 2021 20:39:45 +1100 Subject: [PATCH 12/13] Changed Environment.NewLine to explicit "\r\n" since the text file read does not change based on host OS. Issue #232 --- tests/NHapi.NUnit/Parser/PipeParserTests.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/NHapi.NUnit/Parser/PipeParserTests.cs b/tests/NHapi.NUnit/Parser/PipeParserTests.cs index fef62e267..e4caaf0f4 100644 --- a/tests/NHapi.NUnit/Parser/PipeParserTests.cs +++ b/tests/NHapi.NUnit/Parser/PipeParserTests.cs @@ -252,7 +252,6 @@ public void GreedyMode() parser.Encode(msg), new ParserOptions { NonGreedyMode = false }) as Model.V251.Message.OML_O21; Assert.NotNull(msg); - { var actual = msg.GetORDER(0).ORC.OrderControl.Value; Assert.AreEqual("NW", actual); @@ -282,7 +281,7 @@ public void MoreGreedyMode() var testDataDir = Path.Combine(TestContext.CurrentContext.TestDirectory, "TestData", "Parser"); var messagesAsString = File.ReadAllText(Path.Combine(testDataDir, "OML_O21_messages.txt")); var messages = messagesAsString.Split( - new[] { Environment.NewLine + Environment.NewLine }, + new[] { "\r\n\r\n" }, StringSplitOptions.RemoveEmptyEntries); var parser = new PipeParser(); From a24737944b555ff259c50ba9fba0dbdff5156b77 Mon Sep 17 00:00:00 2001 From: IanGralinski-Invetech <91176557+IanGralinski-Invetech@users.noreply.github.com> Date: Thu, 18 Nov 2021 11:05:32 +1100 Subject: [PATCH 13/13] Updated test to use pattern-based switch statement. Issue #232 --- tests/NHapi.NUnit/Parser/PipeParserTests.cs | 26 ++++++++++++--------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/tests/NHapi.NUnit/Parser/PipeParserTests.cs b/tests/NHapi.NUnit/Parser/PipeParserTests.cs index e4caaf0f4..30dc2c666 100644 --- a/tests/NHapi.NUnit/Parser/PipeParserTests.cs +++ b/tests/NHapi.NUnit/Parser/PipeParserTests.cs @@ -279,7 +279,8 @@ public void GreedyMode() public void MoreGreedyMode() { var testDataDir = Path.Combine(TestContext.CurrentContext.TestDirectory, "TestData", "Parser"); - var messagesAsString = File.ReadAllText(Path.Combine(testDataDir, "OML_O21_messages.txt")); + var messageFilename = "OML_O21_messages.txt"; + var messagesAsString = File.ReadAllText(Path.Combine(testDataDir, messageFilename)); var messages = messagesAsString.Split( new[] { "\r\n\r\n" }, StringSplitOptions.RemoveEmptyEntries); @@ -289,18 +290,21 @@ public void MoreGreedyMode() foreach (var message in messages) { - var oml25 = parser.Parse(message, greedyConfig) as Model.V25.Message.OML_O21; - var oml251 = parser.Parse(message, greedyConfig) as Model.V251.Message.OML_O21; + var parsedMessage = parser.Parse(message, greedyConfig); - if (oml25 is null) + switch (parsedMessage) { - Assert.NotNull(oml251); - Assert.AreEqual(0, oml251.GetORDER(0).OBSERVATION_REQUEST.PRIOR_RESULTRepetitionsUsed); - } - else - { - Assert.NotNull(oml25); - Assert.AreEqual(0, oml25.GetORDER(0).OBSERVATION_REQUEST.PRIOR_RESULTRepetitionsUsed); + case Model.V251.Message.OML_O21 oml251: + Assert.NotNull(oml251); + Assert.AreEqual(0, oml251.GetORDER(0).OBSERVATION_REQUEST.PRIOR_RESULTRepetitionsUsed); + break; + case Model.V25.Message.OML_O21 oml25: + Assert.NotNull(oml25); + Assert.AreEqual(0, oml25.GetORDER(0).OBSERVATION_REQUEST.PRIOR_RESULTRepetitionsUsed); + break; + default: + Assert.Fail($"Could not parse messages from {messageFilename} into v2.5 nor v.2.5.1 OML_O21."); + return; } } }