1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28 package ca.uhn.hl7v2.parser;
29
30 import java.util.ArrayList;
31 import java.util.List;
32 import java.util.StringTokenizer;
33
34 import org.slf4j.Logger;
35 import org.slf4j.LoggerFactory;
36
37 import ca.uhn.hl7v2.ErrorCode;
38 import ca.uhn.hl7v2.HL7Exception;
39 import ca.uhn.hl7v2.Version;
40 import ca.uhn.hl7v2.model.Group;
41 import ca.uhn.hl7v2.model.Message;
42 import ca.uhn.hl7v2.model.Primitive;
43 import ca.uhn.hl7v2.model.Segment;
44 import ca.uhn.hl7v2.model.Structure;
45 import ca.uhn.hl7v2.model.Type;
46 import ca.uhn.hl7v2.util.FilterIterator;
47 import ca.uhn.hl7v2.util.MessageIterator;
48 import ca.uhn.hl7v2.util.Terser;
49
50
51
52
53
54
55
56
57
58
59
60
61
62 class OldPipeParser extends Parser {
63
64 private static final Logger log = LoggerFactory.getLogger(OldPipeParser.class);
65
66 private final static String segDelim = "\r";
67
68
69 public OldPipeParser() {
70 }
71
72
73
74
75
76
77 public OldPipeParser(ModelClassFactory theFactory) {
78 super(theFactory);
79 }
80
81
82
83
84
85
86
87
88
89
90 public String getEncoding(String message) {
91 String encoding = null;
92
93
94 if (message.length() < 4)
95 return null;
96
97
98 boolean ok = true;
99
100
101 if (!message.startsWith("MSH"))
102 return null;
103
104
105 char fourthChar = message.charAt(3);
106 StringTokenizer st = new StringTokenizer(message, segDelim, false);
107 while (st.hasMoreTokens()) {
108 String x = st.nextToken();
109 if (x.length() > 0) {
110 if (Character.isWhitespace(x.charAt(0)))
111 x = stripLeadingWhitespace(x);
112 if (x.length() >= 4 && x.charAt(3) != fourthChar)
113 return null;
114 }
115 }
116
117
118 int nextFieldDelimLoc = 0;
119 for (int i = 0; i < 11; i++) {
120 nextFieldDelimLoc = message.indexOf(fourthChar, nextFieldDelimLoc + 1);
121 if (nextFieldDelimLoc < 0)
122 return null;
123 }
124
125 encoding = "VB";
126
127 return encoding;
128 }
129
130
131
132
133 public String getDefaultEncoding() {
134 return "VB";
135 }
136
137
138
139
140
141 public boolean supportsEncoding(String encoding) {
142 boolean supports = false;
143 if (encoding != null && encoding.equals("VB"))
144 supports = true;
145 return supports;
146 }
147
148
149
150
151
152
153
154
155 public String getMessageStructure(String message) throws HL7Exception, EncodingNotSupportedException {
156 return getStructure(message).messageStructure;
157 }
158
159
160
161
162 private MessageStructure getStructure(String message) throws HL7Exception {
163 EncodingCharacters ec = getEncodingChars(message);
164 String messageStructure;
165 boolean explicityDefined = true;
166 String wholeFieldNine;
167 try {
168 String[] fields = split(message.substring(0, Math.max(message.indexOf(segDelim), message.length())),
169 String.valueOf(ec.getFieldSeparator()));
170 wholeFieldNine = fields[8];
171
172
173
174 String[] comps = split(wholeFieldNine, String.valueOf(ec.getComponentSeparator()));
175 if (comps.length >= 3) {
176 messageStructure = comps[2];
177 } else if (comps.length > 0 && comps[0] != null && comps[0].equals("ACK")) {
178 messageStructure = "ACK";
179 } else if (comps.length == 2) {
180 explicityDefined = false;
181 messageStructure = comps[0] + "_" + comps[1];
182 }
183
184
185
186 else {
187 String buf = "Can't determine message structure from MSH-9: " + wholeFieldNine +
188 " HINT: there are only " +
189 comps.length +
190 " of 3 components present";
191 throw new HL7Exception(buf, ErrorCode.UNSUPPORTED_MESSAGE_TYPE);
192 }
193 }
194 catch (IndexOutOfBoundsException e) {
195 throw new HL7Exception(
196 "Can't find message structure (MSH-9-3): " + e.getMessage(),
197 ErrorCode.UNSUPPORTED_MESSAGE_TYPE);
198 }
199
200 return new MessageStructure(messageStructure, explicityDefined);
201 }
202
203
204
205
206
207 private static EncodingCharacters getEncodingChars(String message) {
208 return new EncodingCharacters(message.charAt(3), message.substring(4, 8));
209 }
210
211
212
213
214
215
216
217
218
219 protected Message doParse(String message, String version) throws HL7Exception, EncodingNotSupportedException {
220
221
222 MessageStructure structure = getStructure(message);
223 Message m = instantiateMessage(structure.messageStructure, version, structure.explicitlyDefined);
224
225 parse(m, message);
226
227 return m;
228 }
229
230
231
232
233
234
235
236
237 public void parse(Segment destination, String segment, EncodingCharacters encodingChars) throws HL7Exception {
238 int fieldOffset = 0;
239 if (isDelimDefSegment(destination.getName())) {
240 fieldOffset = 1;
241
242 Terser.set(destination, 1, 0, 1, 1, String.valueOf(encodingChars.getFieldSeparator()));
243 }
244
245 String[] fields = split(segment, String.valueOf(encodingChars.getFieldSeparator()));
246
247 for (int i = 1; i < fields.length; i++) {
248 String[] reps = split(fields[i], String.valueOf(encodingChars.getRepetitionSeparator()));
249 log.debug("{} reps delimited by: {}", reps.length, encodingChars.getRepetitionSeparator());
250
251
252 boolean isMSH2 = isDelimDefSegment(destination.getName()) && i+fieldOffset == 2;
253 if (isMSH2) {
254 reps = new String[1];
255 reps[0] = fields[i];
256 }
257
258 for (int j = 0; j < reps.length; j++) {
259 try {
260 String statusMessage = "Parsing field " + (i + fieldOffset) +
261 " repetition " +
262 j;
263 log.debug(statusMessage);
264
265
266 Type field = destination.getField(i + fieldOffset, j);
267 if (isMSH2) {
268 Terser.getPrimitive(field, 1, 1).setValue(reps[j]);
269 } else {
270 parse(field, reps[j], encodingChars);
271 }
272 }
273 catch (HL7Exception e) {
274
275 e.setFieldPosition(i);
276 e.setSegmentRepetition(MessageIterator.getIndex(destination.getParent(), destination).rep);
277 e.setSegmentName(destination.getName());
278 throw e;
279 }
280 }
281 }
282
283
284 if (destination.getClass().getName().contains("OBX")) {
285 FixFieldDataType.fixOBX5(destination, getFactory(), getHapiContext().getParserConfiguration());
286 }
287
288 }
289
290
291
292
293
294
295 private static boolean isDelimDefSegment(String theSegmentName) {
296 boolean is = false;
297 if (theSegmentName.equals("MSH")
298 || theSegmentName.equals("FHS")
299 || theSegmentName.equals("BHS"))
300 {
301 is = true;
302 }
303 return is;
304 }
305
306
307
308
309
310
311
312 public void parse(Type destinationField, String data, EncodingCharacters encodingCharacters) throws HL7Exception {
313 String[] components = split(data, String.valueOf(encodingCharacters.getComponentSeparator()));
314 for (int i = 0; i < components.length; i++) {
315 String[] subcomponents = split(components[i], String.valueOf(encodingCharacters.getSubcomponentSeparator()));
316 for (int j = 0; j < subcomponents.length; j++) {
317 String val = subcomponents[j];
318 if (val != null) {
319 val = Escape.unescape(val, encodingCharacters);
320 }
321 Terser.getPrimitive(destinationField, i+1, j+1).setValue(val);
322 }
323 }
324 }
325
326
327
328
329
330 public static String[] split(String composite, String delim) {
331 List<String> components = new ArrayList<>();
332
333
334 if (composite == null)
335 composite = "";
336 if (delim == null)
337 delim = "";
338
339 StringTokenizer tok = new StringTokenizer(composite, delim, true);
340 boolean previousTokenWasDelim = true;
341 while (tok.hasMoreTokens()) {
342 String thisTok = tok.nextToken();
343 if (thisTok.equals(delim)) {
344 if (previousTokenWasDelim)
345 components.add(null);
346 previousTokenWasDelim = true;
347 }
348 else {
349 components.add(thisTok);
350 previousTokenWasDelim = false;
351 }
352 }
353
354 return components.toArray(new String[0]);
355 }
356
357
358
359
360
361 public static String encode(Type source, EncodingCharacters encodingChars) {
362 StringBuilder field = new StringBuilder();
363 for (int i = 1; i <= Terser.numComponents(source); i++) {
364 StringBuilder comp = new StringBuilder();
365 for (int j = 1; j <= Terser.numSubComponents(source, i); j++) {
366 Primitive p = Terser.getPrimitive(source, i, j);
367 comp.append(encodePrimitive(p, encodingChars));
368 comp.append(encodingChars.getSubcomponentSeparator());
369 }
370 field.append(stripExtraDelimiters(comp.toString(), encodingChars.getSubcomponentSeparator()));
371 field.append(encodingChars.getComponentSeparator());
372 }
373 return stripExtraDelimiters(field.toString(), encodingChars.getComponentSeparator());
374
375 }
376
377 private static String encodePrimitive(Primitive p, EncodingCharacters encodingChars) {
378 String val = p.getValue();
379 if (val == null) {
380 val = "";
381 } else {
382 val = Escape.escape(val, encodingChars);
383 }
384 return val;
385 }
386
387
388
389
390
391
392 private static String stripExtraDelimiters(String in, char delim) {
393 char[] chars = in.toCharArray();
394
395
396 int c = chars.length - 1;
397 boolean found = false;
398 while (c >= 0 && !found) {
399 if (chars[c--] != delim)
400 found = true;
401 }
402
403 String ret = "";
404 if (found)
405 ret = String.valueOf(chars, 0, c + 2);
406 return ret;
407 }
408
409
410
411
412
413
414
415
416
417 protected String doEncode(Message source, String encoding) throws HL7Exception, EncodingNotSupportedException {
418 if (!this.supportsEncoding(encoding))
419 throw new EncodingNotSupportedException("This parser does not support the " + encoding + " encoding");
420
421 return encode(source);
422 }
423
424
425
426
427
428
429
430 protected String doEncode(Message source) throws HL7Exception {
431
432 Segment="../../../../ca/uhn/hl7v2/model/Segment.html#Segment">Segment msh = (Segment) source.get("MSH");
433 String fieldSepString = Terser.get(msh, 1, 0, 1, 1);
434
435 if (fieldSepString == null)
436 throw new HL7Exception("Can't encode message: MSH-1 (field separator) is missing");
437
438 char fieldSep = '|';
439 if (fieldSepString.length() > 0)
440 fieldSep = fieldSepString.charAt(0);
441
442 String encCharString = Terser.get(msh, 2, 0, 1, 1);
443
444 if (encCharString == null)
445 throw new HL7Exception("Can't encode message: MSH-2 (encoding characters) is missing");
446
447 if (encCharString.length() != 4)
448 throw new HL7Exception(
449 "Encoding characters '" + encCharString + "' invalid -- must be 4 characters",
450 ErrorCode.DATA_TYPE_ERROR);
451 EncodingCharactersrs.html#EncodingCharacters">EncodingCharacters en = new EncodingCharacters(fieldSep, encCharString);
452
453
454 return encode(source, en);
455 }
456
457
458
459
460
461 public static String encode(Group source, EncodingCharacters encodingChars) throws HL7Exception {
462 StringBuilder result = new StringBuilder();
463
464 String[] names = source.getNames();
465 for (String name : names) {
466 Structure[] reps = source.getAll(name);
467 for (Structure structure : reps) {
468 if (structure instanceof Group) {
469 result.append(encode((Group) structure, encodingChars));
470 } else {
471 String segString = encode((Segment) structure, encodingChars);
472 if (segString.length() >= 4) {
473 result.append(segString);
474 result.append('\r');
475 }
476 }
477 }
478 }
479 return result.toString();
480 }
481
482 public static String encode(Segment source, EncodingCharacters encodingChars) {
483 StringBuilder result = new StringBuilder();
484 result.append(source.getName());
485 result.append(encodingChars.getFieldSeparator());
486
487
488 int startAt = 1;
489 if (isDelimDefSegment(source.getName()))
490 startAt = 2;
491
492
493 int numFields = source.numFields();
494 for (int i = startAt; i <= numFields; i++) {
495 try {
496 Type[] reps = source.getField(i);
497 for (int j = 0; j < reps.length; j++) {
498 String fieldText = encode(reps[j], encodingChars);
499
500 if (isDelimDefSegment(source.getName()) && i == 2)
501 fieldText = Escape.unescape(fieldText, encodingChars);
502 result.append(fieldText);
503 if (j < reps.length - 1)
504 result.append(encodingChars.getRepetitionSeparator());
505 }
506 }
507 catch (HL7Exception e) {
508 log.error("Error while encoding segment: ", e);
509 }
510 result.append(encodingChars.getFieldSeparator());
511 }
512
513
514 return stripExtraDelimiters(result.toString(), encodingChars.getFieldSeparator());
515 }
516
517
518
519
520
521
522
523 public static String stripLeadingWhitespace(String in) {
524 StringBuilder out = new StringBuilder();
525 char[] chars = in.toCharArray();
526 int c = 0;
527 while (c < chars.length) {
528 if (!Character.isWhitespace(chars[c]))
529 break;
530 c++;
531 }
532 for (int i = c; i < chars.length; i++) {
533 out.append(chars[i]);
534 }
535 return out.toString();
536 }
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553 public Segment getCriticalResponseData(String message) throws HL7Exception {
554
555 int locStartMSH = message.indexOf("MSH");
556 if (locStartMSH < 0)
557 throw new HL7Exception(
558 "Couldn't find MSH segment in message: " + message,
559 ErrorCode.SEGMENT_SEQUENCE_ERROR);
560 int locEndMSH = message.indexOf('\r', locStartMSH + 1);
561 if (locEndMSH < 0)
562 locEndMSH = message.length();
563 String mshString = message.substring(locStartMSH, locEndMSH);
564
565
566 char fieldSep = mshString.charAt(3);
567
568
569 String[] fields = split(mshString, String.valueOf(fieldSep));
570
571 Segment msh;
572 try {
573
574 String encChars = fields[1];
575 char compSep = encChars.charAt(0);
576 String messControlID = fields[9];
577 String[] procIDComps = split(fields[10], String.valueOf(compSep));
578
579
580 String version = Version.lowestAvailableVersion().getVersion();
581 try {
582 version = this.getVersion(message);
583 }
584 catch (Exception e) {
585 }
586
587 msh = Parser.makeControlMSH(version, getFactory());
588 Terser.set(msh, 1, 0, 1, 1, String.valueOf(fieldSep));
589 Terser.set(msh, 2, 0, 1, 1, encChars);
590 Terser.set(msh, 10, 0, 1, 1, messControlID);
591 Terser.set(msh, 11, 0, 1, 1, procIDComps[0]);
592 Terser.set(msh, 12, 0, 1, 1, version);
593
594 }
595 catch (Exception e) {
596 throw new HL7Exception(
597 "Can't parse critical fields from MSH segment ("
598 + e.getClass().getName()
599 + ": "
600 + e.getMessage()
601 + "): "
602 + mshString,
603 ErrorCode.REQUIRED_FIELD_MISSING, e);
604 }
605
606 return msh;
607 }
608
609
610
611
612
613
614
615
616
617
618 public String getAckID(String message) {
619 String ackID = null;
620 int startMSA = message.indexOf("\rMSA");
621 if (startMSA >= 0) {
622 int startFieldOne = startMSA + 5;
623 char fieldDelim = message.charAt(startFieldOne - 1);
624 int start = message.indexOf(fieldDelim, startFieldOne) + 1;
625 int end = message.indexOf(fieldDelim, start);
626 int segEnd = message.indexOf(segDelim, start);
627 if (segEnd > start && segEnd < end)
628 end = segEnd;
629
630
631 if (end < 0) {
632 if (message.charAt(message.length() - 1) == '\r') {
633 end = message.length() - 1;
634 }
635 else {
636 end = message.length();
637 }
638 }
639 if (start > 0 && end > start) {
640 ackID = message.substring(start, end);
641 }
642 }
643 log.debug("ACK ID: {}", ackID);
644 return ackID;
645 }
646
647
648
649
650
651
652
653 public String getVersion(String message) throws HL7Exception {
654 int startMSH = message.indexOf("MSH");
655 int endMSH = message.indexOf(OldPipeParser.segDelim, startMSH);
656 if (endMSH < 0)
657 endMSH = message.length();
658 String msh = message.substring(startMSH, endMSH);
659 String fieldSep;
660 if (msh.length() > 3) {
661 fieldSep = String.valueOf(msh.charAt(3));
662 }
663 else {
664 throw new HL7Exception("Can't find field separator in MSH: " + msh, ErrorCode.UNSUPPORTED_VERSION_ID);
665 }
666
667 String[] fields = split(msh, fieldSep);
668
669 String compSep;
670 if (fields.length >= 2 && fields[1] != null && fields[1].length() == 4) {
671 compSep = String.valueOf(fields[1].charAt(0));
672 }
673 else {
674 throw new HL7Exception("Invalid or incomplete encoding characters - MSH-2 is " + fields[1],
675 ErrorCode.REQUIRED_FIELD_MISSING);
676 }
677
678 String version;
679 if (fields.length >= 12) {
680 String[] comp = split(fields[11], compSep);
681 if (comp.length >= 1) {
682 version = comp[0];
683 } else {
684 throw new HL7Exception("Can't find version ID - MSH.12 is " + fields[11],
685 ErrorCode.REQUIRED_FIELD_MISSING);
686 }
687 }
688 else {
689 throw new HL7Exception(
690 "Can't find version ID - MSH has only " + fields.length + " fields.",
691 ErrorCode.REQUIRED_FIELD_MISSING);
692 }
693 return version;
694 }
695
696
697
698
699 public String doEncode(Segment structure, EncodingCharacters encodingCharacters) {
700 return encode(structure, encodingCharacters);
701 }
702
703
704
705
706 public String doEncode(Type type, EncodingCharacters encodingCharacters) {
707 return encode(type, encodingCharacters);
708 }
709
710
711
712
713
714
715 @Override
716 protected Message doParseForSpecificPackage(String theMessage, String theVersion, String thePackageName) {
717 throw new UnsupportedOperationException("Not supported yet.");
718 }
719
720 public void parse(Message message, String string) throws HL7Exception {
721 MessageIteratoressageIterator">MessageIterator messageIter = new MessageIterator(message, "MSH", true);
722 FilterIterator.Predicate<Structure> segmentsOnly = obj -> Segment.class.isAssignableFrom(obj.getClass());
723 FilterIterator<Structure> segmentIter = new FilterIterator<>(messageIter, segmentsOnly);
724
725 String[] segments = split(string, segDelim);
726
727 char delim = '|';
728 for (int i = 0; i < segments.length; i++) {
729
730
731 if (segments[i] != null && segments[i].length() > 0 && Character.isWhitespace(segments[i].charAt(0)))
732 segments[i] = stripLeadingWhitespace(segments[i]);
733
734
735 if (segments[i] != null && segments[i].length() >= 3) {
736 final String name;
737 if (i == 0) {
738 name = segments[i].substring(0, 3);
739 delim = segments[i].charAt(3);
740 } else {
741 if (segments[i].indexOf(delim) >= 0 ) {
742 name = segments[i].substring(0, segments[i].indexOf(delim));
743 } else {
744 name = segments[i];
745 }
746 }
747
748 log.debug("Parsing segment {}", name);
749
750 messageIter.setDirection(name);
751 FilterIterator.Predicate<Structure> byDirection = obj -> {
752 log.debug("PipeParser iterating message in direction {} at {} ", name, obj.getName());
753 return obj.getName().matches(name + "\\d*");
754 };
755 FilterIterator<Structure> dirIter = new FilterIterator<>(segmentIter, byDirection);
756 if (dirIter.hasNext()) {
757 parse((Segment) dirIter.next(), segments[i], getEncodingChars(string));
758 }
759 }
760 }
761 }
762
763
764
765
766
767
768 private static class MessageStructure {
769 public final String messageStructure;
770 public final boolean explicitlyDefined;
771
772 public MessageStructure(String theMessageStructure, boolean isExplicitlyDefined) {
773 messageStructure = theMessageStructure;
774 explicitlyDefined = isExplicitlyDefined;
775 }
776 }
777
778 }