Skip to content

Commit c9f2b1c

Browse files
committed
Add 2 new Parser Options 'DefaultObx2Type' and 'InvalidObx2Type'
1 parent ab256f7 commit c9f2b1c

31 files changed

+824
-35
lines changed

src/NHapi.Base/Model/AbstractSegment.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -270,6 +270,8 @@ public virtual int GetMaxCardinality(int number)
270270
/// </summary>
271271
/// <param name="fieldNum">Repeatable field number.</param>
272272
/// <param name="index">0-based index to be removed.</param>
273+
/// <exception cref="HL7Exception">If field has no repetitions.</exception>
274+
/// <exception cref="HL7Exception">If field index does not exist.</exception>
273275
public void RemoveRepetition(int fieldNum, int index)
274276
{
275277
if (fieldNum < 1 || fieldNum > items.Count)
@@ -303,6 +305,9 @@ public void RemoveRepetition(int fieldNum, int index)
303305
/// </summary>
304306
/// <param name="fieldNum">Repeatable field number.</param>
305307
/// <param name="removeItem">Item to be removed.</param>
308+
/// <exception cref="HL7Exception">If field number is not valid.</exception>
309+
/// <exception cref="HL7Exception">If field has no repetitions.</exception>
310+
/// <exception cref="HL7Exception">If field does not contain the repetition to remove.</exception>
306311
public void RemoveRepetition(int fieldNum, IType removeItem)
307312
{
308313
if (fieldNum < 1 || fieldNum > items.Count)

src/NHapi.Base/Model/Varies.cs

Lines changed: 48 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -64,8 +64,8 @@ static Varies()
6464
Log = HapiLogFactory.GetHapiLog(typeof(Varies));
6565
}
6666

67-
/// <summary> Creates new Varies.
68-
///
67+
/// <summary>
68+
/// Creates new Varies.
6969
/// </summary>
7070
/// <param name="message">message to which this type belongs.
7171
/// </param>
@@ -75,8 +75,8 @@ public Varies(IMessage message)
7575
Message = message;
7676
}
7777

78-
/// <summary> Creates new Varies.
79-
///
78+
/// <summary>
79+
/// Creates new Varies.
8080
/// </summary>
8181
/// <param name="message">message to which this type belongs.</param>
8282
/// <param name="description">description of what this Type represents.</param>
@@ -139,15 +139,26 @@ public virtual IType Data
139139
"StyleCop.CSharp.NamingRules",
140140
"SA1300:Element should begin with upper-case letter",
141141
Justification = "As this is a public member, we will duplicate the method and mark this one as obsolete.")]
142-
public static void fixOBX5(ISegment segment, IModelClassFactory factory)
142+
public static void fixOBX5(ISegment segment, IModelClassFactory factory, ParserOptions parserOptions)
143143
{
144-
FixOBX5(segment, factory);
144+
FixOBX5(segment, factory, parserOptions);
145145
}
146146

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

161173
if (obx2.Value == null)
162174
{
@@ -174,7 +186,7 @@ public static void FixOBX5(ISegment segment, IModelClassFactory factory)
174186
{
175187
UseDTInsteadOfDTMForEarlierVersionsOfHL7(segment, obx2);
176188

177-
var type = factory.GetTypeClass(obx2.Value, segment.Message.Version);
189+
var type = GetObx5Type(obx2, segment, factory, parserOptions);
178190

179191
if (type == null)
180192
{
@@ -214,6 +226,33 @@ public static void FixOBX5(ISegment segment, IModelClassFactory factory)
214226
}
215227
}
216228

229+
private static Type GetObx5Type(IPrimitive obx2, ISegment segment, IModelClassFactory factory, ParserOptions parserOptions)
230+
{
231+
var type = factory.GetTypeClass(obx2.Value, segment.Message.Version);
232+
233+
if (type == null)
234+
{
235+
if (parserOptions.InvalidObx2Type != null)
236+
{
237+
type = factory.GetTypeClass(parserOptions.InvalidObx2Type, segment.Message.Version);
238+
}
239+
}
240+
241+
return type;
242+
}
243+
244+
private static void SetObx2Fallback(IPrimitive obx2, ISegment segment, ParserOptions parserOptions)
245+
{
246+
if (obx2.Value == null)
247+
{
248+
if (!(parserOptions.DefaultObx2Type is null))
249+
{
250+
Log.Debug($"setting default {segment.GetStructureName()}-{2} type to {parserOptions.DefaultObx2Type}");
251+
obx2.Value = parserOptions.DefaultObx2Type;
252+
}
253+
}
254+
}
255+
217256
private static void UseDTInsteadOfDTMForEarlierVersionsOfHL7(ISegment segment, IPrimitive obx2)
218257
{
219258
var versionsWithoutDTM = new string[] { "2.1", "2.2", "2.3", "2.3.1", "2.4", "2.5" };

src/NHapi.Base/Parser/DefaultXMLParser.cs

Lines changed: 24 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -142,23 +142,33 @@ public override XmlDocument EncodeDocument(IMessage source)
142142
/// <throws> EncodingNotSupportedException if the message encoded. </throws>
143143
/// <summary> is not supported by this parser.
144144
/// </summary>
145-
public override IMessage ParseDocument(XmlDocument xmlMessage, string version)
145+
public override IMessage ParseDocument(XmlDocument xmlMessage, string version, ParserOptions parserOptions)
146146
{
147+
if (parserOptions is null)
148+
{
149+
throw new ArgumentNullException(nameof(parserOptions));
150+
}
151+
147152
var messageName = xmlMessage.DocumentElement.Name;
148153
var message = InstantiateMessage(messageName, version, true);
149-
Parse(message, xmlMessage.DocumentElement);
154+
Parse(message, xmlMessage.DocumentElement, parserOptions);
150155
return message;
151156
}
152157

153158
/// <inheritdoc />
154159
public override void Parse(IMessage message, string @string, ParserOptions parserOptions)
155160
{
161+
if (parserOptions is null)
162+
{
163+
throw new ArgumentNullException(nameof(parserOptions));
164+
}
165+
156166
try
157167
{
158168
var xmlDocument = new XmlDocument();
159169
xmlDocument.Load(new StringReader(@string));
160170

161-
Parse(message, xmlDocument.DocumentElement);
171+
Parse(message, xmlDocument.DocumentElement, parserOptions);
162172
}
163173
catch (XmlException e)
164174
{
@@ -201,7 +211,7 @@ protected internal static string MakeGroupElementName(string messageName, string
201211
/// <summary> Populates the given group object with data from the given group element, ignoring
202212
/// any unrecognized nodes.
203213
/// </summary>
204-
private void Parse(IGroup groupObject, XmlElement groupElement)
214+
private void Parse(IGroup groupObject, XmlElement groupElement, ParserOptions parserOptions)
205215
{
206216
var childNames = groupObject.Names;
207217
var messageName = groupObject.Message.GetStructureName();
@@ -222,14 +232,14 @@ private void Parse(IGroup groupObject, XmlElement groupElement)
222232
for (var i = 0; i < childNames.Length; i++)
223233
{
224234
SupportClass.ICollectionSupport.Remove(unparsedElementList, childNames[i]);
225-
ParseReps(groupElement, groupObject, messageName, childNames[i], childNames[i]);
235+
ParseReps(groupElement, groupObject, messageName, childNames[i], childNames[i], parserOptions);
226236
}
227237

228238
for (var i = 0; i < unparsedElementList.Count; i++)
229239
{
230240
var segName = (string)unparsedElementList[i];
231241
var segIndexName = groupObject.AddNonstandardSegment(segName);
232-
ParseReps(groupElement, groupObject, messageName, segName, segIndexName);
242+
ParseReps(groupElement, groupObject, messageName, segName, segIndexName, parserOptions);
233243
}
234244
}
235245

@@ -284,7 +294,8 @@ private void ParseReps(
284294
IGroup groupObject,
285295
string messageName,
286296
string childName,
287-
string childIndexName)
297+
string childIndexName,
298+
ParserOptions parserOptions)
288299
{
289300
var reps = GetChildElementsByTagName(groupElement, MakeGroupElementName(messageName, childName));
290301
Log.Debug("# of elements matching " + MakeGroupElementName(messageName, childName) + ": " + reps.Count);
@@ -293,36 +304,36 @@ private void ParseReps(
293304
{
294305
for (var i = 0; i < reps.Count; i++)
295306
{
296-
ParseRep((XmlElement)reps[i], groupObject.GetStructure(childIndexName, i));
307+
ParseRep((XmlElement)reps[i], groupObject.GetStructure(childIndexName, i), parserOptions);
297308
}
298309
}
299310
else
300311
{
301312
if (reps.Count > 0)
302313
{
303-
ParseRep((XmlElement)reps[0], groupObject.GetStructure(childIndexName, 0));
314+
ParseRep((XmlElement)reps[0], groupObject.GetStructure(childIndexName, 0), parserOptions);
304315
}
305316

306317
if (reps.Count > 1)
307318
{
308319
var newIndexName = groupObject.AddNonstandardSegment(childName);
309320
for (var i = 1; i < reps.Count; i++)
310321
{
311-
ParseRep((XmlElement)reps[i], groupObject.GetStructure(newIndexName, i - 1));
322+
ParseRep((XmlElement)reps[i], groupObject.GetStructure(newIndexName, i - 1), parserOptions);
312323
}
313324
}
314325
}
315326
}
316327

317-
private void ParseRep(XmlElement theElem, IStructure theObj)
328+
private void ParseRep(XmlElement theElem, IStructure theObj, ParserOptions parserOptions)
318329
{
319330
if (theObj is IGroup)
320331
{
321-
Parse((IGroup)theObj, theElem);
332+
Parse((IGroup)theObj, theElem, parserOptions);
322333
}
323334
else if (theObj is ISegment)
324335
{
325-
Parse((ISegment)theObj, theElem);
336+
Parse((ISegment)theObj, theElem, parserOptions);
326337
}
327338

328339
Log.Debug("Parsed element: " + theElem.Name);

src/NHapi.Base/Parser/LegacyPipeParser.cs

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -259,6 +259,11 @@ public static string StripLeadingWhitespace(string in_Renamed)
259259

260260
public override void Parse(IMessage message, string @string, ParserOptions parserOptions)
261261
{
262+
if (parserOptions is null)
263+
{
264+
throw new ArgumentNullException(nameof(parserOptions));
265+
}
266+
262267
var messageIter = new Util.MessageIterator(message, "MSH", true);
263268
FilterIterator.IPredicate segmentsOnly = new AnonymousClassPredicate(this);
264269
var segmentIter = new FilterIterator(messageIter, segmentsOnly);
@@ -299,7 +304,7 @@ public override void Parse(IMessage message, string @string, ParserOptions parse
299304

300305
if (dirIter.MoveNext())
301306
{
302-
Parse((ISegment)dirIter.Current, segments[i], encodingChars);
307+
Parse((ISegment)dirIter.Current, segments[i], encodingChars, parserOptions);
303308
}
304309
}
305310
}
@@ -384,15 +389,29 @@ public virtual string GetMessageStructure(string message)
384389
return GetStructure(message).Structure;
385390
}
386391

387-
/// <summary> Parses a segment string and populates the given Segment object. Unexpected fields are
392+
/// <summary>
393+
/// Parses a segment string and populates the given Segment object. Unexpected fields are
388394
/// added as Varies' at the end of the segment.
389-
///
390395
/// </summary>
391396
/// <throws> HL7Exception if the given string does not contain the. </throws>
392397
/// <summary> given segment or if the string is not encoded properly.
393398
/// </summary>
394399
public virtual void Parse(ISegment destination, string segment, EncodingCharacters encodingChars)
395400
{
401+
Parse(destination, segment, encodingChars, DefaultParserOptions);
402+
}
403+
404+
/// <summary>
405+
/// Parses a segment string and populates the given Segment object. Unexpected fields are
406+
/// added as Varies' at the end of the segment.
407+
/// </summary>
408+
/// <throws> HL7Exception if the given string does not contain the. </throws>
409+
/// <summary> given segment or if the string is not encoded properly.
410+
/// </summary>
411+
public virtual void Parse(ISegment destination, string segment, EncodingCharacters encodingChars, ParserOptions parserOptions)
412+
{
413+
parserOptions = parserOptions ?? DefaultParserOptions;
414+
396415
var fieldOffset = 0;
397416
if (IsDelimDefSegment(destination.GetStructureName()))
398417
{
@@ -451,7 +470,7 @@ public virtual void Parse(ISegment destination, string segment, EncodingCharacte
451470
// set data type of OBX-5
452471
if (destination.GetType().FullName.IndexOf("OBX") >= 0)
453472
{
454-
Varies.FixOBX5(destination, Factory);
473+
Varies.FixOBX5(destination, Factory, parserOptions);
455474
}
456475
}
457476

src/NHapi.Base/Parser/ParserBase.cs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,9 +44,9 @@ namespace NHapi.Base.Parser
4444
/// <author>Bryan Tripp ([email protected]).</author>
4545
public abstract class ParserBase
4646
{
47-
private static readonly IHapiLog Log;
48-
private static readonly ParserOptions DefaultParserOptions = new ParserOptions();
47+
protected static readonly ParserOptions DefaultParserOptions = new ParserOptions();
4948

49+
private static readonly IHapiLog Log;
5050
private IValidationContext validationContext;
5151
private MessageValidator messageValidator;
5252

@@ -265,6 +265,11 @@ public virtual IMessage Parse(string message, string version)
265265
/// <exception cref="ArgumentNullException">If <paramref name="parserOptions"/> is null.</exception>
266266
public virtual IMessage Parse(string message, string version, ParserOptions parserOptions)
267267
{
268+
if (parserOptions is null)
269+
{
270+
throw new ArgumentNullException(nameof(parserOptions));
271+
}
272+
268273
var encoding = GetEncoding(message);
269274
if (!SupportsEncoding(encoding))
270275
{

src/NHapi.Base/Parser/ParserOptions.cs

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,51 @@ public class ParserOptions
44
{
55
public ParserOptions()
66
{
7+
DefaultObx2Type = null;
8+
InvalidObx2Type = null;
79
NonGreedyMode = false;
810
}
911

12+
/// <summary>
13+
/// If this property is set, the value provides a default datatype ("ST",
14+
/// "NM", etc) for an OBX segment with a missing OBX-2 value. This is useful
15+
/// when parsing messages from systems which do not correctly populate OBX-2.
16+
/// </summary>
17+
/// <example>
18+
/// <para>
19+
/// For example, if this property is set to "ST", and the following OBX
20+
/// segment is encountered:
21+
/// <code>
22+
/// OBX|||||This is a value
23+
/// </code>
24+
/// It will be parsed as though it had read:
25+
/// <code>
26+
/// OBX||ST|||This is a value
27+
/// </code>
28+
/// </para>
29+
/// </example>
30+
public string DefaultObx2Type { get; set; }
31+
32+
/// <summary>
33+
/// If this property is set, the value provides a default datatype ("ST",
34+
/// "NM", etc) for an OBX segment with an invalid OBX-2 value. This is useful
35+
/// when parsing messages from systems which do not correctly populate OBX-2.
36+
/// </summary>
37+
/// <example>
38+
/// <para>
39+
/// For example, if this property is set to "ST", and the following OBX
40+
/// segment is encountered:
41+
/// <code>
42+
/// OBX||INVALID|||This is a value
43+
/// </code>
44+
/// It will be parsed as though it had read:
45+
/// <code>
46+
/// OBX||ST|||This is a value
47+
/// </code>
48+
/// </para>
49+
/// </example>
50+
public string InvalidObx2Type { get; set; }
51+
1052
/// <summary>
1153
/// If set to <c>true</c> (default is <c>false</c>), pipe parser will be put in non-greedy mode. This setting
1254
/// applies only to <see cref="PipeParser"/> and will have no effect on <see cref="XMLParser"/>.

0 commit comments

Comments
 (0)