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.Arrays;
32 import java.util.HashMap;
33 import java.util.List;
34 import java.util.Map;
35 import java.util.Set;
36 import java.util.StringTokenizer;
37
38 import ca.uhn.hl7v2.validation.ValidationContext;
39 import org.slf4j.Logger;
40 import org.slf4j.LoggerFactory;
41
42 import ca.uhn.hl7v2.DefaultHapiContext;
43 import ca.uhn.hl7v2.ErrorCode;
44 import ca.uhn.hl7v2.HL7Exception;
45 import ca.uhn.hl7v2.HapiContext;
46 import ca.uhn.hl7v2.Version;
47 import ca.uhn.hl7v2.model.AbstractSuperMessage;
48 import ca.uhn.hl7v2.model.DoNotCacheStructure;
49 import ca.uhn.hl7v2.model.Group;
50 import ca.uhn.hl7v2.model.Message;
51 import ca.uhn.hl7v2.model.Primitive;
52 import ca.uhn.hl7v2.model.Segment;
53 import ca.uhn.hl7v2.model.Structure;
54 import ca.uhn.hl7v2.model.SuperStructure;
55 import ca.uhn.hl7v2.model.Type;
56 import ca.uhn.hl7v2.model.Varies;
57 import ca.uhn.hl7v2.util.ReflectionUtil;
58 import ca.uhn.hl7v2.util.Terser;
59 import ca.uhn.hl7v2.validation.impl.NoValidation;
60 import ca.uhn.hl7v2.validation.impl.ValidationContextFactory;
61
62
63
64
65
66
67
68
69
70 public class PipeParser extends Parser {
71
72 private static final Logger log = LoggerFactory.getLogger(PipeParser.class);
73
74
75
76
77 final static String SEGMENT_DELIMITER = "\r";
78
79 private final HashMap<Class<? extends Message>, HashMap<String, StructureDefinition>> myStructureDefinitions = new HashMap<>();
80
81
82
83
84
85
86
87 public static final String DEFAULT_LEGACY_MODE_PROPERTY = "ca.uhn.hl7v2.parser.PipeParser.default_legacy_mode";
88
89 private Boolean myLegacyMode = null;
90
91 public PipeParser() {
92 super();
93 }
94
95
96
97
98
99 public PipeParser(HapiContext context) {
100 super(context);
101 }
102
103
104
105
106
107
108
109 public PipeParser(ModelClassFactory theFactory) {
110 super(theFactory);
111 }
112
113 @Override
114 public void setValidationContext(ValidationContext context) {
115 super.setValidationContext(context);
116 }
117
118
119
120
121
122
123
124
125
126
127 public String getEncoding(String message) {
128 return EncodingDetector.isEr7Encoded(message) ? getDefaultEncoding() : null;
129 }
130
131
132
133
134 public String getDefaultEncoding() {
135 return "VB";
136 }
137
138
139
140
141
142
143
144 public String getMessageStructure(String message) throws HL7Exception {
145 return getStructure(message).messageStructure;
146 }
147
148
149
150
151 private MessageStructure getStructure(String message) throws HL7Exception {
152 EncodingCharacters ec = getEncodingChars(message);
153 String messageStructure;
154 boolean explicityDefined = true;
155 String wholeFieldNine;
156 try {
157 String[] fields = split(message.substring(0, Math.max(message.indexOf(SEGMENT_DELIMITER), message.length())), String.valueOf(ec.getFieldSeparator()));
158 wholeFieldNine = fields[8];
159
160
161
162
163
164 String[] comps = split(wholeFieldNine, String.valueOf(ec.getComponentSeparator()));
165 if (comps.length >= 3) {
166 messageStructure = comps[2];
167 } else if (comps.length > 0 && comps[0] != null && comps[0].equals("ACK")) {
168 messageStructure = "ACK";
169 } else if (comps.length == 2) {
170 explicityDefined = false;
171 messageStructure = comps[0] + "_" + comps[1];
172 }
173
174
175
176
177
178 else {
179 String buf = "Can't determine message structure from MSH-9: " + wholeFieldNine +
180 " HINT: there are only " +
181 comps.length +
182 " of 3 components present";
183 throw new HL7Exception(buf, ErrorCode.UNSUPPORTED_MESSAGE_TYPE);
184 }
185 } catch (IndexOutOfBoundsException e) {
186 throw new HL7Exception("Can't find message structure (MSH-9-3): " + e.getMessage(), ErrorCode.UNSUPPORTED_MESSAGE_TYPE);
187 }
188
189 return new MessageStructure(messageStructure, explicityDefined);
190 }
191
192
193
194
195
196
197
198
199
200
201 private static EncodingCharacters getEncodingChars(String message) throws HL7Exception {
202 if (message.length() < 9) {
203 throw new HL7Exception("Invalid message content: \"" + message + "\"");
204 }
205 return new EncodingCharacters(message.charAt(3), message.substring(4, 9));
206 }
207
208
209
210
211
212
213
214
215
216
217 protected Message doParse(String message, String version) throws HL7Exception {
218
219
220 MessageStructure structure = getStructure(message);
221 Message m = instantiateMessage(structure.messageStructure, version, structure.explicitlyDefined);
222 m.setParser(this);
223 parse(m, message);
224 return m;
225 }
226
227
228
229
230 protected Message doParseForSpecificPackage(String message, String version, String packageName) throws HL7Exception {
231
232
233 MessageStructure structure = getStructure(message);
234 Message m = instantiateMessageInASpecificPackage(structure.messageStructure, version, structure.explicitlyDefined, packageName);
235
236 parse(m, message);
237
238 return m;
239 }
240
241
242
243
244 private IStructureDefinition getStructureDefinition(Message theMessage) throws HL7Exception {
245
246 Class<? extends Message> clazz = theMessage.getClass();
247 HashMap<String, StructureDefinition> definitions = myStructureDefinitions.get(clazz);
248
249 StructureDefinition retVal;
250 if (definitions != null) {
251 retVal = definitions.get(theMessage.getName());
252 if (retVal != null) {
253 return retVal;
254 }
255 }
256
257 if (theMessage instanceof SuperStructure) {
258 Set<String> appliesTo = ((SuperStructure) theMessage).getStructuresWhichChildAppliesTo("MSH");
259 if (!appliesTo.contains(theMessage.getName())) {
260 throw new HL7Exception("Superstructure " + theMessage.getClass().getSimpleName() + " does not apply to message " + theMessage.getName() + ", can not parse.");
261 }
262 }
263
264 if (clazz.isAnnotationPresent(DoNotCacheStructure.class)) {
265 Holder<StructureDefinition> previousLeaf = new Holder<>();
266 retVal = createStructureDefinition(theMessage, previousLeaf, theMessage.getName());
267 } else {
268 Message message = ReflectionUtil.instantiateMessage(clazz, getFactory());
269 Holder<StructureDefinition> previousLeaf = new Holder<>();
270 retVal = createStructureDefinition(message, previousLeaf, theMessage.getName());
271
272 if (!myStructureDefinitions.containsKey(clazz)) {
273 myStructureDefinitions.put(clazz, new HashMap<>());
274 }
275 myStructureDefinitions.get(clazz).put(theMessage.getName(), retVal);
276 }
277
278 return retVal;
279 }
280
281 private StructureDefinition createStructureDefinition(Structure theStructure, Holder<StructureDefinition> thePreviousLeaf, String theStructureName) throws HL7Exception {
282
283 StructureDefinition retVal = new StructureDefinition();
284 retVal.setName(theStructure.getName());
285
286 if (theStructure instanceof Group) {
287 retVal.setSegment(false);
288 Group group = (Group) theStructure;
289 int index = 0;
290 List<String> childNames = Arrays.asList(group.getNames());
291
292
293
294
295
296
297 if (theStructure instanceof SuperStructure) {
298 String struct = theStructureName;
299 Map<String, String> evtMap = new DefaultModelClassFactory().getEventMapForVersion(Version.versionOf(theStructure.getMessage().getVersion()));
300 if (evtMap.containsKey(struct)) {
301 struct = evtMap.get(struct);
302 }
303 childNames = ((SuperStructure) theStructure).getChildNamesForStructure(struct);
304 }
305
306 for (String nextName : childNames) {
307 Structure nextChild = group.get(nextName);
308 StructureDefinition structureDefinition = createStructureDefinition(nextChild, thePreviousLeaf, theStructureName);
309 structureDefinition.setNameAsItAppearsInParent(nextName);
310 structureDefinition.setRepeating(group.isRepeating(nextName));
311 structureDefinition.setRequired(group.isRequired(nextName));
312 structureDefinition.setChoiceElement(group.isChoiceElement(nextName));
313 structureDefinition.setPosition(index++);
314 structureDefinition.setParent(retVal);
315 retVal.addChild(structureDefinition);
316 }
317 } else {
318 if (thePreviousLeaf.getObject() != null) {
319 thePreviousLeaf.getObject().setNextLeaf(retVal);
320 }
321 thePreviousLeaf.setObject(retVal);
322 retVal.setSegment(true);
323 }
324
325 return retVal;
326 }
327
328
329
330
331
332
333
334
335
336
337
338
339 public void parse(Segment destination, String segment, EncodingCharacters encodingChars) throws HL7Exception {
340 parse(destination, segment, encodingChars, 0);
341 }
342
343
344
345
346
347
348
349
350
351
352
353
354
355 public void parse(Segment destination, String segment, EncodingCharacters encodingChars, int theRepetition) throws HL7Exception {
356 int fieldOffset = 0;
357 if (isDelimDefSegment(destination.getName())) {
358 fieldOffset = 1;
359
360 Terser.set(destination, 1, 0, 1, 1, String.valueOf(encodingChars.getFieldSeparator()));
361 }
362
363 String[] fields = split(segment, String.valueOf(encodingChars.getFieldSeparator()));
364
365 for (int i = 1; i < fields.length; i++) {
366 String[] reps = split(fields[i], String.valueOf(encodingChars.getRepetitionSeparator()));
367
368
369 boolean isMSH2 = isDelimDefSegment(destination.getName()) && i + fieldOffset == 2;
370 if (isMSH2) {
371 reps = new String[1];
372 reps[0] = fields[i];
373 }
374
375 for (int j = 0; j < reps.length; j++) {
376 try {
377 log.trace("Parsing field {} repetition {}", i + fieldOffset, j);
378 Type field = destination.getField(i + fieldOffset, j);
379 if (isMSH2) {
380 Terser.getPrimitive(field, 1, 1).setValue(reps[j]);
381 } else {
382 parse(field, reps[j], encodingChars);
383 }
384 } catch (HL7Exception e) {
385
386 e.setFieldPosition(i);
387 if (theRepetition > 1) {
388 e.setSegmentRepetition(theRepetition);
389 }
390 e.setSegmentName(destination.getName());
391 throw e;
392 }
393 }
394 }
395
396
397 if (destination.getClass().getName().contains("OBX")) {
398 FixFieldDataType.fixOBX5(destination, getFactory(), getHapiContext().getParserConfiguration());
399 }
400
401 if (destination.getClass().getName().contains("MFE") &&
402 Version.versionOf(destination.getMessage().getVersion()).isGreaterThan(Version.V23)) {
403 FixFieldDataType.fixMFE4(destination, getFactory(), getHapiContext().getParserConfiguration());
404 }
405
406 }
407
408
409
410
411
412
413
414 private static boolean isDelimDefSegment(String theSegmentName) {
415 boolean is = false;
416 if (theSegmentName.equals("MSH") || theSegmentName.equals("FHS") || theSegmentName.equals("BHS")) {
417 is = true;
418 }
419 return is;
420 }
421
422
423
424
425
426
427
428
429
430
431
432
433 @Override
434 public void parse(Type destinationField, String data, EncodingCharacters encodingCharacters) throws HL7Exception {
435 String[] components = split(data, String.valueOf(encodingCharacters.getComponentSeparator()));
436 for (int i = 0; i < components.length; i++) {
437 String[] subcomponents = split(components[i], String.valueOf(encodingCharacters.getSubcomponentSeparator()));
438 for (int j = 0; j < subcomponents.length; j++) {
439 String val = subcomponents[j];
440 if (val != null) {
441 val = getParserConfiguration().getEscaping().unescape(val, encodingCharacters);
442 }
443 Terser.getPrimitive(destinationField, i + 1, j + 1).setValue(val);
444 }
445 }
446 }
447
448
449
450
451
452
453
454
455
456 public static String[] split(String composite, String delim) {
457 ArrayList<String> components = new ArrayList<>();
458
459
460 if (composite == null)
461 composite = "";
462 if (delim == null)
463 delim = "";
464
465 StringTokenizer tok = new StringTokenizer(composite, delim, true);
466 boolean previousTokenWasDelim = true;
467 while (tok.hasMoreTokens()) {
468 String thisTok = tok.nextToken();
469 if (thisTok.equals(delim)) {
470 if (previousTokenWasDelim)
471 components.add(null);
472 previousTokenWasDelim = true;
473 } else {
474 components.add(thisTok);
475 previousTokenWasDelim = false;
476 }
477 }
478
479 String[] ret = new String[components.size()];
480 for (int i = 0; i < components.size(); i++) {
481 ret[i] = components.get(i);
482 }
483
484 return ret;
485 }
486
487
488
489
490 @Override
491 public String doEncode(Segment structure, EncodingCharacters encodingCharacters) {
492 return encode(structure, encodingCharacters, getParserConfiguration(), null);
493 }
494
495
496
497
498 @Override
499 public String doEncode(Type type, EncodingCharacters encodingCharacters) {
500 return encode(type, encodingCharacters, getParserConfiguration(), null);
501 }
502
503
504
505
506
507
508
509
510
511
512 public static String encode(Type source, EncodingCharacters encodingChars) {
513 return encode(source, encodingChars, source.getMessage().getParser().getParserConfiguration(), null);
514 }
515
516 private static String encode(Type source, EncodingCharacters encodingChars, ParserConfiguration parserConfig, String currentTerserPath) {
517 if (source instanceof Varies) {
518 Varies varies = (Varies) source;
519 if (varies.getData() != null) {
520 source = varies.getData();
521 }
522 }
523
524 StringBuilder field = new StringBuilder();
525 for (int i = 1; i <= Terser.numComponents(source); i++) {
526 StringBuilder comp = new StringBuilder();
527 for (int j = 1; j <= Terser.numSubComponents(source, i); j++) {
528 Primitive p = Terser.getPrimitive(source, i, j);
529 comp.append(encodePrimitive(p, parserConfig.getEscaping(), encodingChars));
530 comp.append(encodingChars.getSubcomponentSeparator());
531 }
532 field.append(stripExtraDelimiters(comp.toString(), encodingChars.getSubcomponentSeparator()));
533 field.append(encodingChars.getComponentSeparator());
534 }
535
536 int forceUpToFieldNum = 0;
537 if (parserConfig != null && currentTerserPath != null) {
538 for (String nextPath : parserConfig.getForcedEncode()) {
539 if (nextPath.startsWith(currentTerserPath + "-") && nextPath.length() > currentTerserPath.length()) {
540 int endOfFieldDef = nextPath.indexOf('-', currentTerserPath.length());
541 if (endOfFieldDef == -1) {
542 forceUpToFieldNum = 0;
543 break;
544 }
545 String fieldNumString = nextPath.substring(endOfFieldDef + 1);
546 if (fieldNumString.length() > 0) {
547 forceUpToFieldNum = Math.max(forceUpToFieldNum, Integer.parseInt(fieldNumString));
548 }
549 }
550 }
551 }
552
553 char componentSeparator = encodingChars.getComponentSeparator();
554 String retVal = stripExtraDelimiters(field.toString(), componentSeparator);
555
556 while (forceUpToFieldNum > 0 && (countInstancesOf(retVal, componentSeparator) + 1) < forceUpToFieldNum) {
557 retVal = retVal + componentSeparator;
558 }
559
560 return retVal;
561 }
562
563 private static String encodePrimitive(Primitive p, Escaping escaping, EncodingCharacters encodingChars) {
564 String val = (p).getValue();
565 if (val == null) {
566 val = "";
567 } else {
568 val = escaping.escape(val, encodingChars);
569 }
570 return val;
571 }
572
573
574
575
576
577
578 private static String stripExtraDelimiters(String in, char delim) {
579 char[] chars = in.toCharArray();
580
581
582 int c = chars.length - 1;
583 boolean found = false;
584 while (c >= 0 && !found) {
585 if (chars[c--] != delim)
586 found = true;
587 }
588
589 String ret = "";
590 if (found)
591 ret = String.valueOf(chars, 0, c + 2);
592 return ret;
593 }
594
595
596
597
598
599
600
601
602
603
604
605 protected String doEncode(Message source, String encoding) throws HL7Exception {
606 if (!this.supportsEncoding(encoding))
607 throw new EncodingNotSupportedException("This parser does not support the " + encoding + " encoding");
608
609 return encode(source);
610 }
611
612
613
614
615
616
617
618
619
620 protected String doEncode(Message source) throws HL7Exception {
621
622 Segment msh = (Segment) source.get("MSH");
623 String fieldSepString = Terser.get(msh, 1, 0, 1, 1);
624
625 if (fieldSepString == null)
626 throw new HL7Exception("Can't encode message: MSH-1 (field separator) is missing");
627
628 char fieldSep = '|';
629 if (fieldSepString.length() > 0) fieldSep = fieldSepString.charAt(0);
630
631 EncodingCharacters en = getValidEncodingCharacters(fieldSep, msh);
632
633
634
635 return encode(source, en, getParserConfiguration(), "");
636 }
637
638 private EncodingCharacters getValidEncodingCharacters(char fieldSep, Segment msh) throws HL7Exception {
639 String encCharString = Terser.get(msh, 2, 0, 1, 1);
640
641 if (encCharString == null) {
642 throw new HL7Exception("Can't encode message: MSH-2 (encoding characters) is missing");
643 }
644
645 if (Version.V27.isGreaterThan(Version.versionOf(msh.getMessage().getVersion())) && encCharString.length() != 4) {
646 throw new HL7Exception("Encoding characters (MSH-2) value '" + encCharString + "' invalid -- must be 4 characters", ErrorCode.DATA_TYPE_ERROR);
647 } else if (encCharString.length() != 4 && encCharString.length() != 5) {
648 throw new HL7Exception("Encoding characters (MSH-2) value '" + encCharString + "' invalid -- must be 4 or 5 characters", ErrorCode.DATA_TYPE_ERROR);
649 }
650
651 return new EncodingCharacters(fieldSep, encCharString);
652 }
653
654
655
656
657
658
659
660
661
662
663 public static String encode(Group source, EncodingCharacters encodingChars) throws HL7Exception {
664 return encode(source, encodingChars, source.getMessage().getParser().getParserConfiguration(), "");
665 }
666
667
668
669
670
671 private static String encode(Group source, EncodingCharacters encodingChars, ParserConfiguration parserConfiguration, String currentTerserPath) throws HL7Exception {
672 StringBuilder result = new StringBuilder();
673
674 String[] names = source.getNames();
675
676 String firstMandatorySegmentName = null;
677 boolean haveEncounteredMandatorySegment = false;
678 boolean haveEncounteredContent = false;
679 boolean haveHadMandatorySegment = false;
680 boolean haveHadSegmentBeforeMandatorySegment = false;
681
682 for (String nextName : names) {
683
684
685 Structure[] reps = source.getAll(nextName);
686 boolean nextNameIsRequired = source.isRequired(nextName);
687
688 boolean havePreviouslyEncounteredMandatorySegment = haveEncounteredMandatorySegment;
689 haveEncounteredMandatorySegment |= nextNameIsRequired;
690 if (nextNameIsRequired && !haveHadMandatorySegment) {
691 if (!source.isGroup(nextName)) {
692 firstMandatorySegmentName = nextName;
693 }
694 }
695
696 String nextTerserPath = currentTerserPath.length() > 0 ? currentTerserPath + "/" + nextName : nextName;
697
698
699 for (Structure rep : reps) {
700
701 if (rep instanceof Group) {
702
703 String encodedGroup = encode((Group) rep, encodingChars, parserConfiguration, nextTerserPath);
704 result.append(encodedGroup);
705
706 if (encodedGroup.length() > 0) {
707 if (!haveHadMandatorySegment && !haveEncounteredMandatorySegment) {
708 haveHadSegmentBeforeMandatorySegment = true;
709 }
710 if (nextNameIsRequired && !haveHadMandatorySegment && !havePreviouslyEncounteredMandatorySegment) {
711 haveHadMandatorySegment = true;
712 }
713 haveEncounteredContent = true;
714 }
715
716 } else {
717
718
719
720 boolean encodeEmptySegments = parserConfiguration.determineForcedEncodeIncludesTerserPath(nextTerserPath);
721 String segString = encode((Segment) rep, encodingChars, parserConfiguration, nextTerserPath);
722 if (segString.length() >= 4 || encodeEmptySegments) {
723 result.append(segString);
724
725 if (segString.length() == 3) {
726 result.append(encodingChars.getFieldSeparator());
727 }
728
729 result.append(SEGMENT_DELIMITER);
730
731 haveEncounteredContent = true;
732
733 if (nextNameIsRequired) {
734 haveHadMandatorySegment = true;
735 }
736
737 if (!haveHadMandatorySegment && !haveEncounteredMandatorySegment) {
738 haveHadSegmentBeforeMandatorySegment = true;
739 }
740
741 }
742
743 }
744
745 }
746
747 }
748
749 if (firstMandatorySegmentName != null && !haveHadMandatorySegment && !haveHadSegmentBeforeMandatorySegment && haveEncounteredContent && parserConfiguration.isEncodeEmptyMandatorySegments()) {
750 return firstMandatorySegmentName.substring(0, 3) + encodingChars.getFieldSeparator() + SEGMENT_DELIMITER + result;
751 } else {
752 return result.toString();
753 }
754 }
755
756
757
758
759
760
761
762
763 public static PipeParser getInstanceWithNoValidation() {
764 HapiContext context = new DefaultHapiContext();
765 context.setValidationContext(ValidationContextFactory.noValidation());
766 return new PipeParser(context);
767 }
768
769
770
771
772
773
774
775
776 public static String encode(Segment source, EncodingCharacters encodingChars) {
777 return encode(source, encodingChars, source.getMessage().getParser().getParserConfiguration(), null);
778 }
779
780 private static String encode(Segment source, EncodingCharacters encodingChars, ParserConfiguration parserConfig, String currentTerserPath) {
781 StringBuilder result = new StringBuilder();
782 result.append(source.getName());
783 result.append(encodingChars.getFieldSeparator());
784
785
786
787 int startAt = 1;
788 if (isDelimDefSegment(source.getName()))
789 startAt = 2;
790
791
792
793 int numFields = source.numFields();
794
795 int forceUpToFieldNum = 0;
796 if (parserConfig != null && currentTerserPath != null) {
797 forceUpToFieldNum = parserConfig.determineForcedFieldNumForTerserPath(currentTerserPath);
798 }
799 numFields = Math.max(numFields, forceUpToFieldNum);
800
801 for (int i = startAt; i <= numFields; i++) {
802
803 String nextFieldTerserPath = currentTerserPath + "-" + i;
804 if (parserConfig != null && currentTerserPath != null) {
805 for (String nextPath : parserConfig.getForcedEncode()) {
806 if (nextPath.startsWith(nextFieldTerserPath + "-")) {
807 try {
808 source.getField(i, 0);
809 } catch (HL7Exception e) {
810 log.error("Error while encoding segment: ", e);
811 }
812 }
813 }
814 }
815
816 try {
817 Type[] reps = source.getField(i);
818 for (int j = 0; j < reps.length; j++) {
819 String fieldText = encode(reps[j], encodingChars, parserConfig, nextFieldTerserPath);
820
821
822 if (isDelimDefSegment(source.getName()) && i == 2)
823 fieldText = parserConfig.getEscaping().unescape(fieldText, encodingChars);
824 result.append(fieldText);
825 if (j < reps.length - 1)
826 result.append(encodingChars.getRepetitionSeparator());
827 }
828 } catch (HL7Exception e) {
829 log.error("Error while encoding segment: ", e);
830 }
831 result.append(encodingChars.getFieldSeparator());
832 }
833
834
835 char fieldSeparator = encodingChars.getFieldSeparator();
836 String retVal = stripExtraDelimiters(result.toString(), fieldSeparator);
837
838 int offset = isDelimDefSegment(source.getName()) ? 1 : 0;
839 while (forceUpToFieldNum > 0 && (countInstancesOf(retVal, fieldSeparator) + offset) < forceUpToFieldNum) {
840 retVal = retVal + fieldSeparator;
841 }
842
843 return retVal;
844 }
845
846 private static int countInstancesOf(String theString, char theCharToSearchFor) {
847 int retVal = 0;
848 for (int i = 0; i < theString.length(); i++) {
849 if (theString.charAt(i) == theCharToSearchFor) {
850 retVal++;
851 }
852 }
853 return retVal;
854 }
855
856
857
858
859
860
861
862
863
864
865
866
867 public static String stripLeadingWhitespace(String in) {
868 StringBuilder out = new StringBuilder();
869 char[] chars = in.toCharArray();
870 int c = 0;
871 while (c < chars.length) {
872 if (!Character.isWhitespace(chars[c]))
873 break;
874 c++;
875 }
876 for (int i = c; i < chars.length; i++) {
877 out.append(chars[i]);
878 }
879 return out.toString();
880 }
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901 public Segment getCriticalResponseData(String message) throws HL7Exception {
902
903 int locStartMSH = message.indexOf("MSH");
904 if (locStartMSH < 0)
905 throw new HL7Exception("Couldn't find MSH segment in message: " + message, ErrorCode.SEGMENT_SEQUENCE_ERROR);
906 int locEndMSH = message.indexOf('\r', locStartMSH + 1);
907 if (locEndMSH < 0)
908 locEndMSH = message.length();
909 String mshString = message.substring(locStartMSH, locEndMSH);
910
911
912 char fieldSep = mshString.charAt(3);
913
914
915 String[] fields = split(mshString, String.valueOf(fieldSep));
916
917 try {
918
919 String encChars = fields[1];
920 char compSep = encChars.charAt(0);
921 String messControlID = fields[9];
922 String[] procIDComps = split(fields[10], String.valueOf(compSep));
923
924
925 String version = null;
926 try {
927 version = getVersion(message);
928 } catch (Exception e) {
929 }
930
931 if (version == null) {
932 Version availableVersion = Version.highestAvailableVersionOrDefault();
933 version = availableVersion.getVersion();
934 }
935
936 Segment msh = Parser.makeControlMSH(version, getFactory());
937 Terser.set(msh, 1, 0, 1, 1, String.valueOf(fieldSep));
938 Terser.set(msh, 2, 0, 1, 1, encChars);
939 Terser.set(msh, 10, 0, 1, 1, messControlID);
940 Terser.set(msh, 11, 0, 1, 1, procIDComps[0]);
941 Terser.set(msh, 12, 0, 1, 1, version);
942 return msh;
943
944 } catch (Exception e) {
945 throw new HL7Exception("Can't parse critical fields from MSH segment (" + e.getClass().getName() + ": " + e.getMessage() + "): " + mshString, ErrorCode.REQUIRED_FIELD_MISSING, e);
946 }
947
948 }
949
950
951
952
953
954
955
956
957
958
959 public String getAckID(String message) {
960 String ackID = null;
961 int startMSA = message.indexOf("\rMSA");
962 if (startMSA >= 0) {
963 int startFieldOne = startMSA + 5;
964 char fieldDelim = message.charAt(startFieldOne - 1);
965 int start = message.indexOf(fieldDelim, startFieldOne) + 1;
966 int end = message.indexOf(fieldDelim, start);
967 int segEnd = message.indexOf(SEGMENT_DELIMITER, start);
968 if (segEnd > start && segEnd < end)
969 end = segEnd;
970
971
972
973 if (end < 0) {
974 if (message.charAt(message.length() - 1) == '\r') {
975 end = message.length() - 1;
976 } else {
977 end = message.length();
978 }
979 }
980 if (start > 0 && end > start) {
981 ackID = message.substring(start, end);
982 }
983 }
984 log.trace("ACK ID: {}", ackID);
985 return ackID;
986 }
987
988
989
990
991
992
993
994 public void setLegacyMode(boolean legacyMode) {
995 this.myLegacyMode = legacyMode;
996 }
997
998
999
1000
1001 @Override
1002 public String encode(Message source) throws HL7Exception {
1003 if (myLegacyMode != null && myLegacyMode) {
1004
1005 @SuppressWarnings("deprecation")
1006 OldPipeParser oldPipeParser = new OldPipeParser(getFactory());
1007
1008 return oldPipeParser.encode(source);
1009 }
1010 return super.encode(source);
1011 }
1012
1013
1014
1015
1016 @Override
1017 public Message parse(String message) throws HL7Exception {
1018 if (myLegacyMode != null && myLegacyMode) {
1019
1020 @SuppressWarnings("deprecation")
1021 OldPipeParser oldPipeParser = new OldPipeParser(getFactory());
1022
1023 return oldPipeParser.parse(message);
1024 }
1025 return super.parse(message);
1026 }
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052 public boolean isLegacyMode() {
1053 if (myLegacyMode == null)
1054 return (Boolean.parseBoolean(System.getProperty(DEFAULT_LEGACY_MODE_PROPERTY)));
1055 return this.myLegacyMode;
1056 }
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067 public String getVersion(String message) throws HL7Exception {
1068 int startMSH = message.indexOf("MSH");
1069 int endMSH = message.indexOf(PipeParser.SEGMENT_DELIMITER, startMSH);
1070 if (endMSH < 0)
1071 endMSH = message.length();
1072 String msh = message.substring(startMSH, endMSH);
1073 String fieldSep;
1074 if (msh.length() > 3) {
1075 fieldSep = String.valueOf(msh.charAt(3));
1076 } else {
1077 throw new HL7Exception("Can't find field separator in MSH: " + msh, ErrorCode.UNSUPPORTED_VERSION_ID);
1078 }
1079
1080 String[] fields = split(msh, fieldSep);
1081
1082 String compSep;
1083 if (fields.length >= 2 && fields[1] != null && (fields[1].length() == 4 || fields[1].length() == 5)) {
1084 compSep = String.valueOf(fields[1].charAt(0));
1085 } else {
1086 throw new HL7Exception("Invalid or incomplete encoding characters - MSH-2 is " + fields[1], ErrorCode.REQUIRED_FIELD_MISSING);
1087 }
1088
1089 String version;
1090 if (fields.length >= 12) {
1091 String[] comp = split(fields[11], compSep);
1092 if (comp.length >= 1) {
1093 version = comp[0];
1094 } else {
1095 throw new HL7Exception("Can't find version ID - MSH.12 is " + fields[11], ErrorCode.REQUIRED_FIELD_MISSING);
1096 }
1097 } else if (getParserConfiguration().isAllowUnknownVersions()) {
1098 return Version.highestAvailableVersionOrDefault().getVersion();
1099 } else {
1100 throw new HL7Exception("Can't find version ID - MSH has only " + fields.length + " fields.", ErrorCode.REQUIRED_FIELD_MISSING);
1101 }
1102 return version;
1103 }
1104
1105 @Override
1106 public void parse(Message message, String string) throws HL7Exception {
1107 if (message instanceof AbstractSuperMessage && message.getName() == null) {
1108 String structure = getStructure(string).messageStructure;
1109 ((AbstractSuperMessage) message).setName(structure);
1110 }
1111
1112 message.setParser(this);
1113
1114 IStructureDefinition structureDef = getStructureDefinition(message);
1115 MessageIterator messageIter = new MessageIterator(message, structureDef, "MSH", true);
1116
1117 String[] segments = split(string, SEGMENT_DELIMITER);
1118
1119 if (segments.length == 0) {
1120 throw new HL7Exception("Invalid message content: \"" + string + "\"");
1121 }
1122
1123 if (segments[0] == null || segments[0].length() < 4) {
1124 throw new HL7Exception("Invalid message content: \"" + string + "\"");
1125 }
1126
1127 char delim = '|';
1128 String prevName = null;
1129 int repNum = 1;
1130 for (int i = 0; i < segments.length; i++) {
1131
1132
1133 if (segments[i] != null && segments[i].length() > 0 && Character.isWhitespace(segments[i].charAt(0)))
1134 segments[i] = stripLeadingWhitespace(segments[i]);
1135
1136
1137 if (segments[i] != null && segments[i].length() >= 3) {
1138
1139 final String name;
1140 if (i == 0) {
1141 if (segments[i].length() < 4) {
1142 throw new HL7Exception("Invalid message content: \"" + string + "\"");
1143 }
1144 name = segments[i].substring(0, 3);
1145 delim = segments[i].charAt(3);
1146 } else {
1147 if (segments[i].indexOf(delim) >= 0) {
1148 name = segments[i].substring(0, segments[i].indexOf(delim));
1149 } else {
1150 name = segments[i];
1151 }
1152 }
1153
1154 log.trace("Parsing segment {}", name);
1155
1156 if (name.equals(prevName)) {
1157 repNum++;
1158 } else {
1159 repNum = 1;
1160 prevName = name;
1161 }
1162
1163 messageIter.setDirection(name);
1164
1165 try {
1166 if (messageIter.hasNext()) {
1167 Segment next = (Segment) messageIter.next();
1168 parse(next, segments[i], getEncodingChars(string), repNum);
1169 }
1170 } catch (Error e) {
1171 if (e.getCause() instanceof HL7Exception) {
1172 throw (HL7Exception)e.getCause();
1173 }
1174 throw e;
1175 }
1176 }
1177 }
1178
1179 applySuperStructureName(message);
1180 }
1181
1182
1183
1184
1185
1186 private static class MessageStructure {
1187 public final String messageStructure;
1188 public final boolean explicitlyDefined;
1189
1190 public MessageStructure(String theMessageStructure, boolean isExplicitlyDefined) {
1191 messageStructure = theMessageStructure;
1192 explicitlyDefined = isExplicitlyDefined;
1193 }
1194 }
1195
1196 private static class Holder<T> {
1197 private T myObject;
1198
1199 public T getObject() {
1200 return myObject;
1201 }
1202
1203 public void setObject(T theObject) {
1204 myObject = theObject;
1205 }
1206 }
1207
1208 }