diff --git a/.gitattributes b/.gitattributes index f6c098b67..0c1426c52 100644 --- a/.gitattributes +++ b/.gitattributes @@ -3,10 +3,12 @@ # Declare files that will always have CRLF line endings on checkout. *.txt text eol=crlf +*.hl7 text eol=crlf # Denote all files that are truly binary and should not be modified. *.png binary *.jpg binary # Treat these files as text so we don;t get inconsistency with line endings between OS -tests/NHapi.NUnit/TestData/BadInputs/**/* -text \ No newline at end of file +tests/NHapi.NUnit/TestData/BadInputs/**/* -text +tests/NHapi.NUnit/TestData/Util/**/* -text \ No newline at end of file diff --git a/src/NHapi.Base/Parser/LegacyPipeParser.cs b/src/NHapi.Base/Parser/LegacyPipeParser.cs index 6bc1bd870..e84820528 100644 --- a/src/NHapi.Base/Parser/LegacyPipeParser.cs +++ b/src/NHapi.Base/Parser/LegacyPipeParser.cs @@ -265,7 +265,7 @@ public override void Parse(IMessage message, string @string, ParserOptions parse } var messageIter = new Util.MessageIterator(message, "MSH", true); - FilterIterator.IPredicate segmentsOnly = new AnonymousClassPredicate(this); + FilterIterator.IPredicate segmentsOnly = new IsSegmentPredicate(this); var segmentIter = new FilterIterator(messageIter, segmentsOnly); var segments = Split(@string, SegDelim); @@ -299,7 +299,7 @@ public override void Parse(IMessage message, string @string, ParserOptions parse Log.Debug("Parsing segment " + name); messageIter.Direction = name; - FilterIterator.IPredicate byDirection = new AnonymousClassPredicate1(name, this); + FilterIterator.IPredicate byDirection = new ByDirectionPredicate(name, this); var dirIter = new FilterIterator(segmentIter, byDirection); if (dirIter.MoveNext()) @@ -939,46 +939,46 @@ public MessageStructure(string theMessageStructure, bool isExplicitlyDefined) public bool ExplicitlyDefined { get; } } - private class AnonymousClassPredicate : FilterIterator.IPredicate + private sealed class IsSegmentPredicate : FilterIterator.IPredicate { - public AnonymousClassPredicate(LegacyPipeParser enclosingInstance) + public IsSegmentPredicate(LegacyPipeParser enclosingInstance) { - Enclosing_Instance = enclosingInstance; + EnclosingInstance = enclosingInstance; } - public LegacyPipeParser Enclosing_Instance { get; } + public LegacyPipeParser EnclosingInstance { get; } - public virtual bool evaluate(object obj) + public bool evaluate(object obj) { return Evaluate(obj); } - public virtual bool Evaluate(object obj) + public bool Evaluate(object obj) { - return typeof(ISegment).IsAssignableFrom(obj.GetType()); + return obj is ISegment; } } - private class AnonymousClassPredicate1 : FilterIterator.IPredicate + private sealed class ByDirectionPredicate : FilterIterator.IPredicate { - public AnonymousClassPredicate1(string name, LegacyPipeParser enclosingInstance) + public ByDirectionPredicate(string name, LegacyPipeParser enclosingInstance) { Name = name; - Enclosing_Instance = enclosingInstance; + EnclosingInstance = enclosingInstance; } - public LegacyPipeParser Enclosing_Instance { get; } + public LegacyPipeParser EnclosingInstance { get; } private string Name { get; } /// - public virtual bool evaluate(object obj) + public bool evaluate(object obj) { return Evaluate(obj); } /// - public virtual bool Evaluate(object obj) + public bool Evaluate(object obj) { var structureName = ((IStructure)obj).GetStructureName(); Log.Debug($"LegacyPipeParser iterating message in direction {Name} at {structureName}"); diff --git a/src/NHapi.Base/Util/MessageNavigator.cs b/src/NHapi.Base/Util/MessageNavigator.cs index 3231a34ee..afe115133 100644 --- a/src/NHapi.Base/Util/MessageNavigator.cs +++ b/src/NHapi.Base/Util/MessageNavigator.cs @@ -28,6 +28,7 @@ namespace NHapi.Base.Util { using System; using System.Collections; + using System.Collections.Generic; using NHapi.Base.Model; @@ -48,7 +49,7 @@ namespace NHapi.Base.Util /// public class MessageNavigator { - private ArrayList ancestors; + private Stack ancestors; private int currentChild; // -1 means current structure is current group (special case used for root) private string[] childNames; @@ -112,16 +113,14 @@ public virtual void DrillDown(int childNumber, int rep) if (childNumber != -1) { var s = CurrentGroup.GetStructure(childNames[childNumber], rep); - if (!(s is IGroup)) + if (s is not IGroup group) { throw new HL7Exception("Can't drill into segment", ErrorCode.APPLICATION_INTERNAL_ERROR); } - var group = (IGroup)s; - // stack the current group and location var gc = new GroupContext(this, CurrentGroup, currentChild); - ancestors.Add(gc); + ancestors.Push(gc); CurrentGroup = group; } @@ -166,7 +165,7 @@ public virtual bool DrillUp() // pop the top group and resume search there if (ancestors.Count != 0) { - var gc = (GroupContext)SupportClass.StackSupport.Pop(ancestors); + var gc = ancestors.Pop(); CurrentGroup = gc.Group; currentChild = gc.Child; childNames = CurrentGroup.Names; @@ -226,22 +225,23 @@ public virtual void NextChild() "StyleCop.CSharp.NamingRules", "SA1300:Element should begin with upper-case letter", Justification = "As this is a public member, we will duplicate the method and mark this one as obsolete.")] - public virtual void toChild(int child) + public virtual string toChild(int child) { - ToChild(child); + return ToChild(child); } /// Moves to the sibling of the current location at the specified index. - public virtual void ToChild(int child) + public virtual string ToChild(int child) { if (child >= 0 && child < childNames.Length) { currentChild = child; + return this.childNames[child]; } else { throw new HL7Exception( - "Can't advance to child " + child + " -- only " + childNames.Length + " children", + $"Can't advance to child {child} -- only {childNames.Length} children", ErrorCode.APPLICATION_INTERNAL_ERROR); } } @@ -259,7 +259,7 @@ public virtual void reset() /// Resets the location to the beginning of the tree (the root). public virtual void Reset() { - ancestors = new ArrayList(); + ancestors = new Stack(); CurrentGroup = Root; currentChild = -1; childNames = CurrentGroup.Names; @@ -280,18 +280,13 @@ public virtual IStructure getCurrentStructure(int rep) /// public virtual IStructure GetCurrentStructure(int rep) { - IStructure ret = null; - if (currentChild != -1) - { - var childName = childNames[currentChild]; - ret = CurrentGroup.GetStructure(childName, rep); - } - else + if (currentChild == -1) { - ret = CurrentGroup; + return CurrentGroup; } - return ret; + var childName = childNames[currentChild]; + return this.CurrentGroup.GetStructure(childName, rep); } [Obsolete("This method has been replaced by 'Iterate'.")] @@ -315,36 +310,30 @@ public virtual void iterate(bool segmentsOnly, bool loop) /// if true, loops back to beginning when end of msg reached; if false, /// throws HL7Exception if end of msg reached. /// - public virtual void Iterate(bool segmentsOnly, bool loop) + public virtual string Iterate(bool segmentsOnly, bool loop) { - IStructure start = null; - - if (currentChild == -1) - { - start = CurrentGroup; - } - else - { - start = CurrentGroup.GetStructure(childNames[currentChild]); - } + var start = currentChild == -1 + ? CurrentGroup + : CurrentGroup.GetStructure(childNames[currentChild]); // using a non-existent direction and not allowing segment creation means that only // the first rep of anything is traversed. IEnumerator it = new MessageIterator(start, "doesn't exist", false); if (segmentsOnly) { - FilterIterator.IPredicate predicate = new AnonymousClassPredicate(this); + FilterIterator.IPredicate predicate = new IsSegmentPredicate(this); it = new FilterIterator(it, predicate); } if (it.MoveNext()) { var next = (IStructure)it.Current; - DrillHere(next); + return DrillHere(next); } else if (loop) { Reset(); + return string.Empty; } else { @@ -355,19 +344,19 @@ public virtual void Iterate(bool segmentsOnly, bool loop) } /// Navigates to a specific location in the message. - private void DrillHere(IStructure destination) + private string DrillHere(IStructure destination) { var pathElem = destination; - var pathStack = new ArrayList(); - var indexStack = new ArrayList(); + var pathStack = new Stack(); + var indexStack = new Stack(); do { var index = MessageIterator.GetIndex(pathElem.ParentStructure, pathElem); - indexStack.Add(index); + indexStack.Push(index); pathElem = pathElem.ParentStructure; - pathStack.Add(pathElem); + pathStack.Push(pathElem); } - while (!Root.Equals(pathElem) && !typeof(IMessage).IsAssignableFrom(pathElem.GetType())); + while (!Root.Equals(pathElem) && pathElem is not IMessage); if (!Root.Equals(pathElem)) { @@ -375,10 +364,11 @@ private void DrillHere(IStructure destination) } Reset(); + string result = null; while (pathStack.Count != 0) { - var parent = (IGroup)SupportClass.StackSupport.Pop(pathStack); - var index = (MessageIterator.Index)SupportClass.StackSupport.Pop(indexStack); + var parent = (IGroup)pathStack.Pop(); + var index = indexStack.Pop(); var child = Search(parent.Names, index.Name); if (pathStack.Count != 0) { @@ -386,15 +376,17 @@ private void DrillHere(IStructure destination) } else { - ToChild(child); + result = ToChild(child); } } + + return result; } /// Like Arrays.binarySearch, only probably slower and doesn't require /// a sorted list. Also just returns -1 if item isn't found. /// - private int Search(object[] list, object item) + private int Search(string[] list, object item) { var found = -1; for (var i = 0; i < list.Length && found == -1; i++) @@ -408,59 +400,43 @@ private int Search(object[] list, object item) return found; } - /// Drills down recursively until a segment is reached. - private void FindLeaf() - { - if (currentChild == -1) - { - currentChild = 0; - } - - var c = CurrentGroup.GetClass(childNames[currentChild]); - if (typeof(IGroup).IsAssignableFrom(c)) - { - DrillDown(currentChild, 0); - FindLeaf(); - } - } - /// A structure to hold current location information at /// one level of the message tree. A stack of these /// identifies the current location completely. /// private class GroupContext { - public GroupContext(MessageNavigator enclosingInstance, IGroup g, int c) + public GroupContext(MessageNavigator enclosingInstance, IGroup group, int child) { EnclosingInstance = enclosingInstance; - Group = g; - Child = c; + Group = group; + Child = child; } public MessageNavigator EnclosingInstance { get; } - public IGroup Group { get; set; } + public IGroup Group { get; } - public int Child { get; set; } + public int Child { get; } } - private class AnonymousClassPredicate : FilterIterator.IPredicate + private sealed class IsSegmentPredicate : FilterIterator.IPredicate { - public AnonymousClassPredicate(MessageNavigator enclosingInstance) + public IsSegmentPredicate(MessageNavigator enclosingInstance) { EnclosingInstance = enclosingInstance; } public MessageNavigator EnclosingInstance { get; } - public virtual bool evaluate(object obj) + public bool evaluate(object obj) { return Evaluate(obj); } - public virtual bool Evaluate(object obj) + public bool Evaluate(object obj) { - return typeof(ISegment).IsAssignableFrom(obj.GetType()); + return obj is ISegment; } } } diff --git a/src/NHapi.Base/Util/SegmentFinder.cs b/src/NHapi.Base/Util/SegmentFinder.cs index e33c0cacd..e7b9bc299 100644 --- a/src/NHapi.Base/Util/SegmentFinder.cs +++ b/src/NHapi.Base/Util/SegmentFinder.cs @@ -36,6 +36,24 @@ namespace NHapi.Base.Util /// public class SegmentFinder : MessageNavigator { + [System.Diagnostics.CodeAnalysis.SuppressMessage( + "StyleCop.CSharp.NamingRules", + "SA1310:Field names should not contain underscore", + Justification = "Because these are constants and not just fields there is a rule clash.")] + private const string VALID_PATTERN = "[\\w\\*\\?]*"; + + [System.Diagnostics.CodeAnalysis.SuppressMessage( + "StyleCop.CSharp.NamingRules", + "SA1310:Field names should not contain underscore", + Justification = "Because these are constants and not just fields there is a rule clash.")] + private const string LITERAL_UNBOUNDED = "\\*"; + + [System.Diagnostics.CodeAnalysis.SuppressMessage( + "StyleCop.CSharp.NamingRules", + "SA1310:Field names should not contain underscore", + Justification = "Because these are constants and not just fields there is a rule clash.")] + private const string LITERAL_OPTIONAL = "\\?"; + /// Creates a new instance of SegmentFinder. /// the scope of searches -- may be a whole message or only a branch. /// @@ -67,12 +85,12 @@ public virtual ISegment findSegment(string namePattern, int rep) /// the repetition of the segment to return. public virtual ISegment FindSegment(string namePattern, int rep) { - IStructure s = null; + IStructure s; do { s = FindStructure(namePattern, rep); } - while (!typeof(ISegment).IsAssignableFrom(s.GetType())); + while (s is not ISegment); return (ISegment)s; } @@ -90,12 +108,12 @@ public virtual IGroup findGroup(string namePattern, int rep) /// As findSegment(), but will only return a group. public virtual IGroup FindGroup(string namePattern, int rep) { - IStructure s = null; + IStructure s; do { s = FindStructure(namePattern, rep); } - while (!typeof(IGroup).IsAssignableFrom(s.GetType())); + while (s is not IGroup); return (IGroup)s; } @@ -126,12 +144,12 @@ public virtual ISegment getSegment(string namePattern, int rep) public virtual ISegment GetSegment(string namePattern, int rep) { var s = GetStructure(namePattern, rep); - if (!typeof(ISegment).IsAssignableFrom(s.GetType())) + if (s is not ISegment segment) { - throw new HL7Exception(s.GetStructureName() + " is not a segment", ErrorCode.APPLICATION_INTERNAL_ERROR); + throw new HL7Exception($"{s.GetStructureName()} is not a segment", ErrorCode.APPLICATION_INTERNAL_ERROR); } - return (ISegment)s; + return segment; } [Obsolete("This method has been replaced by 'GetGroup'.")] @@ -148,12 +166,12 @@ public virtual IGroup getGroup(string namePattern, int rep) public virtual IGroup GetGroup(string namePattern, int rep) { var s = GetStructure(namePattern, rep); - if (!typeof(IGroup).IsAssignableFrom(s.GetType())) + if (s is not IGroup group) { - throw new HL7Exception(s.GetStructureName() + " is not a group", ErrorCode.APPLICATION_INTERNAL_ERROR); + throw new HL7Exception($"{s.GetStructureName()} is not a group", ErrorCode.APPLICATION_INTERNAL_ERROR); } - return (IGroup)s; + return group; } /// Returns the first matching structure AFTER the current position. @@ -163,9 +181,9 @@ private IStructure FindStructure(string namePattern, int rep) while (s == null) { - Iterate(false, false); + var currentNameInParent = Iterate(false, false); var currentName = GetCurrentStructure(0).GetStructureName(); - if (Matches(namePattern, currentName)) + if (Matches(namePattern, currentName) || Matches(namePattern, currentNameInParent)) { s = GetCurrentStructure(rep); } @@ -195,7 +213,7 @@ private IStructure GetStructure(string namePattern, int rep) if (s == null) { - throw new HL7Exception("Can't find " + namePattern + " as a direct child", ErrorCode.APPLICATION_INTERNAL_ERROR); + throw new HL7Exception($"Can't find {namePattern} as a direct child", ErrorCode.APPLICATION_INTERNAL_ERROR); } return s; @@ -210,15 +228,15 @@ private bool Matches(string pattern, string candidate) return true; } - if (!Regex.IsMatch(pattern, "[\\w\\*\\?]*")) + if (!Regex.IsMatch(pattern, VALID_PATTERN)) { - throw new ArgumentException("The pattern " + pattern + " is not valid. Only [\\w\\*\\?]* allowed."); + throw new ArgumentException($"The pattern {pattern} is not valid. Only [\\w\\*\\?]* allowed."); } - pattern = Regex.Replace(pattern, "\\*", ".*"); - pattern = Regex.Replace(pattern, "\\?", "."); + pattern = Regex.Replace(pattern, LITERAL_UNBOUNDED, ".*"); + pattern = Regex.Replace(pattern, LITERAL_OPTIONAL, "."); - return Regex.IsMatch(candidate, pattern); + return Regex.IsMatch(candidate, $"^{pattern}$"); } } } \ No newline at end of file diff --git a/src/NHapi.Base/Util/Terser.cs b/src/NHapi.Base/Util/Terser.cs index b8958aa78..6175cab9b 100644 --- a/src/NHapi.Base/Util/Terser.cs +++ b/src/NHapi.Base/Util/Terser.cs @@ -192,15 +192,14 @@ public static IPrimitive GetPrimitive(IType type, int component, int subcomponen var comp = GetComponent(type, component); - if (type is Varies && comp is GenericPrimitive && subcomponent > 1) + if (type is Varies varies && comp is GenericPrimitive && subcomponent > 1) { try { - var varies = (Varies)type; var comp2 = new GenericComposite(type.Message); varies.Data = comp2; - comp = GetComponent(type, component); + comp = GetComponent(varies, component); } catch (DataTypeException ex) { @@ -237,10 +236,10 @@ public static int[] GetIndices(string spec) tok.NextToken(); // skip over segment if (!tok.HasMoreTokens()) { - throw new HL7Exception("Must specify field in spec " + spec, ErrorCode.APPLICATION_INTERNAL_ERROR); + throw new HL7Exception($"Must specify field in spec {spec}", ErrorCode.APPLICATION_INTERNAL_ERROR); } - int[] ret = null; + int[] ret; try { var fieldSpec = new SupportClass.Tokenizer(tok.NextToken(), "()", false); @@ -263,12 +262,12 @@ public static int[] GetIndices(string spec) subcomponent = int.Parse(tok.NextToken()); } - var result = new int[] { fieldNum, fieldRep, component, subcomponent }; + var result = new[] { fieldNum, fieldRep, component, subcomponent }; ret = result; } catch (FormatException) { - throw new HL7Exception("Invalid integer in spec " + spec, ErrorCode.APPLICATION_INTERNAL_ERROR); + throw new HL7Exception($"Invalid integer in spec {spec}", ErrorCode.APPLICATION_INTERNAL_ERROR); } return ret; @@ -293,21 +292,21 @@ public static int numSubComponents(IType type, int component) /// numbered from 1. public static int NumSubComponents(IType type, int component) { - var n = -1; - if (component == 1 && typeof(IPrimitive).IsAssignableFrom(type.GetType())) + int subComponentTotal; + if (component == 1 && type is IPrimitive) { // note that getComponent(primitive, 1) below returns the primitive // itself -- if we do numComponents on it, we'll end up with the // number of components in the field, not the number of subcomponents - n = 1; + subComponentTotal = 1; } else { var comp = GetComponent(type, component); - n = NumComponents(comp); + subComponentTotal = NumComponents(comp); } - return n; + return subComponentTotal; } [Obsolete("This method has been replaced by 'NumComponents'.")] @@ -326,13 +325,16 @@ public static int numComponents(IType type) /// public static int NumComponents(IType type) { - if (typeof(Varies).IsAssignableFrom(type.GetType())) + while (true) { - return NumComponents(((Varies)type).Data); - } - else - { - return NumStandardComponents(type) + type.ExtraComponents.NumComponents(); + if (type is Varies varies) + { + type = varies.Data; + } + else + { + return NumStandardComponents(type) + type.ExtraComponents.NumComponents(); + } } } @@ -368,7 +370,7 @@ public virtual ISegment getSegment(string segSpec) /// Returns the segment specified in the given segment_path_spec. public virtual ISegment GetSegment(string segSpec) { - ISegment seg = null; + ISegment segment = null; if (segSpec.Substring(0, 1 - 0).Equals("/")) { @@ -381,47 +383,29 @@ public virtual ISegment GetSegment(string segSpec) { var pathSpec = tok.NextToken(); var ps = ParsePathSpec(pathSpec); - if (tok.HasMoreTokens()) - { - ps.IsGroup = true; - } - else - { - ps.IsGroup = false; - } + ps.IsGroup = tok.HasMoreTokens(); if (ps.IsGroup) { - IGroup g = null; - if (ps.Find) - { - g = finder.FindGroup(ps.Pattern, ps.Rep); - } - else - { - g = finder.GetGroup(ps.Pattern, ps.Rep); - } + var group = ps.Find + ? finder.FindGroup(ps.Pattern, ps.Rep) + : finder.GetGroup(ps.Pattern, ps.Rep); - finder = new SegmentFinder(g); + finder = new SegmentFinder(group); } else { - if (ps.Find) - { - seg = finder.FindSegment(ps.Pattern, ps.Rep); - } - else - { - seg = finder.GetSegment(ps.Pattern, ps.Rep); - } + segment = ps.Find + ? finder.FindSegment(ps.Pattern, ps.Rep) + : finder.GetSegment(ps.Pattern, ps.Rep); } } - return seg; + return segment; } /// Sets the string value of the field specified. See class docs for location spec syntax. - public virtual void Set(string spec, string value_Renamed) + public virtual void Set(string spec, string valueRenamed) { var tok = new SupportClass.Tokenizer(spec, "-", false); var segment = GetSegment(tok.NextToken()); @@ -429,11 +413,10 @@ public virtual void Set(string spec, string value_Renamed) var ind = GetIndices(spec); if (log.DebugEnabled) { - log.Debug("Setting " + spec + " seg: " + segment.GetStructureName() + " ind: " + ind[0] + " " + ind[1] + " " + - ind[2] + " " + ind[3]); + log.Debug($"Setting {spec} seg: {segment.GetStructureName()} ind: {ind[0]} {ind[1]} {ind[2]} {ind[3]}"); } - Set(segment, ind[0], ind[1], ind[2], ind[3], value_Renamed); + Set(segment, ind[0], ind[1], ind[2], ind[3], valueRenamed); } /// @@ -451,24 +434,23 @@ private static IPrimitive GetPrimitive(ISegment segment, int field, int rep, int /// private static IPrimitive GetPrimitive(IType type) { - if (type is IPrimitive) + switch (type) { - return (IPrimitive)type; - } + case IPrimitive primitive: + return primitive; + case IComposite composite: + try + { + return GetPrimitive(composite[0]); + } + catch (HL7Exception e) + { + throw new ApplicationException("Internal error: HL7Exception thrown on Composite.getComponent(0).", e); + } - if (type is IComposite) - { - try - { - return GetPrimitive(((IComposite)type)[0]); - } - catch (HL7Exception e) - { - throw new ApplicationException("Internal error: HL7Exception thrown on Composite.getComponent(0).", e); - } + default: + return GetPrimitive(((Varies)type).Data); } - - return GetPrimitive(((Varies)type).Data); } /// Returns the component (or sub-component, as the case may be) at the given @@ -485,13 +467,13 @@ private static IType GetComponent(IType type, int comp) return type; } - if (type is IComposite) + if (type is IComposite composite) { - if (comp <= NumStandardComponents(type) || type is GenericComposite) + if (comp <= NumStandardComponents(composite) || composite is GenericComposite) { try { - return ((IComposite)type)[comp - 1]; + return composite[comp - 1]; } catch (DataTypeException ex) { @@ -502,48 +484,39 @@ private static IType GetComponent(IType type, int comp) } } - if (type is Varies) + if (type is Varies varies) { - var v = (Varies)type; - try { - if (comp > 1 && v.Data is GenericPrimitive) + if (comp > 1 && varies.Data is GenericPrimitive) { - v.Data = new GenericComposite(v.Message); + varies.Data = new GenericComposite(varies.Message); } } catch (DataTypeException de) { - var message = "Unexpected exception copying data to generic composite: " + de.Message; + var message = $"Unexpected exception copying data to generic composite: {de.Message}"; log.Error(message, de); throw new ApplicationException(message); } - return GetComponent(v.Data, comp); + return GetComponent(varies.Data, comp); } return type.ExtraComponents.getComponent(comp - NumStandardComponents(type) - 1); } - private static int NumStandardComponents(IType t) + private static int NumStandardComponents(IType type) { - var n = 0; - if (typeof(Varies).IsAssignableFrom(t.GetType())) - { - n = NumStandardComponents(((Varies)t).Data); - } - else if (typeof(IComposite).IsAssignableFrom(t.GetType())) + var length = type switch { - n = ((IComposite)t).Components.Length; - } - else - { - n = 1; - } + Varies varies => NumStandardComponents(varies.Data), + IComposite composite => composite.Components.Length, + _ => 1 + }; - return n; + return length; } /// Gets path information from a path spec. @@ -577,7 +550,7 @@ private PathSpec ParsePathSpec(string spec) } catch (FormatException) { - throw new HL7Exception(repString + " is not a valid rep #", ErrorCode.APPLICATION_INTERNAL_ERROR); + throw new HL7Exception($"{repString} is not a valid rep #", ErrorCode.APPLICATION_INTERNAL_ERROR); } } else diff --git a/tests/NHapi.NUnit/NHapi.NUnit.csproj b/tests/NHapi.NUnit/NHapi.NUnit.csproj index ea4bf4ebe..972e751f1 100644 --- a/tests/NHapi.NUnit/NHapi.NUnit.csproj +++ b/tests/NHapi.NUnit/NHapi.NUnit.csproj @@ -25,6 +25,9 @@ PreserveNewest + + PreserveNewest + diff --git a/tests/NHapi.NUnit/TestData/Util/segmentfinder_a24.hl7 b/tests/NHapi.NUnit/TestData/Util/segmentfinder_a24.hl7 new file mode 100644 index 000000000..ab1c38870 --- /dev/null +++ b/tests/NHapi.NUnit/TestData/Util/segmentfinder_a24.hl7 @@ -0,0 +1,4 @@ +MSH^~|\&^SENDING^200M^RECEIVING^200^20120918173931-0500^^ADT~A24^2003582144^T^2.4^^^AL^AL^ +EVN^A24^20120918173931-0500 +PID^1^^11111~~~FOO&&0363~NI~FACILITY ID&200M&L^^SMITH~JOHN~TEST +PID^2^^22222~~~FOO&&0363~NI~FACILITY ID&200M&L^^SMITH~JOHN~TEST diff --git a/tests/NHapi.NUnit/Util/MessageNavigatorTests.cs b/tests/NHapi.NUnit/Util/MessageNavigatorTests.cs new file mode 100644 index 000000000..88184f3d4 --- /dev/null +++ b/tests/NHapi.NUnit/Util/MessageNavigatorTests.cs @@ -0,0 +1,86 @@ +namespace NHapi.NUnit.Util +{ + using global::NUnit.Framework; + + using NHapi.Base.Util; + using NHapi.Model.V24.Message; + using NHapi.Model.V24.Segment; + + [TestFixture] + public class MessageNavigatorTests + { + [Test] + public void TestNavigate() + { + // Arrange + var message = new ADT_A01(); + message.GetINSURANCE().IN1.CoverageType.Value = "a"; + message.MSH.CountryCode.Value = "b"; + message.PD1.Handicap.Value = "c"; + + var sut = new MessageNavigator(message); + + // Act + sut.DrillDown(16, 0); + var in1 = (IN1)sut.GetCurrentStructure(0); + + sut.Reset(); + sut.NextChild(); + var msh = (MSH)sut.GetCurrentStructure(0); + + sut.Reset(); + sut.NextChild(); + sut.NextChild(); + sut.NextChild(); + sut.NextChild(); + var pd1 = (PD1)sut.GetCurrentStructure(0); + + // Assert + Assert.AreEqual("a", in1.CoverageType.Value); + Assert.AreEqual("b", msh.CountryCode.Value); + Assert.AreEqual("a", in1.CoverageType.Value); + Assert.AreEqual("c", pd1.Handicap.Value); + } + + [Test] + public void TestIterator() + { + // Arrange + var message = new ADT_A01(); + message.GetINSURANCE().IN1.CoverageType.Value = "a"; + message.MSH.CountryCode.Value = "b"; + message.PD1.Handicap.Value = "c"; + + var sut = new MessageNavigator(message); + + // Act + sut.Iterate(true, false); + sut.Iterate(true, false); + sut.Iterate(true, false); + sut.Iterate(true, false); + sut.Iterate(true, false); + sut.Iterate(true, false); + sut.Iterate(true, false); + sut.Iterate(true, false); + sut.Iterate(true, false); + sut.Iterate(true, false); + sut.Iterate(true, false); + sut.Iterate(true, false); + sut.Iterate(true, false); + sut.Iterate(true, false); + sut.Iterate(true, false); + sut.Iterate(true, false); + sut.Iterate(true, false); + sut.Iterate(true, false); + var in1 = (IN1)sut.GetCurrentStructure(0); + + sut.Reset(); + sut.ToChild(3); + var pd1 = (PD1)sut.GetCurrentStructure(0); + + // Assert + Assert.AreEqual("a", in1.CoverageType.Value); + Assert.AreEqual("c", pd1.Handicap.Value); + } + } +} \ No newline at end of file diff --git a/tests/NHapi.NUnit/Util/SegmentFinderTests.cs b/tests/NHapi.NUnit/Util/SegmentFinderTests.cs new file mode 100644 index 000000000..afaa4d0ed --- /dev/null +++ b/tests/NHapi.NUnit/Util/SegmentFinderTests.cs @@ -0,0 +1,147 @@ +namespace NHapi.NUnit.Util +{ + using System.IO; + + using global::NUnit.Framework; + + using NHapi.Base.Model; + using NHapi.Base.Parser; + using NHapi.Base.Util; + using NHapi.Model.V24.Message; + using NHapi.Model.V26.Group; + using NHapi.Model.V26.Segment; + + using ORU_R01 = NHapi.Model.V26.Message.ORU_R01; + using ROL = NHapi.Model.V24.Segment.ROL; + + [TestFixture] + public class SegmentFinderTests + { + [Test] + public void FindSegment_ReturnsExpectedValue() + { + // Arrange + var message = new ADT_A01(); + message.GetROL().ActionCode.Value = "a"; + message.GetROL2().ActionCode.Value = "b"; + message.GetPROCEDURE().GetROL().ActionCode.Value = "c"; + message.GetINSURANCE().GetROL().ActionCode.Value = "d"; + + var finder = new SegmentFinder(message); + + // Act + var first = ((ROL)finder.FindSegment("ROL", 0)).ActionCode.Value; + var second = ((ROL)finder.FindSegment("ROL", 0)).ActionCode.Value; + var third = ((ROL)finder.FindSegment("ROL", 0)).ActionCode.Value; + var fourth = ((ROL)finder.FindSegment("ROL", 0)).ActionCode.Value; + + // Assert + Assert.AreEqual("a", first); + Assert.AreEqual("b", second); + Assert.AreEqual("c", third); + Assert.AreEqual("d", fourth); + } + + [Test] + public void GetSegment_ReturnExpectedValue() + { + // Arrange + var r01 = new ORU_R01(); + r01.MSH.MessageType.MessageStructure.Value = "ORU"; + r01.MSH.MessageType.TriggerEvent.Value = "R01"; + r01.MSH.FieldSeparator.Value = "|"; + r01.MSH.EncodingCharacters.Value = @"^~\&"; + r01.MSH.VersionID.VersionID.Value = "2.4"; + r01.MSH.ProcessingID.ProcessingID.Value = "T"; + + r01.GetSFT(0).SoftwareVendorOrganization.OrganizationName.Value = "A"; + r01.GetSFT(1).SoftwareVendorOrganization.OrganizationName.Value = "B"; + r01.GetSFT(2).SoftwareVendorOrganization.OrganizationName.Value = "C"; + + var encodingCharacters = EncodingCharacters.FromMessage(r01); + + var finder = new SegmentFinder(r01); + + // Act + var sft1 = (SFT)finder.GetSegment("SFT", 0); + var sft2 = (SFT)finder.GetSegment("SFT", 1); + var sft3 = (SFT)finder.GetSegment("SFT", 2); + + // Assert + Assert.AreEqual("A", PipeParser.Encode(sft1.SoftwareVendorOrganization, encodingCharacters)); + Assert.AreEqual("B", PipeParser.Encode(sft2.SoftwareVendorOrganization, encodingCharacters)); + Assert.AreEqual("C", PipeParser.Encode(sft3.SoftwareVendorOrganization, encodingCharacters)); + } + + [Test] + public void FindGroup_ReturnsExpectedValue() + { + // Arrange + var r01 = new ORU_R01(); + r01.MSH.MessageType.MessageStructure.Value = "ORU"; + r01.MSH.MessageType.TriggerEvent.Value = "R01"; + r01.MSH.FieldSeparator.Value = "|"; + r01.MSH.EncodingCharacters.Value = @"^~\&"; + r01.MSH.VersionID.VersionID.Value = "2.4"; + r01.MSH.ProcessingID.ProcessingID.Value = "T"; + + r01.GetPATIENT_RESULT().PATIENT.PID.SetIDPID.Value = "1"; + r01.GetPATIENT_RESULT().GetORDER_OBSERVATION(0).OBR.SetIDOBR.Value = "2"; + r01.GetPATIENT_RESULT().GetORDER_OBSERVATION(1).OBR.SetIDOBR.Value = "3"; + r01.GetPATIENT_RESULT().GetORDER_OBSERVATION(2).OBR.SetIDOBR.Value = "4"; + + var encodingCharacters = EncodingCharacters.FromMessage(r01); + + var finder = new SegmentFinder(r01); + + // Act + var pid = (PID)finder.FindSegment("PID", 0); + finder = new SegmentFinder(r01); + var patient = (ORU_R01_PATIENT)finder.FindGroup("PATIENT", 0); + + var orderObservation = (ORU_R01_ORDER_OBSERVATION)finder.FindGroup("ORDER_OBSERVATION", 0); + + // Assert + Assert.AreEqual("1", PipeParser.Encode(pid.SetIDPID, encodingCharacters)); + Assert.AreEqual("1", PipeParser.Encode(patient.PID.SetIDPID, encodingCharacters)); + + Assert.AreEqual("2", PipeParser.Encode(orderObservation.OBR.SetIDOBR, encodingCharacters)); + } + + [Test] + public void TestPID1() + { + // Arrange + var message = LoadMessage(); + var finder = new SegmentFinder(message); + + // Act + var segment = finder.FindSegment("PID", 0); + + // Assert + Assert.AreEqual("1", ((IPrimitive)segment.GetField(1, 0)).Value); + } + + [Test] + public void TestPID2() + { + // Arrange + var message = LoadMessage(); + var finder = new SegmentFinder(message); + + // Act + var segment = finder.FindSegment("PID2", 0); + + // Assert + Assert.AreEqual("2", ((IPrimitive)segment.GetField(1, 0)).Value); + } + + private static IMessage LoadMessage() + { + var parser = new PipeParser(); + var testDataDir = $"{TestContext.CurrentContext.TestDirectory}/TestData/Util/"; + var messageString = File.ReadAllText($"{testDataDir}/segmentfinder_a24.hl7"); + return parser.Parse(messageString); + } + } +} \ No newline at end of file diff --git a/tests/NHapi.NUnit/Util/TerserTests.cs b/tests/NHapi.NUnit/Util/TerserTests.cs index 385d64da8..df91c902e 100644 --- a/tests/NHapi.NUnit/Util/TerserTests.cs +++ b/tests/NHapi.NUnit/Util/TerserTests.cs @@ -224,6 +224,31 @@ public void TestPatientId() Assert.AreEqual(expected, terser.Get("/QUERY_RESPONSE(0)/.PID-3-1")); } + /// + /// https://github.com/nHapiNET/nHapi/issues/319 + /// + [Test] + public void Get_OMD_O03_ValidTerserPathSpecifaction_ReturnsExpectedResult() + { + var parser = new PipeParser(); + + var omdText = "MSH|^~\\&|EDITE|TEST|TEST|TEST|20210519141200||OMD^O03^OMD_O03|202105191412001|P|2.5|||||FRA|8859/1\r" + + "PID|1|465 306 5961||407623|Wood^Patrick^^^MR||19700101|1|||High Street^^Oxford^^Ox1 4DP~George St ^^Oxford^^Ox1 5AP|||||||\r" + + "ORC|NW|1254481^EDITEUR|||||^^^20210519143000||20210519143000|611014333^PRESCRIPTEUR^TEST1^^^^^^EDITEUR^^^^ADELI~10002129790^PRESCRIPTEUR^TEST1 ^^^^^^EDITEUR ^^^^RPPS~1027^PRESCRIPTEUR^TEST1^^^^^^EDITEUR^^^^EI||||||||||||||||||||\r" + + "TQ1|1||||||20210519143000|\r" + + "ODS|R||SANSSEL^Sans sel^EDITEUR|"; + + var parsed = parser.Parse(omdText); + + var terser = new Terser(parsed); + + var tq17 = terser.Get("/.ORDER_DIET(0)/TIMING_DIET/TQ1-7"); + var ods1 = terser.Get("/.ORDER_DIET(0)/DIET/ODS(0)-1"); + + Assert.AreEqual("20210519143000", tq17); + Assert.AreEqual("R", ods1); + } + #region Static Methods [Test] @@ -296,4 +321,4 @@ public void TestMultiArg() #endregion } -} +} \ No newline at end of file