001/** 002 * The contents of this file are subject to the Mozilla Public License Version 1.1 003 * (the "License"); you may not use this file except in compliance with the License. 004 * You may obtain a copy of the License at http://www.mozilla.org/MPL/ 005 * Software distributed under the License is distributed on an "AS IS" basis, 006 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the 007 * specific language governing rights and limitations under the License. 008 * 009 * The Original Code is "PipeParser.java". Description: 010 * "An implementation of Parser that supports traditionally encoded (i.e" 011 * 012 * The Initial Developer of the Original Code is University Health Network. Copyright (C) 013 * 2001. All Rights Reserved. 014 * 015 * Contributor(s): Kenneth Beaton. 016 * 017 * Alternatively, the contents of this file may be used under the terms of the 018 * GNU General Public License (the �GPL�), in which case the provisions of the GPL are 019 * applicable instead of those above. If you wish to allow use of your version of this 020 * file only under the terms of the GPL and not to allow others to use your version 021 * of this file under the MPL, indicate your decision by deleting the provisions above 022 * and replace them with the notice and other provisions required by the GPL License. 023 * If you do not delete the provisions above, a recipient may use your version of 024 * this file under either the MPL or the GPL. 025 * 026 */ 027 028package ca.uhn.hl7v2.parser; 029 030import java.util.ArrayList; 031import java.util.List; 032import java.util.StringTokenizer; 033 034import org.slf4j.Logger; 035import org.slf4j.LoggerFactory; 036 037import ca.uhn.hl7v2.HL7Exception; 038import ca.uhn.hl7v2.model.Group; 039import ca.uhn.hl7v2.model.Message; 040import ca.uhn.hl7v2.model.Primitive; 041import ca.uhn.hl7v2.model.Segment; 042import ca.uhn.hl7v2.model.Structure; 043import ca.uhn.hl7v2.model.Type; 044import ca.uhn.hl7v2.model.Varies; 045import ca.uhn.hl7v2.util.FilterIterator; 046import ca.uhn.hl7v2.util.MessageIterator; 047import ca.uhn.hl7v2.util.Terser; 048 049/** 050 * This is a legacy implementation of the PipeParser and should not be used 051 * for new projects. 052 * 053 * In version 1.0 of HAPI, a behaviour was corrected where unexpected segments 054 * would be placed at the tail end of the first segment group encountered. Any 055 * legacy code which still depends on previous behaviour can use this 056 * implementation. 057 * 058 * @author Bryan Tripp (bryan_tripp@sourceforge.net) 059 * @deprecated 060 */ 061public class OldPipeParser extends Parser { 062 063 private static final Logger log = LoggerFactory.getLogger(OldPipeParser.class); 064 065 private final static String segDelim = "\r"; //see section 2.8 of spec 066 067 /** Creates a new PipeParser */ 068 public OldPipeParser() { 069 } 070 071 /** 072 * Creates a new PipeParser 073 * 074 * @param theFactory custom factory to use for model class lookup 075 */ 076 public OldPipeParser(ModelClassFactory theFactory) { 077 super(theFactory); 078 } 079 080 /** 081 * Returns a String representing the encoding of the given message, if 082 * the encoding is recognized. For example if the given message appears 083 * to be encoded using HL7 2.x XML rules then "XML" would be returned. 084 * If the encoding is not recognized then null is returned. That this 085 * method returns a specific encoding does not guarantee that the 086 * message is correctly encoded (e.g. well formed XML) - just that 087 * it is not encoded using any other encoding than the one returned. 088 */ 089 public String getEncoding(String message) { 090 String encoding = null; 091 092 //quit if the string is too short 093 if (message.length() < 4) 094 return null; 095 096 //see if it looks like this message is | encoded ... 097 boolean ok = true; 098 099 //string should start with "MSH" 100 if (!message.startsWith("MSH")) 101 return null; 102 103 //4th character of each segment should be field delimiter 104 char fourthChar = message.charAt(3); 105 StringTokenizer st = new StringTokenizer(message, String.valueOf(segDelim), false); 106 while (st.hasMoreTokens()) { 107 String x = st.nextToken(); 108 if (x.length() > 0) { 109 if (Character.isWhitespace(x.charAt(0))) 110 x = stripLeadingWhitespace(x); 111 if (x.length() >= 4 && x.charAt(3) != fourthChar) 112 return null; 113 } 114 } 115 116 //should be at least 11 field delimiters (because MSH-12 is required) 117 int nextFieldDelimLoc = 0; 118 for (int i = 0; i < 11; i++) { 119 nextFieldDelimLoc = message.indexOf(fourthChar, nextFieldDelimLoc + 1); 120 if (nextFieldDelimLoc < 0) 121 return null; 122 } 123 124 if (ok) 125 encoding = "VB"; 126 127 return encoding; 128 } 129 130 /** 131 * @return the preferred encoding of this Parser 132 */ 133 public String getDefaultEncoding() { 134 return "VB"; 135 } 136 137 /** 138 * Returns true if and only if the given encoding is supported 139 * by this Parser. 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 * @deprecated this method should not be public 150 * @param message 151 * @return 152 * @throws HL7Exception 153 * @throws EncodingNotSupportedException 154 */ 155 public String getMessageStructure(String message) throws HL7Exception, EncodingNotSupportedException { 156 return getStructure(message).messageStructure; 157 } 158 159 /** 160 * @returns the message structure from MSH-9-3 161 */ 162 private MessageStructure getStructure(String message) throws HL7Exception, EncodingNotSupportedException { 163 EncodingCharacters ec = getEncodingChars(message); 164 String messageStructure = null; 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 //message structure is component 3 but we'll accept a composite of 1 and 2 if there is no component 3 ... 173 // if component 1 is ACK, then the structure is ACK regardless of component 2 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 /*else if (comps.length == 1 && comps[0] != null && comps[0].equals("ACK")) { 184 messageStructure = "ACK"; //it's common for people to only populate component 1 in an ACK msg 185 }*/ 186 else { 187 StringBuffer buf = new StringBuffer("Can't determine message structure from MSH-9: "); 188 buf.append(wholeFieldNine); 189 if (comps.length < 3) { 190 buf.append(" HINT: there are only "); 191 buf.append(comps.length); 192 buf.append(" of 3 components present"); 193 } 194 throw new HL7Exception(buf.toString(), HL7Exception.UNSUPPORTED_MESSAGE_TYPE); 195 } 196 } 197 catch (IndexOutOfBoundsException e) { 198 throw new HL7Exception( 199 "Can't find message structure (MSH-9-3): " + e.getMessage(), 200 HL7Exception.UNSUPPORTED_MESSAGE_TYPE); 201 } 202 203 return new MessageStructure(messageStructure, explicityDefined); 204 } 205 206 /** 207 * Returns object that contains the field separator and encoding characters 208 * for this message. 209 */ 210 private static EncodingCharacters getEncodingChars(String message) { 211 return new EncodingCharacters(message.charAt(3), message.substring(4, 8)); 212 } 213 214 /** 215 * Parses a message string and returns the corresponding Message 216 * object. Unexpected segments added at the end of their group. 217 * 218 * @throws HL7Exception if the message is not correctly formatted. 219 * @throws EncodingNotSupportedException if the message encoded 220 * is not supported by this parser. 221 */ 222 protected Message doParse(String message, String version) throws HL7Exception, EncodingNotSupportedException { 223 224 //try to instantiate a message object of the right class 225 MessageStructure structure = getStructure(message); 226 Message m = instantiateMessage(structure.messageStructure, version, structure.explicitlyDefined); 227 228 parse(m, message); 229 230 return m; 231 } 232 233 /** 234 * Parses a segment string and populates the given Segment object. Unexpected fields are 235 * added as Varies' at the end of the segment. 236 * 237 * @throws HL7Exception if the given string does not contain the 238 * given segment or if the string is not encoded properly 239 */ 240 public void parse(Segment destination, String segment, EncodingCharacters encodingChars) throws HL7Exception { 241 int fieldOffset = 0; 242 if (isDelimDefSegment(destination.getName())) { 243 fieldOffset = 1; 244 //set field 1 to fourth character of string 245 Terser.set(destination, 1, 0, 1, 1, String.valueOf(encodingChars.getFieldSeparator())); 246 } 247 248 String[] fields = split(segment, String.valueOf(encodingChars.getFieldSeparator())); 249 //destination.setName(fields[0]); 250 for (int i = 1; i < fields.length; i++) { 251 String[] reps = split(fields[i], String.valueOf(encodingChars.getRepetitionSeparator())); 252 log.debug("{} reps delimited by: {}", reps.length, encodingChars.getRepetitionSeparator()); 253 254 //MSH-2 will get split incorrectly so we have to fudge it ... 255 boolean isMSH2 = isDelimDefSegment(destination.getName()) && i+fieldOffset == 2; 256 if (isMSH2) { 257 reps = new String[1]; 258 reps[0] = fields[i]; 259 } 260 261 for (int j = 0; j < reps.length; j++) { 262 try { 263 StringBuffer statusMessage = new StringBuffer("Parsing field "); 264 statusMessage.append(i+fieldOffset); 265 statusMessage.append(" repetition "); 266 statusMessage.append(j); 267 log.debug(statusMessage.toString()); 268 //parse(destination.getField(i + fieldOffset, j), reps[j], encodingChars, false); 269 270 Type field = destination.getField(i + fieldOffset, j); 271 if (isMSH2) { 272 Terser.getPrimitive(field, 1, 1).setValue(reps[j]); 273 } else { 274 parse(field, reps[j], encodingChars); 275 } 276 } 277 catch (HL7Exception e) { 278 //set the field location and throw again ... 279 e.setFieldPosition(i); 280 e.setSegmentRepetition(MessageIterator.getIndex(destination.getParent(), destination).rep); 281 e.setSegmentName(destination.getName()); 282 throw e; 283 } 284 } 285 } 286 287 //set data type of OBX-5 288 if (destination.getClass().getName().indexOf("OBX") >= 0) { 289 Varies.fixOBX5(destination, getFactory()); 290 } 291 292 } 293 294 /** 295 * @return true if the segment is MSH, FHS, or BHS. These need special treatment 296 * because they define delimiters. 297 * @param theSegmentName 298 */ 299 private static boolean isDelimDefSegment(String theSegmentName) { 300 boolean is = false; 301 if (theSegmentName.equals("MSH") 302 || theSegmentName.equals("FHS") 303 || theSegmentName.equals("BHS")) 304 { 305 is = true; 306 } 307 return is; 308 } 309 310 /** 311 * Fills a field with values from an unparsed string representing the field. 312 * @param destinationField the field Type 313 * @param data the field string (including all components and subcomponents; not including field delimiters) 314 * @param encodingCharacters the encoding characters used in the message 315 */ 316 public void parse(Type destinationField, String data, EncodingCharacters encodingCharacters) throws HL7Exception { 317 String[] components = split(data, String.valueOf(encodingCharacters.getComponentSeparator())); 318 for (int i = 0; i < components.length; i++) { 319 String[] subcomponents = split(components[i], String.valueOf(encodingCharacters.getSubcomponentSeparator())); 320 for (int j = 0; j < subcomponents.length; j++) { 321 String val = subcomponents[j]; 322 if (val != null) { 323 val = Escape.unescape(val, encodingCharacters); 324 } 325 Terser.getPrimitive(destinationField, i+1, j+1).setValue(val); 326 } 327 } 328 } 329 330 /** 331 * Splits the given composite string into an array of components using the given 332 * delimiter. 333 */ 334 public static String[] split(String composite, String delim) { 335 List<String> components = new ArrayList<String>(); 336 337 //defend against evil nulls 338 if (composite == null) 339 composite = ""; 340 if (delim == null) 341 delim = ""; 342 343 StringTokenizer tok = new StringTokenizer(composite, delim, true); 344 boolean previousTokenWasDelim = true; 345 while (tok.hasMoreTokens()) { 346 String thisTok = tok.nextToken(); 347 if (thisTok.equals(delim)) { 348 if (previousTokenWasDelim) 349 components.add(null); 350 previousTokenWasDelim = true; 351 } 352 else { 353 components.add(thisTok); 354 previousTokenWasDelim = false; 355 } 356 } 357 358 return components.toArray(new String[components.size()]); 359 } 360 361 /** 362 * Encodes the given Type, using the given encoding characters. 363 * It is assumed that the Type represents a complete field rather than a component. 364 */ 365 public static String encode(Type source, EncodingCharacters encodingChars) { 366 StringBuffer field = new StringBuffer(); 367 for (int i = 1; i <= Terser.numComponents(source); i++) { 368 StringBuffer comp = new StringBuffer(); 369 for (int j = 1; j <= Terser.numSubComponents(source, i); j++) { 370 Primitive p = Terser.getPrimitive(source, i, j); 371 comp.append(encodePrimitive(p, encodingChars)); 372 comp.append(encodingChars.getSubcomponentSeparator()); 373 } 374 field.append(stripExtraDelimiters(comp.toString(), encodingChars.getSubcomponentSeparator())); 375 field.append(encodingChars.getComponentSeparator()); 376 } 377 return stripExtraDelimiters(field.toString(), encodingChars.getComponentSeparator()); 378 //return encode(source, encodingChars, false); 379 } 380 381 private static String encodePrimitive(Primitive p, EncodingCharacters encodingChars) { 382 String val = ((Primitive) p).getValue(); 383 if (val == null) { 384 val = ""; 385 } else { 386 val = Escape.escape(val, encodingChars); 387 } 388 return val; 389 } 390 391 /** 392 * Removes unecessary delimiters from the end of a field or segment. 393 * This seems to be more convenient than checking to see if they are needed 394 * while we are building the encoded string. 395 */ 396 private static String stripExtraDelimiters(String in, char delim) { 397 char[] chars = in.toCharArray(); 398 399 //search from back end for first occurance of non-delimiter ... 400 int c = chars.length - 1; 401 boolean found = false; 402 while (c >= 0 && !found) { 403 if (chars[c--] != delim) 404 found = true; 405 } 406 407 String ret = ""; 408 if (found) 409 ret = String.valueOf(chars, 0, c + 2); 410 return ret; 411 } 412 413 /** 414 * Formats a Message object into an HL7 message string using the given 415 * encoding. 416 * @throws HL7Exception if the data fields in the message do not permit encoding 417 * (e.g. required fields are null) 418 * @throws EncodingNotSupportedException if the requested encoding is not 419 * supported by this parser. 420 */ 421 protected String doEncode(Message source, String encoding) throws HL7Exception, EncodingNotSupportedException { 422 if (!this.supportsEncoding(encoding)) 423 throw new EncodingNotSupportedException("This parser does not support the " + encoding + " encoding"); 424 425 return encode(source); 426 } 427 428 /** 429 * Formats a Message object into an HL7 message string using this parser's 430 * default encoding ("VB"). 431 * @throws HL7Exception if the data fields in the message do not permit encoding 432 * (e.g. required fields are null) 433 */ 434 protected String doEncode(Message source) throws HL7Exception { 435 //get encoding characters ... 436 Segment msh = (Segment) source.get("MSH"); 437 String fieldSepString = Terser.get(msh, 1, 0, 1, 1); 438 439 if (fieldSepString == null) 440 throw new HL7Exception("Can't encode message: MSH-1 (field separator) is missing"); 441 442 char fieldSep = '|'; 443 if (fieldSepString != null && fieldSepString.length() > 0) 444 fieldSep = fieldSepString.charAt(0); 445 446 String encCharString = Terser.get(msh, 2, 0, 1, 1); 447 448 if (encCharString == null) 449 throw new HL7Exception("Can't encode message: MSH-2 (encoding characters) is missing"); 450 451 if (encCharString.length() != 4) 452 throw new HL7Exception( 453 "Encoding characters '" + encCharString + "' invalid -- must be 4 characters", 454 HL7Exception.DATA_TYPE_ERROR); 455 EncodingCharacters en = new EncodingCharacters(fieldSep, encCharString); 456 457 //pass down to group encoding method which will operate recursively on children ... 458 return encode((Group) source, en); 459 } 460 461 /** 462 * Returns given group serialized as a pipe-encoded string - this method is called 463 * by encode(Message source, String encoding). 464 */ 465 public static String encode(Group source, EncodingCharacters encodingChars) throws HL7Exception { 466 StringBuffer result = new StringBuffer(); 467 468 String[] names = source.getNames(); 469 for (int i = 0; i < names.length; i++) { 470 Structure[] reps = source.getAll(names[i]); 471 for (int rep = 0; rep < reps.length; rep++) { 472 if (reps[rep] instanceof Group) { 473 result.append(encode((Group) reps[rep], encodingChars)); 474 } 475 else { 476 String segString = encode((Segment) reps[rep], encodingChars); 477 if (segString.length() >= 4) { 478 result.append(segString); 479 result.append('\r'); 480 } 481 } 482 } 483 } 484 return result.toString(); 485 } 486 487 public static String encode(Segment source, EncodingCharacters encodingChars) { 488 StringBuffer result = new StringBuffer(); 489 result.append(source.getName()); 490 result.append(encodingChars.getFieldSeparator()); 491 492 //start at field 2 for MSH segment because field 1 is the field delimiter 493 int startAt = 1; 494 if (isDelimDefSegment(source.getName())) 495 startAt = 2; 496 497 //loop through fields; for every field delimit any repetitions and add field delimiter after ... 498 int numFields = source.numFields(); 499 for (int i = startAt; i <= numFields; i++) { 500 try { 501 Type[] reps = source.getField(i); 502 for (int j = 0; j < reps.length; j++) { 503 String fieldText = encode(reps[j], encodingChars); 504 //if this is MSH-2, then it shouldn't be escaped, so unescape it again 505 if (isDelimDefSegment(source.getName()) && i == 2) 506 fieldText = Escape.unescape(fieldText, encodingChars); 507 result.append(fieldText); 508 if (j < reps.length - 1) 509 result.append(encodingChars.getRepetitionSeparator()); 510 } 511 } 512 catch (HL7Exception e) { 513 log.error("Error while encoding segment: ", e); 514 } 515 result.append(encodingChars.getFieldSeparator()); 516 } 517 518 //strip trailing delimiters ... 519 return stripExtraDelimiters(result.toString(), encodingChars.getFieldSeparator()); 520 } 521 522 /** 523 * Removes leading whitespace from the given string. This method was created to deal with frequent 524 * problems parsing messages that have been hand-written in windows. The intuitive way to delimit 525 * segments is to hit <ENTER> at the end of each segment, but this creates both a carriage return 526 * and a line feed, so to the parser, the first character of the next segment is the line feed. 527 */ 528 public static String stripLeadingWhitespace(String in) { 529 StringBuffer out = new StringBuffer(); 530 char[] chars = in.toCharArray(); 531 int c = 0; 532 while (c < chars.length) { 533 if (!Character.isWhitespace(chars[c])) 534 break; 535 c++; 536 } 537 for (int i = c; i < chars.length; i++) { 538 out.append(chars[i]); 539 } 540 return out.toString(); 541 } 542 543 /** 544 * <p>Returns a minimal amount of data from a message string, including only the 545 * data needed to send a response to the remote system. This includes the 546 * following fields: 547 * <ul><li>field separator</li> 548 * <li>encoding characters</li> 549 * <li>processing ID</li> 550 * <li>message control ID</li></ul> 551 * This method is intended for use when there is an error parsing a message, 552 * (so the Message object is unavailable) but an error message must be sent 553 * back to the remote system including some of the information in the inbound 554 * message. This method parses only that required information, hopefully 555 * avoiding the condition that caused the original error. The other 556 * fields in the returned MSH segment are empty.</p> 557 */ 558 public Segment getCriticalResponseData(String message) throws HL7Exception { 559 //try to get MSH segment 560 int locStartMSH = message.indexOf("MSH"); 561 if (locStartMSH < 0) 562 throw new HL7Exception( 563 "Couldn't find MSH segment in message: " + message, 564 HL7Exception.SEGMENT_SEQUENCE_ERROR); 565 int locEndMSH = message.indexOf('\r', locStartMSH + 1); 566 if (locEndMSH < 0) 567 locEndMSH = message.length(); 568 String mshString = message.substring(locStartMSH, locEndMSH); 569 570 //find out what the field separator is 571 char fieldSep = mshString.charAt(3); 572 573 //get field array 574 String[] fields = split(mshString, String.valueOf(fieldSep)); 575 576 Segment msh = null; 577 try { 578 //parse required fields 579 String encChars = fields[1]; 580 char compSep = encChars.charAt(0); 581 String messControlID = fields[9]; 582 String[] procIDComps = split(fields[10], String.valueOf(compSep)); 583 584 //fill MSH segment 585 String version = "2.4"; //default 586 try { 587 version = this.getVersion(message); 588 } 589 catch (Exception e) { /* use the default */ 590 } 591 592 msh = Parser.makeControlMSH(version, getFactory()); 593 Terser.set(msh, 1, 0, 1, 1, String.valueOf(fieldSep)); 594 Terser.set(msh, 2, 0, 1, 1, encChars); 595 Terser.set(msh, 10, 0, 1, 1, messControlID); 596 Terser.set(msh, 11, 0, 1, 1, procIDComps[0]); 597 Terser.set(msh, 12, 0, 1, 1, version); 598 599 } 600 catch (Exception e) { 601 throw new HL7Exception( 602 "Can't parse critical fields from MSH segment (" 603 + e.getClass().getName() 604 + ": " 605 + e.getMessage() 606 + "): " 607 + mshString, 608 HL7Exception.REQUIRED_FIELD_MISSING, e); 609 } 610 611 return msh; 612 } 613 614 /** 615 * For response messages, returns the value of MSA-2 (the message ID of the message 616 * sent by the sending system). This value may be needed prior to main message parsing, 617 * so that (particularly in a multi-threaded scenario) the message can be routed to 618 * the thread that sent the request. We need this information first so that any 619 * parse exceptions are thrown to the correct thread. 620 * Returns null if MSA-2 can not be found (e.g. if the message is not a 621 * response message). 622 */ 623 public String getAckID(String message) { 624 String ackID = null; 625 int startMSA = message.indexOf("\rMSA"); 626 if (startMSA >= 0) { 627 int startFieldOne = startMSA + 5; 628 char fieldDelim = message.charAt(startFieldOne - 1); 629 int start = message.indexOf(fieldDelim, startFieldOne) + 1; 630 int end = message.indexOf(fieldDelim, start); 631 int segEnd = message.indexOf(String.valueOf(segDelim), start); 632 if (segEnd > start && segEnd < end) 633 end = segEnd; 634 635 //if there is no field delim after MSH-2, need to go to end of message, but not including end seg delim if it exists 636 if (end < 0) { 637 if (message.charAt(message.length() - 1) == '\r') { 638 end = message.length() - 1; 639 } 640 else { 641 end = message.length(); 642 } 643 } 644 if (start > 0 && end > start) { 645 ackID = message.substring(start, end); 646 } 647 } 648 log.debug("ACK ID: {}", ackID); 649 return ackID; 650 } 651 652 /** 653 * Returns the version ID (MSH-12) from the given message, without fully parsing the message. 654 * The version is needed prior to parsing in order to determine the message class 655 * into which the text of the message should be parsed. 656 * @throws HL7Exception if the version field can not be found. 657 */ 658 public String getVersion(String message) throws HL7Exception { 659 int startMSH = message.indexOf("MSH"); 660 int endMSH = message.indexOf(OldPipeParser.segDelim, startMSH); 661 if (endMSH < 0) 662 endMSH = message.length(); 663 String msh = message.substring(startMSH, endMSH); 664 String fieldSep = null; 665 if (msh.length() > 3) { 666 fieldSep = String.valueOf(msh.charAt(3)); 667 } 668 else { 669 throw new HL7Exception("Can't find field separator in MSH: " + msh, HL7Exception.UNSUPPORTED_VERSION_ID); 670 } 671 672 String[] fields = split(msh, fieldSep); 673 674 String compSep = null; 675 if (fields.length >= 2 && fields[1] != null && fields[1].length() == 4) { 676 compSep = String.valueOf(fields[1].charAt(0)); //get component separator as 1st encoding char 677 } 678 else { 679 throw new HL7Exception("Invalid or incomplete encoding characters - MSH-2 is " + fields[1], 680 HL7Exception.REQUIRED_FIELD_MISSING); 681 } 682 683 String version = null; 684 if (fields.length >= 12) { 685 String[] comp = split(fields[11], compSep); 686 if (comp.length >= 1) { 687 version = comp[0]; 688 } else { 689 throw new HL7Exception("Can't find version ID - MSH.12 is " + fields[11], 690 HL7Exception.REQUIRED_FIELD_MISSING); 691 } 692 } 693 else { 694 throw new HL7Exception( 695 "Can't find version ID - MSH has only " + fields.length + " fields.", 696 HL7Exception.REQUIRED_FIELD_MISSING); 697 } 698 return version; 699 } 700 701 /** 702 * {@inheritDoc } 703 */ 704 public String doEncode(Segment structure, EncodingCharacters encodingCharacters) throws HL7Exception { 705 return encode(structure, encodingCharacters); 706 } 707 708 /** 709 * {@inheritDoc } 710 */ 711 public String doEncode(Type type, EncodingCharacters encodingCharacters) throws HL7Exception { 712 return encode(type, encodingCharacters); 713 } 714 715 /** 716 * Throws unsupported operation exception 717 * 718 * @throws Unsupported operation exception 719 */ 720 @Override 721 protected Message doParseForSpecificPackage(String theMessage, String theVersion, String thePackageName) throws HL7Exception, EncodingNotSupportedException { 722 throw new UnsupportedOperationException("Not supported yet."); 723 } 724 725 public void parse(Message message, String string) throws HL7Exception { 726 //MessagePointer ptr = new MessagePointer(this, m, getEncodingChars(message)); 727 MessageIterator messageIter = new MessageIterator(message, "MSH", true); 728 FilterIterator.Predicate<Structure> segmentsOnly = new FilterIterator.Predicate<Structure>() { 729 public boolean evaluate(Structure obj) { 730 if (Segment.class.isAssignableFrom(obj.getClass())) { 731 return true; 732 } else { 733 return false; 734 } 735 } 736 }; 737 FilterIterator<Structure> segmentIter = new FilterIterator<Structure>(messageIter, segmentsOnly); 738 739 String[] segments = split(string, segDelim); 740 741 char delim = '|'; 742 for (int i = 0; i < segments.length; i++) { 743 744 //get rid of any leading whitespace characters ... 745 if (segments[i] != null && segments[i].length() > 0 && Character.isWhitespace(segments[i].charAt(0))) 746 segments[i] = stripLeadingWhitespace(segments[i]); 747 748 //sometimes people put extra segment delimiters at end of msg ... 749 if (segments[i] != null && segments[i].length() >= 3) { 750 final String name; 751 if (i == 0) { 752 name = segments[i].substring(0, 3); 753 delim = segments[i].charAt(3); 754 } else { 755 if (segments[i].indexOf(delim) >= 0 ) { 756 name = segments[i].substring(0, segments[i].indexOf(delim)); 757 } else { 758 name = segments[i]; 759 } 760 } 761 762 log.debug("Parsing segment {}", name); 763 764 messageIter.setDirection(name); 765 FilterIterator.Predicate<Structure> byDirection = new FilterIterator.Predicate<Structure>() { 766 public boolean evaluate(Structure obj) { 767 Structure s = (Structure) obj; 768 log.debug("PipeParser iterating message in direction {} at {} ", name, s.getName()); 769 return s.getName().matches(name + "\\d*"); 770 } 771 }; 772 FilterIterator<Structure> dirIter = new FilterIterator<Structure>(segmentIter, byDirection); 773 if (dirIter.hasNext()) { 774 parse((Segment) dirIter.next(), segments[i], getEncodingChars(string)); 775 } 776 } 777 } 778 } 779 780 781 /** 782 * A struct for holding a message class string and a boolean indicating whether it 783 * was defined explicitly. 784 */ 785 private static class MessageStructure { 786 public String messageStructure; 787 public boolean explicitlyDefined; 788 789 public MessageStructure(String theMessageStructure, boolean isExplicitlyDefined) { 790 messageStructure = theMessageStructure; 791 explicitlyDefined = isExplicitlyDefined; 792 } 793 } 794 795}