Skip to content

Commit

Permalink
Add 2 new Parser Options 'DefaultObx2Type' and 'InvalidObx2Type'
Browse files Browse the repository at this point in the history
  • Loading branch information
milkshakeuk committed Nov 22, 2021
1 parent ab256f7 commit 967a60e
Show file tree
Hide file tree
Showing 31 changed files with 773 additions and 36 deletions.
5 changes: 5 additions & 0 deletions src/NHapi.Base/Model/AbstractSegment.cs
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,8 @@ public virtual int GetMaxCardinality(int number)
/// </summary>
/// <param name="fieldNum">Repeatable field number.</param>
/// <param name="index">0-based index to be removed.</param>
/// <exception cref="HL7Exception">If field has no repetitions.</exception>
/// <exception cref="HL7Exception">If field index does not exist.</exception>
public void RemoveRepetition(int fieldNum, int index)
{
if (fieldNum < 1 || fieldNum > items.Count)
Expand Down Expand Up @@ -303,6 +305,9 @@ public void RemoveRepetition(int fieldNum, int index)
/// </summary>
/// <param name="fieldNum">Repeatable field number.</param>
/// <param name="removeItem">Item to be removed.</param>
/// <exception cref="HL7Exception">If field number is not valid.</exception>
/// <exception cref="HL7Exception">If field has no repetitions.</exception>
/// <exception cref="HL7Exception">If field does not contain the repetition to remove.</exception>
public void RemoveRepetition(int fieldNum, IType removeItem)
{
if (fieldNum < 1 || fieldNum > items.Count)
Expand Down
57 changes: 48 additions & 9 deletions src/NHapi.Base/Model/Varies.cs
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,8 @@ static Varies()
Log = HapiLogFactory.GetHapiLog(typeof(Varies));
}

/// <summary> Creates new Varies.
///
/// <summary>
/// Creates new Varies.
/// </summary>
/// <param name="message">message to which this type belongs.
/// </param>
Expand All @@ -75,8 +75,8 @@ public Varies(IMessage message)
Message = message;
}

/// <summary> Creates new Varies.
///
/// <summary>
/// Creates new Varies.
/// </summary>
/// <param name="message">message to which this type belongs.</param>
/// <param name="description">description of what this Type represents.</param>
Expand Down Expand Up @@ -139,15 +139,26 @@ public virtual IType Data
"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 static void fixOBX5(ISegment segment, IModelClassFactory factory)
public static void fixOBX5(ISegment segment, IModelClassFactory factory, ParserOptions parserOptions)
{
FixOBX5(segment, factory);
FixOBX5(segment, factory, parserOptions);
}

/// <summary> Sets the data type of field 5 in the given OBX segment to the value of OBX-2. The argument
/// <summary>
/// Sets the data type of field 5 in the given OBX segment to the value of OBX-2. The argument
/// is a Segment as opposed to a particular OBX because it is meant to work with any version.
/// <para>
/// Note that if no value is present in OBX-2, or an invalid value is present in
/// OBX-2, this method will throw an error.This behaviour can be corrected by using the
/// <see cref="ParserOptions.DefaultObx2Type"/> and <see cref="ParserOptions.InvalidObx2Type"/>.
/// </para>
/// </summary>
public static void FixOBX5(ISegment segment, IModelClassFactory factory)
/// <param name="segment"><see cref="ISegment"/> instance.</param>
/// <param name="factory"><see cref="IModelClassFactory"/> to be used.</param>
/// <param name="parserOptions"><see cref="ParserOptions"/> to be used.</param>
/// <exception cref="HL7Exception">If no value is present in OBX-2.</exception>
/// <exception cref="HL7Exception">If an invalid value is present in OBX-2.</exception>
public static void FixOBX5(ISegment segment, IModelClassFactory factory, ParserOptions parserOptions)
{
try
{
Expand All @@ -157,6 +168,7 @@ public static void FixOBX5(ISegment segment, IModelClassFactory factory)
foreach (var repetition in segment.GetField(5))
{
var v = (Varies)repetition;
SetObx2Fallback(obx2, segment, parserOptions);

if (obx2.Value == null)
{
Expand All @@ -174,7 +186,7 @@ public static void FixOBX5(ISegment segment, IModelClassFactory factory)
{
UseDTInsteadOfDTMForEarlierVersionsOfHL7(segment, obx2);

var type = factory.GetTypeClass(obx2.Value, segment.Message.Version);
var type = GetObx5Type(obx2, segment, factory, parserOptions);

if (type == null)
{
Expand Down Expand Up @@ -214,6 +226,33 @@ public static void FixOBX5(ISegment segment, IModelClassFactory factory)
}
}

private static Type GetObx5Type(IPrimitive obx2, ISegment segment, IModelClassFactory factory, ParserOptions parserOptions)
{
var type = factory.GetTypeClass(obx2.Value, segment.Message.Version);

if (type == null)
{
if (parserOptions.InvalidObx2Type != null)
{
type = factory.GetTypeClass(parserOptions.InvalidObx2Type, segment.Message.Version);
}
}

return type;
}

private static void SetObx2Fallback(IPrimitive obx2, ISegment segment, ParserOptions parserOptions)
{
if (obx2.Value == null)
{
if (!(parserOptions.DefaultObx2Type is null))
{
Log.Debug($"setting default {segment.GetStructureName()}-{2} type to {parserOptions.DefaultObx2Type}");
obx2.Value = parserOptions.DefaultObx2Type;
}
}
}

private static void UseDTInsteadOfDTMForEarlierVersionsOfHL7(ISegment segment, IPrimitive obx2)
{
var versionsWithoutDTM = new string[] { "2.1", "2.2", "2.3", "2.3.1", "2.4", "2.5" };
Expand Down
37 changes: 24 additions & 13 deletions src/NHapi.Base/Parser/DefaultXMLParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -142,23 +142,33 @@ public override XmlDocument EncodeDocument(IMessage source)
/// <throws> EncodingNotSupportedException if the message encoded. </throws>
/// <summary> is not supported by this parser.
/// </summary>
public override IMessage ParseDocument(XmlDocument xmlMessage, string version)
public override IMessage ParseDocument(XmlDocument xmlMessage, string version, ParserOptions parserOptions)
{
if (parserOptions is null)
{
throw new ArgumentNullException(nameof(parserOptions));
}

var messageName = xmlMessage.DocumentElement.Name;
var message = InstantiateMessage(messageName, version, true);
Parse(message, xmlMessage.DocumentElement);
Parse(message, xmlMessage.DocumentElement, parserOptions);
return message;
}

/// <inheritdoc />
public override void Parse(IMessage message, string @string, ParserOptions parserOptions)
{
if (parserOptions is null)
{
throw new ArgumentNullException(nameof(parserOptions));
}

try
{
var xmlDocument = new XmlDocument();
xmlDocument.Load(new StringReader(@string));

Parse(message, xmlDocument.DocumentElement);
Parse(message, xmlDocument.DocumentElement, parserOptions);
}
catch (XmlException e)
{
Expand Down Expand Up @@ -201,7 +211,7 @@ protected internal static string MakeGroupElementName(string messageName, string
/// <summary> Populates the given group object with data from the given group element, ignoring
/// any unrecognized nodes.
/// </summary>
private void Parse(IGroup groupObject, XmlElement groupElement)
private void Parse(IGroup groupObject, XmlElement groupElement, ParserOptions parserOptions)
{
var childNames = groupObject.Names;
var messageName = groupObject.Message.GetStructureName();
Expand All @@ -222,14 +232,14 @@ private void Parse(IGroup groupObject, XmlElement groupElement)
for (var i = 0; i < childNames.Length; i++)
{
SupportClass.ICollectionSupport.Remove(unparsedElementList, childNames[i]);
ParseReps(groupElement, groupObject, messageName, childNames[i], childNames[i]);
ParseReps(groupElement, groupObject, messageName, childNames[i], childNames[i], parserOptions);
}

for (var i = 0; i < unparsedElementList.Count; i++)
{
var segName = (string)unparsedElementList[i];
var segIndexName = groupObject.AddNonstandardSegment(segName);
ParseReps(groupElement, groupObject, messageName, segName, segIndexName);
ParseReps(groupElement, groupObject, messageName, segName, segIndexName, parserOptions);
}
}

Expand Down Expand Up @@ -284,7 +294,8 @@ private void Encode(IGroup groupObject, XmlElement groupElement)
IGroup groupObject,
string messageName,
string childName,
string childIndexName)
string childIndexName,
ParserOptions parserOptions)
{
var reps = GetChildElementsByTagName(groupElement, MakeGroupElementName(messageName, childName));
Log.Debug("# of elements matching " + MakeGroupElementName(messageName, childName) + ": " + reps.Count);
Expand All @@ -293,36 +304,36 @@ private void Encode(IGroup groupObject, XmlElement groupElement)
{
for (var i = 0; i < reps.Count; i++)
{
ParseRep((XmlElement)reps[i], groupObject.GetStructure(childIndexName, i));
ParseRep((XmlElement)reps[i], groupObject.GetStructure(childIndexName, i), parserOptions);
}
}
else
{
if (reps.Count > 0)
{
ParseRep((XmlElement)reps[0], groupObject.GetStructure(childIndexName, 0));
ParseRep((XmlElement)reps[0], groupObject.GetStructure(childIndexName, 0), parserOptions);
}

if (reps.Count > 1)
{
var newIndexName = groupObject.AddNonstandardSegment(childName);
for (var i = 1; i < reps.Count; i++)
{
ParseRep((XmlElement)reps[i], groupObject.GetStructure(newIndexName, i - 1));
ParseRep((XmlElement)reps[i], groupObject.GetStructure(newIndexName, i - 1), parserOptions);
}
}
}
}

private void ParseRep(XmlElement theElem, IStructure theObj)
private void ParseRep(XmlElement theElem, IStructure theObj, ParserOptions parserOptions)
{
if (theObj is IGroup)
{
Parse((IGroup)theObj, theElem);
Parse((IGroup)theObj, theElem, parserOptions);
}
else if (theObj is ISegment)
{
Parse((ISegment)theObj, theElem);
Parse((ISegment)theObj, theElem, parserOptions);
}

Log.Debug("Parsed element: " + theElem.Name);
Expand Down
13 changes: 10 additions & 3 deletions src/NHapi.Base/Parser/LegacyPipeParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,11 @@ public static string StripLeadingWhitespace(string in_Renamed)

public override void Parse(IMessage message, string @string, ParserOptions parserOptions)
{
if (parserOptions is null)
{
throw new ArgumentNullException(nameof(parserOptions));
}

var messageIter = new Util.MessageIterator(message, "MSH", true);
FilterIterator.IPredicate segmentsOnly = new AnonymousClassPredicate(this);
var segmentIter = new FilterIterator(messageIter, segmentsOnly);
Expand Down Expand Up @@ -299,7 +304,7 @@ public override void Parse(IMessage message, string @string, ParserOptions parse

if (dirIter.MoveNext())
{
Parse((ISegment)dirIter.Current, segments[i], encodingChars);
Parse((ISegment)dirIter.Current, segments[i], encodingChars, parserOptions);
}
}
}
Expand Down Expand Up @@ -391,8 +396,10 @@ public virtual string GetMessageStructure(string message)
/// <throws> HL7Exception if the given string does not contain the. </throws>
/// <summary> given segment or if the string is not encoded properly.
/// </summary>
public virtual void Parse(ISegment destination, string segment, EncodingCharacters encodingChars)
public virtual void Parse(ISegment destination, string segment, EncodingCharacters encodingChars, ParserOptions parserOptions = null)
{
parserOptions = parserOptions ?? DefaultParserOptions;

var fieldOffset = 0;
if (IsDelimDefSegment(destination.GetStructureName()))
{
Expand Down Expand Up @@ -451,7 +458,7 @@ public virtual void Parse(ISegment destination, string segment, EncodingCharacte
// set data type of OBX-5
if (destination.GetType().FullName.IndexOf("OBX") >= 0)
{
Varies.FixOBX5(destination, Factory);
Varies.FixOBX5(destination, Factory, parserOptions);
}
}

Expand Down
7 changes: 6 additions & 1 deletion src/NHapi.Base/Parser/ParserBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ namespace NHapi.Base.Parser
public abstract class ParserBase
{
private static readonly IHapiLog Log;
private static readonly ParserOptions DefaultParserOptions = new ParserOptions();
protected static readonly ParserOptions DefaultParserOptions = new ParserOptions();

private IValidationContext validationContext;
private MessageValidator messageValidator;
Expand Down Expand Up @@ -265,6 +265,11 @@ public virtual IMessage Parse(string message, string version)
/// <exception cref="ArgumentNullException">If <paramref name="parserOptions"/> is null.</exception>
public virtual IMessage Parse(string message, string version, ParserOptions parserOptions)
{
if (parserOptions is null)
{
throw new ArgumentNullException(nameof(parserOptions));
}

var encoding = GetEncoding(message);
if (!SupportsEncoding(encoding))
{
Expand Down
42 changes: 42 additions & 0 deletions src/NHapi.Base/Parser/ParserOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,51 @@ public class ParserOptions
{
public ParserOptions()
{
DefaultObx2Type = null;
InvalidObx2Type = null;
NonGreedyMode = false;
}

/// <summary>
/// If this property is set, the value provides a default datatype ("ST",
/// "NM", etc) for an OBX segment with a missing OBX-2 value. This is useful
/// when parsing messages from systems which do not correctly populate OBX-2.
/// </summary>
/// <example>
/// <para>
/// For example, if this property is set to "ST", and the following OBX
/// segment is encountered:
/// <code>
/// OBX|||||This is a value
/// </code>
/// It will be parsed as though it had read:
/// <code>
/// OBX||ST|||This is a value
/// </code>
/// </para>
/// </example>
public string DefaultObx2Type { get; set; }

/// <summary>
/// If this property is set, the value provides a default datatype ("ST",
/// "NM", etc) for an OBX segment with an invalid OBX-2 value. This is useful
/// when parsing messages from systems which do not correctly populate OBX-2.
/// </summary>
/// <example>
/// <para>
/// For example, if this property is set to "ST", and the following OBX
/// segment is encountered:
/// <code>
/// OBX||INVALID|||This is a value
/// </code>
/// It will be parsed as though it had read:
/// <code>
/// OBX||ST|||This is a value
/// </code>
/// </para>
/// </example>
public string InvalidObx2Type { get; set; }

/// <summary>
/// If set to <c>true</c> (default is <c>false</c>), pipe parser will be put in non-greedy mode. This setting
/// applies only to <see cref="PipeParser"/> and will have no effect on <see cref="XMLParser"/>.
Expand Down
Loading

0 comments on commit 967a60e

Please sign in to comment.