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